/* * panel-menu-bar.c: panel Applications/Places/Desktop menu bar * * Copyright (C) 2003 Sun Microsystems, Inc. * Copyright (C) 2004 Vincent Untz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301, USA. * * Authors: * Mark McLoughlin * Vincent Untz */ #include #include "panel-menu-bar.h" #include #include #include #include #include #include "panel-util.h" #include "panel-background.h" #include "panel-action-button.h" #include "applet.h" #include "menu.h" #include "panel-menu-items.h" #include "panel-globals.h" #include "panel-profile.h" #include "panel-lockdown.h" #include "panel-stock-icons.h" #include "panel-typebuiltins.h" #include "panel-icon-names.h" #include "panel-schemas.h" G_DEFINE_TYPE (PanelMenuBar, panel_menu_bar, GTK_TYPE_MENU_BAR) #define PANEL_MENU_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PANEL_TYPE_MENU_BAR, PanelMenuBarPrivate)) struct _PanelMenuBarPrivate { AppletInfo* info; PanelWidget* panel; GtkWidget* applications_menu; GtkWidget* applications_item; GtkWidget* places_item; GtkWidget* desktop_item; GSettings* settings; PanelOrientation orientation; }; enum { PROP_0, PROP_ORIENTATION, }; static void panel_menu_bar_update_text_gravity(PanelMenuBar* menubar); static gboolean panel_menu_bar_reinit_tooltip(GtkWidget* widget, PanelMenuBar* menubar) { g_object_set(menubar->priv->applications_item, "has-tooltip", TRUE, NULL); g_object_set(menubar->priv->places_item, "has-tooltip", TRUE, NULL); g_object_set(menubar->priv->desktop_item, "has-tooltip", TRUE, NULL); return FALSE; } static gboolean panel_menu_bar_hide_tooltip_and_focus(GtkWidget* widget, PanelMenuBar* menubar) { /* remove focus that would be drawn on the currently focused child of * the toplevel. See bug#308632. */ gtk_window_set_focus(GTK_WINDOW(menubar->priv->panel->toplevel), NULL); g_object_set(widget, "has-tooltip", FALSE, NULL); return FALSE; } static void panel_menu_bar_setup_tooltip(PanelMenuBar* menubar) { panel_util_set_tooltip_text(menubar->priv->applications_item, _("Browse and run installed applications")); panel_util_set_tooltip_text(menubar->priv->places_item, _("Access documents, folders and network places")); panel_util_set_tooltip_text(menubar->priv->desktop_item, _("Change desktop appearance and behavior, get help, or log out")); //FIXME: this doesn't handle the right-click case. Sigh. /* Hide tooltip if a menu is activated */ g_signal_connect(menubar->priv->applications_item, "activate", G_CALLBACK (panel_menu_bar_hide_tooltip_and_focus), menubar); g_signal_connect(menubar->priv->places_item, "activate", G_CALLBACK (panel_menu_bar_hide_tooltip_and_focus), menubar); g_signal_connect(menubar->priv->desktop_item, "activate", G_CALLBACK (panel_menu_bar_hide_tooltip_and_focus), menubar); /* Reset tooltip when the menu bar is not used */ g_signal_connect(GTK_MENU_SHELL (menubar), "deactivate", G_CALLBACK (panel_menu_bar_reinit_tooltip), menubar); } static void panel_menu_bar_update_visibility (GSettings* settings, gchar* key, PanelMenuBar* menubar) { GtkWidget* image; gchar *str; if (!GTK_IS_WIDGET (menubar)) return; gtk_widget_set_visible (GTK_WIDGET (menubar->priv->applications_item), g_settings_get_boolean (settings, PANEL_MENU_BAR_SHOW_APPLICATIONS_KEY)); gtk_widget_set_visible (GTK_WIDGET (menubar->priv->places_item), g_settings_get_boolean (settings, PANEL_MENU_BAR_SHOW_PLACES_KEY)); gtk_widget_set_visible (GTK_WIDGET (menubar->priv->desktop_item), g_settings_get_boolean (settings, PANEL_MENU_BAR_SHOW_DESKTOP_KEY)); if (g_settings_get_boolean (settings, PANEL_MENU_BAR_SHOW_ICON_KEY)) { str = g_settings_get_string (settings, PANEL_MENU_BAR_ICON_NAME_KEY); if (str != NULL && str[0] != 0) image = gtk_image_new_from_icon_name(str, panel_menu_bar_icon_get_size()); else image = gtk_image_new_from_icon_name(PANEL_ICON_MAIN_MENU, panel_menu_bar_icon_get_size()); g_free (str); } else image = NULL; gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menubar->priv->applications_item), image); } static void panel_menu_bar_init(PanelMenuBar* menubar) { #if GTK_CHECK_VERSION (3, 0, 0) GtkCssProvider *provider; #endif menubar->priv = PANEL_MENU_BAR_GET_PRIVATE(menubar); #if GTK_CHECK_VERSION (3, 0, 0) provider = gtk_css_provider_new (); gtk_css_provider_load_from_data (provider, "PanelMenuBar {\n" " border-width: 0px;\n" "}", -1, NULL); gtk_style_context_add_provider (gtk_widget_get_style_context (GTK_WIDGET (menubar)), GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); g_object_unref (provider); #endif menubar->priv->info = NULL; menubar->priv->settings = g_settings_new (PANEL_MENU_BAR_SCHEMA); menubar->priv->applications_menu = create_applications_menu("mate-applications.menu", NULL, TRUE); menubar->priv->applications_item = panel_image_menu_item_new(); gtk_menu_item_set_label(GTK_MENU_ITEM(menubar->priv->applications_item), _("Applications")); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menubar->priv->applications_item), menubar->priv->applications_menu); gtk_menu_shell_append(GTK_MENU_SHELL(menubar), menubar->priv->applications_item); menubar->priv->places_item = panel_place_menu_item_new(FALSE); gtk_menu_shell_append(GTK_MENU_SHELL(menubar), menubar->priv->places_item); menubar->priv->desktop_item = panel_desktop_menu_item_new(FALSE, TRUE); gtk_menu_shell_append(GTK_MENU_SHELL(menubar), menubar->priv->desktop_item); panel_menu_bar_setup_tooltip(menubar); panel_menu_bar_update_visibility(menubar->priv->settings, NULL, menubar); g_signal_connect(menubar->priv->settings, "changed", G_CALLBACK (panel_menu_bar_update_visibility), menubar); panel_menu_bar_update_text_gravity(menubar); g_signal_connect(menubar, "screen-changed", G_CALLBACK(panel_menu_bar_update_text_gravity), NULL); } static void panel_menu_bar_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec) { PanelMenuBar* menubar; g_return_if_fail(PANEL_IS_MENU_BAR(object)); menubar = PANEL_MENU_BAR (object); switch (prop_id) { case PROP_ORIENTATION: g_value_set_enum(value, menubar->priv->orientation); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void panel_menu_bar_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec) { PanelMenuBar* menubar; g_return_if_fail (PANEL_IS_MENU_BAR (object)); menubar = PANEL_MENU_BAR(object); switch (prop_id) { case PROP_ORIENTATION: panel_menu_bar_set_orientation(menubar, g_value_get_enum(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void panel_menu_bar_parent_set(GtkWidget* widget, GtkWidget* previous_parent) { PanelMenuBar* menubar = PANEL_MENU_BAR(widget); GtkWidget* parent; parent = gtk_widget_get_parent(widget); g_assert(!parent || PANEL_IS_WIDGET(parent)); menubar->priv->panel = (PanelWidget*) parent; if (menubar->priv->applications_menu) { mate_panel_applet_menu_set_recurse(GTK_MENU(menubar->priv->applications_menu), "menu_panel", menubar->priv->panel); } if (menubar->priv->places_item) { panel_place_menu_item_set_panel(menubar->priv->places_item, menubar->priv->panel); } if (menubar->priv->desktop_item) { panel_desktop_menu_item_set_panel(menubar->priv->desktop_item, menubar->priv->panel); } } static void panel_menu_bar_size_allocate(GtkWidget* widget, GtkAllocation* allocation) { GtkAllocation old_allocation; GtkAllocation widget_allocation; PanelBackground* background; gtk_widget_get_allocation(widget, &widget_allocation); old_allocation.x = widget_allocation.x; old_allocation.y = widget_allocation.y; old_allocation.width = widget_allocation.width; old_allocation.height = widget_allocation.height; GTK_WIDGET_CLASS(panel_menu_bar_parent_class)->size_allocate (widget, allocation); if (old_allocation.x == allocation->x && old_allocation.y == allocation->y && old_allocation.width == allocation->width && old_allocation.height == allocation->height) { return; } background = &PANEL_MENU_BAR(widget)->priv->panel->background; if (background->type == PANEL_BACK_NONE || (background->type == PANEL_BACK_COLOR && !background->has_alpha)) { return; } panel_menu_bar_change_background(PANEL_MENU_BAR(widget)); } static void panel_menu_bar_finalize (GObject* object) { PanelMenuBar* menubar; menubar = PANEL_MENU_BAR (object); if (menubar->priv->settings != NULL) { g_object_unref (menubar->priv->settings); menubar->priv->settings = NULL; } } static void panel_menu_bar_class_init(PanelMenuBarClass* klass) { GObjectClass* gobject_class = (GObjectClass*) klass; GtkWidgetClass* widget_class = (GtkWidgetClass*) klass; gobject_class->get_property = panel_menu_bar_get_property; gobject_class->set_property = panel_menu_bar_set_property; gobject_class->finalize = panel_menu_bar_finalize; widget_class->parent_set = panel_menu_bar_parent_set; widget_class->size_allocate = panel_menu_bar_size_allocate; g_type_class_add_private(klass, sizeof(PanelMenuBarPrivate)); g_object_class_install_property(gobject_class, PROP_ORIENTATION, g_param_spec_enum("orientation", "Orientation", "The PanelMenuBar orientation", PANEL_TYPE_ORIENTATION, PANEL_ORIENTATION_TOP, G_PARAM_READWRITE)); #if !GTK_CHECK_VERSION (3, 0, 0) gtk_rc_parse_string ( "style \"panel-menubar-style\"\n" "{\n" " GtkMenuBar::shadow-type = none\n" " GtkMenuBar::internal-padding = 0\n" "}\n" "class \"PanelMenuBar\" style \"panel-menubar-style\""); #endif } #if GTK_CHECK_VERSION (3, 0, 0) static gboolean panel_menu_bar_on_draw (GtkWidget* widget, cairo_t* cr, gpointer data) { PanelMenuBar* menubar = data; if (gtk_widget_has_focus(GTK_WIDGET(menubar))) { GtkStyleContext *context; context = gtk_widget_get_style_context (widget); gtk_style_context_save (context); gtk_style_context_set_state (context, gtk_widget_get_state_flags (widget)); cairo_save (cr); gtk_render_focus (context, cr, 0, 0, gtk_widget_get_allocated_width (widget), gtk_widget_get_allocated_height (widget)); cairo_restore (cr); gtk_style_context_restore (context); } return FALSE; } #else static gboolean panel_menu_bar_on_expose (GtkWidget* widget, GdkEventExpose* event, gpointer data) { PanelMenuBar* menubar = data; if (gtk_widget_has_focus(GTK_WIDGET(menubar))) { gtk_paint_focus(gtk_widget_get_style(widget), gtk_widget_get_window(widget), gtk_widget_get_state(GTK_WIDGET(menubar)), NULL, widget, "menubar-applet", 0, 0, -1, -1); } return FALSE; } #endif static void panel_menu_bar_load(PanelWidget* panel, gboolean locked, int position, gboolean exactpos, const char* id) { PanelMenuBar* menubar; g_return_if_fail (panel != NULL); menubar = g_object_new(PANEL_TYPE_MENU_BAR, NULL); menubar->priv->info = mate_panel_applet_register(GTK_WIDGET(menubar), NULL, NULL, panel, locked, position, exactpos, PANEL_OBJECT_MENU_BAR, id); if (!menubar->priv->info) { gtk_widget_destroy(GTK_WIDGET(menubar)); return; } mate_panel_applet_add_callback(menubar->priv->info, "help", GTK_STOCK_HELP, _("_Help"), NULL); /* Menu editors */ if (!panel_lockdown_get_locked_down () && (panel_is_program_in_path("mozo") || panel_is_program_in_path("matemenu-simple-editor"))) { mate_panel_applet_add_callback (menubar->priv->info, "edit", NULL, _("_Edit Menus"), NULL); } g_signal_connect_after(menubar, "focus-in-event", G_CALLBACK(gtk_widget_queue_draw), menubar); g_signal_connect_after(menubar, "focus-out-event", G_CALLBACK(gtk_widget_queue_draw), menubar); #if GTK_CHECK_VERSION (3, 0, 0) g_signal_connect_after(menubar, "draw", G_CALLBACK(panel_menu_bar_on_draw), menubar); #else g_signal_connect_after(menubar, "expose-event", G_CALLBACK(panel_menu_bar_on_expose), menubar); #endif gtk_widget_set_can_focus(GTK_WIDGET(menubar), TRUE); panel_widget_set_applet_expandable(panel, GTK_WIDGET(menubar), FALSE, TRUE); panel_menu_bar_update_visibility(menubar->priv->settings, NULL, menubar); } void panel_menu_bar_load_from_gsettings (PanelWidget* panel, gboolean locked, int position, gboolean exactpos, const char* id) { panel_menu_bar_load(panel, locked, position, exactpos, id); } void panel_menu_bar_create(PanelToplevel* toplevel, int position) { char* id; id = panel_profile_prepare_object(PANEL_OBJECT_MENU_BAR, toplevel, position, FALSE); panel_profile_add_to_list(PANEL_GSETTINGS_OBJECTS, id); g_free(id); } void panel_menu_bar_invoke_menu(PanelMenuBar* menubar, const char* callback_name) { GdkScreen* screen; g_return_if_fail(PANEL_IS_MENU_BAR(menubar)); g_return_if_fail(callback_name != NULL); screen = gtk_widget_get_screen(GTK_WIDGET(menubar)); if (!strcmp(callback_name, "help")) { panel_show_help(screen, "mate-user-guide", "menubar", NULL); } else if (!strcmp(callback_name, "edit")) { GError* error = NULL; panel_launch_desktop_file_with_fallback("mozo.desktop", "mozo", screen, &error); if (error) { g_error_free(error); panel_launch_desktop_file_with_fallback("matemenu-simple-editor.desktop", "matemenu-simple-editor", screen, NULL); } } } void panel_menu_bar_popup_menu(PanelMenuBar* menubar, guint32 activate_time) { GtkMenu* menu; GtkMenuShell* menu_shell; g_return_if_fail(PANEL_IS_MENU_BAR(menubar)); menu = GTK_MENU(menubar->priv->applications_menu); /* * We need to call _gtk_menu_shell_activate() here as is done in * window_key_press_handler in gtkmenubar.c which pops up menu * when F10 is pressed. * * As that function is private its code is replicated here. */ menu_shell = GTK_MENU_SHELL(menubar); #if !GTK_CHECK_VERSION (3, 0, 0) if (!menu_shell->active) { gtk_grab_add(GTK_WIDGET(menu_shell)); menu_shell->have_grab = TRUE; menu_shell->active = TRUE; } #endif gtk_menu_shell_select_item(menu_shell, gtk_menu_get_attach_widget(menu)); } void panel_menu_bar_change_background(PanelMenuBar* menubar) { #if GTK_CHECK_VERSION (3, 0, 0) panel_background_apply_css(&menubar->priv->panel->background, GTK_WIDGET(menubar)); #else panel_background_change_background_on_widget(&menubar->priv->panel->background, GTK_WIDGET(menubar)); #endif } static void set_item_text_gravity(GtkWidget* item) { GtkWidget* label; PangoLayout* layout; PangoContext* context; label = gtk_bin_get_child(GTK_BIN(item)); layout = gtk_label_get_layout(GTK_LABEL(label)); context = pango_layout_get_context(layout); pango_context_set_base_gravity(context, PANGO_GRAVITY_AUTO); } static void panel_menu_bar_update_text_gravity(PanelMenuBar* menubar) { set_item_text_gravity(menubar->priv->applications_item); set_item_text_gravity(menubar->priv->places_item); set_item_text_gravity(menubar->priv->desktop_item); } static void set_item_text_angle_and_alignment(GtkWidget* item, double text_angle, float xalign, float yalign) { GtkWidget *label; label = gtk_bin_get_child (GTK_BIN (item)); gtk_label_set_angle (GTK_LABEL (label), text_angle); #if GTK_CHECK_VERSION (3, 16, 0) gtk_label_set_xalign (GTK_LABEL (label), xalign); gtk_label_set_yalign (GTK_LABEL (label), yalign); #else gtk_misc_set_alignment (GTK_MISC (label), xalign, yalign); #endif } static void panel_menu_bar_update_orientation(PanelMenuBar* menubar) { GtkPackDirection pack_direction; double text_angle; float text_xalign; float text_yalign; pack_direction = GTK_PACK_DIRECTION_LTR; text_angle = 0.0; text_xalign = 0.0; text_yalign = 0.5; switch (menubar->priv->orientation) { case PANEL_ORIENTATION_TOP: case PANEL_ORIENTATION_BOTTOM: break; case PANEL_ORIENTATION_LEFT: pack_direction = GTK_PACK_DIRECTION_BTT; text_angle = 90.0; text_xalign = 0.5; text_yalign = 0.0; break; case PANEL_ORIENTATION_RIGHT: pack_direction = GTK_PACK_DIRECTION_TTB; text_angle = 270.0; text_xalign = 0.5; text_yalign = 0.0; break; default: g_assert_not_reached(); break; } gtk_menu_bar_set_pack_direction(GTK_MENU_BAR(menubar), pack_direction); gtk_menu_bar_set_child_pack_direction(GTK_MENU_BAR(menubar), pack_direction); set_item_text_angle_and_alignment(menubar->priv->applications_item, text_angle, text_xalign, text_yalign); set_item_text_angle_and_alignment(menubar->priv->places_item, text_angle, text_xalign, text_yalign); set_item_text_angle_and_alignment(menubar->priv->desktop_item, text_angle, text_xalign, text_yalign); } void panel_menu_bar_set_orientation(PanelMenuBar* menubar, PanelOrientation orientation) { g_return_if_fail(PANEL_IS_MENU_BAR(menubar)); if (menubar->priv->orientation == orientation) { return; } menubar->priv->orientation = orientation; panel_menu_bar_update_orientation(menubar); g_object_notify(G_OBJECT(menubar), "orientation"); } PanelOrientation panel_menu_bar_get_orientation(PanelMenuBar* menubar) { g_return_val_if_fail(PANEL_IS_MENU_BAR(menubar), 0); return menubar->priv->orientation; }