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 
GameComboConfiguration()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 
globalGameComboConfiguration()111 inline GameComboConfiguration& globalGameComboConfiguration()
112 {
113   return LazyStaticGameComboConfiguration::instance();
114 }
115 
116 
117 struct gamecombo_t
118 {
gamecombo_tgamecombo_t119   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 
gamecombo_for_dir(const char * dir)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 
gamecombo_for_gamename(const char * gamename)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 
path_copy_clean(char * destination,const char * source)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 
OnSelchangeComboWhatgame(GtkWidget * widget,GameCombo * combo)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 
MappingMode()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 
globalMappingMode()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 
ProjectSettingsDialog_construct(ProjectSettingsDialog & dialog,ModalDialog & modal)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 
ProjectSettingsDialog_ok(ProjectSettingsDialog & dialog)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 
DoProjectSettings()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 
DoSides(int type,int axis)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 
about_button_changelog(GtkWidget * widget,gpointer data)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 
about_button_credits(GtkWidget * widget,gpointer data)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 
DoAbout()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 
DoTextureListDlg()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 
DoTextureLayout(float * fx,float * fy)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 
editor_delete(GtkWidget * widget,gpointer data)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 
editor_save(GtkWidget * widget,gpointer data)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 
editor_close(GtkWidget * widget,gpointer data)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 
CreateGtkTextEditor()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 
DoGtkTextEditor(const char * filename,guint cursorpos)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 
DoLightIntensityDlg(int * intensity)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 
DoTextEditor(const char * filename,int cursorpos)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 }
1075