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