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