/** * @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 . */ #include #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& 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(mainMenu); } /** * Register a dialog window. * * @param dialog The dialog. */ void GenericPluginUI::RegisterDialog(SmartPointer& 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::iterator controlleeIter = _widgetControlMap[widget].begin(); for (; controlleeIter != _widgetControlMap[widget].end(); ++controlleeIter) { GtkWidget *controllee = *controlleeIter; std::vector::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); }