]> git.xonotic.org Git - xonotic/netradiant.git/blobdiff - contrib/meshtex/GenericPluginUI.cpp
Merge commit '4645e19ce9e8f8034233ac965a4103a13b75714c' into garux-merge
[xonotic/netradiant.git] / contrib / meshtex / GenericPluginUI.cpp
diff --git a/contrib/meshtex/GenericPluginUI.cpp b/contrib/meshtex/GenericPluginUI.cpp
new file mode 100644 (file)
index 0000000..2da58c8
--- /dev/null
@@ -0,0 +1,374 @@
+/**
+ * @file GenericPluginUI.cpp
+ * Implements the GenericPluginUI class.
+ * @ingroup generic-ui
+ */
+
+/*
+ * Copyright 2012 Joel Baxter
+ *
+ * This file is part of MeshTex.
+ *
+ * MeshTex is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MeshTex is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MeshTex.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtk/gtk.h>
+
+#include "GenericPluginUI.h"
+
+
+/**
+ * Default constructor. Initialize object state; the initial callback ID to
+ * use is 1 because 0 is reserved for "invalid". Note that as this is a
+ * protected method, GenericPluginUI objects cannot be created directly; only
+ * subclasses of GenericPluginUI can be created.
+ */
+GenericPluginUI::GenericPluginUI() :
+   _window(NULL),
+   _mainMenu(NULL),
+   _callbackID(1),
+   _widgetControlCallback(*this)
+{
+}
+
+/**
+ * Virtual destructor. Remove references to UI elements (which should
+ * trigger garbage collection).
+ */
+GenericPluginUI::~GenericPluginUI()
+{
+   // Remove any reference to the menu object.
+   if (_mainMenu != NULL)
+   {
+      delete _mainMenu;
+   }
+   // The _dialogMap will also be deleted by the normal destructor operation,
+   // which will clear any references we hold on dialog windows.
+}
+
+/**
+ * Register the command menu.
+ *
+ * @param mainMenu The command menu.
+ */
+void
+GenericPluginUI::RegisterMainMenu(SmartPointer<GenericMainMenu>& mainMenu)
+{
+   // Remember the menu object, and hold a reference on it so it won't be
+   // garbage-collected while this object exists.
+   _mainMenu = new SmartPointer<GenericMainMenu>(mainMenu);
+}
+
+/**
+ * Register a dialog window.
+ *
+ * @param dialog The dialog.
+ */
+void
+GenericPluginUI::RegisterDialog(SmartPointer<GenericDialog>& dialog)
+{
+   // Remember the association between key and dialog, and hold a reference
+   // on it so it won't be garbage-collected while this object exists.
+   _dialogMap.insert(std::make_pair(dialog->GetKey(), dialog));
+}
+
+/**
+ * Specify the main application window.
+ *
+ * @param window The main window.
+ */
+void
+GenericPluginUI::SetWindow(GtkWidget *window)
+{
+   // Remember it.
+   _window = window;
+   // Set it as the parent for every dialog window.
+   DialogMap::const_iterator dialogMapIter = _dialogMap.begin();
+   for (; dialogMapIter != _dialogMap.end(); ++dialogMapIter)
+   {
+      if (dialogMapIter->second.get() != NULL)
+      {
+         dialogMapIter->second->SetWindow(window);
+      }
+   }
+}
+
+/**
+ * Get the command menu.
+ *
+ * @return The command menu, or NULL if none has been registered.
+ */
+GenericMainMenu *
+GenericPluginUI::MainMenu()
+{
+   if (_mainMenu == NULL)
+   {
+      return NULL;
+   }
+   return _mainMenu->get();
+}
+
+/**
+ * Get the dialog identified by the specified key.
+ *
+ * @param key The key.
+ *
+ * @return The dialog, or NULL if none found for that key.
+ */
+GenericDialog *
+GenericPluginUI::Dialog(const std::string& key)
+{
+   DialogMap::const_iterator dialogMapIter = _dialogMap.find(key);
+   if (dialogMapIter == _dialogMap.end())
+   {
+      return NULL;
+   }
+   return dialogMapIter->second;
+}
+
+/**
+ * Generic event callback used to invoke the specific callback functions
+ * registered with this manager. Those specific callbacks are not themselves
+ * registered directly with GTK+ because they may be methods that must be
+ * invoked on objects. (Unlike this function, which is a static method.)
+ *
+ * @param widget The widget generating the event.
+ * @param event  The event.
+ * @param data   ID of the specific callback registered with this manager.
+ *
+ * @return The return value from the specific callback.
+ */
+gint
+GenericPluginUI::DialogEventCallbackDispatch(GtkWidget *widget,
+                                             GdkEvent* event,
+                                             gpointer data)
+{
+   // Look up the callback ID in our registration map.
+   DialogEventCallbackMap::iterator dialogEventCallbackMapIter =
+      UIInstance()._dialogEventCallbackMap.find(data);
+   if (dialogEventCallbackMapIter == UIInstance()._dialogEventCallbackMap.end())
+   {
+      // If we didn't find it, nothing to do.
+      return TRUE;
+   }
+   // Otherwise invoke that callback.
+   return dialogEventCallbackMapIter->second(widget, event, data);
+}
+
+/**
+ * Generic signal callback used to invoke the specific callback functions
+ * registered with this manager. Those specific callbacks are not themselves
+ * registered directly with GTK+ because they may be methods that must be
+ * invoked on objects. (Unlike this function, which is a static method.)
+ *
+ * @param widget The widget generating the signal.
+ * @param data   ID of the specific callback registered with this manager.
+ */
+void
+GenericPluginUI::DialogSignalCallbackDispatch(GtkWidget *widget,
+                                              gpointer data)
+{
+   // Look up the callback ID in our registration map.
+   DialogSignalCallbackMap::iterator dialogSignalCallbackMapIter =
+      UIInstance()._dialogSignalCallbackMap.find(data);
+   if (dialogSignalCallbackMapIter == UIInstance()._dialogSignalCallbackMap.end())
+   {
+      // If we didn't find it, nothing to do.
+      return;
+   }
+   // Otherwise invoke that callback.
+   dialogSignalCallbackMapIter->second(widget, data);
+}
+
+/**
+ * Register a function to be invoked when a widget generates an event.
+ *
+ * @param widget   The widget generating the event.
+ * @param name     The name of the event.
+ * @param callback The callback function.
+ *
+ * @return The unique ID for the registered callback function.
+ */
+gpointer
+GenericPluginUI::RegisterDialogEventCallback(GtkWidget *widget,
+                                             const gchar *name,
+                                             const DialogEventCallback& callback)
+{
+   // Get the next callback ID to use.
+   gpointer callbackID = GUINT_TO_POINTER(_callbackID++);
+   // Make that event on that dialog widget trigger our event dispatch.
+   g_signal_connect(G_OBJECT(widget), name,
+                    G_CALLBACK(DialogEventCallbackDispatch), callbackID);
+   // Save the association between callback ID and function.
+   _dialogEventCallbackMap.insert(std::make_pair(callbackID, callback));
+   // Return the generated unique callback ID.
+   return callbackID;
+}
+
+/**
+ * Register a function to be invoked when a widget generates a signal.
+ *
+ * @param widget   The widget generating the signal.
+ * @param name     The name of the signal.
+ * @param callback The callback function.
+ *
+ * @return The unique ID for the registered callback function.
+ */
+gpointer
+GenericPluginUI::RegisterDialogSignalCallback(GtkWidget *widget,
+                                              const gchar *name,
+                                              const DialogSignalCallback& callback)
+{
+   // Get the next callback ID to use.
+   gpointer callbackID = GUINT_TO_POINTER(_callbackID++);
+   // Make that signal on that dialog widget trigger our signal dispatch.
+   g_signal_connect(G_OBJECT(widget), name,
+                    G_CALLBACK(DialogSignalCallbackDispatch), callbackID);
+   // Save the association between callback ID and function.
+   _dialogSignalCallbackMap.insert(std::make_pair(callbackID, callback));
+   // Return the generated unique callback ID.
+   return callbackID;
+}
+
+/**
+ * Declare that the controllee widget should be inactive when the
+ * controller widget is inactive. The controllee will be active only
+ * when all of its controllers allow it to be so.
+ *
+ * @param controller The controller widget.
+ * @param controllee The controllee widget.
+ */
+void
+GenericPluginUI::RegisterWidgetDependence(GtkWidget *controller,
+                                          GtkWidget *controllee)
+{
+   // Make sure we get a callback when the controller is toggled.
+   if (_widgetControlMap.find(controller) == _widgetControlMap.end())
+   {
+      RegisterDialogSignalCallback(controller, "clicked", _widgetControlCallback);
+   }
+   // Save the association.
+   _widgetControlMap[controller].push_back(controllee);
+   _widgetControlledByMap[controllee].push_back(controller);
+}
+
+/**
+ * Declare that the controllee widget should be inactive when the
+ * controller widget is active. The controllee will be active only
+ * when all of its controllers allow it to be so.
+ *
+ * @param controller The controller widget.
+ * @param controllee The controllee widget.
+ */
+void
+GenericPluginUI::RegisterWidgetAntiDependence(GtkWidget *controller,
+                                              GtkWidget *controllee)
+{
+   // Make sure we get a callback when the controller is toggled.
+   if (_widgetControlMap.find(controller) == _widgetControlMap.end())
+   {
+      RegisterDialogSignalCallback(controller, "clicked", _widgetControlCallback);
+   }
+   // Save the association.
+   _widgetControlMap[controller].push_back(controllee);
+   _widgetAntiControlledByMap[controllee].push_back(controller);
+}
+
+/**
+ * Manage the state of controllee widgets when a controller widget is clicked.
+ *
+ * @param widget     The controller widget.
+ * @param callbackID Unique numerical ID for the callback.
+ */
+void
+GenericPluginUI::WidgetControlCallback(GtkWidget *widget,
+                                       gpointer callbackID)
+{
+   // Iterate over all controllees registered for this widget.
+   std::vector<GtkWidget *>::iterator controlleeIter =
+      _widgetControlMap[widget].begin();
+   for (; controlleeIter != _widgetControlMap[widget].end(); ++controlleeIter)
+   {
+      GtkWidget *controllee = *controlleeIter;
+      std::vector<GtkWidget *>::iterator controllerIter;
+      // Start with an assumption that the controllee widget will be active.
+      bool sensitive = true;
+      // Look for a dependence on any widget.
+      controllerIter = _widgetControlledByMap[controllee].begin();
+      for (; controllerIter != _widgetControlledByMap[controllee].end(); ++controllerIter)
+      {
+         // Dependence found; honor it.
+         if (!(GTK_TOGGLE_BUTTON(*controllerIter)->active))
+         {
+            sensitive = false;
+            break;
+         }
+      }
+      // Look for an anti-dependence on any widget.
+      controllerIter = _widgetAntiControlledByMap[controllee].begin();
+      for (; controllerIter != _widgetAntiControlledByMap[controllee].end(); ++controllerIter)
+      {
+         // Anti-dependence found; honor it.
+         if (GTK_TOGGLE_BUTTON(*controllerIter)->active)
+         {
+            sensitive = false;
+            break;
+         }
+      }
+      // Set the active state of the controllee appropriately.
+      gtk_widget_set_sensitive(controllee, sensitive);
+   }
+}
+
+/**
+ * Generate an error dialog.
+ *
+ * @param title   The dialog title.
+ * @param message The error message.
+ */
+void
+GenericPluginUI::ErrorReportDialog(const char *title,
+                                   const char *message)
+{
+   // Pass this operation to Radiant.
+   GlobalRadiant().m_pfnMessageBox(UIInstance()._window, message, title, eMB_OK, eMB_ICONERROR);
+}
+
+/**
+ * Generate a warning dialog.
+ *
+ * @param title   The dialog title.
+ * @param message The warning message.
+ */
+void
+GenericPluginUI::WarningReportDialog(const char *title,
+                                     const char *message)
+{
+   // Pass this operation to Radiant.
+   GlobalRadiant().m_pfnMessageBox(UIInstance()._window, message, title, eMB_OK, eMB_ICONWARNING);
+}
+
+/**
+ * Generate an info dialog.
+ *
+ * @param title   The dialog title.
+ * @param message The info message.
+ */
+void
+GenericPluginUI::InfoReportDialog(const char *title,
+                                  const char *message)
+{
+   // Pass this operation to Radiant.
+   GlobalRadiant().m_pfnMessageBox(UIInstance()._window, message, title, eMB_OK, eMB_ICONDEFAULT);
+}
\ No newline at end of file