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