netradiant-custom/contrib/meshtex/GenericPluginUI.cpp

374 lines
12 KiB
C++

/**
* @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);
}