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>
55 #include "math/aabb.h"
56 #include "container/array.h"
57 #include "generic/static.h"
58 #include "stream/stringstream.h"
60 #include "gtkutil/messagebox.h"
61 #include "gtkutil/image.h"
64 #include "brushmanip.h"
67 #include "texwindow.h"
69 #include "mainframe.h"
70 #include "preferences.h"
76 // =============================================================================
77 // Project settings dialog
79 class GameComboConfiguration {
81 const char *basegame_dir;
83 const char *known_dir;
87 GameComboConfiguration() :
88 basegame_dir(g_pGameDescription->getRequiredKeyValue("basegame")),
89 basegame(g_pGameDescription->getRequiredKeyValue("basegamename")),
90 known_dir(g_pGameDescription->getKeyValue("knowngame")),
91 known(g_pGameDescription->getKeyValue("knowngamename")),
92 custom(g_pGameDescription->getRequiredKeyValue("unknowngamename"))
97 typedef LazyStatic<GameComboConfiguration> LazyStaticGameComboConfiguration;
99 inline GameComboConfiguration &globalGameComboConfiguration()
101 return LazyStaticGameComboConfiguration::instance();
106 gamecombo_t(int _game, const char *_fs_game, bool _sensitive)
107 : game(_game), fs_game(_fs_game), sensitive(_sensitive)
115 gamecombo_t gamecombo_for_dir(const char *dir)
117 if (string_equal(dir, globalGameComboConfiguration().basegame_dir)) {
118 return gamecombo_t(0, "", false);
119 } else if (string_equal(dir, globalGameComboConfiguration().known_dir)) {
120 return gamecombo_t(1, dir, false);
122 return gamecombo_t(string_empty(globalGameComboConfiguration().known_dir) ? 1 : 2, dir, true);
126 gamecombo_t gamecombo_for_gamename(const char *gamename)
128 if ((strlen(gamename) == 0) || !strcmp(gamename, globalGameComboConfiguration().basegame)) {
129 return gamecombo_t(0, "", false);
130 } else if (!strcmp(gamename, globalGameComboConfiguration().known)) {
131 return gamecombo_t(1, globalGameComboConfiguration().known_dir, false);
133 return gamecombo_t(string_empty(globalGameComboConfiguration().known_dir) ? 1 : 2, "", true);
137 inline void path_copy_clean(char *destination, const char *source)
139 char *i = destination;
141 while (*source != '\0') {
142 *i++ = (*source == '\\') ? '/' : *source;
146 if (i != destination && *(i - 1) != '/') {
155 ui::ComboBoxText game_select{ui::null};
156 ui::Entry fsgame_entry{ui::null};
159 gboolean OnSelchangeComboWhatgame(ui::Widget widget, GameCombo *combo)
161 const char *gamename;
164 gtk_combo_box_get_active_iter(combo->game_select, &iter);
165 gtk_tree_model_get(gtk_combo_box_get_model(combo->game_select), &iter, 0, (gpointer *) &gamename, -1);
168 gamecombo_t gamecombo = gamecombo_for_gamename(gamename);
170 combo->fsgame_entry.text(gamecombo.fs_game);
171 gtk_widget_set_sensitive(combo->fsgame_entry, gamecombo.sensitive);
178 bool do_mapping_mode;
179 const char *sp_mapping_mode;
180 const char *mp_mapping_mode;
183 do_mapping_mode(!string_empty(g_pGameDescription->getKeyValue("show_gamemode"))),
184 sp_mapping_mode("Single Player mapping mode"),
185 mp_mapping_mode("Multiplayer mapping mode")
190 typedef LazyStatic<MappingMode> LazyStaticMappingMode;
192 inline MappingMode &globalMappingMode()
194 return LazyStaticMappingMode::instance();
197 class ProjectSettingsDialog {
199 GameCombo game_combo;
200 ui::ComboBox gamemode_combo{ui::null};
203 ui::Window ProjectSettingsDialog_construct(ProjectSettingsDialog &dialog, ModalDialog &modal)
205 auto window = MainFrame_getWindow().create_dialog_window("Project Settings", G_CALLBACK(dialog_delete_callback),
209 auto table1 = create_dialog_table(1, 2, 4, 4, 4);
212 auto vbox = create_dialog_vbox(4);
213 table1.attach(vbox, {1, 2, 0, 1}, {GTK_FILL, GTK_FILL});
215 auto button = create_dialog_button("OK", G_CALLBACK(dialog_button_ok), &modal);
216 vbox.pack_start(button, FALSE, FALSE, 0);
219 auto button = create_dialog_button("Cancel", G_CALLBACK(dialog_button_cancel), &modal);
220 vbox.pack_start(button, FALSE, FALSE, 0);
224 auto frame = create_dialog_frame("Project settings");
225 table1.attach(frame, {0, 1, 0, 1}, {GTK_EXPAND | GTK_FILL, GTK_FILL});
227 auto table2 = create_dialog_table((globalMappingMode().do_mapping_mode) ? 4 : 3, 2, 4, 4, 4);
231 auto label = ui::Label("Select mod");
233 table2.attach(label, {0, 1, 0, 1}, {GTK_FILL, 0});
234 gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
237 dialog.game_combo.game_select = ui::ComboBoxText(ui::New);
239 gtk_combo_box_text_append_text(dialog.game_combo.game_select,
240 globalGameComboConfiguration().basegame);
241 if (globalGameComboConfiguration().known[0] != '\0') {
242 gtk_combo_box_text_append_text(dialog.game_combo.game_select,
243 globalGameComboConfiguration().known);
245 gtk_combo_box_text_append_text(dialog.game_combo.game_select,
246 globalGameComboConfiguration().custom);
248 dialog.game_combo.game_select.show();
249 table2.attach(dialog.game_combo.game_select, {1, 2, 0, 1}, {GTK_EXPAND | GTK_FILL, 0});
251 dialog.game_combo.game_select.connect("changed", G_CALLBACK(OnSelchangeComboWhatgame),
256 auto label = ui::Label("fs_game");
258 table2.attach(label, {0, 1, 1, 2}, {GTK_FILL, 0});
259 gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
262 auto entry = ui::Entry(ui::New);
264 table2.attach(entry, {1, 2, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
266 dialog.game_combo.fsgame_entry = entry;
269 if (globalMappingMode().do_mapping_mode) {
270 auto label = ui::Label("Mapping mode");
272 table2.attach(label, {0, 1, 3, 4}, {GTK_FILL, 0});
273 gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
275 auto combo = ui::ComboBoxText(ui::New);
276 gtk_combo_box_text_append_text(combo, globalMappingMode().sp_mapping_mode);
277 gtk_combo_box_text_append_text(combo, globalMappingMode().mp_mapping_mode);
280 table2.attach(combo, {1, 2, 3, 4}, {GTK_EXPAND | GTK_FILL, 0});
282 dialog.gamemode_combo = combo;
288 // initialise the fs_game selection from the project settings into the dialog
289 const char *dir = gamename_get();
290 gamecombo_t gamecombo = gamecombo_for_dir(dir);
292 gtk_combo_box_set_active(dialog.game_combo.game_select, gamecombo.game);
293 dialog.game_combo.fsgame_entry.text(gamecombo.fs_game);
294 gtk_widget_set_sensitive(dialog.game_combo.fsgame_entry, gamecombo.sensitive);
296 if (globalMappingMode().do_mapping_mode) {
297 const char *gamemode = gamemode_get();
298 if (string_empty(gamemode) || string_equal(gamemode, "sp")) {
299 gtk_combo_box_set_active(dialog.gamemode_combo, 0);
301 gtk_combo_box_set_active(dialog.gamemode_combo, 1);
308 void ProjectSettingsDialog_ok(ProjectSettingsDialog &dialog)
310 const char *dir = gtk_entry_get_text(dialog.game_combo.fsgame_entry);
312 const char *new_gamename = path_equal(dir, globalGameComboConfiguration().basegame_dir)
316 if (!path_equal(new_gamename, gamename_get())) {
317 ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Changing Game Name");
319 EnginePath_Unrealise();
321 gamename_set(new_gamename);
323 EnginePath_Realise();
326 if (globalMappingMode().do_mapping_mode) {
327 // read from gamemode_combo
328 int active = gtk_combo_box_get_active(dialog.gamemode_combo);
329 if (active == -1 || active == 0) {
337 void DoProjectSettings()
339 if (ConfirmModified("Edit Project Settings")) {
341 ProjectSettingsDialog dialog;
343 ui::Window window = ProjectSettingsDialog_construct(dialog, modal);
345 if (modal_dialog_show(window, modal) == eIDOK) {
346 ProjectSettingsDialog_ok(dialog);
353 // =============================================================================
354 // Arbitrary Sides dialog
356 void DoSides(int type, int axis)
360 auto window = MainFrame_getWindow().create_dialog_window("Arbitrary sides", G_CALLBACK(dialog_delete_callback),
363 auto accel = ui::AccelGroup(ui::New);
364 window.add_accel_group(accel);
366 auto sides_entry = ui::Entry(ui::New);
368 auto hbox = create_dialog_hbox(4, 4);
371 auto label = ui::Label("Sides:");
373 hbox.pack_start(label, FALSE, FALSE, 0);
376 auto entry = sides_entry;
378 hbox.pack_start(entry, FALSE, FALSE, 0);
379 gtk_widget_grab_focus(entry);
382 auto vbox = create_dialog_vbox(4);
383 hbox.pack_start(vbox, TRUE, TRUE, 0);
385 auto button = create_dialog_button("OK", G_CALLBACK(dialog_button_ok), &dialog);
386 vbox.pack_start(button, FALSE, FALSE, 0);
387 widget_make_default(button);
388 gtk_widget_add_accelerator(button, "clicked", accel, GDK_KEY_Return, (GdkModifierType) 0,
392 auto button = create_dialog_button("Cancel", G_CALLBACK(dialog_button_cancel), &dialog);
393 vbox.pack_start(button, FALSE, FALSE, 0);
394 gtk_widget_add_accelerator(button, "clicked", accel, GDK_KEY_Escape, (GdkModifierType) 0,
400 if (modal_dialog_show(window, dialog) == eIDOK) {
401 const char *str = gtk_entry_get_text(sides_entry);
403 Scene_BrushConstructPrefab(GlobalSceneGraph(), (EBrushPrefab) type, atoi(str),
404 TextureBrowser_GetSelectedShader(GlobalTextureBrowser()));
410 // =============================================================================
411 // About dialog (no program is complete without one)
413 void about_button_changelog(ui::Widget widget, gpointer data)
415 StringOutputStream log(256);
416 log << "https://gitlab.com/xonotic/netradiant/commits/master";
417 OpenURL(log.c_str());
420 void about_button_credits(ui::Widget widget, gpointer data)
422 StringOutputStream cred(256);
423 cred << "https://gitlab.com/xonotic/netradiant/graphs/master";
424 OpenURL(cred.c_str());
427 void about_button_issues(ui::Widget widget, gpointer data)
429 StringOutputStream cred(256);
430 cred << "https://gitlab.com/xonotic/netradiant/issues";
431 OpenURL(cred.c_str());
437 ModalDialogButton ok_button(dialog, eIDOK);
439 auto window = MainFrame_getWindow().create_modal_dialog_window("About NetRadiant", dialog);
442 auto vbox = create_dialog_vbox(4, 4);
446 auto hbox = create_dialog_hbox(4);
447 vbox.pack_start(hbox, FALSE, TRUE, 0);
450 auto vbox2 = create_dialog_vbox(4);
451 hbox.pack_start(vbox2, TRUE, FALSE, 0);
453 auto frame = create_dialog_frame(0, ui::Shadow::IN);
454 vbox2.pack_start(frame, FALSE, FALSE, 0);
456 auto image = new_local_image("logo.png");
464 char const *label_text = "NetRadiant " RADIANT_VERSION "\n"
466 RADIANT_ABOUTMSG "\n\n"
467 "This program is free software\n"
468 "licensed under the GNU GPL.\n\n"
469 "NetRadiant is unsupported, however\n"
470 "you may report your problems at\n"
471 "https://gitlab.com/xonotic/netradiant/issues";
473 auto label = ui::Label(label_text);
476 hbox.pack_start(label, FALSE, FALSE, 0);
477 gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
478 gtk_label_set_justify(label, GTK_JUSTIFY_LEFT);
482 auto vbox2 = create_dialog_vbox(4);
483 hbox.pack_start(vbox2, FALSE, TRUE, 0);
485 auto button = create_modal_dialog_button("OK", ok_button);
486 vbox2.pack_start(button, FALSE, FALSE, 0);
489 auto button = create_dialog_button("Credits", G_CALLBACK(about_button_credits), 0);
490 vbox2.pack_start(button, FALSE, FALSE, 0);
493 auto button = create_dialog_button("Changes", G_CALLBACK(about_button_changelog), 0);
494 vbox2.pack_start(button, FALSE, FALSE, 0);
497 auto button = create_dialog_button("Issues", G_CALLBACK(about_button_issues), 0);
498 vbox2.pack_start(button, FALSE, FALSE, 0);
503 auto frame = create_dialog_frame("OpenGL Properties");
504 vbox.pack_start(frame, FALSE, FALSE, 0);
506 auto table = create_dialog_table(3, 2, 4, 4, 4);
509 auto label = ui::Label("Vendor:");
511 table.attach(label, {0, 1, 0, 1}, {GTK_FILL, 0});
512 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
515 auto label = ui::Label("Version:");
517 table.attach(label, {0, 1, 1, 2}, {GTK_FILL, 0});
518 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
521 auto label = ui::Label("Renderer:");
523 table.attach(label, {0, 1, 2, 3}, {GTK_FILL, 0});
524 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
527 auto label = ui::Label(reinterpret_cast<const char *>( glGetString(GL_VENDOR)));
529 table.attach(label, {1, 2, 0, 1}, {GTK_FILL, 0});
530 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
533 auto label = ui::Label(reinterpret_cast<const char *>( glGetString(GL_VERSION)));
535 table.attach(label, {1, 2, 1, 2}, {GTK_FILL, 0});
536 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
539 auto label = ui::Label(reinterpret_cast<const char *>( glGetString(GL_RENDERER)));
541 table.attach(label, {1, 2, 2, 3}, {GTK_FILL, 0});
542 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
546 auto frame = create_dialog_frame("OpenGL Extensions");
547 vbox.pack_start(frame, TRUE, TRUE, 0);
549 auto sc_extensions = create_scrolled_window(ui::Policy::AUTOMATIC, ui::Policy::ALWAYS, 4);
550 frame.add(sc_extensions);
552 auto text_extensions = ui::TextView(ui::New);
553 gtk_text_view_set_editable(text_extensions, FALSE);
554 sc_extensions.add(text_extensions);
555 text_extensions.text(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)));
556 gtk_text_view_set_wrap_mode(text_extensions, GTK_WRAP_WORD);
557 text_extensions.show();
564 modal_dialog_show(window, dialog);
569 // =============================================================================
570 // TextureLayout dialog
572 // Last used texture scale values
573 static float last_used_texture_layout_scale_x = 4.0;
574 static float last_used_texture_layout_scale_y = 4.0;
576 EMessageBoxReturn DoTextureLayout(float *fx, float *fy)
579 ModalDialogButton ok_button(dialog, eIDOK);
580 ModalDialogButton cancel_button(dialog, eIDCANCEL);
581 ui::Entry x{ui::null};
582 ui::Entry y{ui::null};
584 auto window = MainFrame_getWindow().create_modal_dialog_window("Patch texture layout", dialog);
586 auto accel = ui::AccelGroup(ui::New);
587 window.add_accel_group(accel);
590 auto hbox = create_dialog_hbox(4, 4);
593 auto vbox = create_dialog_vbox(4);
594 hbox.pack_start(vbox, TRUE, TRUE, 0);
596 auto label = ui::Label("Texture will be fit across the patch based\n"
597 "on the x and y values given. Values of 1x1\n"
598 "will \"fit\" the texture. 2x2 will repeat\n"
601 vbox.pack_start(label, TRUE, TRUE, 0);
602 gtk_label_set_justify(label, GTK_JUSTIFY_LEFT);
605 auto table = create_dialog_table(2, 2, 4, 4);
607 vbox.pack_start(table, TRUE, TRUE, 0);
609 auto label = ui::Label("Texture x:");
611 table.attach(label, {0, 1, 0, 1}, {GTK_FILL, 0});
612 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
615 auto label = ui::Label("Texture y:");
617 table.attach(label, {0, 1, 1, 2}, {GTK_FILL, 0});
618 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
621 auto entry = ui::Entry(ui::New);
623 table.attach(entry, {1, 2, 0, 1}, {GTK_EXPAND | GTK_FILL, 0});
628 auto entry = ui::Entry(ui::New);
630 table.attach(entry, {1, 2, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
637 auto vbox = create_dialog_vbox(4);
638 hbox.pack_start(vbox, FALSE, FALSE, 0);
640 auto button = create_modal_dialog_button("OK", ok_button);
641 vbox.pack_start(button, FALSE, FALSE, 0);
642 widget_make_default(button);
643 gtk_widget_add_accelerator(button, "clicked", accel, GDK_KEY_Return, (GdkModifierType) 0,
647 auto button = create_modal_dialog_button("Cancel", cancel_button);
648 vbox.pack_start(button, FALSE, FALSE, 0);
649 gtk_widget_add_accelerator(button, "clicked", accel, GDK_KEY_Escape, (GdkModifierType) 0,
655 // Initialize with last used values
658 sprintf(buf, "%f", last_used_texture_layout_scale_x);
661 sprintf(buf, "%f", last_used_texture_layout_scale_y);
664 // Set focus after intializing the values
665 gtk_widget_grab_focus(x);
667 EMessageBoxReturn ret = modal_dialog_show(window, dialog);
669 *fx = static_cast<float>( atof(gtk_entry_get_text(x)));
670 *fy = static_cast<float>( atof(gtk_entry_get_text(y)));
672 // Remember last used values
673 last_used_texture_layout_scale_x = *fx;
674 last_used_texture_layout_scale_y = *fy;
682 // =============================================================================
683 // Text Editor dialog
685 // master window widget
686 static ui::Window text_editor{ui::null};
687 static ui::Widget text_widget{ui::null}; // slave, text widget from the gtk editor
689 static gint editor_delete(ui::Widget widget, gpointer data)
691 if (ui::alert(widget.window(), "Close the shader editor ?", "Radiant", ui::alert_type::YESNO,
692 ui::alert_icon::Question) == ui::alert_response::NO) {
701 static void editor_save(ui::Widget widget, gpointer data)
703 FILE *f = fopen((char *) g_object_get_data(G_OBJECT(data), "filename"), "w");
704 gpointer text = g_object_get_data(G_OBJECT(data), "text");
707 ui::alert(ui::Widget::from(data).window(), "Error saving file !");
711 char *str = gtk_editable_get_chars(GTK_EDITABLE(text), 0, -1);
712 fwrite(str, 1, strlen(str), f);
716 static void editor_close(ui::Widget widget, gpointer data)
718 if (ui::alert(text_editor.window(), "Close the shader editor ?", "Radiant", ui::alert_type::YESNO,
719 ui::alert_icon::Question) == ui::alert_response::NO) {
726 static void CreateGtkTextEditor()
728 auto dlg = ui::Window(ui::window_type::TOP);
730 dlg.connect("delete_event",
731 G_CALLBACK(editor_delete), 0);
732 gtk_window_set_default_size(dlg, 600, 300);
734 auto vbox = ui::VBox(FALSE, 5);
737 gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
739 auto scr = ui::ScrolledWindow(ui::New);
741 vbox.pack_start(scr, TRUE, TRUE, 0);
742 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scr), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
743 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scr), GTK_SHADOW_IN);
745 auto text = ui::TextView(ui::New);
748 g_object_set_data(G_OBJECT(dlg), "text", (gpointer) text);
749 gtk_text_view_set_editable(text, TRUE);
751 auto hbox = ui::HBox(FALSE, 5);
753 vbox.pack_start(hbox, FALSE, TRUE, 0);
755 auto button = ui::Button("Close");
757 hbox.pack_end(button, FALSE, FALSE, 0);
758 button.connect("clicked",
759 G_CALLBACK(editor_close), dlg);
760 button.dimensions(60, -1);
762 button = ui::Button("Save");
764 hbox.pack_end(button, FALSE, FALSE, 0);
765 button.connect("clicked",
766 G_CALLBACK(editor_save), dlg);
767 button.dimensions(60, -1);
773 static void DoGtkTextEditor(const char *filename, guint cursorpos)
776 CreateGtkTextEditor(); // build it the first time we need it
780 FILE *f = fopen(filename, "r");
783 globalOutputStream() << "Unable to load file " << filename << " in shader editor.\n";
786 fseek(f, 0, SEEK_END);
788 void *buf = malloc(len);
792 assert(fread(buf, 1, len, f));
794 gtk_window_set_title(text_editor, filename);
796 auto text_buffer = gtk_text_view_get_buffer(ui::TextView::from(text_widget));
797 gtk_text_buffer_set_text(text_buffer, (char *) buf, len);
799 old_filename = g_object_get_data(G_OBJECT(text_editor), "filename");
803 g_object_set_data(G_OBJECT(text_editor), "filename", strdup(filename));
805 // trying to show later
812 // only move the cursor if it's not exceeding the size..
813 // NOTE: this is erroneous, cursorpos is the offset in bytes, not in characters
814 // len is the max size in bytes, not in characters either, but the character count is below that limit..
815 // thinking .. the difference between character count and byte count would be only because of CR/LF?
817 GtkTextIter text_iter;
818 // character offset, not byte offset
819 gtk_text_buffer_get_iter_at_offset(text_buffer, &text_iter, cursorpos);
820 gtk_text_buffer_place_cursor(text_buffer, &text_iter);
824 gtk_widget_queue_draw( text_widget );
832 // =============================================================================
833 // Light Intensity dialog
835 EMessageBoxReturn DoLightIntensityDlg(int *intensity)
838 ui::Entry intensity_entry{ui::null};
839 ModalDialogButton ok_button(dialog, eIDOK);
840 ModalDialogButton cancel_button(dialog, eIDCANCEL);
842 ui::Window window = MainFrame_getWindow().create_modal_dialog_window("Light intensity", dialog, -1, -1);
844 auto accel_group = ui::AccelGroup(ui::New);
845 window.add_accel_group(accel_group);
848 auto hbox = create_dialog_hbox(4, 4);
851 auto vbox = create_dialog_vbox(4);
852 hbox.pack_start(vbox, TRUE, TRUE, 0);
854 auto label = ui::Label("ESC for default, ENTER to validate");
856 vbox.pack_start(label, FALSE, FALSE, 0);
859 auto entry = ui::Entry(ui::New);
861 vbox.pack_start(entry, TRUE, TRUE, 0);
863 gtk_widget_grab_focus(entry);
865 intensity_entry = entry;
869 auto vbox = create_dialog_vbox(4);
870 hbox.pack_start(vbox, FALSE, FALSE, 0);
873 auto button = create_modal_dialog_button("OK", ok_button);
874 vbox.pack_start(button, FALSE, FALSE, 0);
875 widget_make_default(button);
876 gtk_widget_add_accelerator(button, "clicked", accel_group, GDK_KEY_Return, (GdkModifierType) 0,
880 auto button = create_modal_dialog_button("Cancel", cancel_button);
881 vbox.pack_start(button, FALSE, FALSE, 0);
882 gtk_widget_add_accelerator(button, "clicked", accel_group, GDK_KEY_Escape, (GdkModifierType) 0,
889 sprintf(buf, "%d", *intensity);
890 intensity_entry.text(buf);
892 EMessageBoxReturn ret = modal_dialog_show(window, dialog);
894 *intensity = atoi(gtk_entry_get_text(intensity_entry));
902 // =============================================================================
903 // Add new shader tag dialog
905 EMessageBoxReturn DoShaderTagDlg(CopiedString *tag, const char *title)
908 ModalDialogButton ok_button(dialog, eIDOK);
909 ModalDialogButton cancel_button(dialog, eIDCANCEL);
911 auto window = MainFrame_getWindow().create_modal_dialog_window(title, dialog, -1, -1);
913 auto accel_group = ui::AccelGroup(ui::New);
914 window.add_accel_group(accel_group);
916 auto textentry = ui::Entry(ui::New);
918 auto hbox = create_dialog_hbox(4, 4);
921 auto vbox = create_dialog_vbox(4);
922 hbox.pack_start(vbox, TRUE, TRUE, 0);
924 //GtkLabel* label = GTK_LABEL(gtk_label_new("Enter one ore more tags separated by spaces"));
925 auto label = ui::Label("ESC to cancel, ENTER to validate");
927 vbox.pack_start(label, FALSE, FALSE, 0);
930 auto entry = textentry;
932 vbox.pack_start(entry, TRUE, TRUE, 0);
934 gtk_widget_grab_focus(entry);
938 auto vbox = create_dialog_vbox(4);
939 hbox.pack_start(vbox, FALSE, FALSE, 0);
942 auto button = create_modal_dialog_button("OK", ok_button);
943 vbox.pack_start(button, FALSE, FALSE, 0);
944 widget_make_default(button);
945 gtk_widget_add_accelerator(button, "clicked", accel_group, GDK_KEY_Return, (GdkModifierType) 0,
949 auto button = create_modal_dialog_button("Cancel", cancel_button);
950 vbox.pack_start(button, FALSE, FALSE, 0);
951 gtk_widget_add_accelerator(button, "clicked", accel_group, GDK_KEY_Escape, (GdkModifierType) 0,
957 EMessageBoxReturn ret = modal_dialog_show(window, dialog);
959 *tag = gtk_entry_get_text(textentry);
967 EMessageBoxReturn DoShaderInfoDlg(const char *name, const char *filename, const char *title)
970 ModalDialogButton ok_button(dialog, eIDOK);
972 auto window = MainFrame_getWindow().create_modal_dialog_window(title, dialog, -1, -1);
974 auto accel_group = ui::AccelGroup(ui::New);
975 window.add_accel_group(accel_group);
978 auto hbox = create_dialog_hbox(4, 4);
981 auto vbox = create_dialog_vbox(4);
982 hbox.pack_start(vbox, FALSE, FALSE, 0);
984 auto label = ui::Label("The selected shader");
986 vbox.pack_start(label, FALSE, FALSE, 0);
989 auto label = ui::Label(name);
991 vbox.pack_start(label, FALSE, FALSE, 0);
994 auto label = ui::Label("is located in file");
996 vbox.pack_start(label, FALSE, FALSE, 0);
999 auto label = ui::Label(filename);
1001 vbox.pack_start(label, FALSE, FALSE, 0);
1004 auto button = create_modal_dialog_button("OK", ok_button);
1005 vbox.pack_start(button, FALSE, FALSE, 0);
1006 widget_make_default(button);
1007 gtk_widget_add_accelerator(button, "clicked", accel_group, GDK_KEY_Return, (GdkModifierType) 0,
1013 EMessageBoxReturn ret = modal_dialog_show(window, dialog);
1022 #include <gdk/gdkwin32.h>
1026 // use the file associations to open files instead of builtin Gtk editor
1027 bool g_TextEditor_useWin32Editor = true;
1029 // custom shader editor
1030 bool g_TextEditor_useCustomEditor = false;
1031 CopiedString g_TextEditor_editorCommand("");
1034 void DoTextEditor(const char *filename, int cursorpos)
1037 if ( g_TextEditor_useWin32Editor ) {
1038 globalOutputStream() << "opening file '" << filename << "' (line " << cursorpos << " info ignored)\n";
1039 ShellExecute( (HWND)GDK_WINDOW_HWND( gtk_widget_get_window( MainFrame_getWindow() ) ), "open", filename, 0, 0, SW_SHOW );
1043 // check if a custom editor is set
1044 if (g_TextEditor_useCustomEditor && !g_TextEditor_editorCommand.empty()) {
1045 StringOutputStream strEditCommand(256);
1046 strEditCommand << g_TextEditor_editorCommand.c_str() << " \"" << filename << "\"";
1048 globalOutputStream() << "Launching: " << strEditCommand.c_str() << "\n";
1049 // note: linux does not return false if the command failed so it will assume success
1050 if (Q_Exec(0, const_cast<char *>( strEditCommand.c_str()), 0, true, false) == false) {
1051 globalOutputStream() << "Failed to execute " << strEditCommand.c_str() << ", using default\n";
1053 // the command (appeared) to run successfully, no need to do anything more
1059 DoGtkTextEditor(filename, cursorpos);