Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Portions of this file are copyright Rebirth contributors and licensed as
  3.  * described in COPYING.txt.
  4.  * Portions of this file are copyright Parallax Software and licensed
  5.  * according to the Parallax license below.
  6.  * See COPYING.txt for license details.
  7.  
  8. THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
  9. SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
  10. END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
  11. ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
  12. IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
  13. SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
  14. FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
  15. CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
  16. AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
  17. COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
  18. */
  19.  
  20. #include <stdlib.h>
  21. #include <string.h>
  22.  
  23. #include "event.h"
  24. #include "maths.h"
  25. #include "pstypes.h"
  26. #include "gr.h"
  27. #include "key.h"
  28. #include "mouse.h"
  29. #include "strutil.h"
  30. #include "ui.h"
  31. #include "window.h"
  32. #include "u_mem.h"
  33. #include "physfsx.h"
  34. #include "physfs_list.h"
  35.  
  36. #include "compiler-range_for.h"
  37. #include "d_range.h"
  38. #include <memory>
  39.  
  40. namespace dcx {
  41.  
  42. bool isDirectory(const char *fname) // Pierre-Marie Baty -- work around PHYSFS_isDirectory() deprecation
  43. {
  44.     PHYSFS_Stat statbuf;
  45.     if (!PHYSFS_stat(fname, &statbuf)) return false;
  46.     return (statbuf.filetype == PHYSFS_FILETYPE_DIRECTORY);
  47. }
  48.  
  49. static PHYSFSX_counted_list file_getdirlist(const char *dir)
  50. {
  51.         ntstring<PATH_MAX - 1> path;
  52.         auto dlen = path.copy_if(dir);
  53.         if ((!dlen && dir[0] != '\0') || !path.copy_if(dlen, "/"))
  54.                 return nullptr;
  55.         ++ dlen;
  56.         PHYSFSX_counted_list list{PHYSFS_enumerateFiles(dir)};
  57.         if (!list)
  58.                 return nullptr;
  59.         const auto predicate = [&](char *i) -> bool {
  60.                 if (path.copy_if(dlen, i) && /*PHYSFS_*/isDirectory(path)) // Pierre-Marie Baty -- work around PHYSFS_isDirectory() deprecation
  61.                         return false;
  62.                 free(i);
  63.                 return true;
  64.         };
  65.         auto j = std::remove_if(list.begin(), list.end(), predicate);
  66.         *j = NULL;
  67.         auto NumDirs = j.get() - list.get();
  68.         qsort(list.get(), NumDirs, sizeof(char *), string_array_sort_func);
  69.         if (*dir)
  70.         {
  71.                 // Put the 'go to parent directory' sequence '..' first
  72.                 ++NumDirs;
  73.                 auto r = reinterpret_cast<char **>(realloc(list.get(), sizeof(char *)*(NumDirs + 1)));
  74.                 if (!r)
  75.                         return list;
  76.                 list.release();
  77.                 list.reset(r);
  78.                 std::move_backward(r, r + NumDirs, r + NumDirs + 1);
  79.                 list[0] = d_strdup("..");
  80.         }
  81.         list.set_count(NumDirs);
  82.         return list;
  83. }
  84.  
  85. static PHYSFSX_counted_list file_getfilelist(const char *filespec, const char *dir)
  86. {
  87.         PHYSFSX_counted_list list{PHYSFS_enumerateFiles(dir)};
  88.         if (!list)
  89.                 return nullptr;
  90.  
  91.         if (*filespec == '*')
  92.                 filespec++;
  93.  
  94.         const auto predicate = [&](char *i) -> bool {
  95.                 auto ext = strrchr(i, '.');
  96.                 if (ext && (!d_stricmp(ext, filespec)))
  97.                         return false;
  98.                 free(i);
  99.                 return true;
  100.         };
  101.         auto j = std::remove_if(list.begin(), list.end(), predicate);
  102.         *j = NULL;
  103.         auto NumFiles = j.get() - list.get();
  104.         list.set_count(NumFiles);
  105.         qsort(list.get(), NumFiles, sizeof(char *), string_array_sort_func);
  106.         return list;
  107. }
  108.  
  109. namespace {
  110.  
  111. struct ui_file_browser
  112. {
  113.         char            *filename;
  114.         const char              *filespec;
  115.         const char              *message;
  116.         PHYSFSX_counted_list filename_list, directory_list;
  117.         std::unique_ptr<UI_GADGET_BUTTON> button1, button2, help_button;
  118.         std::unique_ptr<UI_GADGET_LISTBOX> listbox1, listbox2;
  119.         std::unique_ptr<UI_GADGET_INPUTBOX> user_file;
  120.         std::array<char, 35> spaces;
  121.         char            view_dir[PATH_MAX];
  122. };
  123.  
  124. }
  125.  
  126. static window_event_result browser_handler(UI_DIALOG *const dlg, const d_event &event, ui_file_browser *const b)
  127. {
  128.         window_event_result rval = window_event_result::ignored;
  129.  
  130.         if (event.type == EVENT_UI_DIALOG_DRAW)
  131.         {
  132.                 ui_dputs_at( dlg, 10, 5, b->message );
  133.  
  134.                 ui_dprintf_at( dlg, 20, 32,"N&ame" );
  135.                 ui_dprintf_at( dlg, 20, 86,"&Files" );
  136.                 ui_dprintf_at( dlg, 210, 86,"&Dirs" );
  137.                
  138.                 ui_dputs_at(dlg, 20, 60, b->spaces.data());
  139.                 ui_dputs_at( dlg, 20, 60, b->view_dir );
  140.                
  141.                 return window_event_result::handled;
  142.         }
  143.  
  144.         if (GADGET_PRESSED(b->button2.get()))
  145.         {
  146.                 b->filename_list.reset();
  147.                 b->directory_list.reset();
  148.                 return window_event_result::close;
  149.         }
  150.        
  151.         if (GADGET_PRESSED(b->help_button.get()))
  152.         {
  153.                 ui_messagebox( -1, -1, 1, "Sorry, no help is available!", "Ok" );
  154.                 rval = window_event_result::handled;
  155.         }
  156.        
  157.         if (event.type == EVENT_UI_LISTBOX_MOVED)
  158.         {
  159.                 if ((ui_event_get_gadget(event) == b->listbox1.get()) && (b->listbox1->current_item >= 0) && b->filename_list[b->listbox1->current_item])
  160.                         ui_inputbox_set_text(b->user_file.get(), b->filename_list[b->listbox1->current_item]);
  161.  
  162.                 if ((ui_event_get_gadget(event) == b->listbox2.get()) && (b->listbox2->current_item >= 0) && b->directory_list[b->listbox2->current_item])
  163.                         ui_inputbox_set_text(b->user_file.get(), b->directory_list[b->listbox2->current_item]);
  164.  
  165.                 rval = window_event_result::handled;
  166.         }
  167.        
  168.         if (GADGET_PRESSED(b->button1.get()) || GADGET_PRESSED(b->user_file.get()) || event.type == EVENT_UI_LISTBOX_SELECTED)
  169.         {
  170.                 char *p;
  171.                
  172.                 if (ui_event_get_gadget(event) == b->listbox2.get())
  173.                         strcpy(b->user_file->text.get(), b->directory_list[b->listbox2->current_item]);
  174.                
  175.                 strncpy(b->filename, b->view_dir, PATH_MAX);
  176.                
  177.                 p = b->user_file->text.get();
  178.                 while (!strncmp(p, "..", 2))    // shorten the path manually
  179.                 {
  180.                         char *sep = strrchr(b->filename, '/');
  181.                         if (sep)
  182.                                 *sep = 0;
  183.                         else
  184.                                 *b->filename = 0;       // look directly in search paths
  185.                        
  186.                         p += 2;
  187.                         if (*p == '/')
  188.                                 p++;
  189.                 }
  190.                
  191.                 if (*b->filename && *p)
  192.                         strncat(b->filename, "/", PATH_MAX - strlen(b->filename));
  193.                 strncat(b->filename, p, PATH_MAX - strlen(b->filename));
  194.                
  195.                 if (!/*PHYSFS_*/isDirectory(b->filename)) // Pierre-Marie Baty -- work around PHYSFS_isDirectory() deprecation
  196.                 {
  197.                         if (RAIIPHYSFS_File{PHYSFS_openRead(b->filename)})
  198.                         {
  199.                                 // Looks like a valid filename that already exists!
  200.                                 return window_event_result::close;
  201.                         }
  202.                        
  203.                         // File doesn't exist, but can we create it?
  204.                         if (RAIIPHYSFS_File TempFile{PHYSFS_openWrite(b->filename)})
  205.                         {
  206.                                 TempFile.reset();
  207.                                 // Looks like a valid filename!
  208.                                 PHYSFS_delete(b->filename);
  209.                                 return window_event_result::close;
  210.                         }
  211.                 }
  212.                 else
  213.                 {
  214.                         if (b->filename[strlen(b->filename) - 1] == '/')        // user typed a separator on the end
  215.                                 b->filename[strlen(b->filename) - 1] = 0;
  216.                        
  217.                         strcpy(b->view_dir, b->filename);
  218.                         b->filename_list = file_getfilelist(b->filespec, b->view_dir);
  219.                         if (!b->filename_list)
  220.                         {
  221.                                 b->directory_list.reset();
  222.                                 return window_event_result::close;
  223.                         }
  224.                        
  225.                         ui_inputbox_set_text(b->user_file.get(), b->filespec);
  226.                         b->directory_list = file_getdirlist(b->view_dir);
  227.                         if (!b->directory_list)
  228.                         {
  229.                                 b->filename_list.reset();
  230.                                 return window_event_result::close;
  231.                         }
  232.                        
  233.                         ui_listbox_change(dlg, b->listbox1.get(), b->filename_list.get_count(), b->filename_list.get());
  234.                         ui_listbox_change(dlg, b->listbox2.get(), b->directory_list.get_count(), b->directory_list.get());
  235.                        
  236.                         //i = TICKER;
  237.                         //while ( TICKER < i+2 );
  238.                        
  239.                 }
  240.                
  241.                 rval = window_event_result::handled;
  242.         }
  243.        
  244.         return rval;
  245. }
  246.  
  247. int ui_get_filename(char (&filename)[PATH_MAX], const char *const filespec, const char *const message)
  248. {
  249.         char            InputText[PATH_MAX];
  250.         char            *p;
  251.         UI_DIALOG       *dlg;
  252.         int                     rval = 0;
  253.         auto b = std::make_unique<ui_file_browser>();
  254.         if ((p = strrchr(filename, '/')))
  255.         {
  256.                 *p++ = 0;
  257.                 strcpy(b->view_dir, filename);
  258.                 strcpy(InputText, p);
  259.         }
  260.         else
  261.         {
  262.                 strcpy(b->view_dir, "");
  263.                 strcpy(InputText, filename);
  264.         }
  265.  
  266.         b->filename_list = file_getfilelist(filespec, b->view_dir);
  267.         if (!b->filename_list)
  268.         {
  269.                 return 0;
  270.         }
  271.        
  272.         b->directory_list = file_getdirlist(b->view_dir);
  273.         if (!b->directory_list)
  274.         {
  275.                 b->filename_list.reset();
  276.                 return 0;
  277.         }
  278.  
  279.         //ui_messagebox( -2,-2, 1,"DEBUG:0", "Ok" );
  280.         range_for (const int i, xrange(35u))
  281.                 b->spaces[i] = ' ';
  282.         b->spaces[34] = 0;
  283.  
  284.         dlg = ui_create_dialog( 200, 100, 400, 370, static_cast<dialog_flags>(DF_DIALOG | DF_MODAL), browser_handler, b.get());
  285.  
  286.         b->user_file  = ui_add_gadget_inputbox<40>(dlg, 60, 30, InputText);
  287.  
  288.         b->listbox1 = ui_add_gadget_listbox(dlg,  20, 110, 125, 200, b->filename_list.get_count(), b->filename_list.get());
  289.         b->listbox2 = ui_add_gadget_listbox(dlg, 210, 110, 100, 200, b->directory_list.get_count(), b->directory_list.get());
  290.  
  291.         b->button1 = ui_add_gadget_button( dlg,     20, 330, 60, 25, "Ok", NULL );
  292.         b->button2 = ui_add_gadget_button( dlg,    100, 330, 60, 25, "Cancel", NULL );
  293.         b->help_button = ui_add_gadget_button( dlg, 180, 330, 60, 25, "Help", NULL );
  294.  
  295.         dlg->keyboard_focus_gadget = b->user_file.get();
  296.  
  297.         b->button1->hotkey = KEY_CTRLED + KEY_ENTER;
  298.         b->button2->hotkey = KEY_ESC;
  299.         b->help_button->hotkey = KEY_F1;
  300.         b->listbox1->hotkey = KEY_ALTED + KEY_F;
  301.         b->listbox2->hotkey = KEY_ALTED + KEY_D;
  302.         b->user_file->hotkey = KEY_ALTED + KEY_A;
  303.  
  304.         ui_gadget_calc_keys(dlg);
  305.  
  306.         b->filename = filename;
  307.         b->filespec = filespec;
  308.         b->message = message;
  309.        
  310.         event_process_all();
  311.  
  312.         //key_flush();
  313.  
  314.         rval = static_cast<bool>(b->filename_list);
  315.         b->filename_list.reset();
  316.         b->directory_list.reset();
  317.         return rval;
  318. }
  319.  
  320. int ui_get_file( char * filename, const char * Filespec  )
  321. {
  322.         int x;
  323.         auto list = file_getfilelist(Filespec, "");
  324.         if (!list) return 0;
  325.         x = MenuX(-1, -1, list.get_count(), list.get());
  326.         if (x > 0)
  327.                 strcpy(filename, list[x - 1]);
  328.         return (x > 0);
  329. }
  330.  
  331. }
  332.