]> git.xonotic.org Git - xonotic/netradiant.git/blobdiff - contrib/sunplug/sunplug.cpp
reformat code! now the code is only ugly on the *inside*
[xonotic/netradiant.git] / contrib / sunplug / sunplug.cpp
index cebf44ebabcbaedeb6dc05736ec276ba2c149bc5..5aa06e87028eba465fc9d27d82f18adc9cc5676e 100644 (file)
-/*\r
-Sunplug plugin for GtkRadiant\r
-Copyright (C) 2004 Topsun\r
-Thanks to SPoG for help!\r
-\r
-This library is free software; you can redistribute it and/or\r
-modify it under the terms of the GNU Lesser General Public\r
-License as published by the Free Software Foundation; either\r
-version 2.1 of the License, or (at your option) any later version.\r
-\r
-This library is distributed in the hope that it will be useful,\r
-but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
-Lesser General Public License for more details.\r
-\r
-You should have received a copy of the GNU Lesser General Public\r
-License along with this library; if not, write to the Free Software\r
-Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
-*/\r
-\r
-#include "sunplug.h"\r
-\r
-#include "debugging/debugging.h"\r
-\r
-#include "iplugin.h"\r
-\r
-#include "string/string.h"\r
-#include "modulesystem/singletonmodule.h"\r
-\r
-#include "iundo.h"       // declaration of undo system\r
-#include "ientity.h"     // declaration of entity system\r
-#include "iscenegraph.h" // declaration of datastructure of the map\r
-\r
-#include "scenelib.h"    // declaration of datastructure of the map\r
-#include "qerplugin.h"   // declaration to use other interfaces as a plugin\r
-\r
-#include <gtk/gtk.h>     // to display something with gtk (windows, buttons etc.), the whole package might not be necessary\r
-\r
-void about_plugin_window();\r
-void MapCoordinator();\r
-\r
-#ifdef __linux__\r
-// linux itoa implementation\r
-char* itoa( int value, char* result, int base )\r
-{      \r
-       // check that the base if valid\r
-       if (base < 2 || base > 16)\r
-       {\r
-         *result = 0;\r
-         return result;\r
-       }\r
-       \r
-       char* out = result;\r
-       int quotient = value;\r
-       \r
-       do\r
-       {\r
-               *out = "0123456789abcdef"[abs(quotient % base)];\r
-               ++out;\r
-       \r
-               quotient /= base;\r
-       } while (quotient);\r
-       \r
-       // Only apply negative sign for base 10\r
-       if( value < 0 && base == 10)\r
-         *out++ = '-';\r
-       \r
-       std::reverse(result, out);\r
-       \r
-       *out = 0;\r
-       return result;\r
-}\r
-#endif\r
-\r
-typedef struct _mapcoord_setting_packet {\r
-  GtkSpinButton *spinner1, *spinner2, *spinner3, *spinner4;\r
-  Entity* worldspawn;\r
-} mapcoord_setting_packet;\r
-\r
-static int map_minX, map_maxX, map_minY, map_maxY;\r
-static int minX, maxX, minY, maxY;\r
-mapcoord_setting_packet msp;\r
-\r
-//  **************************\r
-// ** find entities by class **  from radiant/map.cpp\r
-//  **************************\r
-class EntityFindByClassname : public scene::Graph::Walker\r
-{\r
-  const char* m_name;\r
-  Entity*& m_entity;\r
-public:\r
-  EntityFindByClassname(const char* name, Entity*& entity) : m_name(name), m_entity(entity)\r
-  {\r
-    m_entity = 0;\r
-  }\r
-  bool pre(const scene::Path& path, scene::Instance& instance) const\r
-  {\r
-    if(m_entity == 0)\r
-    {\r
-      Entity* entity = Node_getEntity(path.top());\r
-      if(entity != 0\r
-        && string_equal(m_name, entity->getKeyValue("classname")))\r
-      {\r
-        m_entity = entity;\r
-      }\r
-    }\r
-    return true;\r
-  }\r
-};\r
-\r
-Entity* Scene_FindEntityByClass(const char* name)\r
-{\r
-  Entity* entity;\r
-  GlobalSceneGraph().traverse(EntityFindByClassname(name, entity));\r
-  return entity;\r
-}\r
-\r
-//  **************************\r
-// ** GTK callback functions **\r
-//  **************************\r
-\r
-static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)\r
-{\r
-  /* If you return FALSE in the "delete_event" signal handler,\r
-   * GTK will emit the "destroy" signal. Returning TRUE means\r
-   * you don't want the window to be destroyed.\r
-   * This is useful for popping up 'are you sure you want to quit?'\r
-   * type dialogs. */\r
-\r
-  return FALSE;\r
-}\r
-\r
-// destroy widget if destroy signal is passed to widget\r
-static void destroy(GtkWidget *widget, gpointer data)\r
-{\r
-  gtk_widget_destroy(widget);\r
-}\r
-\r
-// function for close button to destroy the toplevel widget\r
-static void close_window(GtkWidget *widget, gpointer data)\r
-{\r
-  gtk_widget_destroy(gtk_widget_get_toplevel(widget));\r
-}\r
-\r
-// callback function to assign the optimal mapcoords to the spinboxes\r
-static void input_optimal(GtkWidget *widget, gpointer data)\r
-{\r
-  gtk_spin_button_set_value(msp.spinner1, minX);\r
-  gtk_spin_button_set_value(msp.spinner2, maxY);\r
-  gtk_spin_button_set_value(msp.spinner3, maxX);\r
-  gtk_spin_button_set_value(msp.spinner4, minY);\r
-}\r
-\r
-// Spinner return value function\r
-gint grab_int_value(GtkSpinButton *a_spinner, gpointer user_data) {\r
-  return gtk_spin_button_get_value_as_int(a_spinner);\r
-}\r
-\r
-// write the values of the Spinner-Boxes to the worldspawn\r
-static void set_coordinates(GtkWidget *widget, gpointer data)\r
-{\r
-  //Str str_min, str_max;\r
-  char buffer[10], str_min[20], str_max[20];\r
-\r
-  itoa(gtk_spin_button_get_value_as_int(msp.spinner1), str_min, 10);\r
-  itoa(gtk_spin_button_get_value_as_int(msp.spinner2), buffer, 10);\r
-  strcat(str_min, " ");\r
-  strcat(str_min, buffer);\r
-  msp.worldspawn->setKeyValue("mapcoordsmins", str_min);\r
-\r
-  itoa(gtk_spin_button_get_value_as_int(msp.spinner3), str_max, 10);\r
-  itoa(gtk_spin_button_get_value_as_int(msp.spinner4), buffer, 10);\r
-  strcat(str_max, " ");\r
-  strcat(str_max, buffer);\r
-  UndoableCommand undo("SunPlug.entitySetMapcoords");\r
-  msp.worldspawn->setKeyValue("mapcoordsmaxs", str_max);\r
-\r
-  close_window(widget, NULL);\r
-}\r
-\r
-class SunPlugPluginDependencies :\r
-  public GlobalRadiantModuleRef,    // basic class for all other module refs\r
-  public GlobalUndoModuleRef,       // used to say radiant that something has changed and to undo that\r
-  public GlobalSceneGraphModuleRef, // necessary to handle data in the mapfile (change, retrieve data)\r
-  public GlobalEntityModuleRef      // to access and modify the entities\r
-{\r
-public:\r
-  SunPlugPluginDependencies() :\r
-    GlobalEntityModuleRef(GlobalRadiant().getRequiredGameDescriptionKeyValue("entities"))//,\r
-  {\r
-  }\r
-};\r
-\r
-//  *************************\r
-// ** standard plugin stuff **\r
-//  *************************\r
-namespace SunPlug\r
-{\r
-  GtkWindow* main_window;\r
-  char MenuList[100] = "";\r
-\r
-  const char* init(void* hApp, void* pMainWidget)\r
-  {\r
-    main_window = GTK_WINDOW(pMainWidget);\r
-    return "Initializing SunPlug for GTKRadiant";\r
-  }\r
-  const char* getName()\r
-  {\r
-    return "SunPlug"; // name that is shown in the menue\r
-  }\r
-  const char* getCommandList()\r
-  {\r
-    const char about[] = "About...";\r
-    const char etMapCoordinator[] = ";ET-MapCoordinator";\r
-\r
-    strcat(MenuList, about);\r
-    if (strncmp(GlobalRadiant().getGameName(), "etmain", 6) == 0) strcat(MenuList, etMapCoordinator);\r
-    return (const char*)MenuList;\r
-  }\r
-  const char* getCommandTitleList()\r
-  {\r
-    return "";\r
-  }\r
-  void dispatch(const char* command, float* vMin, float* vMax, bool bSingleBrush) // message processing\r
-  {\r
-    if(string_equal(command, "About..."))\r
-    {\r
-         about_plugin_window();\r
-       }\r
-    if(string_equal(command, "ET-MapCoordinator"))\r
-    {\r
-         MapCoordinator();\r
-    }\r
-  }\r
-} // namespace\r
-\r
-class SunPlugModule : public TypeSystemRef\r
-{\r
-  _QERPluginTable                      m_plugin;\r
-public:\r
-  typedef _QERPluginTable      Type;\r
-  STRING_CONSTANT(Name, "SunPlug");\r
-\r
-  SunPlugModule()\r
-  {\r
-       m_plugin.m_pfnQERPlug_Init = &SunPlug::init;\r
-       m_plugin.m_pfnQERPlug_GetName = &SunPlug::getName;\r
-       m_plugin.m_pfnQERPlug_GetCommandList = &SunPlug::getCommandList;\r
-       m_plugin.m_pfnQERPlug_GetCommandTitleList = &SunPlug::getCommandTitleList;\r
-       m_plugin.m_pfnQERPlug_Dispatch = &SunPlug::dispatch;\r
-  }\r
-  _QERPluginTable* getTable()\r
-  {\r
-    return &m_plugin;\r
-  }\r
-};\r
-\r
-typedef SingletonModule<SunPlugModule, SunPlugPluginDependencies> SingletonSunPlugModule;\r
-\r
-SingletonSunPlugModule g_SunPlugModule;\r
-\r
-\r
-extern "C" void RADIANT_DLLEXPORT Radiant_RegisterModules(ModuleServer& server)\r
-{\r
-  initialiseModule(server);\r
-\r
-  g_SunPlugModule.selfRegister();\r
-}\r
-\r
-//  ************\r
-// ** my stuff **\r
-//  ************\r
-\r
-// About dialog\r
-void about_plugin_window()\r
-{\r
-    GtkWidget *window, *vbox, *label, *button;\r
-\r
-    window = gtk_window_new(GTK_WINDOW_TOPLEVEL); // create a window\r
-    gtk_window_set_transient_for(GTK_WINDOW(window), SunPlug::main_window); // make the window to stay in front of the main window\r
-    g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL); // connect the delete event\r
-    g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy), NULL); // connect the destroy event for the window\r
-    gtk_window_set_title(GTK_WINDOW(window), "About SunPlug"); // set the title of the window for the window\r
-    gtk_window_set_resizable(GTK_WINDOW(window), FALSE); // don't let the user resize the window\r
-    gtk_window_set_modal(GTK_WINDOW(window), TRUE); // force the user not to do something with the other windows\r
-    gtk_container_set_border_width(GTK_CONTAINER(window), 10); // set the border of the window\r
-\r
-    vbox = gtk_vbox_new(FALSE, 10); // create a box to arrange new objects vertically\r
-    gtk_container_add(GTK_CONTAINER(window), vbox); // add the box to the window\r
-\r
-    label = gtk_label_new("SunPlug v1.0 for NetRadiant 1.5\nby Topsun"); // create a label\r
-    gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); // text align left\r
-    gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 2); // insert the label in the box\r
-\r
-    button = gtk_button_new_with_label("OK"); // create a button with text\r
-    g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK (gtk_widget_destroy), window); // connect the click event to close the window\r
-    gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 2); // insert the button in the box\r
-\r
-    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); // center the window on screen\r
-\r
-    gtk_widget_show_all(window); // show the window and all subelements\r
-}\r
-\r
-// get the current bounding box and return the optimal coordinates\r
-void GetOptimalCoordinates(AABB *levelBoundingBox)\r
-{\r
-  int half_width, half_heigth, center_x, center_y;\r
-\r
-  half_width = levelBoundingBox->extents.x();\r
-  half_heigth = levelBoundingBox->extents.y();\r
-  center_x = levelBoundingBox->origin.x();\r
-  center_y = levelBoundingBox->origin.y();\r
-\r
-  if (half_width > 175 || half_heigth > 175) // the square must be at least 350x350 units\r
-  {\r
-    // the wider side is the indicator for the square\r
-    if (half_width >= half_heigth)\r
-    {\r
-         minX = center_x - half_width;\r
-      maxX = center_x + half_width;\r
-      minY = center_y - half_width;\r
-      maxY = center_y + half_width;\r
-    } else {\r
-      minX = center_x - half_heigth;\r
-      maxX = center_x + half_heigth;\r
-         minY = center_y - half_heigth;\r
-      maxY = center_y + half_heigth;\r
-    }\r
-  } else {\r
-    minX = center_x - 175;\r
-    maxX = center_x + 175;\r
-    minY = center_y - 175;\r
-    maxY = center_y + 175;\r
-  }\r
-}\r
-\r
-// MapCoordinator dialog window\r
-void MapCoordinator()\r
-{\r
-  GtkWidget *window, *vbox, *table, *label, *spinnerMinX, *spinnerMinY, *spinnerMaxX, *spinnerMaxY, *button;\r
-  GtkAdjustment *spinner_adj_MinX, *spinner_adj_MinY, *spinner_adj_MaxX, *spinner_adj_MaxY;\r
-  Entity *theWorldspawn = NULL;\r
-  const char *buffer;\r
-  char line[20];\r
-\r
-  // in any case we need a window to show the user what to do\r
-  window = gtk_window_new(GTK_WINDOW_TOPLEVEL); // create the window\r
-  gtk_window_set_transient_for(GTK_WINDOW(window), SunPlug::main_window); // make the window to stay in front of the main window\r
-  g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL); // connect the delete event for the window\r
-  g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy), NULL); // connect the destroy event for the window\r
-  gtk_window_set_title(GTK_WINDOW(window), "ET-MapCoordinator"); // set the title of the window for the window\r
-  gtk_window_set_resizable(GTK_WINDOW(window), FALSE); // don't let the user resize the window\r
-  gtk_window_set_modal(GTK_WINDOW(window), TRUE); // force the user not to do something with the other windows\r
-  gtk_container_set_border_width(GTK_CONTAINER(window), 10); // set the border of the window\r
-\r
-  vbox = gtk_vbox_new(FALSE, 10); // create a box to arrange new objects vertically\r
-  gtk_container_add(GTK_CONTAINER(window), vbox); // add the box to the window\r
-\r
-  scene::Path path = makeReference(GlobalSceneGraph().root()); // get the path to the root element of the graph\r
-  scene::Instance* instance = GlobalSceneGraph().find(path); // find the instance to the given path\r
-  AABB levelBoundingBox = instance->worldAABB(); // get the bounding box of the level\r
-\r
-  theWorldspawn = Scene_FindEntityByClass("worldspawn"); // find the entity worldspawn\r
-  if (theWorldspawn != 0) { // need to have a worldspawn otherwise setting a value crashes the radiant\r
-    // next two if's: get the current values of the mapcoords\r
-    buffer = theWorldspawn->getKeyValue("mapcoordsmins"); // upper left corner\r
-       if (strlen(buffer) > 0) {\r
-      strncpy(line, buffer, 19);\r
-      map_minX = atoi(strtok(line, " ")); // minimum of x value\r
-      map_minY = atoi(strtok(NULL, " ")); // maximum of y value\r
-       } else {\r
-               map_minX = 0;\r
-               map_minY = 0;\r
-       }\r
-    buffer = theWorldspawn->getKeyValue("mapcoordsmaxs"); // lower right corner\r
-       if (strlen(buffer) > 0) {\r
-         strncpy(line, buffer, 19);\r
-      map_maxX = atoi(strtok(line, " ")); // maximum of x value\r
-      map_maxY = atoi(strtok(NULL, " ")); // minimum of y value\r
-       } else {\r
-               map_maxX = 0;\r
-               map_maxY = 0;\r
-       }\r
-\r
-    globalOutputStream() << "SunPlug: calculating optimal coordinates\n"; // write to console that we are calculating the coordinates\r
-    GetOptimalCoordinates(&levelBoundingBox); // calculate optimal mapcoords with the dimensions of the level bounding box\r
-    globalOutputStream() << "SunPlug: adviced mapcoordsmins=" << minX << " " << maxY << "\n"; // console info about mapcoordsmins\r
-    globalOutputStream() << "SunPlug: adviced mapcoordsmaxs=" << maxX << " " << minY << "\n"; // console info about mapcoordsmaxs\r
-\r
-    spinner_adj_MinX = (GtkAdjustment *)gtk_adjustment_new(map_minX, -65536.0, 65536.0, 1.0, 5.0, 5.0); // create adjustment for value and range of minimum x value\r
-    spinner_adj_MinY = (GtkAdjustment *)gtk_adjustment_new(map_minY, -65536.0, 65536.0, 1.0, 5.0, 5.0); // create adjustment for value and range of minimum y value\r
-    spinner_adj_MaxX = (GtkAdjustment *)gtk_adjustment_new(map_maxX, -65536.0, 65536.0, 1.0, 5.0, 5.0); // create adjustment for value and range of maximum x value\r
-    spinner_adj_MaxY = (GtkAdjustment *)gtk_adjustment_new(map_maxY, -65536.0, 65536.0, 1.0, 5.0, 5.0); // create adjustment for value and range of maximum y value\r
\r
-    button = gtk_button_new_with_label("Get optimal mapcoords"); // create button with text\r
-    g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(input_optimal), NULL); // connect button with callback function\r
-    gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 2); // insert button into vbox\r
-\r
-    gtk_box_pack_start(GTK_BOX(vbox), gtk_hseparator_new(), FALSE, FALSE, 2); // insert separator into vbox\r
-\r
-    table = gtk_table_new(4, 3, TRUE); // create table\r
-    gtk_table_set_row_spacings(GTK_TABLE(table), 8); // set row spacings\r
-    gtk_table_set_col_spacings(GTK_TABLE(table), 8); // set column spacings\r
-    gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 2); // insert table into vbox\r
-\r
-    label = gtk_label_new("x"); // create label\r
-    gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); // align text to the left side\r
-    gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 0, 1); // insert label into table\r
-\r
-    label = gtk_label_new("y"); // create label\r
-    gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); // align text to the left side\r
-    gtk_table_attach_defaults(GTK_TABLE(table), label, 2, 3, 0, 1); // insert label into table\r
-\r
-    label = gtk_label_new("mapcoordsmins"); // create label\r
-    gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); // align text to the left side\r
-    gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2); // insert label into table\r
-\r
-    spinnerMinX = gtk_spin_button_new(spinner_adj_MinX, 1.0, 0); // create textbox wiht value spin, value and value range\r
-    gtk_table_attach_defaults(GTK_TABLE(table), spinnerMinX, 1, 2, 1, 2); // insert spinbox into table\r
-\r
-    spinnerMinY = gtk_spin_button_new(spinner_adj_MinY, 1.0, 0); // create textbox wiht value spin, value and value range\r
-    gtk_table_attach_defaults(GTK_TABLE(table), spinnerMinY, 2, 3, 1, 2); // insert spinbox into table\r
-\r
-    label = gtk_label_new("mapcoordsmaxs"); // create label\r
-    gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); // align text to the left side\r
-    gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3); // insert label into table\r
-\r
-    spinnerMaxX = gtk_spin_button_new(spinner_adj_MaxX, 1.0, 0); // create textbox wiht value spin, value and value range\r
-    gtk_table_attach_defaults(GTK_TABLE(table), spinnerMaxX, 1, 2, 2, 3); // insert spinbox into table\r
-\r
-    spinnerMaxY = gtk_spin_button_new(spinner_adj_MaxY, 1.0, 0); // create textbox wiht value spin, value and value range\r
-    gtk_table_attach_defaults(GTK_TABLE(table), spinnerMaxY, 2, 3, 2, 3); // insert spinbox into table\r
-\r
-    // put the references to the spinboxes and the worldspawn into the global exchange\r
-    msp.spinner1 = GTK_SPIN_BUTTON(spinnerMinX);\r
-    msp.spinner2 = GTK_SPIN_BUTTON(spinnerMinY);\r
-    msp.spinner3 = GTK_SPIN_BUTTON(spinnerMaxX);\r
-    msp.spinner4 = GTK_SPIN_BUTTON(spinnerMaxY);\r
-    msp.worldspawn = theWorldspawn;\r
-\r
-    button = gtk_button_new_with_label("Set"); // create button with text\r
-    g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(set_coordinates), NULL); // connect button with callback function\r
-    gtk_table_attach_defaults(GTK_TABLE(table), button, 1, 2, 3, 4); // insert button into table\r
-\r
-    button = gtk_button_new_with_label("Cancel"); // create button with text\r
-    g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(close_window), NULL); // connect button with callback function\r
-    gtk_table_attach_defaults(GTK_TABLE(table), button, 2, 3, 3, 4); // insert button into table\r
-  } else {\r
-       globalOutputStream() << "SunPlug: no worldspawn found!\n"; // output error to console\r
-       \r
-       label = gtk_label_new("ERROR: No worldspawn was found in the map!\nIn order to use this tool the map must have at least one brush in the worldspawn. "); // create a label\r
-    gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); // text align left\r
-    gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 2); // insert the label in the box\r
-\r
-    button = gtk_button_new_with_label("OK"); // create a button with text\r
-    g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(close_window), NULL); // connect the click event to close the window\r
-    gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 2); // insert the button in the box\r
-  }\r
-\r
-  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); // center the window\r
-  gtk_widget_show_all(window); // show the window and all subelements\r
-}\r
+/*
+   Sunplug plugin for GtkRadiant
+   Copyright (C) 2004 Topsun
+   Thanks to SPoG for help!
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "sunplug.h"
+#include "globaldefs.h"
+
+#include "debugging/debugging.h"
+
+#include "iplugin.h"
+
+#include "string/string.h"
+#include "modulesystem/singletonmodule.h"
+
+#include "iundo.h"       // declaration of undo system
+#include "ientity.h"     // declaration of entity system
+#include "iscenegraph.h" // declaration of datastructure of the map
+
+#include "scenelib.h"    // declaration of datastructure of the map
+#include "qerplugin.h"   // declaration to use other interfaces as a plugin
+
+#include <gtk/gtk.h>     // to display something with gtk (windows, buttons etc.), the whole package might not be necessary
+
+void about_plugin_window();
+
+void MapCoordinator();
+
+#if !GDEF_OS_WINDOWS
+
+// linux itoa implementation
+char *itoa(int value, char *result, int base)
+{
+    // check that the base if valid
+    if (base < 2 || base > 16) {
+        *result = 0;
+        return result;
+    }
+
+    char *out = result;
+    int quotient = value;
+
+    do {
+        *out = "0123456789abcdef"[abs(quotient % base)];
+        ++out;
+
+        quotient /= base;
+    } while (quotient);
+
+    // Only apply negative sign for base 10
+    if (value < 0 && base == 10) {
+        *out++ = '-';
+    }
+
+    std::reverse(result, out);
+
+    *out = 0;
+    return result;
+}
+
+#endif
+
+typedef struct _mapcoord_setting_packet {
+    ui::SpinButton spinner1{ui::null}, spinner2{ui::null}, spinner3{ui::null}, spinner4{ui::null};
+    Entity *worldspawn;
+} mapcoord_setting_packet;
+
+static int map_minX, map_maxX, map_minY, map_maxY;
+static int minX, maxX, minY, maxY;
+mapcoord_setting_packet msp;
+
+//  **************************
+// ** find entities by class **  from radiant/map.cpp
+//  **************************
+class EntityFindByClassname : public scene::Graph::Walker {
+    const char *m_name;
+    Entity *&m_entity;
+public:
+    EntityFindByClassname(const char *name, Entity *&entity) : m_name(name), m_entity(entity)
+    {
+        m_entity = 0;
+    }
+
+    bool pre(const scene::Path &path, scene::Instance &instance) const
+    {
+        if (m_entity == 0) {
+            Entity *entity = Node_getEntity(path.top());
+            if (entity != 0
+                && string_equal(m_name, entity->getKeyValue("classname"))) {
+                m_entity = entity;
+            }
+        }
+        return true;
+    }
+};
+
+Entity *Scene_FindEntityByClass(const char *name)
+{
+    Entity *entity;
+    GlobalSceneGraph().traverse(EntityFindByClassname(name, entity));
+    return entity;
+}
+
+//  **************************
+// ** GTK callback functions **
+//  **************************
+
+static gboolean delete_event(ui::Widget widget, GdkEvent *event, gpointer data)
+{
+    /* If you return FALSE in the "delete_event" signal handler,
+     * GTK will emit the "destroy" signal. Returning TRUE means
+     * you don't want the window to be destroyed.
+     * This is useful for popping up 'are you sure you want to quit?'
+     * type dialogs. */
+
+    return FALSE;
+}
+
+// destroy widget if destroy signal is passed to widget
+static void destroy(ui::Widget widget, gpointer data)
+{
+    widget.destroy();
+}
+
+// function for close button to destroy the toplevel widget
+static void close_window(ui::Widget widget, gpointer data)
+{
+    widget.window().destroy();
+}
+
+// callback function to assign the optimal mapcoords to the spinboxes
+static void input_optimal(ui::Widget widget, gpointer data)
+{
+    gtk_spin_button_set_value(msp.spinner1, minX);
+    gtk_spin_button_set_value(msp.spinner2, maxY);
+    gtk_spin_button_set_value(msp.spinner3, maxX);
+    gtk_spin_button_set_value(msp.spinner4, minY);
+}
+
+// Spinner return value function
+gint grab_int_value(ui::SpinButton a_spinner, gpointer user_data)
+{
+    return gtk_spin_button_get_value_as_int(a_spinner);
+}
+
+// write the values of the Spinner-Boxes to the worldspawn
+static void set_coordinates(ui::Widget widget, gpointer data)
+{
+    //Str str_min, str_max;
+    char buffer[10], str_min[20], str_max[20];
+
+    itoa(gtk_spin_button_get_value_as_int(msp.spinner1), str_min, 10);
+    itoa(gtk_spin_button_get_value_as_int(msp.spinner2), buffer, 10);
+    strcat(str_min, " ");
+    strcat(str_min, buffer);
+    msp.worldspawn->setKeyValue("mapcoordsmins", str_min);
+
+    itoa(gtk_spin_button_get_value_as_int(msp.spinner3), str_max, 10);
+    itoa(gtk_spin_button_get_value_as_int(msp.spinner4), buffer, 10);
+    strcat(str_max, " ");
+    strcat(str_max, buffer);
+    UndoableCommand undo("SunPlug.entitySetMapcoords");
+    msp.worldspawn->setKeyValue("mapcoordsmaxs", str_max);
+
+    close_window(widget, NULL);
+}
+
+class SunPlugPluginDependencies :
+        public GlobalRadiantModuleRef,  // basic class for all other module refs
+        public GlobalUndoModuleRef,     // used to say radiant that something has changed and to undo that
+        public GlobalSceneGraphModuleRef, // necessary to handle data in the mapfile (change, retrieve data)
+        public GlobalEntityModuleRef    // to access and modify the entities
+{
+public:
+    SunPlugPluginDependencies() :
+            GlobalEntityModuleRef(GlobalRadiant().getRequiredGameDescriptionKeyValue("entities"))
+    { //,
+    }
+};
+
+//  *************************
+// ** standard plugin stuff **
+//  *************************
+namespace SunPlug {
+    ui::Window main_window{ui::null};
+    char MenuList[100] = "";
+
+    const char *init(void *hApp, void *pMainWidget)
+    {
+        main_window = ui::Window::from(pMainWidget);
+        return "Initializing SunPlug for GTKRadiant";
+    }
+
+    const char *getName()
+    {
+        return "SunPlug"; // name that is shown in the menue
+    }
+
+    const char *getCommandList()
+    {
+        const char about[] = "About...";
+        const char etMapCoordinator[] = ";ET-MapCoordinator";
+
+        strcat(MenuList, about);
+        if (strncmp(GlobalRadiant().getGameName(), "etmain", 6) == 0) {
+            strcat(MenuList, etMapCoordinator);
+        }
+        return (const char *) MenuList;
+    }
+
+    const char *getCommandTitleList()
+    {
+        return "";
+    }
+
+    void dispatch(const char *command, float *vMin, float *vMax, bool bSingleBrush)
+    { // message processing
+        if (string_equal(command, "About...")) {
+            about_plugin_window();
+        }
+        if (string_equal(command, "ET-MapCoordinator")) {
+            MapCoordinator();
+        }
+    }
+} // namespace
+
+class SunPlugModule : public TypeSystemRef {
+    _QERPluginTable m_plugin;
+public:
+    typedef _QERPluginTable Type;
+
+    STRING_CONSTANT(Name, "SunPlug");
+
+    SunPlugModule()
+    {
+        m_plugin.m_pfnQERPlug_Init = &SunPlug::init;
+        m_plugin.m_pfnQERPlug_GetName = &SunPlug::getName;
+        m_plugin.m_pfnQERPlug_GetCommandList = &SunPlug::getCommandList;
+        m_plugin.m_pfnQERPlug_GetCommandTitleList = &SunPlug::getCommandTitleList;
+        m_plugin.m_pfnQERPlug_Dispatch = &SunPlug::dispatch;
+    }
+
+    _QERPluginTable *getTable()
+    {
+        return &m_plugin;
+    }
+};
+
+typedef SingletonModule<SunPlugModule, SunPlugPluginDependencies> SingletonSunPlugModule;
+
+SingletonSunPlugModule g_SunPlugModule;
+
+
+extern "C" void RADIANT_DLLEXPORT Radiant_RegisterModules(ModuleServer &server)
+{
+    initialiseModule(server);
+
+    g_SunPlugModule.selfRegister();
+}
+
+//  ************
+// ** my stuff **
+//  ************
+
+// About dialog
+void about_plugin_window()
+{
+    auto window = ui::Window(ui::window_type::TOP); // create a window
+    gtk_window_set_transient_for(window, SunPlug::main_window); // make the window to stay in front of the main window
+    window.connect("delete_event", G_CALLBACK(delete_event), NULL); // connect the delete event
+    window.connect("destroy", G_CALLBACK(destroy), NULL); // connect the destroy event for the window
+    gtk_window_set_title(window, "About SunPlug"); // set the title of the window for the window
+    gtk_window_set_resizable(window, FALSE); // don't let the user resize the window
+    gtk_window_set_modal(window, TRUE); // force the user not to do something with the other windows
+    gtk_container_set_border_width(GTK_CONTAINER(window), 10); // set the border of the window
+
+    auto vbox = ui::VBox(FALSE, 10); // create a box to arrange new objects vertically
+    window.add(vbox);
+
+    auto label = ui::Label("SunPlug v1.0 for NetRadiant 1.5\nby Topsun"); // create a label
+    gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); // text align left
+    vbox.pack_start(label, FALSE, FALSE, 2); // insert the label in the box
+
+    auto button = ui::Button("OK"); // create a button with text
+    g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(gtk_widget_destroy),
+                             (void *) window); // connect the click event to close the window
+    vbox.pack_start(button, FALSE, FALSE, 2); // insert the button in the box
+
+    gtk_window_set_position(window, GTK_WIN_POS_CENTER); // center the window on screen
+
+    gtk_widget_show_all(window); // show the window and all subelements
+}
+
+// get the current bounding box and return the optimal coordinates
+void GetOptimalCoordinates(AABB *levelBoundingBox)
+{
+    int half_width, half_heigth, center_x, center_y;
+
+    half_width = levelBoundingBox->extents.x();
+    half_heigth = levelBoundingBox->extents.y();
+    center_x = levelBoundingBox->origin.x();
+    center_y = levelBoundingBox->origin.y();
+
+    if (half_width > 175 || half_heigth > 175) { // the square must be at least 350x350 units
+        // the wider side is the indicator for the square
+        if (half_width >= half_heigth) {
+            minX = center_x - half_width;
+            maxX = center_x + half_width;
+            minY = center_y - half_width;
+            maxY = center_y + half_width;
+        } else {
+            minX = center_x - half_heigth;
+            maxX = center_x + half_heigth;
+            minY = center_y - half_heigth;
+            maxY = center_y + half_heigth;
+        }
+    } else {
+        minX = center_x - 175;
+        maxX = center_x + 175;
+        minY = center_y - 175;
+        maxY = center_y + 175;
+    }
+}
+
+// MapCoordinator dialog window
+void MapCoordinator()
+{
+    Entity *theWorldspawn = NULL;
+    const char *buffer;
+    char line[20];
+
+    // in any case we need a window to show the user what to do
+    auto window = ui::Window(ui::window_type::TOP); // create the window
+    gtk_window_set_transient_for(window, SunPlug::main_window); // make the window to stay in front of the main window
+    window.connect("delete_event", G_CALLBACK(delete_event), NULL); // connect the delete event for the window
+    window.connect("destroy", G_CALLBACK(destroy), NULL); // connect the destroy event for the window
+    gtk_window_set_title(window, "ET-MapCoordinator"); // set the title of the window for the window
+    gtk_window_set_resizable(window, FALSE); // don't let the user resize the window
+    gtk_window_set_modal(window, TRUE); // force the user not to do something with the other windows
+    gtk_container_set_border_width(GTK_CONTAINER(window), 10); // set the border of the window
+
+    auto vbox = ui::VBox(FALSE, 10); // create a box to arrange new objects vertically
+    window.add(vbox);
+
+    scene::Path path = makeReference(GlobalSceneGraph().root()); // get the path to the root element of the graph
+    scene::Instance *instance = GlobalSceneGraph().find(path); // find the instance to the given path
+    AABB levelBoundingBox = instance->worldAABB(); // get the bounding box of the level
+
+    theWorldspawn = Scene_FindEntityByClass("worldspawn"); // find the entity worldspawn
+    if (theWorldspawn != 0) { // need to have a worldspawn otherwise setting a value crashes the radiant
+        // next two if's: get the current values of the mapcoords
+        buffer = theWorldspawn->getKeyValue("mapcoordsmins"); // upper left corner
+        if (strlen(buffer) > 0) {
+            strncpy(line, buffer, 19);
+            map_minX = atoi(strtok(line, " ")); // minimum of x value
+            map_minY = atoi(strtok(NULL, " ")); // maximum of y value
+        } else {
+            map_minX = 0;
+            map_minY = 0;
+        }
+        buffer = theWorldspawn->getKeyValue("mapcoordsmaxs"); // lower right corner
+        if (strlen(buffer) > 0) {
+            strncpy(line, buffer, 19);
+            map_maxX = atoi(strtok(line, " ")); // maximum of x value
+            map_maxY = atoi(strtok(NULL, " ")); // minimum of y value
+        } else {
+            map_maxX = 0;
+            map_maxY = 0;
+        }
+
+        globalOutputStream()
+                << "SunPlug: calculating optimal coordinates\n"; // write to console that we are calculating the coordinates
+        GetOptimalCoordinates(
+                &levelBoundingBox); // calculate optimal mapcoords with the dimensions of the level bounding box
+        globalOutputStream() << "SunPlug: adviced mapcoordsmins=" << minX << " " << maxY
+                             << "\n"; // console info about mapcoordsmins
+        globalOutputStream() << "SunPlug: adviced mapcoordsmaxs=" << maxX << " " << minY
+                             << "\n"; // console info about mapcoordsmaxs
+
+        auto spinner_adj_MinX = ui::Adjustment(map_minX, -65536.0, 65536.0, 1.0, 5.0,
+                                               0); // create adjustment for value and range of minimum x value
+        auto spinner_adj_MinY = ui::Adjustment(map_minY, -65536.0, 65536.0, 1.0, 5.0,
+                                               0); // create adjustment for value and range of minimum y value
+        auto spinner_adj_MaxX = ui::Adjustment(map_maxX, -65536.0, 65536.0, 1.0, 5.0,
+                                               0); // create adjustment for value and range of maximum x value
+        auto spinner_adj_MaxY = ui::Adjustment(map_maxY, -65536.0, 65536.0, 1.0, 5.0,
+                                               0); // create adjustment for value and range of maximum y value
+
+        auto button = ui::Button("Get optimal mapcoords"); // create button with text
+        button.connect("clicked", G_CALLBACK(input_optimal), NULL); // connect button with callback function
+        vbox.pack_start(button, FALSE, FALSE, 2); // insert button into vbox
+
+        vbox.pack_start(ui::Widget::from(gtk_hseparator_new()), FALSE, FALSE, 2); // insert separator into vbox
+
+        auto table = ui::Table(4, 3, TRUE); // create table
+        gtk_table_set_row_spacings(table, 8); // set row spacings
+        gtk_table_set_col_spacings(table, 8); // set column spacings
+        vbox.pack_start(table, FALSE, FALSE, 2); // insert table into vbox
+
+        auto label = ui::Label("x"); // create label
+        gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); // align text to the left side
+        table.attach(label, {1, 2, 0, 1}); // insert label into table
+
+        label = ui::Label("y"); // create label
+        gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); // align text to the left side
+        table.attach(label, {2, 3, 0, 1}); // insert label into table
+
+        label = ui::Label("mapcoordsmins"); // create label
+        gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); // align text to the left side
+        table.attach(label, {0, 1, 1, 2}); // insert label into table
+
+        auto spinnerMinX = ui::SpinButton(spinner_adj_MinX, 1.0,
+                                          0); // create textbox wiht value spin, value and value range
+        table.attach(spinnerMinX, {1, 2, 1, 2}); // insert spinbox into table
+
+        auto spinnerMinY = ui::SpinButton(spinner_adj_MinY, 1.0,
+                                          0); // create textbox wiht value spin, value and value range
+        table.attach(spinnerMinY, {2, 3, 1, 2}); // insert spinbox into table
+
+        label = ui::Label("mapcoordsmaxs"); // create label
+        gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); // align text to the left side
+        table.attach(label, {0, 1, 2, 3}); // insert label into table
+
+        auto spinnerMaxX = ui::SpinButton(spinner_adj_MaxX, 1.0,
+                                          0); // create textbox wiht value spin, value and value range
+        table.attach(spinnerMaxX, {1, 2, 2, 3}); // insert spinbox into table
+
+        auto spinnerMaxY = ui::SpinButton(spinner_adj_MaxY, 1.0,
+                                          0); // create textbox wiht value spin, value and value range
+        table.attach(spinnerMaxY, {2, 3, 2, 3}); // insert spinbox into table
+
+        // put the references to the spinboxes and the worldspawn into the global exchange
+        msp.spinner1 = spinnerMinX;
+        msp.spinner2 = spinnerMinY;
+        msp.spinner3 = spinnerMaxX;
+        msp.spinner4 = spinnerMaxY;
+        msp.worldspawn = theWorldspawn;
+
+        button = ui::Button("Set"); // create button with text
+        button.connect("clicked", G_CALLBACK(set_coordinates), NULL); // connect button with callback function
+        table.attach(button, {1, 2, 3, 4}); // insert button into table
+
+        button = ui::Button("Cancel"); // create button with text
+        button.connect("clicked", G_CALLBACK(close_window), NULL); // connect button with callback function
+        table.attach(button, {2, 3, 3, 4}); // insert button into table
+    } else {
+        globalOutputStream() << "SunPlug: no worldspawn found!\n"; // output error to console
+
+        auto label = ui::Label(
+                "ERROR: No worldspawn was found in the map!\nIn order to use this tool the map must have at least one brush in the worldspawn. "); // create a label
+        gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); // text align left
+        vbox.pack_start(label, FALSE, FALSE, 2); // insert the label in the box
+
+        auto button = ui::Button("OK"); // create a button with text
+        button.connect("clicked", G_CALLBACK(close_window), NULL); // connect the click event to close the window
+        vbox.pack_start(button, FALSE, FALSE, 2); // insert the button in the box
+    }
+
+    gtk_window_set_position(window, GTK_WIN_POS_CENTER); // center the window
+    gtk_widget_show_all(window); // show the window and all subelements
+}