]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - radiant/groupdialog.cpp
apply misc fixes from Markus Fischer and Rambetter
[xonotic/netradiant.git] / radiant / groupdialog.cpp
index 1b89cc2f7b3ada21cbdb17f06b9b7696ef229a22..410e5af65b548831f52513076e86c6b86f7adfe4 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (C) 1999-2006 Id Software, Inc. and contributors.
+Copyright (C) 1999-2007 id Software, Inc. and contributors.
 For a list of contributors, see the accompanying CONTRIBUTORS file.
 
 This file is part of GtkRadiant.
@@ -26,207 +26,1686 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 // Leonardo Zide (leo@lokigames.com)
 //
 
+#ifndef _WIN32
+  #include <unistd.h>
+#endif
+#include <gdk/gdkkeysyms.h>
+#include <glib/gi18n.h>
+#include "stdafx.h"
 #include "groupdialog.h"
 
-#include "debugging/debugging.h"
+GtkWidget*     EntWidgets[EntLast];
+GtkListStore* g_entlist_store;
+GtkListStore* g_entprops_store;
+int                                    inspector_mode;         // W_TEXTURE, W_ENTITY, or W_CONSOLE
+qboolean               multiple_entities;
+qboolean               disable_spawn_get = false;
+entity_t               *edit_entity;
+/*
+static GdkPixmap *tree_pixmaps[7];
+static GdkBitmap *tree_masks[7];
+*/
+#define IMG_PATCH 0
+#define IMG_BRUSH 1
+#define IMG_GROUP 2
+#define IMG_ENTITY 3
+#define IMG_ENTITYGROUP 4
+#define IMG_MODEL 5
+#define IMG_SCRIPT 6
+
+// misc group support
+#define MAX_GROUPS 4096
+#define GROUP_DELIMETER '@'
+#define GROUPNAME "QER_Group_%i"
+
+GroupDlg g_wndGroup;
+GroupDlg *g_pGroupDlg = &g_wndGroup;
+
+// group_t are loaded / saved through "group_info" entities
+// they hold epairs for group settings and additionnal access info (tree nodes)
+group_t *g_pGroups = NULL;
+
+// the number of active spawnflags
+static int spawnflag_count;
+// table: index, match spawnflag item to the spawnflag index (i.e. which bit)
+static int spawn_table[MAX_FLAGS];
+// we change the layout depending on how many spawn flags we need to display
+// the table is a 4x4 in which we need to put the comment box EntWidgets[EntComment] and the spawn flags..
+static GtkWidget *LayoutTable;
+// 0: none of them are hooked
+// 1: only the text, 2: text and four checks, 3: text and 8 checks
+static int widget_state = 0;
 
-#include <vector>
+static void entity_check (GtkWidget *widget, gpointer data);
+
+// =============================================================================
+// Global functions
+
+/*
+===============================================================
+
+ENTITY WINDOW
+
+===============================================================
+*/
 
-#include <gtk/gtknotebook.h>
-#include <gtk/gtktextview.h>
-#include <gtk/gtklabel.h>
-#include <gtk/gtkscrolledwindow.h>
+void FillClassList ()
+{
+  GtkListStore* store = g_entlist_store;
 
-#include "gtkutil/widget.h"
-#include "gtkutil/accelerator.h"
-#include "entityinspector.h"
-#include "gtkmisc.h"
-#include "multimon.h"
-#include "console.h"
-#include "commands.h"
+  gtk_list_store_clear(store);
 
+  for (eclass_t* e = eclass ; e ; e = e->next)
+  {
+    GtkTreeIter iter;
+    gtk_list_store_append(store, &iter);
+    gtk_list_store_set(store, &iter, 0, e->name, 1, e, -1);
+  }
+}
 
-#include <gtk/gtkwidget.h>
-#include "gtkutil/window.h"
+// SetKeyValuePairs
+//
+// Reset the key/value (aka property) listbox and fill it with the
+// k/v pairs from the entity being edited.
+//
 
-class GroupDlg 
+void SetKeyValuePairs (bool bClearMD3)
 {
-public:
-  GtkWidget* m_pNotebook;
-  GtkWindow* m_window;
+  GtkListStore* store = g_entprops_store;
 
-  GroupDlg();
-  void Create(GtkWindow* parent);
+  gtk_list_store_clear(store);
 
-  void Show()
+  if (edit_entity == NULL)
   {
-    // workaround for strange gtk behaviour - modifying the contents of a window while it is not visible causes the window position to change without sending a configure_event
-    m_position_tracker.sync(m_window);
-    gtk_widget_show(GTK_WIDGET(m_window));
+    // if there's no entity, then display no key/values
+    return;
   }
-  void Hide()
+
+  // save current key/val pair around filling epair box
+  // row_select wipes it and sets to first in list
+  Str strKey = gtk_entry_get_text (GTK_ENTRY (EntWidgets[EntKeyField]));
+  Str strVal = gtk_entry_get_text (GTK_ENTRY (EntWidgets[EntValueField]));
+
+
+  // Walk through list and add pairs
+  for(epair_t* epair = edit_entity->epairs ; epair ; epair = epair->next)
   {
-    gtk_widget_hide(GTK_WIDGET(m_window));
+    GtkTreeIter iter;
+    gtk_list_store_append(store, &iter);
+    gtk_list_store_set(store, &iter, 0, epair->key, 1, epair->value, -1);
   }
 
-  WindowPositionTracker m_position_tracker;
-};
+  gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntKeyField]), strKey.GetBuffer());
+  gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntValueField]), strVal.GetBuffer());
+
+  Sys_UpdateWindows(W_CAMERA | W_XY);
+}
 
-namespace
+// SetSpawnFlags
+//
+// Update the checkboxes to reflect the flag state of the entity
+//
+void SetSpawnFlags(void)
 {
-  GroupDlg g_GroupDlg;
+  int f, i, v;
 
-  std::size_t g_current_page;
-  std::vector<StringExportCallback> g_pages;
+       disable_spawn_get = true;
+
+  f = atoi(ValueForKey (edit_entity, "spawnflags"));
+  for (i=0 ; i<spawnflag_count ; i++)
+  {
+    v = !!(f&(1<<spawn_table[i]));
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (EntWidgets[EntCheck1+i]), v);
+  }
+  // take care of the remaining ones
+  for (i=spawnflag_count ; i<MAX_FLAGS ; i++)
+  {
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (EntWidgets[EntCheck1+i]), FALSE);
+  }
+
+       disable_spawn_get = false;
 }
 
-void GroupDialog_updatePageTitle(GtkWindow* window, std::size_t pageIndex)
+// GetSpawnFlags
+//
+// Update the entity flags to reflect the state of the checkboxes
+//
+// NOTE: this function had a tendency to add "spawnflags" "0" on most entities
+//   if this wants to set spawnflags to zero, remove the key
+
+void GetSpawnFlags(void)
 {
-  if(pageIndex < g_pages.size())
+  int f, i, v;
+  char sz[32];
+
+  f = 0;
+  for (i=0 ; i<spawnflag_count ; i++)
+  {
+    v = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (EntWidgets[EntCheck1+i]));
+    f |= v<<spawn_table[i];
+  }
+
+  if (f==0)
   {
-    g_pages[pageIndex](PointerCaller1<GtkWindow, const char*, gtk_window_set_title>(window));
+    // remove all "spawnflags" keys
+    if (multiple_entities)
+    {
+      brush_t  *b;
+
+      for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+        DeleteKey (b->owner, "spawnflags");
+    }
+    else
+      DeleteKey (edit_entity, "spawnflags");
   }
+  else
+  {
+    sprintf (sz, "%i", f);
+    if (multiple_entities)
+    {
+      brush_t  *b;
+
+      for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+        SetKeyValue(b->owner, "spawnflags", sz);
+    }
+    else
+      SetKeyValue (edit_entity, "spawnflags", sz);
+  }
+  SetKeyValuePairs ();
 }
 
