1 //------------------------------------------------------------------------
2 // FILE-RELATED DIALOGS
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 #include "m_files.h"
24 #include "m_game.h"
25 #include "w_wad.h"
26
27 #include "ui_window.h"
28 #include "ui_file.h"
29
30
31 #define FREE_COL fl_rgb_color(0x33, 0xFF, 0xAA)
32 #define USED_COL (gui_scheme == 2 ? fl_rgb_color(0xFF, 0x11, 0x11) : fl_rgb_color(0xFF, 0x88, 0x88))
33
34
35 // TODO: find a better home for this
ValidateMapName(const char * p)36 bool ValidateMapName(const char *p)
37 {
38 size_t len = strlen(p);
39
40 if (len == 0 || len > 8)
41 return false;
42
43 if (! isalpha(*p))
44 return false;
45
46 for ( ; *p ; p++)
47 if (! (isalnum(*p) || *p == '_'))
48 return false;
49
50 return true;
51 }
52
53
UI_ChooseMap(const char * initial_name,Wad_file * _rename_wad)54 UI_ChooseMap::UI_ChooseMap(const char *initial_name,
55 Wad_file *_rename_wad) :
56 UI_Escapable_Window(420, 385, "Choose Map"),
57 rename_wad(_rename_wad),
58 action(ACT_none)
59 {
60 resizable(NULL);
61
62 callback(close_callback, this);
63
64 {
65 map_name = new Fl_Input(120, 35, 120, 25, "Map slot: ");
66 map_name->labelfont(FL_HELVETICA_BOLD);
67 }
68
69 map_name->when(FL_WHEN_CHANGED);
70 map_name->callback(input_callback, this);
71 map_name->value(initial_name);
72
73 Fl::focus(map_name);
74
75 map_buttons = new Fl_Group(x(), y() + 60, w(), y() + 320);
76 map_buttons->end();
77
78 {
79 int bottom_y = 320;
80
81 Fl_Group* o = new Fl_Group(0, bottom_y, 420, 65);
82 o->box(FL_FLAT_BOX);
83 o->color(WINDOW_BG, WINDOW_BG);
84
85 ok_but = new Fl_Return_Button(260, bottom_y + 17, 100, 35, "OK");
86 ok_but->labelfont(FL_HELVETICA_BOLD);
87 ok_but->callback(ok_callback, this);
88
89 Fl_Button *cancel = new Fl_Button(75, bottom_y + 17, 100, 35, "Cancel");
90 cancel->callback(close_callback, this);
91
92 o->end();
93 }
94
95 end();
96
97 CheckMapName();
98 }
99
100
~UI_ChooseMap()101 UI_ChooseMap::~UI_ChooseMap()
102 {
103 }
104
105
PopulateButtons(char format,Wad_file * test_wad)106 void UI_ChooseMap::PopulateButtons(char format, Wad_file *test_wad)
107 {
108 int but_W = 60;
109
110 for (int col = 0 ; col < 5 ; col++)
111 for (int row = 0 ; row < 8 ; row++)
112 {
113 int cx = x() + 30 + col * (but_W + but_W / 5);
114 int cy = y() + 80 + row * 24 + (row / 2) * 10;
115
116 char name_buf[20];
117
118 if (format == 'E')
119 {
120 int epi = 1 + row / 2;
121 int map = 1 + col + (row & 1) * 5;
122
123 if (map > 9)
124 continue;
125
126 snprintf(name_buf, sizeof(name_buf), "E%dM%d", epi, map);
127 }
128 else
129 {
130 int map = 1 + col + row * 5;
131
132 // this logic matches UI_OpenMap on the IWAD
133 if (row >= 2)
134 map--;
135 else if (row == 1 && col == 4)
136 continue;
137
138 if (map < 1 || map > 32)
139 continue;
140
141 snprintf(name_buf, sizeof(name_buf), "MAP%02d", map);
142 }
143
144 Fl_Button * but = new Fl_Button(cx, cy, 60, 20);
145 but->copy_label(name_buf);
146 but->callback(button_callback, this);
147
148 if (test_wad && test_wad->LevelFind(name_buf) >= 0)
149 {
150 if (rename_wad)
151 but->deactivate();
152 else
153 but->color(USED_COL);
154 }
155 else
156 but->color(FREE_COL);
157
158 map_buttons->add(but);
159 }
160 }
161
162
Run()163 const char * UI_ChooseMap::Run()
164 {
165 set_modal();
166
167 show();
168
169 while (action == ACT_none)
170 {
171 Fl::wait(0.2);
172 }
173
174 if (action == ACT_CANCEL)
175 return NULL;
176
177 return StringUpper(map_name->value());
178 }
179
180
close_callback(Fl_Widget * w,void * data)181 void UI_ChooseMap::close_callback(Fl_Widget *w, void *data)
182 {
183 UI_ChooseMap * that = (UI_ChooseMap *)data;
184
185 that->action = ACT_CANCEL;
186 }
187
188
ok_callback(Fl_Widget * w,void * data)189 void UI_ChooseMap::ok_callback(Fl_Widget *w, void *data)
190 {
191 UI_ChooseMap * that = (UI_ChooseMap *)data;
192
193 // sanity check
194 if (ValidateMapName(that->map_name->value()))
195 that->action = ACT_ACCEPT;
196 else
197 fl_beep();
198 }
199
200
button_callback(Fl_Widget * w,void * data)201 void UI_ChooseMap::button_callback(Fl_Widget *w, void *data)
202 {
203 UI_ChooseMap * that = (UI_ChooseMap *)data;
204
205 that->map_name->value(w->label());
206 that->action = ACT_ACCEPT;
207 }
208
209
input_callback(Fl_Widget * w,void * data)210 void UI_ChooseMap::input_callback(Fl_Widget *w, void *data)
211 {
212 UI_ChooseMap * that = (UI_ChooseMap *)data;
213
214 that->CheckMapName();
215 }
216
217
CheckMapName()218 void UI_ChooseMap::CheckMapName()
219 {
220 bool was_valid = ok_but->active();
221 bool is_valid = ValidateMapName(map_name->value());
222
223 if (rename_wad && is_valid)
224 {
225 if (rename_wad->LevelFind(map_name->value()) >= 0)
226 is_valid = false;
227 }
228
229 if (was_valid == is_valid)
230 return;
231
232 if (is_valid)
233 {
234 ok_but->activate();
235 map_name->textcolor(FL_FOREGROUND_COLOR);
236 }
237 else
238 {
239 ok_but->deactivate();
240 map_name->textcolor(FL_RED);
241 }
242
243 map_name->redraw();
244 }
245
246
247 //------------------------------------------------------------------------
248
249
UI_OpenMap()250 UI_OpenMap::UI_OpenMap() :
251 UI_Escapable_Window(420, 475, "Open Map"),
252 action(ACT_none),
253 loaded_wad(NULL),
254 using_wad(NULL)
255 {
256 resizable(NULL);
257
258 callback(close_callback, this);
259
260 {
261 look_where = new Fl_Choice(130, 80, 190, 25, "Find map in: ");
262 look_where->labelfont(FL_HELVETICA_BOLD);
263 look_where->add("the PWAD above|the Game IWAD|the Resource wads");
264 look_where->callback(look_callback, this);
265
266 look_where->value(edit_wad ? LOOK_PWad : LOOK_IWad);
267 }
268
269 {
270 Fl_Box* o = new Fl_Box(15, 15, 270, 20, "PWAD file:");
271 o->labelfont(FL_HELVETICA_BOLD);
272 o->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
273 }
274
275 pwad_name = new Fl_Output(20, 40, 295, 26);
276
277 Fl_Button *load_but = new Fl_Button(330, 39, 65, 28, "Load");
278 load_but->callback(load_callback, this);
279
280
281 map_name = new Fl_Input(99, 125, 100, 26, "Map slot: ");
282 map_name->labelfont(FL_HELVETICA_BOLD);
283 map_name->when(FL_WHEN_CHANGED);
284 map_name->callback(input_callback, this);
285
286 {
287 Fl_Box *o = new Fl_Box(230, 125, 180, 26, "Available maps:");
288 // o->labelfont(FL_HELVETICA_BOLD);
289 o->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
290 }
291
292
293 // all the map buttons go into this group
294
295 button_grp = new UI_Scroll(5, 165, w()-10, 230, +1 /* bar_side */);
296 button_grp->align(FL_ALIGN_TOP | FL_ALIGN_INSIDE);
297 button_grp->resize_horiz(false);
298 button_grp->Line_size(24);
299 button_grp->box(FL_FLAT_BOX);
300
301 /* bottom buttons */
302
303 {
304 int bottom_y = h() - 70;
305
306 Fl_Group* o = new Fl_Group(0, bottom_y, w(), 70);
307 o->box(FL_FLAT_BOX);
308 o->color(WINDOW_BG, WINDOW_BG);
309
310 ok_but = new Fl_Return_Button(260, bottom_y + 20, 100, 34, "OK");
311 ok_but->labelfont(FL_HELVETICA_BOLD);
312 ok_but->callback(ok_callback, this);
313
314 Fl_Button * cancel = new Fl_Button(75, bottom_y + 20, 100, 35, "Cancel");
315 cancel->callback(close_callback, this);
316
317 o->end();
318 }
319
320 end();
321
322 CheckMapName();
323 }
324
325
~UI_OpenMap()326 UI_OpenMap::~UI_OpenMap()
327 { }
328
329
Run(const char ** map_v,bool * did_load)330 Wad_file * UI_OpenMap::Run(const char ** map_v, bool * did_load)
331 {
332 *map_v = NULL;
333 *did_load = false;
334
335 if (edit_wad)
336 SetPWAD(edit_wad->PathName());
337
338 Populate();
339
340 set_modal();
341 show();
342
343 while (action == ACT_none)
344 {
345 Fl::wait(0.2);
346 }
347
348 if (action != ACT_ACCEPT)
349 using_wad = NULL;
350
351 if (using_wad)
352 {
353 *map_v = StringUpper(map_name->value());
354
355 if (using_wad == loaded_wad)
356 {
357 *did_load = true;
358 loaded_wad = NULL;
359 }
360 }
361
362 // if we are not returning a pwad which got loaded, e.g. because
363 // the user cancelled or chose the game IWAD, then close it now.
364 if (loaded_wad)
365 delete loaded_wad;
366
367 return using_wad;
368 }
369
370
CheckMapName()371 void UI_OpenMap::CheckMapName()
372 {
373 bool was_valid = ok_but->active();
374
375 bool is_valid = (using_wad != NULL) &&
376 ValidateMapName(map_name->value()) &&
377 (using_wad->LevelFind(map_name->value()) >= 0);
378
379 if (was_valid == is_valid)
380 return;
381
382 if (is_valid)
383 {
384 ok_but->activate();
385 map_name->textcolor(FL_FOREGROUND_COLOR);
386 }
387 else
388 {
389 ok_but->deactivate();
390 map_name->textcolor(FL_RED);
391 }
392
393 map_name->redraw();
394 }
395
396
Populate()397 void UI_OpenMap::Populate()
398 {
399 button_grp->label("\n\n\n\n\nNO MAPS FOUND");
400 button_grp->Remove_all();
401
402 using_wad = NULL;
403
404 if (look_where->value() == LOOK_IWad)
405 {
406 using_wad = game_wad;
407 PopulateButtons();
408 }
409 else if (look_where->value() >= LOOK_Resource)
410 {
411 int first = 1;
412 int last = (int)master_dir.size() - 1;
413
414 if (edit_wad)
415 last--;
416
417 // we simply use the last resource which contains levels
418
419 // TODO: probably should collect ones with a map, add to look_where choices
420
421 for (int r = last ; r >= first ; r--)
422 {
423 if (master_dir[r]->LevelCount() >= 0)
424 {
425 using_wad = master_dir[r];
426 PopulateButtons();
427 break;
428 }
429 }
430 }
431 else if (loaded_wad)
432 {
433 using_wad = loaded_wad;
434 PopulateButtons();
435 }
436 else if (edit_wad)
437 {
438 using_wad = edit_wad;
439 PopulateButtons();
440 }
441
442 button_grp->Init_sizes();
443 button_grp->redraw();
444 }
445
446
DifferentEpisode(const char * A,const char * B)447 static bool DifferentEpisode(const char *A, const char *B)
448 {
449 if (A[0] != B[0])
450 return true;
451
452 // handle ExMx
453 if (toupper(A[0]) == 'E')
454 {
455 return A[1] != B[1];
456 }
457
458 // handle MAPxx
459 if (strlen(A) < 4 && strlen(B) < 4)
460 return false;
461
462 return A[3] != B[3];
463 }
464
465
PopulateButtons()466 void UI_OpenMap::PopulateButtons()
467 {
468 Wad_file *wad = using_wad;
469 SYS_ASSERT(wad);
470
471 int num_levels = wad->LevelCount();
472
473 if (num_levels == 0)
474 return;
475
476 button_grp->label("");
477
478 std::map<std::string, int> level_names;
479 std::map<std::string, int>::iterator IT;
480
481 for (int lev = 0 ; lev < num_levels ; lev++)
482 {
483 Lump_c *lump = wad->GetLump(wad->LevelHeader(lev));
484
485 level_names[std::string(lump->Name())] = 1;
486 }
487
488 int cx_base = button_grp->x() + 25;
489 int cy_base = button_grp->y() + 5;
490 int but_W = 60;
491
492 // create them buttons!!
493
494 int row = 0;
495 int col = 0;
496
497 const char *last_name = NULL;
498
499 for (IT = level_names.begin() ; IT != level_names.end() ; IT++)
500 {
501 const char *name = IT->first.c_str();
502
503 if (col > 0 && last_name && DifferentEpisode(last_name, name))
504 {
505 col = 0;
506 row++;
507 }
508
509 int cx = cx_base + col * (but_W + but_W / 5);
510 int cy = cy_base + row * 24 + (row / 2) * 8;
511
512 Fl_Button * but = new Fl_Button(cx, cy, but_W, 20);
513 but->copy_label(name);
514 but->color(FREE_COL);
515 but->callback(button_callback, this);
516
517 button_grp->Add(but);
518
519 col++;
520 if (col >= 5)
521 {
522 col = 0;
523 row++;
524 }
525
526 last_name = name;
527 }
528
529 redraw();
530 }
531
532
SetPWAD(const char * name)533 void UI_OpenMap::SetPWAD(const char *name)
534 {
535 pwad_name->value(fl_filename_name(name));
536 }
537
538
close_callback(Fl_Widget * w,void * data)539 void UI_OpenMap::close_callback(Fl_Widget *w, void *data)
540 {
541 UI_OpenMap * that = (UI_OpenMap *)data;
542
543 that->action = ACT_CANCEL;
544 }
545
546
ok_callback(Fl_Widget * w,void * data)547 void UI_OpenMap::ok_callback(Fl_Widget *w, void *data)
548 {
549 UI_OpenMap * that = (UI_OpenMap *)data;
550
551 // sanity check
552 if (that->using_wad && ValidateMapName(that->map_name->value()))
553 that->action = ACT_ACCEPT;
554 else
555 fl_beep();
556 }
557
558
button_callback(Fl_Widget * w,void * data)559 void UI_OpenMap::button_callback(Fl_Widget *w, void *data)
560 {
561 UI_OpenMap * that = (UI_OpenMap *)data;
562
563 // sanity check
564 if (! that->using_wad)
565 return;
566
567 that->map_name->value(w->label());
568 that->action = ACT_ACCEPT;
569 }
570
571
input_callback(Fl_Widget * w,void * data)572 void UI_OpenMap::input_callback(Fl_Widget *w, void *data)
573 {
574 UI_OpenMap * that = (UI_OpenMap *)data;
575
576 that->CheckMapName();
577 }
578
579
look_callback(Fl_Widget * w,void * data)580 void UI_OpenMap::look_callback(Fl_Widget *w, void *data)
581 {
582 UI_OpenMap * that = (UI_OpenMap *)data;
583
584 that->Populate();
585 that->CheckMapName();
586 }
587
588
load_callback(Fl_Widget * w,void * data)589 void UI_OpenMap::load_callback(Fl_Widget *w, void *data)
590 {
591 UI_OpenMap * that = (UI_OpenMap *)data;
592
593 that->LoadFile();
594 that->CheckMapName();
595 }
596
597
LoadFile()598 void UI_OpenMap::LoadFile()
599 {
600 Fl_Native_File_Chooser chooser;
601
602 chooser.title("Pick file to open");
603 chooser.type(Fl_Native_File_Chooser::BROWSE_FILE);
604 chooser.filter("Wads\t*.wad");
605 chooser.directory(Main_FileOpFolder());
606
607 // Show native chooser
608 switch (chooser.show())
609 {
610 case -1:
611 LogPrintf("Open Map: error choosing file:\n");
612 LogPrintf(" %s\n", chooser.errmsg());
613
614 DLG_Notify("Unable to open the map:\n\n%s",
615 chooser.errmsg());
616 return;
617
618 case 1:
619 LogPrintf("Open Map: cancelled by user\n");
620 return;
621
622 default:
623 break; // OK
624 }
625
626
627 Wad_file * wad = Wad_file::Open(chooser.filename(), 'a');
628
629 if (! wad)
630 {
631 // FIXME: get an error message, add it here
632
633 DLG_Notify("Unable to open the chosen WAD file.\n\n"
634 "Please try again.");
635 return;
636 }
637
638 if (wad->LevelCount() < 0)
639 {
640 DLG_Notify("The chosen WAD contains no levels.\n\n"
641 "Please try again.");
642 return;
643 }
644
645
646 // replace existing one
647 if (loaded_wad)
648 delete loaded_wad;
649
650 loaded_wad = wad;
651
652 SetPWAD(loaded_wad->PathName());
653
654 if (using_wad == loaded_wad)
655 using_wad = wad;
656
657
658 // change the "Find map in ..." setting
659 look_where->value(LOOK_PWad);
660
661 Populate();
662 }
663
664
665 //------------------------------------------------------------------------
666
667
668 UI_ProjectSetup * UI_ProjectSetup::_instance = NULL;
669
670
671 #define STARTUP_MSG "No IWADs could be found."
672
673
UI_ProjectSetup(bool new_project,bool is_startup)674 UI_ProjectSetup::UI_ProjectSetup(bool new_project, bool is_startup) :
675 UI_Escapable_Window(400, is_startup ? 200 : 440, new_project ? "New Project" : "Manage Project"),
676 action(ACT_none),
677 game(NULL), port(NULL),
678 map_format(MAPF_INVALID), name_space()
679 {
680 callback(close_callback, this);
681
682 resizable(NULL);
683
684 _instance = this; // meh, hacky
685
686 int by = 0;
687
688 if (is_startup)
689 {
690 Fl_Box * message = new Fl_Box(FL_FLAT_BOX, 15, 15, 370, 46, STARTUP_MSG);
691 message->align(FL_ALIGN_INSIDE);
692 message->color(FL_RED, FL_RED);
693 message->labelcolor(FL_YELLOW);
694 message->labelsize(18);
695
696 by += 60;
697 }
698
699 game_choice = new Fl_Choice(140, by+25, 150, 29, "Game IWAD: ");
700 game_choice->labelfont(FL_HELVETICA_BOLD);
701 game_choice->down_box(FL_BORDER_BOX);
702 game_choice->callback((Fl_Callback*)game_callback, this);
703
704 {
705 Fl_Button* o = new Fl_Button(305, by+27, 75, 25, "Find");
706 o->callback((Fl_Callback*)find_callback, this);
707 }
708
709 port_choice = new Fl_Choice(140, by+62, 150, 29, "Source Port: ");
710 port_choice->labelfont(FL_HELVETICA_BOLD);
711 port_choice->down_box(FL_BORDER_BOX);
712 port_choice->callback((Fl_Callback*)port_callback, this);
713
714 {
715 Fl_Button* o = new Fl_Button(305, by+64, 75, 25, "Setup");
716 o->callback((Fl_Callback*)setup_callback, this);
717
718 if (is_startup)
719 o->hide();
720 }
721
722 format_choice = new Fl_Choice(140, by+99, 150, 29, "Map Type: ");
723 format_choice->labelfont(FL_HELVETICA_BOLD);
724 format_choice->down_box(FL_BORDER_BOX);
725 format_choice->callback((Fl_Callback*)format_callback, this);
726
727 #if 0 // Disabled for now
728 namespace_choice = new Fl_Choice(140, by+140, 150, 29, "Namespace: ");
729 namespace_choice->labelfont(FL_HELVETICA_BOLD);
730 namespace_choice->down_box(FL_BORDER_BOX);
731 namespace_choice->callback((Fl_Callback*)namespace_callback, this);
732 namespace_choice->hide();
733 #endif
734
735 if (is_startup)
736 {
737 port_choice->hide();
738 format_choice->hide();
739 }
740
741 // Resource section
742
743 if (! is_startup)
744 {
745 Fl_Box *res_title = new Fl_Box(15, by+190, 185, 30, "Resource Files:");
746 res_title->labelfont(FL_HELVETICA_BOLD);
747 res_title->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
748 }
749
750 for (int r = 0 ; r < RES_NUM ; r++)
751 {
752 res[r] = NULL;
753
754 if (is_startup)
755 continue;
756
757 int cy = by + 220 + r * 35;
758
759 char res_label[64];
760 snprintf(res_label, sizeof(res_label), "%d. ", 1 + r);
761
762 res_name[r] = new Fl_Output(55, cy, 205, 25);
763 res_name[r]->copy_label(res_label);
764
765 Fl_Button *kill = new Fl_Button(270, cy, 30, 25, "x");
766 kill->labelsize(20);
767 kill->callback((Fl_Callback*)kill_callback, (void *)(long)r);
768
769 Fl_Button *load = new Fl_Button(315, cy, 75, 25, "Load");
770 load->callback((Fl_Callback*)load_callback, (void *)(long)r);
771 }
772
773 // bottom buttons
774 {
775 by += is_startup ? 80 : 375;
776
777 Fl_Group *g = new Fl_Group(0, by, 400, h() - by);
778 g->box(FL_FLAT_BOX);
779 g->color(WINDOW_BG, WINDOW_BG);
780
781 const char *cancel_text = is_startup ? "Quit" : "Cancel";
782
783 cancel = new Fl_Button(90, g->y() + 14, 80, 35, cancel_text);
784 cancel->callback((Fl_Callback*)close_callback, this);
785
786 const char *ok_text = (is_startup | new_project) ? "OK" : "Use";
787
788 ok_but = new Fl_Button(240, g->y() + 14, 80, 35, ok_text);
789 ok_but->labelfont(FL_HELVETICA_BOLD);
790 ok_but->callback((Fl_Callback*)use_callback, this);
791
792 g->end();
793 }
794
795 end();
796 }
797
798
~UI_ProjectSetup()799 UI_ProjectSetup::~UI_ProjectSetup()
800 {
801 _instance = NULL;
802 }
803
804
Run()805 bool UI_ProjectSetup::Run()
806 {
807 PopulateIWADs();
808 PopulatePort();
809 PopulateMapFormat();
810 PopulateResources();
811
812 set_modal();
813
814 show();
815
816 while (action == ACT_none)
817 {
818 Fl::wait(0.2);
819 }
820
821 return (action == ACT_ACCEPT);
822 }
823
824
PopulateIWADs()825 void UI_ProjectSetup::PopulateIWADs()
826 {
827 // This is called (a) when dialog is first opened, or (b) when
828 // the user has found a new iwad. For the latter case, we want
829 // to show the newly found game.
830
831 const char *prev_game = game;
832
833 if (! prev_game) prev_game = Game_name;
834 if (! prev_game) prev_game = "doom2";
835
836
837 game = NULL;
838
839 game_choice->clear();
840
841
842 const char *menu_string;
843 int menu_value = 0;
844
845 menu_string = M_CollectGamesForMenu(&menu_value, prev_game);
846
847 if (menu_string[0])
848 {
849 game_choice->add(menu_string);
850 game_choice->value(menu_value);
851
852 game = StringDup(game_choice->mvalue()->text);
853 }
854
855 if (game)
856 ok_but->activate();
857 else
858 ok_but->deactivate();
859 }
860
861
PopulatePort()862 void UI_ProjectSetup::PopulatePort()
863 {
864 const char *prev_port = NULL;
865
866 if (port_choice->mvalue())
867 prev_port = StringDup(port_choice->mvalue()->text);
868
869 if (! prev_port) prev_port = Port_name;
870 if (! prev_port) prev_port = "vanilla";
871
872
873 port = "vanilla";
874
875 port_choice->clear();
876
877 // if no game, port doesn't matter
878 if (! game)
879 return;
880
881
882 const char *base_game = NULL;
883
884 if (game_choice->mvalue())
885 base_game = M_GetBaseGame(game_choice->mvalue()->text);
886 else if (Game_name)
887 base_game = M_GetBaseGame(Game_name);
888
889 if (! base_game)
890 base_game = "doom2";
891
892
893 const char *menu_string;
894 int menu_value = 0;
895
896 menu_string = M_CollectPortsForMenu(base_game, &menu_value, prev_port);
897
898 if (menu_string[0])
899 {
900 port_choice->add (menu_string);
901 port_choice->value(menu_value);
902
903 port = StringDup(port_choice->mvalue()->text);
904 }
905 }
906
907
PopulateMapFormat()908 void UI_ProjectSetup::PopulateMapFormat()
909 {
910 map_format_e prev_fmt = map_format;
911
912 if (prev_fmt == MAPF_INVALID)
913 prev_fmt = Level_format;
914
915
916 format_choice->clear();
917
918 // if no game, format doesn't matter
919 if (! game)
920 {
921 map_format = MAPF_Doom;
922 name_space = "";
923 return;
924 }
925
926
927 // determine the usable formats, from current game and port
928 const char *c_game = "doom2";
929 const char *c_port = "vanilla";
930
931 if (game_choice->mvalue())
932 c_game = game_choice->mvalue()->text;
933
934 if (port_choice->mvalue())
935 c_port = port_choice->mvalue()->text;
936
937 usable_formats = M_DetermineMapFormats(c_game, c_port);
938
939 SYS_ASSERT(usable_formats != 0);
940
941
942 // reconstruct the menu
943 int menu_value = 0;
944 int entry_id = 0;
945
946 if (usable_formats & (1 << MAPF_Doom))
947 {
948 format_choice->add("Doom Format");
949 entry_id++;
950 }
951
952 if (usable_formats & (1 << MAPF_Hexen))
953 {
954 if (prev_fmt == MAPF_Hexen)
955 menu_value = entry_id;
956
957 format_choice->add("Hexen Format");
958 entry_id++;
959 }
960
961 if (udmf_testing && (usable_formats & (1 << MAPF_UDMF)))
962 {
963 if (prev_fmt == MAPF_UDMF)
964 menu_value = entry_id;
965
966 format_choice->add("UDMF");
967 entry_id++;
968 }
969
970 format_choice->value(menu_value);
971
972 // set map_format field based on current menu entry.
973 format_callback(format_choice, (void *)this);
974
975
976 // determine the UDMF namespace
977 name_space = "";
978
979 PortInfo_c *pinfo = M_LoadPortInfo(port_choice->mvalue()->text);
980 if (pinfo)
981 name_space = pinfo->udmf_namespace;
982
983 // don't leave namespace as "" when chosen format is UDMF.
984 // [ this is to handle broken config files somewhat sanely ]
985 if (name_space.empty() && map_format == MAPF_UDMF)
986 name_space = "Hexen";
987 }
988
989
PopulateNamespaces()990 void UI_ProjectSetup::PopulateNamespaces()
991 {
992 #if 0 // Disabled for now
993
994 if (map_format != MAPF_UDMF)
995 {
996 namespace_choice->hide();
997 return;
998 }
999
1000 namespace_choice->show();
1001
1002 // get previous value
1003 const char *prev_ns = name_space.c_str();
1004
1005 if (prev_ns[0] == 0)
1006 prev_ns = Udmf_namespace.c_str();
1007
1008
1009 namespace_choice->clear();
1010
1011 if (! port_choice->mvalue())
1012 return;
1013
1014 PortInfo_c *pinfo = M_LoadPortInfo(port_choice->mvalue()->text);
1015 if (! pinfo)
1016 return;
1017
1018 int menu_value = 0;
1019
1020 for (int i = 0 ; i < (int)pinfo->namespaces.size() ; i++)
1021 {
1022 const char * ns = pinfo->namespaces[i].c_str();
1023
1024 namespace_choice->add(ns);
1025
1026 // keep same entry as before, when possible
1027 if (strcmp(prev_ns, ns) == 0)
1028 menu_value = i;
1029 }
1030
1031 namespace_choice->value(menu_value);
1032
1033 if (menu_value < (int)pinfo->namespaces.size())
1034 name_space = pinfo->namespaces[menu_value];
1035 #endif
1036 }
1037
1038
PopulateResources()1039 void UI_ProjectSetup::PopulateResources()
1040 {
1041 // Note: these resource wads may be invalid (not exist) during startup.
1042 // This is probably NOT the place to validate them...
1043
1044 for (int r = 0 ; r < RES_NUM ; r++)
1045 {
1046 // the resource widgets are not created for the missing-iwad dialog
1047 if (! res_name[r])
1048 continue;
1049
1050 if (r < (int)Resource_list.size())
1051 {
1052 res[r] = StringDup(Resource_list[r]);
1053
1054 res_name[r]->value(fl_filename_name(res[r]));
1055 }
1056 }
1057 }
1058
1059
close_callback(Fl_Widget * w,void * data)1060 void UI_ProjectSetup::close_callback(Fl_Widget *w, void *data)
1061 {
1062 UI_ProjectSetup * that = (UI_ProjectSetup *)data;
1063
1064 that->action = ACT_CANCEL;
1065 }
1066
1067
use_callback(Fl_Button * w,void * data)1068 void UI_ProjectSetup::use_callback(Fl_Button *w, void *data)
1069 {
1070 UI_ProjectSetup * that = (UI_ProjectSetup *)data;
1071
1072 that->action = ACT_ACCEPT;
1073 }
1074
1075
game_callback(Fl_Choice * w,void * data)1076 void UI_ProjectSetup::game_callback(Fl_Choice *w, void *data)
1077 {
1078 UI_ProjectSetup * that = (UI_ProjectSetup *)data;
1079
1080 const char * name = w->mvalue()->text;
1081
1082 if (M_QueryKnownIWAD(name))
1083 {
1084 that->game = StringDup(name);
1085 that->ok_but->activate();
1086 }
1087 else
1088 {
1089 that->game = NULL;
1090 that->ok_but->deactivate();
1091 }
1092
1093 that->PopulatePort();
1094 that->PopulateMapFormat();
1095 }
1096
1097
port_callback(Fl_Choice * w,void * data)1098 void UI_ProjectSetup::port_callback(Fl_Choice *w, void *data)
1099 {
1100 UI_ProjectSetup * that = (UI_ProjectSetup *)data;
1101
1102 const char * name = w->mvalue()->text;
1103
1104 that->port = StringDup(name);
1105
1106 that->PopulateMapFormat();
1107 }
1108
1109
format_callback(Fl_Choice * w,void * data)1110 void UI_ProjectSetup::format_callback(Fl_Choice *w, void *data)
1111 {
1112 UI_ProjectSetup * that = (UI_ProjectSetup *)data;
1113
1114 const char * fmt_str = w->mvalue()->text;
1115
1116 if (strstr(fmt_str, "UDMF"))
1117 that->map_format = MAPF_UDMF;
1118 else if (strstr(fmt_str, "Hexen"))
1119 that->map_format = MAPF_Hexen;
1120 else
1121 that->map_format = MAPF_Doom;
1122
1123 that->PopulateNamespaces();
1124 }
1125
1126
namespace_callback(Fl_Choice * w,void * data)1127 void UI_ProjectSetup::namespace_callback(Fl_Choice *w, void *data)
1128 {
1129 UI_ProjectSetup * that = (UI_ProjectSetup *)data;
1130
1131 that->name_space = w->mvalue()->text;
1132 }
1133
1134
find_callback(Fl_Button * w,void * data)1135 void UI_ProjectSetup::find_callback(Fl_Button *w, void *data)
1136 {
1137 UI_ProjectSetup * that = (UI_ProjectSetup *)data;
1138
1139 Fl_Native_File_Chooser chooser;
1140
1141 chooser.title("Pick file to open");
1142 chooser.type(Fl_Native_File_Chooser::BROWSE_FILE);
1143 chooser.filter("Wads\t*.wad");
1144 chooser.directory(Main_FileOpFolder());
1145
1146 switch (chooser.show())
1147 {
1148 case -1: // error
1149 DLG_Notify("Unable to open that wad:\n\n%s", chooser.errmsg());
1150 return;
1151
1152 case 1: // cancelled
1153 return;
1154
1155 default:
1156 break; // OK
1157 }
1158
1159 // check that a game definition exists
1160
1161 const char *game = GameNameFromIWAD(chooser.filename());
1162
1163 if (! M_CanLoadDefinitions("games", game))
1164 {
1165 DLG_Notify("That game is not supported (no definition file).\n\n"
1166 "Please try again.");
1167 return;
1168 }
1169
1170
1171 M_AddKnownIWAD(chooser.filename());
1172 M_SaveRecent();
1173
1174 that->game = StringDup(game);
1175
1176 that->PopulateIWADs();
1177 that->PopulatePort();
1178 that->PopulateMapFormat();
1179 }
1180
1181
1182 // m_testmap.cc
1183 extern bool M_PortSetupDialog(const char *port, const char *game);
1184
1185
setup_callback(Fl_Button * w,void * data)1186 void UI_ProjectSetup::setup_callback(Fl_Button *w, void *data)
1187 {
1188 UI_ProjectSetup * that = (UI_ProjectSetup *)data;
1189
1190 // FIXME : deactivate button when this is true
1191 if (that->game == NULL || that->port == NULL)
1192 {
1193 fl_beep();
1194 return;
1195 }
1196
1197 M_PortSetupDialog(that->port, that->game);
1198 }
1199
1200
load_callback(Fl_Button * w,void * data)1201 void UI_ProjectSetup::load_callback(Fl_Button *w, void *data)
1202 {
1203 int r = (int)(long)data;
1204 SYS_ASSERT(0 <= r && r < RES_NUM);
1205
1206 UI_ProjectSetup * that = _instance;
1207 SYS_ASSERT(that);
1208
1209 Fl_Native_File_Chooser chooser;
1210
1211 chooser.title("Pick file to open");
1212 chooser.type(Fl_Native_File_Chooser::BROWSE_FILE);
1213 chooser.filter("Wads\t*.wad\nEureka defs\t*.ugh");
1214 chooser.directory(Main_FileOpFolder());
1215
1216 switch (chooser.show())
1217 {
1218 case -1: // error
1219 DLG_Notify("Unable to open that wad:\n\n%s", chooser.errmsg());
1220 return;
1221
1222 case 1: // cancelled
1223 return;
1224
1225 default:
1226 break; // OK
1227 }
1228
1229 if (that->res[r])
1230 StringFree(that->res[r]);
1231
1232 that->res[r] = StringDup(chooser.filename());
1233
1234 that->res_name[r]->value(fl_filename_name(that->res[r]));
1235 }
1236
1237
kill_callback(Fl_Button * w,void * data)1238 void UI_ProjectSetup::kill_callback(Fl_Button *w, void *data)
1239 {
1240 int r = (int)(long)data;
1241 SYS_ASSERT(0 <= r && r < RES_NUM);
1242
1243 UI_ProjectSetup * that = _instance;
1244 SYS_ASSERT(that);
1245
1246 if (that->res[r])
1247 {
1248 StringFree(that->res[r]);
1249 that->res[r] = NULL;
1250
1251 that->res_name[r]->value("");
1252 }
1253 }
1254
1255
1256 //--- editor settings ---
1257 // vi:ts=4:sw=4:noexpandtab
1258