/* Tool Palette * * A tool palette widget shows groups of toolbar items as a grid of icons * or a list of names. */ #include #include static GtkWidget *window = NULL; static void load_icon_items (GtkToolPalette *palette); static void load_toggle_items (GtkToolPalette *palette); static void load_special_items (GtkToolPalette *palette); typedef struct _CanvasItem CanvasItem; struct _CanvasItem { GdkPixbuf *pixbuf; gdouble x, y; }; static gboolean drag_data_requested_for_drop = FALSE; static CanvasItem *drop_item = NULL; static GList *canvas_items = NULL; /********************************/ /* ====== Canvas drawing ====== */ /********************************/ static CanvasItem* canvas_item_new (GtkWidget *widget, GtkToolButton *button, gdouble x, gdouble y) { CanvasItem *item = NULL; const gchar *icon_name; GdkPixbuf *pixbuf; GtkIconTheme *icon_theme; int width; icon_name = gtk_tool_button_get_icon_name (button); icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget)); gtk_icon_size_lookup (GTK_ICON_SIZE_DIALOG, &width, NULL); pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name, width, GTK_ICON_LOOKUP_GENERIC_FALLBACK, NULL); if (pixbuf) { item = g_slice_new0 (CanvasItem); item->pixbuf = pixbuf; item->x = x; item->y = y; } return item; } static void canvas_item_free (CanvasItem *item) { g_object_unref (item->pixbuf); g_slice_free (CanvasItem, item); } static void canvas_item_draw (const CanvasItem *item, cairo_t *cr, gboolean preview) { gdouble cx = gdk_pixbuf_get_width (item->pixbuf); gdouble cy = gdk_pixbuf_get_height (item->pixbuf); gdk_cairo_set_source_pixbuf (cr, item->pixbuf, item->x - cx * 0.5, item->y - cy * 0.5); if (preview) cairo_paint_with_alpha (cr, 0.6); else cairo_paint (cr); } static gboolean canvas_draw (GtkWidget *widget, cairo_t *cr) { GList *iter; cairo_set_source_rgb (cr, 1, 1, 1); cairo_paint (cr); for (iter = canvas_items; iter; iter = iter->next) canvas_item_draw (iter->data, cr, FALSE); if (drop_item) canvas_item_draw (drop_item, cr, TRUE); return TRUE; } /*****************************/ /* ====== Palette DnD ====== */ /*****************************/ static void palette_drop_item (GtkToolItem *drag_item, GtkToolItemGroup *drop_group, gint x, gint y) { GtkWidget *drag_group = gtk_widget_get_parent (GTK_WIDGET (drag_item)); GtkToolItem *drop_item = gtk_tool_item_group_get_drop_item (drop_group, x, y); gint drop_position = -1; if (drop_item) drop_position = gtk_tool_item_group_get_item_position (GTK_TOOL_ITEM_GROUP (drop_group), drop_item); if (GTK_TOOL_ITEM_GROUP (drag_group) != drop_group) { gboolean homogeneous, expand, fill, new_row; g_object_ref (drag_item); gtk_container_child_get (GTK_CONTAINER (drag_group), GTK_WIDGET (drag_item), "homogeneous", &homogeneous, "expand", &expand, "fill", &fill, "new-row", &new_row, NULL); gtk_container_remove (GTK_CONTAINER (drag_group), GTK_WIDGET (drag_item)); gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (drop_group), drag_item, drop_position); gtk_container_child_set (GTK_CONTAINER (drop_group), GTK_WIDGET (drag_item), "homogeneous", homogeneous, "expand", expand, "fill", fill, "new-row", new_row, NULL); g_object_unref (drag_item); } else gtk_tool_item_group_set_item_position (GTK_TOOL_ITEM_GROUP (drop_group), drag_item, drop_position); } static void palette_drop_group (GtkToolPalette *palette, GtkToolItemGroup *drag_group, GtkToolItemGroup *drop_group) { gint drop_position = -1; if (drop_group) drop_position = gtk_tool_palette_get_group_position (palette, drop_group); gtk_tool_palette_set_group_position (palette, drag_group, drop_position); } static void palette_drag_data_received (GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *selection, guint info, guint time, gpointer data) { GtkAllocation allocation; GtkToolItemGroup *drop_group = NULL; GtkWidget *drag_palette = gtk_drag_get_source_widget (context); GtkWidget *drag_item = NULL; while (drag_palette && !GTK_IS_TOOL_PALETTE (drag_palette)) drag_palette = gtk_widget_get_parent (drag_palette); if (drag_palette) { drag_item = gtk_tool_palette_get_drag_item (GTK_TOOL_PALETTE (drag_palette), selection); drop_group = gtk_tool_palette_get_drop_group (GTK_TOOL_PALETTE (widget), x, y); } if (GTK_IS_TOOL_ITEM_GROUP (drag_item)) palette_drop_group (GTK_TOOL_PALETTE (drag_palette), GTK_TOOL_ITEM_GROUP (drag_item), drop_group); else if (GTK_IS_TOOL_ITEM (drag_item) && drop_group) { gtk_widget_get_allocation (GTK_WIDGET (drop_group), &allocation); palette_drop_item (GTK_TOOL_ITEM (drag_item), drop_group, x - allocation.x, y - allocation.y); } } /********************************/ /* ====== Passive Canvas ====== */ /********************************/ static void passive_canvas_drag_data_received (GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *selection, guint info, guint time, gpointer data) { /* find the tool button, which is the source of this DnD operation */ GtkWidget *palette = gtk_drag_get_source_widget (context); CanvasItem *canvas_item = NULL; GtkWidget *tool_item = NULL; while (palette && !GTK_IS_TOOL_PALETTE (palette)) palette = gtk_widget_get_parent (palette); if (palette) tool_item = gtk_tool_palette_get_drag_item (GTK_TOOL_PALETTE (palette), selection); g_assert (NULL == drop_item); /* append a new canvas item when a tool button was found */ if (GTK_IS_TOOL_ITEM (tool_item)) canvas_item = canvas_item_new (widget, GTK_TOOL_BUTTON (tool_item), x, y); if (canvas_item) { canvas_items = g_list_append (canvas_items, canvas_item); gtk_widget_queue_draw (widget); } } /************************************/ /* ====== Interactive Canvas ====== */ /************************************/ static gboolean interactive_canvas_drag_motion (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, gpointer data) { if (drop_item) { /* already have a drop indicator - just update position */ drop_item->x = x; drop_item->y = y; gtk_widget_queue_draw (widget); gdk_drag_status (context, GDK_ACTION_COPY, time); } else { /* request DnD data for creating a drop indicator */ GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL); if (!target) return FALSE; drag_data_requested_for_drop = FALSE; gtk_drag_get_data (widget, context, target, time); } return TRUE; } static void interactive_canvas_drag_data_received (GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *selection, guint info, guint time, gpointer data) { /* find the tool button which is the source of this DnD operation */ GtkWidget *palette = gtk_drag_get_source_widget (context); GtkWidget *tool_item = NULL; CanvasItem *item; while (palette && !GTK_IS_TOOL_PALETTE (palette)) palette = gtk_widget_get_parent (palette); if (palette) tool_item = gtk_tool_palette_get_drag_item (GTK_TOOL_PALETTE (palette), selection); /* create a canvas item when a tool button was found */ g_assert (NULL == drop_item); if (!GTK_IS_TOOL_ITEM (tool_item)) return; if (drop_item) { canvas_item_free (drop_item); drop_item = NULL; } item = canvas_item_new (widget, GTK_TOOL_BUTTON (tool_item), x, y); /* Either create a new item or just create a preview item, depending on why the drag data was requested. */ if(drag_data_requested_for_drop) { canvas_items = g_list_append (canvas_items, item); drop_item = NULL; gtk_drag_finish (context, TRUE, FALSE, time); } else { drop_item = item; gdk_drag_status (context, GDK_ACTION_COPY, time); } gtk_widget_queue_draw (widget); } static gboolean interactive_canvas_drag_drop (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, gpointer data) { GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL); if (!target) return FALSE; drag_data_requested_for_drop = TRUE; gtk_drag_get_data (widget, context, target, time); return FALSE; } static void interactive_canvas_drag_leave (gpointer data) { if (drop_item) { GtkWidget *widget = GTK_WIDGET (data); canvas_item_free (drop_item); drop_item = NULL; if (widget) gtk_widget_queue_draw (widget); } } static void on_combo_orientation_changed (GtkComboBox *combo_box, gpointer user_data) { GtkToolPalette *palette = GTK_TOOL_PALETTE (user_data); GtkScrolledWindow *sw; GtkTreeModel *model = gtk_combo_box_get_model (combo_box); GtkTreeIter iter; gint val = 0; sw = GTK_SCROLLED_WINDOW (gtk_widget_get_parent (GTK_WIDGET (palette))); if (!gtk_combo_box_get_active_iter (combo_box, &iter)) return; gtk_tree_model_get (model, &iter, 1, &val, -1); gtk_orientable_set_orientation (GTK_ORIENTABLE (palette), val); if (val == GTK_ORIENTATION_HORIZONTAL) gtk_scrolled_window_set_policy (sw, GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); else gtk_scrolled_window_set_policy (sw, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); } static void on_combo_style_changed (GtkComboBox *combo_box, gpointer user_data) { GtkToolPalette *palette = GTK_TOOL_PALETTE (user_data); GtkTreeModel *model = gtk_combo_box_get_model (combo_box); GtkTreeIter iter; gint val = 0; if (!gtk_combo_box_get_active_iter (combo_box, &iter)) return; gtk_tree_model_get (model, &iter, 1, &val, -1); if (val == -1) gtk_tool_palette_unset_style (palette); else gtk_tool_palette_set_style (palette, val); } GtkWidget * do_toolpalette (GtkWidget *do_widget) { GtkWidget *box = NULL; GtkWidget *hbox = NULL; GtkWidget *combo_orientation = NULL; GtkListStore *orientation_model = NULL; GtkWidget *combo_style = NULL; GtkListStore *style_model = NULL; GtkCellRenderer *cell_renderer = NULL; GtkTreeIter iter; GtkWidget *palette = NULL; GtkWidget *palette_scroller = NULL; GtkWidget *notebook = NULL; GtkWidget *contents = NULL; GtkWidget *contents_scroller = NULL; if (!window) { window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_screen (GTK_WINDOW (window), gtk_widget_get_screen (do_widget)); gtk_window_set_title (GTK_WINDOW (window), "Tool Palette"); gtk_window_set_default_size (GTK_WINDOW (window), 200, 600); g_signal_connect (window, "destroy", G_CALLBACK (gtk_widget_destroyed), &window); /* Add widgets to control the ToolPalette appearance: */ box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); g_object_set (box, "margin", 6, NULL); gtk_container_add (GTK_CONTAINER (window), box); /* Orientation combo box: */ orientation_model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT); gtk_list_store_append (orientation_model, &iter); gtk_list_store_set (orientation_model, &iter, 0, "Horizontal", 1, GTK_ORIENTATION_HORIZONTAL, -1); gtk_list_store_append (orientation_model, &iter); gtk_list_store_set (orientation_model, &iter, 0, "Vertical", 1, GTK_ORIENTATION_VERTICAL, -1); combo_orientation = gtk_combo_box_new_with_model (GTK_TREE_MODEL (orientation_model)); cell_renderer = gtk_cell_renderer_text_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_orientation), cell_renderer, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_orientation), cell_renderer, "text", 0, NULL); gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_orientation), &iter); gtk_box_pack_start (GTK_BOX (box), combo_orientation); /* Style combo box: */ style_model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT); gtk_list_store_append (style_model, &iter); gtk_list_store_set (style_model, &iter, 0, "Text", 1, GTK_TOOLBAR_TEXT, -1); gtk_list_store_append (style_model, &iter); gtk_list_store_set (style_model, &iter, 0, "Both", 1, GTK_TOOLBAR_BOTH, -1); gtk_list_store_append (style_model, &iter); gtk_list_store_set (style_model, &iter, 0, "Both: Horizontal", 1, GTK_TOOLBAR_BOTH_HORIZ, -1); gtk_list_store_append (style_model, &iter); gtk_list_store_set (style_model, &iter, 0, "Icons", 1, GTK_TOOLBAR_ICONS, -1); gtk_list_store_append (style_model, &iter); gtk_list_store_set (style_model, &iter, 0, "Default", 1, -1, /* A custom meaning for this demo. */ -1); combo_style = gtk_combo_box_new_with_model (GTK_TREE_MODEL (style_model)); cell_renderer = gtk_cell_renderer_text_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_style), cell_renderer, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_style), cell_renderer, "text", 0, NULL); gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_style), &iter); gtk_box_pack_start (GTK_BOX (box), combo_style); /* Add hbox */ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); gtk_box_pack_start (GTK_BOX (box), hbox); /* Add and fill the ToolPalette: */ palette = gtk_tool_palette_new (); load_icon_items (GTK_TOOL_PALETTE (palette)); load_toggle_items (GTK_TOOL_PALETTE (palette)); load_special_items (GTK_TOOL_PALETTE (palette)); palette_scroller = gtk_scrolled_window_new (NULL, NULL); gtk_widget_set_vexpand (palette_scroller, TRUE); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (palette_scroller), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); g_object_set (palette_scroller, "margin", 6, NULL); gtk_widget_set_hexpand (palette_scroller, TRUE); gtk_container_add (GTK_CONTAINER (palette_scroller), palette); gtk_container_add (GTK_CONTAINER (hbox), palette_scroller); /* Connect signals: */ g_signal_connect (combo_orientation, "changed", G_CALLBACK (on_combo_orientation_changed), palette); g_signal_connect (combo_style, "changed", G_CALLBACK (on_combo_style_changed), palette); /* Keep the widgets in sync: */ on_combo_orientation_changed (GTK_COMBO_BOX (combo_orientation), palette); /* ===== notebook ===== */ notebook = gtk_notebook_new (); g_object_set (notebook, "margin", 6, NULL); gtk_box_pack_end (GTK_BOX(hbox), notebook); /* ===== DnD for tool items ===== */ g_signal_connect (palette, "drag-data-received", G_CALLBACK (palette_drag_data_received), NULL); gtk_tool_palette_add_drag_dest (GTK_TOOL_PALETTE (palette), palette, GTK_DEST_DEFAULT_ALL, GTK_TOOL_PALETTE_DRAG_ITEMS | GTK_TOOL_PALETTE_DRAG_GROUPS, GDK_ACTION_MOVE); /* ===== passive DnD dest ===== */ contents = gtk_drawing_area_new (); g_object_connect (contents, "signal::draw", canvas_draw, NULL, "signal::drag-data-received", passive_canvas_drag_data_received, NULL, NULL); gtk_tool_palette_add_drag_dest (GTK_TOOL_PALETTE (palette), contents, GTK_DEST_DEFAULT_ALL, GTK_TOOL_PALETTE_DRAG_ITEMS, GDK_ACTION_COPY); contents_scroller = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (contents_scroller), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); gtk_container_add (GTK_CONTAINER (contents_scroller), contents); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), contents_scroller, gtk_label_new ("Passive DnD Mode")); g_object_set (contents_scroller, "margin", 6, NULL); /* ===== interactive DnD dest ===== */ contents = gtk_drawing_area_new (); g_object_connect (contents, "signal::draw", canvas_draw, NULL, "signal::drag-motion", interactive_canvas_drag_motion, NULL, "signal::drag-data-received", interactive_canvas_drag_data_received, NULL, "signal::drag-leave", interactive_canvas_drag_leave, contents, "signal::drag-drop", interactive_canvas_drag_drop, NULL, NULL); gtk_tool_palette_add_drag_dest (GTK_TOOL_PALETTE (palette), contents, GTK_DEST_DEFAULT_HIGHLIGHT, GTK_TOOL_PALETTE_DRAG_ITEMS, GDK_ACTION_COPY); contents_scroller = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (contents_scroller), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); gtk_container_add (GTK_CONTAINER (contents_scroller), contents); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), contents_scroller, gtk_label_new ("Interactive DnD Mode")); } if (!gtk_widget_get_visible (window)) { gtk_widget_show (window); } else { gtk_widget_destroy (window); window = NULL; } return window; } static void load_icon_items (GtkToolPalette *palette) { GList *contexts; GList *l; GtkIconTheme *icon_theme; icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (palette))); contexts = gtk_icon_theme_list_contexts (icon_theme); for (l = contexts; l; l = l->next) { gchar *context = l->data; GList *icon_names; GList *ll; const guint max_icons = 10; guint icons_count = 0; GtkWidget *group = gtk_tool_item_group_new (context); gtk_container_add (GTK_CONTAINER (palette), group); if (g_strcmp0 (context, "Animations") == 0) continue; g_message ("Got context '%s'", context); icon_names = gtk_icon_theme_list_icons (icon_theme, context); icon_names = g_list_sort (icon_names, (GCompareFunc) strcmp); for (ll = icon_names; ll; ll = ll->next) { GtkToolItem *item; gchar *id = ll->data; if (g_str_equal (id, "emblem-desktop")) continue; if (g_str_has_suffix (id, "-symbolic")) continue; g_message ("Got id '%s'", id); item = gtk_tool_button_new (NULL, NULL); gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (item), id); gtk_tool_item_set_tooltip_text (GTK_TOOL_ITEM (item), id); gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (group), item, -1); /* Prevent us having an insane number of icons: */ ++icons_count; if(icons_count >= max_icons) break; } g_list_free_full (icon_names, g_free); } g_list_free_full (contexts, g_free); } static void load_toggle_items (GtkToolPalette *palette) { GSList *toggle_group = NULL; GtkToolItem *item; GtkWidget *group; char *label; int i; group = gtk_tool_item_group_new ("Radio Item"); gtk_container_add (GTK_CONTAINER (palette), group); for (i = 1; i <= 10; ++i) { label = g_strdup_printf ("#%d", i); item = gtk_radio_tool_button_new (toggle_group); gtk_tool_button_set_label (GTK_TOOL_BUTTON (item), label); g_free (label); gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (group), item, -1); toggle_group = gtk_radio_tool_button_get_group (GTK_RADIO_TOOL_BUTTON (item)); } } static GtkToolItem * create_entry_item (const char *text) { GtkToolItem *item; GtkWidget *entry; entry = gtk_entry_new (); gtk_entry_set_text (GTK_ENTRY (entry), text); gtk_entry_set_width_chars (GTK_ENTRY (entry), 5); item = gtk_tool_item_new (); gtk_container_add (GTK_CONTAINER (item), entry); return item; } static void load_special_items (GtkToolPalette *palette) { GtkToolItem *item; GtkWidget *group; GtkWidget *label_button; group = gtk_tool_item_group_new (NULL); label_button = gtk_button_new_with_label ("Advanced Features"); gtk_widget_show (label_button); gtk_tool_item_group_set_label_widget (GTK_TOOL_ITEM_GROUP (group), label_button); gtk_container_add (GTK_CONTAINER (palette), group); item = create_entry_item ("homogeneous=FALSE"); gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (group), item, -1); gtk_container_child_set (GTK_CONTAINER (group), GTK_WIDGET (item), "homogeneous", FALSE, NULL); item = create_entry_item ("homogeneous=FALSE, expand=TRUE"); gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (group), item, -1); gtk_container_child_set (GTK_CONTAINER (group), GTK_WIDGET (item), "homogeneous", FALSE, "expand", TRUE, NULL); item = create_entry_item ("homogeneous=FALSE, expand=TRUE, fill=FALSE"); gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (group), item, -1); gtk_container_child_set (GTK_CONTAINER (group), GTK_WIDGET (item), "homogeneous", FALSE, "expand", TRUE, "fill", FALSE, NULL); item = create_entry_item ("homogeneous=FALSE, expand=TRUE, new-row=TRUE"); gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (group), item, -1); gtk_container_child_set (GTK_CONTAINER (group), GTK_WIDGET (item), "homogeneous", FALSE, "expand", TRUE, "new-row", TRUE, NULL); item = gtk_tool_button_new (NULL, NULL); gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (item), "go-up"); gtk_tool_item_set_tooltip_text (item, "Show on vertical palettes only"); gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (group), item, -1); gtk_tool_item_set_visible_horizontal (item, FALSE); item = gtk_tool_button_new (NULL, NULL); gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (item), "go-next"); gtk_tool_item_set_tooltip_text (item, "Show on horizontal palettes only"); gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (group), item, -1); gtk_tool_item_set_visible_vertical (item, FALSE); item = gtk_tool_button_new (NULL, NULL); gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (item), "edit-delete"); gtk_tool_item_set_tooltip_text (item, "Do not show at all"); gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (group), item, -1); gtk_widget_hide (GTK_WIDGET (item)); item = gtk_tool_button_new (NULL, NULL); gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (item), "view-fullscreen"); gtk_tool_item_set_tooltip_text (item, "Expanded this item"); gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (group), item, -1); gtk_container_child_set (GTK_CONTAINER (group), GTK_WIDGET (item), "homogeneous", FALSE, "expand", TRUE, NULL); item = gtk_tool_button_new (NULL, NULL); gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (item), "help-browser"); gtk_tool_item_set_tooltip_text (item, "A regular item"); gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (group), item, -1); }