-static gboolean switch_page(GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, gpointer data)
+//#define DBG_UPDATESEL
+
+// UpdateSel
+//
+// Update the listbox, checkboxes and k/v pairs to reflect the new selection
+// iIndex is the index in the list box with the class name, -1 if not found
+bool UpdateSel(int iIndex, eclass_t *pec)
 {
-  GroupDialog_updatePageTitle(GTK_WINDOW(data), page_num);
-  g_current_page = page_num;
+  int i, next_state;
+  brush_t *b;
 
-  return FALSE;
+  // syndrom of crappy code, we may get into stack overflowing crap with this function and Gtk
+  // if we play with the list of entity classes
+  // using a static flag to prevent recursion
+  static bool bBlockUpdate = false;
+
+  if (bBlockUpdate)
+    return FALSE; // NOTE TTimo wtf is the return value for anyway?
+
+#ifdef DBG_UPDATESEL
+  Sys_FPrintf(SYS_WRN, "UpdateSel\n");
+#endif
+
+  if (selected_brushes.next == &selected_brushes)
+  {
+    edit_entity = world_entity;
+    multiple_entities = false;
+  }
+  else
+  {
+    edit_entity = selected_brushes.next->owner;
+    for (b=selected_brushes.next->next ; b != &selected_brushes ; b=b->next)
+    {
+      if (b->owner != edit_entity)
+      {
+       multiple_entities = true;
+       break;
+      }
+    }
+  }
+
+  if (iIndex != -1)
+  {
+#ifdef DBG_UPDATESEL
+    Sys_FPrintf(SYS_WRN,"Setting focus_row to %d\n", iIndex);
+#endif
+    bBlockUpdate = true;
+
+    GtkTreeView* view = GTK_TREE_VIEW(EntWidgets[EntList]);
+    GtkTreePath* path = gtk_tree_path_new();
+    gtk_tree_path_append_index(path, iIndex);
+    gtk_tree_selection_select_path(gtk_tree_view_get_selection(view), path);
+    gtk_tree_view_scroll_to_cell(view, path, NULL, FALSE, 0, 0);
+    gtk_tree_path_free(path);
+
+    bBlockUpdate = false;
+  }
+
+  if (pec == NULL)
+    return TRUE;
+
+  // Set up the description
+  {
+    GtkTextBuffer* buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(EntWidgets[EntComment]));
+    gtk_text_buffer_set_text (buffer, pec->comments, -1);
+  }
+
+  spawnflag_count = 0;
+
+  // do a first pass to count the spawn flags, don't touch the widgets, we don't know in what state they are
+  for (i=0 ; i<MAX_FLAGS ; i++)
+  {
+    if (pec->flagnames[i] && pec->flagnames[i][0] != 0 && strcmp(pec->flagnames[i],"-"))
+    {
+      spawn_table[spawnflag_count] = i;
+      spawnflag_count++;
+    }
+  }
+
+  // what's new widget state
+  if (spawnflag_count==0)
+    next_state = 1;
+  else if (spawnflag_count<=4)
+    next_state = 2;
+  else if (spawnflag_count<=8)
+    next_state = 3;
+  else if (spawnflag_count<=12)
+    next_state = 4;
+  else
+    next_state = 5;
+  widget_state = next_state;
+  static int last_count = 0;
+
+  // disable all remaining boxes
+  // NOTE: these boxes might not even be on display
+  for (i = 0; i < last_count; i++)
+  {
+    GtkWidget* widget = EntWidgets[EntCheck1+i];
+    gtk_label_set_text (GTK_LABEL (GTK_BIN (widget)->child), " ");
+    gtk_widget_hide (widget);
+    gtk_widget_ref (widget);
+    gtk_container_remove (GTK_CONTAINER (LayoutTable), widget);
+  }
+  last_count = spawnflag_count;
+
+  for (i=0 ; i<spawnflag_count ; i++)
+  {
+    GtkWidget* widget = EntWidgets[EntCheck1+i];
+    gtk_widget_show (widget);
+
+    Str str;
+    str = pec->flagnames[spawn_table[i]];
+    str.MakeLower ();
+
+//    gtk_table_attach (GTK_TABLE (LayoutTable), widget, i%4, i%4+1, i/4, i/4+1,
+    gtk_table_attach (GTK_TABLE (LayoutTable), widget, i%4, i%4+1, i/4, i/4+1,
+                     (GtkAttachOptions) (GTK_FILL),
+                     (GtkAttachOptions) (GTK_FILL), 0, 0);
+    gtk_widget_unref (widget);
+
+    gtk_label_set_text (GTK_LABEL (GTK_BIN (widget)->child), str.GetBuffer ());
+  }
+
+  SetSpawnFlags();
+
+  SetKeyValuePairs();
+
+  return TRUE;
 }
 
-GroupDlg::GroupDlg() : m_window(0)
+bool UpdateEntitySel(eclass_t *pec)
 {
-  m_position_tracker.setPosition(c_default_window_pos);
+#ifdef DBG_UPDATESEL
+  Sys_FPrintf(SYS_WRN, "UpdateEntitySel\n");
+#endif
+
+  GtkTreeModel* model = GTK_TREE_MODEL(g_entlist_store);
+  GtkTreeIter iter;
+  unsigned int i = 0;
+  for(gboolean good = gtk_tree_model_get_iter_first(model, &iter); good != FALSE; good = gtk_tree_model_iter_next(model, &iter))
+  {
+    char* text;
+    gtk_tree_model_get(model, &iter, 0, &text, -1);
+    if (strcmp (text, pec->name) == 0)
+    {
+#ifdef DBG_UPDATESEL
+      Sys_FPrintf(SYS_WRN, "found a match: %d %s\n", i, pec->name);
+#endif
+      return UpdateSel (i, pec);
+    }
+    g_free(text);
+    ++i;
+  }
+  return UpdateSel (-1, pec);
 }
 
-void GroupDlg::Create(GtkWindow* parent)
+// CreateEntity
+//
+// Creates a new entity based on the currently selected brush and entity type.
+//
+
+void CreateEntity(void)
 {
-  ASSERT_MESSAGE(m_window == 0, "dialog already created");
+  GtkTreeView* view = GTK_TREE_VIEW(EntWidgets[EntList]);
+
+  // check to make sure we have a brush
+  if (selected_brushes.next == &selected_brushes)
+  {
+    gtk_MessageBox(g_pParentWnd->m_pWidget, "You must have a selected brush to create an entity", "info");
+    return;
+  }
+
+  // find out what type of entity we are trying to create
+  GtkTreeModel* model;
+  GtkTreeIter iter;
+  if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(view), &model, &iter) == FALSE)
+  {
+    gtk_MessageBox (g_pParentWnd->m_pWidget, "You must have a selected class to create an entity", "info");
+    return;
+  }
 
-  GtkWindow* window = create_persistent_floating_window("Entities", parent);
+  char* text;
+  gtk_tree_model_get(model, &iter, 0, &text, -1);
+  CreateEntityFromName(text, vec3_origin);
+  g_free(text);
 
-  global_accel_connect_window(window);
+  if (selected_brushes.next == &selected_brushes)
+    edit_entity = world_entity;
+  else
+    edit_entity = selected_brushes.next->owner;
 
-  window_connect_focus_in_clear_focus_widget(window);
+  SetKeyValuePairs();
+  Select_Deselect ();
+  Select_Brush (edit_entity->brushes.onext);
+  Sys_UpdateWindows(W_ALL);
+}
 
