/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Author : * Damon Chaplin * * Copyright 1999, Helix Code, Inc. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ /* * EShortcutBar displays a vertical bar with a number of Groups, each of which * contains any number of icons. It is used on the left of the main application * window so users can easily access items such as folders and files. * * The architecture is a bit complicated. EShortcutBar is a sublass of * EGroupBar (which supports a number of groups with buttons to slide them * into view). EShortcutBar places an EIconBar widget in each group page, * which displays an icon and name for each shortcut. */ #include #include #include #include "e-shortcut-bar.h" #include "e-clipped-label.h" #include "e-vscrolled-bar.h" /* Drag and Drop stuff. */ enum { TARGET_SHORTCUT }; static GtkTargetEntry target_table[] = { { "E-SHORTCUT", 0, TARGET_SHORTCUT } }; static guint n_targets = sizeof(target_table) / sizeof(target_table[0]); gboolean e_shortcut_bar_default_icon_loaded = FALSE; GdkPixbuf *e_shortcut_bar_default_icon = NULL; gchar *e_shortcut_bar_default_icon_filename = "gnome-folder.png"; static void e_shortcut_bar_class_init (EShortcutBarClass *class); static void e_shortcut_bar_init (EShortcutBar *shortcut_bar); static void e_shortcut_bar_destroy (GtkObject *object); static void e_shortcut_bar_disconnect_model (EShortcutBar *shortcut_bar); static void e_shortcut_bar_on_model_destroyed (EShortcutModel *model, EShortcutBar *shortcut_bar); static void e_shortcut_bar_on_group_added (EShortcutModel *model, gint group_num, gchar *group_name, EShortcutBar *shortcut_bar); static void e_shortcut_bar_on_group_removed (EShortcutModel *model, gint group_num, EShortcutBar *shortcut_bar); static void e_shortcut_bar_on_item_added (EShortcutModel *model, gint group_num, gint item_num, gchar *item_url, gchar *item_name, EShortcutBar *shortcut_bar); static void e_shortcut_bar_on_item_removed (EShortcutModel *model, gint group_num, gint item_num, EShortcutBar *shortcut_bar); static gint e_shortcut_bar_add_group (EShortcutBar *shortcut_bar, gint position, const gchar *group_name); static void e_shortcut_bar_remove_group (EShortcutBar *shortcut_bar, gint group_num); static gint e_shortcut_bar_add_item (EShortcutBar *shortcut_bar, gint group_num, gint position, const gchar *item_url, const gchar *item_name); static void e_shortcut_bar_remove_item (EShortcutBar *shortcut_bar, gint group_num, gint item_num); static void e_shortcut_bar_set_canvas_style (EShortcutBar *shortcut_bar, GtkWidget *canvas); static void e_shortcut_bar_item_selected (EIconBar *icon_bar, GdkEvent *event, gint item_num, EShortcutBar *shortcut_bar); static void e_shortcut_bar_item_dragged (EIconBar *icon_bar, GdkEvent *event, gint item_num, EShortcutBar *shortcut_bar); static void e_shortcut_bar_on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, guint info, guint time, EShortcutBar *shortcut_bar); static void e_shortcut_bar_on_drag_data_received(GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *data, guint info, guint time, EShortcutBar *shortcut_bar); static void e_shortcut_bar_on_drag_data_delete (GtkWidget *widget, GdkDragContext *context, EShortcutBar *shortcut_bar); static void e_shortcut_bar_on_drag_end (GtkWidget *widget, GdkDragContext *context, EShortcutBar *shortcut_bar); static void e_shortcut_bar_stop_editing (GtkWidget *button, EShortcutBar *shortcut_bar); static GdkPixbuf* e_shortcut_bar_get_image_from_url (EShortcutBar *shortcut_bar, const gchar *item_url); static GdkPixbuf* e_shortcut_bar_load_image (const gchar *filename); enum { ITEM_SELECTED, LAST_SIGNAL }; static guint e_shortcut_bar_signals[LAST_SIGNAL] = {0}; static EGroupBarClass *parent_class; E_MAKE_TYPE(e_shortcut_bar, "EShortcutBar", EShortcutBar, e_shortcut_bar_class_init, e_shortcut_bar_init, e_group_bar_get_type()) static void e_shortcut_bar_class_init (EShortcutBarClass *class) { GtkObjectClass *object_class; GtkWidgetClass *widget_class; parent_class = gtk_type_class (e_group_bar_get_type ()); object_class = (GtkObjectClass *) class; widget_class = (GtkWidgetClass *) class; e_shortcut_bar_signals[ITEM_SELECTED] = gtk_signal_new ("item_selected", GTK_RUN_LAST | GTK_RUN_ACTION, object_class->type, GTK_SIGNAL_OFFSET (EShortcutBarClass, selected_item), gtk_marshal_NONE__POINTER_INT_INT, GTK_TYPE_NONE, 3, GTK_TYPE_GDK_EVENT, GTK_TYPE_INT, GTK_TYPE_INT); gtk_object_class_add_signals (object_class, e_shortcut_bar_signals, LAST_SIGNAL); /* Method override */ object_class->destroy = e_shortcut_bar_destroy; } static void e_shortcut_bar_init (EShortcutBar *shortcut_bar) { shortcut_bar->groups = g_array_new (FALSE, FALSE, sizeof (EShortcutBarGroup)); shortcut_bar->dragged_url = NULL; shortcut_bar->dragged_name = NULL; } GtkWidget * e_shortcut_bar_new (void) { GtkWidget *shortcut_bar; shortcut_bar = GTK_WIDGET (gtk_type_new (e_shortcut_bar_get_type ())); return shortcut_bar; } static void e_shortcut_bar_destroy (GtkObject *object) { EShortcutBar *shortcut_bar; shortcut_bar = E_SHORTCUT_BAR (object); g_array_free (shortcut_bar->groups, TRUE); g_free (shortcut_bar->dragged_url); g_free (shortcut_bar->dragged_name); GTK_OBJECT_CLASS (parent_class)->destroy (object); } void e_shortcut_bar_set_model (EShortcutBar *shortcut_bar, EShortcutModel *model) { gint num_groups, group_num, num_items, item_num; gchar *group_name, *item_url, *item_name; /* Disconnect any existing model. */ e_shortcut_bar_disconnect_model (shortcut_bar); shortcut_bar->model = model; if (!model) return; gtk_signal_connect (GTK_OBJECT (model), "destroy", GTK_SIGNAL_FUNC (e_shortcut_bar_on_model_destroyed), shortcut_bar); gtk_signal_connect (GTK_OBJECT (model), "group_added", GTK_SIGNAL_FUNC (e_shortcut_bar_on_group_added), shortcut_bar); gtk_signal_connect (GTK_OBJECT (model), "group_removed", GTK_SIGNAL_FUNC (e_shortcut_bar_on_group_removed), shortcut_bar); gtk_signal_connect (GTK_OBJECT (model), "item_added", GTK_SIGNAL_FUNC (e_shortcut_bar_on_item_added), shortcut_bar); gtk_signal_connect (GTK_OBJECT (model), "item_removed", GTK_SIGNAL_FUNC (e_shortcut_bar_on_item_removed), shortcut_bar); /* Add any items already in the model. */ num_groups = e_shortcut_model_get_num_groups (model); for (group_num = 0; group_num < num_groups; group_num++) { group_name = e_shortcut_model_get_group_name (model, group_num); e_shortcut_bar_add_group (shortcut_bar, group_num, group_name); g_free (group_name); num_items = e_shortcut_model_get_num_items (model, group_num); for (item_num = 0; item_num < num_items; item_num++) { e_shortcut_model_get_item_info (model, group_num, item_num, &item_url, &item_name); e_shortcut_bar_add_item (shortcut_bar, group_num, item_num, item_url, item_name); g_free (item_url); g_free (item_name); } } } static void e_shortcut_bar_disconnect_model (EShortcutBar *shortcut_bar) { /* Remove all the current groups. */ while (shortcut_bar->groups->len) e_shortcut_bar_remove_group (shortcut_bar, 0); if (shortcut_bar->model) { /* Disconnect all the signals in one go. */ gtk_signal_disconnect_by_data (GTK_OBJECT (shortcut_bar->model), shortcut_bar); shortcut_bar->model = NULL; } } static void e_shortcut_bar_on_model_destroyed (EShortcutModel *model, EShortcutBar *shortcut_bar) { e_shortcut_bar_disconnect_model (shortcut_bar); } static void e_shortcut_bar_on_group_added (EShortcutModel *model, gint group_num, gchar *group_name, EShortcutBar *shortcut_bar) { e_shortcut_bar_add_group (shortcut_bar, group_num, group_name); } static void e_shortcut_bar_on_group_removed (EShortcutModel *model, gint group_num, EShortcutBar *shortcut_bar) { e_shortcut_bar_remove_group (shortcut_bar, group_num); } static void e_shortcut_bar_on_item_added (EShortcutModel *model, gint group_num, gint item_num, gchar *item_url, gchar *item_name, EShortcutBar *shortcut_bar) { e_shortcut_bar_add_item (shortcut_bar, group_num, item_num, item_url, item_name); } static void e_shortcut_bar_on_item_removed (EShortcutModel *model, gint group_num, gint item_num, EShortcutBar *shortcut_bar) { e_shortcut_bar_remove_item (shortcut_bar, group_num, item_num); } static gint e_shortcut_bar_add_group (EShortcutBar *shortcut_bar, gint position, const gchar *group_name) { EShortcutBarGroup *group, tmp_group; gint group_num; GtkWidget *button, *label; g_return_val_if_fail (E_IS_SHORTCUT_BAR (shortcut_bar), -1); g_return_val_if_fail (group_name != NULL, -1); gtk_widget_push_colormap (gdk_rgb_get_cmap ()); gtk_widget_push_visual (gdk_rgb_get_visual ()); group_num = position; g_array_insert_val (shortcut_bar->groups, group_num, tmp_group); group = &g_array_index (shortcut_bar->groups, EShortcutBarGroup, group_num); group->vscrolled_bar = e_vscrolled_bar_new (NULL); gtk_widget_show (group->vscrolled_bar); gtk_signal_connect ( GTK_OBJECT (E_VSCROLLED_BAR (group->vscrolled_bar)->up_button), "pressed", GTK_SIGNAL_FUNC (e_shortcut_bar_stop_editing), shortcut_bar); gtk_signal_connect ( GTK_OBJECT (E_VSCROLLED_BAR (group->vscrolled_bar)->down_button), "pressed", GTK_SIGNAL_FUNC (e_shortcut_bar_stop_editing), shortcut_bar); group->icon_bar = e_icon_bar_new (); gtk_widget_show (group->icon_bar); gtk_container_add (GTK_CONTAINER (group->vscrolled_bar), group->icon_bar); gtk_signal_connect (GTK_OBJECT (group->icon_bar), "item_selected", GTK_SIGNAL_FUNC (e_shortcut_bar_item_selected), shortcut_bar); gtk_signal_connect (GTK_OBJECT (group->icon_bar), "item_dragged", GTK_SIGNAL_FUNC (e_shortcut_bar_item_dragged), shortcut_bar); gtk_signal_connect (GTK_OBJECT (group->icon_bar), "drag_data_get", GTK_SIGNAL_FUNC (e_shortcut_bar_on_drag_data_get), shortcut_bar); gtk_signal_connect (GTK_OBJECT (group->icon_bar), "drag_data_received", GTK_SIGNAL_FUNC (e_shortcut_bar_on_drag_data_received), shortcut_bar); gtk_signal_connect (GTK_OBJECT (group->icon_bar), "drag_data_delete", GTK_SIGNAL_FUNC (e_shortcut_bar_on_drag_data_delete), shortcut_bar); gtk_signal_connect (GTK_OBJECT (group->icon_bar), "drag_end", GTK_SIGNAL_FUNC (e_shortcut_bar_on_drag_end), shortcut_bar); #ifndef E_USE_STYLES e_shortcut_bar_set_canvas_style (shortcut_bar, group->icon_bar); #endif button = gtk_button_new (); label = e_clipped_label_new (group_name); gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5); gtk_widget_show (label); gtk_container_add (GTK_CONTAINER (button), label); gtk_widget_show (button); gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (e_shortcut_bar_stop_editing), shortcut_bar); gtk_drag_dest_set (GTK_WIDGET (group->icon_bar), GTK_DEST_DEFAULT_ALL, target_table, n_targets, GDK_ACTION_COPY | GDK_ACTION_MOVE); gtk_drag_dest_set (GTK_WIDGET (button), GTK_DEST_DEFAULT_ALL, target_table, n_targets, GDK_ACTION_COPY | GDK_ACTION_MOVE); e_group_bar_add_group (E_GROUP_BAR (shortcut_bar), group->vscrolled_bar, button, -1); gtk_widget_pop_visual (); gtk_widget_pop_colormap (); return group_num; } static void e_shortcut_bar_remove_group (EShortcutBar *shortcut_bar, gint group_num) { e_group_bar_remove_group (E_GROUP_BAR (shortcut_bar), group_num); g_array_remove_index (shortcut_bar->groups, group_num); } static gint e_shortcut_bar_add_item (EShortcutBar *shortcut_bar, gint group_num, gint position, const gchar *item_url, const gchar *item_name) { EShortcutBarGroup *group; GdkPixbuf *image; gint item_num; g_return_val_if_fail (E_IS_SHORTCUT_BAR (shortcut_bar), -1); g_return_val_if_fail (group_num >= 0, -1); g_return_val_if_fail (group_num < shortcut_bar->groups->len, -1); g_return_val_if_fail (item_url != NULL, -1); g_return_val_if_fail (item_name != NULL, -1); image = e_shortcut_bar_get_image_from_url (shortcut_bar, item_url); group = &g_array_index (shortcut_bar->groups, EShortcutBarGroup, group_num); item_num = e_icon_bar_add_item (E_ICON_BAR (group->icon_bar), image, item_name, position); gdk_pixbuf_unref (image); e_icon_bar_set_item_data_full (E_ICON_BAR (group->icon_bar), item_num, g_strdup (item_url), g_free); return item_num; } static void e_shortcut_bar_remove_item (EShortcutBar *shortcut_bar, gint group_num, gint item_num) { EShortcutBarGroup *group; g_return_if_fail (E_IS_SHORTCUT_BAR (shortcut_bar)); g_return_if_fail (group_num >= 0); g_return_if_fail (group_num < shortcut_bar->groups->len); group = &g_array_index (shortcut_bar->groups, EShortcutBarGroup, group_num); e_icon_bar_remove_item (E_ICON_BAR (group->icon_bar), item_num); } static void e_shortcut_bar_set_canvas_style (EShortcutBar *shortcut_bar, GtkWidget *canvas) { GtkRcStyle *rc_style; rc_style = gtk_rc_style_new (); rc_style->color_flags[GTK_STATE_NORMAL] = GTK_RC_FG | GTK_RC_BG; rc_style->fg[GTK_STATE_NORMAL].red = 65535; rc_style->fg[GTK_STATE_NORMAL].green = 65535; rc_style->fg[GTK_STATE_NORMAL].blue = 65535; rc_style->bg[GTK_STATE_NORMAL].red = 32512; rc_style->bg[GTK_STATE_NORMAL].green = 32512; rc_style->bg[GTK_STATE_NORMAL].blue = 32512; gtk_widget_modify_style (GTK_WIDGET (canvas), rc_style); gtk_rc_style_unref (rc_style); } void e_shortcut_bar_set_view_type (EShortcutBar *shortcut_bar, gint group_num, EIconBarViewType view_type) { EShortcutBarGroup *group; g_return_if_fail (E_IS_SHORTCUT_BAR (shortcut_bar)); g_return_if_fail (group_num >= 0); g_return_if_fail (group_num < shortcut_bar->groups->len); group = &g_array_index (shortcut_bar->groups, EShortcutBarGroup, group_num); e_icon_bar_set_view_type (E_ICON_BAR (group->icon_bar), view_type); } EIconBarViewType e_shortcut_bar_get_view_type (EShortcutBar *shortcut_bar, gint group_num) { EShortcutBarGroup *group; g_return_val_if_fail (E_IS_SHORTCUT_BAR (shortcut_bar), E_ICON_BAR_SMALL_ICONS); g_return_val_if_fail (group_num >= 0, E_ICON_BAR_SMALL_ICONS); g_return_val_if_fail (group_num < shortcut_bar->groups->len, E_ICON_BAR_SMALL_ICONS); group = &g_array_index (shortcut_bar->groups, EShortcutBarGroup, group_num); return E_ICON_BAR (group->icon_bar)->view_type; } static void e_shortcut_bar_item_selected (EIconBar *icon_bar, GdkEvent *event, gint item_num, EShortcutBar *shortcut_bar) { gint group_num; group_num = e_group_bar_get_group_num (E_GROUP_BAR (shortcut_bar), GTK_WIDGET (icon_bar)->parent); gtk_signal_emit (GTK_OBJECT (shortcut_bar), e_shortcut_bar_signals[ITEM_SELECTED], event, group_num, item_num); } static void e_shortcut_bar_item_dragged (EIconBar *icon_bar, GdkEvent *event, gint item_num, EShortcutBar *shortcut_bar) { GtkTargetList *target_list; gint group_num; group_num = e_group_bar_get_group_num (E_GROUP_BAR (shortcut_bar), GTK_WIDGET (icon_bar)->parent); shortcut_bar->dragged_url = g_strdup (e_icon_bar_get_item_data (icon_bar, item_num)); shortcut_bar->dragged_name = e_icon_bar_get_item_text (icon_bar, item_num); target_list = gtk_target_list_new (target_table, n_targets); gtk_drag_begin (GTK_WIDGET (icon_bar), target_list, GDK_ACTION_COPY | GDK_ACTION_MOVE, 1, event); gtk_target_list_unref (target_list); } static void e_shortcut_bar_on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, guint info, guint time, EShortcutBar *shortcut_bar) { gchar *data; if (info == TARGET_SHORTCUT) { data = g_strdup_printf ("%s%c%s", shortcut_bar->dragged_name, '\0', shortcut_bar->dragged_url); gtk_selection_data_set (selection_data, selection_data->target, 8, data, strlen (shortcut_bar->dragged_name) + strlen (shortcut_bar->dragged_url) + 2); g_free (data); } } static void e_shortcut_bar_on_drag_data_received (GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *data, guint info, guint time, EShortcutBar *shortcut_bar) { gchar *item_name, *item_url; EIconBar *icon_bar; gint position, group_num; icon_bar = E_ICON_BAR (widget); position = icon_bar->dragging_before_item_num; if ((data->length >= 0) && (data->format == 8) && position != -1) { item_name = data->data; item_url = item_name + strlen (item_name) + 1; group_num = e_group_bar_get_group_num (E_GROUP_BAR (shortcut_bar), GTK_WIDGET (icon_bar)->parent); e_shortcut_model_add_item (shortcut_bar->model, group_num, position, item_url, item_name); gtk_drag_finish (context, TRUE, TRUE, time); return; } gtk_drag_finish (context, FALSE, FALSE, time); } static void e_shortcut_bar_on_drag_data_delete (GtkWidget *widget, GdkDragContext *context, EShortcutBar *shortcut_bar) { EIconBar *icon_bar; gint group_num; icon_bar = E_ICON_BAR (widget); group_num = e_group_bar_get_group_num (E_GROUP_BAR (shortcut_bar), widget->parent); e_shortcut_model_remove_item (shortcut_bar->model, group_num, icon_bar->dragged_item_num); } static void e_shortcut_bar_on_drag_end (GtkWidget *widget, GdkDragContext *context, EShortcutBar *shortcut_bar) { g_free (shortcut_bar->dragged_name); shortcut_bar->dragged_name = NULL; g_free (shortcut_bar->dragged_url); shortcut_bar->dragged_url = NULL; } void e_shortcut_bar_start_editing_item (EShortcutBar *shortcut_bar, gint group_num, gint item_num) { EShortcutBarGroup *group; g_return_if_fail (E_IS_SHORTCUT_BAR (shortcut_bar)); g_return_if_fail (group_num >= 0); g_return_if_fail (group_num < shortcut_bar->groups->len); group = &g_array_index (shortcut_bar->groups, EShortcutBarGroup, group_num); e_icon_bar_start_editing_item (E_ICON_BAR (group->icon_bar), item_num); } /* We stop editing any item when a scroll button is pressed. */ static void e_shortcut_bar_stop_editing (GtkWidget *button, EShortcutBar *shortcut_bar) { EShortcutBarGroup *group; gint group_num; for (group_num = 0; group_num < shortcut_bar->groups->len; group_num++) { group = &g_array_index (shortcut_bar->groups, EShortcutBarGroup, group_num); e_icon_bar_stop_editing_item (E_ICON_BAR (group->icon_bar), TRUE); } } /* Sets the callback which is called to return the icon to use for a particular URL. */ void e_shortcut_bar_set_icon_callback (EShortcutBar *shortcut_bar, EShortcutBarIconCallback cb, gpointer data) { shortcut_bar->icon_callback = cb; shortcut_bar->icon_callback_data = data; } static GdkPixbuf * e_shortcut_bar_get_image_from_url (EShortcutBar *shortcut_bar, const gchar *item_url) { GdkPixbuf *icon = NULL; if (shortcut_bar->icon_callback) icon = (*shortcut_bar->icon_callback) (shortcut_bar, item_url, shortcut_bar->icon_callback_data); if (!icon) { if (!e_shortcut_bar_default_icon_loaded) { e_shortcut_bar_default_icon_loaded = TRUE; e_shortcut_bar_default_icon = e_shortcut_bar_load_image (e_shortcut_bar_default_icon_filename); } icon = e_shortcut_bar_default_icon; /* ref the default icon each time we return it */ gdk_pixbuf_ref (icon); } return icon; } static GdkPixbuf * e_shortcut_bar_load_image (const gchar *filename) { gchar *pathname; GdkPixbuf *image = NULL; pathname = gnome_pixmap_file (filename); if (pathname) image = gdk_pixbuf_new_from_file (pathname); else g_warning ("Couldn't find pixmap: %s", filename); g_free (pathname); return image; }