1 //------------------------------------------------------------------------
2 // PREFERENCES DIALOG
3 //------------------------------------------------------------------------
4 //
5 // Eureka DOOM Editor
6 //
7 // Copyright (C) 2012-2019 Andrew Apted
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 //------------------------------------------------------------------------
20
21 #include "main.h"
22 #include "m_config.h"
23
24 #include <algorithm>
25
26 #include "ui_window.h"
27 #include "ui_prefs.h"
28
29 #include <FL/Fl_Color_Chooser.H>
30
31
32 #define PREF_WINDOW_W 600
33 #define PREF_WINDOW_H 520
34
35 #define PREF_WINDOW_TITLE "Eureka Preferences"
36
37
38 static int last_active_tab = 0;
39
40
41 class UI_EditKey : public UI_Escapable_Window
42 {
43 private:
44 bool want_close;
45 bool cancelled;
46
47 bool awaiting_key;
48
49 keycode_t key;
50
51 Fl_Input *key_name;
52 Fl_Button *grab_but;
53
54 Fl_Output *func_name;
55 Fl_Menu_Button *func_choose;
56
57 Fl_Choice *context;
58 Fl_Input *params;
59
60 const editor_command_t *cur_cmd;
61
62 Fl_Menu_Button *keyword_menu;
63 Fl_Menu_Button *flag_menu;
64
65 Fl_Button *cancel;
66 Fl_Button *ok_but;
67
68 private:
BeginGrab()69 void BeginGrab()
70 {
71 SYS_ASSERT(! awaiting_key);
72
73 awaiting_key = true;
74
75 key_name->color(FL_YELLOW, FL_YELLOW);
76 key_name->value("<\077\077\077>");
77 key_name->textcolor(FL_FOREGROUND_COLOR);
78 grab_but->deactivate();
79
80 Fl::focus(this);
81
82 redraw();
83 }
84
FinishGrab()85 void FinishGrab()
86 {
87 if (! awaiting_key)
88 return;
89
90 awaiting_key = false;
91
92 key_name->color(FL_BACKGROUND2_COLOR, FL_SELECTION_COLOR);
93 grab_but->activate();
94
95 redraw();
96 }
97
handle(int event)98 int handle(int event)
99 {
100 if (awaiting_key)
101 {
102 // escape key cancels
103 if (event == FL_KEYDOWN && Fl::event_key() == FL_Escape)
104 {
105 FinishGrab();
106
107 if (key)
108 key_name->value(M_KeyToString(key));
109
110 // if previous key was invalid, need to re-enable OK button
111 validate_callback(this, this);
112
113 return 1;
114 }
115
116 if (event == FL_KEYDOWN ||
117 event == FL_PUSH ||
118 event == FL_MOUSEWHEEL)
119 {
120 keycode_t new_key = M_CookedKeyForEvent(event);
121
122 if (new_key)
123 {
124 FinishGrab();
125
126 key = new_key;
127 key_name->value(M_KeyToString(key));
128
129 // if previous key was invalid, need to re-enable OK button
130 validate_callback(this, this);
131
132 return 1;
133 }
134 }
135 }
136
137 return UI_Escapable_Window::handle(event);
138 }
139
140 private:
141 struct name_CMP_pred
142 {
operator ()UI_EditKey::name_CMP_pred143 inline bool operator() (const char * A, const char * B) const
144 {
145 return (strcmp(A, B) < 0);
146 }
147 };
148
149 // need this because menu is in reverse order
ContextFromMenu()150 key_context_e ContextFromMenu()
151 {
152 int i = context->value();
153 SYS_ASSERT(i >= 0 && i <= 6);
154 return (key_context_e)((int)KCTX_General - i);
155 }
156
SetContext(key_context_e ctx)157 void SetContext(key_context_e ctx)
158 {
159 int i = (int)KCTX_General - (int)ctx;
160 SYS_ASSERT(i >= 0 && i <= 6);
161 context->value(i);
162 }
163
AddContextToMenu(const char * lab,key_context_e ctx,key_context_e limit_ctx)164 void AddContextToMenu(const char *lab, key_context_e ctx, key_context_e limit_ctx)
165 {
166 int flags = 0;
167
168 if (limit_ctx != KCTX_NONE && ctx != limit_ctx)
169 {
170 flags = FL_MENU_INACTIVE;
171 }
172
173 context->add(lab, 0, 0, 0, flags);
174 }
175
PopulateContextMenu(key_context_e want_ctx)176 void PopulateContextMenu(key_context_e want_ctx)
177 {
178 key_context_e limit_ctx = KCTX_NONE;
179
180 if (cur_cmd && cur_cmd->req_context != KCTX_NONE)
181 limit_ctx = cur_cmd->req_context;
182
183 context->clear();
184
185 AddContextToMenu("General (Any)", KCTX_General, limit_ctx);
186
187 AddContextToMenu("Linedefs", KCTX_Line, limit_ctx);
188 AddContextToMenu("Sectors", KCTX_Sector, limit_ctx);
189 AddContextToMenu("Things", KCTX_Thing, limit_ctx);
190 AddContextToMenu("Vertices", KCTX_Vertex, limit_ctx);
191 AddContextToMenu("3D View", KCTX_Render, limit_ctx);
192 AddContextToMenu("Browser", KCTX_Browser, limit_ctx);
193
194 if (want_ctx != KCTX_NONE)
195 SetContext(want_ctx);
196 }
197
PopulateFuncMenu(const char * find_name=NULL)198 void PopulateFuncMenu(const char *find_name = NULL)
199 {
200 func_choose->clear();
201
202 cur_cmd = NULL;
203
204 // add names to menu, and find the current function
205 char buffer[512];
206
207 bool did_separator = false;
208
209 for (int i = 0 ; ; i++)
210 {
211 const editor_command_t *cmd = LookupEditorCommand(i);
212
213 if (! cmd)
214 break;
215
216 if (! did_separator && y_stricmp(cmd->group_name, "General") == 0)
217 {
218 func_choose->add("", 0, 0, 0, FL_MENU_DIVIDER|FL_MENU_INACTIVE);
219 did_separator = true;
220 }
221
222 snprintf(buffer, sizeof(buffer), "%s/%s", cmd->group_name, cmd->name);
223
224 func_choose->add(buffer, 0, 0, (void *)(long)i, 0 /* flags */);
225
226 if (find_name && strcmp(cmd->name, find_name) == 0)
227 {
228 cur_cmd = cmd;
229 }
230 }
231
232 if (cur_cmd)
233 func_name->value(cur_cmd->name);
234 else
235 func_name->value("");
236 }
237
Decode(key_context_e ctx,const char * str)238 void Decode(key_context_e ctx, const char *str)
239 {
240 while (isspace(*str))
241 str++;
242
243 char func_buf[100];
244 unsigned int pos = 0;
245
246 while (*str && ! (isspace(*str) || *str == ':' || *str == '/') &&
247 pos + 4 < sizeof(func_buf))
248 {
249 func_buf[pos++] = *str++;
250 }
251
252 func_buf[pos] = 0;
253
254 // this sets the 'cur_cmd' variable
255 PopulateFuncMenu(func_buf);
256
257 PopulateMenuList(keyword_menu, cur_cmd ? cur_cmd->keyword_list : NULL);
258 PopulateMenuList( flag_menu, cur_cmd ? cur_cmd-> flag_list : NULL);
259
260 if (*str == ':')
261 str++;
262
263 while (isspace(*str))
264 str++;
265
266 params->value(str);
267 }
268
Encode()269 const char * Encode()
270 {
271 static char buffer[1024];
272
273 // should not happen
274 if (! cur_cmd)
275 return "ERROR";
276
277 strcpy(buffer, cur_cmd->name);
278
279 if (strlen(buffer) + strlen(params->value()) + 10 >= sizeof(buffer))
280 return "OVERFLOW";
281
282 strcat(buffer, ": ");
283 strcat(buffer, params->value());
284
285 return buffer;
286 }
287
PopulateMenuList(Fl_Menu_Button * menu,const char * list)288 void PopulateMenuList(Fl_Menu_Button *menu, const char *list)
289 {
290 menu->clear();
291
292 if (! list || ! list[0])
293 {
294 menu->deactivate();
295 return;
296 }
297
298 const char * tokens[32];
299
300 int num_tok = M_ParseLine(list, tokens, 32, 0 /* do_strings */);
301
302 if (num_tok < 1) // shouldn't happen
303 {
304 menu->deactivate();
305 return;
306 }
307
308 for (int i = 0 ; i < num_tok ; i++)
309 menu->add(tokens[i]);
310
311 M_FreeLine(tokens, num_tok);
312
313 menu->activate();
314 }
315
ValidateKey()316 bool ValidateKey()
317 {
318 keycode_t new_key = M_ParseKeyString(key_name->value());
319
320 if (new_key > 0)
321 {
322 key = new_key;
323 return true;
324 }
325
326 return false;
327 }
328
validate_callback(Fl_Widget * w,void * data)329 static void validate_callback(Fl_Widget *w, void *data)
330 {
331 UI_EditKey *dialog = (UI_EditKey *)data;
332
333 bool valid_key = dialog->ValidateKey();
334
335 dialog->key_name->textcolor(valid_key ? FL_FOREGROUND_COLOR : FL_RED);
336
337 // need to redraw the input box (otherwise get a mix of colors)
338 dialog->key_name->redraw();
339
340 if (valid_key)
341 dialog->ok_but->activate();
342 else
343 dialog->ok_but->deactivate();
344 }
345
grab_key_callback(Fl_Button * w,void * data)346 static void grab_key_callback(Fl_Button *w, void *data)
347 {
348 UI_EditKey *dialog = (UI_EditKey *)data;
349
350 dialog->BeginGrab();
351 }
352
close_callback(Fl_Widget * w,void * data)353 static void close_callback(Fl_Widget *w, void *data)
354 {
355 UI_EditKey *dialog = (UI_EditKey *)data;
356
357 dialog->want_close = true;
358 dialog->cancelled = true;
359 }
360
ok_callback(Fl_Button * w,void * data)361 static void ok_callback(Fl_Button *w, void *data)
362 {
363 UI_EditKey *dialog = (UI_EditKey *)data;
364
365 dialog->want_close = true;
366 }
367
SetNewFunction(int cmd_index)368 void SetNewFunction(int cmd_index)
369 {
370 const editor_command_t *old_cmd = cur_cmd;
371
372 cur_cmd = LookupEditorCommand(cmd_index);
373
374 if (! cur_cmd) // shouldn't happen
375 return;
376
377 func_name->value(cur_cmd->name);
378
379 key_context_e want_ctx = ContextFromMenu();
380
381 if (cur_cmd->req_context)
382 want_ctx = cur_cmd->req_context;
383 else if (y_strnicmp(cur_cmd->name, "BR_", 3) == 0)
384 want_ctx = KCTX_Browser;
385 else if (old_cmd && old_cmd->req_context)
386 want_ctx = KCTX_General;
387
388 PopulateContextMenu(want_ctx);
389
390 PopulateMenuList(keyword_menu, cur_cmd ? cur_cmd->keyword_list : NULL);
391 PopulateMenuList( flag_menu, cur_cmd ? cur_cmd-> flag_list : NULL);
392
393 redraw();
394 }
395
func_callback(Fl_Menu_Button * w,void * data)396 static void func_callback(Fl_Menu_Button *w, void *data)
397 {
398 UI_EditKey *dialog = (UI_EditKey *)data;
399
400 int cmd_index = (int)(long)w->mvalue()->user_data_;
401 SYS_ASSERT(cmd_index >= 0);
402
403 dialog->SetNewFunction(cmd_index);
404 }
405
ReplaceKeyword(const char * new_word)406 void ReplaceKeyword(const char *new_word)
407 {
408 // delete existing keyword, if any
409 if (isalnum(params->value()[0]))
410 {
411 const char *str = params->value();
412
413 int len = 0;
414
415 while (str[len] && (isalnum(str[len]) || str[len] == '_'))
416 len++;
417
418 while (str[len] && isspace(str[len]))
419 len++;
420
421 params->replace(0, len, NULL);
422 }
423
424 if (params->size() > 0)
425 params->replace(0, 0, " ");
426
427 params->replace(0, 0, new_word);
428 }
429
ReplaceFlag(const char * new_flag)430 void ReplaceFlag(const char *new_flag)
431 {
432 const char *str = params->value();
433
434 // if flag is already present, remove it
435 const char *pos = strstr(str, new_flag);
436
437 if (pos)
438 {
439 int a = (int)(pos - str);
440 int b = a + (int)strlen(new_flag);
441
442 while (str[b] && isspace(str[b]))
443 b++;
444
445 params->replace(a, b, NULL);
446
447 return;
448 }
449
450 // append the flag, adding a space if necessary
451 int a = params->size();
452
453 if (a > 0 && !isspace(str[a-1]))
454 {
455 params->replace(a, a, " ");
456 a += 1;
457 }
458
459 params->replace(a, a, new_flag);
460 }
461
keyword_callback(Fl_Menu_Button * w,void * data)462 static void keyword_callback(Fl_Menu_Button *w, void *data)
463 {
464 UI_EditKey *dialog = (UI_EditKey *)data;
465
466 dialog->ReplaceKeyword(w->text());
467 }
468
flag_callback(Fl_Menu_Button * w,void * data)469 static void flag_callback(Fl_Menu_Button *w, void *data)
470 {
471 UI_EditKey *dialog = (UI_EditKey *)data;
472
473 dialog->ReplaceFlag(w->text());
474 }
475
476 public:
UI_EditKey(keycode_t _key,key_context_e ctx,const char * _funcname)477 UI_EditKey(keycode_t _key, key_context_e ctx, const char *_funcname) :
478 UI_Escapable_Window(400, 306, "Edit Key Binding"),
479 want_close(false), cancelled(false),
480 awaiting_key(false),
481 key(_key)
482 {
483 // _key may be zero (when "Add" button is used)
484
485 if (ctx == KCTX_NONE)
486 ctx = KCTX_General;
487
488 callback(close_callback, this);
489
490 key_name = new Fl_Input(85, 25, 150, 25, "Key:");
491 key_name->when(FL_WHEN_CHANGED);
492 key_name->callback((Fl_Callback*)validate_callback, this);
493
494 if (key)
495 key_name->value(M_KeyToString(key));
496
497 grab_but = new Fl_Button(255, 25, 90, 25, "Re-bind");
498 grab_but->callback((Fl_Callback*)grab_key_callback, this);
499
500 func_name = new Fl_Output(85, 65, 150, 25, "Function:");
501
502 func_choose = new Fl_Menu_Button(255, 65, 90, 25, "Choose");
503 func_choose->callback((Fl_Callback*) func_callback, this);
504
505 context = new Fl_Choice(85, 105, 150, 25, "Mode:");
506
507 params = new Fl_Input(85, 145, 300, 25, "Params:");
508 params->value("");
509 params->when(FL_WHEN_CHANGED);
510
511 keyword_menu = new Fl_Menu_Button( 85, 180, 135, 25, "Keywords...");
512 keyword_menu->callback((Fl_Callback*) keyword_callback, this);
513
514 flag_menu = new Fl_Menu_Button(250, 180, 135, 25, "Flags...");
515 flag_menu->callback((Fl_Callback*) flag_callback, this);
516
517 { Fl_Group *o = new Fl_Group(0, 240, 400, 66);
518
519 o->box(FL_FLAT_BOX);
520 o->color(WINDOW_BG, WINDOW_BG);
521
522 cancel = new Fl_Button(170, 254, 80, 35, "Cancel");
523 cancel->callback((Fl_Callback*)close_callback, this);
524
525 ok_but = new Fl_Button(295, 254, 80, 35, "OK");
526 ok_but->labelfont(FL_BOLD);
527 ok_but->callback((Fl_Callback*)ok_callback, this);
528 ok_but->deactivate();
529
530 o->end();
531 }
532
533 end();
534
535 // parse line into function name and parameters
536 Decode(ctx, _funcname);
537
538 PopulateContextMenu(ctx == KCTX_NONE ? KCTX_General : ctx);
539 }
540
541
Run(keycode_t * key_v,key_context_e * ctx_v,const char ** func_v,bool start_grabbed)542 bool Run(keycode_t *key_v, key_context_e *ctx_v,
543 const char **func_v, bool start_grabbed)
544 {
545 *key_v = 0;
546 *ctx_v = KCTX_NONE;
547 *func_v = NULL;
548
549 // check the initial state
550 validate_callback(this, this);
551
552 set_modal();
553 show();
554
555 // need this for the 'start_grabbed' feature, get FLTK to
556 // actually put (map) the window onto the screen.
557 Fl::wait(0.1);
558 Fl::wait(0.1);
559
560 if (start_grabbed)
561 BeginGrab();
562 else
563 Fl::focus(params);
564
565 while (! want_close)
566 {
567 Fl::wait(0.2);
568 }
569
570 if (cancelled)
571 return false;
572
573 *key_v = key;
574 *ctx_v = ContextFromMenu();
575 *func_v = Encode();
576
577 return true;
578 }
579 };
580
581
582 //------------------------------------------------------------------------
583
584
585 class UI_Preferences : public Fl_Double_Window
586 {
587 private:
588 bool want_quit;
589 bool want_discard;
590
591 char key_sort_mode;
592 bool key_sort_rev;
593
594 // normally zero (not waiting for a key)
595 int awaiting_line;
596
597 static void close_callback(Fl_Widget *w, void *data);
598 static void color_callback(Fl_Button *w, void *data);
599
600 static void sort_key_callback(Fl_Button *w, void *data);
601 static void bind_key_callback(Fl_Button *w, void *data);
602 static void edit_key_callback(Fl_Button *w, void *data);
603 static void del_key_callback(Fl_Button *w, void *data);
604
605 static void reset_callback(Fl_Button *w, void *data);
606
607 public:
608 UI_Preferences();
609
610 void Run();
611
612 void LoadValues();
613 void SaveValues();
614
615 void LoadKeys();
616 void ReloadKeys();
617
618 int GridSizeToChoice(int size);
619
620 /* FLTK override */
621 int handle(int event);
622
623 void ClearWaiting();
624 void SetBinding(keycode_t key);
625
626 void EnsureKeyVisible(int line);
627
628 public:
629 Fl_Tabs *tabs;
630
631 Fl_Button *apply_but;
632 Fl_Button *discard_but;
633
634 /* General Tab */
635
636 Fl_Round_Button *theme_plastic;
637 Fl_Round_Button *theme_GTK;
638 Fl_Round_Button *theme_FLTK;
639
640 Fl_Round_Button *cols_bright;
641 Fl_Round_Button *cols_default;
642 Fl_Round_Button *cols_custom;
643
644 Fl_Button *bg_colorbox;
645 Fl_Button *ig_colorbox;
646 Fl_Button *fg_colorbox;
647
648 Fl_Check_Button *gen_autoload;
649 Fl_Check_Button *gen_maximized;
650 Fl_Check_Button *gen_swapsides;
651
652 /* Keys Tab */
653
654 Fl_Hold_Browser *key_list;
655
656 Fl_Button *key_group;
657 Fl_Button *key_key;
658 Fl_Button *key_func;
659
660 Fl_Button *key_add;
661 Fl_Button *key_copy;
662 Fl_Button *key_edit;
663 Fl_Button *key_delete;
664 Fl_Button *key_rebind;
665
666 /* Edit Tab */
667
668 Fl_Input *edit_def_port;
669 Fl_Choice *edit_def_mode;
670
671 Fl_Check_Button *edit_samemode;
672 Fl_Check_Button *edit_autoadjustX;
673 Fl_Check_Button *edit_add_del;
674 Fl_Check_Button *edit_full_1S;
675 Fl_Int_Input *edit_sectorsize;
676 Fl_Input *edit_userratio;
677 Fl_Choice *edit_lineinfo;
678
679 Fl_Check_Button *brow_smalltex;
680 Fl_Check_Button *brow_combo;
681
682 /* Grid Tab */
683
684 Fl_Check_Button *gen_scrollbars;
685
686 Fl_Choice *grid_cur_style;
687 Fl_Check_Button *grid_snap;
688 Fl_Check_Button *grid_enabled;
689 Fl_Choice *grid_size;
690
691 Fl_Check_Button *grid_hide_free;
692 Fl_Check_Button *grid_flatrender;
693 Fl_Check_Button *grid_spriterend;
694 Fl_Check_Button *grid_indicator;
695
696 Fl_Button *dotty_axis;
697 Fl_Button *dotty_major;
698 Fl_Button *dotty_minor;
699 Fl_Button *dotty_point;
700
701 Fl_Button *normal_axis;
702 Fl_Button *normal_main;
703 Fl_Button *normal_flat;
704 Fl_Button *normal_small;
705
706 /* 3D Tab */
707
708 Fl_Float_Input *rend_aspect;;
709
710 Fl_Check_Button *rend_high_detail;
711 Fl_Check_Button *rend_lock_grav;
712
713 Fl_Choice *rend_far_clip;
714
715 /* Nodes Tab */
716
717 Fl_Check_Button *nod_on_save;
718 Fl_Check_Button *nod_fast;
719 Fl_Check_Button *nod_warn;
720
721 Fl_Choice *nod_factor;
722
723 Fl_Check_Button *nod_gl_nodes;
724 Fl_Check_Button *nod_force_v5;
725 Fl_Check_Button *nod_force_zdoom;
726 Fl_Check_Button *nod_compress;
727
728 /* Other Tab */
729
730 Fl_Button * reset_conf;
731 Fl_Button * reset_keys;
732 };
733
734
735 #define R_SPACES " "
736
737
UI_Preferences()738 UI_Preferences::UI_Preferences() :
739 Fl_Double_Window(PREF_WINDOW_W, PREF_WINDOW_H, PREF_WINDOW_TITLE),
740 want_quit(false), want_discard(false),
741 key_sort_mode('k'), key_sort_rev(false),
742 awaiting_line(0)
743 {
744 if (gui_color_set == 2)
745 color(fl_gray_ramp(4));
746 else
747 color(WINDOW_BG);
748
749 callback(close_callback, this);
750
751 { tabs = new Fl_Tabs(0, 0, PREF_WINDOW_W-15, PREF_WINDOW_H-70);
752 // tabs->selection_color(FL_WHITE);
753
754 /* ---- General Tab ---- */
755
756 { Fl_Group* o = new Fl_Group(0, 25, 585, 405, " General" R_SPACES);
757 o->labelsize(16);
758 o->selection_color(FL_DARK2);
759 // o->hide();
760
761 { Fl_Box* o = new Fl_Box(25, 45, 145, 30, "GUI Appearance");
762 o->labelfont(FL_BOLD);
763 o->align(Fl_Align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE));
764 }
765 { Fl_Group* o = new Fl_Group(45, 90, 250, 115);
766 { theme_plastic = new Fl_Round_Button(50, 120, 165, 25, " Plastic theme ");
767 theme_plastic->type(102);
768 theme_plastic->down_box(FL_ROUND_DOWN_BOX);
769 }
770 { theme_GTK = new Fl_Round_Button(50, 90, 150, 25, " GTK+ theme ");
771 theme_GTK->type(102);
772 theme_GTK->down_box(FL_ROUND_DOWN_BOX);
773 }
774 { theme_FLTK = new Fl_Round_Button(50, 150, 150, 25, " FLTK theme");
775 theme_FLTK->type(102);
776 theme_FLTK->down_box(FL_ROUND_DOWN_BOX);
777 }
778 o->end();
779 }
780 { Fl_Group* o = new Fl_Group(220, 90, 190, 90);
781 { cols_bright = new Fl_Round_Button(245, 90, 140, 25, "bright colors");
782 cols_bright->type(102);
783 cols_bright->down_box(FL_ROUND_DOWN_BOX);
784 }
785 { cols_default = new Fl_Round_Button(245, 120, 135, 25, "default colors");
786 cols_default->type(102);
787 cols_default->down_box(FL_ROUND_DOWN_BOX);
788 }
789 { cols_custom = new Fl_Round_Button(245, 150, 165, 25, "custom colors ---->");
790 cols_custom->type(102);
791 cols_custom->down_box(FL_ROUND_DOWN_BOX);
792 }
793 o->end();
794 }
795 { Fl_Group* o = new Fl_Group(385, 80, 205, 100);
796 o->color(FL_LIGHT1);
797 o->align(Fl_Align(FL_ALIGN_BOTTOM_LEFT|FL_ALIGN_INSIDE));
798 { bg_colorbox = new Fl_Button(430, 90, 45, 25, "background");
799 bg_colorbox->box(FL_BORDER_BOX);
800 bg_colorbox->align(Fl_Align(FL_ALIGN_RIGHT));
801 bg_colorbox->callback((Fl_Callback*)color_callback, this);
802 }
803 { ig_colorbox = new Fl_Button(430, 120, 45, 25, "input bg");
804 ig_colorbox->box(FL_BORDER_BOX);
805 ig_colorbox->color(FL_BACKGROUND2_COLOR);
806 ig_colorbox->align(Fl_Align(FL_ALIGN_RIGHT));
807 ig_colorbox->callback((Fl_Callback*)color_callback, this);
808 }
809 { fg_colorbox = new Fl_Button(430, 150, 45, 25, "text color");
810 fg_colorbox->box(FL_BORDER_BOX);
811 fg_colorbox->color(FL_GRAY0);
812 fg_colorbox->align(Fl_Align(FL_ALIGN_RIGHT));
813 fg_colorbox->callback((Fl_Callback*)color_callback, this);
814 }
815 o->end();
816 }
817 { Fl_Box* o = new Fl_Box(30, 240, 280, 35, "Miscellaneous");
818 o->labelfont(FL_BOLD);
819 o->align(Fl_Align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE));
820 }
821 { gen_autoload = new Fl_Check_Button(50, 280, 380, 25, " automatically open the most recent pwad");
822 }
823 { gen_swapsides = new Fl_Check_Button(50, 310, 380, 25, " swap upper and lower sidedefs in Linedef panel");
824 }
825 { gen_maximized = new Fl_Check_Button(50, 340, 380, 25, " maximize the window when Eureka starts");
826 // not supported on MacOS X
827 // (on that platform we should restore last window position, but I don't
828 // know how to code that)
829 #ifdef __APPLE__
830 gen_maximized->hide();
831 #endif
832 }
833 o->end();
834 }
835
836 /* ---- Key bindings Tab ---- */
837
838 { Fl_Group* o = new Fl_Group(0, 25, 585, 410, " Keys" R_SPACES);
839 o->labelsize(16);
840 o->selection_color(FL_DARK2);
841 o->hide();
842
843 { Fl_Box* o = new Fl_Box(20, 45, 355, 30, "Key Bindings");
844 o->labelfont(FL_BOLD);
845 o->align(Fl_Align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE));
846 }
847
848 { key_key = new Fl_Button(25, 87, 125, 25, "KEY");
849 key_key->color((Fl_Color)231);
850 key_key->align(Fl_Align(FL_ALIGN_INSIDE));
851 key_key->callback((Fl_Callback*)sort_key_callback, this);
852 }
853 { key_group = new Fl_Button(155, 87, 75, 25, "MODE");
854 key_group->color((Fl_Color)231);
855 key_group->align(Fl_Align(FL_ALIGN_INSIDE));
856 key_group->callback((Fl_Callback*)sort_key_callback, this);
857 }
858 { key_func = new Fl_Button(235, 87, 205, 25, "FUNCTION");
859 key_func->color((Fl_Color)231);
860 key_func->align(Fl_Align(FL_ALIGN_INSIDE));
861 key_func->callback((Fl_Callback*)sort_key_callback, this);
862 }
863 { key_list = new Fl_Hold_Browser(20, 115, 442, 308);
864 key_list->textfont(FL_COURIER);
865 key_list->has_scrollbar(Fl_Browser_::VERTICAL);
866 }
867 { key_add = new Fl_Button(480, 155, 85, 30, "&Add");
868 key_add->callback((Fl_Callback*)edit_key_callback, this);
869 }
870 { key_copy = new Fl_Button(480, 195, 85, 30, "&Copy");
871 key_copy->callback((Fl_Callback*)edit_key_callback, this);
872 }
873 { key_edit = new Fl_Button(480, 235, 85, 30, "&Edit");
874 key_edit->callback((Fl_Callback*)edit_key_callback, this);
875 }
876 { key_delete = new Fl_Button(480, 275, 85, 30, "Delete");
877 key_delete->callback((Fl_Callback*)del_key_callback, this);
878 key_delete->shortcut(FL_Delete);
879 }
880 { key_rebind = new Fl_Button(480, 370, 85, 30, "&Re-bind");
881 key_rebind->callback((Fl_Callback*)bind_key_callback, this);
882 // key_rebind->shortcut(FL_Enter);
883 }
884 o->end();
885 }
886
887 /* ---- Editing Tab ---- */
888
889 { Fl_Group* o = new Fl_Group(0, 25, 585, 410, " Editing" R_SPACES);
890 o->labelsize(16);
891 o->selection_color(FL_DARK2);
892 o->hide();
893
894 { Fl_Box* o = new Fl_Box(25, 45, 355, 30, "Editing Options");
895 o->labelfont(FL_BOLD);
896 o->align(Fl_Align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE));
897 }
898 { edit_def_port = new Fl_Input(150, 85, 95, 25, "default port: ");
899 edit_def_port->align(FL_ALIGN_LEFT);
900 }
901 { edit_def_mode = new Fl_Choice(440, 85, 105, 25, "default edit mode: ");
902 edit_def_mode->align(FL_ALIGN_LEFT);
903 edit_def_mode->add("Things|Linedefs|Sectors|Vertices");
904 }
905 { edit_autoadjustX = new Fl_Check_Button(50, 150, 260, 30, " auto-adjust X offsets");
906 }
907 { edit_samemode = new Fl_Check_Button(50, 180, 270, 30, " same mode key will clear selection");
908 }
909 { edit_add_del = new Fl_Check_Button(50, 210, 270, 30, " enable sidedef ADD / DEL buttons");
910 }
911 { edit_full_1S = new Fl_Check_Button(50, 240, 270, 30, " show all textures on a one-sided linedef");
912 }
913 { edit_sectorsize = new Fl_Int_Input(440, 120, 105, 25, "new sector size:");
914 }
915 { edit_userratio = new Fl_Input(440, 150, 105, 25, "user ratio:");
916 }
917 { edit_lineinfo = new Fl_Choice(440, 180, 105, 25, "line info:");
918 edit_lineinfo->add("NONE|Length|Angle|Ratio|Len+Ang|Len+Ratio");
919 }
920
921 { Fl_Box* o = new Fl_Box(25, 295, 355, 30, "Browser Options");
922 o->labelfont(FL_BOLD);
923 o->align(Fl_Align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE));
924 }
925 { brow_smalltex = new Fl_Check_Button(50, 330, 265, 30, " smaller textures");
926 }
927 { brow_combo = new Fl_Check_Button(50, 360, 265, 30, " combine flats and textures in a single browser");
928 }
929 o->end();
930 }
931
932 /* ---- Grid Tab ---- */
933
934 { Fl_Group* o = new Fl_Group(0, 25, 585, 410, " Grid" R_SPACES);
935 o->labelsize(16);
936 o->selection_color(FL_DARK2);
937 o->hide();
938
939 { Fl_Box* o = new Fl_Box(25, 45, 355, 30, "Map Grid and Scrolling");
940 o->labelfont(FL_BOLD);
941 o->align(Fl_Align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE));
942 }
943 { grid_cur_style = new Fl_Choice(125, 90, 95, 25, "grid style ");
944 grid_cur_style->add("Squares|Dotty");
945 }
946 { grid_enabled = new Fl_Check_Button(50, 125, 95, 25, " default grid to ON");
947 }
948 { grid_snap = new Fl_Check_Button(50, 155, 235, 25, " default snap to ON");
949 }
950 { grid_flatrender = new Fl_Check_Button(50, 185, 270, 25, " default sector-render to ON");
951 }
952 { grid_spriterend = new Fl_Check_Button(50, 215, 270, 25, " default sprites to ON");
953 }
954 { grid_size = new Fl_Choice(420, 90, 95, 25, "default grid size ");
955 grid_size->add("1024|512|256|192|128|64|32|16|8|4|2");
956 }
957 { gen_scrollbars = new Fl_Check_Button(300, 125, 245, 25, " enable scroll-bars for map view");
958 }
959 { grid_indicator = new Fl_Check_Button(300, 155, 245, 25, " enable snap-pos indicator");
960 }
961 { grid_hide_free = new Fl_Check_Button(300, 185, 245, 25, " hide grid in FREE mode");
962 }
963
964 { Fl_Box* o = new Fl_Box(25, 270, 355, 30, "Grid Colors");
965 o->labelfont(FL_BOLD);
966 o->align(Fl_Align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE));
967 }
968
969 { normal_axis = new Fl_Button(150 + 0*55, 300, 45, 25, "Normal Grid : ");
970 normal_axis->box(FL_BORDER_BOX);
971 normal_axis->align(FL_ALIGN_LEFT);
972 normal_axis->callback((Fl_Callback*)color_callback, this);
973 normal_axis->tooltip("X/Y axis color");
974 }
975 { normal_main = new Fl_Button(150 + 1*55, 300, 45, 25, "");
976 normal_main->box(FL_BORDER_BOX);
977 normal_main->align(FL_ALIGN_RIGHT);
978 normal_main->callback((Fl_Callback*)color_callback, this);
979 normal_main->tooltip("large square color");
980 }
981 { normal_flat = new Fl_Button(150 + 2*55, 300, 45, 25, "");
982 normal_flat->box(FL_BORDER_BOX);
983 normal_flat->align(FL_ALIGN_RIGHT);
984 normal_flat->callback((Fl_Callback*)color_callback, this);
985 normal_flat->tooltip("64x64 square color");
986 }
987 { normal_small = new Fl_Button(150 + 3*55, 300, 45, 25, "");
988 normal_small->box(FL_BORDER_BOX);
989 normal_small->align(FL_ALIGN_RIGHT);
990 normal_small->callback((Fl_Callback*)color_callback, this);
991 normal_small->tooltip("small square color");
992 }
993
994 { dotty_axis = new Fl_Button(150 + 0*55, 340, 45, 25, "Dotty Grid : ");
995 dotty_axis->box(FL_BORDER_BOX);
996 dotty_axis->align(FL_ALIGN_LEFT);
997 dotty_axis->callback((Fl_Callback*)color_callback, this);
998 dotty_axis->tooltip("X/Y axis color");
999 }
1000 { dotty_major = new Fl_Button(150 + 1*55, 340, 45, 25, "");
1001 dotty_major->box(FL_BORDER_BOX);
1002 dotty_major->align(FL_ALIGN_RIGHT);
1003 dotty_major->callback((Fl_Callback*)color_callback, this);
1004 dotty_major->tooltip("large square color");
1005 }
1006 { dotty_minor = new Fl_Button(150 + 2*55, 340, 45, 25, "");
1007 dotty_minor->box(FL_BORDER_BOX);
1008 dotty_minor->align(FL_ALIGN_RIGHT);
1009 dotty_minor->callback((Fl_Callback*)color_callback, this);
1010 dotty_minor->tooltip("small square color");
1011 }
1012 { dotty_point = new Fl_Button(150 + 3*55, 340, 45, 25, "");
1013 dotty_point->box(FL_BORDER_BOX);
1014 dotty_point->align(FL_ALIGN_RIGHT);
1015 dotty_point->callback((Fl_Callback*)color_callback, this);
1016 dotty_point->tooltip("dot color");
1017 }
1018
1019 o->end();
1020 }
1021
1022 /* ---- 3D Tab ---- */
1023
1024 { Fl_Group* o = new Fl_Group(0, 25, 585, 410, " 3D View" R_SPACES);
1025 o->labelsize(16);
1026 o->selection_color(FL_DARK2);
1027 o->hide();
1028
1029 { Fl_Box* o = new Fl_Box(25, 45, 280, 30, "3D View Settings");
1030 o->labelfont(FL_BOLD);
1031 o->align(Fl_Align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE));
1032 }
1033 { rend_aspect = new Fl_Float_Input(190, 90, 95, 25, "Pixel aspect ratio: ");
1034
1035 Fl_Box* o = new Fl_Box(300, 90, 150, 25, "(higher is wider, default is 0.83)");
1036 o->align(Fl_Align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE));
1037 }
1038 { rend_lock_grav = new Fl_Check_Button(50, 125, 360, 30, " Locked gravity -- cannot move up or down");
1039 }
1040 { rend_high_detail = new Fl_Check_Button(50, 155, 360, 30, " High detail -- slower but looks better");
1041 #ifndef NO_OPENGL
1042 rend_high_detail->hide();
1043 #endif
1044 }
1045 { rend_far_clip = new Fl_Choice(195, 160, 100, 30, "Far clip distance: ");
1046 rend_far_clip->add("32768|16384|8192|4096|2048|1024");
1047 #ifdef NO_OPENGL
1048 rend_far_clip->hide();
1049 #endif
1050 }
1051
1052 o->end();
1053 }
1054
1055 /* ---- Nodes Tab ---- */
1056
1057 { Fl_Group* o = new Fl_Group(0, 25, 585, 410, " Nodes" R_SPACES);
1058 o->labelsize(16);
1059 o->selection_color(FL_DARK2);
1060 o->hide();
1061
1062 { Fl_Box* o = new Fl_Box(25, 45, 280, 30, "Node Building");
1063 o->labelfont(FL_BOLD);
1064 o->align(Fl_Align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE));
1065 }
1066 { nod_on_save = new Fl_Check_Button(50, 80, 220, 30, " Always build nodes after saving (recommended)");
1067 }
1068 { nod_fast = new Fl_Check_Button(50, 110, 440, 30, " Fast mode (the nodes may not be as good)");
1069 }
1070 { nod_warn = new Fl_Check_Button(50, 140, 220, 30, " Warning messages in the logs");
1071 }
1072
1073 { Fl_Box* o = new Fl_Box(25, 205, 250, 30, "Advanced BSP Settings");
1074 o->labelfont(FL_BOLD);
1075 o->align(Fl_Align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE));
1076 }
1077 { nod_gl_nodes = new Fl_Check_Button(50, 245, 150, 30, " Build GL-Nodes");
1078 }
1079 { nod_force_v5 = new Fl_Check_Button(50, 275, 250, 30, " Force V5 of GL-Nodes");
1080 }
1081 { nod_force_zdoom = new Fl_Check_Button(50, 305, 250, 30, " Force ZDoom format of normal nodes");
1082 }
1083 // CURRENTLY HIDDEN -- NOT SURE IT IS WORTH HAVING
1084 { nod_compress = new Fl_Check_Button(50, 335, 250, 30, " Force zlib compression");
1085 nod_compress->hide();
1086 }
1087 { nod_factor = new Fl_Choice(160, 345, 180, 30, "Seg split logic: ");
1088 nod_factor->add("NORMAL|Minimize Splits|Balance BSP Tree");
1089 }
1090 o->end();
1091 }
1092
1093 /* ---- Other Tab ---- */
1094
1095 { Fl_Group* o = new Fl_Group(0, 25, 585, 410, " Other" R_SPACES);
1096 o->labelsize(16);
1097 o->selection_color(FL_DARK2);
1098 o->hide();
1099
1100 { Fl_Box* o = new Fl_Box(25, 255, 280, 30, "Configuration Reset");
1101 o->labelfont(FL_BOLD);
1102 o->align(Fl_Align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE));
1103 }
1104 { reset_conf = new Fl_Button(100, 290, 200, 30, "Reset Config Settings");
1105 reset_conf->callback((Fl_Callback*)reset_callback, this);
1106 }
1107 { reset_keys = new Fl_Button(100, 330, 200, 30, "Reset Key Bindings");
1108 reset_keys->callback((Fl_Callback*)reset_callback, this);
1109 }
1110 o->end();
1111 }
1112 tabs->end();
1113 }
1114 { apply_but = new Fl_Button(PREF_WINDOW_W-150, PREF_WINDOW_H-50, 95, 35, "Apply");
1115 apply_but->labelfont(FL_BOLD);
1116 apply_but->callback(close_callback, this);
1117 }
1118 { discard_but = new Fl_Button(PREF_WINDOW_W-290, PREF_WINDOW_H-50, 95, 35, "Discard");
1119 discard_but->callback(close_callback, this);
1120 }
1121
1122 end();
1123 }
1124
1125
1126 //------------------------------------------------------------------------
1127
close_callback(Fl_Widget * w,void * data)1128 void UI_Preferences::close_callback(Fl_Widget *w, void *data)
1129 {
1130 UI_Preferences *prefs = (UI_Preferences *)data;
1131
1132 prefs->want_quit = true;
1133
1134 if (w == prefs->discard_but)
1135 prefs->want_discard = true;
1136 }
1137
1138
color_callback(Fl_Button * w,void * data)1139 void UI_Preferences::color_callback(Fl_Button *w, void *data)
1140 {
1141 // UI_Preferences *dialog = (UI_Preferences *)data;
1142
1143 uchar r, g, b;
1144
1145 Fl::get_color(w->color(), r, g, b);
1146
1147 if (! fl_color_chooser("New color:", r, g, b, 3))
1148 return;
1149
1150 w->color(fl_rgb_color(r, g, b));
1151
1152 w->redraw();
1153 }
1154
1155
bind_key_callback(Fl_Button * w,void * data)1156 void UI_Preferences::bind_key_callback(Fl_Button *w, void *data)
1157 {
1158 UI_Preferences *prefs = (UI_Preferences *)data;
1159
1160 int line = prefs->key_list->value();
1161 if (line < 1)
1162 {
1163 fl_beep();
1164 return;
1165 }
1166
1167 prefs->EnsureKeyVisible(line);
1168
1169
1170 int bind_idx = line - 1;
1171
1172 // show we're ready to accept a new key
1173
1174 const char *str = M_StringForBinding(bind_idx, true /* changing_key */);
1175 SYS_ASSERT(str);
1176
1177 prefs->key_list->text(line, str);
1178 prefs->key_list->selection_color(FL_YELLOW);
1179
1180 Fl::focus(prefs);
1181
1182 prefs->awaiting_line = line;
1183 }
1184
1185
sort_key_callback(Fl_Button * w,void * data)1186 void UI_Preferences::sort_key_callback(Fl_Button *w, void *data)
1187 {
1188 UI_Preferences *prefs = (UI_Preferences *)data;
1189
1190 if (w == prefs->key_group)
1191 {
1192 if (prefs->key_sort_mode != 'c')
1193 {
1194 prefs->key_sort_mode = 'c';
1195 prefs->key_sort_rev = false;
1196 }
1197 else
1198 prefs->key_sort_rev = !prefs->key_sort_rev;
1199 }
1200 else if (w == prefs->key_key)
1201 {
1202 if (prefs->key_sort_mode != 'k')
1203 {
1204 prefs->key_sort_mode = 'k';
1205 prefs->key_sort_rev = false;
1206 }
1207 else
1208 prefs->key_sort_rev = !prefs->key_sort_rev;
1209 }
1210 else if (w == prefs->key_func)
1211 {
1212 if (prefs->key_sort_mode != 'f')
1213 {
1214 prefs->key_sort_mode = 'f';
1215 prefs->key_sort_rev = false;
1216 }
1217 else
1218 prefs->key_sort_rev = !prefs->key_sort_rev;
1219 }
1220
1221 prefs->LoadKeys();
1222 }
1223
1224
edit_key_callback(Fl_Button * w,void * data)1225 void UI_Preferences::edit_key_callback(Fl_Button *w, void *data)
1226 {
1227 UI_Preferences *prefs = (UI_Preferences *)data;
1228
1229 bool is_add = (w == prefs->key_add);
1230 bool is_copy = (w == prefs->key_copy);
1231
1232
1233 keycode_t new_key = 0;
1234 key_context_e new_context = KCTX_General;
1235 const char * new_func = "Nothing";
1236
1237
1238 int bind_idx = -1;
1239
1240
1241 if (! is_add)
1242 {
1243 int line = prefs->key_list->value();
1244 if (line < 1)
1245 {
1246 fl_beep();
1247 return;
1248 }
1249
1250 prefs->EnsureKeyVisible(line);
1251
1252 bind_idx = line - 1;
1253 SYS_ASSERT(bind_idx >= 0);
1254
1255 M_GetBindingInfo(bind_idx, &new_key, &new_context);
1256 new_func = M_StringForFunc(bind_idx);
1257 }
1258
1259
1260 bool start_grabbed = false; //??? is_add || is_copy;
1261
1262 UI_EditKey *dialog = new UI_EditKey(new_key, new_context, new_func);
1263
1264 bool was_ok = dialog->Run(&new_key, &new_context, &new_func, start_grabbed);
1265
1266 if (was_ok)
1267 {
1268 // assume we can set it, since the dialog validated it
1269
1270 if (is_add || is_copy)
1271 {
1272 M_AddLocalBinding(bind_idx, new_key, new_context, new_func);
1273
1274 if (is_copy)
1275 bind_idx++;
1276 else
1277 bind_idx = M_NumBindings() - 1;
1278 }
1279 else
1280 {
1281 M_SetLocalBinding(bind_idx, new_key, new_context, new_func);
1282 }
1283 }
1284
1285 delete dialog;
1286
1287
1288 // for a new binding, make sure it is visible and selected
1289
1290 if ((is_add || is_copy) && was_ok && bind_idx >= 0)
1291 {
1292 // expand the browser size with a dummy line
1293 // [ the ReloadKeys() below will grab the correct text ]
1294 prefs->key_list->add("");
1295
1296 SYS_ASSERT(bind_idx >= 0);
1297 int line = 1 + bind_idx;
1298
1299 prefs->key_list->select(line);
1300 prefs->EnsureKeyVisible(line);
1301 }
1302
1303 prefs->ReloadKeys();
1304 prefs->redraw();
1305
1306 Fl::focus(prefs->key_list);
1307 }
1308
1309
del_key_callback(Fl_Button * w,void * data)1310 void UI_Preferences::del_key_callback(Fl_Button *w, void *data)
1311 {
1312 UI_Preferences *prefs = (UI_Preferences *)data;
1313
1314 int line = prefs->key_list->value();
1315 if (line < 1)
1316 {
1317 fl_beep();
1318 return;
1319 }
1320
1321 M_DeleteLocalBinding(line - 1);
1322
1323 prefs->key_list->remove(line);
1324 prefs->ReloadKeys();
1325
1326 if (line <= prefs->key_list->size())
1327 {
1328 prefs->key_list->select(line);
1329
1330 Fl::focus(prefs->key_list);
1331 }
1332 }
1333
1334
reset_callback(Fl_Button * w,void * data)1335 void UI_Preferences::reset_callback(Fl_Button *w, void *data)
1336 {
1337 UI_Preferences *prefs = (UI_Preferences *)data;
1338
1339 bool is_keys = (w == prefs->reset_keys);
1340
1341 int res = DLG_Confirm("Cancel|&Reset",
1342 "This will reset all %s to their default values, "
1343 "removing any changes you may have made."
1344 "\n\n"
1345 "If you continue, you can still back out by "
1346 "using the \"Discard\" button at the bottom of "
1347 "the Preferences window."
1348 "\n ",
1349 is_keys ? "key bindings" : "preferences");
1350
1351 if (res <= 0)
1352 return;
1353
1354 if (is_keys)
1355 {
1356 M_CopyBindings(true /* from_defaults */);
1357
1358 prefs->LoadKeys();
1359 }
1360 else
1361 {
1362 if (M_ParseDefaultConfigFile() != 0)
1363 {
1364 DLG_Notify("Installation problem: failed to find the \"defaults.cfg\" file!");
1365 }
1366 else
1367 {
1368 prefs->LoadValues();
1369 }
1370 }
1371 }
1372
1373
Run()1374 void UI_Preferences::Run()
1375 {
1376 if (last_active_tab < tabs->children())
1377 tabs->value(tabs->child(last_active_tab));
1378
1379 M_CopyBindings();
1380
1381 LoadValues();
1382 LoadKeys();
1383
1384 set_modal();
1385
1386 show();
1387
1388 while (! want_quit)
1389 {
1390 Fl::wait(0.2);
1391 }
1392
1393 last_active_tab = tabs->find(tabs->value());
1394
1395 if (want_discard)
1396 {
1397 LogPrintf("Preferences: discarded changes\n");
1398 return;
1399 }
1400
1401 SaveValues();
1402 M_WriteConfigFile();
1403
1404 M_ApplyBindings();
1405 M_SaveBindings();
1406 }
1407
1408
GridSizeToChoice(int size)1409 int UI_Preferences::GridSizeToChoice(int size)
1410 {
1411 if (size > 512) return 0;
1412 if (size > 256) return 1;
1413 if (size > 128) return 2;
1414 if (size > 64) return 3;
1415 if (size > 32) return 4;
1416 if (size > 16) return 5;
1417 if (size > 8) return 6;
1418 if (size > 4) return 7;
1419 if (size > 2) return 8;
1420
1421 return 9;
1422 }
1423
1424
LoadValues()1425 void UI_Preferences::LoadValues()
1426 {
1427 /* Theme stuff */
1428
1429 switch (gui_scheme)
1430 {
1431 case 0: theme_FLTK->value(1); break;
1432 case 1: theme_GTK->value(1); break;
1433 case 2: theme_plastic->value(1); break;
1434 }
1435
1436 switch (gui_color_set)
1437 {
1438 case 0: cols_default->value(1); break;
1439 case 1: cols_bright->value(1); break;
1440 case 2: cols_custom->value(1); break;
1441 }
1442
1443 bg_colorbox->color(gui_custom_bg);
1444 ig_colorbox->color(gui_custom_ig);
1445 fg_colorbox->color(gui_custom_fg);
1446
1447 /* General Tab */
1448
1449 gen_autoload ->value(auto_load_recent ? 1 : 0);
1450 gen_maximized ->value(begin_maximized ? 1 : 0);
1451 gen_swapsides ->value(swap_sidedefs ? 1 : 0);
1452
1453 /* Edit Tab */
1454
1455 edit_def_port->value(default_port);
1456 edit_def_mode->value(CLAMP(0, default_edit_mode, 3));
1457 edit_lineinfo->value(CLAMP(0, highlight_line_info, 5));
1458
1459 edit_sectorsize->value(Int_TmpStr(new_sector_size));
1460 edit_samemode->value(same_mode_clears_selection ? 1 : 0);
1461 edit_add_del->value(sidedef_add_del_buttons ? 1 : 0);
1462 edit_full_1S->value(show_full_one_sided ? 1 : 0);
1463 edit_autoadjustX->value(leave_offsets_alone ? 0 : 1);
1464
1465 brow_smalltex->value(browser_small_tex ? 1 : 0);
1466 brow_combo->value(browser_combine_tex ? 1 : 0);
1467
1468 char ratio_buf[256];
1469 snprintf(ratio_buf, sizeof(ratio_buf), "%d:%d", grid_ratio_high, grid_ratio_low);
1470 edit_userratio->value(ratio_buf);
1471
1472 /* Grid Tab */
1473
1474 if (grid_style < 0 || grid_style > 1)
1475 grid_style = 1;
1476
1477 if (grid_default_mode < 0 || grid_default_mode > 1)
1478 grid_default_mode = 1;
1479
1480 grid_cur_style->value(grid_style);
1481 grid_enabled->value(grid_default_mode);
1482 grid_snap->value(grid_default_snap ? 1 : 0);
1483 grid_size->value(GridSizeToChoice(grid_default_size));
1484 grid_hide_free ->value(grid_hide_in_free_mode ? 1 : 0);
1485 grid_flatrender->value(sector_render_default ? 1 : 0);
1486 grid_spriterend->value(thing_render_default ? 1 : 0);
1487 grid_indicator->value(grid_snap_indicator ? 1 : 0);
1488
1489 gen_scrollbars ->value(map_scroll_bars ? 1 : 0);
1490
1491 dotty_axis ->color(dotty_axis_col);
1492 dotty_major->color(dotty_major_col);
1493 dotty_minor->color(dotty_minor_col);
1494 dotty_point->color(dotty_point_col);
1495
1496 normal_axis ->color(normal_axis_col);
1497 normal_main ->color(normal_main_col);
1498 normal_flat ->color(normal_flat_col);
1499 normal_small->color(normal_small_col);
1500
1501 /* 3D Tab */
1502
1503 render_pixel_aspect = CLAMP(25, render_pixel_aspect, 400);
1504
1505 char aspect_buf[64];
1506 snprintf(aspect_buf, sizeof(aspect_buf), "%1.2f", render_pixel_aspect / 100.0);
1507 rend_aspect->value(aspect_buf);
1508
1509 rend_high_detail->value(render_high_detail ? 1 : 0);
1510 rend_lock_grav->value(render_lock_gravity ? 1 : 0);
1511
1512 if (render_far_clip > 24000)
1513 rend_far_clip->value(0);
1514 else if (render_far_clip > 12000)
1515 rend_far_clip->value(1);
1516 else if (render_far_clip > 6000)
1517 rend_far_clip->value(2);
1518 else if (render_far_clip > 3000)
1519 rend_far_clip->value(3);
1520 else if (render_far_clip > 1500)
1521 rend_far_clip->value(4);
1522 else
1523 rend_far_clip->value(5);
1524
1525 /* Nodes Tab */
1526
1527 nod_on_save->value(bsp_on_save ? 1 : 0);
1528 nod_fast->value(bsp_fast ? 1 : 0);
1529 nod_warn->value(bsp_warnings ? 1 : 0);
1530
1531 if (bsp_split_factor < 7)
1532 nod_factor->value(2); // Balanced BSP tree
1533 else if (bsp_split_factor > 15)
1534 nod_factor->value(1); // Minimize Splits
1535 else
1536 nod_factor->value(0); // NORMAL
1537
1538 nod_gl_nodes->value(bsp_gl_nodes ? 1 : 0);
1539 nod_force_v5->value(bsp_force_v5 ? 1 : 0);
1540 nod_force_zdoom->value(bsp_force_zdoom ? 1 : 0);
1541 nod_compress->value(bsp_compressed ? 1 : 0);
1542
1543 /* Other Tab */
1544
1545 }
1546
1547
SaveValues()1548 void UI_Preferences::SaveValues()
1549 {
1550 /* Theme stuff */
1551
1552 if (theme_FLTK->value())
1553 gui_scheme = 0;
1554 else if (theme_GTK->value())
1555 gui_scheme = 1;
1556 else
1557 gui_scheme = 2;
1558
1559 if (cols_default->value())
1560 gui_color_set = 0;
1561 else if (cols_bright->value())
1562 gui_color_set = 1;
1563 else
1564 gui_color_set = 2;
1565
1566 gui_custom_bg = (rgb_color_t) bg_colorbox->color();
1567 gui_custom_ig = (rgb_color_t) ig_colorbox->color();
1568 gui_custom_fg = (rgb_color_t) fg_colorbox->color();
1569
1570 // update the colors
1571 // FIXME: how to reset the "default" colors??
1572 if (gui_color_set == 1)
1573 {
1574 Fl::background(236, 232, 228);
1575 Fl::background2(255, 255, 255);
1576 Fl::foreground(0, 0, 0);
1577
1578 main_win->redraw();
1579 }
1580 else if (gui_color_set == 2)
1581 {
1582 Fl::background (RGB_RED(gui_custom_bg), RGB_GREEN(gui_custom_bg), RGB_BLUE(gui_custom_bg));
1583 Fl::background2(RGB_RED(gui_custom_ig), RGB_GREEN(gui_custom_ig), RGB_BLUE(gui_custom_ig));
1584 Fl::foreground (RGB_RED(gui_custom_fg), RGB_GREEN(gui_custom_fg), RGB_BLUE(gui_custom_fg));
1585
1586 main_win->redraw();
1587 }
1588
1589 /* General Tab */
1590
1591 auto_load_recent = gen_autoload ->value() ? true : false;
1592 begin_maximized = gen_maximized ->value() ? true : false;
1593 swap_sidedefs = gen_swapsides ->value() ? true : false;
1594
1595 /* Edit Tab */
1596
1597 default_port = StringDup(edit_def_port->value());
1598 default_edit_mode = edit_def_mode->value();
1599 highlight_line_info = edit_lineinfo->value();
1600
1601 new_sector_size = atoi(edit_sectorsize->value());
1602 new_sector_size = CLAMP(4, new_sector_size, 8192);
1603
1604 same_mode_clears_selection = edit_samemode->value() ? true : false;
1605 sidedef_add_del_buttons = edit_add_del->value() ? true : false;
1606 show_full_one_sided = edit_full_1S->value() ? true : false;
1607 leave_offsets_alone = edit_autoadjustX->value() ? false : true;
1608
1609 // changing this requires re-populating the browser
1610 bool new_small_tex = brow_smalltex->value() ? true : false;
1611 bool new_combo = brow_combo->value() ? true : false;
1612
1613 if (new_small_tex != browser_small_tex || new_combo != browser_combine_tex)
1614 {
1615 browser_small_tex = new_small_tex;
1616 browser_combine_tex = new_combo;
1617
1618 main_win->browser->Populate();
1619 }
1620
1621 // decode the user ratio
1622 grid_ratio_low = grid_ratio_high = -1;
1623 sscanf(edit_userratio->value(), "%d:%d", &grid_ratio_high, &grid_ratio_low);
1624 if (grid_ratio_high < 1) grid_ratio_high = 3;
1625 if (grid_ratio_low < 1) grid_ratio_low = 1;
1626
1627 if (grid_ratio_low > grid_ratio_high)
1628 std::swap(grid_ratio_low, grid_ratio_high);
1629
1630 main_win->info_bar->UpdateRatio();
1631
1632 /* Grid Tab */
1633
1634 grid_style = grid_cur_style->value();
1635 grid_default_mode = grid_enabled->value();
1636 grid_default_snap = grid_snap->value() ? true : false;
1637 grid_default_size = atoi(grid_size->mvalue()->text);
1638 grid_hide_in_free_mode = grid_hide_free ->value() ? true : false;
1639 grid_snap_indicator = grid_indicator ->value() ? true : false;
1640 sector_render_default = grid_flatrender->value() ? 1 : 0;
1641 thing_render_default = grid_spriterend->value() ? 1 : 0;
1642
1643 map_scroll_bars = gen_scrollbars ->value() ? true : false;
1644
1645 dotty_axis_col = (rgb_color_t) dotty_axis ->color();
1646 dotty_major_col = (rgb_color_t) dotty_major->color();
1647 dotty_minor_col = (rgb_color_t) dotty_minor->color();
1648 dotty_point_col = (rgb_color_t) dotty_point->color();
1649
1650 normal_axis_col = (rgb_color_t) normal_axis ->color();
1651 normal_main_col = (rgb_color_t) normal_main ->color();
1652 normal_flat_col = (rgb_color_t) normal_flat ->color();
1653 normal_small_col = (rgb_color_t) normal_small->color();
1654
1655 /* Nodes Tab */
1656
1657 bsp_on_save = nod_on_save->value() ? true : false;
1658 bsp_fast = nod_fast->value() ? true : false;
1659 bsp_warnings = nod_warn->value() ? true : false;
1660
1661 if (nod_factor->value() == 1) // Minimize Splits
1662 bsp_split_factor = 29;
1663 else if (nod_factor->value() == 2) // Balanced BSP tree
1664 bsp_split_factor = 2;
1665 else
1666 bsp_split_factor = 11;
1667
1668 bsp_gl_nodes = nod_gl_nodes->value() ? true : false;
1669 bsp_force_v5 = nod_force_v5->value() ? true : false;
1670 bsp_force_zdoom = nod_force_zdoom->value() ? true : false;
1671 bsp_compressed = nod_compress->value() ? true : false;
1672
1673 /* Other Tab */
1674
1675 render_pixel_aspect = (int)(100 * atof(rend_aspect->value()) + 0.2);
1676 render_pixel_aspect = CLAMP(25, render_pixel_aspect, 400);
1677
1678 render_high_detail = rend_high_detail->value() ? true : false;
1679 render_lock_gravity = rend_lock_grav->value() ? true : false;
1680 render_far_clip = atoi(rend_far_clip->mvalue()->text);
1681 }
1682
1683
LoadKeys()1684 void UI_Preferences::LoadKeys()
1685 {
1686 M_SortBindings(key_sort_mode, key_sort_rev);
1687 M_DetectConflictingBinds();
1688
1689 key_list->clear();
1690
1691 for (int i = 0 ; i < M_NumBindings() ; i++)
1692 {
1693 const char *str = M_StringForBinding(i);
1694 SYS_ASSERT(str);
1695
1696 key_list->add(str);
1697 }
1698
1699 key_list->select(1);
1700 }
1701
1702
ReloadKeys()1703 void UI_Preferences::ReloadKeys()
1704 {
1705 M_DetectConflictingBinds();
1706
1707 for (int i = 0 ; i < M_NumBindings() ; i++)
1708 {
1709 const char *str = M_StringForBinding(i);
1710
1711 key_list->text(i + 1, str);
1712 }
1713 }
1714
1715
EnsureKeyVisible(int line)1716 void UI_Preferences::EnsureKeyVisible(int line)
1717 {
1718 if (! key_list->displayed(line))
1719 {
1720 key_list->middleline(line);
1721 }
1722 }
1723
1724
ClearWaiting()1725 void UI_Preferences::ClearWaiting()
1726 {
1727 if (awaiting_line > 0)
1728 {
1729 // restore the text line
1730 ReloadKeys();
1731
1732 Fl::focus(key_list);
1733 }
1734
1735 awaiting_line = 0;
1736
1737 key_list->selection_color(FL_SELECTION_COLOR);
1738 }
1739
1740
SetBinding(keycode_t key)1741 void UI_Preferences::SetBinding(keycode_t key)
1742 {
1743 int bind_idx = awaiting_line - 1;
1744
1745 M_ChangeBindingKey(bind_idx, key);
1746
1747 ClearWaiting();
1748 }
1749
1750
handle(int event)1751 int UI_Preferences::handle(int event)
1752 {
1753 if (awaiting_line > 0)
1754 {
1755 // escape key cancels
1756 if (event == FL_KEYDOWN && Fl::event_key() == FL_Escape)
1757 {
1758 ClearWaiting();
1759 return 1;
1760 }
1761
1762 if (event == FL_KEYDOWN ||
1763 event == FL_PUSH ||
1764 event == FL_MOUSEWHEEL)
1765 {
1766 keycode_t new_key = M_CookedKeyForEvent(event);
1767
1768 if (new_key)
1769 {
1770 SetBinding(new_key);
1771 return 1;
1772 }
1773 }
1774 }
1775
1776 return Fl_Double_Window::handle(event);
1777 }
1778
1779
1780 //------------------------------------------------------------------------
1781
1782
CMD_Preferences()1783 void CMD_Preferences()
1784 {
1785 UI_Preferences * dialog = new UI_Preferences();
1786
1787 dialog->Run();
1788
1789 delete dialog;
1790 }
1791
1792
1793 //--- editor settings ---
1794 // vi:ts=4:sw=4:noexpandtab
1795