-  m_window = window;
+/*
+===============
+AddProp
 
-#ifdef WIN32
-  if( g_multimon_globals.m_bStartOnPrimMon )
+===============
+*/
+void AddProp()
+{
+  if (edit_entity == NULL)
+    return;
+
+  // Get current selection text
+  const char* key = gtk_entry_get_text (GTK_ENTRY (EntWidgets[EntKeyField]));
+  const char* value = gtk_entry_get_text (GTK_ENTRY (EntWidgets[EntValueField]));
+
+
+  // TTimo: if you change the classname to worldspawn you won't merge back in the structural brushes but create a parasite entity
+  if (!strcmp(key, "classname") && !strcmp(value, "worldspawn"))
   {
-    WindowPosition pos(m_position_tracker.getPosition());
-    PositionWindowOnPrimaryScreen(pos);
-    m_position_tracker.setPosition(pos);
+    gtk_MessageBox(g_pParentWnd->m_pWidget,  "Cannot change \"classname\" key back to worldspawn.", NULL, MB_OK );
+    return;
   }
-#endif
-  m_position_tracker.connect(window);
 
+
+       // RR2DO2: we don't want spaces in entity keys
+       if (strstr( key, " " ))
+       {
+    gtk_MessageBox(g_pParentWnd->m_pWidget, "No spaces are allowed in entity keys.", NULL, MB_OK );
+    return;
+       }
+
+  if (multiple_entities)
   {
-    GtkWidget* notebook = gtk_notebook_new();
-    gtk_widget_show(notebook);
-    gtk_container_add (GTK_CONTAINER (window), notebook);
-    gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_BOTTOM);
-    m_pNotebook = notebook;
+    brush_t *b;
+
+    for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+      SetKeyValue(b->owner, key, value);
+  }
+  else
+    SetKeyValue(edit_entity, key, value);
+
+  // refresh the prop listbox
+  SetKeyValuePairs();
+
 
-    g_signal_connect(G_OBJECT(notebook), "switch_page", G_CALLBACK(switch_page), window);
+#ifdef USEPLUGINENTITIES
+  // if it's a plugin entity, perhaps we need to update some drawing parameters
+  // NOTE: perhaps moving this code to a seperate func would help if we need it in other places
+  // TODO: we need to call some update func in the IPluginEntity in case model name changes etc.
+  // ( for the moment only bounding brush is updated ), see UpdateModelBrush in Ritual's Q3Radiant
+  if (edit_entity->eclass->nShowFlags & ECLASS_PLUGINENTITY)
+  {
+    vec3_t mins, maxs;
+    edit_entity->pPlugEnt->GetBounds( mins, maxs );
+    // replace old bounding brush by newly computed one
+    // NOTE: this part is similar to Entity_BuildModelBrush in Ritual's Q3Radiant, it can be
+    // usefull moved into a seperate func
+    brush_t *b,*oldbrush;
+    if (edit_entity->brushes.onext != &edit_entity->brushes)
+      oldbrush = edit_entity->brushes.onext;
+    b = Brush_Create (mins, maxs, &edit_entity->eclass->texdef);
+    Entity_LinkBrush (edit_entity, b);
+    Brush_Build( b, true );
+    Select_Deselect();
+    Brush_AddToList (edit_entity->brushes.onext, &selected_brushes);
+    if (oldbrush)
+      Brush_Free( oldbrush );
   }
+#endif // USEPLUGINENTITIES
 }
 
+/*
+===============
+DelProp
 
-GtkWidget* GroupDialog_addPage(const char* tabLabel, GtkWidget* widget, const StringExportCallback& title)
+===============
+*/
+void DelProp(void)
 {
-  GtkWidget* w = gtk_label_new(tabLabel);
-  gtk_widget_show(w);
-  GtkWidget* page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(g_GroupDlg.m_pNotebook), gtk_notebook_insert_page(GTK_NOTEBOOK(g_GroupDlg.m_pNotebook), widget, w, -1));
-  g_pages.push_back(title);
+  if (edit_entity == NULL)
+    return;
+
+  // Get current selection text
+  const char* key = gtk_entry_get_text (GTK_ENTRY (EntWidgets[EntKeyField]));
 
-  return page;
+  if (multiple_entities)
+  {
+    brush_t *b;
+
+    for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+      DeleteKey(b->owner, key);
+  }
+  else
+    DeleteKey(edit_entity, key);
+
+  // refresh the prop listbox
+  SetKeyValuePairs();
 }
 
+void ResetEntity ()
+{
+  epair_t *pep;
+  int i;
+
+  if (edit_entity == NULL)
+    return;
+
+  if (multiple_entities)
+  {
+    brush_t *b;
 
-bool GroupDialog_isShown()
+    for (b=selected_brushes.next; b != &selected_brushes; b=b->next)
+      for (pep = b->owner->epairs; pep; )
+      {
+       if (strcmp (pep->key, "classname") != 0)
+       {
+         DeleteKey (b->owner, pep->key);
+         pep = b->owner->epairs;
+       }
+       else
+         pep = pep->next;
+      }
+  }
+  else
+    for (pep = edit_entity->epairs; pep; )
+    {
+      if (strcmp (pep->key, "classname") != 0)
+      {
+       DeleteKey (edit_entity, pep->key);
+       pep = edit_entity->epairs;
+      }
+      else
+       pep = pep->next;
+    }
+
+  // refresh the dialog
+  SetKeyValuePairs ();
+  for (i = EntCheck1; i <= EntCheck16; i++)
+    gtk_signal_handler_block_by_func (GTK_OBJECT (EntWidgets[i]), GTK_SIGNAL_FUNC (entity_check), NULL);
+  SetSpawnFlags ();
+  for (i = EntCheck1; i <= EntCheck16; i++)
+    gtk_signal_handler_unblock_by_func (GTK_OBJECT (EntWidgets[i]), GTK_SIGNAL_FUNC (entity_check), NULL);
+}
+
+bool GetSelectAllCriteria(CString &strKey, CString &strVal)
 {
-  return widget_is_visible(GTK_WIDGET(g_GroupDlg.m_window));
+  GtkTreeModel* model;
+  GtkTreeIter iter;
+  if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(EntWidgets[EntProps])), &model, &iter)
+    && (inspector_mode == W_ENTITY)
+    && GTK_WIDGET_VISIBLE (g_pGroupDlg->m_pWidget))
+  {
+    strKey = gtk_entry_get_text (GTK_ENTRY (EntWidgets[EntKeyField]));
+    strVal = gtk_entry_get_text (GTK_ENTRY (EntWidgets[EntValueField]));
+    return TRUE;
+  }
+  return FALSE;
 }
-void GroupDialog_setShown(bool shown)
+
+
+void AssignSound()
 {
-  shown ? g_GroupDlg.Show() : g_GroupDlg.Hide();
+  char buffer[NAME_MAX];
+
+  strcpy (buffer, g_qeglobals.m_strHomeMaps.GetBuffer());
+  strcat (buffer, "sound/");
+
+  if( access(buffer, R_OK) != 0 )
+  {
+    // just go to fsmain
+    strcpy (buffer, g_qeglobals.m_strHomeMaps.GetBuffer());
+    strcat (buffer, "/");
+  }
+
+  const char *filename = file_dialog (g_pGroupDlg->m_pWidget, TRUE, _("Open Wav File"), buffer, "sound");
+  if (filename != NULL)
+  {
+    gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntKeyField]), "noise");
+    char *aux = vfsExtractRelativePath (filename);
+    CString str;
+    if (aux)
+      str = aux;
+    else
+    {
+      Sys_FPrintf (SYS_WRN, "WARNING: could not extract the relative path, using full path instead\n");
+      str = filename;
+    }
+
+    gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntValueField]), str.GetBuffer());
+    AddProp();
+  }
+}
+
+void AssignModel()
+{
+  char buffer[NAME_MAX];
+
+  strcpy (buffer, g_qeglobals.m_strHomeMaps.GetBuffer());
+  strcat (buffer, "models/");
+
+  if( access(buffer, R_OK) != 0 )
+  {
+    // just go to fsmain
+    strcpy (buffer, g_qeglobals.m_strHomeMaps.GetBuffer());
+    strcat (buffer, "/");
+  }
+
+  const char *filename = file_dialog (g_pGroupDlg->m_pWidget, TRUE, _("Open Model"), buffer, MODEL_MAJOR);
+  if (filename != NULL)
+  {
+    gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntKeyField]), "model");
+    // use VFS to get the correct relative path
+    char *aux = vfsExtractRelativePath (filename);
+    CString str;
+    if (aux)
+      str = aux;
+    else
+    {
+      Sys_FPrintf (SYS_WRN, "WARNING: could not extract the relative path, using full path instead\n");
+      str = filename;
+    }
+
+    gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntValueField]), str.GetBuffer());
+    AddProp();
+    edit_entity->brushes.onext->bModelFailed = false;
+  }
 }
