]> git.xonotic.org Git - xonotic/netradiant.git/blob - contrib/meshtex/GenericPluginUI.cpp
Merge commit 'b017c473e86330d5908858b8add1b16674b0a1a8' into garux-merge
[xonotic/netradiant.git] / contrib / meshtex / GenericPluginUI.cpp
1 /**
2  * @file GenericPluginUI.cpp
3  * Implements the GenericPluginUI class.
4  * @ingroup generic-ui
5  */
6
7 /*
8  * Copyright 2012 Joel Baxter
9  *
10  * This file is part of MeshTex.
11  *
12  * MeshTex is free software: you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation, either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * MeshTex is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with MeshTex.  If not, see <http://www.gnu.org/licenses/>.
24  */
25
26 #include <gtk/gtk.h>
27
28 #include "GenericPluginUI.h"
29
30
31 /**
32  * Default constructor. Initialize object state; the initial callback ID to
33  * use is 1 because 0 is reserved for "invalid". Note that as this is a
34  * protected method, GenericPluginUI objects cannot be created directly; only
35  * subclasses of GenericPluginUI can be created.
36  */
37 GenericPluginUI::GenericPluginUI() :
38    _window(NULL),
39    _mainMenu(NULL),
40    _callbackID(1),
41    _widgetControlCallback(*this)
42 {
43 }
44
45 /**
46  * Virtual destructor. Remove references to UI elements (which should
47  * trigger garbage collection).
48  */
49 GenericPluginUI::~GenericPluginUI()
50 {
51    // Remove any reference to the menu object.
52    if (_mainMenu != NULL)
53    {
54       delete _mainMenu;
55    }
56    // The _dialogMap will also be deleted by the normal destructor operation,
57    // which will clear any references we hold on dialog windows.
58 }
59
60 /**
61  * Register the command menu.
62  *
63  * @param mainMenu The command menu.
64  */
65 void
66 GenericPluginUI::RegisterMainMenu(SmartPointer<GenericMainMenu>& mainMenu)
67 {
68    // Remember the menu object, and hold a reference on it so it won't be
69    // garbage-collected while this object exists.
70    _mainMenu = new SmartPointer<GenericMainMenu>(mainMenu);
71 }
72
73 /**
74  * Register a dialog window.
75  *
76  * @param dialog The dialog.
77  */
78 void
79 GenericPluginUI::RegisterDialog(SmartPointer<GenericDialog>& dialog)
80 {
81    // Remember the association between key and dialog, and hold a reference
82    // on it so it won't be garbage-collected while this object exists.
83    _dialogMap.insert(std::make_pair(dialog->GetKey(), dialog));
84 }
85
86 /**
87  * Specify the main application window.
88  *
89  * @param window The main window.
90  */
91 void
92 GenericPluginUI::SetWindow(GtkWidget *window)
93 {
94    // Remember it.
95    _window = window;
96    // Set it as the parent for every dialog window.
97    DialogMap::const_iterator dialogMapIter = _dialogMap.begin();
98    for (; dialogMapIter != _dialogMap.end(); ++dialogMapIter)
99    {
100       if (dialogMapIter->second.get() != NULL)
101       {
102          dialogMapIter->second->SetWindow(window);
103       }
104    }
105 }
106
107 /**
108  * Get the command menu.
109  *
110  * @return The command menu, or NULL if none has been registered.
111  */
112 GenericMainMenu *
113 GenericPluginUI::MainMenu()
114 {
115    if (_mainMenu == NULL)
116    {
117       return NULL;
118    }
119    return _mainMenu->get();
120 }
121
122 /**
123  * Get the dialog identified by the specified key.
124  *
125  * @param key The key.
126  *
127  * @return The dialog, or NULL if none found for that key.
128  */
129 GenericDialog *
130 GenericPluginUI::Dialog(const std::string& key)
131 {
132    DialogMap::const_iterator dialogMapIter = _dialogMap.find(key);
133    if (dialogMapIter == _dialogMap.end())
134    {
135       return NULL;
136    }
137    return dialogMapIter->second;
138 }
139
140 /**
141  * Generic event callback used to invoke the specific callback functions
142  * registered with this manager. Those specific callbacks are not themselves
143  * registered directly with GTK+ because they may be methods that must be
144  * invoked on objects. (Unlike this function, which is a static method.)
145  *
146  * @param widget The widget generating the event.
147  * @param event  The event.
148  * @param data   ID of the specific callback registered with this manager.
149  *
150  * @return The return value from the specific callback.
151  */
152 gint
153 GenericPluginUI::DialogEventCallbackDispatch(GtkWidget *widget,
154                                              GdkEvent* event,
155                                              gpointer data)
156 {
157    // Look up the callback ID in our registration map.
158    DialogEventCallbackMap::iterator dialogEventCallbackMapIter =
159       UIInstance()._dialogEventCallbackMap.find(data);
160    if (dialogEventCallbackMapIter == UIInstance()._dialogEventCallbackMap.end())
161    {
162       // If we didn't find it, nothing to do.
163       return TRUE;
164    }
165    // Otherwise invoke that callback.
166    return dialogEventCallbackMapIter->second(widget, event, data);
167 }
168
169 /**
170  * Generic signal callback used to invoke the specific callback functions
171  * registered with this manager. Those specific callbacks are not themselves
172  * registered directly with GTK+ because they may be methods that must be
173  * invoked on objects. (Unlike this function, which is a static method.)
174  *
175  * @param widget The widget generating the signal.
176  * @param data   ID of the specific callback registered with this manager.
177  */
178 void
179 GenericPluginUI::DialogSignalCallbackDispatch(GtkWidget *widget,
180                                               gpointer data)
181 {
182    // Look up the callback ID in our registration map.
183    DialogSignalCallbackMap::iterator dialogSignalCallbackMapIter =
184       UIInstance()._dialogSignalCallbackMap.find(data);
185    if (dialogSignalCallbackMapIter == UIInstance()._dialogSignalCallbackMap.end())
186    {
187       // If we didn't find it, nothing to do.
188       return;
189    }
190    // Otherwise invoke that callback.
191    dialogSignalCallbackMapIter->second(widget, data);
192 }
193
194 /**
195  * Register a function to be invoked when a widget generates an event.
196  *
197  * @param widget   The widget generating the event.
198  * @param name     The name of the event.
199  * @param callback The callback function.
200  *
201  * @return The unique ID for the registered callback function.
202  */
203 gpointer
204 GenericPluginUI::RegisterDialogEventCallback(GtkWidget *widget,
205                                              const gchar *name,
206                                              const DialogEventCallback& callback)
207 {
208    // Get the next callback ID to use.
209    gpointer callbackID = GUINT_TO_POINTER(_callbackID++);
210    // Make that event on that dialog widget trigger our event dispatch.
211    g_signal_connect(G_OBJECT(widget), name,
212                     G_CALLBACK(DialogEventCallbackDispatch), callbackID);
213    // Save the association between callback ID and function.
214    _dialogEventCallbackMap.insert(std::make_pair(callbackID, callback));
215    // Return the generated unique callback ID.
216    return callbackID;
217 }
218
219 /**
220  * Register a function to be invoked when a widget generates a signal.
221  *
222  * @param widget   The widget generating the signal.
223  * @param name     The name of the signal.
224  * @param callback The callback function.
225  *
226  * @return The unique ID for the registered callback function.
227  */
228 gpointer
229 GenericPluginUI::RegisterDialogSignalCallback(GtkWidget *widget,
230                                               const gchar *name,
231                                               const DialogSignalCallback& callback)
232 {
233    // Get the next callback ID to use.
234    gpointer callbackID = GUINT_TO_POINTER(_callbackID++);
235    // Make that signal on that dialog widget trigger our signal dispatch.
236    g_signal_connect(G_OBJECT(widget), name,
237                     G_CALLBACK(DialogSignalCallbackDispatch), callbackID);
238    // Save the association between callback ID and function.
239    _dialogSignalCallbackMap.insert(std::make_pair(callbackID, callback));
240    // Return the generated unique callback ID.
241    return callbackID;
242 }
243
244 /**
245  * Declare that the controllee widget should be inactive when the
246  * controller widget is inactive. The controllee will be active only
247  * when all of its controllers allow it to be so.
248  *
249  * @param controller The controller widget.
250  * @param controllee The controllee widget.
251  */
252 void
253 GenericPluginUI::RegisterWidgetDependence(GtkWidget *controller,
254                                           GtkWidget *controllee)
255 {
256    // Make sure we get a callback when the controller is toggled.
257    if (_widgetControlMap.find(controller) == _widgetControlMap.end())
258    {
259       RegisterDialogSignalCallback(controller, "clicked", _widgetControlCallback);
260    }
261    // Save the association.
262    _widgetControlMap[controller].push_back(controllee);
263    _widgetControlledByMap[controllee].push_back(controller);
264 }
265
266 /**
267  * Declare that the controllee widget should be inactive when the
268  * controller widget is active. The controllee will be active only
269  * when all of its controllers allow it to be so.
270  *
271  * @param controller The controller widget.
272  * @param controllee The controllee widget.
273  */
274 void
275 GenericPluginUI::RegisterWidgetAntiDependence(GtkWidget *controller,
276                                               GtkWidget *controllee)
277 {
278    // Make sure we get a callback when the controller is toggled.
279    if (_widgetControlMap.find(controller) == _widgetControlMap.end())
280    {
281       RegisterDialogSignalCallback(controller, "clicked", _widgetControlCallback);
282    }
283    // Save the association.
284    _widgetControlMap[controller].push_back(controllee);
285    _widgetAntiControlledByMap[controllee].push_back(controller);
286 }
287
288 /**
289  * Manage the state of controllee widgets when a controller widget is clicked.
290  *
291  * @param widget     The controller widget.
292  * @param callbackID Unique numerical ID for the callback.
293  */
294 void
295 GenericPluginUI::WidgetControlCallback(GtkWidget *widget,
296                                        gpointer callbackID)
297 {
298    // Iterate over all controllees registered for this widget.
299    std::vector<GtkWidget *>::iterator controlleeIter =
300       _widgetControlMap[widget].begin();
301    for (; controlleeIter != _widgetControlMap[widget].end(); ++controlleeIter)
302    {
303       GtkWidget *controllee = *controlleeIter;
304       std::vector<GtkWidget *>::iterator controllerIter;
305       // Start with an assumption that the controllee widget will be active.
306       bool sensitive = true;
307       // Look for a dependence on any widget.
308       controllerIter = _widgetControlledByMap[controllee].begin();
309       for (; controllerIter != _widgetControlledByMap[controllee].end(); ++controllerIter)
310       {
311          // Dependence found; honor it.
312          if (!(GTK_TOGGLE_BUTTON(*controllerIter)->active))
313          {
314             sensitive = false;
315             break;
316          }
317       }
318       // Look for an anti-dependence on any widget.
319       controllerIter = _widgetAntiControlledByMap[controllee].begin();
320       for (; controllerIter != _widgetAntiControlledByMap[controllee].end(); ++controllerIter)
321       {
322          // Anti-dependence found; honor it.
323          if (GTK_TOGGLE_BUTTON(*controllerIter)->active)
324          {
325             sensitive = false;
326             break;
327          }
328       }
329       // Set the active state of the controllee appropriately.
330       gtk_widget_set_sensitive(controllee, sensitive);
331    }
332 }
333
334 /**
335  * Generate an error dialog.
336  *
337  * @param title   The dialog title.
338  * @param message The error message.
339  */
340 void
341 GenericPluginUI::ErrorReportDialog(const char *title,
342                                    const char *message)
343 {
344    // Pass this operation to Radiant.
345    GlobalRadiant().m_pfnMessageBox(UIInstance()._window, message, title, eMB_OK, eMB_ICONERROR);
346 }
347
348 /**
349  * Generate a warning dialog.
350  *
351  * @param title   The dialog title.
352  * @param message The warning message.
353  */
354 void
355 GenericPluginUI::WarningReportDialog(const char *title,
356                                      const char *message)
357 {
358    // Pass this operation to Radiant.
359    GlobalRadiant().m_pfnMessageBox(UIInstance()._window, message, title, eMB_OK, eMB_ICONWARNING);
360 }
361
362 /**
363  * Generate an info dialog.
364  *
365  * @param title   The dialog title.
366  * @param message The info message.
367  */
368 void
369 GenericPluginUI::InfoReportDialog(const char *title,
370                                   const char *message)
371 {
372    // Pass this operation to Radiant.
373    GlobalRadiant().m_pfnMessageBox(UIInstance()._window, message, title, eMB_OK, eMB_ICONDEFAULT);
374 }