2 Copyright (c) 2001, Loki software, inc.
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
8 Redistributions of source code must retain the above copyright notice, this list
9 of conditions and the following disclaimer.
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.
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
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.
32 // Some small dialogs that don't need much
34 // Leonardo Zide (leo@lokigames.com)
38 #include "globaldefs.h"
42 #include "debugging/debugging.h"
47 #include "iscenegraph.h"
48 #include "iselection.h"
50 #include <gdk/gdkkeysyms.h>
51 #include <uilib/uilib.h>
54 #include "math/aabb.h"
55 #include "container/array.h"
56 #include "generic/static.h"
57 #include "stream/stringstream.h"
59 #include "gtkutil/messagebox.h"
60 #include "gtkutil/image.h"
63 #include "brushmanip.h"
66 #include "texwindow.h"
68 #include "mainframe.h"
69 #include "preferences.h"
75 // =============================================================================
76 // Project settings dialog
78 class GameComboConfiguration {
80 const char *basegame_dir;
82 const char *known_dir;
86 GameComboConfiguration() :
87 basegame_dir(g_pGameDescription->getRequiredKeyValue("basegame")),
88 basegame(g_pGameDescription->getRequiredKeyValue("basegamename")),
89 known_dir(g_pGameDescription->getKeyValue("knowngame")),
90 known(g_pGameDescription->getKeyValue("knowngamename")),
91 custom(g_pGameDescription->getRequiredKeyValue("unknowngamename"))
96 typedef LazyStatic<GameComboConfiguration> LazyStaticGameComboConfiguration;
98 inline GameComboConfiguration &globalGameComboConfiguration()
100 return LazyStaticGameComboConfiguration::instance();
105 gamecombo_t(int _game, const char *_fs_game, bool _sensitive)
106 : game(_game), fs_game(_fs_game), sensitive(_sensitive)
114 gamecombo_t gamecombo_for_dir(const char *dir)
116 if (string_equal(dir, globalGameComboConfiguration().basegame_dir)) {
117 return gamecombo_t(0, "", false);
118 } else if (string_equal(dir, globalGameComboConfiguration().known_dir)) {
119 return gamecombo_t(1, dir, false);
121 return gamecombo_t(string_empty(globalGameComboConfiguration().known_dir) ? 1 : 2, dir, true);
125 gamecombo_t gamecombo_for_gamename(const char *gamename)
127 if ((strlen(gamename) == 0) || !strcmp(gamename, globalGameComboConfiguration().basegame)) {
128 return gamecombo_t(0, "", false);
129 } else if (!strcmp(gamename, globalGameComboConfiguration().known)) {
130 return gamecombo_t(1, globalGameComboConfiguration().known_dir, false);
132 return gamecombo_t(string_empty(globalGameComboConfiguration().known_dir) ? 1 : 2, "", true);
136 inline void path_copy_clean(char *destination, const char *source)
138 char *i = destination;
140 while (*source != '\0') {
141 *i++ = (*source == '\\') ? '/' : *source;
145 if (i != destination && *(i - 1) != '/') {
154 ui::ComboBoxText game_select{ui::null};
155 ui::Entry fsgame_entry{ui::null};
158 gboolean OnSelchangeComboWhatgame(ui::Widget widget, GameCombo *combo)
160 const char *gamename;
163 gtk_combo_box_get_active_iter(combo->game_select, &iter);
164 gtk_tree_model_get(gtk_combo_box_get_model(combo->game_select), &iter, 0, (gpointer *) &gamename, -1);
167 gamecombo_t gamecombo = gamecombo_for_gamename(gamename);
169 combo->fsgame_entry.text(gamecombo.fs_game);
170 gtk_widget_set_sensitive(combo->fsgame_entry, gamecombo.sensitive);
177 bool do_mapping_mode;
178 const char *sp_mapping_mode;
179 const char *mp_mapping_mode;
182 do_mapping_mode(!string_empty(g_pGameDescription->getKeyValue("show_gamemode"))),
183 sp_mapping_mode("Single Player mapping mode"),
184 mp_mapping_mode("Multiplayer mapping mode")
189 typedef LazyStatic<MappingMode> LazyStaticMappingMode;
191 inline MappingMode &globalMappingMode()
193 return LazyStaticMappingMode::instance();
196 class ProjectSettingsDialog {
198 GameCombo game_combo;
199 ui::ComboBox gamemode_combo{ui::null};
202 ui::Window ProjectSettingsDialog_construct(ProjectSettingsDialog &dialog, ModalDialog &modal)
204 auto window = MainFrame_getWindow().create_dialog_window("Project Settings", G_CALLBACK(dialog_delete_callback),
208 auto table1 = create_dialog_table(1, 2, 4, 4, 4);
211 auto vbox = create_dialog_vbox(4);
212 table1.attach(vbox, {1, 2, 0, 1}, {GTK_FILL, GTK_FILL});
214 auto button = create_dialog_button("OK", G_CALLBACK(dialog_button_ok), &modal);
215 vbox.pack_start(button, FALSE, FALSE, 0);
218 auto button = create_dialog_button("Cancel", G_CALLBACK(dialog_button_cancel), &modal);
219 vbox.pack_start(button, FALSE, FALSE, 0);
223 auto frame = create_dialog_frame("Project settings");
224 table1.attach(frame, {0, 1, 0, 1}, {GTK_EXPAND | GTK_FILL, GTK_FILL});
226 auto table2 = create_dialog_table((globalMappingMode().do_mapping_mode) ? 4 : 3, 2, 4, 4, 4);
230 auto label = ui::Label("Select mod");
232 table2.attach(label, {0, 1, 0, 1}, {GTK_FILL, 0});
233 gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
236 dialog.game_combo.game_select = ui::ComboBoxText(ui::New);
238 gtk_combo_box_text_append_text(dialog.game_combo.game_select,
239 globalGameComboConfiguration().basegame);
240 if (globalGameComboConfiguration().known[0] != '\0') {
241 gtk_combo_box_text_append_text(dialog.game_combo.game_select,
242 globalGameComboConfiguration().known);
244 gtk_combo_box_text_append_text(dialog.game_combo.game_select,
245 globalGameComboConfiguration().custom);
247 dialog.game_combo.game_select.show();
248 table2.attach(dialog.game_combo.game_select, {1, 2, 0, 1}, {GTK_EXPAND | GTK_FILL, 0});
250 dialog.game_combo.game_select.connect("changed", G_CALLBACK(OnSelchangeComboWhatgame),
255 auto label = ui::Label("fs_game");
257 table2.attach(label, {0, 1, 1, 2}, {GTK_FILL, 0});
258 gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
261 auto entry = ui::Entry(ui::New);
263 table2.attach(entry, {1, 2, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
265 dialog.game_combo.fsgame_entry = entry;
268 if (globalMappingMode().do_mapping_mode) {
269 auto label = ui::Label("Mapping mode");
271 table2.attach(label, {0, 1, 3, 4}, {GTK_FILL, 0});
272 gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
274 auto combo = ui::ComboBoxText(ui::New);
275 gtk_combo_box_text_append_text(combo, globalMappingMode().sp_mapping_mode);
276 gtk_combo_box_text_append_text(combo, globalMappingMode().mp_mapping_mode);
279 table2.attach(combo, {1, 2, 3, 4}, {GTK_EXPAND | GTK_FILL, 0});
281 dialog.gamemode_combo = combo;
287 // initialise the fs_game selection from the project settings into the dialog
288 const char *dir = gamename_get();
289 gamecombo_t gamecombo = gamecombo_for_dir(dir);
291 gtk_combo_box_set_active(dialog.game_combo.game_select, gamecombo.game);
292 dialog.game_combo.fsgame_entry.text(gamecombo.fs_game);
293 gtk_widget_set_sensitive(dialog.game_combo.fsgame_entry, gamecombo.sensitive);
295 if (globalMappingMode().do_mapping_mode) {
296 const char *gamemode = gamemode_get();
297 if (string_empty(gamemode) || string_equal(gamemode, "sp")) {
298 gtk_combo_box_set_active(dialog.gamemode_combo, 0);
300 gtk_combo_box_set_active(dialog.gamemode_combo, 1);
307 void ProjectSettingsDialog_ok(ProjectSettingsDialog &dialog)
309 const char *dir = gtk_entry_get_text(dialog.game_combo.fsgame_entry);
311 const char *new_gamename = path_equal(dir, globalGameComboConfiguration().basegame_dir)
315 if (!path_equal(new_gamename, gamename_get())) {
316 ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Changing Game Name");
318 EnginePath_Unrealise();
320 gamename_set(new_gamename);
322 EnginePath_Realise();
325 if (globalMappingMode().do_mapping_mode) {
326 // read from gamemode_combo
327 int active = gtk_combo_box_get_active(dialog.gamemode_combo);
328 if (active == -1 || active == 0) {
336 void DoProjectSettings()
338 if (ConfirmModified("Edit Project Settings")) {
340 ProjectSettingsDialog dialog;
342 ui::Window window = ProjectSettingsDialog_construct(dialog, modal);
344 if (modal_dialog_show(window, modal) == eIDOK) {
345 ProjectSettingsDialog_ok(dialog);
352 // =============================================================================
353 // Arbitrary Sides dialog
355 void DoSides(int type, int axis)
359 auto window = MainFrame_getWindow().create_dialog_window("Arbitrary sides", G_CALLBACK(dialog_delete_callback),
362 auto accel = ui::AccelGroup(ui::New);
363 window.add_accel_group(accel);
365 auto sides_entry = ui::Entry(ui::New);
367 auto hbox = create_dialog_hbox(4, 4);
370 auto label = ui::Label("Sides:");
372 hbox.pack_start(label, FALSE, FALSE, 0);
375 auto entry = sides_entry;
377 hbox.pack_start(entry, FALSE, FALSE, 0);
378 gtk_widget_grab_focus(entry);
381 auto vbox = create_dialog_vbox(4);
382 hbox.pack_start(vbox, TRUE, TRUE, 0);
384 auto button = create_dialog_button("OK", G_CALLBACK(dialog_button_ok), &dialog);
385 vbox.pack_start(button, FALSE, FALSE, 0);
386 widget_make_default(button);
387 gtk_widget_add_accelerator(button, "clicked", accel, GDK_KEY_Return, (GdkModifierType) 0,
391 auto button = create_dialog_button("Cancel", G_CALLBACK(dialog_button_cancel), &dialog);
392 vbox.pack_start(button, FALSE, FALSE, 0);
393 gtk_widget_add_accelerator(button, "clicked", accel, GDK_KEY_Escape, (GdkModifierType) 0,
399 if (modal_dialog_show(window, dialog) == eIDOK) {
400 const char *str = gtk_entry_get_text(sides_entry);
402 Scene_BrushConstructPrefab(GlobalSceneGraph(), (EBrushPrefab) type, atoi(str),
403 TextureBrowser_GetSelectedShader(GlobalTextureBrowser()));
409 // =============================================================================
410 // About dialog (no program is complete without one)
412 void about_button_changelog(ui::Widget widget, gpointer data)
414 StringOutputStream log(256);
415 log << "https://gitlab.com/xonotic/netradiant/commits/master";
416 OpenURL(log.c_str());
419 void about_button_credits(ui::Widget widget, gpointer data)
421 StringOutputStream cred(256);
422 cred << "https://gitlab.com/xonotic/netradiant/graphs/master";
423 OpenURL(cred.c_str());
426 void about_button_issues(ui::Widget widget, gpointer data)
428 StringOutputStream cred(256);
429 cred << "https://gitlab.com/xonotic/netradiant/issues";
430 OpenURL(cred.c_str());
436 ModalDialogButton ok_button(dialog, eIDOK);
438 auto window = MainFrame_getWindow().create_modal_dialog_window("About NetRadiant", dialog);
441 auto vbox = create_dialog_vbox(4, 4);
445 auto hbox = create_dialog_hbox(4);
446 vbox.pack_start(hbox, FALSE, TRUE, 0);
449 auto vbox2 = create_dialog_vbox(4);
450 hbox.pack_start(vbox2, TRUE, FALSE, 0);
452 auto frame = create_dialog_frame(0, ui::Shadow::IN);
453 vbox2.pack_start(frame, FALSE, FALSE, 0);
455 auto image = new_local_image("logo.png");
463 char const *label_text = "NetRadiant " RADIANT_VERSION "\n"
465 RADIANT_ABOUTMSG "\n\n"
466 "This program is free software\n"
467 "licensed under the GNU GPL.\n\n"
468 "NetRadiant is unsupported, however\n"
469 "you may report your problems at\n"
470 "https://gitlab.com/xonotic/netradiant/issues";
472 auto label = ui::Label(label_text);
475 hbox.pack_start(label, FALSE, FALSE, 0);
476 gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
477 gtk_label_set_justify(label, GTK_JUSTIFY_LEFT);
481 auto vbox2 = create_dialog_vbox(4);
482 hbox.pack_start(vbox2, FALSE, TRUE, 0);
484 auto button = create_modal_dialog_button("OK", ok_button);
485 vbox2.pack_start(button, FALSE, FALSE, 0);
488 auto button = create_dialog_button("Credits", G_CALLBACK(about_button_credits), 0);
489 vbox2.pack_start(button, FALSE, FALSE, 0);
492 auto button = create_dialog_button("Changes", G_CALLBACK(about_button_changelog), 0);
493 vbox2.pack_start(button, FALSE, FALSE, 0);
496 auto button = create_dialog_button("Issues", G_CALLBACK(about_button_issues), 0);
497 vbox2.pack_start(button, FALSE, FALSE, 0);
502 auto frame = create_dialog_frame("OpenGL Properties");
503 vbox.pack_start(frame, FALSE, FALSE, 0);
505 auto table = create_dialog_table(3, 2, 4, 4, 4);
508 auto label = ui::Label("Vendor:");
510 table.attach(label, {0, 1, 0, 1}, {GTK_FILL, 0});
511 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
514 auto label = ui::Label("Version:");
516 table.attach(label, {0, 1, 1, 2}, {GTK_FILL, 0});
517 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
520 auto label = ui::Label("Renderer:");
522 table.attach(label, {0, 1, 2, 3}, {GTK_FILL, 0});
523 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
526 auto label = ui::Label(reinterpret_cast<const char *>( glGetString(GL_VENDOR)));
528 table.attach(label, {1, 2, 0, 1}, {GTK_FILL, 0});
529 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
532 auto label = ui::Label(reinterpret_cast<const char *>( glGetString(GL_VERSION)));
534 table.attach(label, {1, 2, 1, 2}, {GTK_FILL, 0});
535 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
538 auto label = ui::Label(reinterpret_cast<const char *>( glGetString(GL_RENDERER)));
540 table.attach(label, {1, 2, 2, 3}, {GTK_FILL, 0});
541 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
545 auto frame = create_dialog_frame("OpenGL Extensions");
546 vbox.pack_start(frame, TRUE, TRUE, 0);
548 auto sc_extensions = create_scrolled_window(ui::Policy::AUTOMATIC, ui::Policy::ALWAYS, 4);
549 frame.add(sc_extensions);
551 auto text_extensions = ui::TextView(ui::New);
552 gtk_text_view_set_editable(text_extensions, FALSE);
553 sc_extensions.add(text_extensions);
554 text_extensions.text(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)));
555 gtk_text_view_set_wrap_mode(text_extensions, GTK_WRAP_WORD);
556 text_extensions.show();
563 modal_dialog_show(window, dialog);
568 // =============================================================================
569 // TextureLayout dialog
571 // Last used texture scale values
572 static float last_used_texture_layout_scale_x = 4.0;
573 static float last_used_texture_layout_scale_y = 4.0;
575 EMessageBoxReturn DoTextureLayout(float *fx, float *fy)
578 ModalDialogButton ok_button(dialog, eIDOK);
579 ModalDialogButton cancel_button(dialog, eIDCANCEL);
580 ui::Entry x{ui::null};
581 ui::Entry y{ui::null};
583 auto window = MainFrame_getWindow().create_modal_dialog_window("Patch texture layout", dialog);
585 auto accel = ui::AccelGroup(ui::New);
586 window.add_accel_group(accel);
589 auto hbox = create_dialog_hbox(4, 4);
592 auto vbox = create_dialog_vbox(4);
593 hbox.pack_start(vbox, TRUE, TRUE, 0);
595 auto label = ui::Label("Texture will be fit across the patch based\n"
596 "on the x and y values given. Values of 1x1\n"
597 "will \"fit\" the texture. 2x2 will repeat\n"
600 vbox.pack_start(label, TRUE, TRUE, 0);
601 gtk_label_set_justify(label, GTK_JUSTIFY_LEFT);
604 auto table = create_dialog_table(2, 2, 4, 4);
606 vbox.pack_start(table, TRUE, TRUE, 0);
608 auto label = ui::Label("Texture x:");
610 table.attach(label, {0, 1, 0, 1}, {GTK_FILL, 0});
611 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
614 auto label = ui::Label("Texture y:");
616 table.attach(label, {0, 1, 1, 2}, {GTK_FILL, 0});
617 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
620 auto entry = ui::Entry(ui::New);
622 table.attach(entry, {1, 2, 0, 1}, {GTK_EXPAND | GTK_FILL, 0});
627 auto entry = ui::Entry(ui::New);
629 table.attach(entry, {1, 2, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
636 auto vbox = create_dialog_vbox(4);
637 hbox.pack_start(vbox, FALSE, FALSE, 0);
639 auto button = create_modal_dialog_button("OK", ok_button);
640 vbox.pack_start(button, FALSE, FALSE, 0);
641 widget_make_default(button);
642 gtk_widget_add_accelerator(button, "clicked", accel, GDK_KEY_Return, (GdkModifierType) 0,
646 auto button = create_modal_dialog_button("Cancel", cancel_button);
647 vbox.pack_start(button, FALSE, FALSE, 0);
648 gtk_widget_add_accelerator(button, "clicked", accel, GDK_KEY_Escape, (GdkModifierType) 0,
654 // Initialize with last used values
657 sprintf(buf, "%f", last_used_texture_layout_scale_x);
660 sprintf(buf, "%f", last_used_texture_layout_scale_y);
663 // Set focus after intializing the values
664 gtk_widget_grab_focus(x);
666 EMessageBoxReturn ret = modal_dialog_show(window, dialog);
668 *fx = static_cast<float>( atof(gtk_entry_get_text(x)));
669 *fy = static_cast<float>( atof(gtk_entry_get_text(y)));
671 // Remember last used values
672 last_used_texture_layout_scale_x = *fx;
673 last_used_texture_layout_scale_y = *fy;
681 // =============================================================================
682 // Text Editor dialog
684 // master window widget
685 static ui::Window text_editor{ui::null};
686 static ui::Widget text_widget{ui::null}; // slave, text widget from the gtk editor
688 static gint editor_delete(ui::Widget widget, gpointer data)
690 if (ui::alert(widget.window(), "Close the shader editor ?", "Radiant", ui::alert_type::YESNO,
691 ui::alert_icon::Question) == ui::alert_response::NO) {
700 static void editor_save(ui::Widget widget, gpointer data)
702 FILE *f = fopen((char *) g_object_get_data(G_OBJECT(data), "filename"), "w");
703 gpointer text = g_object_get_data(G_OBJECT(data), "text");
706 ui::alert(ui::Widget::from(data).window(), "Error saving file !");
710 char *str = gtk_editable_get_chars(GTK_EDITABLE(text), 0, -1);
711 fwrite(str, 1, strlen(str), f);
715 static void editor_close(ui::Widget widget, gpointer data)
717 if (ui::alert(text_editor.window(), "Close the shader editor ?", "Radiant", ui::alert_type::YESNO,
718 ui::alert_icon::Question) == ui::alert_response::NO) {
725 static void CreateGtkTextEditor()
727 auto dlg = ui::Window(ui::window_type::TOP);
729 dlg.connect("delete_event",
730 G_CALLBACK(editor_delete), 0);
731 gtk_window_set_default_size(dlg, 600, 300);
733 auto vbox = ui::VBox(FALSE, 5);
736 gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
738 auto scr = ui::ScrolledWindow(ui::New);
740 vbox.pack_start(scr, TRUE, TRUE, 0);
741 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scr), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
742 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scr), GTK_SHADOW_IN);
744 auto text = ui::TextView(ui::New);
747 g_object_set_data(G_OBJECT(dlg), "text", (gpointer) text);
748 gtk_text_view_set_editable(text, TRUE);
750 auto hbox = ui::HBox(FALSE, 5);
752 vbox.pack_start(hbox, FALSE, TRUE, 0);
754 auto button = ui::Button("Close");
756 hbox.pack_end(button, FALSE, FALSE, 0);
757 button.connect("clicked",
758 G_CALLBACK(editor_close), dlg);
759 button.dimensions(60, -1);
761 button = ui::Button("Save");
763 hbox.pack_end(button, FALSE, FALSE, 0);
764 button.connect("clicked",
765 G_CALLBACK(editor_save), dlg);
766 button.dimensions(60, -1);
772 static void DoGtkTextEditor(const char *filename, guint cursorpos)
775 CreateGtkTextEditor(); // build it the first time we need it
779 FILE *f = fopen(filename, "r");
782 globalOutputStream() << "Unable to load file " << filename << " in shader editor.\n";
785 fseek(f, 0, SEEK_END);
787 void *buf = malloc(len);
791 fread(buf, 1, len, f);
793 gtk_window_set_title(text_editor, filename);
795 auto text_buffer = gtk_text_view_get_buffer(ui::TextView::from(text_widget));
796 gtk_text_buffer_set_text(text_buffer, (char *) buf, len);
798 old_filename = g_object_get_data(G_OBJECT(text_editor), "filename");
802 g_object_set_data(G_OBJECT(text_editor), "filename", strdup(filename));
804 // trying to show later
811 // only move the cursor if it's not exceeding the size..
812 // NOTE: this is erroneous, cursorpos is the offset in bytes, not in characters
813 // len is the max size in bytes, not in characters either, but the character count is below that limit..
814 // thinking .. the difference between character count and byte count would be only because of CR/LF?
816 GtkTextIter text_iter;
817 // character offset, not byte offset
818 gtk_text_buffer_get_iter_at_offset(text_buffer, &text_iter, cursorpos);
819 gtk_text_buffer_place_cursor(text_buffer, &text_iter);
823 gtk_widget_queue_draw( text_widget );
831 // =============================================================================
832 // Light Intensity dialog
834 EMessageBoxReturn DoLightIntensityDlg(int *intensity)
837 ui::Entry intensity_entry{ui::null};
838 ModalDialogButton ok_button(dialog, eIDOK);
839 ModalDialogButton cancel_button(dialog, eIDCANCEL);
841 ui::Window window = MainFrame_getWindow().create_modal_dialog_window("Light intensity", dialog, -1, -1);
843 auto accel_group = ui::AccelGroup(ui::New);
844 window.add_accel_group(accel_group);
847 auto hbox = create_dialog_hbox(4, 4);
850 auto vbox = create_dialog_vbox(4);
851 hbox.pack_start(vbox, TRUE, TRUE, 0);
853 auto label = ui::Label("ESC for default, ENTER to validate");
855 vbox.pack_start(label, FALSE, FALSE, 0);
858 auto entry = ui::Entry(ui::New);
860 vbox.pack_start(entry, TRUE, TRUE, 0);
862 gtk_widget_grab_focus(entry);
864 intensity_entry = entry;
868 auto vbox = create_dialog_vbox(4);
869 hbox.pack_start(vbox, FALSE, FALSE, 0);
872 auto button = create_modal_dialog_button("OK", ok_button);
873 vbox.pack_start(button, FALSE, FALSE, 0);
874 widget_make_default(button);
875 gtk_widget_add_accelerator(button, "clicked", accel_group, GDK_KEY_Return, (GdkModifierType) 0,
879 auto button = create_modal_dialog_button("Cancel", cancel_button);
880 vbox.pack_start(button, FALSE, FALSE, 0);
881 gtk_widget_add_accelerator(button, "clicked", accel_group, GDK_KEY_Escape, (GdkModifierType) 0,
888 sprintf(buf, "%d", *intensity);
889 intensity_entry.text(buf);
891 EMessageBoxReturn ret = modal_dialog_show(window, dialog);
893 *intensity = atoi(gtk_entry_get_text(intensity_entry));
901 // =============================================================================
902 // Add new shader tag dialog
904 EMessageBoxReturn DoShaderTagDlg(CopiedString *tag, const char *title)
907 ModalDialogButton ok_button(dialog, eIDOK);
908 ModalDialogButton cancel_button(dialog, eIDCANCEL);
910 auto window = MainFrame_getWindow().create_modal_dialog_window(title, dialog, -1, -1);
912 auto accel_group = ui::AccelGroup(ui::New);
913 window.add_accel_group(accel_group);
915 auto textentry = ui::Entry(ui::New);
917 auto hbox = create_dialog_hbox(4, 4);
920 auto vbox = create_dialog_vbox(4);
921 hbox.pack_start(vbox, TRUE, TRUE, 0);
923 //GtkLabel* label = GTK_LABEL(gtk_label_new("Enter one ore more tags separated by spaces"));
924 auto label = ui::Label("ESC to cancel, ENTER to validate");
926 vbox.pack_start(label, FALSE, FALSE, 0);
929 auto entry = textentry;
931 vbox.pack_start(entry, TRUE, TRUE, 0);
933 gtk_widget_grab_focus(entry);
937 auto vbox = create_dialog_vbox(4);
938 hbox.pack_start(vbox, FALSE, FALSE, 0);
941 auto button = create_modal_dialog_button("OK", ok_button);
942 vbox.pack_start(button, FALSE, FALSE, 0);
943 widget_make_default(button);
944 gtk_widget_add_accelerator(button, "clicked", accel_group, GDK_KEY_Return, (GdkModifierType) 0,
948 auto button = create_modal_dialog_button("Cancel", cancel_button);
949 vbox.pack_start(button, FALSE, FALSE, 0);
950 gtk_widget_add_accelerator(button, "clicked", accel_group, GDK_KEY_Escape, (GdkModifierType) 0,
956 EMessageBoxReturn ret = modal_dialog_show(window, dialog);
958 *tag = gtk_entry_get_text(textentry);
966 EMessageBoxReturn DoShaderInfoDlg(const char *name, const char *filename, const char *title)
969 ModalDialogButton ok_button(dialog, eIDOK);
971 auto window = MainFrame_getWindow().create_modal_dialog_window(title, dialog, -1, -1);
973 auto accel_group = ui::AccelGroup(ui::New);
974 window.add_accel_group(accel_group);
977 auto hbox = create_dialog_hbox(4, 4);
980 auto vbox = create_dialog_vbox(4);
981 hbox.pack_start(vbox, FALSE, FALSE, 0);
983 auto label = ui::Label("The selected shader");
985 vbox.pack_start(label, FALSE, FALSE, 0);
988 auto label = ui::Label(name);
990 vbox.pack_start(label, FALSE, FALSE, 0);
993 auto label = ui::Label("is located in file");
995 vbox.pack_start(label, FALSE, FALSE, 0);
998 auto label = ui::Label(filename);
1000 vbox.pack_start(label, FALSE, FALSE, 0);
1003 auto button = create_modal_dialog_button("OK", ok_button);
1004 vbox.pack_start(button, FALSE, FALSE, 0);
1005 widget_make_default(button);
1006 gtk_widget_add_accelerator(button, "clicked", accel_group, GDK_KEY_Return, (GdkModifierType) 0,
1012 EMessageBoxReturn ret = modal_dialog_show(window, dialog);
1021 #include <gdk/gdkwin32.h>
1025 // use the file associations to open files instead of builtin Gtk editor
1026 bool g_TextEditor_useWin32Editor = true;
1028 // custom shader editor
1029 bool g_TextEditor_useCustomEditor = false;
1030 CopiedString g_TextEditor_editorCommand("");
1033 void DoTextEditor(const char *filename, int cursorpos)
1036 if ( g_TextEditor_useWin32Editor ) {
1037 globalOutputStream() << "opening file '" << filename << "' (line " << cursorpos << " info ignored)\n";
1038 ShellExecute( (HWND)GDK_WINDOW_HWND( gtk_widget_get_window( MainFrame_getWindow() ) ), "open", filename, 0, 0, SW_SHOW );
1042 // check if a custom editor is set
1043 if (g_TextEditor_useCustomEditor && !g_TextEditor_editorCommand.empty()) {
1044 StringOutputStream strEditCommand(256);
1045 strEditCommand << g_TextEditor_editorCommand.c_str() << " \"" << filename << "\"";
1047 globalOutputStream() << "Launching: " << strEditCommand.c_str() << "\n";
1048 // note: linux does not return false if the command failed so it will assume success
1049 if (Q_Exec(0, const_cast<char *>( strEditCommand.c_str()), 0, true, false) == false) {
1050 globalOutputStream() << "Failed to execute " << strEditCommand.c_str() << ", using default\n";
1052 // the command (appeared) to run successfully, no need to do anything more
1058 DoGtkTextEditor(filename, cursorpos);