-void GroupDialog_ToggleShow()
-{  
-  GroupDialog_setShown(!GroupDialog_isShown());
+
+/*
+==============
+SetInspectorMode
+==============
+*/
+void SetInspectorMode(int iType)
+{
+  if (iType == W_GROUP)
+    gtk_MessageBox(g_pParentWnd->m_pWidget, "Brush grouping is not functional yet", NULL, MB_OK | MB_ICONWARNING );
+
+  if (!g_pParentWnd->FloatingGroupDialog() &&
+      (iType == W_TEXTURE || iType == W_CONSOLE))
+    return;
+
+  // Is the caller asking us to cycle to the next window?
+  if (iType == -1)
+  {
+    if (inspector_mode == W_ENTITY)
+      iType = W_TEXTURE;
+    else if (inspector_mode == W_TEXTURE)
+      iType = W_CONSOLE;
+    else if (inspector_mode == W_CONSOLE)
+      iType = W_GROUP;
+    else
+      iType = W_ENTITY;
+  }
+
+  switch(iType)
+  {
+  case W_ENTITY:
+    // entity is always first in the inspector
+    gtk_window_set_title (GTK_WINDOW (g_qeglobals_gui.d_entity), "Entities");
+    gtk_notebook_set_page (GTK_NOTEBOOK (g_pGroupDlg->m_pNotebook), 0);
+    break;
+
+  case W_TEXTURE:
+    g_pParentWnd->GetTexWnd()->FocusEdit();
+    gtk_window_set_title (GTK_WINDOW (g_qeglobals_gui.d_entity), "Textures");
+    if (g_pParentWnd->FloatingGroupDialog())
+      gtk_notebook_set_page (GTK_NOTEBOOK (g_pGroupDlg->m_pNotebook), 1);
+    break;
+
+  case W_CONSOLE:
+    gtk_window_set_title (GTK_WINDOW (g_qeglobals_gui.d_entity), "Console");
+    if (g_pParentWnd->FloatingGroupDialog())
+      gtk_notebook_set_page (GTK_NOTEBOOK (g_pGroupDlg->m_pNotebook), 2);
+    break;
+
+  case W_GROUP:
+    if (g_pParentWnd->FloatingGroupDialog())
+      gtk_notebook_set_page (GTK_NOTEBOOK (g_pGroupDlg->m_pNotebook), 3);
+    else
+      gtk_notebook_set_page (GTK_NOTEBOOK (g_pGroupDlg->m_pNotebook), 1);
+    break;
+
+  default:
+    break;
+  }
 }
 
-void GroupDialog_constructWindow(GtkWindow* main_window)
+void Group_Add(entity_t *e)
 {
-  g_GroupDlg.Create(main_window);
+  /*
+  group_t *g = (group_t*)qmalloc(sizeof(group_t));
+  g->epairs = e->epairs;
+  g->next = NULL;
+  e->epairs = NULL;
+
+  // create a new group node
+  char *text = ValueForKey(g->epairs, "group");
+  g->itemOwner = gtk_ctree_insert_node (GTK_CTREE (g_wndGroup.m_pTree), g_wndGroup.m_hWorld, NULL, &text, 0,
+                                       tree_pixmaps[IMG_GROUP], tree_masks[IMG_GROUP],
+                                       tree_pixmaps[IMG_GROUP], tree_masks[IMG_GROUP], TRUE, TRUE);
+  g->next = g_pGroups;
+  g_pGroups = g;
+  */
 }
-void GroupDialog_destroyWindow()
+/*
+group_t* Group_Alloc(char *name)
 {
-  ASSERT_NOTNULL(g_GroupDlg.m_window);
-  destroy_floating_window(g_GroupDlg.m_window);
-  g_GroupDlg.m_window = 0;
+  group_t *g = (group_t*)qmalloc(sizeof(group_t));
+  SetKeyValue( g->epairs, "group", name );
+  return g;
 }
 
+group_t* Group_ForName(const char * name)
+{
+  group_t *g = g_pGroups;
+  while (g != NULL)
+  {
+    if (strcmp( ValueForKey(g->epairs,"group"), name ) == 0)
+      break;
+    g = g->next;
+  }
+  return g;
+}
 
-GtkWindow* GroupDialog_getWindow()
+void Group_AddToItem(brush_t *b, GtkCTreeNode* item)
 {
-  return g_GroupDlg.m_window;
+  int nImage = IMG_BRUSH;
+  if (!g_qeglobals.m_bBrushPrimitMode)
+  {
+    return;
+  }
+  const char *pName = NULL;
+  //  const char *pNamed = Brush_GetKeyValue(b, "name");
+
+  if (!b->owner || (b->owner == world_entity))
+  {
+    if (b->patchBrush)
+    {
+      pName = "Generic Patch";
+      nImage = IMG_PATCH;
+    }
+    else
+    {
+      pName = "Generic Brush";
+      nImage = IMG_BRUSH;
+    }
+  }
+  else
+  {
+    pName = b->owner->eclass->name;
+    if (b->owner->eclass->fixedsize)
+    {
+      nImage = IMG_ENTITY;
+    }
+    else
+    {
+      nImage = IMG_ENTITYGROUP;
+    }
+  }
+
+  GtkCTreeNode *newItem;
+  int i = (b->patchBrush) ? IMG_PATCH : IMG_BRUSH;
+  newItem = gtk_ctree_insert_node (GTK_CTREE (g_wndGroup.m_pTree), item, NULL, (gchar**)&pName, 0,
+                                  tree_pixmaps[i], tree_masks[i], tree_pixmaps[i],
+                                  tree_masks[i], TRUE, TRUE);
+  gtk_ctree_node_set_row_data (GTK_CTREE (g_wndGroup.m_pTree), newItem, b);
+  b->itemOwner = newItem;
 }
-void GroupDialog_show()
+*/
+void Group_RemoveBrush(brush_t *b)
 {
-  g_GroupDlg.Show();
+  /*
+  if (!g_qeglobals.m_bBrushPrimitMode)
+  {
+    return;
+  }
+  if (b->itemOwner)
+  {
+    gtk_ctree_remove_node (GTK_CTREE (g_pGroupDlg->m_pTree), b->itemOwner);
+    b->itemOwner = NULL;
+  }
+  DeleteKey(b->epairs, "group");
+  */
 }
+/*
+void Group_AddToWorld(brush_t *b)
+{
+  if (!g_qeglobals.m_bBrushPrimitMode)
+  {
+    return;
+  }
+  GtkCTreeNode *parent = gtk_ctree_node_nth (GTK_CTREE (g_pGroupDlg->m_pTree), 0);
+  Group_AddToItem(b, parent);
+}
+*/
+void Group_AddToProperGroup(brush_t *b)
+{
+  /*
+  if (!g_qeglobals.m_bBrushPrimitMode)
+  {
+    return;
+  }
+
+  // NOTE: we do a local copy of the "group" key because it gets erased by Group_RemoveBrush
+  const char *pGroup = Brush_GetKeyValue(b, "group");
+  // remove the entry in the tree if there's one
+  if (b->itemOwner)
+  {
+    gtk_ctree_remove_node (GTK_CTREE (g_pGroupDlg->m_pTree), b->itemOwner);
+    b->itemOwner = NULL;
+  }
 
-GtkWidget* GroupDialog_getPage()
+  if (*pGroup != 0)
+  {
+    // find the item
+    group_t *g = Group_ForName(pGroup);
+    if (g)
+      Group_AddToItem(b, g->itemOwner);
+#ifdef _DEBUG
+    else
+      Sys_Printf("WARNING: unexpected Group_ForName not found in Group_AddToProperGroup\n");
+#endif
+  }
+  else
+  {
+    Group_AddToWorld(b);
+  }
+  */
+}
+/*
+void Group_AddToSelected(brush_t *b)
+{
+  if (!g_qeglobals.m_bBrushPrimitMode)
+  {
+    return;
+  }
+  GtkCTreeNode *item;
+  item = gtk_ctree_node_nth (GTK_CTREE (g_pGroupDlg->m_pTree), GTK_CLIST (g_pGroupDlg->m_pTree)->focus_row);
+  if (item == NULL)
+  {
+    item = gtk_ctree_node_nth (GTK_CTREE (g_pGroupDlg->m_pTree), 0);
+  }
+  Group_AddToItem(b, item);
+}
+*/
+/*
+void Group_Save(FILE *f)
 {
-  return gtk_notebook_get_nth_page(GTK_NOTEBOOK(g_GroupDlg.m_pNotebook), gint(g_current_page));
+  group_t *g = g_pGroups;
+  while (g)
+  {
+    fprintf(f,"{\n\"classname\" \"group_info\"\n\"group\" \"%s\"\n}\n", ValueForKey( g->epairs, "group" ));
+    g = g->next;
+  }
 }
+*/
 
-void GroupDialog_setPage(GtkWidget* page)
+void Group_Init()
 {
-  g_current_page = gtk_notebook_page_num(GTK_NOTEBOOK(g_GroupDlg.m_pNotebook), page);
-  gtk_notebook_set_current_page(GTK_NOTEBOOK(g_GroupDlg.m_pNotebook), gint(g_current_page));
+  if (!g_qeglobals.m_bBrushPrimitMode)
+  {
+    return;
+  }
+  // start by cleaning everything
+  // clean the groups
+  //++timo FIXME: we leak, delete the groups on the way (I don't have time to do it now)
+#ifdef _DEBUG
+  Sys_Printf("TODO: fix leak in Group_Init\n");
+#endif
+  group_t *g = g_pGroups;
+  while (g)
+  {
+    epair_t *ep,*enext;
+    for (ep = g->epairs ; ep ; ep=enext )
+    {
+      enext = ep->next;
+      free (ep->key);
+      free (ep->value);
+      free (ep);
+    }
+    g = g->next;
+  }
+  /*
+  GtkCTreeNode *world;
+  char *text = "World";
+  g_pGroups = NULL;
+  gtk_clist_clear (GTK_CLIST (g_wndGroup.m_pTree));
+  world = gtk_ctree_insert_node (GTK_CTREE (g_wndGroup.m_pTree), NULL, NULL, &text, 0,
+                                tree_pixmaps[IMG_GROUP], tree_masks[IMG_GROUP], tree_pixmaps[IMG_GROUP],
+                                tree_masks[IMG_GROUP], FALSE, TRUE);
+  */
+  // walk through all the brushes, remove the itemOwner key and add them back where they belong
+  brush_t *b;
+  for (b = active_brushes.next; b != &active_brushes; b = b->next)
+  {
+    b->itemOwner = NULL;
+    Group_AddToProperGroup(b);
+  }
+  for (b = selected_brushes.next ; b != &selected_brushes ; b = b->next)
+  {
+    b->itemOwner = NULL;
+    Group_AddToProperGroup(b);
+  }
+}
+/*
+// scan through world_entity for groups in this map?
+// we use GROUPNAME "QER_group_%i" to look for existing groups and their naming
+//++timo FIXME: is this actually needed for anything?
+void Group_GetListFromWorld(GSList **pArray)
+{
+  if (!g_qeglobals.m_bBrushPrimitMode)
+  {
+    return;
+  }
+
+  if (world_entity == NULL)
+  {
+    return;
+  }
+
+  char cBuff[1024];
+  for (int i =0; i < MAX_GROUPS; i++)
+  {
+    sprintf(cBuff, GROUPNAME, i);
+    char *pGroup = ValueForKey(world_entity, cBuff);
+    if (pGroup && strlen(pGroup) > 0)
+    {
+      *pArray = g_slist_append (*pArray, g_strdup (pGroup));
+    }
+    else
+    {
+      break;
+    }
+  }
 }
 
-void GroupDialog_showPage(GtkWidget* page)
+void Group_RemoveListFromWorld()
 {
-  if(GroupDialog_getPage() == page)
+  if (!g_qeglobals.m_bBrushPrimitMode)
   {
-    GroupDialog_ToggleShow();
+    return;
   }
-  else
+  GSList* array = NULL;
+  Group_GetListFromWorld(&array);
+
+  while (array)
+  {
+    DeleteKey(world_entity, (char*)array->data);
+    g_free (array->data);
+    array = g_slist_remove (array, array->data);
+  }
+}
+
+int CountChar(const char *p, char c)
+{
+  int nCount = 0;
+  int nLen = strlen(p)-1;
+  while (nLen-- >= 0)
+  {
+    if (p[nLen] == c)
+    {
+      nCount++;
+    }
+  }
+  return nCount;
+}
+*/
+// =============================================================================
+// callbacks
+
+static void eclasslist_selection_changed(GtkTreeSelection* selection, gpointer data)
+{
+  GtkTreeModel* model;
+  GtkTreeIter selected;
+  // no world entity, we are not ready yet
+  if( !world_entity ) {
+    return;
+  }
+  if(gtk_tree_selection_get_selected(selection, &model, &selected))
   {
-    gtk_widget_show(GTK_WIDGET(g_GroupDlg.m_window));
-    GroupDialog_setPage(page);
+    eclass_t* eclass;
+    gtk_tree_model_get(model, &selected, 1, &eclass, -1);
+    if(eclass != NULL)
+    {
+      GtkTreePath* path = gtk_tree_model_get_path(model, &selected);
+      UpdateSel(gtk_tree_path_get_indices(path)[0], eclass);
+      gtk_tree_path_free(path);
+    }
   }
 }
 
-void GroupDialog_cycle()
+static gint eclasslist_button_press (GtkWidget *widget, GdkEventButton *event, gpointer data)
 {
-  g_current_page = (g_current_page + 1) % g_pages.size(); 
-  gtk_notebook_set_current_page(GTK_NOTEBOOK(g_GroupDlg.m_pNotebook), gint(g_current_page));
+  if (event->type == GDK_2BUTTON_PRESS)
+  {
+    CreateEntity ();
+    return TRUE;
+  }
+  return FALSE;
+}
+
+static gint eclasslist_keypress (GtkWidget* widget, GdkEventKey* event, gpointer data)
+{
+  unsigned int code = gdk_keyval_to_upper (event->keyval);
+
+  if (event->keyval == GDK_Return)
+  {
+    CreateEntity ();
+    return TRUE;
+  }
+
+  // select the entity that starts with the key pressed
+  if (code <= 'Z' && code >= 'A')
+  {
+    GtkTreeView* view = GTK_TREE_VIEW(EntWidgets[EntList]);
+    GtkTreeModel* model;
+    GtkTreeIter iter;
+    if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(view), &model, &iter) == FALSE
+      || gtk_tree_model_iter_next(model, &iter) == FALSE)
+    {
+      gtk_tree_model_get_iter_first(model, &iter);
+    }
+
+    for(unsigned int count = gtk_tree_model_iter_n_children(model, NULL); count > 0; --count)
+    {
+      char* text;
+      gtk_tree_model_get(model, &iter, 0, &text, -1);
+
+      if (toupper (text[0]) == (int)code)
+      {
+        GtkTreePath* path = gtk_tree_model_get_path(model, &iter);
+        gtk_tree_selection_select_path(gtk_tree_view_get_selection(view), path);
+        gtk_tree_view_scroll_to_cell(view, path, NULL, FALSE, 0, 0);
+        gtk_tree_path_free(path);
+        count = 1;
+      }
+
+      g_free(text);
+
+      if(gtk_tree_model_iter_next(model, &iter) == FALSE)
+        gtk_tree_model_get_iter_first(model, &iter);
+    }
+
+    return TRUE;
+  }
+  return FALSE;
 }
 
