* reactivated some old quake2 code
[xonotic/netradiant.git] / radiant / groupdialog.cpp
1 /*
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 //
23 // Floating dialog that contains a notebook with at least Entities and Group tabs
24 // I merged the 2 MS Windows dialogs in a single class
25 //
26 // Leonardo Zide (leo@lokigames.com)
27 //
28
29 #ifndef _WIN32
30   #include <unistd.h>
31 #endif
32 #include <gdk/gdkkeysyms.h>
33 #include <glib/gi18n.h>
34 #include "stdafx.h"
35 #include "groupdialog.h"
36
37 GtkWidget*      EntWidgets[EntLast];
38 GtkListStore* g_entlist_store;
39 GtkListStore* g_entprops_store;
40 int                                     inspector_mode;         // W_TEXTURE, W_ENTITY, or W_CONSOLE
41 qboolean                multiple_entities;
42 qboolean                disable_spawn_get = false;
43 entity_t                *edit_entity;
44 /*
45 static GdkPixmap *tree_pixmaps[7];
46 static GdkBitmap *tree_masks[7];
47 */
48 #define IMG_PATCH 0
49 #define IMG_BRUSH 1
50 #define IMG_GROUP 2
51 #define IMG_ENTITY 3
52 #define IMG_ENTITYGROUP 4
53 #define IMG_MODEL 5
54 #define IMG_SCRIPT 6
55
56 // misc group support
57 #define MAX_GROUPS 4096
58 #define GROUP_DELIMETER '@'
59 #define GROUPNAME "QER_Group_%i"
60
61 GroupDlg g_wndGroup;
62 GroupDlg *g_pGroupDlg = &g_wndGroup;
63
64 // group_t are loaded / saved through "group_info" entities
65 // they hold epairs for group settings and additionnal access info (tree nodes)
66 group_t *g_pGroups = NULL;
67
68 // the number of active spawnflags
69 static int spawnflag_count;
70 // table: index, match spawnflag item to the spawnflag index (i.e. which bit)
71 static int spawn_table[MAX_FLAGS];
72 // we change the layout depending on how many spawn flags we need to display
73 // the table is a 4x4 in which we need to put the comment box EntWidgets[EntComment] and the spawn flags..
74 static GtkWidget *LayoutTable;
75 // 0: none of them are hooked
76 // 1: only the text, 2: text and four checks, 3: text and 8 checks
77 static int widget_state = 0;
78
79 static void entity_check (GtkWidget *widget, gpointer data);
80
81 // =============================================================================
82 // Global functions
83
84 /*
85 ===============================================================
86
87 ENTITY WINDOW
88
89 ===============================================================
90 */
91
92 void FillClassList ()
93 {
94   GtkListStore* store = g_entlist_store;
95
96   gtk_list_store_clear(store);
97
98   for (eclass_t* e = eclass ; e ; e = e->next)
99   {
100     GtkTreeIter iter;
101     gtk_list_store_append(store, &iter);
102     gtk_list_store_set(store, &iter, 0, e->name, 1, e, -1);
103   }
104 }
105
106 // SetKeyValuePairs
107 //
108 // Reset the key/value (aka property) listbox and fill it with the
109 // k/v pairs from the entity being edited.
110 //
111
112 void SetKeyValuePairs (bool bClearMD3)
113 {
114   GtkListStore* store = g_entprops_store;
115
116   gtk_list_store_clear(store);
117
118   if (edit_entity == NULL)
119   {
120     // if there's no entity, then display no key/values
121     return;
122   }
123
124   // save current key/val pair around filling epair box
125   // row_select wipes it and sets to first in list
126   Str strKey = gtk_entry_get_text (GTK_ENTRY (EntWidgets[EntKeyField]));
127   Str strVal = gtk_entry_get_text (GTK_ENTRY (EntWidgets[EntValueField]));
128
129
130   // Walk through list and add pairs
131   for(epair_t* epair = edit_entity->epairs ; epair ; epair = epair->next)
132   {
133     GtkTreeIter iter;
134     gtk_list_store_append(store, &iter);
135     gtk_list_store_set(store, &iter, 0, epair->key, 1, epair->value, -1);
136   }
137
138   gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntKeyField]), strKey.GetBuffer());
139   gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntValueField]), strVal.GetBuffer());
140
141   Sys_UpdateWindows(W_CAMERA | W_XY);
142 }
143
144 // SetSpawnFlags
145 //
146 // Update the checkboxes to reflect the flag state of the entity
147 //
148 void SetSpawnFlags(void)
149 {
150   int f, i, v;
151
152         disable_spawn_get = true;
153
154   f = atoi(ValueForKey (edit_entity, "spawnflags"));
155   for (i=0 ; i<spawnflag_count ; i++)
156   {
157     v = !!(f&(1<<spawn_table[i]));
158     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (EntWidgets[EntCheck1+i]), v);
159   }
160   // take care of the remaining ones
161   for (i=spawnflag_count ; i<MAX_FLAGS ; i++)
162   {
163     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (EntWidgets[EntCheck1+i]), FALSE);
164   }
165
166         disable_spawn_get = false;
167 }
168
169 // GetSpawnFlags
170 //
171 // Update the entity flags to reflect the state of the checkboxes
172 //
173 // NOTE: this function had a tendency to add "spawnflags" "0" on most entities
174 //   if this wants to set spawnflags to zero, remove the key
175
176 void GetSpawnFlags(void)
177 {
178   int f, i, v;
179   char sz[32];
180
181   f = 0;
182   for (i=0 ; i<spawnflag_count ; i++)
183   {
184     v = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (EntWidgets[EntCheck1+i]));
185     f |= v<<spawn_table[i];
186   }
187
188   if (f==0)
189   {
190     // remove all "spawnflags" keys
191     if (multiple_entities)
192     {
193       brush_t   *b;
194
195       for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
196         DeleteKey (b->owner, "spawnflags");
197     }
198     else
199       DeleteKey (edit_entity, "spawnflags");
200   }
201   else
202   {
203     sprintf (sz, "%i", f);
204     if (multiple_entities)
205     {
206       brush_t   *b;
207
208       for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
209         SetKeyValue(b->owner, "spawnflags", sz);
210     }
211     else
212       SetKeyValue (edit_entity, "spawnflags", sz);
213   }
214   SetKeyValuePairs ();
215 }
216
217 //#define DBG_UPDATESEL
218
219 // UpdateSel
220 //
221 // Update the listbox, checkboxes and k/v pairs to reflect the new selection
222 // iIndex is the index in the list box with the class name, -1 if not found
223 bool UpdateSel(int iIndex, eclass_t *pec)
224 {
225   int i, next_state;
226   brush_t *b;
227
228   // syndrom of crappy code, we may get into stack overflowing crap with this function and Gtk
229   // if we play with the list of entity classes
230   // using a static flag to prevent recursion
231   static bool bBlockUpdate = false;
232
233   if (bBlockUpdate)
234     return FALSE; // NOTE TTimo wtf is the return value for anyway?
235
236 #ifdef DBG_UPDATESEL
237   Sys_FPrintf(SYS_WRN, "UpdateSel\n");
238 #endif
239
240   if (selected_brushes.next == &selected_brushes)
241   {
242     edit_entity = world_entity;
243     multiple_entities = false;
244   }
245   else
246   {
247     edit_entity = selected_brushes.next->owner;
248     for (b=selected_brushes.next->next ; b != &selected_brushes ; b=b->next)
249     {
250       if (b->owner != edit_entity)
251       {
252         multiple_entities = true;
253         break;
254       }
255     }
256   }
257
258   if (iIndex != -1)
259   {
260 #ifdef DBG_UPDATESEL
261     Sys_FPrintf(SYS_WRN,"Setting focus_row to %d\n", iIndex);
262 #endif
263     bBlockUpdate = true;
264
265     GtkTreeView* view = GTK_TREE_VIEW(EntWidgets[EntList]);
266     GtkTreePath* path = gtk_tree_path_new();
267     gtk_tree_path_append_index(path, iIndex);
268     gtk_tree_selection_select_path(gtk_tree_view_get_selection(view), path);
269     gtk_tree_view_scroll_to_cell(view, path, NULL, FALSE, 0, 0);
270     gtk_tree_path_free(path);
271
272     bBlockUpdate = false;
273   }
274
275   if (pec == NULL)
276     return TRUE;
277
278   // Set up the description
279   {
280     GtkTextBuffer* buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(EntWidgets[EntComment]));
281     gtk_text_buffer_set_text (buffer, pec->comments, -1);
282   }
283
284   spawnflag_count = 0;
285
286   // do a first pass to count the spawn flags, don't touch the widgets, we don't know in what state they are
287   for (i=0 ; i<MAX_FLAGS ; i++)
288   {
289     if (pec->flagnames[i] && pec->flagnames[i][0] != 0 && strcmp(pec->flagnames[i],"-"))
290     {
291       spawn_table[spawnflag_count] = i;
292       spawnflag_count++;
293     }
294   }
295
296   // what's new widget state
297   if (spawnflag_count==0)
298     next_state = 1;
299   else if (spawnflag_count<=4)
300     next_state = 2;
301   else if (spawnflag_count<=8)
302     next_state = 3;
303   else if (spawnflag_count<=12)
304     next_state = 4;
305   else
306     next_state = 5;
307   widget_state = next_state;
308   static int last_count = 0;
309
310   // disable all remaining boxes
311   // NOTE: these boxes might not even be on display
312   for (i = 0; i < last_count; i++)
313   {
314     GtkWidget* widget = EntWidgets[EntCheck1+i];
315     gtk_label_set_text (GTK_LABEL (GTK_BIN (widget)->child), " ");
316     gtk_widget_hide (widget);
317     gtk_widget_ref (widget);
318     gtk_container_remove (GTK_CONTAINER (LayoutTable), widget);
319   }
320   last_count = spawnflag_count;
321
322   for (i=0 ; i<spawnflag_count ; i++)
323   {
324     GtkWidget* widget = EntWidgets[EntCheck1+i];
325     gtk_widget_show (widget);
326
327     Str str;
328     str = pec->flagnames[spawn_table[i]];
329     str.MakeLower ();
330
331 //    gtk_table_attach (GTK_TABLE (LayoutTable), widget, i%4, i%4+1, i/4, i/4+1,
332     gtk_table_attach (GTK_TABLE (LayoutTable), widget, i%4, i%4+1, i/4, i/4+1,
333                       (GtkAttachOptions) (GTK_FILL),
334                       (GtkAttachOptions) (GTK_FILL), 0, 0);
335     gtk_widget_unref (widget);
336
337     gtk_label_set_text (GTK_LABEL (GTK_BIN (widget)->child), str.GetBuffer ());
338   }
339
340   SetSpawnFlags();
341
342   SetKeyValuePairs();
343
344   return TRUE;
345 }
346
347 bool UpdateEntitySel(eclass_t *pec)
348 {
349 #ifdef DBG_UPDATESEL
350   Sys_FPrintf(SYS_WRN, "UpdateEntitySel\n");
351 #endif
352
353   GtkTreeModel* model = GTK_TREE_MODEL(g_entlist_store);
354   GtkTreeIter iter;
355   unsigned int i = 0;
356   for(gboolean good = gtk_tree_model_get_iter_first(model, &iter); good != FALSE; good = gtk_tree_model_iter_next(model, &iter))
357   {
358     char* text;
359     gtk_tree_model_get(model, &iter, 0, &text, -1);
360     if (strcmp (text, pec->name) == 0)
361     {
362 #ifdef DBG_UPDATESEL
363       Sys_FPrintf(SYS_WRN, "found a match: %d %s\n", i, pec->name);
364 #endif
365       return UpdateSel (i, pec);
366     }
367     g_free(text);
368     ++i;
369   }
370   return UpdateSel (-1, pec);
371 }
372
373 // CreateEntity
374 //
375 // Creates a new entity based on the currently selected brush and entity type.
376 //
377
378 void CreateEntity(void)
379 {
380   GtkTreeView* view = GTK_TREE_VIEW(EntWidgets[EntList]);
381
382   // check to make sure we have a brush
383   if (selected_brushes.next == &selected_brushes)
384   {
385     gtk_MessageBox(g_pParentWnd->m_pWidget, "You must have a selected brush to create an entity", "info");
386     return;
387   }
388
389   // find out what type of entity we are trying to create
390   GtkTreeModel* model;
391   GtkTreeIter iter;
392   if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(view), &model, &iter) == FALSE)
393   {
394     gtk_MessageBox (g_pParentWnd->m_pWidget, "You must have a selected class to create an entity", "info");
395     return;
396   }
397
398   char* text;
399   gtk_tree_model_get(model, &iter, 0, &text, -1);
400   CreateEntityFromName(text, vec3_origin);
401   g_free(text);
402
403   if (selected_brushes.next == &selected_brushes)
404     edit_entity = world_entity;
405   else
406     edit_entity = selected_brushes.next->owner;
407
408   SetKeyValuePairs();
409   Select_Deselect ();
410   Select_Brush (edit_entity->brushes.onext);
411   Sys_UpdateWindows(W_ALL);
412 }
413
414 /*
415 ===============
416 AddProp
417
418 ===============
419 */
420 void AddProp()
421 {
422   if (edit_entity == NULL)
423     return;
424
425   // Get current selection text
426   const char* key = gtk_entry_get_text (GTK_ENTRY (EntWidgets[EntKeyField]));
427   const char* value = gtk_entry_get_text (GTK_ENTRY (EntWidgets[EntValueField]));
428
429
430   // TTimo: if you change the classname to worldspawn you won't merge back in the structural brushes but create a parasite entity
431   if (!strcmp(key, "classname") && !strcmp(value, "worldspawn"))
432   {
433     gtk_MessageBox(g_pParentWnd->m_pWidget,  "Cannot change \"classname\" key back to worldspawn.", NULL, MB_OK );
434     return;
435   }
436
437
438         // RR2DO2: we don't want spaces in entity keys
439         if (strstr( key, " " ))
440         {
441     gtk_MessageBox(g_pParentWnd->m_pWidget, "No spaces are allowed in entity keys.", NULL, MB_OK );
442     return;
443         }
444
445   if (multiple_entities)
446   {
447     brush_t *b;
448
449     for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
450       SetKeyValue(b->owner, key, value);
451   }
452   else
453     SetKeyValue(edit_entity, key, value);
454
455   // refresh the prop listbox
456   SetKeyValuePairs();
457
458
459 #ifdef USEPLUGINENTITIES
460   // if it's a plugin entity, perhaps we need to update some drawing parameters
461   // NOTE: perhaps moving this code to a seperate func would help if we need it in other places
462   // TODO: we need to call some update func in the IPluginEntity in case model name changes etc.
463   // ( for the moment only bounding brush is updated ), see UpdateModelBrush in Ritual's Q3Radiant
464   if (edit_entity->eclass->nShowFlags & ECLASS_PLUGINENTITY)
465   {
466     vec3_t mins, maxs;
467     edit_entity->pPlugEnt->GetBounds( mins, maxs );
468     // replace old bounding brush by newly computed one
469     // NOTE: this part is similar to Entity_BuildModelBrush in Ritual's Q3Radiant, it can be
470     // usefull moved into a seperate func
471     brush_t *b,*oldbrush;
472     if (edit_entity->brushes.onext != &edit_entity->brushes)
473       oldbrush = edit_entity->brushes.onext;
474     b = Brush_Create (mins, maxs, &edit_entity->eclass->texdef);
475     Entity_LinkBrush (edit_entity, b);
476     Brush_Build( b, true );
477     Select_Deselect();
478     Brush_AddToList (edit_entity->brushes.onext, &selected_brushes);
479     if (oldbrush)
480       Brush_Free( oldbrush );
481   }
482 #endif // USEPLUGINENTITIES
483 }
484
485 /*
486 ===============
487 DelProp
488
489 ===============
490 */
491 void DelProp(void)
492 {
493   if (edit_entity == NULL)
494     return;
495
496   // Get current selection text
497   const char* key = gtk_entry_get_text (GTK_ENTRY (EntWidgets[EntKeyField]));
498
499   if (multiple_entities)
500   {
501     brush_t *b;
502
503     for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
504       DeleteKey(b->owner, key);
505   }
506   else
507     DeleteKey(edit_entity, key);
508
509   // refresh the prop listbox
510   SetKeyValuePairs();
511 }
512
513 void ResetEntity ()
514 {
515   epair_t *pep;
516   int i;
517
518   if (edit_entity == NULL)
519     return;
520
521   if (multiple_entities)
522   {
523     brush_t *b;
524
525     for (b=selected_brushes.next; b != &selected_brushes; b=b->next)
526       for (pep = b->owner->epairs; pep; )
527       {
528         if (strcmp (pep->key, "classname") != 0)
529         {
530           DeleteKey (b->owner, pep->key);
531           pep = b->owner->epairs;
532         }
533         else
534           pep = pep->next;
535       }
536   }
537   else
538     for (pep = edit_entity->epairs; pep; )
539     {
540       if (strcmp (pep->key, "classname") != 0)
541       {
542         DeleteKey (edit_entity, pep->key);
543         pep = edit_entity->epairs;
544       }
545       else
546         pep = pep->next;
547     }
548
549   // refresh the dialog
550   SetKeyValuePairs ();
551   for (i = EntCheck1; i <= EntCheck16; i++)
552     gtk_signal_handler_block_by_func (GTK_OBJECT (EntWidgets[i]), GTK_SIGNAL_FUNC (entity_check), NULL);
553   SetSpawnFlags ();
554   for (i = EntCheck1; i <= EntCheck16; i++)
555     gtk_signal_handler_unblock_by_func (GTK_OBJECT (EntWidgets[i]), GTK_SIGNAL_FUNC (entity_check), NULL);
556 }
557
558 bool GetSelectAllCriteria(CString &strKey, CString &strVal)
559 {
560   GtkTreeModel* model;
561   GtkTreeIter iter;
562   if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(EntWidgets[EntProps])), &model, &iter)
563     && (inspector_mode == W_ENTITY)
564     && GTK_WIDGET_VISIBLE (g_pGroupDlg->m_pWidget))
565   {
566     strKey = gtk_entry_get_text (GTK_ENTRY (EntWidgets[EntKeyField]));
567     strVal = gtk_entry_get_text (GTK_ENTRY (EntWidgets[EntValueField]));
568     return TRUE;
569   }
570   return FALSE;
571 }
572
573
574 void AssignSound()
575 {
576   char buffer[NAME_MAX];
577
578   strcpy (buffer, g_qeglobals.m_strHomeMaps.GetBuffer());
579   strcat (buffer, "sound/");
580
581   if( access(buffer, R_OK) != 0 )
582   {
583     // just go to fsmain
584     strcpy (buffer, g_qeglobals.m_strHomeMaps.GetBuffer());
585     strcat (buffer, "/");
586   }
587
588   const char *filename = file_dialog (g_pGroupDlg->m_pWidget, TRUE, _("Open Wav File"), buffer, "sound");
589   if (filename != NULL)
590   {
591     gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntKeyField]), "noise");
592     char *aux = vfsExtractRelativePath (filename);
593     CString str;
594     if (aux)
595       str = aux;
596     else
597     {
598       Sys_FPrintf (SYS_WRN, "WARNING: could not extract the relative path, using full path instead\n");
599       str = filename;
600     }
601
602     gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntValueField]), str.GetBuffer());
603     AddProp();
604   }
605 }
606
607 void AssignModel()
608 {
609   char buffer[NAME_MAX];
610
611   strcpy (buffer, g_qeglobals.m_strHomeMaps.GetBuffer());
612   strcat (buffer, "models/");
613
614   if( access(buffer, R_OK) != 0 )
615   {
616     // just go to fsmain
617     strcpy (buffer, g_qeglobals.m_strHomeMaps.GetBuffer());
618     strcat (buffer, "/");
619   }
620
621   const char *filename = file_dialog (g_pGroupDlg->m_pWidget, TRUE, _("Open Model"), buffer, MODEL_MAJOR);
622   if (filename != NULL)
623   {
624     gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntKeyField]), "model");
625     // use VFS to get the correct relative path
626     char *aux = vfsExtractRelativePath (filename);
627     CString str;
628     if (aux)
629       str = aux;
630     else
631     {
632       Sys_FPrintf (SYS_WRN, "WARNING: could not extract the relative path, using full path instead\n");
633       str = filename;
634     }
635
636     gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntValueField]), str.GetBuffer());
637     AddProp();
638     edit_entity->brushes.onext->bModelFailed = false;
639   }
640 }
641
642 /*
643 ==============
644 SetInspectorMode
645 ==============
646 */
647 void SetInspectorMode(int iType)
648 {
649   if (iType == W_GROUP)
650     gtk_MessageBox(g_pParentWnd->m_pWidget, "Brush grouping is not functional yet", NULL, MB_OK | MB_ICONWARNING );
651
652   if (!g_pParentWnd->FloatingGroupDialog() &&
653       (iType == W_TEXTURE || iType == W_CONSOLE))
654     return;
655
656   // Is the caller asking us to cycle to the next window?
657   if (iType == -1)
658   {
659     if (inspector_mode == W_ENTITY)
660       iType = W_TEXTURE;
661     else if (inspector_mode == W_TEXTURE)
662       iType = W_CONSOLE;
663     else if (inspector_mode == W_CONSOLE)
664       iType = W_GROUP;
665     else
666       iType = W_ENTITY;
667   }
668
669   switch(iType)
670   {
671   case W_ENTITY:
672     // entity is always first in the inspector
673     gtk_window_set_title (GTK_WINDOW (g_qeglobals_gui.d_entity), "Entities");
674     gtk_notebook_set_page (GTK_NOTEBOOK (g_pGroupDlg->m_pNotebook), 0);
675     break;
676
677   case W_TEXTURE:
678     g_pParentWnd->GetTexWnd()->FocusEdit();
679     gtk_window_set_title (GTK_WINDOW (g_qeglobals_gui.d_entity), "Textures");
680     if (g_pParentWnd->FloatingGroupDialog())
681       gtk_notebook_set_page (GTK_NOTEBOOK (g_pGroupDlg->m_pNotebook), 1);
682     break;
683
684   case W_CONSOLE:
685     gtk_window_set_title (GTK_WINDOW (g_qeglobals_gui.d_entity), "Console");
686     if (g_pParentWnd->FloatingGroupDialog())
687       gtk_notebook_set_page (GTK_NOTEBOOK (g_pGroupDlg->m_pNotebook), 2);
688     break;
689
690   case W_GROUP:
691     if (g_pParentWnd->FloatingGroupDialog())
692       gtk_notebook_set_page (GTK_NOTEBOOK (g_pGroupDlg->m_pNotebook), 3);
693     else
694       gtk_notebook_set_page (GTK_NOTEBOOK (g_pGroupDlg->m_pNotebook), 1);
695     break;
696
697   default:
698     break;
699   }
700 }
701
702 void Group_Add(entity_t *e)
703 {
704   /*
705   group_t *g = (group_t*)qmalloc(sizeof(group_t));
706   g->epairs = e->epairs;
707   g->next = NULL;
708   e->epairs = NULL;
709
710   // create a new group node
711   char *text = ValueForKey(g->epairs, "group");
712   g->itemOwner = gtk_ctree_insert_node (GTK_CTREE (g_wndGroup.m_pTree), g_wndGroup.m_hWorld, NULL, &text, 0,
713                                         tree_pixmaps[IMG_GROUP], tree_masks[IMG_GROUP],
714                                         tree_pixmaps[IMG_GROUP], tree_masks[IMG_GROUP], TRUE, TRUE);
715   g->next = g_pGroups;
716   g_pGroups = g;
717   */
718 }
719 /*
720 group_t* Group_Alloc(char *name)
721 {
722   group_t *g = (group_t*)qmalloc(sizeof(group_t));
723   SetKeyValue( g->epairs, "group", name );
724   return g;
725 }
726
727 group_t* Group_ForName(const char * name)
728 {
729   group_t *g = g_pGroups;
730   while (g != NULL)
731   {
732     if (strcmp( ValueForKey(g->epairs,"group"), name ) == 0)
733       break;
734     g = g->next;
735   }
736   return g;
737 }
738
739 void Group_AddToItem(brush_t *b, GtkCTreeNode* item)
740 {
741   int nImage = IMG_BRUSH;
742   if (!g_qeglobals.m_bBrushPrimitMode)
743   {
744     return;
745   }
746   const char *pName = NULL;
747   //  const char *pNamed = Brush_GetKeyValue(b, "name");
748
749   if (!b->owner || (b->owner == world_entity))
750   {
751     if (b->patchBrush)
752     {
753       pName = "Generic Patch";
754       nImage = IMG_PATCH;
755     }
756     else
757     {
758       pName = "Generic Brush";
759       nImage = IMG_BRUSH;
760     }
761   }
762   else
763   {
764     pName = b->owner->eclass->name;
765     if (b->owner->eclass->fixedsize)
766     {
767       nImage = IMG_ENTITY;
768     }
769     else
770     {
771       nImage = IMG_ENTITYGROUP;
772     }
773   }
774
775   GtkCTreeNode *newItem;
776   int i = (b->patchBrush) ? IMG_PATCH : IMG_BRUSH;
777   newItem = gtk_ctree_insert_node (GTK_CTREE (g_wndGroup.m_pTree), item, NULL, (gchar**)&pName, 0,
778                                    tree_pixmaps[i], tree_masks[i], tree_pixmaps[i],
779                                    tree_masks[i], TRUE, TRUE);
780   gtk_ctree_node_set_row_data (GTK_CTREE (g_wndGroup.m_pTree), newItem, b);
781   b->itemOwner = newItem;
782 }
783 */
784 void Group_RemoveBrush(brush_t *b)
785 {
786   /*
787   if (!g_qeglobals.m_bBrushPrimitMode)
788   {
789     return;
790   }
791   if (b->itemOwner)
792   {
793     gtk_ctree_remove_node (GTK_CTREE (g_pGroupDlg->m_pTree), b->itemOwner);
794     b->itemOwner = NULL;
795   }
796   DeleteKey(b->epairs, "group");
797   */
798 }
799 /*
800 void Group_AddToWorld(brush_t *b)
801 {
802   if (!g_qeglobals.m_bBrushPrimitMode)
803   {
804     return;
805   }
806   GtkCTreeNode *parent = gtk_ctree_node_nth (GTK_CTREE (g_pGroupDlg->m_pTree), 0);
807   Group_AddToItem(b, parent);
808 }
809 */
810 void Group_AddToProperGroup(brush_t *b)
811 {
812   /*
813   if (!g_qeglobals.m_bBrushPrimitMode)
814   {
815     return;
816   }
817
818   // NOTE: we do a local copy of the "group" key because it gets erased by Group_RemoveBrush
819   const char *pGroup = Brush_GetKeyValue(b, "group");
820   // remove the entry in the tree if there's one
821   if (b->itemOwner)
822   {
823     gtk_ctree_remove_node (GTK_CTREE (g_pGroupDlg->m_pTree), b->itemOwner);
824     b->itemOwner = NULL;
825   }
826
827   if (*pGroup != 0)
828   {
829     // find the item
830     group_t *g = Group_ForName(pGroup);
831     if (g)
832       Group_AddToItem(b, g->itemOwner);
833 #ifdef _DEBUG
834     else
835       Sys_Printf("WARNING: unexpected Group_ForName not found in Group_AddToProperGroup\n");
836 #endif
837   }
838   else
839   {
840     Group_AddToWorld(b);
841   }
842   */
843 }
844 /*
845 void Group_AddToSelected(brush_t *b)
846 {
847   if (!g_qeglobals.m_bBrushPrimitMode)
848   {
849     return;
850   }
851   GtkCTreeNode *item;
852   item = gtk_ctree_node_nth (GTK_CTREE (g_pGroupDlg->m_pTree), GTK_CLIST (g_pGroupDlg->m_pTree)->focus_row);
853   if (item == NULL)
854   {
855     item = gtk_ctree_node_nth (GTK_CTREE (g_pGroupDlg->m_pTree), 0);
856   }
857   Group_AddToItem(b, item);
858 }
859 */
860 /*
861 void Group_Save(FILE *f)
862 {
863   group_t *g = g_pGroups;
864   while (g)
865   {
866     fprintf(f,"{\n\"classname\" \"group_info\"\n\"group\" \"%s\"\n}\n", ValueForKey( g->epairs, "group" ));
867     g = g->next;
868   }
869 }
870 */
871
872 void Group_Init()
873 {
874   if (!g_qeglobals.m_bBrushPrimitMode)
875   {
876     return;
877   }
878   // start by cleaning everything
879   // clean the groups
880   //++timo FIXME: we leak, delete the groups on the way (I don't have time to do it now)
881 #ifdef _DEBUG
882   Sys_Printf("TODO: fix leak in Group_Init\n");
883 #endif
884   group_t *g = g_pGroups;
885   while (g)
886   {
887     epair_t *ep,*enext;
888     for (ep = g->epairs ; ep ; ep=enext )
889     {
890       enext = ep->next;
891       free (ep->key);
892       free (ep->value);
893       free (ep);
894     }
895     g = g->next;
896   }
897   /*
898   GtkCTreeNode *world;
899   char *text = "World";
900   g_pGroups = NULL;
901   gtk_clist_clear (GTK_CLIST (g_wndGroup.m_pTree));
902   world = gtk_ctree_insert_node (GTK_CTREE (g_wndGroup.m_pTree), NULL, NULL, &text, 0,
903                                  tree_pixmaps[IMG_GROUP], tree_masks[IMG_GROUP], tree_pixmaps[IMG_GROUP],
904                                  tree_masks[IMG_GROUP], FALSE, TRUE);
905   */
906   // walk through all the brushes, remove the itemOwner key and add them back where they belong
907   brush_t *b;
908   for (b = active_brushes.next; b != &active_brushes; b = b->next)
909   {
910     b->itemOwner = NULL;
911     Group_AddToProperGroup(b);
912   }
913   for (b = selected_brushes.next ; b != &selected_brushes ; b = b->next)
914   {
915     b->itemOwner = NULL;
916     Group_AddToProperGroup(b);
917   }
918 }
919 /*
920 // scan through world_entity for groups in this map?
921 // we use GROUPNAME "QER_group_%i" to look for existing groups and their naming
922 //++timo FIXME: is this actually needed for anything?
923 void Group_GetListFromWorld(GSList **pArray)
924 {
925   if (!g_qeglobals.m_bBrushPrimitMode)
926   {
927     return;
928   }
929
930   if (world_entity == NULL)
931   {
932     return;
933   }
934
935   char cBuff[1024];
936   for (int i =0; i < MAX_GROUPS; i++)
937   {
938     sprintf(cBuff, GROUPNAME, i);
939     char *pGroup = ValueForKey(world_entity, cBuff);
940     if (pGroup && strlen(pGroup) > 0)
941     {
942       *pArray = g_slist_append (*pArray, g_strdup (pGroup));
943     }
944     else
945     {
946       break;
947     }
948   }
949 }
950
951 void Group_RemoveListFromWorld()
952 {
953   if (!g_qeglobals.m_bBrushPrimitMode)
954   {
955     return;
956   }
957   GSList* array = NULL;
958   Group_GetListFromWorld(&array);
959
960   while (array)
961   {
962     DeleteKey(world_entity, (char*)array->data);
963     g_free (array->data);
964     array = g_slist_remove (array, array->data);
965   }
966 }
967
968 int CountChar(const char *p, char c)
969 {
970   int nCount = 0;
971   int nLen = strlen(p)-1;
972   while (nLen-- >= 0)
973   {
974     if (p[nLen] == c)
975     {
976       nCount++;
977     }
978   }
979   return nCount;
980 }
981 */
982 // =============================================================================
983 // callbacks
984
985 static void eclasslist_selection_changed(GtkTreeSelection* selection, gpointer data)
986 {
987   GtkTreeModel* model;
988   GtkTreeIter selected;
989   // no world entity, we are not ready yet
990   if( !world_entity ) {
991     return;
992   }
993   if(gtk_tree_selection_get_selected(selection, &model, &selected))
994   {
995     eclass_t* eclass;
996     gtk_tree_model_get(model, &selected, 1, &eclass, -1);
997     if(eclass != NULL)
998     {
999       GtkTreePath* path = gtk_tree_model_get_path(model, &selected);
1000       UpdateSel(gtk_tree_path_get_indices(path)[0], eclass);
1001       gtk_tree_path_free(path);
1002     }
1003   }
1004 }
1005
1006 static gint eclasslist_button_press (GtkWidget *widget, GdkEventButton *event, gpointer data)
1007 {
1008   if (event->type == GDK_2BUTTON_PRESS)
1009   {
1010     CreateEntity ();
1011     return TRUE;
1012   }
1013   return FALSE;
1014 }
1015
1016 static gint eclasslist_keypress (GtkWidget* widget, GdkEventKey* event, gpointer data)
1017 {
1018   unsigned int code = gdk_keyval_to_upper (event->keyval);
1019
1020   if (event->keyval == GDK_Return)
1021   {
1022     CreateEntity ();
1023     return TRUE;
1024   }
1025
1026   // select the entity that starts with the key pressed
1027   if (code <= 'Z' && code >= 'A')
1028   {
1029     GtkTreeView* view = GTK_TREE_VIEW(EntWidgets[EntList]);
1030     GtkTreeModel* model;
1031     GtkTreeIter iter;
1032     if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(view), &model, &iter) == FALSE
1033       || gtk_tree_model_iter_next(model, &iter) == FALSE)
1034     {
1035       gtk_tree_model_get_iter_first(model, &iter);
1036     }
1037
1038     for(unsigned int count = gtk_tree_model_iter_n_children(model, NULL); count > 0; --count)
1039     {
1040       char* text;
1041       gtk_tree_model_get(model, &iter, 0, &text, -1);
1042
1043       if (toupper (text[0]) == (int)code)
1044       {
1045         GtkTreePath* path = gtk_tree_model_get_path(model, &iter);
1046         gtk_tree_selection_select_path(gtk_tree_view_get_selection(view), path);
1047         gtk_tree_view_scroll_to_cell(view, path, NULL, FALSE, 0, 0);
1048         gtk_tree_path_free(path);
1049         count = 1;
1050       }
1051
1052       g_free(text);
1053
1054       if(gtk_tree_model_iter_next(model, &iter) == FALSE)
1055         gtk_tree_model_get_iter_first(model, &iter);
1056     }
1057
1058     return TRUE;
1059   }
1060   return FALSE;
1061 }
1062
1063
1064 static void proplist_selection_changed(GtkTreeSelection* selection, gpointer data)
1065 {
1066   // find out what type of entity we are trying to create
1067   GtkTreeModel* model;
1068   GtkTreeIter iter;
1069   if(gtk_tree_selection_get_selected(selection, &model, &iter) == FALSE)
1070   {
1071     return;
1072   }
1073
1074   char* key;
1075   char* val;
1076   gtk_tree_model_get(model, &iter, 0, &key, 1, &val, -1);
1077
1078   gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntKeyField]), key);
1079   gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntValueField]), val);
1080
1081   g_free(key);
1082   g_free(val);
1083 }
1084
1085 static void entity_check (GtkWidget *widget, gpointer data)
1086 {
1087   if( !disable_spawn_get )
1088     GetSpawnFlags();
1089 }
1090
1091 static void entitylist_angle (GtkWidget *widget, gpointer data)
1092 {
1093   SetKeyValue (edit_entity, "angle", (char*)data);
1094   SetKeyValuePairs ();
1095 }
1096
1097 static gint entityentry_keypress (GtkWidget* widget, GdkEventKey* event, gpointer data)
1098 {
1099   if (event->keyval == GDK_Tab)
1100   {
1101     if (widget == EntWidgets[EntKeyField])
1102     {
1103       //gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntValueField]), "");
1104       gtk_window_set_focus (GTK_WINDOW (g_pGroupDlg->m_pWidget), EntWidgets[EntValueField]);
1105     }
1106     else
1107       gtk_window_set_focus (GTK_WINDOW (g_pGroupDlg->m_pWidget), EntWidgets[EntKeyField]);
1108
1109     return TRUE;
1110   }
1111   else if (event->keyval == GDK_Return)
1112   {
1113     if (widget == EntWidgets[EntKeyField])
1114     {
1115       gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntValueField]), "");
1116       gtk_window_set_focus (GTK_WINDOW (g_pGroupDlg->m_pWidget), EntWidgets[EntValueField]);
1117     }
1118     else
1119     {
1120       AddProp ();
1121     }
1122     return TRUE;
1123   }
1124
1125   return FALSE;
1126 }
1127 /*
1128 // add a new group, put all selected brushes into the group
1129 static void groupdlg_add (GtkWidget *widget, gpointer data)
1130 {
1131   char* name = DoNameDlg ("New Group");
1132
1133   if (name != NULL)
1134   {
1135     // create a new group node
1136     GtkCTreeNode *item;
1137     item = gtk_ctree_insert_node (GTK_CTREE (g_wndGroup.m_pTree), g_pGroupDlg->m_hWorld, NULL, &name, 0,
1138                                   tree_pixmaps[IMG_GROUP], tree_masks[IMG_GROUP],
1139                                   tree_pixmaps[IMG_GROUP], tree_masks[IMG_GROUP], FALSE, TRUE);
1140
1141     // create a new group
1142     group_t *g = Group_Alloc (name);
1143     g->itemOwner = item;
1144     g->next = g_pGroups;
1145     g_pGroups = g;
1146
1147     // now add the selected brushes
1148     // NOTE: it would be much faster to give the group_t for adding
1149     // but Select_AddToGroup is the standard way for all other cases
1150     Select_AddToGroup (name);
1151     g_free (name);
1152   }
1153 }
1154 */
1155 static void switch_page (GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, gpointer data)
1156 {
1157   char *text;
1158   gtk_label_get(GTK_LABEL(gtk_notebook_get_tab_label(notebook, gtk_notebook_get_nth_page(notebook, page_num))), &text);
1159   gtk_window_set_title (GTK_WINDOW (data), text);
1160
1161   gpointer item = g_object_get_data (G_OBJECT (g_pParentWnd->m_pWidget), "menu_misc_selectentitycolor");
1162
1163   if (g_pParentWnd->FloatingGroupDialog())
1164   {
1165     switch (page_num)
1166     {
1167     case 0: inspector_mode = W_ENTITY; break;
1168     case 1: inspector_mode = W_TEXTURE; break;
1169     case 2: inspector_mode = W_CONSOLE; break;
1170     default: inspector_mode = W_GROUP; break;
1171     }
1172   }
1173   else
1174   {
1175     if (page_num == 0)
1176       inspector_mode = W_ENTITY;
1177     else
1178       inspector_mode = W_GROUP;
1179   }
1180
1181   if (inspector_mode == W_ENTITY)
1182     gtk_widget_set_sensitive (GTK_WIDGET (item), TRUE);
1183   else
1184     gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
1185 }
1186
1187 // =============================================================================
1188 // GroupDlg class
1189
1190 // NOTE: when a key is hit with group window focused, we catch in this handler but it gets propagated to mainframe too
1191 //   therefore the message will be intercepted and used as a ID_SELECTION_DESELECT
1192 static gint OnDialogKey (GtkWidget* widget, GdkEventKey* event, gpointer data)
1193 {
1194 #ifdef DBG_PI
1195   Sys_Printf("OnDialogKey\n");
1196 #endif
1197   if ((event->keyval == GDK_Escape) && (g_pParentWnd->CurrentStyle() != MainFrame::eFloating))
1198   {
1199     // toggle off the group view (whatever part of it is currently displayed)
1200     // this used to be done with a g_pParentWnd->OnViewEntity(); but it had bad consequences
1201     // http://fenris.lokigames.com/show_bug.cgi?id=2773
1202     widget_delete_hide (g_qeglobals_gui.d_entity);
1203     return TRUE;
1204   }
1205   return FALSE;
1206 }
1207
1208 GroupDlg::GroupDlg ()
1209 {
1210   m_pWidget = NULL;
1211   m_hWorld = NULL;
1212 }
1213
1214 #ifdef _WIN32
1215 extern void PositionWindowOnPrimaryScreen(window_position_t& position);
1216 #endif
1217
1218 void GroupDlg::Create ()
1219 {
1220   if (m_pWidget != NULL)
1221     return;
1222
1223   GtkWidget* dlg = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1224
1225 #ifdef _WIN32
1226   if( g_PrefsDlg.m_bStartOnPrimMon ) {
1227     PositionWindowOnPrimaryScreen( g_PrefsDlg.mWindowInfo.posEntityWnd );
1228   }
1229 #endif
1230   load_window_pos (dlg, g_PrefsDlg.mWindowInfo.posEntityWnd);
1231
1232   gtk_window_set_title (GTK_WINDOW (dlg), "Entities");
1233   gtk_signal_connect (GTK_OBJECT (dlg), "delete_event", GTK_SIGNAL_FUNC (widget_delete_hide), NULL);
1234   // catch 'Esc'
1235   gtk_signal_connect (GTK_OBJECT (dlg), "key_press_event", GTK_SIGNAL_FUNC (OnDialogKey), NULL);
1236   gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (g_pParentWnd->m_pWidget));
1237   g_qeglobals_gui.d_entity = dlg;
1238
1239   {
1240     GtkWidget* notebook = gtk_notebook_new ();
1241     gtk_widget_show (notebook);
1242     gtk_container_add (GTK_CONTAINER (dlg), notebook);
1243     gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_BOTTOM);
1244     m_pNotebook = notebook;
1245
1246     {
1247       GtkWidget* vbox = gtk_vbox_new (FALSE, 2);
1248       gtk_widget_show (vbox);
1249       gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
1250
1251       {
1252         GtkWidget* label = gtk_label_new ("Entities");
1253         gtk_widget_show (label);
1254         gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, label);
1255       }
1256
1257       {
1258         GtkWidget* split1 = gtk_vpaned_new ();
1259         gtk_box_pack_start (GTK_BOX (vbox), split1, TRUE, TRUE, 0);
1260         gtk_widget_show (split1);
1261
1262         {
1263           GtkWidget* split2 = gtk_vpaned_new ();
1264           gtk_paned_add1 (GTK_PANED (split1), split2);
1265           gtk_widget_show (split2);
1266
1267           g_object_set_data (G_OBJECT (dlg), "split1", split1);
1268           g_object_set_data (G_OBJECT (dlg), "split2", split2);
1269
1270           {
1271             GtkWidget* vbox2 = gtk_vbox_new (FALSE, 2);
1272             gtk_widget_show (vbox2);
1273             gtk_paned_pack2 (GTK_PANED (split1), vbox2, FALSE, FALSE);
1274
1275             {
1276               GtkWidget* scr = gtk_scrolled_window_new (NULL, NULL);
1277               gtk_widget_show (scr);
1278               gtk_paned_add1 (GTK_PANED (split2), scr);
1279               gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scr), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
1280               gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scr), GTK_SHADOW_IN);
1281
1282               {
1283                 GtkListStore* store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1284
1285                 GtkWidget* view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1286                 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
1287                 g_signal_connect(G_OBJECT(view), "button_press_event", G_CALLBACK(eclasslist_button_press), NULL);
1288                 g_signal_connect(G_OBJECT(view), "key_press_event", G_CALLBACK(eclasslist_keypress), this);
1289
1290                 {
1291                   GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
1292                   GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("Key", renderer, "text", 0, NULL);
1293                   gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
1294                 }
1295
1296                 {
1297                   GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
1298                   g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(eclasslist_selection_changed), dlg);
1299                 }
1300
1301                 gtk_widget_show(view);
1302
1303                 gtk_container_add(GTK_CONTAINER (scr), view);
1304
1305                 g_object_unref(G_OBJECT(store));
1306                 EntWidgets[EntList] = view;
1307                 g_entlist_store = store;
1308               }
1309             }
1310
1311             {
1312               GtkWidget* scr = gtk_scrolled_window_new (NULL, NULL);
1313               gtk_widget_show (scr);
1314               gtk_paned_add2 (GTK_PANED (split2), scr);
1315               gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scr), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
1316               gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scr), GTK_SHADOW_IN);
1317
1318               {
1319                 GtkWidget* text = gtk_text_view_new();
1320                 gtk_widget_set_size_request(text, 0, -1); // allow shrinking
1321                 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
1322                 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
1323                 gtk_widget_show (text);
1324                 gtk_container_add (GTK_CONTAINER (scr), text);
1325                 EntWidgets[EntComment] = text;
1326               }
1327             }
1328
1329             {
1330               // Spawnflags (4 colums wide max, or window gets too wide.)
1331               GtkWidget* LayoutTable = gtk_table_new (4, 4, FALSE);
1332               gtk_box_pack_start (GTK_BOX (vbox2), LayoutTable, FALSE, TRUE, 0);
1333               gtk_widget_show(LayoutTable);
1334
1335               for (int i = 0; i < MAX_FLAGS; i++)
1336               {
1337                 GtkWidget* check = gtk_check_button_new_with_label ("");
1338                 gtk_widget_ref (check);
1339                 gtk_signal_connect (GTK_OBJECT (check), "toggled", GTK_SIGNAL_FUNC (entity_check), NULL);
1340                 EntWidgets[EntCheck1+i] = check;
1341               }
1342
1343                                 if (g_pGameDescription->quake2 || ( g_pGameDescription->mGameFile == "q2.game" ) || ( g_pGameDescription->mGameFile == "heretic2.game" )) {
1344                                         GtkWidget *check = gtk_check_button_new_with_label (_("!Easy"));
1345                                         gtk_widget_show (check);
1346                                         gtk_signal_connect (GTK_OBJECT (check), "toggled", GTK_SIGNAL_FUNC (entity_check), NULL);
1347 /*                                      gtk_table_attach (GTK_TABLE (table), check, 2, 3, 0, 1,
1348                                                                                 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1349                                                                                 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);*/
1350                                         EntWidgets[EntCheck17] = check;
1351
1352                                         check = gtk_check_button_new_with_label (_("!Medium"));
1353                                         gtk_widget_show (check);
1354                                         gtk_signal_connect (GTK_OBJECT (check), "toggled", GTK_SIGNAL_FUNC (entity_check), NULL);
1355 /*                                      gtk_table_attach (GTK_TABLE (table), check, 2, 3, 1, 2,
1356                                                                                 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1357                                                                                 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);*/
1358                                         EntWidgets[EntCheck18] = check;
1359
1360                                         check = gtk_check_button_new_with_label (_("!Hard"));
1361                                         gtk_widget_show (check);
1362                                         gtk_signal_connect (GTK_OBJECT (check), "toggled", GTK_SIGNAL_FUNC (entity_check), NULL);
1363 /*                                      gtk_table_attach (GTK_TABLE (table), check, 2, 3, 2, 3,
1364                                                                                 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1365                                                                                 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);*/
1366                                         EntWidgets[EntCheck19] = check;
1367
1368                                         check = gtk_check_button_new_with_label (_("!DeathMatch"));
1369                                         gtk_widget_show (check);
1370                                         gtk_signal_connect (GTK_OBJECT (check), "toggled", GTK_SIGNAL_FUNC (entity_check), NULL);
1371 /*                                      gtk_table_attach (GTK_TABLE (table), check, 2, 3, 3, 4,
1372                                                                                 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1373                                                                                 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);*/
1374                                         EntWidgets[EntCheck20] = check;
1375                                 }
1376             }
1377
1378             {
1379               GtkWidget* scr = gtk_scrolled_window_new (NULL, NULL);
1380               gtk_widget_show (scr);
1381               gtk_box_pack_start (GTK_BOX (vbox2), scr, TRUE, TRUE, 0);
1382               gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scr), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1383               gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scr), GTK_SHADOW_IN);
1384
1385               {
1386                 GtkListStore* store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
1387
1388                 GtkWidget* view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1389                 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
1390
1391                 {
1392                   GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
1393                   GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("", renderer, "text", 0, NULL);
1394                   gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
1395                 }
1396
1397                 {
1398                   GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
1399                   GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("", renderer, "text", 1, NULL);
1400                   gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
1401                 }
1402
1403                 {
1404                   GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
1405                   g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(proplist_selection_changed), dlg);
1406                 }
1407
1408                 gtk_widget_show(view);
1409
1410                 gtk_container_add(GTK_CONTAINER (scr), view);
1411
1412                 g_object_unref(G_OBJECT(store));
1413
1414                 EntWidgets[EntProps] = view;
1415                 g_entprops_store = store;
1416               }
1417             }
1418           }
1419
1420           int x = g_PrefsDlg.mWindowInfo.nEntitySplit1;
1421           if (x != -1)
1422           {
1423             gtk_paned_set_position (GTK_PANED (split1), x);
1424
1425             while (gtk_events_pending ())  gtk_main_iteration ();
1426             x = g_PrefsDlg.mWindowInfo.nEntitySplit2;
1427
1428             if (x != -1)
1429               gtk_paned_set_position (GTK_PANED (split2), x);
1430           }
1431         }
1432       }
1433
1434       {
1435         GtkWidget* table = gtk_table_new (2, 2, FALSE);
1436         gtk_widget_show (table);
1437         gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, TRUE, 0);
1438         gtk_table_set_row_spacings (GTK_TABLE (table), 3);
1439         gtk_table_set_col_spacings (GTK_TABLE (table), 5);
1440
1441         {
1442           GtkWidget* entry = gtk_entry_new ();
1443           gtk_widget_show (entry);
1444           gtk_table_attach (GTK_TABLE (table), entry, 1, 2, 0, 1,
1445                             (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1446                             (GtkAttachOptions) (0), 0, 0);
1447           gtk_widget_set_events (entry, GDK_KEY_PRESS_MASK);
1448           gtk_signal_connect (GTK_OBJECT (entry), "key_press_event",
1449                               GTK_SIGNAL_FUNC (entityentry_keypress), this);
1450           EntWidgets[EntKeyField] = entry;
1451         }
1452
1453         {
1454           GtkWidget* entry = gtk_entry_new ();
1455           gtk_widget_show (entry);
1456           gtk_table_attach (GTK_TABLE (table), entry, 1, 2, 1, 2,
1457                             (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1458                             (GtkAttachOptions) (0), 0, 0);
1459           gtk_widget_set_events (entry, GDK_KEY_PRESS_MASK);
1460           gtk_signal_connect (GTK_OBJECT (entry), "key_press_event",
1461                               GTK_SIGNAL_FUNC (entityentry_keypress), this);
1462           EntWidgets[EntValueField] = entry;
1463         }
1464
1465         {
1466           GtkWidget* label = gtk_label_new (_("Value"));
1467           gtk_widget_show (label);
1468           gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
1469                             (GtkAttachOptions) (GTK_FILL),
1470                             (GtkAttachOptions) (0), 0, 0);
1471           gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
1472         }
1473
1474         {
1475           GtkWidget* label = gtk_label_new (_("Key"));
1476           gtk_widget_show (label);
1477           gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
1478                             (GtkAttachOptions) (GTK_FILL),
1479                             (GtkAttachOptions) (0), 0, 0);
1480           gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
1481         }
1482       }
1483
1484       {
1485         GtkWidget* hbox = gtk_hbox_new (FALSE, 5);
1486         gtk_widget_show (hbox);
1487         gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
1488
1489         {
1490           GtkWidget* table = gtk_table_new (3, 3, TRUE);
1491           gtk_widget_show (table);
1492           gtk_box_pack_start (GTK_BOX (hbox), table, FALSE, TRUE, 0);
1493
1494           {
1495             GtkWidget* button = gtk_button_new_with_label (_("360"));
1496             gtk_widget_show (button);
1497             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"360");
1498             gtk_table_attach (GTK_TABLE (table), button, 2, 3, 1, 2,
1499                               (GtkAttachOptions) (GTK_FILL),
1500                               (GtkAttachOptions) (GTK_FILL), 0, 0);
1501           }
1502
1503           {
1504             GtkWidget* button = gtk_button_new_with_label (_("45"));
1505             gtk_widget_show (button);
1506             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"45");
1507             gtk_table_attach (GTK_TABLE (table), button, 2, 3, 0, 1,
1508                               (GtkAttachOptions) (GTK_FILL),
1509                               (GtkAttachOptions) (GTK_FILL), 0, 0);
1510           }
1511
1512           {
1513             GtkWidget* button = gtk_button_new_with_label (_("90"));
1514             gtk_widget_show (button);
1515             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"90");
1516             gtk_table_attach (GTK_TABLE (table), button, 1, 2, 0, 1,
1517                               (GtkAttachOptions) (GTK_FILL),
1518                               (GtkAttachOptions) (GTK_FILL), 0, 0);
1519           }
1520
1521
1522           {
1523             GtkWidget* button = gtk_button_new_with_label (_("135"));
1524             gtk_widget_show (button);
1525             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"135");
1526             gtk_table_attach (GTK_TABLE (table), button, 0, 1, 0, 1,
1527                               (GtkAttachOptions) (GTK_FILL),
1528                               (GtkAttachOptions) (GTK_FILL), 0, 0);
1529           }
1530
1531           {
1532             GtkWidget* button = gtk_button_new_with_label (_("180"));
1533             gtk_widget_show (button);
1534             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"180");
1535             gtk_table_attach (GTK_TABLE (table), button, 0, 1, 1, 2,
1536                               (GtkAttachOptions) (GTK_FILL),
1537                               (GtkAttachOptions) (GTK_FILL), 0, 0);
1538           }
1539
1540           {
1541             GtkWidget* button = gtk_button_new_with_label (_("225"));
1542             gtk_widget_show (button);
1543             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"225");
1544             gtk_table_attach (GTK_TABLE (table), button, 0, 1, 2, 3,
1545                               (GtkAttachOptions) (GTK_FILL),
1546                               (GtkAttachOptions) (GTK_FILL), 0, 0);
1547           }
1548
1549           {
1550             GtkWidget* button = gtk_button_new_with_label (_("270"));
1551             gtk_widget_show (button);
1552             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"270");
1553             gtk_table_attach (GTK_TABLE (table), button, 1, 2, 2, 3,
1554                               (GtkAttachOptions) (GTK_FILL),
1555                               (GtkAttachOptions) (GTK_FILL), 0, 0);
1556           }
1557
1558           {
1559             GtkWidget* button = gtk_button_new_with_label (_("315"));
1560             gtk_widget_show (button);
1561             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"315");
1562             gtk_table_attach (GTK_TABLE (table), button, 2, 3, 2, 3,
1563                               (GtkAttachOptions) (GTK_FILL),
1564                               (GtkAttachOptions) (GTK_FILL), 0, 0);
1565           }
1566         }
1567
1568         {
1569           GtkWidget* vbox2 = gtk_vbox_new (FALSE, 0);
1570           gtk_widget_show (vbox2);
1571           gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 0);
1572
1573           {
1574             GtkWidget* button = gtk_button_new_with_label (_("Reset"));
1575             gtk_widget_show (button);
1576             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (ResetEntity), NULL);
1577             gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
1578           }
1579
1580           {
1581             GtkWidget* button = gtk_button_new_with_label (_("Up"));
1582             gtk_widget_show (button);
1583             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"-1");
1584             gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
1585           }
1586
1587           {
1588             GtkWidget* button = gtk_button_new_with_label (_("Dn"));
1589             gtk_widget_show (button);
1590             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"-2");
1591             gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
1592           }
1593         }
1594
1595         {
1596           GtkWidget* vbox2 = gtk_vbox_new (FALSE, 0);
1597           gtk_widget_show (vbox2);
1598           gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 0);
1599
1600           {
1601             GtkWidget* button = gtk_button_new_with_label (_("Del Key/Pair"));
1602             gtk_widget_show (button);
1603             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (DelProp), NULL);
1604             gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
1605           }
1606
1607           {
1608             GtkWidget* button = gtk_button_new_with_label (_("Sound..."));
1609             gtk_widget_show (button);
1610             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (AssignSound), NULL);
1611             gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
1612           }
1613
1614           {
1615             GtkWidget* button = gtk_button_new_with_label (_("Model..."));
1616             gtk_widget_show (button);
1617             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (AssignModel), NULL);
1618             gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
1619           }
1620         }
1621       }
1622     }
1623
1624     if (g_pParentWnd->FloatingGroupDialog())
1625     {
1626       {
1627         GtkWidget* scr = gtk_scrolled_window_new (NULL, NULL);
1628         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scr), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1629         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scr), GTK_SHADOW_IN);
1630         gtk_widget_show (scr);
1631         gtk_container_set_border_width (GTK_CONTAINER (scr), 3);
1632
1633         {
1634           GtkWidget* text = gtk_text_view_new ();
1635           gtk_widget_set_size_request(text, 0, -1); // allow shrinking
1636           gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
1637           gtk_text_view_set_editable (GTK_TEXT_VIEW(text), FALSE);
1638           gtk_container_add (GTK_CONTAINER (scr), text);
1639           gtk_widget_show (text);
1640           g_qeglobals_gui.d_edit = text;
1641         }
1642
1643         {
1644           GtkWidget* label = gtk_label_new (_("Console"));
1645           gtk_widget_show (label);
1646           gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scr, label);
1647         }
1648       }
1649     }
1650
1651
1652   //++timo NOTE: this part for grouping code, don't remove! (we'll put it back in sometime soon)
1653
1654   /*
1655   vbox = gtk_vbox_new (FALSE, 5);
1656   gtk_widget_show (vbox);
1657   gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
1658
1659   scr = gtk_scrolled_window_new (NULL, NULL);
1660   gtk_widget_show (scr);
1661   gtk_box_pack_start (GTK_BOX (vbox), scr, TRUE, TRUE, 0);
1662   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scr), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1663
1664   ctree = gtk_ctree_new (1, 0);
1665   gtk_widget_show (ctree);
1666   gtk_container_add (GTK_CONTAINER (scr), ctree);
1667   gtk_clist_column_titles_hide (GTK_CLIST (ctree));
1668   m_pTree = ctree;
1669
1670   hbox = gtk_hbox_new (FALSE, 5);
1671   gtk_widget_show (hbox);
1672   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
1673
1674   button = gtk_button_new_with_label (_("Add..."));
1675   gtk_widget_show (button);
1676   gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (groupdlg_add), NULL);
1677   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
1678   gtk_widget_set_usize (button, 60, -2);
1679
1680   button = gtk_button_new_with_label (_("Edit..."));
1681   gtk_widget_show (button);
1682   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
1683   gtk_widget_set_usize (button, 60, -2);
1684
1685   button = gtk_button_new_with_label (_("Delete"));
1686   gtk_widget_show (button);
1687   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
1688   gtk_widget_set_usize (button, 60, -2);
1689
1690   label = gtk_label_new (_("Groups"));
1691   gtk_widget_show (label);
1692   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, label);
1693   */
1694   inspector_mode = W_ENTITY;
1695   //  gtk_window_set_title (GTK_WINDOW (dlg), _("Entities"));
1696   m_pWidget = dlg;
1697   /*
1698   load_pixmap ("grouptree1.bmp", g_pParentWnd->m_pWidget, &tree_pixmaps[0], &tree_masks[0]);
1699   load_pixmap ("grouptree2.bmp", g_pParentWnd->m_pWidget, &tree_pixmaps[1], &tree_masks[1]);
1700   load_pixmap ("grouptree3.bmp", g_pParentWnd->m_pWidget, &tree_pixmaps[2], &tree_masks[2]);
1701   load_pixmap ("grouptree4.bmp", g_pParentWnd->m_pWidget, &tree_pixmaps[3], &tree_masks[3]);
1702   load_pixmap ("grouptree5.bmp", g_pParentWnd->m_pWidget, &tree_pixmaps[4], &tree_masks[4]);
1703   load_pixmap ("grouptree6.bmp", g_pParentWnd->m_pWidget, &tree_pixmaps[5], &tree_masks[5]);
1704   load_pixmap ("grouptree7.bmp", g_pParentWnd->m_pWidget, &tree_pixmaps[6], &tree_masks[6]);
1705
1706   Group_Init();
1707 */
1708     g_signal_connect (G_OBJECT (notebook), "switch_page", G_CALLBACK (switch_page), dlg);
1709   }
1710 }
1711