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