-void GroupDialog_updatePageTitle(GtkWidget* page)
+
+static void proplist_selection_changed(GtkTreeSelection* selection, gpointer data)
 {
-  if(GroupDialog_getPage() == page)
+  // find out what type of entity we are trying to create
+  GtkTreeModel* model;
+  GtkTreeIter iter;
+  if(gtk_tree_selection_get_selected(selection, &model, &iter) == FALSE)
   {
-    GroupDialog_updatePageTitle(g_GroupDlg.m_window, g_current_page);
+    return;
   }
+
+  char* key;
+  char* val;
+  gtk_tree_model_get(model, &iter, 0, &key, 1, &val, -1);
+
+  gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntKeyField]), key);
+  gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntValueField]), val);
+
+  g_free(key);
+  g_free(val);
 }
 
+static void entity_check (GtkWidget *widget, gpointer data)
+{
+  if( !disable_spawn_get )
+    GetSpawnFlags();
+}
+
+static void entitylist_angle (GtkWidget *widget, gpointer data)
+{
+  SetKeyValue (edit_entity, "angle", (char*)data);
+  SetKeyValuePairs ();
+}
+
+static gint entityentry_keypress (GtkWidget* widget, GdkEventKey* event, gpointer data)
+{
+  if (event->keyval == GDK_Tab)
+  {
+    if (widget == EntWidgets[EntKeyField])
+    {
+      //gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntValueField]), "");
+      gtk_window_set_focus (GTK_WINDOW (g_pGroupDlg->m_pWidget), EntWidgets[EntValueField]);
+    }
+    else
+      gtk_window_set_focus (GTK_WINDOW (g_pGroupDlg->m_pWidget), EntWidgets[EntKeyField]);
+
+    return TRUE;
+  }
+  else if (event->keyval == GDK_Return)
+  {
+    if (widget == EntWidgets[EntKeyField])
+    {
+      gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntValueField]), "");
+      gtk_window_set_focus (GTK_WINDOW (g_pGroupDlg->m_pWidget), EntWidgets[EntValueField]);
+    }
+    else
+    {
+      AddProp ();
+    }
+    return TRUE;
+  }
+
+  return FALSE;
+}
+/*
+// add a new group, put all selected brushes into the group
+static void groupdlg_add (GtkWidget *widget, gpointer data)
+{
+  char* name = DoNameDlg ("New Group");
+
+  if (name != NULL)
+  {
+    // create a new group node
+    GtkCTreeNode *item;
+    item = gtk_ctree_insert_node (GTK_CTREE (g_wndGroup.m_pTree), g_pGroupDlg->m_hWorld, NULL, &name, 0,
+                                 tree_pixmaps[IMG_GROUP], tree_masks[IMG_GROUP],
+                                 tree_pixmaps[IMG_GROUP], tree_masks[IMG_GROUP], FALSE, TRUE);
+
+    // create a new group
+    group_t *g = Group_Alloc (name);
+    g->itemOwner = item;
+    g->next = g_pGroups;
+    g_pGroups = g;
+
+    // now add the selected brushes
+    // NOTE: it would be much faster to give the group_t for adding
+    // but Select_AddToGroup is the standard way for all other cases
+    Select_AddToGroup (name);
+    g_free (name);
+  }
+}
+*/
+static void switch_page (GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, gpointer data)
+{
+  char *text;
+  gtk_label_get(GTK_LABEL(gtk_notebook_get_tab_label(notebook, gtk_notebook_get_nth_page(notebook, page_num))), &text);
+  gtk_window_set_title (GTK_WINDOW (data), text);
+
+  gpointer item = g_object_get_data (G_OBJECT (g_pParentWnd->m_pWidget), "menu_misc_selectentitycolor");
+
+  if (g_pParentWnd->FloatingGroupDialog())
+  {
+    switch (page_num)
+    {
+    case 0: inspector_mode = W_ENTITY; break;
+    case 1: inspector_mode = W_TEXTURE; break;
+    case 2: inspector_mode = W_CONSOLE; break;
+    default: inspector_mode = W_GROUP; break;
+    }
+  }
+  else
+  {
+    if (page_num == 0)
+      inspector_mode = W_ENTITY;
+    else
+      inspector_mode = W_GROUP;
+  }
 
-#include "preferencesystem.h"
+  if (inspector_mode == W_ENTITY)
+    gtk_widget_set_sensitive (GTK_WIDGET (item), TRUE);
+  else
+    gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
+}
 
