]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/gtkdlgs.cpp
fixed const inconsistencies
[xonotic/netradiant.git] / radiant / gtkdlgs.cpp
1 /*
2 Copyright (c) 2001, Loki software, inc.
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without modification, 
6 are permitted provided that the following conditions are met:
7
8 Redistributions of source code must retain the above copyright notice, this list 
9 of conditions and the following disclaimer.
10
11 Redistributions in binary form must reproduce the above copyright notice, this
12 list of conditions and the following disclaimer in the documentation and/or
13 other materials provided with the distribution.
14
15 Neither the name of Loki software nor the names of its contributors may be used 
16 to endorse or promote products derived from this software without specific prior 
17 written permission. 
18
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' 
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
22 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY 
23 DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
24 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
25 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
26 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
28 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
29 */
30
31 //
32 // Some small dialogs that don't need much
33 //
34 // Leonardo Zide (leo@lokigames.com)
35 //
36
37 #include "gtkdlgs.h"
38
39 #include "debugging/debugging.h"
40 #include "version.h"
41 #include "aboutmsg.h"
42
43 #include "igl.h"
44 #include "iscenegraph.h"
45 #include "iselection.h"
46
47 #include <gdk/gdkkeysyms.h>
48 #include <gtk/gtkmain.h>
49 #include <gtk/gtkentry.h>
50 #include <gtk/gtkhbox.h>
51 #include <gtk/gtkvbox.h>
52 #include <gtk/gtkframe.h>
53 #include <gtk/gtklabel.h>
54 #include <gtk/gtktable.h>
55 #include <gtk/gtkbutton.h>
56 #include <gtk/gtkcombobox.h>
57 #include <gtk/gtkscrolledwindow.h>
58 #include <gtk/gtktextview.h>
59 #include <gtk/gtktextbuffer.h>
60 #include <gtk/gtktreeview.h>
61 #include <gtk/gtkcellrenderertext.h>
62 #include <gtk/gtktreeselection.h>
63 #include <gtk/gtkliststore.h>
64
65 #include "os/path.h"
66 #include "math/aabb.h"
67 #include "container/array.h"
68 #include "generic/static.h"
69 #include "stream/stringstream.h"
70 #include "convert.h"
71 #include "gtkutil/messagebox.h"
72 #include "gtkutil/image.h"
73
74 #include "gtkmisc.h"
75 #include "brushmanip.h"
76 #include "build.h"
77 #include "qe3.h"
78 #include "texwindow.h"
79 #include "xywindow.h"
80 #include "mainframe.h"
81 #include "preferences.h"
82 #include "url.h"
83 #include "cmdlib.h"
84
85
86
87 // =============================================================================
88 // Project settings dialog
89
90 class GameComboConfiguration
91 {
92 public:
93   const char* basegame_dir;
94   const char* basegame;
95   const char* known_dir;
96   const char* known;
97   const char* custom;
98
99   GameComboConfiguration() :
100     basegame_dir(g_pGameDescription->getRequiredKeyValue("basegame")),
101     basegame(g_pGameDescription->getRequiredKeyValue("basegamename")),
102     known_dir(g_pGameDescription->getKeyValue("knowngame")),
103     known(g_pGameDescription->getKeyValue("knowngamename")),
104     custom(g_pGameDescription->getRequiredKeyValue("unknowngamename"))
105   {
106   }
107 };
108
109 typedef LazyStatic<GameComboConfiguration> LazyStaticGameComboConfiguration;
110
111 inline GameComboConfiguration& globalGameComboConfiguration()
112 {
113   return LazyStaticGameComboConfiguration::instance();
114 }
115
116
117 struct gamecombo_t
118 {
119   gamecombo_t(int _game, const char* _fs_game, bool _sensitive)
120     : game(_game), fs_game(_fs_game), sensitive(_sensitive)
121   {}
122   int game;
123   const char* fs_game;
124   bool sensitive;
125 };
126
127 gamecombo_t gamecombo_for_dir(const char* dir)
128 {
129   if(string_equal(dir, globalGameComboConfiguration().basegame_dir))
130   {
131     return gamecombo_t(0, "", false);
132   }
133   else if(string_equal(dir, globalGameComboConfiguration().known_dir))
134   {
135     return gamecombo_t(1, dir, false);
136   }
137   else
138   {
139     return gamecombo_t(string_empty(globalGameComboConfiguration().known_dir) ? 1 : 2, dir, true);
140   }
141 }
142
143 gamecombo_t gamecombo_for_gamename(const char* gamename)
144 {
145   if ((strlen(gamename) == 0) || !strcmp(gamename, globalGameComboConfiguration().basegame))
146   {
147     return gamecombo_t(0, "", false);
148   }
149   else if (!strcmp(gamename, globalGameComboConfiguration().known))
150   {
151     return gamecombo_t(1, globalGameComboConfiguration().known_dir, false);
152   }
153   else
154   {
155     return gamecombo_t(string_empty(globalGameComboConfiguration().known_dir) ? 1 : 2, "", true);
156   }
157 }
158
159 inline void path_copy_clean(char* destination, const char* source)
160 {
161   char* i = destination;
162
163   while(*source != '\0')
164   {
165     *i++ = (*source == '\\') ? '/' : *source;
166     ++source;
167   }
168
169   if(i != destination && *(i-1) != '/')
170     *(i++) = '/';
171
172   *i = '\0';
173 }
174
175
176 struct GameCombo
177 {
178   GtkComboBox* game_select;
179   GtkEntry* fsgame_entry;
180 };
181
182 gboolean OnSelchangeComboWhatgame(GtkWidget *widget, GameCombo* combo)
183 {
184   const char *gamename;
185   {
186     GtkTreeIter iter;
187     gtk_combo_box_get_active_iter(combo->game_select, &iter);
188     gtk_tree_model_get(gtk_combo_box_get_model(combo->game_select), &iter, 0, (gpointer*)&gamename, -1);
189   }
190
191   gamecombo_t gamecombo = gamecombo_for_gamename(gamename);
192   
193   gtk_entry_set_text(combo->fsgame_entry, gamecombo.fs_game);
194   gtk_widget_set_sensitive(GTK_WIDGET(combo->fsgame_entry), gamecombo.sensitive);
195
196   return FALSE;
197 }
198
199 class MappingMode
200 {
201 public:
202   bool do_mapping_mode;
203   const char* sp_mapping_mode;
204   const char* mp_mapping_mode;
205
206   MappingMode() :
207     do_mapping_mode(!string_empty(g_pGameDescription->getKeyValue("show_gamemode"))),
208     sp_mapping_mode("Single Player mapping mode"),
209     mp_mapping_mode("Multiplayer mapping mode")
210   {
211   }
212 };
213
214 typedef LazyStatic<MappingMode> LazyStaticMappingMode;
215
216 inline MappingMode& globalMappingMode()
217 {
218   return LazyStaticMappingMode::instance();
219 }
220
221 class ProjectSettingsDialog
222 {
223 public:
224   GameCombo game_combo;
225   GtkComboBox* gamemode_combo;
226 };
227
228 GtkWindow* ProjectSettingsDialog_construct(ProjectSettingsDialog& dialog, ModalDialog& modal)
229 {
230   GtkWindow* window = create_dialog_window(MainFrame_getWindow(), "Project Settings", G_CALLBACK(dialog_delete_callback), &modal);
231
232   {
233     GtkTable* table1 = create_dialog_table(1, 2, 4, 4, 4);
234     gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(table1));
235     {
236       GtkVBox* vbox = create_dialog_vbox(4);
237       gtk_table_attach(table1, GTK_WIDGET(vbox), 1, 2, 0, 1,
238                         (GtkAttachOptions) (GTK_FILL),
239                         (GtkAttachOptions) (GTK_FILL), 0, 0);
240       {
241         GtkButton* button = create_dialog_button("OK", G_CALLBACK(dialog_button_ok), &modal);
242         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
243       }
244       {
245         GtkButton* button = create_dialog_button("Cancel", G_CALLBACK(dialog_button_cancel), &modal);
246         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
247       }
248     }
249     {
250       GtkFrame* frame = create_dialog_frame("Project settings");
251       gtk_table_attach(table1, GTK_WIDGET(frame), 0, 1, 0, 1,
252                         (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
253                         (GtkAttachOptions) (GTK_FILL), 0, 0);
254       {
255         GtkTable* table2 = create_dialog_table((globalMappingMode().do_mapping_mode) ? 4 : 3, 2, 4, 4, 4);
256         gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(table2));
257
258         {
259           GtkLabel* label = GTK_LABEL(gtk_label_new("Select mod"));
260           gtk_widget_show(GTK_WIDGET(label));
261           gtk_table_attach(table2, GTK_WIDGET(label), 0, 1, 0, 1,
262                             (GtkAttachOptions) (GTK_FILL),
263                             (GtkAttachOptions) (0), 0, 0);
264           gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
265         }
266         {
267           dialog.game_combo.game_select = GTK_COMBO_BOX(gtk_combo_box_new_text());
268
269           gtk_combo_box_append_text(dialog.game_combo.game_select, globalGameComboConfiguration().basegame);
270           if(globalGameComboConfiguration().known[0] != '\0')
271             gtk_combo_box_append_text(dialog.game_combo.game_select, globalGameComboConfiguration().known);
272           gtk_combo_box_append_text(dialog.game_combo.game_select, globalGameComboConfiguration().custom);
273
274           gtk_widget_show(GTK_WIDGET(dialog.game_combo.game_select));
275           gtk_table_attach(table2, GTK_WIDGET(dialog.game_combo.game_select), 1, 2, 0, 1,
276                             (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
277                             (GtkAttachOptions) (0), 0, 0);
278
279           g_signal_connect(G_OBJECT(dialog.game_combo.game_select), "changed", G_CALLBACK(OnSelchangeComboWhatgame), &dialog.game_combo);
280         }
281
282         {
283           GtkLabel* label = GTK_LABEL(gtk_label_new("fs_game"));
284           gtk_widget_show(GTK_WIDGET(label));
285           gtk_table_attach(table2, GTK_WIDGET(label), 0, 1, 1, 2,
286                             (GtkAttachOptions) (GTK_FILL),
287                             (GtkAttachOptions) (0), 0, 0);
288           gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
289         }
290         {
291           GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
292           gtk_widget_show(GTK_WIDGET(entry));
293           gtk_table_attach(table2, GTK_WIDGET(entry), 1, 2, 1, 2,
294                             (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
295                             (GtkAttachOptions) (0), 0, 0);
296
297           dialog.game_combo.fsgame_entry = entry;
298        }
299   
300         if(globalMappingMode().do_mapping_mode)
301         {
302           GtkLabel* label = GTK_LABEL(gtk_label_new("Mapping mode"));
303           gtk_widget_show(GTK_WIDGET(label));
304           gtk_table_attach(table2, GTK_WIDGET(label), 0, 1, 3, 4,
305             (GtkAttachOptions) (GTK_FILL),
306             (GtkAttachOptions) (0), 0, 0);
307           gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
308
309           GtkComboBox* combo = GTK_COMBO_BOX(gtk_combo_box_new_text());
310           gtk_combo_box_append_text(combo, globalMappingMode().sp_mapping_mode);
311           gtk_combo_box_append_text(combo, globalMappingMode().mp_mapping_mode);
312
313           gtk_widget_show(GTK_WIDGET(combo));
314           gtk_table_attach(table2, GTK_WIDGET(combo), 1, 2, 3, 4,
315             (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
316             (GtkAttachOptions) (0), 0, 0);
317
318           dialog.gamemode_combo = combo;
319         }
320       }
321     }
322   }
323
324   // initialise the fs_game selection from the project settings into the dialog
325   const char* dir = gamename_get();
326   gamecombo_t gamecombo = gamecombo_for_dir(dir);
327
328   gtk_combo_box_set_active(dialog.game_combo.game_select, gamecombo.game);
329   gtk_entry_set_text(dialog.game_combo.fsgame_entry, gamecombo.fs_game);
330   gtk_widget_set_sensitive(GTK_WIDGET(dialog.game_combo.fsgame_entry), gamecombo.sensitive);
331
332   if(globalMappingMode().do_mapping_mode)
333   {
334     const char *gamemode = gamemode_get();
335     if (string_empty(gamemode) || string_equal(gamemode, "sp"))
336     {
337       gtk_combo_box_set_active(dialog.gamemode_combo, 0);
338     }
339     else
340     {
341       gtk_combo_box_set_active(dialog.gamemode_combo, 1);
342     }
343   }
344
345   return window;
346 }
347
348 void ProjectSettingsDialog_ok(ProjectSettingsDialog& dialog)
349 {
350   const char* dir = gtk_entry_get_text(dialog.game_combo.fsgame_entry);
351   
352   const char* new_gamename = path_equal(dir, globalGameComboConfiguration().basegame_dir)
353     ? ""
354     : dir;
355
356   if(!path_equal(new_gamename, gamename_get()))
357   {
358     ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Changing Game Name");
359
360     EnginePath_Unrealise();
361
362     gamename_set(new_gamename);
363
364     EnginePath_Realise();
365   }
366
367   if(globalMappingMode().do_mapping_mode)
368   {
369     // read from gamemode_combo
370     int active = gtk_combo_box_get_active(dialog.gamemode_combo);
371     if(active == -1 || active == 0)
372     {
373       gamemode_set("sp");
374     }
375     else
376     {
377       gamemode_set("mp");
378     }
379   }
380 }
381
382 void DoProjectSettings()
383 {
384   if(ConfirmModified("Edit Project Settings"))
385   {
386     ModalDialog modal;
387     ProjectSettingsDialog dialog;
388
389     GtkWindow* window = ProjectSettingsDialog_construct(dialog, modal);
390
391     if(modal_dialog_show(window, modal) == eIDOK)
392     {
393       ProjectSettingsDialog_ok(dialog);
394     }
395
396     gtk_widget_destroy(GTK_WIDGET(window));
397   }
398 }
399
400 // =============================================================================
401 // Arbitrary Sides dialog
402
403 void DoSides (int type, int axis)
404 {
405   ModalDialog dialog;
406   GtkEntry* sides_entry;
407
408   GtkWindow* window = create_dialog_window(MainFrame_getWindow(), "Arbitrary sides", G_CALLBACK(dialog_delete_callback), &dialog);
409
410   GtkAccelGroup* accel = gtk_accel_group_new();
411   gtk_window_add_accel_group(window, accel);
412
413   {
414     GtkHBox* hbox = create_dialog_hbox(4, 4);
415     gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(hbox));
416     {
417       GtkLabel* label = GTK_LABEL(gtk_label_new("Sides:"));
418       gtk_widget_show(GTK_WIDGET(label));
419       gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), FALSE, FALSE, 0);
420     }
421     {
422       GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
423       gtk_widget_show(GTK_WIDGET(entry));
424       gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(entry), FALSE, FALSE, 0);
425       sides_entry = entry;
426       gtk_widget_grab_focus(GTK_WIDGET(entry));
427     }
428     {
429       GtkVBox* vbox = create_dialog_vbox(4);
430       gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), TRUE, TRUE, 0);
431       {
432         GtkButton* button = create_dialog_button("OK", G_CALLBACK(dialog_button_ok), &dialog);
433         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
434         widget_make_default(GTK_WIDGET(button));
435         gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0);
436       }
437       {
438         GtkButton* button = create_dialog_button("Cancel", G_CALLBACK(dialog_button_cancel), &dialog);
439         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
440         gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0);
441       }
442     }
443   }
444
445   if(modal_dialog_show(window, dialog) == eIDOK)
446   {
447     const char *str = gtk_entry_get_text(sides_entry);
448
449     Scene_BrushConstructPrefab(GlobalSceneGraph(), (EBrushPrefab)type, atoi(str), TextureBrowser_GetSelectedShader(GlobalTextureBrowser()));
450   }
451
452   gtk_widget_destroy(GTK_WIDGET(window));
453 }
454
455 // =============================================================================
456 // About dialog (no program is complete without one)
457
458 void about_button_changelog (GtkWidget *widget, gpointer data)
459 {
460   StringOutputStream log(256);
461   log << AppPath_get() << "changelog.txt";
462   OpenURL(log.c_str());
463 }
464
465 void about_button_credits (GtkWidget *widget, gpointer data)
466 {
467   StringOutputStream cred(256);
468   cred << AppPath_get() << "credits.html";
469   OpenURL(cred.c_str());
470 }
471
472 void DoAbout()
473 {
474   ModalDialog dialog;
475   ModalDialogButton ok_button(dialog, eIDOK);
476
477   GtkWindow* window = create_modal_dialog_window(MainFrame_getWindow(), "About GtkRadiant", dialog);
478
479   {
480     GtkVBox* vbox = create_dialog_vbox(4, 4);
481     gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox));
482
483     {
484       GtkHBox* hbox = create_dialog_hbox(4);
485       gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), FALSE, TRUE, 0);
486
487       {
488         GtkVBox* vbox2 = create_dialog_vbox(4);
489         gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox2), TRUE, FALSE, 0);
490         {
491           GtkFrame* frame = create_dialog_frame(0, GTK_SHADOW_IN);
492           gtk_box_pack_start(GTK_BOX (vbox2), GTK_WIDGET(frame), FALSE, FALSE, 0);
493           {
494             GtkImage* image = new_local_image("logo.bmp");
495             gtk_widget_show(GTK_WIDGET(image));
496             gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(image));
497           }
498         }
499       }
500
501       {
502         GtkLabel* label = GTK_LABEL(gtk_label_new("GtkRadiant " RADIANT_VERSION "\n"
503           __DATE__ "\n\n"
504           RADIANT_ABOUTMSG "\n\n"
505           "By qeradiant.com\n\n"
506           "This product contains software technology\n"
507           "from id Software, Inc. ('id Technology').\n"
508           "id Technology 2000 id Software,Inc.\n\n"
509           "GtkRadiant is unsupported, however\n"
510           "you may report your problems at\n"
511           "http://zerowing.idsoftware.com/bugzilla"
512         ));
513                        
514         gtk_widget_show(GTK_WIDGET(label));
515         gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), FALSE, FALSE, 0);
516         gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
517         gtk_label_set_justify(label, GTK_JUSTIFY_LEFT);
518       }
519
520       {
521         GtkVBox* vbox2 = create_dialog_vbox(4);
522         gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox2), FALSE, TRUE, 0);
523         {
524           GtkButton* button = create_modal_dialog_button("OK", ok_button);
525           gtk_box_pack_start (GTK_BOX (vbox2), GTK_WIDGET(button), FALSE, FALSE, 0);
526         }
527         {
528           GtkButton* button = create_dialog_button("Credits", G_CALLBACK(about_button_credits), 0);
529           gtk_box_pack_start (GTK_BOX (vbox2), GTK_WIDGET(button), FALSE, FALSE, 0);
530         }
531         {
532           GtkButton* button = create_dialog_button("Changelog", G_CALLBACK(about_button_changelog), 0);
533           gtk_box_pack_start (GTK_BOX (vbox2), GTK_WIDGET(button), FALSE, FALSE, 0);
534         }
535       }
536     }
537     {
538       GtkFrame* frame = create_dialog_frame("OpenGL Properties");
539       gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(frame), FALSE, FALSE, 0);
540       {
541         GtkTable* table = create_dialog_table(3, 2, 4, 4, 4);
542         gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(table));
543         {
544           GtkLabel* label = GTK_LABEL(gtk_label_new("Vendor:"));
545           gtk_widget_show(GTK_WIDGET(label));
546           gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 0, 1,
547                             (GtkAttachOptions) (GTK_FILL),
548                             (GtkAttachOptions) (0), 0, 0);
549           gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
550         }
551         {
552           GtkLabel* label = GTK_LABEL(gtk_label_new("Version:"));
553           gtk_widget_show(GTK_WIDGET(label));
554           gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 1, 2,
555                             (GtkAttachOptions) (GTK_FILL),
556                             (GtkAttachOptions) (0), 0, 0);
557           gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
558         }
559         {
560           GtkLabel* label = GTK_LABEL(gtk_label_new("Renderer:"));
561           gtk_widget_show(GTK_WIDGET(label));
562           gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 2, 3,
563                             (GtkAttachOptions) (GTK_FILL),
564                             (GtkAttachOptions) (0), 0, 0);
565           gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
566         }
567         {
568           GtkLabel* label = GTK_LABEL(gtk_label_new(reinterpret_cast<const char*>(glGetString(GL_VENDOR))));
569           gtk_widget_show(GTK_WIDGET(label));
570           gtk_table_attach(table, GTK_WIDGET(label), 1, 2, 0, 1,
571                             (GtkAttachOptions) (GTK_FILL),
572                             (GtkAttachOptions) (0), 0, 0);
573           gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
574         }
575         {
576           GtkLabel* label = GTK_LABEL(gtk_label_new(reinterpret_cast<const char*>(glGetString(GL_VERSION))));
577           gtk_widget_show(GTK_WIDGET(label));
578           gtk_table_attach(table, GTK_WIDGET(label), 1, 2, 1, 2,
579                             (GtkAttachOptions) (GTK_FILL),
580                             (GtkAttachOptions) (0), 0, 0);
581           gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
582         }
583         {
584           GtkLabel* label = GTK_LABEL(gtk_label_new(reinterpret_cast<const char*>(glGetString(GL_RENDERER))));
585           gtk_widget_show(GTK_WIDGET(label));
586           gtk_table_attach(table, GTK_WIDGET(label), 1, 2, 2, 3,
587                             (GtkAttachOptions) (GTK_FILL),
588                             (GtkAttachOptions) (0), 0, 0);
589           gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
590         }
591       }
592       {
593         GtkFrame* frame = create_dialog_frame("OpenGL Extensions");
594         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(frame), TRUE, TRUE, 0);
595         {
596           GtkScrolledWindow* sc_extensions = create_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, 4);
597           gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET(sc_extensions));
598           {
599             GtkWidget* text_extensions = gtk_text_view_new();
600             gtk_text_view_set_editable(GTK_TEXT_VIEW(text_extensions), FALSE);
601             gtk_container_add (GTK_CONTAINER (sc_extensions), text_extensions);
602             GtkTextBuffer* buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_extensions));
603             gtk_text_buffer_set_text(buffer, reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)), -1);
604             gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_extensions), GTK_WRAP_WORD);
605             gtk_widget_show(text_extensions);
606           }
607         }
608       }
609     }
610   }
611
612   modal_dialog_show(window, dialog);
613
614   gtk_widget_destroy(GTK_WIDGET(window));
615 }
616
617 // =============================================================================
618 // Texture List dialog 
619
620 void DoTextureListDlg()
621 {
622   ModalDialog dialog;
623   ModalDialogButton ok_button(dialog, eIDOK);
624   ModalDialogButton cancel_button(dialog, eIDCANCEL);
625   GtkWidget* texture_list;
626
627   GtkWindow* window = create_modal_dialog_window(MainFrame_getWindow(), "Textures", dialog, 400, 400);
628
629   GtkHBox* hbox = create_dialog_hbox(4, 4);
630   gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(hbox));
631
632   {
633     GtkScrolledWindow* scr = create_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
634     gtk_box_pack_start(GTK_BOX (hbox), GTK_WIDGET(scr), TRUE, TRUE, 0);
635
636
637     {
638       GtkListStore* store = gtk_list_store_new(1, G_TYPE_STRING);
639
640       GtkWidget* view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
641       gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); 
642
643       {
644         GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
645         GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("", renderer, "text", 0, 0);
646         gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
647       }
648
649       gtk_widget_show(view);
650       gtk_container_add(GTK_CONTAINER (scr), view);
651
652       {
653         // Initialize dialog
654         GSList *textures = 0;
655         TextureGroupsMenu_ListItems(textures);
656         while (textures != 0)
657         {
658           {
659             GtkTreeIter iter;
660             gtk_list_store_append(store, &iter);
661             StringOutputStream name(64);
662             name << ConvertLocaleToUTF8(reinterpret_cast<const char*>(textures->data));
663             gtk_list_store_set(store, &iter, 0, name.c_str(), -1);
664           }
665           textures = g_slist_remove (textures, textures->data);
666         }
667       }
668     
669       g_object_unref(G_OBJECT(store));
670
671       texture_list = view;
672     }
673   }
674
675   GtkVBox* vbox = create_dialog_vbox(4);
676   gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, TRUE, 0);
677   {
678     GtkButton* button = create_modal_dialog_button("Load", ok_button);
679     gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
680   }
681   {
682     GtkButton* button = create_modal_dialog_button("Close", cancel_button);
683     gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
684   }
685
686   if(modal_dialog_show(window, dialog) == eIDOK)
687   {
688     GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(texture_list));
689
690     GtkTreeModel* model;
691     GtkTreeIter iter;
692     if(gtk_tree_selection_get_selected(selection, &model, &iter))
693     {
694       GtkTreePath* path = gtk_tree_model_get_path(model, &iter);
695       if(gtk_tree_path_get_depth(path) == 1)
696         TextureBrowser_ShowDirectory(GlobalTextureBrowser(), TextureGroupsMenu_GetName(gtk_tree_path_get_indices(path)[0]));
697       gtk_tree_path_free(path);
698     }
699   }
700
701   gtk_widget_destroy(GTK_WIDGET(window));
702 }
703
704 // =============================================================================
705 // TextureLayout dialog 
706
707 EMessageBoxReturn DoTextureLayout (float *fx, float *fy)
708 {
709   ModalDialog dialog;
710   ModalDialogButton ok_button(dialog, eIDOK);
711   ModalDialogButton cancel_button(dialog, eIDCANCEL);
712   GtkEntry* x;
713   GtkEntry* y;
714
715   GtkWindow* window = create_modal_dialog_window(MainFrame_getWindow(), "Patch texture layout", dialog);
716
717   GtkAccelGroup* accel = gtk_accel_group_new();
718   gtk_window_add_accel_group(window, accel);
719
720   {
721     GtkHBox* hbox = create_dialog_hbox(4, 4);
722     gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(hbox));
723     {
724       GtkVBox* vbox = create_dialog_vbox(4);
725       gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), TRUE, TRUE, 0);
726       {
727         GtkLabel* label = GTK_LABEL(gtk_label_new("Texture will be fit across the patch based\n"
728           "on the x and y values given. Values of 1x1\n"
729           "will \"fit\" the texture. 2x2 will repeat\n"
730           "it twice, etc."));
731         gtk_widget_show(GTK_WIDGET(label));
732         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(label), TRUE, TRUE, 0);
733         gtk_label_set_justify(label, GTK_JUSTIFY_LEFT);
734       }
735       {
736         GtkTable* table = create_dialog_table(2, 2, 4, 4);
737         gtk_widget_show(GTK_WIDGET(table));
738         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(table), TRUE, TRUE, 0);
739         {
740           GtkLabel* label = GTK_LABEL(gtk_label_new("Texture x:"));
741           gtk_widget_show(GTK_WIDGET(label));
742           gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 0, 1,
743                             (GtkAttachOptions) (GTK_FILL),
744                             (GtkAttachOptions) (0), 0, 0);
745           gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
746         }
747         {
748           GtkLabel* label = GTK_LABEL(gtk_label_new("Texture y:"));
749           gtk_widget_show(GTK_WIDGET(label));
750           gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 1, 2,
751                             (GtkAttachOptions) (GTK_FILL),
752                             (GtkAttachOptions) (0), 0, 0);
753           gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
754         }
755         {
756           GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
757           gtk_widget_show(GTK_WIDGET(entry));
758           gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 0, 1,
759                             (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
760                             (GtkAttachOptions) (0), 0, 0);
761
762           gtk_widget_grab_focus(GTK_WIDGET(entry));
763
764           x = entry;
765         }
766         {
767           GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
768           gtk_widget_show(GTK_WIDGET(entry));
769           gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 1, 2,
770                             (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
771                             (GtkAttachOptions) (0), 0, 0);
772
773           y = entry;
774         }
775       }
776     }
777     {
778       GtkVBox* vbox = create_dialog_vbox(4);
779       gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 0);
780       {
781         GtkButton* button = create_modal_dialog_button("OK", ok_button);
782         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
783         widget_make_default(GTK_WIDGET(button));
784         gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0);
785       }
786       {
787         GtkButton* button = create_modal_dialog_button("Cancel", cancel_button);
788         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
789         gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0);
790       }
791     }
792   }
793
794   // Initialize
795   gtk_entry_set_text(x, "4.0");
796   gtk_entry_set_text(y, "4.0");
797
798
799   EMessageBoxReturn ret = modal_dialog_show(window, dialog);
800   if (ret == eIDOK)
801   {
802     *fx = static_cast<float>(atof(gtk_entry_get_text(x)));
803     *fy = static_cast<float>(atof(gtk_entry_get_text(y)));
804   }
805
806   gtk_widget_destroy(GTK_WIDGET(window));
807
808   return ret;
809 }
810
811 // =============================================================================
812 // Text Editor dialog 
813
814 // master window widget
815 static GtkWidget *text_editor = 0;
816 static GtkWidget *text_widget; // slave, text widget from the gtk editor
817
818 static gint editor_delete (GtkWidget *widget, gpointer data)
819 {
820   if (gtk_MessageBox (widget, "Close the shader editor ?", "Radiant", eMB_YESNO, eMB_ICONQUESTION) == eIDNO)
821     return TRUE;
822
823   gtk_widget_hide (text_editor);
824
825   return TRUE;
826 }
827
828 static void editor_save (GtkWidget *widget, gpointer data)
829 {
830   FILE *f = fopen ((char*)g_object_get_data (G_OBJECT (data), "filename"), "w");
831   gpointer text = g_object_get_data (G_OBJECT (data), "text");
832
833   if (f == 0)
834   {
835     gtk_MessageBox (GTK_WIDGET(data), "Error saving file !");
836     return;
837   }
838
839   char *str = gtk_editable_get_chars (GTK_EDITABLE (text), 0, -1);
840   fwrite (str, 1, strlen (str), f);
841   fclose (f);
842 }
843
844 static void editor_close (GtkWidget *widget, gpointer data)
845 {
846   if (gtk_MessageBox (text_editor, "Close the shader editor ?", "Radiant", eMB_YESNO, eMB_ICONQUESTION) == eIDNO)
847     return;
848
849   gtk_widget_hide (text_editor);
850 }
851
852 static void CreateGtkTextEditor()
853 {
854   GtkWidget *dlg;
855   GtkWidget *vbox, *hbox, *button, *scr, *text;
856
857   dlg = gtk_window_new (GTK_WINDOW_TOPLEVEL);
858
859   g_signal_connect(G_OBJECT(dlg), "delete_event",
860                       G_CALLBACK(editor_delete), 0);
861   gtk_window_set_default_size (GTK_WINDOW (dlg), 600, 300);
862
863   vbox = gtk_vbox_new (FALSE, 5);
864   gtk_widget_show (vbox);
865   gtk_container_add(GTK_CONTAINER(dlg), GTK_WIDGET(vbox));
866   gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
867
868   scr = gtk_scrolled_window_new (0, 0);
869   gtk_widget_show (scr);
870   gtk_box_pack_start(GTK_BOX(vbox), scr, TRUE, TRUE, 0);
871   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scr), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
872   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scr), GTK_SHADOW_IN);
873
874   text = gtk_text_view_new();
875   gtk_container_add (GTK_CONTAINER (scr), text);
876   gtk_widget_show (text);
877   g_object_set_data (G_OBJECT (dlg), "text", text);
878   gtk_text_view_set_editable (GTK_TEXT_VIEW(text), TRUE);
879
880   hbox = gtk_hbox_new (FALSE, 5);
881   gtk_widget_show (hbox);
882   gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), FALSE, TRUE, 0);
883
884   button = gtk_button_new_with_label ("Close");
885   gtk_widget_show (button);
886   gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
887   g_signal_connect(G_OBJECT(button), "clicked",
888                       G_CALLBACK(editor_close), dlg);
889   gtk_widget_set_usize (button, 60, -2);
890
891   button = gtk_button_new_with_label ("Save");
892   gtk_widget_show (button);
893   gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
894   g_signal_connect(G_OBJECT(button), "clicked",
895                       G_CALLBACK(editor_save), dlg);
896   gtk_widget_set_usize (button, 60, -2);
897
898   text_editor = dlg;
899   text_widget = text;
900 }
901
902 static void DoGtkTextEditor (const char* filename, guint cursorpos)
903 {
904   if (!text_editor)
905     CreateGtkTextEditor(); // build it the first time we need it
906
907   // Load file
908   FILE *f = fopen (filename, "r");
909
910   if (f == 0)
911   {
912     globalOutputStream() << "Unable to load file " << filename << " in shader editor.\n";
913     gtk_widget_hide (text_editor);
914   }
915   else
916   {
917     fseek (f, 0, SEEK_END);
918     int len = ftell (f);
919     void *buf = malloc (len);
920     void *old_filename;
921
922     rewind (f);
923     fread (buf, 1, len, f);
924
925     gtk_window_set_title (GTK_WINDOW (text_editor), filename);
926
927     GtkTextBuffer* text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_widget));
928     gtk_text_buffer_set_text(text_buffer, (char*)buf, len);
929
930     old_filename = g_object_get_data (G_OBJECT (text_editor), "filename");
931     if (old_filename)
932       free(old_filename);
933     g_object_set_data (G_OBJECT (text_editor), "filename", strdup (filename));
934
935     // trying to show later
936     gtk_widget_show (text_editor);
937
938 #ifdef WIN32
939     process_gui();
940 #endif
941
942     // only move the cursor if it's not exceeding the size..
943     // NOTE: this is erroneous, cursorpos is the offset in bytes, not in characters
944     // len is the max size in bytes, not in characters either, but the character count is below that limit..
945     // thinking .. the difference between character count and byte count would be only because of CR/LF?
946     {
947       GtkTextIter text_iter;
948       // character offset, not byte offset
949       gtk_text_buffer_get_iter_at_offset(text_buffer, &text_iter, cursorpos);
950       gtk_text_buffer_place_cursor(text_buffer, &text_iter);
951     }
952
953 #ifdef WIN32
954     gtk_widget_queue_draw(text_widget);
955 #endif
956
957     free (buf);
958     fclose (f);
959   }
960 }
961
962 // =============================================================================
963 // Light Intensity dialog 
964
965 EMessageBoxReturn DoLightIntensityDlg (int *intensity)
966 {
967   ModalDialog dialog;
968   GtkEntry* intensity_entry;
969   ModalDialogButton ok_button(dialog, eIDOK);
970   ModalDialogButton cancel_button(dialog, eIDCANCEL);
971
972   GtkWindow* window = create_modal_dialog_window(MainFrame_getWindow(), "Light intensity", dialog, -1, -1);
973
974   GtkAccelGroup *accel_group = gtk_accel_group_new();
975   gtk_window_add_accel_group(window, accel_group);
976
977   {
978     GtkHBox* hbox = create_dialog_hbox(4, 4);
979     gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(hbox));
980     {
981       GtkVBox* vbox = create_dialog_vbox(4);
982       gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), TRUE, TRUE, 0);
983       {
984         GtkLabel* label = GTK_LABEL(gtk_label_new("ESC for default, ENTER to validate"));
985         gtk_widget_show(GTK_WIDGET(label));
986         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(label), FALSE, FALSE, 0);
987       }
988       {
989         GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
990         gtk_widget_show(GTK_WIDGET(entry));
991         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(entry), TRUE, TRUE, 0);
992
993         gtk_widget_grab_focus(GTK_WIDGET(entry));
994
995         intensity_entry = entry;
996       }
997     }
998     {
999       GtkVBox* vbox = create_dialog_vbox(4);
1000       gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 0);
1001
1002       {
1003         GtkButton* button = create_modal_dialog_button("OK", ok_button);
1004         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
1005         widget_make_default(GTK_WIDGET(button));
1006         gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE);
1007       }
1008       {
1009         GtkButton* button = create_modal_dialog_button("Cancel", cancel_button);
1010         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
1011         gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel_group, GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE);
1012       }
1013     }
1014   }
1015
1016   char buf[16];
1017   sprintf (buf, "%d", *intensity);
1018   gtk_entry_set_text(intensity_entry, buf);
1019
1020   EMessageBoxReturn ret = modal_dialog_show(window, dialog);
1021   if(ret == eIDOK)
1022     *intensity = atoi (gtk_entry_get_text(intensity_entry));
1023
1024   gtk_widget_destroy(GTK_WIDGET(window));
1025
1026   return ret;
1027 }
1028
1029
1030 #ifdef WIN32
1031 #include <gdk/gdkwin32.h>
1032 #endif
1033
1034 #ifdef WIN32
1035   // use the file associations to open files instead of builtin Gtk editor
1036 bool g_TextEditor_useWin32Editor = true;
1037 #else
1038   // custom shader editor
1039 bool g_TextEditor_useCustomEditor = false;
1040 CopiedString g_TextEditor_editorCommand("");
1041 #endif
1042
1043 void DoTextEditor (const char* filename, int cursorpos)
1044 {
1045 #ifdef WIN32
1046   if (g_TextEditor_useWin32Editor)
1047   {
1048     globalOutputStream() << "opening file '" << filename << "' (line " << cursorpos << " info ignored)\n";
1049     ShellExecute((HWND)GDK_WINDOW_HWND (GTK_WIDGET(MainFrame_getWindow())->window), "open", filename, 0, 0, SW_SHOW );
1050     return;
1051   }
1052 #else
1053   // check if a custom editor is set
1054   if(g_TextEditor_useCustomEditor && !g_TextEditor_editorCommand.empty())
1055   {
1056         StringOutputStream strEditCommand(256);
1057     strEditCommand << g_TextEditor_editorCommand.c_str() << " \"" << filename << "\"";
1058     
1059     globalOutputStream() << "Launching: " << strEditCommand.c_str() << "\n";
1060     // note: linux does not return false if the command failed so it will assume success
1061     if (Q_Exec(0, const_cast<char*>(strEditCommand.c_str()), 0, true) == false)
1062     {
1063       globalOutputStream() << "Failed to execute " << strEditCommand.c_str() << ", using default\n";
1064     }
1065     else
1066     {
1067       // the command (appeared) to run successfully, no need to do anything more
1068       return;
1069     }
1070   }
1071 #endif
1072   
1073   DoGtkTextEditor (filename, cursorpos);
1074 }