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