-void GroupDialog_Construct()
+// =============================================================================
+// GroupDlg class
+
+// NOTE: when a key is hit with group window focused, we catch in this handler but it gets propagated to mainframe too
+//   therefore the message will be intercepted and used as a ID_SELECTION_DESELECT
+static gint OnDialogKey (GtkWidget* widget, GdkEventKey* event, gpointer data)
 {
-  GlobalPreferenceSystem().registerPreference("EntityWnd", WindowPositionTrackerImportStringCaller(g_GroupDlg.m_position_tracker), WindowPositionTrackerExportStringCaller(g_GroupDlg.m_position_tracker));
+#ifdef DBG_PI
+  Sys_Printf("OnDialogKey\n");
+#endif
+  if ((event->keyval == GDK_Escape) && (g_pParentWnd->CurrentStyle() != MainFrame::eFloating))
+  {
+    // toggle off the group view (whatever part of it is currently displayed)
+    // this used to be done with a g_pParentWnd->OnViewEntity(); but it had bad consequences
+    // http://fenris.lokigames.com/show_bug.cgi?id=2773
+    widget_delete_hide (g_qeglobals_gui.d_entity);
+    return TRUE;
+  }
+  return FALSE;
+}
 
-  GlobalCommands_insert("ViewEntityInfo", FreeCaller<GroupDialog_ToggleShow>(), Accelerator('N'));
+GroupDlg::GroupDlg ()
+{
+  m_pWidget = NULL;
+  m_hWorld = NULL;
 }
