1 // SPDX-License-Identifier: GPL-2.0-or-later
2 
3 /**
4  * @file
5  * Inkscape toolbar definitions and general utility functions.
6  * Each tool should have its own xxx-toolbar implementation file
7  */
8 /* Authors:
9  *   MenTaLguY <mental@rydia.net>
10  *   Lauris Kaplinski <lauris@kaplinski.com>
11  *   bulia byak <buliabyak@users.sf.net>
12  *   Frank Felfe <innerspace@iname.com>
13  *   John Cliff <simarilius@yahoo.com>
14  *   David Turner <novalis@gnu.org>
15  *   Josh Andler <scislac@scislac.com>
16  *   Jon A. Cruz <jon@joncruz.org>
17  *   Maximilian Albert <maximilian.albert@gmail.com>
18  *   Tavmjong Bah <tavmjong@free.fr>
19  *   Abhishek Sharma
20  *   Kris De Gussem <Kris.DeGussem@gmail.com>
21  *   Jabiertxo Arraiza <jabier.arraiza@marker.es>
22  *
23  * Copyright (C) 2004 David Turner
24  * Copyright (C) 2003 MenTaLguY
25  * Copyright (C) 1999-2015 authors
26  * Copyright (C) 2001-2002 Ximian, Inc.
27  *
28  * Released under GNU GPL v2+, read the file 'COPYING' for more information.
29  */
30 
31 #include <gtkmm.h>
32 #include <glibmm/i18n.h>
33 
34 #include "desktop-style.h"
35 #include "desktop.h"
36 #include "document-undo.h"
37 #include "inkscape.h"
38 #include "verbs.h"
39 
40 #include "ink-action.h"
41 
42 #include "helper/action.h"
43 #include "helper/verb-action.h"
44 
45 #include "include/gtkmm_version.h"
46 
47 #include "io/resource.h"
48 
49 #include "object/sp-namedview.h"
50 
51 #include "ui/icon-names.h"
52 #include "ui/tools-switch.h"
53 #include "ui/uxmanager.h"
54 #include "ui/widget/button.h"
55 #include "ui/widget/spinbutton.h"
56 #include "ui/widget/style-swatch.h"
57 #include "ui/widget/unit-tracker.h"
58 
59 #include "widgets/spw-utilities.h"
60 #include "widgets/widget-sizes.h"
61 
62 #include "xml/attribute-record.h"
63 #include "xml/node-event-vector.h"
64 
65 #include "ui/toolbar/arc-toolbar.h"
66 #include "ui/toolbar/box3d-toolbar.h"
67 #include "ui/toolbar/calligraphy-toolbar.h"
68 #include "ui/toolbar/connector-toolbar.h"
69 #include "ui/toolbar/dropper-toolbar.h"
70 #include "ui/toolbar/eraser-toolbar.h"
71 #include "ui/toolbar/gradient-toolbar.h"
72 #include "ui/toolbar/lpe-toolbar.h"
73 #include "ui/toolbar/mesh-toolbar.h"
74 #include "ui/toolbar/measure-toolbar.h"
75 #include "ui/toolbar/node-toolbar.h"
76 #include "ui/toolbar/rect-toolbar.h"
77 #include "ui/toolbar/paintbucket-toolbar.h"
78 #include "ui/toolbar/pencil-toolbar.h"
79 #include "ui/toolbar/select-toolbar.h"
80 //#include "ui/toolbar/snap-toolbar.h"
81 #include "ui/toolbar/spray-toolbar.h"
82 #include "ui/toolbar/spiral-toolbar.h"
83 #include "ui/toolbar/star-toolbar.h"
84 #include "ui/toolbar/tweak-toolbar.h"
85 #include "ui/toolbar/text-toolbar.h"
86 #include "ui/toolbar/zoom-toolbar.h"
87 
88 #include "toolbox.h"
89 
90 #include "ui/tools/tool-base.h"
91 
92 //#define DEBUG_TEXT
93 
94 using Inkscape::UI::UXManager;
95 using Inkscape::DocumentUndo;
96 using Inkscape::UI::ToolboxFactory;
97 using Inkscape::UI::Tools::ToolBase;
98 
99 using Inkscape::IO::Resource::get_filename;
100 using Inkscape::IO::Resource::UIS;
101 
102 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
103 typedef void (*UpdateFunction)(SPDesktop *desktop, ToolBase *eventcontext, GtkWidget *toolbox);
104 
105 enum BarId {
106     BAR_TOOL = 0,
107     BAR_AUX,
108     BAR_COMMANDS,
109     BAR_SNAP,
110 };
111 
112 #define BAR_ID_KEY "BarIdValue"
113 #define HANDLE_POS_MARK "x-inkscape-pos"
114 
prefToSize(Glib::ustring const & path,int base)115 GtkIconSize ToolboxFactory::prefToSize( Glib::ustring const &path, int base ) {
116     static GtkIconSize sizeChoices[] = {
117         GTK_ICON_SIZE_LARGE_TOOLBAR,
118         GTK_ICON_SIZE_SMALL_TOOLBAR,
119         GTK_ICON_SIZE_DND,
120         GTK_ICON_SIZE_DIALOG
121     };
122     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
123     int index = prefs->getIntLimited( path, base, 0, G_N_ELEMENTS(sizeChoices) );
124     return sizeChoices[index];
125 }
126 
prefToSize_mm(Glib::ustring const & path,int base)127 Gtk::IconSize ToolboxFactory::prefToSize_mm(Glib::ustring const &path, int base)
128 {
129     static Gtk::IconSize sizeChoices[] = { Gtk::ICON_SIZE_LARGE_TOOLBAR, Gtk::ICON_SIZE_SMALL_TOOLBAR,
130                                            Gtk::ICON_SIZE_DND, Gtk::ICON_SIZE_DIALOG };
131     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
132     int index = prefs->getIntLimited(path, base, 0, G_N_ELEMENTS(sizeChoices));
133     return sizeChoices[index];
134 }
135 
136 static struct {
137     gchar const *type_name;
138     gchar const *data_name;
139     sp_verb_t verb;
140     sp_verb_t doubleclick_verb;
141 } const tools[] = {
142     // clang-format off
143     { "/tools/select",           "select_tool",       SP_VERB_CONTEXT_SELECT,        SP_VERB_CONTEXT_SELECT_PREFS        },
144     { "/tools/nodes",            "node_tool",         SP_VERB_CONTEXT_NODE,          SP_VERB_CONTEXT_NODE_PREFS          },
145     { "/tools/tweak",            "tweak_tool",        SP_VERB_CONTEXT_TWEAK,         SP_VERB_CONTEXT_TWEAK_PREFS         },
146     { "/tools/spray",            "spray_tool",        SP_VERB_CONTEXT_SPRAY,         SP_VERB_CONTEXT_SPRAY_PREFS         },
147     { "/tools/zoom",             "zoom_tool",         SP_VERB_CONTEXT_ZOOM,          SP_VERB_CONTEXT_ZOOM_PREFS          },
148     { "/tools/measure",          "measure_tool",      SP_VERB_CONTEXT_MEASURE,       SP_VERB_CONTEXT_MEASURE_PREFS       },
149     { "/tools/shapes/rect",      "rect_tool",         SP_VERB_CONTEXT_RECT,          SP_VERB_CONTEXT_RECT_PREFS          },
150     { "/tools/shapes/3dbox",     "3dbox_tool",        SP_VERB_CONTEXT_3DBOX,         SP_VERB_CONTEXT_3DBOX_PREFS         },
151     { "/tools/shapes/arc",       "arc_tool",          SP_VERB_CONTEXT_ARC,           SP_VERB_CONTEXT_ARC_PREFS           },
152     { "/tools/shapes/star",      "star_tool",         SP_VERB_CONTEXT_STAR,          SP_VERB_CONTEXT_STAR_PREFS          },
153     { "/tools/shapes/spiral",    "spiral_tool",       SP_VERB_CONTEXT_SPIRAL,        SP_VERB_CONTEXT_SPIRAL_PREFS        },
154     { "/tools/freehand/pencil",  "pencil_tool",       SP_VERB_CONTEXT_PENCIL,        SP_VERB_CONTEXT_PENCIL_PREFS        },
155     { "/tools/freehand/pen",     "pen_tool",          SP_VERB_CONTEXT_PEN,           SP_VERB_CONTEXT_PEN_PREFS           },
156     { "/tools/calligraphic",     "dyna_draw_tool",    SP_VERB_CONTEXT_CALLIGRAPHIC,  SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS  },
157     { "/tools/lpetool",          "lpetool_tool",      SP_VERB_CONTEXT_LPETOOL,       SP_VERB_CONTEXT_LPETOOL_PREFS       },
158     { "/tools/eraser",           "eraser_tool",       SP_VERB_CONTEXT_ERASER,        SP_VERB_CONTEXT_ERASER_PREFS        },
159     { "/tools/paintbucket",      "paintbucket_tool",  SP_VERB_CONTEXT_PAINTBUCKET,   SP_VERB_CONTEXT_PAINTBUCKET_PREFS   },
160     { "/tools/text",             "text_tool",         SP_VERB_CONTEXT_TEXT,          SP_VERB_CONTEXT_TEXT_PREFS          },
161     { "/tools/connector",        "connector_tool",    SP_VERB_CONTEXT_CONNECTOR,     SP_VERB_CONTEXT_CONNECTOR_PREFS     },
162     { "/tools/gradient",         "gradient_tool",     SP_VERB_CONTEXT_GRADIENT,      SP_VERB_CONTEXT_GRADIENT_PREFS      },
163     { "/tools/mesh",             "mesh_tool",         SP_VERB_CONTEXT_MESH,          SP_VERB_CONTEXT_MESH_PREFS          },
164     { "/tools/dropper",          "dropper_tool",      SP_VERB_CONTEXT_DROPPER,       SP_VERB_CONTEXT_DROPPER_PREFS       },
165     { nullptr,                   nullptr,             0,                             0,                                  },
166     // clang-format on
167 };
168 
169 static struct {
170     gchar const *type_name;
171     gchar const *data_name;
172     GtkWidget *(*create_func)(SPDesktop *desktop);
173     gchar const *ui_name;
174     gint swatch_verb_id;
175     gchar const *swatch_tool;
176     gchar const *swatch_tip;
177 } const aux_toolboxes[] = {
178     // clang-format off
179     { "/tools/select",          "select_toolbox",      Inkscape::UI::Toolbar::SelectToolbar::create,        "SelectToolbar",
180       SP_VERB_INVALID,                    nullptr,                  nullptr},
181     { "/tools/nodes",           "node_toolbox",        Inkscape::UI::Toolbar::NodeToolbar::create,          "NodeToolbar",
182       SP_VERB_INVALID,                    nullptr,                  nullptr},
183     { "/tools/tweak",           "tweak_toolbox",       Inkscape::UI::Toolbar::TweakToolbar::create,         "TweakToolbar",
184       SP_VERB_CONTEXT_TWEAK_PREFS,        "/tools/tweak",           N_("Color/opacity used for color tweaking")},
185     { "/tools/spray",           "spray_toolbox",       Inkscape::UI::Toolbar::SprayToolbar::create,         "SprayToolbar",
186       SP_VERB_INVALID,                    nullptr,                  nullptr},
187     { "/tools/zoom",            "zoom_toolbox",        Inkscape::UI::Toolbar::ZoomToolbar::create,          "ZoomToolbar",
188       SP_VERB_INVALID,                    nullptr,                  nullptr},
189     // If you change MeasureToolbar here, change it also in desktop-widget.cpp
190     { "/tools/measure",         "measure_toolbox",     Inkscape::UI::Toolbar::MeasureToolbar::create,       "MeasureToolbar",
191       SP_VERB_INVALID,                    nullptr,                  nullptr},
192     { "/tools/shapes/star",     "star_toolbox",        Inkscape::UI::Toolbar::StarToolbar::create,          "StarToolbar",
193       SP_VERB_CONTEXT_STAR_PREFS,         "/tools/shapes/star",     N_("Style of new stars")},
194     { "/tools/shapes/rect",     "rect_toolbox",        Inkscape::UI::Toolbar::RectToolbar::create,          "RectToolbar",
195       SP_VERB_CONTEXT_RECT_PREFS,         "/tools/shapes/rect",     N_("Style of new rectangles")},
196     { "/tools/shapes/3dbox",    "3dbox_toolbox",       Inkscape::UI::Toolbar::Box3DToolbar::create,         "3DBoxToolbar",
197       SP_VERB_CONTEXT_3DBOX_PREFS,        "/tools/shapes/3dbox",    N_("Style of new 3D boxes")},
198     { "/tools/shapes/arc",      "arc_toolbox",         Inkscape::UI::Toolbar::ArcToolbar::create,           "ArcToolbar",
199       SP_VERB_CONTEXT_ARC_PREFS,          "/tools/shapes/arc",      N_("Style of new ellipses")},
200     { "/tools/shapes/spiral",   "spiral_toolbox",      Inkscape::UI::Toolbar::SpiralToolbar::create,        "SpiralToolbar",
201       SP_VERB_CONTEXT_SPIRAL_PREFS,       "/tools/shapes/spiral",   N_("Style of new spirals")},
202     { "/tools/freehand/pencil", "pencil_toolbox",      Inkscape::UI::Toolbar::PencilToolbar::create_pencil, "PencilToolbar",
203       SP_VERB_CONTEXT_PENCIL_PREFS,       "/tools/freehand/pencil", N_("Style of new paths created by Pencil")},
204     { "/tools/freehand/pen",    "pen_toolbox",         Inkscape::UI::Toolbar::PencilToolbar::create_pen,    "PenToolbar",
205       SP_VERB_CONTEXT_PEN_PREFS,          "/tools/freehand/pen",    N_("Style of new paths created by Pen")},
206     { "/tools/calligraphic",    "calligraphy_toolbox", Inkscape::UI::Toolbar::CalligraphyToolbar::create,   "CalligraphyToolbar",
207       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "/tools/calligraphic",    N_("Style of new calligraphic strokes")},
208     { "/tools/eraser",          "eraser_toolbox",      Inkscape::UI::Toolbar::EraserToolbar::create,        "EraserToolbar",
209       SP_VERB_CONTEXT_ERASER_PREFS,       "/tools/eraser",           _("TBD")},
210     { "/tools/lpetool",         "lpetool_toolbox",     Inkscape::UI::Toolbar::LPEToolbar::create,           "LPEToolToolbar",
211       SP_VERB_CONTEXT_LPETOOL_PREFS,      "/tools/lpetool",          _("TBD")},
212     // If you change TextToolbar here, change it also in desktop-widget.cpp
213     { "/tools/text",            "text_toolbox",        Inkscape::UI::Toolbar::TextToolbar::create,          "TextToolbar",
214       SP_VERB_INVALID,                    nullptr,                   nullptr},
215     { "/tools/dropper",         "dropper_toolbox",     Inkscape::UI::Toolbar::DropperToolbar::create,       "DropperToolbar",
216       SP_VERB_INVALID,                    nullptr,                   nullptr},
217     { "/tools/connector",       "connector_toolbox",   Inkscape::UI::Toolbar::ConnectorToolbar::create,     "ConnectorToolbar",
218       SP_VERB_INVALID,                    nullptr,                   nullptr},
219     { "/tools/gradient",        "gradient_toolbox",    Inkscape::UI::Toolbar::GradientToolbar::create,      "GradientToolbar",
220       SP_VERB_INVALID,                    nullptr,                   nullptr},
221     { "/tools/mesh",            "mesh_toolbox",        Inkscape::UI::Toolbar::MeshToolbar::create,          "MeshToolbar",
222       SP_VERB_INVALID,                    nullptr,                   nullptr},
223     { "/tools/paintbucket",     "paintbucket_toolbox", Inkscape::UI::Toolbar::PaintbucketToolbar::create,   "PaintbucketToolbar",
224       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "/tools/paintbucket",       N_("Style of Paint Bucket fill objects")},
225     { nullptr,                  nullptr,               nullptr,                                             nullptr,
226         SP_VERB_INVALID,                 nullptr,                    nullptr }
227     // clang-format on
228 };
229 
230 
231 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
232 
233 static void setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop);
234 static void update_tool_toolbox(SPDesktop *desktop, ToolBase *eventcontext, GtkWidget *toolbox);
235 
236 static void setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop);
237 static void update_aux_toolbox(SPDesktop *desktop, ToolBase *eventcontext, GtkWidget *toolbox);
238 
239 static void setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop);
240 static void update_commands_toolbox(SPDesktop *desktop, ToolBase *eventcontext, GtkWidget *toolbox);
241 
trigger_sp_action(GtkAction *,gpointer user_data)242 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
243 {
244     SPAction* targetAction = SP_ACTION(user_data);
245     if ( targetAction ) {
246         sp_action_perform( targetAction, nullptr );
247     }
248 }
249 
create_action_for_verb(Inkscape::Verb * verb,Inkscape::UI::View::View * view,GtkIconSize size)250 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, GtkIconSize size )
251 {
252     GtkAction* act = nullptr;
253 
254     SPAction* targetAction = verb->get_action(Inkscape::ActionContext(view));
255     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
256     act = GTK_ACTION(inky);
257     gtk_action_set_sensitive( act, targetAction->sensitive );
258 
259     g_signal_connect( G_OBJECT(inky), "activate", G_CALLBACK(trigger_sp_action), targetAction );
260 
261     // FIXME: memory leak: this is not unrefed anywhere
262     g_object_ref(G_OBJECT(targetAction));
263     g_object_set_data_full(G_OBJECT(inky), "SPAction", (void*) targetAction, (GDestroyNotify) &g_object_unref);
264     targetAction->signal_set_sensitive.connect(
265         sigc::bind<0>(
266             sigc::ptr_fun(&gtk_action_set_sensitive),
267             GTK_ACTION(inky)));
268 
269     return act;
270 }
271 
272 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
273 
desktopDestructHandler(SPDesktop * desktop)274 static void desktopDestructHandler(SPDesktop *desktop)
275 {
276     std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> >::iterator it = groups.find(desktop);
277     if (it != groups.end())
278     {
279         groups.erase(it);
280     }
281 }
282 
create_or_fetch_actions(SPDesktop * desktop)283 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
284 {
285     Inkscape::UI::View::View *view = desktop;
286     gint verbsToUse[] = {
287         // disabled until we have icons for them:
288         //find
289         //SP_VERB_EDIT_TILE,
290         //SP_VERB_EDIT_UNTILE,
291         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
292         SP_VERB_DIALOG_PREFERENCES,
293         SP_VERB_DIALOG_FILL_STROKE,
294         SP_VERB_DIALOG_DOCPROPERTIES,
295         SP_VERB_DIALOG_TEXT,
296         SP_VERB_DIALOG_XML_EDITOR,
297         SP_VERB_DIALOG_SELECTORS,
298         SP_VERB_DIALOG_LAYERS,
299         SP_VERB_EDIT_CLONE,
300         SP_VERB_EDIT_COPY,
301         SP_VERB_EDIT_CUT,
302         SP_VERB_EDIT_DUPLICATE,
303         SP_VERB_EDIT_PASTE,
304         SP_VERB_EDIT_REDO,
305         SP_VERB_EDIT_UNDO,
306         SP_VERB_EDIT_UNLINK_CLONE,
307         //SP_VERB_FILE_EXPORT,
308         SP_VERB_DIALOG_EXPORT,
309         SP_VERB_FILE_IMPORT,
310         SP_VERB_FILE_NEW,
311         SP_VERB_FILE_OPEN,
312         SP_VERB_FILE_PRINT,
313         SP_VERB_FILE_SAVE,
314         SP_VERB_OBJECT_TO_CURVE,
315         SP_VERB_SELECTION_GROUP,
316         SP_VERB_SELECTION_OUTLINE,
317         SP_VERB_SELECTION_UNGROUP,
318     };
319 
320     GtkIconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small");
321     Glib::RefPtr<Gtk::ActionGroup> mainActions;
322     if (desktop == nullptr)
323     {
324         return mainActions;
325     }
326 
327     if ( groups.find(desktop) != groups.end() ) {
328         mainActions = groups[desktop];
329     }
330 
331     if ( !mainActions ) {
332         mainActions = Gtk::ActionGroup::create("main");
333         groups[desktop] = mainActions;
334         desktop->connectDestroy(&desktopDestructHandler);
335     }
336 
337     for (int i : verbsToUse) {
338         Inkscape::Verb* verb = Inkscape::Verb::get(i);
339         if ( verb ) {
340             if (!mainActions->get_action(verb->get_id())) {
341                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
342                 mainActions->add(Glib::wrap(act));
343             }
344         }
345     }
346 
347     if ( !mainActions->get_action("ToolZoom") ) {
348         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
349             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view);
350             if ( va ) {
351                 mainActions->add(va);
352                 if ( i == 0 ) {
353                     va->set_active(true);
354                 }
355             } else {
356                 // This creates a blank action using the data_name, this can replace
357                 // tools that have been disabled by compile time options.
358                 Glib::RefPtr<Gtk::Action> act = Gtk::Action::create(Glib::ustring(tools[i].data_name));
359                 act->set_sensitive(false);
360                 mainActions->add(act);
361             }
362         }
363     }
364 
365     return mainActions;
366 }
367 
368 
toolboxNewCommon(GtkWidget * tb,BarId id,GtkPositionType)369 static GtkWidget* toolboxNewCommon( GtkWidget* tb, BarId id, GtkPositionType /*handlePos*/ )
370 {
371     g_object_set_data(G_OBJECT(tb), "desktop", nullptr);
372 
373     gtk_widget_set_sensitive(tb, TRUE);
374 
375     GtkWidget *hb = gtk_event_box_new(); // A simple, neutral container.
376     gtk_widget_set_name(hb, "ToolboxCommon");
377 
378     gtk_container_add(GTK_CONTAINER(hb), tb);
379     gtk_widget_show(GTK_WIDGET(tb));
380 
381     sigc::connection* conn = new sigc::connection;
382     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
383 
384     gpointer val = GINT_TO_POINTER(id);
385     g_object_set_data(G_OBJECT(hb), BAR_ID_KEY, val);
386 
387     return hb;
388 }
389 
createToolToolbox()390 GtkWidget *ToolboxFactory::createToolToolbox()
391 {
392     auto tb = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
393     gtk_widget_set_name(tb, "ToolToolbox");
394     gtk_box_set_homogeneous(GTK_BOX(tb), FALSE);
395 
396     return toolboxNewCommon( tb, BAR_TOOL, GTK_POS_TOP );
397 }
398 
createAuxToolbox()399 GtkWidget *ToolboxFactory::createAuxToolbox()
400 {
401     auto tb = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
402     gtk_widget_set_name(tb, "AuxToolbox");
403     gtk_box_set_homogeneous(GTK_BOX(tb), FALSE);
404 
405     return toolboxNewCommon( tb, BAR_AUX, GTK_POS_LEFT );
406 }
407 
408 //####################################
409 //# Commands Bar
410 //####################################
411 
createCommandsToolbox()412 GtkWidget *ToolboxFactory::createCommandsToolbox()
413 {
414     auto tb = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
415     gtk_widget_set_name(tb, "CommandsToolbox");
416     gtk_box_set_homogeneous(GTK_BOX(tb), FALSE);
417 
418     return toolboxNewCommon( tb, BAR_COMMANDS, GTK_POS_LEFT );
419 }
420 
createSnapToolbox()421 GtkWidget *ToolboxFactory::createSnapToolbox()
422 {
423     auto tb = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
424     gtk_widget_set_name(tb, "SnapToolbox");
425     gtk_box_set_homogeneous(GTK_BOX(tb), FALSE);
426 
427     Glib::ustring snap_toolbar_builder_file = get_filename(UIS, "toolbar-snap.ui");
428     auto builder = Gtk::Builder::create();
429     try
430     {
431         builder->add_from_file(snap_toolbar_builder_file);
432     }
433     catch (const Glib::Error& ex)
434     {
435         std::cerr << "ToolboxFactor::createSnapToolbox: " << snap_toolbar_builder_file << " file not read! " << ex.what() << std::endl;
436     }
437 
438     Gtk::Toolbar* toolbar = nullptr;
439     builder->get_widget("snap-toolbar", toolbar);
440     if (!toolbar) {
441         std::cerr << "InkscapeWindow: Failed to load snap toolbar!" << std::endl;
442     } else {
443         gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(toolbar->gobj()), false, false, 0);
444 
445         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
446         if ( prefs->getBool("/toolbox/icononly", true) ) {
447             toolbar->set_toolbar_style( Gtk::TOOLBAR_ICONS );
448         }
449 
450         GtkIconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
451         toolbar->set_icon_size (static_cast<Gtk::IconSize>(toolboxSize));
452     }
453 
454     return toolboxNewCommon( tb, BAR_SNAP, GTK_POS_LEFT );
455 }
456 
setToolboxDesktop(GtkWidget * toolbox,SPDesktop * desktop)457 void ToolboxFactory::setToolboxDesktop(GtkWidget *toolbox, SPDesktop *desktop)
458 {
459     sigc::connection *conn = static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
460                                                                               "event_context_connection"));
461 
462     BarId id = static_cast<BarId>( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY)) );
463 
464     SetupFunction setup_func = nullptr;
465     UpdateFunction update_func = nullptr;
466 
467     switch (id) {
468         case BAR_TOOL:
469             setup_func = setup_tool_toolbox;
470             update_func = update_tool_toolbox;
471             break;
472 
473         case BAR_AUX:
474             toolbox = gtk_bin_get_child(GTK_BIN(toolbox));
475             setup_func = setup_aux_toolbox;
476             update_func = update_aux_toolbox;
477             break;
478 
479         case BAR_COMMANDS:
480             setup_func = setup_commands_toolbox;
481             update_func = update_commands_toolbox;
482             break;
483 
484         case BAR_SNAP:
485             setup_func = nullptr;
486             update_func = nullptr;
487             break;
488         default:
489             g_warning("Unexpected toolbox id encountered.");
490     }
491 
492     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
493     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
494 
495     if (old_desktop) {
496         std::vector<Gtk::Widget*> children = Glib::wrap(GTK_CONTAINER(toolbox))->get_children();
497         for ( auto i:children ) {
498             gtk_container_remove( GTK_CONTAINER(toolbox), i->gobj() );
499         }
500     }
501 
502     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
503 
504     if (desktop && setup_func && update_func) {
505         gtk_widget_set_sensitive(toolbox, TRUE);
506         setup_func(toolbox, desktop);
507         update_func(desktop, desktop->event_context, toolbox);
508         *conn = desktop->connectEventContextChanged(sigc::bind (sigc::ptr_fun(update_func), toolbox));
509     } else {
510         gtk_widget_set_sensitive(toolbox, TRUE);
511     }
512 
513 } // end of sp_toolbox_set_desktop()
514 
515 
setupToolboxCommon(GtkWidget * toolbox,SPDesktop * desktop,gchar const * ui_file,gchar const * toolbarName,gchar const * sizePref)516 static void setupToolboxCommon( GtkWidget *toolbox,
517                                 SPDesktop *desktop,
518                                 gchar const *ui_file,
519                                 gchar const* toolbarName,
520                                 gchar const* sizePref )
521 {
522     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
523     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
524 
525     GtkUIManager* mgr = gtk_ui_manager_new();
526     GError* err = nullptr;
527 
528     GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL;
529 
530     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
531 
532     Glib::ustring filename = get_filename(UIS, ui_file);
533     gtk_ui_manager_add_ui_from_file( mgr, filename.c_str(), &err );
534     if(err) {
535         g_warning("Failed to load %s: %s", filename.c_str(), err->message);
536         g_error_free(err);
537         return;
538     }
539 
540     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, toolbarName );
541     if ( prefs->getBool("/toolbox/icononly", true) ) {
542         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
543     }
544 
545     GtkIconSize toolboxSize = ToolboxFactory::prefToSize(sizePref);
546     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
547 
548     GtkPositionType pos = static_cast<GtkPositionType>(GPOINTER_TO_INT(g_object_get_data( G_OBJECT(toolbox), HANDLE_POS_MARK )));
549     orientation = ((pos == GTK_POS_LEFT) || (pos == GTK_POS_RIGHT)) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
550     gtk_orientable_set_orientation (GTK_ORIENTABLE(toolBar), orientation);
551     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
552 
553     g_object_set_data(G_OBJECT(toolBar), "desktop", nullptr);
554 
555     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
556     if ( child ) {
557         gtk_container_remove( GTK_CONTAINER(toolbox), child );
558     }
559 
560     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
561 }
562 
563 #define noDUMP_DETAILS 1
564 
setOrientation(GtkWidget * toolbox,GtkOrientation orientation)565 void ToolboxFactory::setOrientation(GtkWidget* toolbox, GtkOrientation orientation)
566 {
567 #if DUMP_DETAILS
568     g_message("Set orientation for %p to be %d", toolbox, orientation);
569     GType type = G_OBJECT_TYPE(toolbox);
570     g_message("        [%s]", g_type_name(type));
571     g_message("             %p", g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY));
572 #endif
573 
574     GtkPositionType pos = (orientation == GTK_ORIENTATION_HORIZONTAL) ? GTK_POS_LEFT : GTK_POS_TOP;
575 
576     if (GTK_IS_BIN(toolbox)) {
577 #if DUMP_DETAILS
578         g_message("            is a BIN");
579 #endif // DUMP_DETAILS
580         GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
581         if (child) {
582 #if DUMP_DETAILS
583             GType type2 = G_OBJECT_TYPE(child);
584             g_message("            child    [%s]", g_type_name(type2));
585 #endif // DUMP_DETAILS
586 
587             if (GTK_IS_BOX(child)) {
588 #if DUMP_DETAILS
589                 g_message("                is a BOX");
590 #endif // DUMP_DETAILS
591 
592                 std::vector<Gtk::Widget*> children = Glib::wrap(GTK_CONTAINER(child))->get_children();
593                 if (!children.empty()) {
594                     for (auto curr:children) {
595                         GtkWidget* child2 = curr->gobj();
596 #if DUMP_DETAILS
597                         GType type3 = G_OBJECT_TYPE(child2);
598                         g_message("                child2   [%s]", g_type_name(type3));
599 #endif // DUMP_DETAILS
600 
601                         if (GTK_IS_CONTAINER(child2)) {
602                             std::vector<Gtk::Widget*> children2 = Glib::wrap(GTK_CONTAINER(child2))->get_children();
603                             if (!children2.empty()) {
604                                 for (auto curr2:children2) {
605                                     GtkWidget* child3 = curr2->gobj();
606 #if DUMP_DETAILS
607                                     GType type4 = G_OBJECT_TYPE(child3);
608                                     g_message("                    child3   [%s]", g_type_name(type4));
609 #endif // DUMP_DETAILS
610                                     if (GTK_IS_TOOLBAR(child3)) {
611                                         GtkToolbar* childBar = GTK_TOOLBAR(child3);
612                                         gtk_orientable_set_orientation(GTK_ORIENTABLE(childBar), orientation);
613                                     }
614                                 }
615                             }
616                         }
617 
618 
619                         if (GTK_IS_TOOLBAR(child2)) {
620                             GtkToolbar* childBar = GTK_TOOLBAR(child2);
621                             gtk_orientable_set_orientation(GTK_ORIENTABLE(childBar), orientation);
622                         } else {
623                             g_message("need to add dynamic switch");
624                         }
625                     }
626                 } else {
627                     // The call is being made before the toolbox proper has been setup.
628                     g_object_set_data(G_OBJECT(toolbox), HANDLE_POS_MARK, GINT_TO_POINTER(pos));
629                 }
630             } else if (GTK_IS_TOOLBAR(child)) {
631                 GtkToolbar* toolbar = GTK_TOOLBAR(child);
632                 gtk_orientable_set_orientation( GTK_ORIENTABLE(toolbar), orientation );
633             }
634         }
635     }
636 }
637 
setup_tool_toolbox(GtkWidget * toolbox,SPDesktop * desktop)638 void setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
639 {
640     setupToolboxCommon(toolbox, desktop, "toolbar-tool.ui", "/ui/ToolToolbar", "/toolbox/tools/small");
641 }
642 
update_tool_toolbox(SPDesktop * desktop,ToolBase * eventcontext,GtkWidget *)643 void update_tool_toolbox( SPDesktop *desktop, ToolBase *eventcontext, GtkWidget * /*toolbox*/ )
644 {
645     gchar const *const tname = ( eventcontext
646                                  ? eventcontext->getPrefsPath().c_str() //g_type_name(G_OBJECT_TYPE(eventcontext))
647                                  : nullptr );
648     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
649 
650     for (int i = 0 ; tools[i].type_name ; i++ ) {
651         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
652         if ( act ) {
653             bool setActive = tname && !strcmp(tname, tools[i].type_name);
654             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
655             if ( verbAct ) {
656                 verbAct->set_active(setActive);
657             }
658         }
659     }
660 }
661 
662 /**
663  * \brief Generate the auxiliary toolbox
664  *
665  * \details This is the one that appears below the main menu, and contains
666  *          tool-specific toolbars.  Each toolbar is created here, using
667  *          its "create" method.
668  *
669  *          The actual method used for each toolbar is specified in the
670  *          "aux_toolboxes" array, defined above.
671  */
setup_aux_toolbox(GtkWidget * toolbox,SPDesktop * desktop)672 void setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
673 {
674     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
675 
676     // Loop through all the toolboxes and create them using either
677     // their "create" methods.
678     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
679         if (aux_toolboxes[i].create_func) {
680             GtkWidget *sub_toolbox = aux_toolboxes[i].create_func(desktop);
681             gtk_widget_set_name( sub_toolbox, "SubToolBox" );
682 
683             auto holder = gtk_grid_new();
684             gtk_grid_attach(GTK_GRID(holder), sub_toolbox, 0, 0, 1, 1);
685 
686             // This part is just for styling
687             if ( prefs->getBool( "/toolbox/icononly", true) ) {
688                 gtk_toolbar_set_style( GTK_TOOLBAR(sub_toolbox), GTK_TOOLBAR_ICONS );
689             }
690 
691             GtkIconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small");
692             gtk_toolbar_set_icon_size( GTK_TOOLBAR(sub_toolbox), static_cast<GtkIconSize>(toolboxSize) );
693             gtk_widget_set_hexpand(sub_toolbox, TRUE);
694 
695             // Add a swatch widget if one was specified
696             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
697                 auto swatch = new Inkscape::UI::Widget::StyleSwatch( nullptr, _(aux_toolboxes[i].swatch_tip) );
698                 swatch->setDesktop( desktop );
699                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
700                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
701                 swatch->set_margin_start(AUX_BETWEEN_BUTTON_GROUPS);
702                 swatch->set_margin_end(AUX_BETWEEN_BUTTON_GROUPS);
703                 swatch->set_margin_top(AUX_SPACING);
704                 swatch->set_margin_bottom(AUX_SPACING);
705 
706                 auto swatch_ = GTK_WIDGET( swatch->gobj() );
707                 gtk_grid_attach( GTK_GRID(holder), swatch_, 1, 0, 1, 1);
708             }
709 
710             // Add the new toolbar into the toolbox (i.e., make it the visible toolbar)
711             // and also store a pointer to it inside the toolbox.  This allows the
712             // active toolbar to be changed.
713             gtk_container_add(GTK_CONTAINER(toolbox), holder);
714             gtk_widget_set_name( holder, aux_toolboxes[i].ui_name );
715 
716             // TODO: We could make the toolbox a custom subclass of GtkEventBox
717             //       so that we can store a list of toolbars, rather than using
718             //       GObject data
719             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder);
720             gtk_widget_show(sub_toolbox);
721             gtk_widget_show(holder);
722         } else if (aux_toolboxes[i].swatch_verb_id != SP_VERB_NONE) {
723             g_warning("Could not create toolbox %s", aux_toolboxes[i].ui_name);
724         }
725     }
726 }
727 
update_aux_toolbox(SPDesktop *,ToolBase * eventcontext,GtkWidget * toolbox)728 void update_aux_toolbox(SPDesktop * /*desktop*/, ToolBase *eventcontext, GtkWidget *toolbox)
729 {
730     gchar const *tname = ( eventcontext
731                            ? eventcontext->getPrefsPath().c_str() //g_type_name(G_OBJECT_TYPE(eventcontext))
732                            : nullptr );
733     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
734         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
735         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
736             gtk_widget_show_now(sub_toolbox);
737             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
738         } else {
739             gtk_widget_hide(sub_toolbox);
740         }
741         //FIX issue #Inkscape686
742         GtkAllocation allocation;
743         gtk_widget_get_allocation(sub_toolbox, &allocation);
744         gtk_widget_size_allocate(sub_toolbox, &allocation);
745     }
746     //FIX issue #Inkscape125
747     GtkAllocation allocation;
748     gtk_widget_get_allocation(toolbox, &allocation);
749     gtk_widget_size_allocate(toolbox, &allocation);
750 }
751 
setup_commands_toolbox(GtkWidget * toolbox,SPDesktop * desktop)752 void setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
753 {
754     setupToolboxCommon(toolbox, desktop, "toolbar-commands.ui", "/ui/CommandsToolbar", "/toolbox/small");
755 }
756 
update_commands_toolbox(SPDesktop *,ToolBase *,GtkWidget *)757 void update_commands_toolbox(SPDesktop * /*desktop*/, ToolBase * /*eventcontext*/, GtkWidget * /*toolbox*/)
758 {
759 }
760 
getToolboxName(GtkWidget * toolbox)761 Glib::ustring ToolboxFactory::getToolboxName(GtkWidget* toolbox)
762 {
763     Glib::ustring name;
764     BarId id = static_cast<BarId>( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY)) );
765     switch(id) {
766         case BAR_TOOL:
767             name = "ToolToolbar";
768             break;
769         case BAR_AUX:
770             name = "AuxToolbar";
771             break;
772         case BAR_COMMANDS:
773             name = "CommandsToolbar";
774             break;
775         case BAR_SNAP:
776             name = "SnapToolbar";
777             break;
778     }
779 
780     return name;
781 }
782 
showAuxToolbox(GtkWidget * toolbox_toplevel)783 void ToolboxFactory::showAuxToolbox(GtkWidget *toolbox_toplevel)
784 {
785     gtk_widget_show(toolbox_toplevel);
786     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
787 
788     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
789     if (!shown_toolbox) {
790         return;
791     }
792     gtk_widget_show(toolbox);
793 }
794 
795 #define MODE_LABEL_WIDTH 70
796 
797 
798 /*
799   Local Variables:
800   mode:c++
801   c-file-style:"stroustrup"
802   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
803   indent-tabs-mode:nil
804   fill-column:99
805   End:
806 */
807 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
808