1 //------------------------------------------------------------------------
2 // FILES : Unix/FLTK File boxes
3 //------------------------------------------------------------------------
4 //
5 //  GL-Friendly Node Builder (C) 2000-2005 Andrew Apted
6 //
7 //  Based on 'BSP 2.3' by Colin Reed, Lee Killough and others.
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 // this includes everything we need
22 #include "local.h"
23 
24 
25 #define MY_GWA_COLOR  \
26     fl_color_cube((FL_NUM_RED-1)*3/4, 0, 0)
27 
28 
file_box_inout_CB(Fl_Widget * w,void * data)29 static void file_box_inout_CB(Fl_Widget *w, void *data)
30 {
31   guix_win->files->WriteInfo();
32 
33   // update output box in GWA mode
34   guix_win->files->InFileChanged();
35 }
36 
37 
file_in_browse_CB(Fl_Widget * w,void * data)38 static void file_in_browse_CB(Fl_Widget *w, void *data)
39 {
40   const char *name = fl_file_chooser("Select an input file",
41       "*.wad", guix_win->files->in_file->value());
42 
43   // cancelled ?
44   if (! name)
45     return;
46 
47   guix_win->files->in_file->value(name);
48   guix_win->files->in_file->redraw();
49   guix_win->files->WriteInfo();
50 
51   guix_win->files->InFileChanged();
52 }
53 
file_out_browse_CB(Fl_Widget * w,void * data)54 static void file_out_browse_CB(Fl_Widget *w, void *data)
55 {
56   const char *name = fl_file_chooser("Select an output file",
57       "*.wad", guix_win->files->out_file->value());
58 
59   if (! name)
60     return;
61 
62   guix_win->files->out_file->value(name);
63   guix_win->files->out_file->redraw();
64   guix_win->files->WriteInfo();
65 }
66 
file_out_guess_CB(Fl_Widget * w,void * data)67 static void file_out_guess_CB(Fl_Widget *w, void *data)
68 {
69   guix_win->files->out_file->value(
70       HelperGuessOutput(guix_win->files->in_file->value()));
71 
72   guix_win->files->out_file->redraw();
73   guix_win->files->WriteInfo();
74 }
75 
76 
77 //
78 // FileBox Constructor
79 //
Guix_FileBox(int x,int y,int w,int h)80 Guix_FileBox::Guix_FileBox(int x, int y, int w, int h) :
81     Fl_Group(x, y, w, h, "Files")
82 {
83   // cancel the automatic 'begin' in Fl_Group constructor
84   end();
85 
86   box(FL_THIN_UP_BOX);
87 
88   labelfont(FL_HELVETICA | FL_BOLD);
89   labeltype(FL_NORMAL_LABEL);
90   align(FL_ALIGN_INSIDE | FL_ALIGN_LEFT | FL_ALIGN_TOP);
91 
92   // create the file group -- serves as the resizable
93   int len = w - 62 - 178;
94 
95   file_group = new Fl_Group(x+62, y, len, h);
96   file_group->end();
97   add(file_group);
98 
99   resizable(file_group);
100 
101   // create input and output file widgets
102 
103   int CX = x;
104   int CY = y+18;
105 
106   in_label = new Fl_Box(CX, CY, 62, 26, "Input ");
107   in_label->align(FL_ALIGN_INSIDE | FL_ALIGN_RIGHT);
108   add(in_label);
109 
110   CY += 30;
111 
112   out_label = new Fl_Box(CX, CY, 62, 26, "Output ");
113   out_label->align(FL_ALIGN_INSIDE | FL_ALIGN_RIGHT);
114   add(out_label);
115 
116   CX = x+62;
117   CY = y+18;
118 
119   in_file = new Fl_Input(CX, CY, len, 26);
120   in_file->callback((Fl_Callback *) file_box_inout_CB);
121   in_file->when(FL_WHEN_CHANGED);
122   file_group->add(in_file);
123 
124   CY += 30;
125 
126   out_file = new Fl_Input(CX, CY, len, 26);
127   out_file->callback((Fl_Callback *) file_box_inout_CB);
128   out_file->when(FL_WHEN_CHANGED);
129   file_group->add(out_file);
130 
131   // this widget is normally hidden.  It occupies the same space as
132   // the out_file widget.  When GWA mode is selected, the normal
133   // output box is hidden and this one is shown instead.
134 
135   gwa_filename = GlbspStrDup("");
136   out_gwa_file = new Fl_Output(CX, CY, len, 26);
137   out_gwa_file->textcolor(MY_GWA_COLOR);
138   out_gwa_file->selection_color(FL_CYAN);
139   out_gwa_file->hide();
140   file_group->add(out_gwa_file);
141 
142   CX = x+70+len;
143   CY = y+18;
144 
145   in_browse = new Fl_Button(CX, CY, 80, 26, "Browse");
146   in_browse->align(FL_ALIGN_INSIDE);
147   in_browse->callback((Fl_Callback *) file_in_browse_CB, in_file);
148   add(in_browse);
149 
150   CY += 30;
151 
152   out_browse = new Fl_Button(CX, CY, 80, 26, "Browse");
153   out_browse->align(FL_ALIGN_INSIDE);
154   out_browse->callback((Fl_Callback *) file_out_browse_CB, out_file);
155   add(out_browse);
156 
157   CX += 86;
158 
159   out_guess = new Fl_Button(CX, CY, 70, 26, "Guess");
160   out_guess->align(FL_ALIGN_INSIDE);
161   out_guess->callback((Fl_Callback *) file_out_guess_CB, out_file);
162   add(out_guess);
163 
164   ReadInfo();
165 }
166 
167 //
168 // FileBox Destructor
169 //
~Guix_FileBox()170 Guix_FileBox::~Guix_FileBox()
171 {
172   WriteInfo();
173 
174   GlbspFree(gwa_filename);
175 }
176 
177 
ReadInfo()178 void Guix_FileBox::ReadInfo()
179 {
180   in_file->value(guix_info.input_file);
181   in_file->redraw();
182 
183   out_file->value(guix_info.output_file);
184   out_file->redraw();
185 
186   InFileChanged();
187   GWA_Changed();
188 }
189 
190 
WriteInfo()191 void Guix_FileBox::WriteInfo()
192 {
193   GlbspFree(guix_info.input_file);
194   guix_info.input_file = NULL;
195 
196   GlbspFree(guix_info.output_file);
197   guix_info.output_file = NULL;
198 
199   // treat "" the same as a NULL string ptr
200   if (in_file->value() && strlen(in_file->value()) > 0)
201     guix_info.input_file = GlbspStrDup(in_file->value());
202 
203   if (out_file->value() && strlen(out_file->value()) > 0)
204     guix_info.output_file = GlbspStrDup(out_file->value());
205 }
206 
207 
GWA_Changed()208 void Guix_FileBox::GWA_Changed()
209 {
210   if (guix_info.gwa_mode)
211   {
212     out_file->hide();
213     out_gwa_file->show();
214     out_browse->deactivate();
215     out_guess->deactivate();
216   }
217   else
218   {
219     out_gwa_file->hide();
220     out_file->show();
221     out_browse->activate();
222     out_guess->activate();
223   }
224 }
225 
226 
InFileChanged(void)227 void Guix_FileBox::InFileChanged(void)
228 {
229   GlbspFree(gwa_filename);
230 
231   gwa_filename = GlbspStrDup(
232       HelperReplaceExt(guix_info.input_file, "gwa"));
233 
234   out_gwa_file->value(gwa_filename);
235   out_gwa_file->redraw();
236 }
237 
238 
LockOut(boolean_g lock_it)239 void Guix_FileBox::LockOut(boolean_g lock_it)
240 {
241   if (lock_it)
242   {
243     in_file->set_output();
244     out_file->set_output();
245     out_gwa_file->set_output();
246 
247     in_browse->set_output();
248     out_browse->set_output();
249     out_guess->set_output();
250   }
251   else
252   {
253     in_file->clear_output();
254     out_file->clear_output();
255     out_gwa_file->clear_output();
256 
257     in_browse->clear_output();
258     out_browse->clear_output();
259     out_guess->clear_output();
260   }
261 }
262 
263 
264 //------------------------------------------------------------------------
265 
266 
267 //
268 // FactorBox Constructor
269 //
Guix_FactorBox(int x,int y,int w,int h)270 Guix_FactorBox::Guix_FactorBox(int x, int y, int w, int h) :
271     Fl_Group(x, y, w, h)
272 {
273   // cancel the automatic 'begin' in Fl_Group constructor
274   end();
275 
276   box(FL_THIN_UP_BOX);
277   resizable(0);  // no resizing the kiddies, please
278 
279   // create factor input box
280 
281   factor = new Fl_Counter(x+60, y+8, 100, 24, "Factor ");
282   factor->align(FL_ALIGN_LEFT);
283   factor->type(FL_SIMPLE_COUNTER);
284   factor->range(1, 32);
285   factor->step(1, 1);
286   add(factor);
287 
288   ReadInfo();
289 }
290 
291 //
292 // FactorBox Destructor
293 //
~Guix_FactorBox()294 Guix_FactorBox::~Guix_FactorBox()
295 {
296   WriteInfo();
297 }
298 
299 
ReadInfo()300 void Guix_FactorBox::ReadInfo()
301 {
302   factor->value(guix_info.factor);
303 }
304 
305 
WriteInfo()306 void Guix_FactorBox::WriteInfo()
307 {
308   guix_info.factor = (int) factor->value();
309 }
310 
311 
LockOut(boolean_g lock_it)312 void Guix_FactorBox::LockOut(boolean_g lock_it)
313 {
314   if (lock_it)
315     factor->set_output();
316   else
317     factor->clear_output();
318 }
319 
320 
321 //------------------------------------------------------------------------
322 
BuildValidateOptions(void)323 static boolean_g BuildValidateOptions(void)
324 {
325   // This routine checks for all manners of nasty input/output file
326   // problems.  Belt up, it's a bumpy ride !!
327 
328   int choice;
329   char buffer[1024];
330 
331   // a) Empty or invalid filenames
332 
333   if (!guix_info.input_file || guix_info.input_file[0] == 0)
334   {
335     DialogShowAndGetChoice(ALERT_TXT, skull_image,
336         "Please choose an Input filename.");
337     return FALSE;
338   }
339 
340   if (!guix_info.output_file || guix_info.output_file[0] == 0)
341   {
342     DialogShowAndGetChoice(ALERT_TXT, skull_image,
343         "Please choose an Output filename.");
344     return FALSE;
345   }
346 
347   if (! HelperFilenameValid(guix_info.input_file))
348   {
349     sprintf(buffer,
350         "Invalid Input filename:\n"
351         "\n"
352         "      %s\n"
353         "\n"
354         "Please check the filename and try again.",
355         guix_info.input_file);
356 
357     DialogShowAndGetChoice(ALERT_TXT, skull_image, buffer);
358     return FALSE;
359   }
360 
361   if (! HelperFilenameValid(guix_info.output_file))
362   {
363     sprintf(buffer,
364         "Invalid Output filename:\n"
365         "\n"
366         "      %s\n"
367         "\n"
368         "Please check the filename and try again.",
369         guix_info.output_file);
370 
371     DialogShowAndGetChoice(ALERT_TXT, skull_image, buffer);
372     return FALSE;
373   }
374 
375 
376   // b) Missing extensions
377 
378   if (! HelperHasExt(guix_info.input_file))
379   {
380     if (guix_prefs.lack_ext_warn)
381     {
382       sprintf(buffer,
383           "The Input file you selected has no extension.\n"
384           "\n"
385           "Do you want to add \".WAD\" and continue ?");
386 
387       choice = DialogShowAndGetChoice(ALERT_TXT, skull_image,
388           buffer, "OK", "Cancel");
389 
390       if (choice != 0)
391         return FALSE;
392     }
393 
394     char *new_input = HelperReplaceExt(guix_info.input_file, "wad");
395 
396     GlbspFree(guix_info.input_file);
397     guix_info.input_file = GlbspStrDup(new_input);
398 
399     guix_win->files->ReadInfo();
400   }
401 
402   if (! HelperHasExt(guix_info.output_file))
403   {
404     if (guix_prefs.lack_ext_warn)
405     {
406       sprintf(buffer,
407           "The Output file you selected has no extension.\n"
408           "\n"
409           "Do you want to add \".%s\" and continue ?",
410           guix_info.gwa_mode ? "GWA" : "WAD");
411 
412       choice = DialogShowAndGetChoice(ALERT_TXT, skull_image,
413           buffer, "OK", "Cancel");
414 
415       if (choice != 0)
416         return FALSE;
417     }
418 
419     char *new_output = HelperReplaceExt(guix_info.output_file,
420         guix_info.gwa_mode ? "gwa" : "wad");
421 
422     GlbspFree(guix_info.output_file);
423     guix_info.output_file = GlbspStrDup(new_output);
424 
425     guix_win->files->ReadInfo();
426   }
427 
428 
429   // c) No such input file
430 
431   if (! HelperFileExists(guix_info.input_file))
432   {
433     sprintf(buffer,
434         "Could not open the Input file:\n"
435         "\n"
436         "      %s\n"
437         "\n"
438         "Please check the filename and try again.",
439         guix_info.input_file);
440 
441     DialogShowAndGetChoice(ALERT_TXT, skull_image, buffer);
442     return FALSE;
443   }
444 
445 
446   // d) Use of the ".gwa" extension
447 
448   if (HelperCheckExt(guix_info.input_file, "gwa"))
449   {
450     DialogShowAndGetChoice(ALERT_TXT, skull_image,
451         "The Input file you selected has the GWA extension, "
452         "but GWA files do not contain any level data, so "
453         "there wouldn't be anything to build nodes for.\n"
454         "\n"
455         "Please choose another Input file.");
456     return FALSE;
457   }
458 
459   if (HelperCheckExt(guix_info.output_file, "gwa") &&
460       ! guix_info.gwa_mode)
461   {
462     choice = DialogShowAndGetChoice(ALERT_TXT, skull_image,
463         "The Output file you selected has the GWA extension, "
464         "but the GWA Mode option is not enabled.\n"
465         "\n"
466         "Do you want to enable GWA Mode and continue ?",
467         "OK", "Cancel");
468 
469     if (choice != 0)
470       return FALSE;
471 
472     guix_info.gwa_mode = TRUE;
473     guix_info.no_normal = FALSE;
474     guix_info.force_normal = FALSE;
475 
476     guix_win->build_mode->ReadInfo();
477     guix_win->files->GWA_Changed();
478     guix_win->misc_opts->GWA_Changed();
479   }
480 
481 
482   // e) Input == Output
483   // f) Output file already exists
484 
485   guix_info.load_all = FALSE;
486 
487   if (HelperCaseCmp(guix_info.input_file, guix_info.output_file) == 0)
488   {
489     if (guix_prefs.same_file_warn)
490     {
491       sprintf(buffer,
492           "Warning: Input and Output files are the same.\n"
493           "\n"
494           "This will use a lot more memory than normal, since the "
495           "whole input file must be loaded in.  On a low memory "
496           "machine, the node building may fail (especially if the "
497           "wad is very large, e.g. DOOM2.WAD).  There is also a "
498           "small risk: if something goes wrong during saving, your "
499           "wad file will be toast.\n"
500           "\n"
501           "Do you want to continue ?");
502 
503       choice = DialogShowAndGetChoice(ALERT_TXT, skull_image,
504           buffer, "OK", "Cancel");
505 
506       if (choice != 0)
507         return FALSE;
508     }
509 
510     guix_info.load_all = TRUE;
511   }
512   else if (guix_prefs.overwrite_warn &&
513       HelperFileExists(guix_info.output_file))
514   {
515     sprintf(buffer,
516         "Warning: the chosen Output file already exists:\n"
517         "\n"
518         "      %s\n"
519         "\n"
520         "Do you want to overwrite it ?",
521         guix_info.output_file);
522 
523     choice = DialogShowAndGetChoice(ALERT_TXT, skull_image,
524         buffer, "OK", "Cancel");
525 
526     if (choice != 0)
527       return FALSE;
528   }
529 
530   return TRUE;
531 }
532 
BuildCheckInfo(void)533 static boolean_g BuildCheckInfo(void)
534 {
535   char buffer[1024];
536 
537   for (;;)
538   {
539     glbsp_ret_e ret = GlbspCheckInfo(&guix_info, &guix_comms);
540 
541     if (ret == GLBSP_E_OK)
542       return TRUE;
543 
544     if (ret != GLBSP_E_BadInfoFixed)
545     {
546       sprintf(buffer,
547           "The following problem was detected with the current "
548           "node building options:\n"
549           "\n"
550           "      %s\n"
551           "\n"
552           "Please fix the problem and try again.",
553           guix_comms.message ? guix_comms.message : MISSING_COMMS);
554 
555       // user doesn't have any real choice here
556       DialogShowAndGetChoice(ALERT_TXT, skull_image, buffer);
557 
558       guix_win->ReadAllInfo();
559       break;
560     }
561 
562     sprintf(buffer,
563         "The following problem was detected with the current "
564         "node building options:\n"
565         "\n"
566         "      %s\n"
567         "\n"
568         "However, the option causing the problem has now been "
569         "changed into something that should work.  "
570         "Do you want to continue ?",
571         guix_comms.message ? guix_comms.message : MISSING_COMMS);
572 
573     int choice = DialogShowAndGetChoice(ALERT_TXT, skull_image,
574         buffer, "OK", "Cancel");
575 
576     if (choice != 0)
577       break;
578   }
579 
580   return FALSE;
581 }
582 
BuildDoBuild(void)583 static void BuildDoBuild(void)
584 {
585   glbsp_ret_e ret = GlbspBuildNodes(&guix_info, &guix_funcs, &guix_comms);
586 
587   if (ret == GLBSP_E_OK)
588     return;
589 
590   guix_win->progress->ClearBars();
591 
592   if (ret == GLBSP_E_Cancelled)
593   {
594     guix_win->text_box->AddMsg("\n*** Cancelled ***\n", FL_BLUE, TRUE);
595     return;
596   }
597 
598   // something went wrong :(
599 
600   char err_kind = '?';
601 
602   switch (ret)
603   {
604     case GLBSP_E_ReadError:
605       guix_win->text_box->AddMsg("\n*** Read Error ***\n", FL_BLUE, TRUE);
606       err_kind = 'r';
607       break;
608 
609     case GLBSP_E_WriteError:
610       guix_win->text_box->AddMsg("\n*** Write Error ***\n", FL_BLUE, TRUE);
611       err_kind = 'w';
612       break;
613 
614     // these two shouldn't happen
615     case GLBSP_E_BadArgs:
616     case GLBSP_E_BadInfoFixed:
617       guix_win->text_box->AddMsg("\n*** Option Error ***\n", FL_BLUE, TRUE);
618       err_kind = 'o';
619       break;
620 
621     case GLBSP_E_Unknown:
622     default:
623       guix_win->text_box->AddMsg("\n*** Error ***\n", FL_BLUE, TRUE);
624       break;
625   }
626 
627   char buffer[1024];
628 
629   sprintf(buffer,
630       "The following problem was encountered when trying to "
631       "build the nodes:\n"
632       "\n"
633       "      %s\n",
634       guix_comms.message ? guix_comms.message : MISSING_COMMS);
635 
636   if (err_kind == 'r')
637   {
638     strcat(buffer, "\nCheck that the Input file is a valid WAD "
639         "file, and it is not corrupted.");
640   }
641   else if (err_kind == 'w')
642   {
643     strcat(buffer, "\nCheck that your hard disk (or floppy disk, etc) "
644         "has not run out of storage space.");
645   }
646 
647   DialogShowAndGetChoice(ALERT_TXT, pldie_image, buffer);
648 }
649 
builder_build_CB(Fl_Widget * w,void * data)650 static void builder_build_CB(Fl_Widget *w, void *data)
651 {
652   // disable most of the interface, especially the BUILD button itself
653   // (this routine is NOT reentrant !).
654   guix_win->LockOut(TRUE);
655 
656   // make sure info is up-to-date
657   guix_win->WriteAllInfo();
658 
659   // save cookies, in case the build crashes or calls the fatal-error
660   // function.
661   CookieWriteAll();
662 
663   // sleight of hand for GWA mode: we remember the old output name in
664   // the nodebuildinfo and replace it with the gwa name.  The memory
665   // stuff is messy, since we can't be 100% sure that 'output_file'
666   // field won't be freed and assigned a new value by the main code.
667 
668   const char *old_output = guix_info.output_file;
669   boolean_g gwa_hack = FALSE;
670 
671   if (guix_info.gwa_mode)
672   {
673     guix_info.output_file = GlbspStrDup(guix_win->files->gwa_filename);
674     gwa_hack = TRUE;
675   }
676 
677 #if 0  // DEBUG
678   fprintf(stderr, "BUILD\n  INPUT = [%s]\n  OUTPUT = [%s]\n\n",
679       guix_info.input_file, guix_info.output_file);
680 #endif
681 
682   if (BuildValidateOptions())
683   {
684     if (BuildCheckInfo())
685     {
686       BuildDoBuild();
687       guix_win->text_box->AddHorizBar();
688     }
689   }
690 
691   if (gwa_hack)
692   {
693     GlbspFree(guix_info.output_file);
694     guix_info.output_file = old_output;
695   }
696 
697   // if the build info changed, make sure widgets are in sync.
698   // This is not the ideal place, it would be better to call this as
699   // soon as any change could've happened -- but the GWA hack
700   // interferes with that approach.
701   //
702   guix_win->ReadAllInfo();
703 
704   GlbspFree(guix_comms.message);
705   guix_comms.message = NULL;
706 
707   // restore user interface to normal
708   guix_win->LockOut(FALSE);
709   guix_win->progress->ClearBars();
710 }
711 
builder_cancel_CB(Fl_Widget * w,void * data)712 static void builder_cancel_CB(Fl_Widget *w, void *data)
713 {
714   guix_comms.cancelled = TRUE;
715 }
716 
717 
718 //
719 // BuildButton Constructor
720 //
Guix_BuildButton(int x,int y,int w,int h)721 Guix_BuildButton::Guix_BuildButton(int x, int y, int w, int h) :
722     Fl_Group(x, y, w, h)
723 {
724   end();  // turn off auto-add-widgets
725 
726   resizable(0);  // no resizing the kiddies, please
727 
728   // create button sitting in a space of its own
729 
730   build = new Fl_Button(x+10, y+10, 90, 30, "Build");
731   build->box(FL_ROUND_UP_BOX);
732   build->align(FL_ALIGN_INSIDE);
733   build->callback((Fl_Callback *) builder_build_CB);
734   add(build);
735 
736   stopper = new Fl_Button(x+140, y+10, 90, 30, "Stop");
737   stopper->box(FL_ROUND_UP_BOX);
738   stopper->align(FL_ALIGN_INSIDE);
739   stopper->callback((Fl_Callback *) builder_cancel_CB);
740   stopper->shortcut(FL_Escape);
741   add(stopper);
742 }
743 
744 //
745 // BuildButton Destructor
746 //
~Guix_BuildButton()747 Guix_BuildButton::~Guix_BuildButton()
748 {
749   // nothing to do
750 }
751 
752 
LockOut(boolean_g lock_it)753 void Guix_BuildButton::LockOut(boolean_g lock_it)
754 {
755   if (lock_it)
756     build->set_output();
757   else
758     build->clear_output();
759 }
760 
761