/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ /* e-shell-folder-title-bar.c * * Copyright (C) 2000, 2001 Ximian, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Author: Ettore Perazzoli */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include "e-shell-constants.h" #include "e-shell-marshal.h" #include "e-shell-folder-title-bar.h" struct _EShellFolderTitleBarPrivate { GdkPixbuf *icon; /* We have an icon, a label and a button that contains an icon and a label. When the button is enabled, the stand-alone label icon get hidden; when the button is disabled, the button gets hidden and the label and the icon get shown. This is pretty ugly but it easier to manage the GTK layout this way. */ /* The stand-alone icon/label combo. */ GtkWidget *title_icon; GtkWidget *title_label; /* The button. */ GtkWidget *title_button; GtkWidget *title_button_icon; GtkWidget *title_button_label; GtkWidget *title_button_arrow; /* Holds extra information that is to be shown on the bar. */ GtkWidget *folder_bar_label; /* Navigation buttons. */ GtkWidget *back_button; GtkWidget *forward_button; gboolean title_clickable; }; enum { TITLE_TOGGLED, BACK_CLICKED, FORWARD_CLICKED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE (EShellFolderTitleBar, e_shell_folder_title_bar, GTK_TYPE_HBOX) /* Utility functions for managing icons and icon widgets. */ static GdkPixbuf * new_empty_pixbuf (void) { GdkPixbuf *empty_pixbuf; unsigned char *pixels; empty_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 1, 1); pixels = gdk_pixbuf_get_pixels (empty_pixbuf); memset (pixels, 0, 4); return empty_pixbuf; } static GtkWidget * new_empty_image_widget (void) { GtkWidget *image_widget; GdkPixmap *pixmap; GdkBitmap *mask; GdkPixbuf *empty_pixbuf; empty_pixbuf = new_empty_pixbuf (); gdk_pixbuf_render_pixmap_and_mask (empty_pixbuf, &pixmap, &mask, 127); image_widget = gtk_image_new_from_pixmap (pixmap, mask); g_object_unref (empty_pixbuf); return image_widget; } /* Utility functions. */ static gint get_max_label_width (GtkWidget *label) { PangoLayout *layout; gint width; layout = gtk_label_get_layout (GTK_LABEL (label)); pango_layout_get_pixel_size (layout, &width, NULL); width += 2 * GTK_MISC (label)->xpad; return width; } static void size_allocate_title_button (EShellFolderTitleBar *title_bar, GtkAllocation *allocation, int offset, int *available_width_inout) { EShellFolderTitleBarPrivate *priv; GtkAllocation child_allocation; GtkRequisition child_requisition; int border_width; priv = title_bar->priv; /* Keep a little distance from the navigation arrows. */ allocation->x += 2; border_width = GTK_CONTAINER (title_bar)->border_width; gtk_widget_get_child_requisition (priv->title_button, &child_requisition); child_allocation.x = allocation->x + border_width + offset; child_allocation.y = allocation->y + border_width; child_allocation.height = allocation->height - 2 * border_width; child_allocation.width = child_requisition.width; child_allocation.width += get_max_label_width (priv->title_button_label); child_allocation.width = MIN (child_allocation.width, *available_width_inout); gtk_widget_size_allocate (priv->title_button, & child_allocation); *available_width_inout -= child_allocation.width; } static int size_allocate_navigation_buttons_and_title_icon (EShellFolderTitleBar *title_bar, GtkAllocation *allocation) { EShellFolderTitleBarPrivate *priv; GtkRequisition child_requisition; GtkAllocation child_allocation; int border_width; priv = title_bar->priv; border_width = GTK_CONTAINER (title_bar)->border_width; child_allocation.x = allocation->x + border_width; child_allocation.y = allocation->y + border_width; child_allocation.height = allocation->height - 2 * border_width; gtk_widget_size_request (priv->back_button, &child_requisition); child_allocation.width = child_requisition.width; gtk_widget_size_allocate (priv->back_button, &child_allocation); child_allocation.x += child_allocation.width; gtk_widget_size_request (priv->forward_button, &child_requisition); child_allocation.width = child_requisition.width; gtk_widget_size_allocate (priv->forward_button, &child_allocation); if (! priv->title_clickable) { /* Keep a little distance from the navigation arrows. */ child_allocation.x += child_allocation.width + 5; gtk_widget_size_request (priv->title_icon, &child_requisition); child_allocation.width = child_requisition.width; gtk_widget_size_allocate (priv->title_icon, &child_allocation); } return child_allocation.x + child_allocation.width; } static void size_allocate_label (EShellFolderTitleBar *title_bar, GtkAllocation *allocation, int offset, int *available_width_inout) { EShellFolderTitleBarPrivate *priv; GtkAllocation child_allocation; int border_width; priv = title_bar->priv; border_width = GTK_CONTAINER (title_bar)->border_width; child_allocation.x = allocation->x + border_width + offset; child_allocation.y = allocation->y + border_width; child_allocation.height = allocation->height - 2 * border_width; child_allocation.width = MIN (get_max_label_width (priv->title_label), *available_width_inout); gtk_widget_size_allocate (priv->title_label, & child_allocation); *available_width_inout -= child_allocation.width; } /* The back/forward navigation buttons. */ static void back_button_clicked_callback (GtkButton *button, void *data) { EShellFolderTitleBar *folder_title_bar; folder_title_bar = E_SHELL_FOLDER_TITLE_BAR (data); g_signal_emit (folder_title_bar, signals[BACK_CLICKED], 0); } static void forward_button_clicked_callback (GtkButton *button, void *data) { EShellFolderTitleBar *folder_title_bar; folder_title_bar = E_SHELL_FOLDER_TITLE_BAR (data); g_signal_emit (folder_title_bar, signals[FORWARD_CLICKED], 0); } static void add_navigation_buttons (EShellFolderTitleBar *folder_title_bar) { EShellFolderTitleBarPrivate *priv; GtkWidget *back_image; GtkWidget *forward_image; priv = folder_title_bar->priv; priv->back_button = gtk_button_new (); gtk_button_set_relief (GTK_BUTTON (priv->back_button), GTK_RELIEF_NONE); GTK_WIDGET_UNSET_FLAGS (priv->back_button, GTK_CAN_FOCUS); back_image = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE); gtk_container_add (GTK_CONTAINER (priv->back_button), back_image); g_signal_connect (priv->back_button, "clicked", G_CALLBACK (back_button_clicked_callback), folder_title_bar); priv->forward_button = gtk_button_new (); gtk_button_set_relief (GTK_BUTTON (priv->forward_button), GTK_RELIEF_NONE); GTK_WIDGET_UNSET_FLAGS (priv->forward_button, GTK_CAN_FOCUS); forward_image = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE); gtk_container_add (GTK_CONTAINER (priv->forward_button), forward_image); g_signal_connect (priv->forward_button, "clicked", G_CALLBACK (forward_button_clicked_callback), folder_title_bar); gtk_box_pack_start (GTK_BOX (folder_title_bar), priv->back_button, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (folder_title_bar), priv->forward_button, FALSE, FALSE, 0); gtk_widget_show_all (priv->back_button); gtk_widget_show_all (priv->forward_button); } /* Popup button callback. */ static void title_button_toggled_cb (GtkToggleButton *title_button, void *data) { EShellFolderTitleBar *folder_title_bar; folder_title_bar = E_SHELL_FOLDER_TITLE_BAR (data); g_signal_emit (folder_title_bar, signals[TITLE_TOGGLED], 0, gtk_toggle_button_get_active (title_button)); } /* GObject methods. */ static void impl_dispose (GObject *object) { EShellFolderTitleBar *folder_title_bar; EShellFolderTitleBarPrivate *priv; folder_title_bar = E_SHELL_FOLDER_TITLE_BAR (object); priv = folder_title_bar->priv; if (priv->icon != NULL) { g_object_unref (priv->icon); priv->icon = NULL; } (* G_OBJECT_CLASS (e_shell_folder_title_bar_parent_class)->dispose) (object); } static void impl_finalize (GObject *object) { EShellFolderTitleBar *folder_title_bar; EShellFolderTitleBarPrivate *priv; folder_title_bar = E_SHELL_FOLDER_TITLE_BAR (object); priv = folder_title_bar->priv; g_free (priv); (* G_OBJECT_CLASS (e_shell_folder_title_bar_parent_class)->finalize) (object); } /* GTkWidget methods. */ static void impl_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { EShellFolderTitleBar *title_bar; EShellFolderTitleBarPrivate *priv; GtkAllocation label_allocation; int border_width; int available_width; int width_before_icon; int offset; title_bar = E_SHELL_FOLDER_TITLE_BAR (widget); priv = title_bar->priv; border_width = GTK_CONTAINER (widget)->border_width; available_width = allocation->width - 2 * border_width; offset = size_allocate_navigation_buttons_and_title_icon (title_bar, allocation); available_width -= offset; width_before_icon = available_width; if (priv->title_clickable) size_allocate_title_button (title_bar, allocation, offset, & available_width); else size_allocate_label (title_bar, allocation, offset, & available_width); label_allocation.x = allocation->x + width_before_icon - available_width - border_width + offset; label_allocation.y = allocation->y + border_width; label_allocation.width = available_width - 2 * border_width; label_allocation.height = allocation->height - 2 * border_width; gtk_widget_size_allocate (priv->folder_bar_label, & label_allocation); widget->allocation = *allocation; } static int impl_expose_event (GtkWidget *widget, GdkEventExpose *event) { gtk_paint_flat_box (widget->style, widget->window, GTK_STATE_ACTIVE, GTK_SHADOW_OUT, &event->area, widget, "EShellFolderTitleBar", widget->allocation.x, widget->allocation.y, widget->allocation.width, widget->allocation.height); (* GTK_WIDGET_CLASS (e_shell_folder_title_bar_parent_class)->expose_event) (widget, event); return FALSE; } static void e_shell_folder_title_bar_class_init (EShellFolderTitleBarClass *klass) { GObjectClass *object_class; GtkWidgetClass *widget_class; object_class = G_OBJECT_CLASS (klass); object_class->dispose = impl_dispose; object_class->finalize = impl_finalize; widget_class = GTK_WIDGET_CLASS (klass); widget_class->size_allocate = impl_size_allocate; widget_class->expose_event = impl_expose_event; signals[TITLE_TOGGLED] = g_signal_new ("title_toggled", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (EShellFolderTitleBarClass, title_toggled), NULL, NULL, e_shell_marshal_NONE__BOOL, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); signals[BACK_CLICKED] = g_signal_new ("back_clicked", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (EShellFolderTitleBarClass, back_clicked), NULL, NULL, e_shell_marshal_NONE__NONE, G_TYPE_NONE, 0); signals[FORWARD_CLICKED] = g_signal_new ("forward_clicked", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (EShellFolderTitleBarClass, forward_clicked), NULL, NULL, e_shell_marshal_NONE__NONE, G_TYPE_NONE, 0); } static void e_shell_folder_title_bar_init (EShellFolderTitleBar *shell_folder_title_bar) { EShellFolderTitleBarPrivate *priv; priv = g_new (EShellFolderTitleBarPrivate, 1); priv->icon = NULL; priv->title_icon = NULL; priv->title_label = NULL; priv->title_button = NULL; priv->title_button_icon = NULL; priv->title_button_label = NULL; priv->title_button_arrow = NULL; priv->folder_bar_label = NULL; priv->back_button = NULL; priv->forward_button = NULL; priv->title_clickable = TRUE; shell_folder_title_bar->priv = priv; } /** * e_shell_folder_title_bar_construct: * @folder_title_bar: * * Construct the folder title bar widget. **/ void e_shell_folder_title_bar_construct (EShellFolderTitleBar *folder_title_bar) { EShellFolderTitleBarPrivate *priv; GtkWidget *title_button_hbox; GtkWidget *label; g_return_if_fail (folder_title_bar != NULL); g_return_if_fail (E_IS_SHELL_FOLDER_TITLE_BAR (folder_title_bar)); priv = folder_title_bar->priv; priv->title_icon = new_empty_image_widget (); gtk_misc_set_alignment (GTK_MISC (priv->title_icon), 1.0, .5); gtk_misc_set_padding (GTK_MISC (priv->title_icon), 2, 0); gtk_widget_show (priv->title_icon); label = gtk_label_new (""); gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END); gtk_misc_set_padding (GTK_MISC (label), 0, 0); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); priv->title_label = label; label = gtk_label_new (""); gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END); gtk_misc_set_padding (GTK_MISC (label), 2, 0); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_widget_show (label); priv->title_button_label = label; label = gtk_label_new (""); gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); gtk_widget_show (label); priv->folder_bar_label = label; priv->title_button_icon = new_empty_image_widget (); gtk_widget_show (priv->title_button_icon); title_button_hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (title_button_hbox), priv->title_button_icon, FALSE, TRUE, 2); gtk_box_pack_start (GTK_BOX (title_button_hbox), priv->title_button_label, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (title_button_hbox), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE), FALSE, TRUE, 2); gtk_widget_show (title_button_hbox); priv->title_button = gtk_toggle_button_new (); gtk_button_set_relief (GTK_BUTTON (priv->title_button), GTK_RELIEF_NONE); gtk_container_add (GTK_CONTAINER (priv->title_button), title_button_hbox); GTK_WIDGET_UNSET_FLAGS (priv->title_button, GTK_CAN_FOCUS); gtk_widget_show (priv->title_button); gtk_container_set_border_width (GTK_CONTAINER (folder_title_bar), 2); gtk_box_pack_start (GTK_BOX (folder_title_bar), priv->title_icon, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (folder_title_bar), priv->title_label, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (folder_title_bar), priv->title_button, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (folder_title_bar), priv->folder_bar_label, TRUE, TRUE, 0); /* Make the label have a border as large as the button's. FIXME: This is really hackish. The hardcoded numbers should be OK as the padding is hardcoded in GtkButton too (see CHILD_SPACING in gtkbutton.c). */ gtk_misc_set_padding (GTK_MISC (priv->title_label), GTK_WIDGET (priv->title_button)->style->xthickness, GTK_WIDGET (priv->title_button)->style->ythickness + 2); g_signal_connect (priv->title_button, "toggled", G_CALLBACK (title_button_toggled_cb), folder_title_bar); add_navigation_buttons (folder_title_bar); e_shell_folder_title_bar_set_title (folder_title_bar, NULL); } /** * e_shell_folder_title_bar_new: * @void: * * Create a new title bar widget. * * Return value: **/ GtkWidget * e_shell_folder_title_bar_new (void) { EShellFolderTitleBar *new; new = g_object_new (e_shell_folder_title_bar_get_type (), NULL); e_shell_folder_title_bar_construct (new); return GTK_WIDGET (new); } /** * e_shell_folder_title_bar_set_title: * @folder_title_bar: * @title: * * Set the title for the title bar. **/ void e_shell_folder_title_bar_set_title (EShellFolderTitleBar *folder_title_bar, const char *title) { EShellFolderTitleBarPrivate *priv; gchar *markup; g_return_if_fail (folder_title_bar != NULL); g_return_if_fail (E_IS_SHELL_FOLDER_TITLE_BAR (folder_title_bar)); priv = folder_title_bar->priv; if (title == NULL) title = _("Untitled"); markup = g_markup_printf_escaped ("%s", title); gtk_label_set_markup (GTK_LABEL (priv->title_button_label), markup); gtk_label_set_markup (GTK_LABEL (priv->title_label), markup); g_free (markup); gtk_widget_queue_resize (GTK_WIDGET (folder_title_bar)); } /** * e_shell_folder_title_bar_set_folder_bar_label: * @folder_title_bar: * @text: Some text to show in the label. * * Sets the right-justified text label (to the left of the icon) for * the title bar. **/ void e_shell_folder_title_bar_set_folder_bar_label (EShellFolderTitleBar *folder_title_bar, const char *text) { EShellFolderTitleBarPrivate *priv; g_return_if_fail (folder_title_bar != NULL); g_return_if_fail (E_IS_SHELL_FOLDER_TITLE_BAR (folder_title_bar)); priv = folder_title_bar->priv; gtk_label_set_text (GTK_LABEL (priv->folder_bar_label), text); /* FIXME: Might want to set the styles somewhere in here too, black text on grey background isn't the best combination */ gtk_widget_queue_resize (GTK_WIDGET (folder_title_bar)); } /** * e_shell_folder_title_bar_set_icon: * @folder_title_bar: * @icon: * * Set the name of the icon for the title bar. **/ void e_shell_folder_title_bar_set_icon (EShellFolderTitleBar *folder_title_bar, GdkPixbuf *icon) { EShellFolderTitleBarPrivate *priv; GdkPixmap *pixmap; GdkBitmap *mask; g_return_if_fail (folder_title_bar != NULL); g_return_if_fail (E_IS_SHELL_FOLDER_TITLE_BAR (folder_title_bar)); priv = folder_title_bar->priv; if (icon == NULL) { if (priv->icon != NULL) g_object_unref (priv->icon); priv->icon = new_empty_pixbuf (); } else { g_object_ref (icon); if (priv->icon != NULL) g_object_unref (priv->icon); priv->icon = icon; } gdk_pixbuf_render_pixmap_and_mask (priv->icon, &pixmap, &mask, 127); gtk_image_set_from_pixmap (GTK_IMAGE (priv->title_button_icon), pixmap, mask); gdk_pixbuf_render_pixmap_and_mask (priv->icon, &pixmap, &mask, 127); gtk_image_set_from_pixmap (GTK_IMAGE (priv->title_icon), pixmap, mask); } /** * e_shell_folder_title_bar_set_toggle_state: * @folder_title_bar: * @state: * * Set whether the title bar's button is in pressed state (TRUE) or not (FALSE). **/ void e_shell_folder_title_bar_set_toggle_state (EShellFolderTitleBar *folder_title_bar, gboolean state) { EShellFolderTitleBarPrivate *priv; g_return_if_fail (folder_title_bar != NULL); g_return_if_fail (E_IS_SHELL_FOLDER_TITLE_BAR (folder_title_bar)); priv = folder_title_bar->priv; gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->title_button), state); } /** * e_shell_folder_title_bar_set_clickable: * @folder_title_bar: * @clickable: * * Specify whether the title in the @folder_title_bar is clickable. If not, * the arrow pixmap is not shown. **/ void e_shell_folder_title_bar_set_title_clickable (EShellFolderTitleBar *folder_title_bar, gboolean title_clickable) { EShellFolderTitleBarPrivate *priv; g_return_if_fail (folder_title_bar != NULL); g_return_if_fail (E_IS_SHELL_FOLDER_TITLE_BAR (folder_title_bar)); priv = folder_title_bar->priv; if ((priv->title_clickable && title_clickable) || (! priv->title_clickable && ! title_clickable)) return; if (title_clickable) { gtk_widget_hide (priv->title_label); gtk_widget_hide (priv->title_icon); gtk_widget_show_all (priv->title_button); } else { gtk_widget_hide (priv->title_button); gtk_widget_show (priv->title_icon); gtk_widget_show (priv->title_label); } priv->title_clickable = !! title_clickable; } void e_shell_folder_title_bar_update_navigation_buttons (EShellFolderTitleBar *folder_title_bar, gboolean can_go_back, gboolean can_go_forward) { EShellFolderTitleBarPrivate *priv; g_return_if_fail (folder_title_bar != NULL); g_return_if_fail (E_IS_SHELL_FOLDER_TITLE_BAR (folder_title_bar)); priv = folder_title_bar->priv; gtk_widget_set_sensitive (priv->back_button, can_go_back); gtk_widget_set_sensitive (priv->forward_button, can_go_forward); }