]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/groupdialog.cpp
* improved quake2 support by added a quake2 boolean to the CGameDescription class...
[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 "stdafx.h"
34 #include "groupdialog.h"
35
36 GtkWidget*      EntWidgets[EntLast];
37 GtkListStore* g_entlist_store;
38 GtkListStore* g_entprops_store;
39 int                                     inspector_mode;         // W_TEXTURE, W_ENTITY, or W_CONSOLE
40 qboolean                multiple_entities;
41 // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=17
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   // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=917
991   if( !world_entity ) {
992     return;  
993   }
994   if(gtk_tree_selection_get_selected(selection, &model, &selected))
995   {
996     eclass_t* eclass;
997     gtk_tree_model_get(model, &selected, 1, &eclass, -1);
998     if(eclass != NULL)
999     {
1000       GtkTreePath* path = gtk_tree_model_get_path(model, &selected);
1001       UpdateSel(gtk_tree_path_get_indices(path)[0], eclass);
1002       gtk_tree_path_free(path);
1003     }
1004   }
1005 }
1006
1007 static gint eclasslist_button_press (GtkWidget *widget, GdkEventButton *event, gpointer data)
1008 {
1009   if (event->type == GDK_2BUTTON_PRESS)
1010   {
1011     CreateEntity ();
1012     return TRUE;
1013   }
1014   return FALSE;
1015 }
1016
1017 static gint eclasslist_keypress (GtkWidget* widget, GdkEventKey* event, gpointer data)
1018 {
1019   unsigned int code = gdk_keyval_to_upper (event->keyval);
1020
1021   if (event->keyval == GDK_Return)
1022   {
1023     CreateEntity ();
1024     return TRUE;
1025   }
1026
1027   // select the entity that starts with the key pressed
1028   if (code <= 'Z' && code >= 'A')
1029   {
1030     GtkTreeView* view = GTK_TREE_VIEW(EntWidgets[EntList]);
1031     GtkTreeModel* model;
1032     GtkTreeIter iter;
1033     if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(view), &model, &iter) == FALSE
1034       || gtk_tree_model_iter_next(model, &iter) == FALSE)
1035     {
1036       gtk_tree_model_get_iter_first(model, &iter);
1037     }
1038
1039     for(unsigned int count = gtk_tree_model_iter_n_children(model, NULL); count > 0; --count)
1040     {
1041       char* text;
1042       gtk_tree_model_get(model, &iter, 0, &text, -1);
1043
1044       if (toupper (text[0]) == (int)code)
1045       {
1046         GtkTreePath* path = gtk_tree_model_get_path(model, &iter);
1047         gtk_tree_selection_select_path(gtk_tree_view_get_selection(view), path);
1048         gtk_tree_view_scroll_to_cell(view, path, NULL, FALSE, 0, 0);
1049         gtk_tree_path_free(path);
1050         count = 1;
1051       }
1052
1053       g_free(text);
1054
1055       if(gtk_tree_model_iter_next(model, &iter) == FALSE)
1056         gtk_tree_model_get_iter_first(model, &iter);
1057     }
1058
1059     return TRUE;
1060   }
1061   return FALSE;
1062 }
1063
1064
1065 static void proplist_selection_changed(GtkTreeSelection* selection, gpointer data)
1066 {
1067   // find out what type of entity we are trying to create
1068   GtkTreeModel* model;
1069   GtkTreeIter iter;
1070   if(gtk_tree_selection_get_selected(selection, &model, &iter) == FALSE)
1071   {
1072     return;
1073   }
1074
1075   char* key;
1076   char* val;
1077   gtk_tree_model_get(model, &iter, 0, &key, 1, &val, -1);
1078
1079   gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntKeyField]), key);
1080   gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntValueField]), val);
1081
1082   g_free(key);
1083   g_free(val);
1084 }
1085
1086 static void entity_check (GtkWidget *widget, gpointer data)
1087 {
1088   if( !disable_spawn_get )
1089     GetSpawnFlags();
1090 }
1091
1092 static void entitylist_angle (GtkWidget *widget, gpointer data)
1093 {
1094   SetKeyValue (edit_entity, "angle", (char*)data);
1095   SetKeyValuePairs ();
1096 }
1097
1098 static gint entityentry_keypress (GtkWidget* widget, GdkEventKey* event, gpointer data)
1099 {
1100   if (event->keyval == GDK_Tab)
1101   {
1102     if (widget == EntWidgets[EntKeyField])
1103     {
1104       //gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntValueField]), "");
1105       gtk_window_set_focus (GTK_WINDOW (g_pGroupDlg->m_pWidget), EntWidgets[EntValueField]);
1106     }
1107     else
1108       gtk_window_set_focus (GTK_WINDOW (g_pGroupDlg->m_pWidget), EntWidgets[EntKeyField]);
1109
1110     return TRUE;
1111   }
1112   else if (event->keyval == GDK_Return)
1113   {
1114     if (widget == EntWidgets[EntKeyField])
1115     {
1116       gtk_entry_set_text (GTK_ENTRY (EntWidgets[EntValueField]), "");
1117       gtk_window_set_focus (GTK_WINDOW (g_pGroupDlg->m_pWidget), EntWidgets[EntValueField]);
1118     }
1119     else
1120     {
1121       AddProp ();
1122     }
1123     return TRUE;
1124   }
1125
1126   return FALSE;
1127 }
1128 /*
1129 // add a new group, put all selected brushes into the group
1130 static void groupdlg_add (GtkWidget *widget, gpointer data)
1131 {
1132   char* name = DoNameDlg ("New Group");
1133
1134   if (name != NULL)
1135   {
1136     // create a new group node  
1137     GtkCTreeNode *item;
1138     item = gtk_ctree_insert_node (GTK_CTREE (g_wndGroup.m_pTree), g_pGroupDlg->m_hWorld, NULL, &name, 0,
1139                                   tree_pixmaps[IMG_GROUP], tree_masks[IMG_GROUP],
1140                                   tree_pixmaps[IMG_GROUP], tree_masks[IMG_GROUP], FALSE, TRUE);
1141
1142     // create a new group
1143     group_t *g = Group_Alloc (name);
1144     g->itemOwner = item;
1145     g->next = g_pGroups;
1146     g_pGroups = g;
1147
1148     // now add the selected brushes
1149     // NOTE: it would be much faster to give the group_t for adding
1150     // but Select_AddToGroup is the standard way for all other cases
1151     Select_AddToGroup (name);
1152     g_free (name);
1153   }
1154 }
1155 */
1156 static void switch_page (GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, gpointer data)
1157 {
1158   char *text;
1159   gtk_label_get(GTK_LABEL(gtk_notebook_get_tab_label(notebook, gtk_notebook_get_nth_page(notebook, page_num))), &text);
1160   gtk_window_set_title (GTK_WINDOW (data), text);
1161
1162   gpointer item = g_object_get_data (G_OBJECT (g_pParentWnd->m_pWidget), "menu_misc_selectentitycolor");
1163
1164   if (g_pParentWnd->FloatingGroupDialog())
1165   {
1166     switch (page_num)
1167     {
1168     case 0: inspector_mode = W_ENTITY; break;
1169     case 1: inspector_mode = W_TEXTURE; break;
1170     case 2: inspector_mode = W_CONSOLE; break;
1171     default: inspector_mode = W_GROUP; break;
1172     }
1173   }
1174   else
1175   {
1176     if (page_num == 0)
1177       inspector_mode = W_ENTITY;
1178     else
1179       inspector_mode = W_GROUP;
1180   }
1181   
1182   if (inspector_mode == W_ENTITY)
1183     gtk_widget_set_sensitive (GTK_WIDGET (item), TRUE);
1184   else
1185     gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
1186 }
1187
1188 // =============================================================================
1189 // GroupDlg class
1190
1191 // NOTE: when a key is hit with group window focused, we catch in this handler but it gets propagated to mainframe too
1192 //   therefore the message will be intercepted and used as a ID_SELECTION_DESELECT
1193 static gint OnDialogKey (GtkWidget* widget, GdkEventKey* event, gpointer data)
1194 {
1195 #ifdef DBG_PI
1196   Sys_Printf("OnDialogKey\n");
1197 #endif
1198   if ((event->keyval == GDK_Escape) && (g_pParentWnd->CurrentStyle() != MainFrame::eFloating))
1199   {
1200     // toggle off the group view (whatever part of it is currently displayed)
1201     // this used to be done with a g_pParentWnd->OnViewEntity(); but it had bad consequences
1202     // http://fenris.lokigames.com/show_bug.cgi?id=2773
1203     widget_delete_hide (g_qeglobals_gui.d_entity);
1204     return TRUE;
1205   }
1206   return FALSE;
1207 }
1208
1209 GroupDlg::GroupDlg ()
1210 {
1211   m_pWidget = NULL;
1212   m_hWorld = NULL;
1213 }
1214
1215 #ifdef _WIN32
1216 extern void PositionWindowOnPrimaryScreen(window_position_t& position);
1217 #endif
1218
1219 void GroupDlg::Create ()
1220 {
1221   if (m_pWidget != NULL)
1222     return;
1223
1224   GtkWidget* dlg = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1225
1226 #ifdef _WIN32
1227   if( g_PrefsDlg.m_bStartOnPrimMon ) {
1228     PositionWindowOnPrimaryScreen( g_PrefsDlg.mWindowInfo.posEntityWnd );
1229   }
1230 #endif
1231   load_window_pos (dlg, g_PrefsDlg.mWindowInfo.posEntityWnd);
1232
1233   gtk_window_set_title (GTK_WINDOW (dlg), "Entities");
1234   gtk_signal_connect (GTK_OBJECT (dlg), "delete_event", GTK_SIGNAL_FUNC (widget_delete_hide), NULL);
1235   // catch 'Esc'
1236   gtk_signal_connect (GTK_OBJECT (dlg), "key_press_event", GTK_SIGNAL_FUNC (OnDialogKey), NULL);
1237   gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (g_pParentWnd->m_pWidget));
1238   g_qeglobals_gui.d_entity = dlg;
1239
1240   {
1241     GtkWidget* notebook = gtk_notebook_new ();
1242     gtk_widget_show (notebook);
1243     gtk_container_add (GTK_CONTAINER (dlg), notebook);
1244     gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_BOTTOM);
1245     m_pNotebook = notebook;
1246
1247     {
1248       GtkWidget* vbox = gtk_vbox_new (FALSE, 2);
1249       gtk_widget_show (vbox);
1250       gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
1251
1252       {
1253         GtkWidget* label = gtk_label_new ("Entities");
1254         gtk_widget_show (label);
1255         gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, label);
1256       }
1257
1258       {
1259         GtkWidget* split1 = gtk_vpaned_new ();
1260         gtk_box_pack_start (GTK_BOX (vbox), split1, TRUE, TRUE, 0);
1261         gtk_widget_show (split1);
1262   
1263         {
1264           GtkWidget* split2 = gtk_vpaned_new ();
1265           gtk_paned_add1 (GTK_PANED (split1), split2);
1266           gtk_widget_show (split2);
1267
1268           g_object_set_data (G_OBJECT (dlg), "split1", split1);
1269           g_object_set_data (G_OBJECT (dlg), "split2", split2);
1270
1271           {
1272             GtkWidget* vbox2 = gtk_vbox_new (FALSE, 2);
1273             gtk_widget_show (vbox2);
1274             gtk_paned_pack2 (GTK_PANED (split1), vbox2, FALSE, FALSE);
1275
1276             {
1277               GtkWidget* scr = gtk_scrolled_window_new (NULL, NULL);
1278               gtk_widget_show (scr);
1279               gtk_paned_add1 (GTK_PANED (split2), scr);
1280               gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scr), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
1281               gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scr), GTK_SHADOW_IN);
1282
1283               {
1284                 GtkListStore* store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1285
1286                 GtkWidget* view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1287                 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
1288                 g_signal_connect(G_OBJECT(view), "button_press_event", G_CALLBACK(eclasslist_button_press), NULL);
1289                 g_signal_connect(G_OBJECT(view), "key_press_event", G_CALLBACK(eclasslist_keypress), this);
1290
1291                 {
1292                   GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
1293                   GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("Key", renderer, "text", 0, NULL);
1294                   gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
1295                 }
1296
1297                 {
1298                   GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
1299                   g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(eclasslist_selection_changed), dlg);
1300                 }
1301
1302                 gtk_widget_show(view);
1303
1304                 gtk_container_add(GTK_CONTAINER (scr), view);
1305     
1306                 g_object_unref(G_OBJECT(store));
1307                 EntWidgets[EntList] = view;
1308                 g_entlist_store = store;
1309               }
1310             }
1311
1312             {
1313               GtkWidget* scr = gtk_scrolled_window_new (NULL, NULL);
1314               gtk_widget_show (scr);
1315               gtk_paned_add2 (GTK_PANED (split2), scr);
1316               gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scr), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
1317               gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scr), GTK_SHADOW_IN);
1318
1319               {
1320                 GtkWidget* text = gtk_text_view_new();
1321                 gtk_widget_set_size_request(text, 0, -1); // allow shrinking
1322                 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
1323                 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
1324                 gtk_widget_show (text);
1325                 gtk_container_add (GTK_CONTAINER (scr), text);
1326                 EntWidgets[EntComment] = text;
1327               }
1328             }
1329
1330             {
1331               // Spawnflags (4 colums wide max, or window gets too wide.)
1332               GtkWidget* table = LayoutTable = gtk_table_new (4, 4, FALSE);
1333               gtk_box_pack_start (GTK_BOX (vbox2), LayoutTable, FALSE, TRUE, 0);
1334               gtk_widget_show(LayoutTable);
1335
1336               for (int i = 0; i < MAX_FLAGS; i++)
1337               {
1338                 GtkWidget* check = gtk_check_button_new_with_label ("");
1339                 gtk_widget_ref (check);
1340                 gtk_signal_connect (GTK_OBJECT (check), "toggled", GTK_SIGNAL_FUNC (entity_check), NULL);
1341                 EntWidgets[EntCheck1+i] = check;
1342               }
1343
1344               //++timo cleanme: these flags where Q2 stuff
1345             /*
1346               check = gtk_check_button_new_with_label ("!Easy");
1347               gtk_widget_show (check);
1348               gtk_signal_connect (GTK_OBJECT (check), "toggled", GTK_SIGNAL_FUNC (entity_check), NULL);
1349               gtk_table_attach (GTK_TABLE (table), check, 2, 3, 0, 1,
1350                                 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1351                                 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);
1352               EntWidgets[EntCheck17] = check;
1353
1354               check = gtk_check_button_new_with_label ("!Medium");
1355               gtk_widget_show (check);
1356               gtk_signal_connect (GTK_OBJECT (check), "toggled", GTK_SIGNAL_FUNC (entity_check), NULL);
1357               gtk_table_attach (GTK_TABLE (table), check, 2, 3, 1, 2,
1358                                 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1359                                 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);
1360               EntWidgets[EntCheck18] = check;
1361
1362               check = gtk_check_button_new_with_label ("!Hard");
1363               gtk_widget_show (check);
1364               gtk_signal_connect (GTK_OBJECT (check), "toggled", GTK_SIGNAL_FUNC (entity_check), NULL);
1365               gtk_table_attach (GTK_TABLE (table), check, 2, 3, 2, 3,
1366                                 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1367                                 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);
1368               EntWidgets[EntCheck19] = check;
1369
1370               check = gtk_check_button_new_with_label ("!DeathMatch");
1371               gtk_widget_show (check);
1372               gtk_signal_connect (GTK_OBJECT (check), "toggled", GTK_SIGNAL_FUNC (entity_check), NULL);
1373               gtk_table_attach (GTK_TABLE (table), check, 2, 3, 3, 4,
1374                                 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1375                                 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);
1376               EntWidgets[EntCheck20] = check;
1377             */
1378             }
1379
1380             {
1381               GtkWidget* scr = gtk_scrolled_window_new (NULL, NULL);
1382               gtk_widget_show (scr);
1383               gtk_box_pack_start (GTK_BOX (vbox2), scr, TRUE, TRUE, 0);
1384               gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scr), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1385               gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scr), GTK_SHADOW_IN);
1386
1387               {
1388                 GtkListStore* store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
1389
1390                 GtkWidget* view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1391                 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
1392
1393                 {
1394                   GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
1395                   GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("", renderer, "text", 0, NULL);
1396                   gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
1397                 }
1398
1399                 {
1400                   GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
1401                   GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("", renderer, "text", 1, NULL);
1402                   gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
1403                 }
1404
1405                 {
1406                   GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
1407                   g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(proplist_selection_changed), dlg);
1408                 }
1409
1410                 gtk_widget_show(view);
1411
1412                 gtk_container_add(GTK_CONTAINER (scr), view);
1413     
1414                 g_object_unref(G_OBJECT(store));
1415
1416                 EntWidgets[EntProps] = view;
1417                 g_entprops_store = store;
1418               }
1419             }
1420           }
1421
1422           int x = g_PrefsDlg.mWindowInfo.nEntitySplit1;
1423           if (x != -1)
1424           {
1425             gtk_paned_set_position (GTK_PANED (split1), x);
1426
1427             while (gtk_events_pending ())  gtk_main_iteration ();
1428             x = g_PrefsDlg.mWindowInfo.nEntitySplit2;
1429
1430             if (x != -1)
1431               gtk_paned_set_position (GTK_PANED (split2), x);
1432           }
1433         }
1434       }
1435
1436       {
1437         GtkWidget* table = gtk_table_new (2, 2, FALSE);
1438         gtk_widget_show (table);
1439         gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, TRUE, 0);
1440         gtk_table_set_row_spacings (GTK_TABLE (table), 3);
1441         gtk_table_set_col_spacings (GTK_TABLE (table), 5);
1442
1443         {
1444           GtkWidget* entry = gtk_entry_new ();
1445           gtk_widget_show (entry);
1446           gtk_table_attach (GTK_TABLE (table), entry, 1, 2, 0, 1,
1447                             (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1448                             (GtkAttachOptions) (0), 0, 0);
1449           gtk_widget_set_events (entry, GDK_KEY_PRESS_MASK);
1450           gtk_signal_connect (GTK_OBJECT (entry), "key_press_event",
1451                               GTK_SIGNAL_FUNC (entityentry_keypress), this);
1452           EntWidgets[EntKeyField] = entry;
1453         }
1454
1455         {
1456           GtkWidget* entry = gtk_entry_new ();
1457           gtk_widget_show (entry);
1458           gtk_table_attach (GTK_TABLE (table), entry, 1, 2, 1, 2,
1459                             (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1460                             (GtkAttachOptions) (0), 0, 0);
1461           gtk_widget_set_events (entry, GDK_KEY_PRESS_MASK);
1462           gtk_signal_connect (GTK_OBJECT (entry), "key_press_event",
1463                               GTK_SIGNAL_FUNC (entityentry_keypress), this);
1464           EntWidgets[EntValueField] = entry;
1465         }
1466
1467         {
1468           GtkWidget* label = gtk_label_new ("Value");
1469           gtk_widget_show (label);
1470           gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
1471                             (GtkAttachOptions) (GTK_FILL),
1472                             (GtkAttachOptions) (0), 0, 0);
1473           gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
1474         }
1475
1476         {
1477           GtkWidget* label = gtk_label_new ("Key");
1478           gtk_widget_show (label);
1479           gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
1480                             (GtkAttachOptions) (GTK_FILL),
1481                             (GtkAttachOptions) (0), 0, 0);
1482           gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
1483         }
1484       }
1485
1486       {
1487         GtkWidget* hbox = gtk_hbox_new (FALSE, 5);
1488         gtk_widget_show (hbox);
1489         gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
1490
1491         {
1492           GtkWidget* table = gtk_table_new (3, 3, TRUE);
1493           gtk_widget_show (table);
1494           gtk_box_pack_start (GTK_BOX (hbox), table, FALSE, TRUE, 0);
1495
1496           {
1497             GtkWidget* button = gtk_button_new_with_label ("360");
1498             gtk_widget_show (button);
1499             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"360");
1500             gtk_table_attach (GTK_TABLE (table), button, 2, 3, 1, 2,
1501                               (GtkAttachOptions) (GTK_FILL),
1502                               (GtkAttachOptions) (GTK_FILL), 0, 0);
1503           }
1504
1505           {
1506             GtkWidget* button = gtk_button_new_with_label ("45");
1507             gtk_widget_show (button);
1508             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"45");
1509             gtk_table_attach (GTK_TABLE (table), button, 2, 3, 0, 1,
1510                               (GtkAttachOptions) (GTK_FILL),
1511                               (GtkAttachOptions) (GTK_FILL), 0, 0);
1512           }
1513
1514           {
1515             GtkWidget* button = gtk_button_new_with_label ("90");
1516             gtk_widget_show (button);
1517             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"90");
1518             gtk_table_attach (GTK_TABLE (table), button, 1, 2, 0, 1,
1519                               (GtkAttachOptions) (GTK_FILL),
1520                               (GtkAttachOptions) (GTK_FILL), 0, 0);
1521           }
1522
1523
1524           {
1525             GtkWidget* button = gtk_button_new_with_label ("135");
1526             gtk_widget_show (button);
1527             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"135");
1528             gtk_table_attach (GTK_TABLE (table), button, 0, 1, 0, 1,
1529                               (GtkAttachOptions) (GTK_FILL),
1530                               (GtkAttachOptions) (GTK_FILL), 0, 0);
1531           }
1532
1533           {
1534             GtkWidget* button = gtk_button_new_with_label ("180");
1535             gtk_widget_show (button);
1536             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"180");
1537             gtk_table_attach (GTK_TABLE (table), button, 0, 1, 1, 2,
1538                               (GtkAttachOptions) (GTK_FILL),
1539                               (GtkAttachOptions) (GTK_FILL), 0, 0);
1540           }
1541
1542           {
1543             GtkWidget* button = gtk_button_new_with_label ("225");
1544             gtk_widget_show (button);
1545             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"225");
1546             gtk_table_attach (GTK_TABLE (table), button, 0, 1, 2, 3,
1547                               (GtkAttachOptions) (GTK_FILL),
1548                               (GtkAttachOptions) (GTK_FILL), 0, 0);
1549           }
1550
1551           {
1552             GtkWidget* button = gtk_button_new_with_label ("270");
1553             gtk_widget_show (button);
1554             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"270");
1555             gtk_table_attach (GTK_TABLE (table), button, 1, 2, 2, 3,
1556                               (GtkAttachOptions) (GTK_FILL),
1557                               (GtkAttachOptions) (GTK_FILL), 0, 0);
1558           }
1559
1560           {
1561             GtkWidget* button = gtk_button_new_with_label ("315");
1562             gtk_widget_show (button);
1563             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"315");
1564             gtk_table_attach (GTK_TABLE (table), button, 2, 3, 2, 3,
1565                               (GtkAttachOptions) (GTK_FILL),
1566                               (GtkAttachOptions) (GTK_FILL), 0, 0);
1567           }
1568         }
1569
1570         {
1571           GtkWidget* vbox2 = gtk_vbox_new (FALSE, 0);
1572           gtk_widget_show (vbox2);
1573           gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 0);
1574
1575           {
1576             GtkWidget* button = gtk_button_new_with_label ("Reset");
1577             gtk_widget_show (button);
1578             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (ResetEntity), NULL);
1579             gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
1580           }
1581
1582           {
1583             GtkWidget* button = gtk_button_new_with_label ("Up");
1584             gtk_widget_show (button);
1585             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"-1");
1586             gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
1587           }
1588
1589           {
1590             GtkWidget* button = gtk_button_new_with_label ("Dn");
1591             gtk_widget_show (button);
1592             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (entitylist_angle), (void *)"-2");
1593             gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
1594           }
1595         }
1596
1597         {
1598           GtkWidget* vbox2 = gtk_vbox_new (FALSE, 0);
1599           gtk_widget_show (vbox2);
1600           gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 0);
1601
1602           {
1603             GtkWidget* button = gtk_button_new_with_label ("Del Key/Pair");
1604             gtk_widget_show (button);
1605             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (DelProp), NULL);
1606             gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
1607           }
1608
1609           {
1610             GtkWidget* button = gtk_button_new_with_label ("Sound...");
1611             gtk_widget_show (button);
1612             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (AssignSound), NULL);
1613             gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
1614           }
1615
1616           {
1617             GtkWidget* button = gtk_button_new_with_label ("Model...");
1618             gtk_widget_show (button);
1619             gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (AssignModel), NULL);
1620             gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
1621           }
1622         }
1623       }
1624     }
1625
1626     if (g_pParentWnd->FloatingGroupDialog())
1627     {
1628       {
1629         GtkWidget* scr = gtk_scrolled_window_new (NULL, NULL);
1630         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scr), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1631         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scr), GTK_SHADOW_IN);
1632         gtk_widget_show (scr);
1633         gtk_container_set_border_width (GTK_CONTAINER (scr), 3);
1634
1635         {
1636           GtkWidget* text = gtk_text_view_new ();
1637           gtk_widget_set_size_request(text, 0, -1); // allow shrinking
1638           gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
1639           gtk_text_view_set_editable (GTK_TEXT_VIEW(text), FALSE);
1640           gtk_container_add (GTK_CONTAINER (scr), text);
1641           gtk_widget_show (text);
1642           g_qeglobals_gui.d_edit = text;
1643         }
1644
1645         {
1646           GtkWidget* label = gtk_label_new ("Console");
1647           gtk_widget_show (label);
1648           gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scr, label);
1649         }
1650       }
1651     }
1652
1653
1654   //++timo NOTE: this part for grouping code, don't remove! (we'll put it back in sometime soon)
1655
1656   /*
1657   vbox = gtk_vbox_new (FALSE, 5);
1658   gtk_widget_show (vbox);
1659   gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
1660
1661   scr = gtk_scrolled_window_new (NULL, NULL);
1662   gtk_widget_show (scr);
1663   gtk_box_pack_start (GTK_BOX (vbox), scr, TRUE, TRUE, 0);
1664   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scr), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1665
1666   ctree = gtk_ctree_new (1, 0);
1667   gtk_widget_show (ctree);
1668   gtk_container_add (GTK_CONTAINER (scr), ctree);
1669   gtk_clist_column_titles_hide (GTK_CLIST (ctree));
1670   m_pTree = ctree;
1671
1672   hbox = gtk_hbox_new (FALSE, 5);
1673   gtk_widget_show (hbox);
1674   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
1675
1676   button = gtk_button_new_with_label ("Add...");
1677   gtk_widget_show (button);
1678   gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (groupdlg_add), NULL);
1679   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
1680   gtk_widget_set_usize (button, 60, -2);
1681
1682   button = gtk_button_new_with_label ("Edit...");
1683   gtk_widget_show (button);
1684   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
1685   gtk_widget_set_usize (button, 60, -2);
1686
1687   button = gtk_button_new_with_label ("Delete");
1688   gtk_widget_show (button);
1689   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
1690   gtk_widget_set_usize (button, 60, -2);
1691
1692   label = gtk_label_new ("Groups");
1693   gtk_widget_show (label);
1694   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, label);
1695   */
1696   inspector_mode = W_ENTITY;
1697   //  gtk_window_set_title (GTK_WINDOW (dlg), "Entities");
1698   m_pWidget = dlg;
1699   /*
1700   load_pixmap ("grouptree1.bmp", g_pParentWnd->m_pWidget, &tree_pixmaps[0], &tree_masks[0]);
1701   load_pixmap ("grouptree2.bmp", g_pParentWnd->m_pWidget, &tree_pixmaps[1], &tree_masks[1]);
1702   load_pixmap ("grouptree3.bmp", g_pParentWnd->m_pWidget, &tree_pixmaps[2], &tree_masks[2]);
1703   load_pixmap ("grouptree4.bmp", g_pParentWnd->m_pWidget, &tree_pixmaps[3], &tree_masks[3]);
1704   load_pixmap ("grouptree5.bmp", g_pParentWnd->m_pWidget, &tree_pixmaps[4], &tree_masks[4]);
1705   load_pixmap ("grouptree6.bmp", g_pParentWnd->m_pWidget, &tree_pixmaps[5], &tree_masks[5]);
1706   load_pixmap ("grouptree7.bmp", g_pParentWnd->m_pWidget, &tree_pixmaps[6], &tree_masks[6]);
1707
1708   Group_Init();
1709 */
1710     g_signal_connect (G_OBJECT (notebook), "switch_page", G_CALLBACK (switch_page), dlg);
1711   }
1712 }
1713