1 #define WNCK_I_KNOW_THIS_IS_UNSTABLE
6 #include <glib/gstdio.h>
8 #include <libwnck/libwnck.h>
14 #include "macmenu-tslist.h" // const char* TS_LIST_DEFAULT
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";
25 #include <gconf/gconf-client.h>
26 #include <panel-applet.h>
27 typedef PanelApplet AppletType
;
28 const gchar
* MAIN_LABEL_TEXT
= "GNOME";
30 // no FOR_GNOME or FOR_XFCE, error
31 #error "Please specify whether to build GNOME panel applet or Xfce panel plugin"
34 const char* TS_LIST_FILENAME
= ".macmenu-tslist";
35 const int MAX_LABEL_WIDTH_N_CHARS
= 15;
36 const int SHORTCUT_SPACING
= 8;
42 GHashTable
* mbars_scks
;
46 GtkNotebook
* notebook
;
48 GtkWidget
* label_space
;
52 // Title subtitle hash
54 GHashTable
* title_subs
;
61 static int handle_x_error (Display
* display
,
64 printf("Caught stupid X error, ignore it...");
68 // don't use wnck's, it would fail if main window isn't shown yet.
69 static Window
get_transient_for(Window window
)
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
90 static gboolean
is_menubar(WnckWindow
* window
)
94 if (wnck_window_get_window_type(window
) != WNCK_WINDOW_DOCK
)
96 else if (strcmp(wnck_window_get_name(window
), "GTK MENUBAR") == 0)
106 if (XGetWindowProperty(gdk_display
,
107 wnck_window_get_xid(window
),
108 XInternAtom(gdk_display
, "_NET_WM_WINDOW_TYPE", FALSE
),
110 False
, XA_ATOM
, &type
, &format
, &nitems
,
111 &bytes_after
, (void*) &data
) == Success
114 if (data
[0] == XInternAtom(gdk_display
, "_KDE_NET_WM_WINDOW_TYPE_TOPMENU", FALSE
))
122 static void find_mbar_by_mwin(gpointer key
,
126 Window mbar
= (Window
) key
;
127 Window mwin
= get_transient_for(mbar
);
128 long* inout
= (long*) user_data
;
129 if (mwin
&& mwin
== inout
[0])
133 static void find_mbar_by_sck(gpointer key
,
137 Window mbar
= (Window
) key
;
138 GtkWidget
* sck
= (GtkWidget
*) value
;
139 long* inout
= (long*) user_data
;
140 if (sck
&& sck
== (GtkWidget
*) inout
[0])
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
);
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
);
168 if (g_stat(ts_list_path
, &sbuf
))
170 FILE* nf
= g_fopen(ts_list_path
, "w");
172 fprintf(stderr
, "Unable to create %s\n", ts_list_path
);
175 fwrite(TS_LIST_DEFAULT
, 1, strlen(TS_LIST_DEFAULT
), nf
);
176 sbuf
.st_mtime
= time(NULL
);
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
);
186 fprintf(stderr
, "Unable to open %s for reading list after successful stat()\n", ts_list_path
);
189 g_hash_table_remove_all(mmb
->title_subs
);
190 gsize line_len
, line_term
;
192 while (g_io_channel_read_line(ioc
, &line_str
, &line_len
, &line_term
,
193 NULL
) == G_IO_STATUS_NORMAL
)
196 if (line_str
!= NULL
&& (sep_pos
= rindex(line_str
, '=')) != NULL
)
198 gchar
* key
= line_str
;
199 gchar
* value
= sep_pos
+ 1;
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);
226 if (! strcmp(orig_name
, "vmware"))
227 return g_strdup("VMware");
229 else if (! strcmp(orig_name
, "Web Browser"))
230 return g_strdup("Epiphany");
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
] = ' ';
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");
266 aname
= g_strdup("Mono");
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')
283 for (int i
=0; i
<strlen(aname
); i
++)
285 if ((aname
[i
] >= 'a' && aname
[i
] <= 'z')
286 && (i
== 0 || aname
[i
-1] == ' '))
289 char* ui
= strstr(aname
, "Ui");
290 if (ui
&& (ui
[2] == 0 || ui
[2] == ' '))
292 char* io
= strstr(aname
, "Io");
293 if (io
&& (io
[2] == 0 || io
[2] == ' '))
295 char* at
= strstr(aname
, "At");
296 if (at
&& (at
[2] == 0 || at
[2] == ' '))
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
);
312 g_hash_table_foreach(mmb
->mbars_scks
, find_mbar_by_mwin
, inout
);
314 sck
= g_hash_table_lookup(mmb
->mbars_scks
, (gpointer
) inout
[1]);
319 gtk_label_set_max_width_chars(mmb
->label
, MAX_LABEL_WIDTH_N_CHARS
* 10);
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
));
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(
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
);
358 static void macmenu_free_data(AppletType
*applet
, MacMenu
* mmb
)
361 g_object_unref(mmb
->gconf
);
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
);
375 static void macmenu_load_from_rc(MacMenu
* mmb
)
378 if (!(file
= xfce_panel_plugin_lookup_rc_file(mmb
->applet
))) return;
379 XfceRc
* rc
= xfce_rc_simple_open(file
, TRUE
);
383 mmb
->hide_label
= xfce_rc_read_bool_entry(rc
, "HideLabel", FALSE
);
388 static void macmenu_write_rc(MacMenu
* mmb
)
391 if (!(file
= xfce_panel_plugin_save_location(mmb
->applet
, TRUE
))) return;
393 XfceRc
* rc
= xfce_rc_simple_open(file
, FALSE
);
397 xfce_rc_write_bool_entry(rc
, "HideLabel", mmb
->hide_label
);
402 static void on_label_toggled(GtkCheckMenuItem
*checkmenuitem
, MacMenu
* mmb
)
404 mmb
->hide_label
= gtk_check_menu_item_get_active(checkmenuitem
);
407 gtk_widget_hide(GTK_WIDGET(mmb
->label
));
408 gtk_widget_hide(GTK_WIDGET(mmb
->label_space
));
412 gtk_widget_show(GTK_WIDGET(mmb
->label
));
413 gtk_widget_show(GTK_WIDGET(mmb
->label_space
));
415 macmenu_write_rc(mmb
);
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
);
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
);
437 static void on_label_toggled(BonoboUIComponent
*uic
, const gchar
*path
,
438 Bonobo_UIComponent_EventType type
, const gchar
*state
,
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
);
448 gtk_widget_hide(GTK_WIDGET(mmb
->label
));
449 gtk_widget_hide(GTK_WIDGET(mmb
->label_space
));
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
)
462 g_signal_emit_by_name(mmb
->applet
, "popup_menu", &unused
);
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
);
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
),
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
);
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
));
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"
559 static const BonoboUIVerb toggle_menu_verbs
[] =
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
),
569 (BonoboUIListenerFn
) on_label_toggled
,
571 on_label_toggled(NULL
, NULL
, 0, NULL
, mmb
);
572 #endif // FOR_XFCE/FOR_GNOME
575 XSetErrorHandler(handle_x_error
);
579 XFCE_PANEL_PLUGIN_REGISTER_EXTERNAL (macmenu_construct
);
582 static gboolean
macmenu_applet_factory (PanelApplet
*applet
,
586 if (!strcmp (iid
, "OAFIID:GNOME_MacMenuApplet"))
588 macmenu_construct (applet
);
595 PANEL_APPLET_BONOBO_FACTORY ("OAFIID:GNOME_MacMenuApplet_Factory",
599 macmenu_applet_factory
,
601 #endif // FOR_XFCE/FOR_GNOME