-void GroupDialog_Destroy()
+
+#ifdef _WIN32
+extern void PositionWindowOnPrimaryScreen(window_position_t& position);
+#endif
+
+void GroupDlg::Create ()
 {
+  if (m_pWidget != NULL)
+    return;
+
+  GtkWidget* dlg = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+#ifdef _WIN32
+  if( g_PrefsDlg.m_bStartOnPrimMon ) {
+    PositionWindowOnPrimaryScreen( g_PrefsDlg.mWindowInfo.posEntityWnd );
+  }
+#endif
+  load_window_pos (dlg, g_PrefsDlg.mWindowInfo.posEntityWnd);
+
+  gtk_window_set_title (GTK_WINDOW (dlg), "Entities");
+  gtk_signal_connect (GTK_OBJECT (dlg), "delete_event", GTK_SIGNAL_FUNC (widget_delete_hide), NULL);
+  // catch 'Esc'
+  gtk_signal_connect (GTK_OBJECT (dlg), "key_press_event", GTK_SIGNAL_FUNC (OnDialogKey), NULL);
+  gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (g_pParentWnd->m_pWidget));
+  g_qeglobals_gui.d_entity = dlg;
+
+  {
+    GtkWidget* notebook = gtk_notebook_new ();
+    gtk_widget_show (notebook);
+    gtk_container_add (GTK_CONTAINER (dlg), notebook);
+    gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_BOTTOM);
+    m_pNotebook = notebook;
+
+    {
+      GtkWidget* vbox = gtk_vbox_new (FALSE, 2);
+      gtk_widget_show (vbox);
+      gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
+
+      {
+        GtkWidget* label = gtk_label_new ("Entities");
+        gtk_widget_show (label);
+        gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, label);
+      }
+
+      {
+        GtkWidget* split1 = gtk_vpaned_new ();
+        gtk_box_pack_start (GTK_BOX (vbox), split1, TRUE, TRUE, 0);
+        gtk_widget_show (split1);
+
+        {
+          GtkWidget* split2 = gtk_vpaned_new ();
+          gtk_paned_add1 (GTK_PANED (split1), split2);
+          gtk_widget_show (split2);
+
+          g_object_set_data (G_OBJECT (dlg), "split1", split1);
+          g_object_set_data (G_OBJECT (dlg), "split2", split2);
+
+          {
+            GtkWidget* vbox2 = gtk_vbox_new (FALSE, 2);
+            gtk_widget_show (vbox2);
+            gtk_paned_pack2 (GTK_PANED (split1), vbox2, FALSE, FALSE);
+
+            {
+              GtkWidget* scr = gtk_scrolled_window_new (NULL, NULL);
+              gtk_widget_show (scr);
+              gtk_paned_add1 (GTK_PANED (split2), scr);
+              gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scr), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
+              gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scr), GTK_SHADOW_IN);
+
+              {
+                GtkListStore* store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
+
+                GtkWidget* view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+                gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
+                g_signal_connect(G_OBJECT(view), "button_press_event", G_CALLBACK(eclasslist_button_press), NULL);
+                g_signal_connect(G_OBJECT(view), "key_press_event", G_CALLBACK(eclasslist_keypress), this);
+
+                {
+                  GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
+                  GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("Key", renderer, "text", 0, NULL);
+                  gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
+                }
+
+                {
+                  GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
+                  g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(eclasslist_selection_changed), dlg);
+                }
+
+                gtk_widget_show(view);
+
+                gtk_container_add(GTK_CONTAINER (scr), view);
+
+                g_object_unref(G_OBJECT(store));
+                EntWidgets[EntList] = view;
+                g_entlist_store = store;
+              }
+            }
+
+            {
+              GtkWidget* scr = gtk_scrolled_window_new (NULL, NULL);
+              gtk_widget_show (scr);
+              gtk_paned_add2 (GTK_PANED (split2), scr);
+              gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scr), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
+              gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scr), GTK_SHADOW_IN);
+
+              {
+                GtkWidget* text = gtk_text_view_new();
+                gtk_widget_set_size_request(text, 0, -1); // allow shrinking
+                gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
+                gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
+                gtk_widget_show (text);
+                gtk_container_add (GTK_CONTAINER (scr), text);
+                EntWidgets[EntComment] = text;
+              }
+            }
+
+            {
+              // Spawnflags (4 colums wide max, or window gets too wide.)
+              LayoutTable = gtk_table_new (4, 4, FALSE);
+              gtk_box_pack_start (GTK_BOX (vbox2), LayoutTable, FALSE, TRUE, 0);
+              gtk_widget_show(LayoutTable);
+
+              for (int i = 0; i < MAX_FLAGS; i++)
+              {
+                GtkWidget* check = gtk_check_button_new_with_label ("");
+                gtk_widget_ref (check);
+                gtk_signal_connect (GTK_OBJECT (check), "toggled", GTK_SIGNAL_FUNC (entity_check), NULL);
+                EntWidgets[EntCheck1+i] = check;
+              }
+
+                               if (g_pGameDescription->quake2) {
+                                       GtkWidget *check = gtk_check_button_new_with_label (_("!Easy"));
+                                       gtk_widget_show (check);
+                                       gtk_signal_connect (GTK_OBJECT (check), "toggled", GTK_SIGNAL_FUNC (entity_check), NULL);
+/*                                     gtk_table_attach (GTK_TABLE (table), check, 2, 3, 0, 1,
+                                                                               (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                                                                               (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);*/
+                                       EntWidgets[EntCheck17] = check;
+
+                                       check = gtk_check_button_new_with_label (_("!Medium"));
+                                       gtk_widget_show (check);
+                                       gtk_signal_connect (GTK_OBJECT (check), "toggled", GTK_SIGNAL_FUNC (entity_check), NULL);
+/*                                     gtk_table_attach (GTK_TABLE (table), check, 2, 3, 1, 2,
+                                                                               (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                                                                               (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);*/
+                                       EntWidgets[EntCheck18] = check;
+
+                                       check = gtk_check_button_new_with_label (_("!Hard"));
+                                       gtk_widget_show (check);
+                                       gtk_signal_connect (GTK_OBJECT (check), "toggled", GTK_SIGNAL_FUNC (entity_check), NULL);
+/*                                     gtk_table_attach (GTK_TABLE (table), check, 2, 3, 2, 3,
+                                                                               (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                                                                               (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);*/
+                                       EntWidgets[EntCheck19] = check;
+
+                                       check = gtk_check_button_new_with_label (_("!DeathMatch"));
+                                       gtk_widget_show (check);
+                                       gtk_signal_connect (GTK_OBJECT (check), "toggled", GTK_SIGNAL_FUNC (entity_check), NULL);
+/*                                     gtk_table_attach (GTK_TABLE (table), check, 2, 3, 3, 4,
+                                                                               (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                                                                               (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);*/
+                                       EntWidgets[EntCheck20] = check;
+                               }
+            }
+
+            {
+              GtkWidget* scr = gtk_scrolled_window_new (NULL, NULL);
+              gtk_widget_show (scr);
+              gtk_box_pack_start (GTK_BOX (vbox2), scr, TRUE, TRUE, 0);
+              gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scr), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+              gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scr), GTK_SHADOW_IN);
+
+              {
+                GtkListStore* store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
+
+                GtkWidget* view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+                gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
+
+                {
+                  GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
+                  GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("", renderer, "text", 0, NULL);
+                  gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
+                }
+
+                {
+                  GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
+                  GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("", renderer, "text", 1, NULL);
+                  gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
+                }
+
+                {
+                  GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
+                  g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(proplist_selection_changed), dlg);
+                }
+
+                gtk_widget_show(view);
+
+                gtk_container_add(GTK_CONTAINER (scr), view);
+
+                g_object_unref(G_OBJECT(store));
+
+                EntWidgets[EntProps] = view;
+                g_entprops_store = store;
+              }
+            }
+          }
+
+          int x = g_PrefsDlg.mWindowInfo.nEntitySplit1;
+          if (x != -1)
+          {
+            gtk_paned_set_position (GTK_PANED (split1), x);
+
+            while (gtk_events_pending ())  gtk_main_iteration ();
+            x = g_PrefsDlg.mWindowInfo.nEntitySplit2;
+
+            if (x != -1)
+              gtk_paned_set_position (GTK_PANED (split2), x);
+          }
+        }
+      }
+
+      {
+        GtkWidget* table = gtk_table_new (2, 2, FALSE);
+        gtk_widget_show (table);
+        gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, TRUE, 0);
+        gtk_table_set_row_spacings (GTK_TABLE (table), 3);
+        gtk_table_set_col_spacings (GTK_TABLE (table), 5);
+
+        {
+          GtkWidget* entry = gtk_entry_new ();
+          gtk_widget_show (entry);
+          gtk_table_attach (GTK_TABLE (table), entry, 1, 2, 0, 1,
+                            (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                            (GtkAttachOptions) (0), 0, 0);
+          gtk_widget_set_events (entry, GDK_KEY_PRESS_MASK);
+          gtk_signal_connect (GTK_OBJECT (entry), "key_press_event",
+                             GTK_SIGNAL_FUNC (entityentry_keypress), this);
+          EntWidgets[EntKeyField] = entry;
+        }
+
+        {
+          GtkWidget* entry = gtk_entry_new ();
+          gtk_widget_show (entry);
+          gtk_table_attach (GTK_TABLE (table), entry, 1, 2, 1, 2,
+                            (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                            (GtkAttachOptions) (0), 0, 0);
+          gtk_widget_set_events (entry, GDK_KEY_PRESS_MASK);
+          gtk_signal_connect (GTK_OBJECT (entry), "key_press_event",
+                             GTK_SIGNAL_FUNC (entityentry_keypress), this);
+          EntWidgets[EntValueField] = entry;
+        }
+
+        {
+          GtkWidget* label = gtk_label_new (_("Value"));
+          gtk_widget_show (label);
+          gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
+                            (GtkAttachOptions) (GTK_FILL),
+                            (GtkAttachOptions) (0), 0, 0);
+          gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+        }
+
+        {
+          GtkWidget* label = gtk_label_new (_("Key"));
+          gtk_widget_show (label);
+          gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
+                            (GtkAttachOptions) (GTK_FILL),
+                            (GtkAttachOptions) (0), 0, 0);
+          gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+        }
+      }
+
+      {
+        GtkWidget* hbox = gtk_hbox_new (FALSE, 5);
+        gtk_widget_show (hbox);
+        gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
+
+        {
+          GtkWidget* table = gtk_table_new (3, 3, TRUE);
+          gtk_widget_show (table);
+          gtk_box_pack_start (GTK_BOX (hbox), table, FALSE, TRUE, 0);
+
+          {
+            GtkWidget* button = gtk_button_new_with_label (_("360"));
+            gtk_widget_show (button);
+            gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"360");
+            gtk_table_attach (GTK_TABLE (table), button, 2, 3, 1, 2,
+                              (GtkAttachOptions) (GTK_FILL),
+                              (GtkAttachOptions) (GTK_FILL), 0, 0);
+          }
+
+          {
+            GtkWidget* button = gtk_button_new_with_label (_("45"));
+            gtk_widget_show (button);
+            gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"45");
+            gtk_table_attach (GTK_TABLE (table), button, 2, 3, 0, 1,
+                              (GtkAttachOptions) (GTK_FILL),
+                              (GtkAttachOptions) (GTK_FILL), 0, 0);
+          }
+
+          {
+            GtkWidget* button = gtk_button_new_with_label (_("90"));
+            gtk_widget_show (button);
+            gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"90");
+            gtk_table_attach (GTK_TABLE (table), button, 1, 2, 0, 1,
+                              (GtkAttachOptions) (GTK_FILL),
+                              (GtkAttachOptions) (GTK_FILL), 0, 0);
+          }
+
+
+          {
+            GtkWidget* button = gtk_button_new_with_label (_("135"));
+            gtk_widget_show (button);
+            gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"135");
+            gtk_table_attach (GTK_TABLE (table), button, 0, 1, 0, 1,
+                              (GtkAttachOptions) (GTK_FILL),
+                              (GtkAttachOptions) (GTK_FILL), 0, 0);
+          }
+
+          {
+            GtkWidget* button = gtk_button_new_with_label (_("180"));
+            gtk_widget_show (button);
+            gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"180");
+            gtk_table_attach (GTK_TABLE (table), button, 0, 1, 1, 2,
+                              (GtkAttachOptions) (GTK_FILL),
+                              (GtkAttachOptions) (GTK_FILL), 0, 0);
+          }
+
+          {
+            GtkWidget* button = gtk_button_new_with_label (_("225"));
+            gtk_widget_show (button);
+            gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"225");
+            gtk_table_attach (GTK_TABLE (table), button, 0, 1, 2, 3,
+                              (GtkAttachOptions) (GTK_FILL),
+                              (GtkAttachOptions) (GTK_FILL), 0, 0);
+          }
+
+          {
+            GtkWidget* button = gtk_button_new_with_label (_("270"));
+            gtk_widget_show (button);
+            gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"270");
+            gtk_table_attach (GTK_TABLE (table), button, 1, 2, 2, 3,
+                              (GtkAttachOptions) (GTK_FILL),
+                              (GtkAttachOptions) (GTK_FILL), 0, 0);
+          }
+
+          {
+            GtkWidget* button = gtk_button_new_with_label (_("315"));
+            gtk_widget_show (button);
+            gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"315");
+            gtk_table_attach (GTK_TABLE (table), button, 2, 3, 2, 3,
+                              (GtkAttachOptions) (GTK_FILL),
+                              (GtkAttachOptions) (GTK_FILL), 0, 0);
+          }
+        }
+
+        {
+          GtkWidget* vbox2 = gtk_vbox_new (FALSE, 0);
+          gtk_widget_show (vbox2);
+          gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 0);
+
+          {
+            GtkWidget* button = gtk_button_new_with_label (_("Reset"));
+            gtk_widget_show (button);
+            gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (ResetEntity), NULL);
+            gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
+          }
+
+          {
+            GtkWidget* button = gtk_button_new_with_label (_("Up"));
+            gtk_widget_show (button);
+            gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"-1");
+            gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
+          }
+
+          {
+            GtkWidget* button = gtk_button_new_with_label (_("Dn"));
+            gtk_widget_show (button);
+            gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"-2");
+            gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
+          }
+        }
+
+        {
+          GtkWidget* vbox2 = gtk_vbox_new (FALSE, 0);
+          gtk_widget_show (vbox2);
+          gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 0);
+
+          {
+            GtkWidget* button = gtk_button_new_with_label (_("Del Key/Pair"));
+            gtk_widget_show (button);
+            gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (DelProp), NULL);
+            gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
+          }
+
+          {
+            GtkWidget* button = gtk_button_new_with_label (_("Sound..."));
+            gtk_widget_show (button);
+            gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (AssignSound), NULL);
+            gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
+          }
+
+          {
+            GtkWidget* button = gtk_button_new_with_label (_("Model..."));
+            gtk_widget_show (button);
+            gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (AssignModel), NULL);
+            gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
+          }
+        }
+      }
+    }
+
+    if (g_pParentWnd->FloatingGroupDialog())
+    {
+      {
+        GtkWidget* scr = gtk_scrolled_window_new (NULL, NULL);
+        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scr), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+        gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scr), GTK_SHADOW_IN);
+        gtk_widget_show (scr);
+        gtk_container_set_border_width (GTK_CONTAINER (scr), 3);
+
+        {
+          GtkWidget* text = gtk_text_view_new ();
+          gtk_widget_set_size_request(text, 0, -1); // allow shrinking
+          gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
+          gtk_text_view_set_editable (GTK_TEXT_VIEW(text), FALSE);
+          gtk_container_add (GTK_CONTAINER (scr), text);
+          gtk_widget_show (text);
+          g_qeglobals_gui.d_edit = text;
+        }
+
+        {
+          GtkWidget* label = gtk_label_new (_("Console"));
+          gtk_widget_show (label);
+          gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scr, label);
+        }
+      }
+    }
+
+
+  //++timo NOTE: this part for grouping code, don't remove! (we'll put it back in sometime soon)
+
+  /*
+  vbox = gtk_vbox_new (FALSE, 5);
+  gtk_widget_show (vbox);
+  gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
+
+  scr = gtk_scrolled_window_new (NULL, NULL);
+  gtk_widget_show (scr);
+  gtk_box_pack_start (GTK_BOX (vbox), scr, TRUE, TRUE, 0);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scr), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+
+  ctree = gtk_ctree_new (1, 0);
+  gtk_widget_show (ctree);
+  gtk_container_add (GTK_CONTAINER (scr), ctree);
+  gtk_clist_column_titles_hide (GTK_CLIST (ctree));
+  m_pTree = ctree;
+
+  hbox = gtk_hbox_new (FALSE, 5);
+  gtk_widget_show (hbox);
+  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
+
+  button = gtk_button_new_with_label (_("Add..."));
+  gtk_widget_show (button);
+  gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (groupdlg_add), NULL);
+  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+  gtk_widget_set_usize (button, 60, -2);
+
+  button = gtk_button_new_with_label (_("Edit..."));
+  gtk_widget_show (button);
+  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+  gtk_widget_set_usize (button, 60, -2);
+
+  button = gtk_button_new_with_label (_("Delete"));
+  gtk_widget_show (button);
+  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+  gtk_widget_set_usize (button, 60, -2);
+
+  label = gtk_label_new (_("Groups"));
+  gtk_widget_show (label);
+  gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, label);
+  */
+  inspector_mode = W_ENTITY;
+  //  gtk_window_set_title (GTK_WINDOW (dlg), _("Entities"));
+  m_pWidget = dlg;
+  /*
+  load_pixmap ("grouptree1.bmp", g_pParentWnd->m_pWidget, &tree_pixmaps[0], &tree_masks[0]);
+  load_pixmap ("grouptree2.bmp", g_pParentWnd->m_pWidget, &tree_pixmaps[1], &tree_masks[1]);
+  load_pixmap ("grouptree3.bmp", g_pParentWnd->m_pWidget, &tree_pixmaps[2], &tree_masks[2]);
+  load_pixmap ("grouptree4.bmp", g_pParentWnd->m_pWidget, &tree_pixmaps[3], &tree_masks[3]);
+  load_pixmap ("grouptree5.bmp", g_pParentWnd->m_pWidget, &tree_pixmaps[4], &tree_masks[4]);
+  load_pixmap ("grouptree6.bmp", g_pParentWnd->m_pWidget, &tree_pixmaps[5], &tree_masks[5]);
+  load_pixmap ("grouptree7.bmp", g_pParentWnd->m_pWidget, &tree_pixmaps[6], &tree_masks[6]);
+
+  Group_Init();
+*/
+    g_signal_connect (G_OBJECT (notebook), "switch_page", G_CALLBACK (switch_page), dlg);
+  }
 }
+