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(>k_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