/* * e-tree-view-frame.c * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) version 3. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with the program; if not, see * */ /** * SECTION: e-tree-view-frame * @include: e-util/e-util.h * @short_description: A frame for #GtkTreeView * * #ETreeViewFrame embeds a #GtkTreeView in a scrolled window and adds an * inline-style toolbar beneath the scrolled window which can be hidden. * * The inline-style toolbar supports "add" and "remove" actions, as well * as move actions if the tree view is reorderable and selection actions * if the tree view supports multiple selections. The action set can be * extended through e_tree_view_frame_insert_toolbar_action(). **/ #include "e-tree-view-frame.h" #include #define E_TREE_VIEW_FRAME_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_TREE_VIEW_FRAME, ETreeViewFramePrivate)) /** * E_TREE_VIEW_FRAME_ACTION_ADD: * * The #GtkAction name for the "add" toolbar button. * * Use e_tree_view_frame_lookup_toolbar_action() to obtain the #GtkAction. **/ /** * E_TREE_VIEW_FRAME_ACTION_REMOVE: * * The #GtkAction name for the "remove" toolbar button. * * Use e_tree_view_frame_lookup_toolbar_action() to obtain the #GtkAction. **/ /** * E_TREE_VIEW_FRAME_ACTION_MOVE_TOP: * * The #GtkAction name for the "move selected items to top" button. * * Use e_tree_view_frame_lookup_toolbar_action() to obtain the #GtkAction. **/ /** * E_TREE_VIEW_FRAME_ACTION_MOVE_UP: * * The #GtkAction name for the "move selected items up" button. * * Use e_tree_view_frame_lookup_toolbar_action() to obtain the #GtkAction. **/ /** * E_TREE_VIEW_FRAME_ACTION_MOVE_DOWN: * * The #GtkAction name for the "move selected items down" button. * * Use e_tree_view_frame_lookup_toolbar_action() to obtain the #GtkAction. **/ /** * E_TREE_VIEW_FRAME_ACTION_MOVE_BOTTOM: * * The #GtkAction name for the "move selected items to bottom" button. * * Use e_tree_view_frame_lookup_toolbar_action() to obtain the #GtkAction. **/ /** * E_TREE_VIEW_FRAME_ACTION_SELECT_ALL: * * The #GtkAction name for the "select all" button. * * Use e_tree_view_frame_lookup_toolbar_action() to obtain the #GtkAction. **/ struct _ETreeViewFramePrivate { GtkTreeView *tree_view; gulong notify_reorderable_handler_id; gulong notify_select_mode_handler_id; gulong selection_changed_handler_id; GtkWidget *scrolled_window; GtkWidget *inline_toolbar; GHashTable *tool_item_ht; GtkPolicyType hscrollbar_policy; GtkPolicyType vscrollbar_policy; gboolean toolbar_visible; }; enum { PROP_0, PROP_HSCROLLBAR_POLICY, PROP_TREE_VIEW, PROP_TOOLBAR_VISIBLE, PROP_VSCROLLBAR_POLICY }; enum { TOOLBAR_ACTION_ACTIVATE, UPDATE_TOOLBAR_ACTIONS, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; G_DEFINE_TYPE_WITH_CODE ( ETreeViewFrame, e_tree_view_frame, GTK_TYPE_BOX, G_IMPLEMENT_INTERFACE ( E_TYPE_EXTENSIBLE, NULL)) static void tree_view_frame_append_action (ETreeViewFrame *tree_view_frame, const gchar *action_name, const gchar *icon_name) { GtkAction *action; GIcon *icon; icon = g_themed_icon_new_with_default_fallbacks (icon_name); action = g_object_new ( GTK_TYPE_ACTION, "name", action_name, "gicon", icon, NULL); e_tree_view_frame_insert_toolbar_action (tree_view_frame, action, -1); g_object_unref (action); g_object_unref (icon); } static void tree_view_frame_dispose_tree_view (ETreeViewFramePrivate *priv) { if (priv->notify_reorderable_handler_id > 0) { g_signal_handler_disconnect ( priv->tree_view, priv->notify_reorderable_handler_id); priv->notify_reorderable_handler_id = 0; } if (priv->notify_select_mode_handler_id > 0) { g_signal_handler_disconnect ( gtk_tree_view_get_selection (priv->tree_view), priv->notify_select_mode_handler_id); priv->notify_select_mode_handler_id = 0; } if (priv->selection_changed_handler_id > 0) { g_signal_handler_disconnect ( gtk_tree_view_get_selection (priv->tree_view), priv->selection_changed_handler_id); priv->selection_changed_handler_id = 0; } g_clear_object (&priv->tree_view); } static gboolean tree_view_frame_first_row_selected (GtkTreeView *tree_view) { GtkTreeModel *tree_model; GtkTreeSelection *selection; GtkTreeIter iter; tree_model = gtk_tree_view_get_model (tree_view); selection = gtk_tree_view_get_selection (tree_view); if (tree_model == NULL) return FALSE; if (!gtk_tree_model_iter_nth_child (tree_model, &iter, NULL, 0)) return FALSE; return gtk_tree_selection_iter_is_selected (selection, &iter); } static gboolean tree_view_frame_last_row_selected (GtkTreeView *tree_view) { GtkTreeModel *tree_model; GtkTreeSelection *selection; GtkTreeIter iter; gint last; tree_model = gtk_tree_view_get_model (tree_view); selection = gtk_tree_view_get_selection (tree_view); if (tree_model == NULL) return FALSE; last = gtk_tree_model_iter_n_children (tree_model, NULL) - 1; if (last < 0) return FALSE; if (!gtk_tree_model_iter_nth_child (tree_model, &iter, NULL, last)) return FALSE; return gtk_tree_selection_iter_is_selected (selection, &iter); } static gboolean tree_view_frame_move_selection_up (GtkTreeView *tree_view) { GtkTreeModel *tree_model; GtkListStore *list_store; GtkTreeSelection *selection; GList *list, *link; tree_model = gtk_tree_view_get_model (tree_view); if (!GTK_IS_LIST_STORE (tree_model)) return FALSE; if (tree_view_frame_first_row_selected (tree_view)) return FALSE; list_store = GTK_LIST_STORE (tree_model); selection = gtk_tree_view_get_selection (tree_view); list = gtk_tree_selection_get_selected_rows (selection, NULL); /* Move all selected rows up one, even * if the selection is not contiguous. */ for (link = list; link != NULL; link = g_list_next (link)) { GtkTreePath *path = link->data; GtkTreeIter iter; GtkTreeIter prev; if (!gtk_tree_model_get_iter (tree_model, &iter, path)) { g_warn_if_reached (); continue; } prev = iter; if (!gtk_tree_model_iter_previous (tree_model, &prev)) { g_warn_if_reached (); continue; } gtk_list_store_swap (list_store, &iter, &prev); } g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free); return TRUE; } static gboolean tree_view_frame_move_selection_down (GtkTreeView *tree_view) { GtkTreeModel *tree_model; GtkListStore *list_store; GtkTreeSelection *selection; GList *list, *link; tree_model = gtk_tree_view_get_model (tree_view); if (!GTK_IS_LIST_STORE (tree_model)) return FALSE; if (tree_view_frame_last_row_selected (tree_view)) return FALSE; list_store = GTK_LIST_STORE (tree_model); selection = gtk_tree_view_get_selection (tree_view); list = gtk_tree_selection_get_selected_rows (selection, NULL); /* Reverse the list so we don't disturb rows we've already moved. */ list = g_list_reverse (list); for (link = list; link != NULL; link = g_list_next (link)) { GtkTreePath *path = link->data; GtkTreeIter iter; GtkTreeIter next; if (!gtk_tree_model_get_iter (tree_model, &iter, path)) { g_warn_if_reached (); continue; } next = iter; if (!gtk_tree_model_iter_next (tree_model, &next)) { g_warn_if_reached (); continue; } gtk_list_store_swap (list_store, &iter, &next); } g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free); return TRUE; } static void tree_view_frame_scroll_to_cursor (GtkTreeView *tree_view) { GtkTreePath *path = NULL; gtk_tree_view_get_cursor (tree_view, &path, NULL); if (path != NULL) { gtk_tree_view_scroll_to_cell ( tree_view, path, NULL, FALSE, 0.0, 0.0); gtk_tree_path_free (path); } } static void tree_view_frame_action_go_top (ETreeViewFrame *tree_view_frame) { GtkTreeView *tree_view; tree_view = e_tree_view_frame_get_tree_view (tree_view_frame); /* Not the most efficient method, but it's simple and works. */ while (tree_view_frame_move_selection_up (tree_view)) ; tree_view_frame_scroll_to_cursor (tree_view); e_tree_view_frame_update_toolbar_actions (tree_view_frame); } static void tree_view_frame_action_go_up (ETreeViewFrame *tree_view_frame) { GtkTreeView *tree_view; tree_view = e_tree_view_frame_get_tree_view (tree_view_frame); tree_view_frame_move_selection_up (tree_view); tree_view_frame_scroll_to_cursor (tree_view); e_tree_view_frame_update_toolbar_actions (tree_view_frame); } static void tree_view_frame_action_go_down (ETreeViewFrame *tree_view_frame) { GtkTreeView *tree_view; tree_view = e_tree_view_frame_get_tree_view (tree_view_frame); tree_view_frame_move_selection_down (tree_view); tree_view_frame_scroll_to_cursor (tree_view); e_tree_view_frame_update_toolbar_actions (tree_view_frame); } static void tree_view_frame_action_go_bottom (ETreeViewFrame *tree_view_frame) { GtkTreeView *tree_view; tree_view = e_tree_view_frame_get_tree_view (tree_view_frame); /* Not the most efficient method, but it's simple and works. */ while (tree_view_frame_move_selection_down (tree_view)) ; tree_view_frame_scroll_to_cursor (tree_view); e_tree_view_frame_update_toolbar_actions (tree_view_frame); } static void tree_view_frame_action_select_all (ETreeViewFrame *tree_view_frame) { GtkTreeView *tree_view; GtkTreeSelection *selection; tree_view = e_tree_view_frame_get_tree_view (tree_view_frame); selection = gtk_tree_view_get_selection (tree_view); gtk_tree_selection_select_all (selection); } static void tree_view_frame_action_activate_cb (GtkAction *action, ETreeViewFrame *tree_view_frame) { GQuark detail; const gchar *action_name; gboolean handled = FALSE; action_name = gtk_action_get_name (action); detail = g_quark_from_string (action_name); g_signal_emit ( tree_view_frame, signals[TOOLBAR_ACTION_ACTIVATE], detail, action, &handled); } static void tree_view_frame_notify_reorderable_cb (GtkTreeView *tree_view, GParamSpec *pspec, ETreeViewFrame *tree_view_frame) { e_tree_view_frame_update_toolbar_actions (tree_view_frame); } static void tree_view_frame_notify_select_mode_cb (GtkTreeSelection *selection, GParamSpec *pspec, ETreeViewFrame *tree_view_frame) { e_tree_view_frame_update_toolbar_actions (tree_view_frame); } static void tree_view_frame_selection_changed_cb (GtkTreeSelection *selection, ETreeViewFrame *tree_view_frame) { e_tree_view_frame_update_toolbar_actions (tree_view_frame); } static void tree_view_frame_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_HSCROLLBAR_POLICY: e_tree_view_frame_set_hscrollbar_policy ( E_TREE_VIEW_FRAME (object), g_value_get_enum (value)); return; case PROP_TREE_VIEW: e_tree_view_frame_set_tree_view ( E_TREE_VIEW_FRAME (object), g_value_get_object (value)); return; case PROP_TOOLBAR_VISIBLE: e_tree_view_frame_set_toolbar_visible ( E_TREE_VIEW_FRAME (object), g_value_get_boolean (value)); return; case PROP_VSCROLLBAR_POLICY: e_tree_view_frame_set_vscrollbar_policy ( E_TREE_VIEW_FRAME (object), g_value_get_enum (value)); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void tree_view_frame_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_HSCROLLBAR_POLICY: g_value_set_enum ( value, e_tree_view_frame_get_hscrollbar_policy ( E_TREE_VIEW_FRAME (object))); return; case PROP_TREE_VIEW: g_value_set_object ( value, e_tree_view_frame_get_tree_view ( E_TREE_VIEW_FRAME (object))); return; case PROP_TOOLBAR_VISIBLE: g_value_set_boolean ( value, e_tree_view_frame_get_toolbar_visible ( E_TREE_VIEW_FRAME (object))); return; case PROP_VSCROLLBAR_POLICY: g_value_set_enum ( value, e_tree_view_frame_get_vscrollbar_policy ( E_TREE_VIEW_FRAME (object))); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void tree_view_frame_dispose (GObject *object) { ETreeViewFramePrivate *priv; priv = E_TREE_VIEW_FRAME_GET_PRIVATE (object); tree_view_frame_dispose_tree_view (priv); g_clear_object (&priv->scrolled_window); g_clear_object (&priv->inline_toolbar); g_hash_table_remove_all (priv->tool_item_ht); /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_tree_view_frame_parent_class)->dispose (object); } static void tree_view_frame_finalize (GObject *object) { ETreeViewFramePrivate *priv; priv = E_TREE_VIEW_FRAME_GET_PRIVATE (object); g_hash_table_destroy (priv->tool_item_ht); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (e_tree_view_frame_parent_class)->finalize (object); } static void tree_view_frame_constructed (GObject *object) { ETreeViewFrame *tree_view_frame; GtkStyleContext *style_context; GtkWidget *widget; tree_view_frame = E_TREE_VIEW_FRAME (object); /* Chain up to parent's constructed() method. */ G_OBJECT_CLASS (e_tree_view_frame_parent_class)->constructed (object); gtk_orientable_set_orientation ( GTK_ORIENTABLE (tree_view_frame), GTK_ORIENTATION_VERTICAL); widget = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN); gtk_box_pack_start (GTK_BOX (tree_view_frame), widget, TRUE, TRUE, 0); tree_view_frame->priv->scrolled_window = g_object_ref (widget); gtk_widget_show (widget); g_object_bind_property ( tree_view_frame, "hscrollbar-policy", widget, "hscrollbar-policy", G_BINDING_SYNC_CREATE); g_object_bind_property ( tree_view_frame, "vscrollbar-policy", widget, "vscrollbar-policy", G_BINDING_SYNC_CREATE); widget = gtk_toolbar_new (); gtk_toolbar_set_show_arrow (GTK_TOOLBAR (widget), FALSE); gtk_toolbar_set_style (GTK_TOOLBAR (widget), GTK_TOOLBAR_ICONS); gtk_toolbar_set_icon_size (GTK_TOOLBAR (widget), GTK_ICON_SIZE_MENU); gtk_box_pack_start (GTK_BOX (tree_view_frame), widget, FALSE, FALSE, 0); tree_view_frame->priv->inline_toolbar = g_object_ref (widget); gtk_widget_show (widget); style_context = gtk_widget_get_style_context (widget); gtk_style_context_add_class ( style_context, GTK_STYLE_CLASS_INLINE_TOOLBAR); gtk_style_context_set_junction_sides ( style_context, GTK_JUNCTION_TOP); g_object_bind_property ( tree_view_frame, "toolbar-visible", widget, "visible", G_BINDING_SYNC_CREATE); /* Define actions for toolbar items. */ tree_view_frame_append_action ( tree_view_frame, E_TREE_VIEW_FRAME_ACTION_ADD, "list-add-symbolic"); tree_view_frame_append_action ( tree_view_frame, E_TREE_VIEW_FRAME_ACTION_REMOVE, "list-remove-symbolic"); tree_view_frame_append_action ( tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_TOP, "go-top-symbolic"); tree_view_frame_append_action ( tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_UP, "go-up-symbolic"); tree_view_frame_append_action ( tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_DOWN, "go-down-symbolic"); tree_view_frame_append_action ( tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_BOTTOM, "go-bottom-symbolic"); tree_view_frame_append_action ( tree_view_frame, E_TREE_VIEW_FRAME_ACTION_SELECT_ALL, "edit-select-all-symbolic"); /* Install a default GtkTreeView. */ e_tree_view_frame_set_tree_view (tree_view_frame, NULL); } static gboolean tree_view_frame_toolbar_action_activate (ETreeViewFrame *tree_view_frame, GtkAction *action) { const gchar *action_name; action_name = gtk_action_get_name (action); g_return_val_if_fail (action_name != NULL, FALSE); if (g_str_equal (action_name, E_TREE_VIEW_FRAME_ACTION_MOVE_TOP)) { tree_view_frame_action_go_top (tree_view_frame); return TRUE; } if (g_str_equal (action_name, E_TREE_VIEW_FRAME_ACTION_MOVE_UP)) { tree_view_frame_action_go_up (tree_view_frame); return TRUE; } if (g_str_equal (action_name, E_TREE_VIEW_FRAME_ACTION_MOVE_DOWN)) { tree_view_frame_action_go_down (tree_view_frame); return TRUE; } if (g_str_equal (action_name, E_TREE_VIEW_FRAME_ACTION_MOVE_BOTTOM)) { tree_view_frame_action_go_bottom (tree_view_frame); return TRUE; } if (g_str_equal (action_name, E_TREE_VIEW_FRAME_ACTION_SELECT_ALL)) { tree_view_frame_action_select_all (tree_view_frame); return TRUE; } return FALSE; } static void tree_view_frame_update_toolbar_actions (ETreeViewFrame *tree_view_frame) { GtkAction *action; GtkTreeView *tree_view; GtkTreeModel *tree_model; GtkTreeSelection *selection; GtkSelectionMode selection_mode; gboolean first_row_selected; gboolean last_row_selected; gboolean sensitive; gboolean visible; gint n_selected_rows; gint n_rows = 0; /* XXX This implementation assumes the tree model is a list store. * A tree store will require special handling, although I don't * yet know if there's even a use case for a tree store here. */ tree_view = e_tree_view_frame_get_tree_view (tree_view_frame); tree_model = gtk_tree_view_get_model (tree_view); if (tree_model != NULL) n_rows = gtk_tree_model_iter_n_children (tree_model, NULL); selection = gtk_tree_view_get_selection (tree_view); selection_mode = gtk_tree_selection_get_mode (selection); n_selected_rows = gtk_tree_selection_count_selected_rows (selection); first_row_selected = tree_view_frame_first_row_selected (tree_view); last_row_selected = tree_view_frame_last_row_selected (tree_view); action = e_tree_view_frame_lookup_toolbar_action ( tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_TOP); visible = gtk_tree_view_get_reorderable (tree_view); sensitive = (n_selected_rows > 0 && !first_row_selected); gtk_action_set_visible (action, visible); gtk_action_set_sensitive (action, sensitive); action = e_tree_view_frame_lookup_toolbar_action ( tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_UP); visible = gtk_tree_view_get_reorderable (tree_view); sensitive = (n_selected_rows > 0 && !first_row_selected); gtk_action_set_visible (action, visible); gtk_action_set_sensitive (action, sensitive); action = e_tree_view_frame_lookup_toolbar_action ( tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_DOWN); visible = gtk_tree_view_get_reorderable (tree_view); sensitive = (n_selected_rows > 0 && !last_row_selected); gtk_action_set_visible (action, visible); gtk_action_set_sensitive (action, sensitive); action = e_tree_view_frame_lookup_toolbar_action ( tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_BOTTOM); visible = gtk_tree_view_get_reorderable (tree_view); sensitive = (n_selected_rows > 0 && !last_row_selected); gtk_action_set_visible (action, visible); gtk_action_set_sensitive (action, sensitive); action = e_tree_view_frame_lookup_toolbar_action ( tree_view_frame, E_TREE_VIEW_FRAME_ACTION_SELECT_ALL); visible = (selection_mode == GTK_SELECTION_MULTIPLE); sensitive = (n_selected_rows < n_rows); gtk_action_set_visible (action, visible); gtk_action_set_sensitive (action, sensitive); } static void e_tree_view_frame_class_init (ETreeViewFrameClass *class) { GObjectClass *object_class; g_type_class_add_private (class, sizeof (ETreeViewFramePrivate)); object_class = G_OBJECT_CLASS (class); object_class->set_property = tree_view_frame_set_property; object_class->get_property = tree_view_frame_get_property; object_class->dispose = tree_view_frame_dispose; object_class->finalize = tree_view_frame_finalize; object_class->constructed = tree_view_frame_constructed; class->toolbar_action_activate = tree_view_frame_toolbar_action_activate; class->update_toolbar_actions = tree_view_frame_update_toolbar_actions; g_object_class_install_property ( object_class, PROP_HSCROLLBAR_POLICY, g_param_spec_enum ( "hscrollbar-policy", "Horizontal Scrollbar Policy", "When the horizontal scrollbar is displayed", GTK_TYPE_POLICY_TYPE, GTK_POLICY_AUTOMATIC, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); /* Don't use G_PARAM_CONSTRUCT here. Our constructed() method * will install a default GtkTreeView once the scrolled window * is set up. */ g_object_class_install_property ( object_class, PROP_TREE_VIEW, g_param_spec_object ( "tree-view", "Tree View", "The tree view widget", GTK_TYPE_TREE_VIEW, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_TOOLBAR_VISIBLE, g_param_spec_boolean ( "toolbar-visible", "Toolbar Visible", "Whether to show the inline toolbar", TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_VSCROLLBAR_POLICY, g_param_spec_enum ( "vscrollbar-policy", "Vertical Scrollbar Policy", "When the vertical scrollbar is displayed", GTK_TYPE_POLICY_TYPE, GTK_POLICY_AUTOMATIC, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); /** * ETreeViewFrame::toolbar-action-activate: * @tree_view_frame: the #ETreeViewFrame that received the signal * @action: the #GtkAction that was activated * * Emitted when a toolbar action is activated. * * This signal supports "::detail" appendices to the signal name, * where the "detail" part is the #GtkAction #GtkAction:name. So * you can connect a signal handler to a particular action. **/ signals[TOOLBAR_ACTION_ACTIVATE] = g_signal_new ( "toolbar-action-activate", G_OBJECT_CLASS_TYPE (class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, G_STRUCT_OFFSET ( ETreeViewFrameClass, toolbar_action_activate), g_signal_accumulator_true_handled, NULL, NULL, G_TYPE_BOOLEAN, 1, GTK_TYPE_ACTION); /** * ETreeViewFrame::update-toolbar-actions: * @tree_view_frame: the #ETreeViewFrame that received the signal * * Requests toolbar actions be updated, usually in response to a * #GtkTreeSelection change. Handlers should update #GtkAction * properties like #GtkAction:visible and #GtkAction:sensitive * based on the current #ETreeViewFrame:tree-view state. **/ signals[UPDATE_TOOLBAR_ACTIONS] = g_signal_new ( "update-toolbar-actions", G_OBJECT_CLASS_TYPE (class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET ( ETreeViewFrameClass, update_toolbar_actions), NULL, NULL, NULL, G_TYPE_NONE, 0); } static void e_tree_view_frame_init (ETreeViewFrame *tree_view_frame) { GHashTable *tool_item_ht; tool_item_ht = g_hash_table_new_full ( (GHashFunc) g_str_hash, (GEqualFunc) g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) g_object_unref); tree_view_frame->priv = E_TREE_VIEW_FRAME_GET_PRIVATE (tree_view_frame); tree_view_frame->priv->tool_item_ht = tool_item_ht; } /** * e_tree_view_frame_new: * * Creates a new #ETreeViewFrame. * * Returns: an #ETreeViewFrame **/ GtkWidget * e_tree_view_frame_new (void) { return g_object_new (E_TYPE_TREE_VIEW_FRAME, NULL); } /** * e_tree_view_frame_get_tree_view: * @tree_view_frame: an #ETreeViewFrame * * Returns the #ETreeViewFrame:tree-view for @tree_view_frame. * * The @tree_view_frame creates its own #GtkTreeView by default, but * that instance can be replaced with e_tree_view_frame_set_tree_view(). * * Returns: a #GtkTreeView **/ GtkTreeView * e_tree_view_frame_get_tree_view (ETreeViewFrame *tree_view_frame) { g_return_val_if_fail (E_IS_TREE_VIEW_FRAME (tree_view_frame), NULL); return tree_view_frame->priv->tree_view; } /** * e_tree_view_frame_set_tree_view: * @tree_view_frame: an #ETreeViewFrame * @tree_view: a #GtkTreeView, or %NULL * * Replaces the previous #ETreeViewFrame:tree-view with the given @tree_view. * If @tree_view is %NULL, the @tree_view_frame creates a new #GtkTreeView. **/ void e_tree_view_frame_set_tree_view (ETreeViewFrame *tree_view_frame, GtkTreeView *tree_view) { GtkTreeSelection *selection; GtkWidget *scrolled_window; gulong handler_id; g_return_if_fail (E_IS_TREE_VIEW_FRAME (tree_view_frame)); if (tree_view != NULL) { g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); g_object_ref (tree_view); } else { tree_view = (GtkTreeView *) gtk_tree_view_new (); g_object_ref_sink (tree_view); } scrolled_window = tree_view_frame->priv->scrolled_window; if (tree_view_frame->priv->tree_view != NULL) { gtk_container_remove ( GTK_CONTAINER (scrolled_window), GTK_WIDGET (tree_view_frame->priv->tree_view)); tree_view_frame_dispose_tree_view (tree_view_frame->priv); } tree_view_frame->priv->tree_view = tree_view; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)); handler_id = g_signal_connect ( tree_view, "notify::reorderable", G_CALLBACK (tree_view_frame_notify_reorderable_cb), tree_view_frame); tree_view_frame->priv->notify_reorderable_handler_id = handler_id; handler_id = g_signal_connect ( selection, "notify::mode", G_CALLBACK (tree_view_frame_notify_select_mode_cb), tree_view_frame); tree_view_frame->priv->notify_select_mode_handler_id = handler_id; handler_id = g_signal_connect ( selection, "changed", G_CALLBACK (tree_view_frame_selection_changed_cb), tree_view_frame); tree_view_frame->priv->selection_changed_handler_id = handler_id; gtk_container_add ( GTK_CONTAINER (scrolled_window), GTK_WIDGET (tree_view)); gtk_widget_show (GTK_WIDGET (tree_view)); g_object_notify (G_OBJECT (tree_view_frame), "tree-view"); e_tree_view_frame_update_toolbar_actions (tree_view_frame); } /** * e_tree_view_frame_get_toolbar_visible: * @tree_view_frame: an #ETreeViewFrame * * Returns whether the inline toolbar in @tree_view_frame is visible. * * Returns: %TRUE if the toolbar is visible, %FALSE if invisible **/ gboolean e_tree_view_frame_get_toolbar_visible (ETreeViewFrame *tree_view_frame) { g_return_val_if_fail (E_IS_TREE_VIEW_FRAME (tree_view_frame), FALSE); return tree_view_frame->priv->toolbar_visible; } /** * e_tree_view_frame_set_toolbar_visible: * @tree_view_frame: an #ETreeViewFrame * @toolbar_visible: whether to make the toolbar visible * * Shows or hides the inline toolbar in @tree_view_frame. **/ void e_tree_view_frame_set_toolbar_visible (ETreeViewFrame *tree_view_frame, gboolean toolbar_visible) { g_return_if_fail (E_IS_TREE_VIEW_FRAME (tree_view_frame)); if (toolbar_visible == tree_view_frame->priv->toolbar_visible) return; tree_view_frame->priv->toolbar_visible = toolbar_visible; g_object_notify (G_OBJECT (tree_view_frame), "toolbar-visible"); } /** * e_tree_view_frame_get_hscrollbar_policy: * @tree_view_frame: an #ETreeViewFrame * * Returns the policy for the horizontal scrollbar in @tree_view_frame. * * Returns: the policy for the horizontal scrollbar **/ GtkPolicyType e_tree_view_frame_get_hscrollbar_policy (ETreeViewFrame *tree_view_frame) { g_return_val_if_fail (E_IS_TREE_VIEW_FRAME (tree_view_frame), 0); return tree_view_frame->priv->hscrollbar_policy; } /** * e_tree_view_frame_set_hscrollbar_policy: * @tree_view_frame: an #ETreeViewFrame * @hscrollbar_policy: the policy for the horizontal scrollbar * * Sets the policy for the horizontal scrollbar in @tree_view_frame. **/ void e_tree_view_frame_set_hscrollbar_policy (ETreeViewFrame *tree_view_frame, GtkPolicyType hscrollbar_policy) { g_return_if_fail (E_IS_TREE_VIEW_FRAME (tree_view_frame)); if (hscrollbar_policy == tree_view_frame->priv->hscrollbar_policy) return; tree_view_frame->priv->hscrollbar_policy = hscrollbar_policy; g_object_notify (G_OBJECT (tree_view_frame), "hscrollbar-policy"); } /** * e_tree_view_frame_get_vscrollbar_policy: * @tree_view_frame: an #ETreeViewFrame * * Returns the policy for the vertical scrollbar in @tree_view_frame. * * Returns: the policy for the vertical scrollbar **/ GtkPolicyType e_tree_view_frame_get_vscrollbar_policy (ETreeViewFrame *tree_view_frame) { g_return_val_if_fail (E_IS_TREE_VIEW_FRAME (tree_view_frame), 0); return tree_view_frame->priv->vscrollbar_policy; } /** * e_tree_view_frame_set_vscrollbar_policy: * @tree_view_frame: an #ETreeViewFrame * @vscrollbar_policy: the policy for the vertical scrollbar * * Sets the policy for the vertical scrollbar in @tree_view_frame. **/ void e_tree_view_frame_set_vscrollbar_policy (ETreeViewFrame *tree_view_frame, GtkPolicyType vscrollbar_policy) { g_return_if_fail (E_IS_TREE_VIEW_FRAME (tree_view_frame)); if (vscrollbar_policy == tree_view_frame->priv->vscrollbar_policy) return; tree_view_frame->priv->vscrollbar_policy = vscrollbar_policy; g_object_notify (G_OBJECT (tree_view_frame), "vscrollbar-policy"); } /** * e_tree_view_frame_insert_toolbar_action: * @tree_view_frame: an #ETreeViewFrame * @action: a #GtkAction * @position: the position of the new action * * Generates a #GtkToolItem from @action and inserts it into the inline * toolbar at the given @position. If @position is zero, the item is * prepended to the start of the toolbar. If @position is negative, * the item is appended to the end of the toolbar. **/ void e_tree_view_frame_insert_toolbar_action (ETreeViewFrame *tree_view_frame, GtkAction *action, gint position) { GtkToolbar *toolbar; GtkWidget *tool_item; GHashTable *tool_item_ht; const gchar *action_name; g_return_if_fail (E_IS_TREE_VIEW_FRAME (tree_view_frame)); g_return_if_fail (GTK_IS_ACTION (action)); action_name = gtk_action_get_name (action); g_return_if_fail (action_name != NULL); tool_item_ht = tree_view_frame->priv->tool_item_ht; toolbar = GTK_TOOLBAR (tree_view_frame->priv->inline_toolbar); if (g_hash_table_contains (tool_item_ht, action_name)) { g_warning ( "%s: Duplicate action name '%s'", G_STRFUNC, action_name); return; } tool_item = gtk_action_create_tool_item (action); g_return_if_fail (GTK_IS_TOOL_ITEM (tool_item)); gtk_toolbar_insert (toolbar, GTK_TOOL_ITEM (tool_item), position); g_hash_table_insert ( tool_item_ht, g_strdup (action_name), g_object_ref (tool_item)); g_signal_connect ( action, "activate", G_CALLBACK (tree_view_frame_action_activate_cb), tree_view_frame); } /** * e_tree_view_frame_lookup_toolbar_action: * @tree_view_frame: an #ETreeViewFrame * @action_name: a #GtkAction name * * Returns the toolbar action named @action_name, or %NULL if no such * toolbar action exists. * * Returns: a #GtkAction, or %NULL **/ GtkAction * e_tree_view_frame_lookup_toolbar_action (ETreeViewFrame *tree_view_frame, const gchar *action_name) { GHashTable *tool_item_ht; GtkActivatable *activatable; GtkAction *action = NULL; g_return_val_if_fail (E_IS_TREE_VIEW_FRAME (tree_view_frame), NULL); g_return_val_if_fail (action_name != NULL, NULL); tool_item_ht = tree_view_frame->priv->tool_item_ht; activatable = g_hash_table_lookup (tool_item_ht, action_name); if (GTK_IS_ACTIVATABLE (activatable)) action = gtk_activatable_get_related_action (activatable); return action; } /** * e_tree_view_frame_update_toolbar_actions: * @tree_view_frame: an #ETreeViewFrame * * Emits the #ETreeViewFrame::update-toolbar-actions signal. * * See the signal description for more details. **/ void e_tree_view_frame_update_toolbar_actions (ETreeViewFrame *tree_view_frame) { g_return_if_fail (E_IS_TREE_VIEW_FRAME (tree_view_frame)); g_signal_emit (tree_view_frame, signals[UPDATE_TOOLBAR_ACTIONS], 0); }