1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /** @file
3 * Inkscape UI action implementation
4 *//*
5 * Authors:
6 * see git history
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 *
9 * Copyright (C) 2018 Authors
10 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
11 */
12
13 #include "helper/action.h"
14 #include "ui/icon-loader.h"
15
16 #include <gtkmm/toolbutton.h>
17
18 #include "debug/logger.h"
19 #include "debug/timestamp.h"
20 #include "debug/simple-event.h"
21 #include "debug/event-tracker.h"
22 #include "ui/view/view.h"
23 #include "desktop.h"
24 #include "document.h"
25 #include "verbs.h"
26
27 static void sp_action_finalize (GObject *object);
28
29 G_DEFINE_TYPE(SPAction, sp_action, G_TYPE_OBJECT);
30
31 /**
32 * SPAction vtable initialization.
33 */
34 static void
sp_action_class_init(SPActionClass * klass)35 sp_action_class_init (SPActionClass *klass)
36 {
37 GObjectClass *object_class = (GObjectClass *) klass;
38 object_class->finalize = sp_action_finalize;
39 }
40
41 /**
42 * Callback for SPAction object initialization.
43 */
44 static void
sp_action_init(SPAction * action)45 sp_action_init (SPAction *action)
46 {
47 action->sensitive = 0;
48 action->active = 0;
49 action->context = Inkscape::ActionContext();
50 action->id = action->name = action->tip = nullptr;
51 action->image = nullptr;
52
53 new (&action->signal_perform) sigc::signal<void>();
54 new (&action->signal_set_sensitive) sigc::signal<void, bool>();
55 new (&action->signal_set_active) sigc::signal<void, bool>();
56 new (&action->signal_set_name) sigc::signal<void, Glib::ustring const &>();
57 }
58
59 /**
60 * Called before SPAction object destruction.
61 */
62 static void
sp_action_finalize(GObject * object)63 sp_action_finalize (GObject *object)
64 {
65 SPAction *action = SP_ACTION(object);
66
67 g_free (action->image);
68 g_free (action->tip);
69 g_free (action->name);
70 g_free (action->id);
71
72 action->signal_perform.~signal();
73 action->signal_set_sensitive.~signal();
74 action->signal_set_active.~signal();
75 action->signal_set_name.~signal();
76
77 G_OBJECT_CLASS(sp_action_parent_class)->finalize (object);
78 }
79
80 /**
81 * Create new SPAction object and set its properties.
82 */
83 SPAction *
sp_action_new(Inkscape::ActionContext const & context,const gchar * id,const gchar * name,const gchar * tip,const gchar * image,Inkscape::Verb * verb)84 sp_action_new(Inkscape::ActionContext const &context,
85 const gchar *id,
86 const gchar *name,
87 const gchar *tip,
88 const gchar *image,
89 Inkscape::Verb * verb)
90 {
91 SPAction *action = (SPAction *)g_object_new(SP_TYPE_ACTION, nullptr);
92
93 action->context = context;
94 action->sensitive = TRUE;
95 action->id = g_strdup (id);
96 action->name = g_strdup (name);
97 action->tip = g_strdup (tip);
98 action->image = g_strdup (image);
99 action->verb = verb;
100
101 return action;
102 }
103
104 namespace {
105
106 using Inkscape::Debug::SimpleEvent;
107 using Inkscape::Debug::Event;
108 using Inkscape::Debug::timestamp;
109
110 typedef SimpleEvent<Event::INTERACTION> ActionEventBase;
111
112 class ActionEvent : public ActionEventBase {
113 public:
ActionEvent(SPAction const * action)114 ActionEvent(SPAction const *action)
115 : ActionEventBase("action")
116 {
117 _addProperty("timestamp", timestamp());
118 SPDocument *document = action->context.getDocument();
119 if (document) {
120 _addProperty("document", document->serial());
121 }
122 _addProperty("verb", action->id);
123 }
124 };
125
126 }
127
128 /**
129 * Executes an action.
130 * @param action The action to be executed.
131 * @param data ignored.
132 */
sp_action_perform(SPAction * action,void *)133 void sp_action_perform(SPAction *action, void * /*data*/)
134 {
135 g_return_if_fail (action != nullptr);
136 g_return_if_fail (SP_IS_ACTION (action));
137
138 Inkscape::Debug::EventTracker<ActionEvent> tracker(action);
139 action->signal_perform.emit();
140 }
141
142 /**
143 * Change activation in all actions that can be taken with the action.
144 */
145 void
sp_action_set_active(SPAction * action,unsigned int active)146 sp_action_set_active (SPAction *action, unsigned int active)
147 {
148 g_return_if_fail (action != nullptr);
149 g_return_if_fail (SP_IS_ACTION (action));
150
151 action->signal_set_active.emit(active);
152 }
153
154 /**
155 * Change sensitivity in all actions that can be taken with the action.
156 */
157 void
sp_action_set_sensitive(SPAction * action,unsigned int sensitive)158 sp_action_set_sensitive (SPAction *action, unsigned int sensitive)
159 {
160 g_return_if_fail (action != nullptr);
161 g_return_if_fail (SP_IS_ACTION (action));
162
163 action->signal_set_sensitive.emit(sensitive);
164 }
165
166 void
sp_action_set_name(SPAction * action,Glib::ustring const & name)167 sp_action_set_name (SPAction *action, Glib::ustring const &name)
168 {
169 g_return_if_fail (action != nullptr);
170 g_return_if_fail (SP_IS_ACTION (action));
171
172 g_free(action->name);
173 action->name = g_strdup(name.data());
174 action->signal_set_name.emit(name);
175 }
176
177 /**
178 * Return Document associated with the action.
179 */
180 SPDocument *
sp_action_get_document(SPAction * action)181 sp_action_get_document (SPAction *action)
182 {
183 g_return_val_if_fail (SP_IS_ACTION (action), NULL);
184 return action->context.getDocument();
185 }
186
187 /**
188 * Return Selection associated with the action
189 */
190 Inkscape::Selection *
sp_action_get_selection(SPAction * action)191 sp_action_get_selection (SPAction *action)
192 {
193 g_return_val_if_fail (SP_IS_ACTION (action), NULL);
194 return action->context.getSelection();
195 }
196
197 /**
198 * Return View associated with the action, if any.
199 */
200 Inkscape::UI::View::View *
sp_action_get_view(SPAction * action)201 sp_action_get_view (SPAction *action)
202 {
203 g_return_val_if_fail (SP_IS_ACTION (action), NULL);
204 return action->context.getView();
205 }
206
207 /**
208 * Return Desktop associated with the action, if any.
209 */
210 SPDesktop *
sp_action_get_desktop(SPAction * action)211 sp_action_get_desktop (SPAction *action)
212 {
213 // TODO: this slightly horrible storage of a UI::View::View*, and
214 // casting to an SPDesktop*, is only done because that's what was
215 // already the norm in the Inkscape codebase. This seems wrong. Surely
216 // we should store an SPDesktop* in the first place? Is there a case
217 // of actions being carried out on a View that is not an SPDesktop?
218 return static_cast<SPDesktop *>(sp_action_get_view(action));
219 }
220
221 /**
222 * \brief Create a toolbutton whose "clicked" signal performs an Inkscape verb
223 *
224 * \param[in] verb_code The code (e.g., SP_VERB_EDIT_SELECT_ALL) for the verb we want
225 *
226 * \todo This should really attach the toolbutton to an application action instead of
227 * hooking up the "clicked" signal. This should probably wait until we've
228 * migrated to Gtk::Application
229 */
230 Gtk::ToolButton *
create_toolbutton_for_verb(unsigned int verb_code,Inkscape::ActionContext & context)231 SPAction::create_toolbutton_for_verb(unsigned int verb_code,
232 Inkscape::ActionContext &context)
233 {
234 // Get display properties for the verb
235 auto verb = Inkscape::Verb::get(verb_code);
236 auto target_action = verb->get_action(context);
237 auto icon_name = verb->get_image() ? verb->get_image() : Glib::ustring();
238
239 // Create a button with the required display properties
240 auto button = Gtk::manage(new Gtk::ToolButton(verb->get_tip()));
241 auto icon_widget = sp_get_icon_image(icon_name, "/toolbox/small");
242 button->set_icon_widget(*icon_widget);
243 button->set_tooltip_text(verb->get_tip());
244
245 // Hook up signal handler
246 auto button_clicked_cb = sigc::bind(sigc::ptr_fun(&sp_action_perform),
247 target_action, nullptr);
248 button->signal_clicked().connect(button_clicked_cb);
249
250 return button;
251 }
252 /*
253 Local Variables:
254 mode:c++
255 c-file-style:"stroustrup"
256 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
257 indent-tabs-mode:nil
258 fill-column:99
259 End:
260 */
261 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
262