updated on Fri Jan 20 00:01:56 UTC 2012
[aur-mirror.git] / xfce4-macmenu-plugin / macmenu-applet.c
blob92b3311e3de63d497e1b2af077b13321b284afdc
1 #define WNCK_I_KNOW_THIS_IS_UNSTABLE
3 #include <X11/Xatom.h>
4 #include <errno.h>
5 #include <gdk/gdkx.h>
6 #include <glib/gstdio.h>
7 #include <gtk/gtk.h>
8 #include <libwnck/libwnck.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <strings.h>
12 #include <time.h>
13 #include <unistd.h>
14 #include "macmenu-tslist.h" // const char* TS_LIST_DEFAULT
16 #ifdef FOR_XFCE
17 // begin FOR_XFCE
18 #include <libxfce4panel/xfce-panel-convenience.h>
19 #include <libxfce4panel/xfce-panel-plugin.h>
20 #include <libxfce4util/libxfce4util.h>
21 typedef XfcePanelPlugin AppletType;
22 const gchar* MAIN_LABEL_TEXT = "Xfce";
23 #elif FOR_GNOME
24 // begin FOR_GNOME
25 #include <gconf/gconf-client.h>
26 #include <panel-applet.h>
27 typedef PanelApplet AppletType;
28 const gchar* MAIN_LABEL_TEXT = "GNOME";
29 #else
30 // no FOR_GNOME or FOR_XFCE, error
31 #error "Please specify whether to build GNOME panel applet or Xfce panel plugin"
32 #endif
34 const char* TS_LIST_FILENAME = ".macmenu-tslist";
35 const int MAX_LABEL_WIDTH_N_CHARS = 15;
36 const int SHORTCUT_SPACING = 8;
38 typedef struct {
39 AppletType* applet;
40 // Core Data
41 WnckScreen* screen;
42 GHashTable* mbars_scks;
43 gboolean hide_label;
44 // Widgets
45 GtkBox* basebox;
46 GtkNotebook* notebook;
47 GtkLabel* label;
48 GtkWidget* label_space;
49 GtkButton* button;
50 GtkWidget* mainsck;
51 GtkWidget* dummysck;
52 // Title subtitle hash
53 time_t ts_mtime;
54 GHashTable* title_subs;
55 #ifdef FOR_GNOME
56 GConfClient* gconf;
57 #endif
58 } MacMenu;
61 static int handle_x_error (Display* display,
62 XErrorEvent* error)
64 printf("Caught stupid X error, ignore it...");
65 return 0;
68 // don't use wnck's, it would fail if main window isn't shown yet.
69 static Window get_transient_for(Window window)
71 Window parent = 0;
72 Window* w = NULL;
73 Atom type;
74 int format;
75 gulong nitems;
76 gulong bytes_after;
77 if (XGetWindowProperty (gdk_display, window,
78 XInternAtom(gdk_display, "WM_TRANSIENT_FOR", FALSE),
79 0, G_MAXLONG, FALSE, XA_WINDOW,
80 &type, &format, &nitems, &bytes_after,
81 (guchar **) &w) == Success
82 && w != NULL)
84 parent = *w;
85 XFree (w);
87 return parent;
90 static gboolean is_menubar(WnckWindow* window)
92 gboolean ret = FALSE;
94 if (wnck_window_get_window_type(window) != WNCK_WINDOW_DOCK)
95 ret = FALSE;
96 else if (strcmp(wnck_window_get_name(window), "GTK MENUBAR") == 0)
97 ret = TRUE;
98 else
100 Atom type;
101 int format;
102 gulong nitems;
103 gulong bytes_after;
104 Atom *data;
106 if (XGetWindowProperty(gdk_display,
107 wnck_window_get_xid(window),
108 XInternAtom(gdk_display, "_NET_WM_WINDOW_TYPE", FALSE),
109 0, G_MAXLONG,
110 False, XA_ATOM, &type, &format, &nitems,
111 &bytes_after, (void*) &data) == Success
112 && data != NULL)
114 if (data[0] == XInternAtom(gdk_display, "_KDE_NET_WM_WINDOW_TYPE_TOPMENU", FALSE))
115 ret = TRUE;
116 XFree (data);
119 return ret;
122 static void find_mbar_by_mwin(gpointer key,
123 gpointer value,
124 gpointer user_data)
126 Window mbar = (Window) key;
127 Window mwin = get_transient_for(mbar);
128 long* inout = (long*) user_data;
129 if (mwin && mwin == inout[0])
130 inout[1] = mbar;
133 static void find_mbar_by_sck(gpointer key,
134 gpointer value,
135 gpointer user_data)
137 Window mbar = (Window) key;
138 GtkWidget* sck = (GtkWidget*) value;
139 long* inout = (long*) user_data;
140 if (sck && sck == (GtkWidget*) inout[0])
141 inout[1] = mbar;
144 static void socket_destroyed(GtkWidget* sck, MacMenu* mmb)
146 long inout[2] = {0, 0};
147 inout[0] = (long) sck;
148 g_hash_table_foreach(mmb->mbars_scks, find_mbar_by_sck, inout);
149 if (inout[1])
150 g_hash_table_remove(mmb->mbars_scks, (gpointer) inout[1]);
153 static void add_menubar(MacMenu* mmb, WnckWindow* mbarwin)
155 Window mbar = wnck_window_get_xid(mbarwin);
156 GtkWidget* sck = gtk_socket_new();
157 g_signal_connect(sck, "destroy", G_CALLBACK(socket_destroyed), mmb);
158 gtk_notebook_append_page(mmb->notebook, GTK_WIDGET(sck), NULL);
159 gtk_socket_steal(GTK_SOCKET(sck), mbar);
160 gtk_widget_show_all(sck);
161 g_hash_table_insert(mmb->mbars_scks, (gpointer) mbar, sck);
164 static void update_title_substitute_table(MacMenu* mmb)
166 gchar* ts_list_path = g_build_filename(g_get_home_dir(), TS_LIST_FILENAME, NULL);
167 struct stat sbuf;
168 if (g_stat(ts_list_path, &sbuf))
170 FILE* nf = g_fopen(ts_list_path, "w");
171 if (nf == NULL)
172 fprintf(stderr, "Unable to create %s\n", ts_list_path);
173 else
175 fwrite(TS_LIST_DEFAULT, 1, strlen(TS_LIST_DEFAULT), nf);
176 sbuf.st_mtime = time(NULL);
177 fclose(nf);
178 fprintf(stdout, "New %s created\n", ts_list_path);
181 // update if the file has been modified
182 if (sbuf.st_mtime > mmb->ts_mtime)
184 GIOChannel* ioc = g_io_channel_new_file(ts_list_path, "r", NULL);
185 if (ioc == NULL)
186 fprintf(stderr, "Unable to open %s for reading list after successful stat()\n", ts_list_path);
187 else
189 g_hash_table_remove_all(mmb->title_subs);
190 gsize line_len, line_term;
191 gchar* line_str;
192 while (g_io_channel_read_line(ioc, &line_str, &line_len, &line_term,
193 NULL) == G_IO_STATUS_NORMAL)
195 char* sep_pos;
196 if (line_str != NULL && (sep_pos = rindex(line_str, '=')) != NULL)
198 gchar* key = line_str;
199 gchar* value = sep_pos + 1;
200 sep_pos[0] = 0;
201 line_str[line_term] = 0;
202 g_hash_table_insert(mmb->title_subs, key, value);
205 g_io_channel_shutdown(ioc, FALSE, NULL);
208 mmb->ts_mtime = sbuf.st_mtime;
209 g_free(ts_list_path);
212 static const char* get_application_name(WnckWindow* window, MacMenu* mmb)
214 update_title_substitute_table(mmb);
215 const gchar* orig_name = wnck_application_get_name(
216 wnck_window_get_application(window));
217 const gchar* new_name = g_hash_table_lookup(
218 mmb->title_subs, (gpointer) orig_name);
219 return (new_name ? new_name: orig_name);
223 //printf("[%s]\n", orig_name);
224 char* aname = NULL;
225 // check vmware
226 if (! strcmp(orig_name, "vmware"))
227 return g_strdup("VMware");
228 // check epiphany
229 else if (! strcmp(orig_name, "Web Browser"))
230 return g_strdup("Epiphany");
231 // check evince
232 else if (! strcmp(orig_name, "Evince Document Viewer"))
233 return g_strdup("Evince");
234 // suse's control center?
235 else if (! strcmp(orig_name, "Gnome Control Center"))
236 return g_strdup("Control Center");
237 // gnome control center
238 else if (! strcmp(orig_name, "control-center"))
239 return g_strdup("Control Center");
240 else if (! strcmp(orig_name, "file-managment-properties"))
241 return g_strdup("File Management");
242 else if (! strcmp(orig_name, "gcin-setup"))
243 return g_strdup("Gcin Setup");
244 // other control center parts
245 else if (! strncmp(orig_name, "gnome-", 6))
247 aname = g_strdup(orig_name+6);
248 for (int i=0; i<strlen(aname); i++)
250 if (aname[i] == '-') aname[i] = ' ';
253 // mono apps
254 else if (! strncmp(orig_name, "/opt/", 5)
255 || ! strncmp(orig_name, "/usr/", 5))
257 aname = g_strdup(rindex(orig_name, '/')+1);
258 if (strlen(aname) > 0)
260 char* dot = strstr(aname, ".exe");
261 if (dot) *dot = 0;
263 else
265 g_free(aname);
266 aname = g_strdup("Mono");
269 else
270 aname = g_strdup(orig_name);
272 gboolean has_upper = FALSE;
273 for (int i=0; i<strlen(aname); i++)
275 if (aname[i] >= 'A' && aname[i] <= 'Z')
277 has_upper = TRUE;
278 break;
281 if (!has_upper)
283 for (int i=0; i<strlen(aname); i++)
285 if ((aname[i] >= 'a' && aname[i] <= 'z')
286 && (i == 0 || aname[i-1] == ' '))
287 aname[i] -= 32;
289 char* ui = strstr(aname, "Ui");
290 if (ui && (ui[2] == 0 || ui[2] == ' '))
291 ui[1] = 'I';
292 char* io = strstr(aname, "Io");
293 if (io && (io[2] == 0 || io[2] == ' '))
294 io[1] = 'O';
295 char* at = strstr(aname, "At");
296 if (at && (at[2] == 0 || at[2] == ' '))
297 at[1] = 'T';
299 return aname;
302 static void desktop_active_window_changed(WnckScreen* screen, WnckWindow *previous_window, MacMenu* mmb)
304 WnckWindow* awin = wnck_screen_get_active_window(screen);
305 GtkWidget* sck = NULL;
306 if (awin != NULL && wnck_window_get_window_type(awin) != WNCK_WINDOW_DESKTOP)
308 long inout[2] = {0, 0};
309 inout[0] = wnck_window_get_xid(awin);
310 if (inout[0])
312 g_hash_table_foreach(mmb->mbars_scks, find_mbar_by_mwin, inout);
313 if (inout[1])
314 sck = g_hash_table_lookup(mmb->mbars_scks, (gpointer) inout[1]);
316 if (sck == NULL)
318 sck = mmb->dummysck;
319 gtk_label_set_max_width_chars(mmb->label, MAX_LABEL_WIDTH_N_CHARS * 10);
321 else
323 gtk_label_set_max_width_chars(mmb->label, MAX_LABEL_WIDTH_N_CHARS);
325 gtk_label_set_text(mmb->label, get_application_name(awin, mmb));
327 else
329 sck = mmb->mainsck;
330 gtk_label_set_max_width_chars(mmb->label, MAX_LABEL_WIDTH_N_CHARS * 10);
331 gtk_label_set_text(mmb->label, MAIN_LABEL_TEXT);
334 gtk_notebook_set_current_page(
335 mmb->notebook,
336 gtk_notebook_page_num(mmb->notebook, sck)
340 static void desktop_window_opened(WnckScreen* screen, WnckWindow* window, MacMenu* mmb)
342 if (is_menubar(window))
343 add_menubar(mmb, window);
346 static void add_all(MacMenu* mmb)
348 GList* windows = wnck_screen_get_windows(mmb->screen);
349 GList* node = windows;
350 while (node != NULL) {
351 WnckWindow* wnckwin = (WnckWindow*) node->data;
352 if (is_menubar(wnckwin))
353 add_menubar(mmb, wnckwin);
354 node = node->next;
358 static void macmenu_free_data(AppletType *applet, MacMenu* mmb)
360 #ifdef FOR_GNOME
361 g_object_unref(mmb->gconf);
362 #endif
363 //finalize_mainsck(mmb);
364 g_hash_table_destroy(mmb->mbars_scks);
365 g_hash_table_destroy(mmb->title_subs);
366 g_slice_free(MacMenu, mmb);
369 static void macmenu_set_size(AppletType *applet, int size, MacMenu* mmb)
371 gtk_widget_set_size_request(GTK_WIDGET(applet), 0, size);
374 #ifdef FOR_XFCE
375 static void macmenu_load_from_rc(MacMenu* mmb)
377 char* file;
378 if (!(file = xfce_panel_plugin_lookup_rc_file(mmb->applet))) return;
379 XfceRc* rc = xfce_rc_simple_open(file, TRUE);
380 g_free(file);
381 if (!rc) return;
383 mmb->hide_label = xfce_rc_read_bool_entry(rc, "HideLabel", FALSE);
385 xfce_rc_close (rc);
388 static void macmenu_write_rc(MacMenu* mmb)
390 char* file;
391 if (!(file = xfce_panel_plugin_save_location(mmb->applet, TRUE))) return;
392 unlink(file);
393 XfceRc* rc = xfce_rc_simple_open(file, FALSE);
394 g_free(file);
395 if (!rc) return;
397 xfce_rc_write_bool_entry(rc, "HideLabel", mmb->hide_label);
399 xfce_rc_close (rc);
402 static void on_label_toggled(GtkCheckMenuItem *checkmenuitem, MacMenu* mmb)
404 mmb->hide_label = gtk_check_menu_item_get_active(checkmenuitem);
405 if (mmb->hide_label)
407 gtk_widget_hide(GTK_WIDGET(mmb->label));
408 gtk_widget_hide(GTK_WIDGET(mmb->label_space));
410 else
412 gtk_widget_show(GTK_WIDGET(mmb->label));
413 gtk_widget_show(GTK_WIDGET(mmb->label_space));
415 macmenu_write_rc(mmb);
418 #elif FOR_GNOME
419 static void macmenu_load_from_gconf(MacMenu* mmb)
421 gchar* prefkey = panel_applet_get_preferences_key(mmb->applet);
422 gchar* fullkey = g_strdup_printf("%s/%s", prefkey, "hide_label");
423 mmb->hide_label = gconf_client_get_bool(mmb->gconf, fullkey, NULL);
424 g_free(prefkey);
425 g_free(fullkey);
428 static void macmenu_write_gconf(MacMenu* mmb)
430 gchar* prefkey = panel_applet_get_preferences_key(mmb->applet);
431 gchar* fullkey = g_strdup_printf("%s/%s", prefkey, "hide_label");
432 gconf_client_set_bool(mmb->gconf, fullkey, mmb->hide_label, NULL);
433 g_free(prefkey);
434 g_free(fullkey);
437 static void on_label_toggled(BonoboUIComponent *uic, const gchar *path,
438 Bonobo_UIComponent_EventType type, const gchar *state,
439 MacMenu *mmb)
441 BonoboUIComponent* popup_component = panel_applet_get_popup_component(mmb->applet);
442 gchar* r = bonobo_ui_component_get_prop(
443 popup_component, "/commands/hide_label", "state", NULL);
444 mmb->hide_label = (strcmp(r, "1") == 0 ? TRUE: FALSE);
445 g_free(r);
446 if (mmb->hide_label)
448 gtk_widget_hide(GTK_WIDGET(mmb->label));
449 gtk_widget_hide(GTK_WIDGET(mmb->label_space));
451 else
453 gtk_widget_show(GTK_WIDGET(mmb->label));
454 gtk_widget_show(GTK_WIDGET(mmb->label_space));
456 macmenu_write_gconf(mmb);
459 static void on_button_pressed(GtkButton* button, MacMenu* mmb)
461 gboolean unused;
462 g_signal_emit_by_name(mmb->applet, "popup_menu", &unused);
463 return;
465 #endif // FOR_GNOME
467 static void macmenu_construct(AppletType* applet)
469 MacMenu *mmb = g_slice_new0(MacMenu);
470 mmb->applet = applet;
471 mmb->screen = wnck_screen_get(gdk_screen_get_number(
472 gtk_widget_get_screen(GTK_WIDGET(applet))
474 mmb->mbars_scks = g_hash_table_new(NULL, NULL);
475 mmb->title_subs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
476 mmb->ts_mtime = 0;
478 mmb->basebox = GTK_BOX(gtk_hbox_new(FALSE, 0));
479 gtk_container_set_border_width(GTK_CONTAINER(mmb->basebox), 0);
480 gtk_container_add(GTK_CONTAINER(applet), GTK_WIDGET(mmb->basebox));
482 mmb->label = GTK_LABEL(gtk_label_new(MAIN_LABEL_TEXT));
483 PangoAttrList *pattr = pango_attr_list_new();
484 PangoAttribute *pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
485 pa->start_index = 0; pa->end_index = 1024;
486 pango_attr_list_insert(pattr, pa);
487 gtk_label_set_attributes(mmb->label, pattr);
488 pango_attr_list_unref(pattr);
489 gtk_label_set_ellipsize(mmb->label, PANGO_ELLIPSIZE_END);
490 gtk_label_set_max_width_chars(mmb->label, MAX_LABEL_WIDTH_N_CHARS * 10);
491 gtk_label_set_single_line_mode(mmb->label, TRUE);
492 gtk_box_pack_start(mmb->basebox, GTK_WIDGET(mmb->label), FALSE, FALSE, 0);
494 mmb->label_space = gtk_event_box_new();
495 gtk_widget_set_size_request(mmb->label_space, 8, 1);
496 gtk_box_pack_start(mmb->basebox, mmb->label_space, FALSE, FALSE, 0);
498 mmb->notebook = GTK_NOTEBOOK(gtk_notebook_new());
499 gtk_notebook_set_show_tabs(mmb->notebook, FALSE);
500 gtk_notebook_set_show_border(mmb->notebook, FALSE);
501 gtk_box_pack_start(mmb->basebox, GTK_WIDGET(mmb->notebook), TRUE, TRUE, 0);
503 mmb->dummysck = gtk_hbox_new(FALSE, 0);
504 gtk_notebook_append_page(mmb->notebook, mmb->dummysck, NULL);
506 mmb->mainsck = gtk_hbox_new(FALSE, SHORTCUT_SPACING);
507 gtk_notebook_append_page(mmb->notebook, mmb->mainsck, NULL);
508 //initialize_mainsck(mmb);
510 mmb->button = GTK_BUTTON(gtk_button_new());
511 gtk_button_set_relief(mmb->button, GTK_RELIEF_NONE);
512 gtk_button_set_focus_on_click(GTK_BUTTON(mmb->button), FALSE);
513 gtk_box_pack_start(mmb->basebox, GTK_WIDGET(mmb->button),
514 FALSE, FALSE, 0);
516 g_signal_connect(mmb->screen, "active-window-changed",
517 G_CALLBACK(desktop_active_window_changed), mmb);
518 g_signal_connect(mmb->screen, "window-opened",
519 G_CALLBACK(desktop_window_opened), mmb);
521 #ifdef FOR_XFCE
522 g_signal_connect(applet, "free-data",
523 G_CALLBACK(macmenu_free_data), mmb);
524 g_signal_connect(applet, "size-changed",
525 G_CALLBACK(macmenu_set_size), mmb);
526 // setup panel applet
527 gtk_widget_show_all(GTK_WIDGET(mmb->basebox));
528 xfce_panel_plugin_add_action_widget (applet, GTK_WIDGET(mmb->button));
529 xfce_panel_plugin_set_expand(applet, TRUE);
530 macmenu_set_size(applet, xfce_panel_plugin_get_size(applet), mmb);
531 // load config, set popup
532 macmenu_load_from_rc(mmb);
533 GtkWidget* label_toggle = gtk_check_menu_item_new_with_label("Hide Label");
534 g_signal_connect(label_toggle, "toggled", G_CALLBACK(on_label_toggled), mmb);
535 gtk_widget_show(label_toggle);
536 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(label_toggle), mmb->hide_label);
537 gtk_check_menu_item_toggled(GTK_CHECK_MENU_ITEM(label_toggle));
538 xfce_panel_plugin_menu_insert_item(mmb->applet, GTK_MENU_ITEM(label_toggle));
540 #elif FOR_GNOME
541 g_signal_connect(applet, "destroy", G_CALLBACK(macmenu_free_data), mmb);
542 // setup panel applet
543 gtk_widget_show_all(GTK_WIDGET(applet));
544 g_signal_connect(GTK_WIDGET(mmb->button), "pressed",
545 G_CALLBACK(on_button_pressed), mmb);
546 panel_applet_set_flags (applet, PANEL_APPLET_EXPAND_MAJOR);
547 macmenu_set_size(applet, panel_applet_get_size(applet), mmb);
548 // load config, set popup
549 mmb->gconf = gconf_client_get_default();
550 macmenu_load_from_gconf(mmb);
551 macmenu_write_gconf(mmb);
552 static const char toggle_menu_xml [] =
553 "<popup name=\"button3\">\n"
554 " <menuitem name=\"hide_label\" "
555 " verb=\"hide_label\" "
556 " _label=\"_Hide Label\"\n"
557 " type=\"toggle\"/>\n"
558 "</popup>\n";
559 static const BonoboUIVerb toggle_menu_verbs[] =
561 BONOBO_UI_VERB_END
563 panel_applet_setup_menu(mmb->applet, toggle_menu_xml, toggle_menu_verbs, mmb);
564 BonoboUIComponent* popup_component = panel_applet_get_popup_component(mmb->applet);
565 bonobo_ui_component_set_prop(popup_component, "/commands/hide_label",
566 "state", mmb->hide_label ? "1": "0", NULL);
567 bonobo_ui_component_add_listener(panel_applet_get_popup_component(mmb->applet),
568 "hide_label",
569 (BonoboUIListenerFn) on_label_toggled,
570 mmb);
571 on_label_toggled(NULL, NULL, 0, NULL, mmb);
572 #endif // FOR_XFCE/FOR_GNOME
574 add_all(mmb);
575 XSetErrorHandler(handle_x_error);
578 #ifdef FOR_XFCE
579 XFCE_PANEL_PLUGIN_REGISTER_EXTERNAL (macmenu_construct);
581 #elif FOR_GNOME
582 static gboolean macmenu_applet_factory (PanelApplet *applet,
583 const gchar *iid,
584 gpointer data)
586 if (!strcmp (iid, "OAFIID:GNOME_MacMenuApplet"))
588 macmenu_construct (applet);
589 return TRUE;
591 else
592 return FALSE;
595 PANEL_APPLET_BONOBO_FACTORY ("OAFIID:GNOME_MacMenuApplet_Factory",
596 PANEL_TYPE_APPLET,
597 "macmenu",
598 "0",
599 macmenu_applet_factory,
600 NULL)
601 #endif // FOR_XFCE/FOR_GNOME