The Battle for Wesnoth  1.17.23+dev
file_dialog.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2011 - 2023
3  by Iris Morelle <shadowm2006@gmail.com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #define GETTEXT_DOMAIN "wesnoth-lib"
17 
19 
20 #include "cursor.hpp"
21 #include "desktop/paths.hpp"
22 #include "filesystem.hpp"
23 #include "formula/string_utils.hpp"
26 #include "gui/dialogs/message.hpp"
28 #include "gui/widgets/button.hpp"
29 #include "gui/widgets/listbox.hpp"
30 #include "gui/widgets/settings.hpp"
31 #include "gui/widgets/text_box.hpp"
33 #include "gui/widgets/window.hpp"
34 #include "gettext.hpp"
35 #include "log.hpp"
37 
38 #include <functional>
39 
40 static lg::log_domain log_filedlg{"gui/dialogs/file_dialog"};
41 #define ERR_FILEDLG LOG_STREAM(err, log_filedlg)
42 #define WRN_FILEDLG LOG_STREAM(warn, log_filedlg)
43 #define LOG_FILEDLG LOG_STREAM(info, log_filedlg)
44 #define DBG_FILEDLG LOG_STREAM(debug, log_filedlg)
45 
46 namespace fs = filesystem;
47 
48 namespace
49 {
50 const std::string icon_dir = "misc/folder-icon.png";
51 // Empty icons with the same size as the above to force the icon column to have a
52 // specific size even when there are no folders in the list.
53 const std::string icon_file = icon_dir + "~O(0)";
54 const std::string icon_parent = icon_dir + "~O(0)";
55 // NOTE: Does not need to be the same as PARENT_DIR! Use PARENT_DIR to build
56 // relative paths for non-presentational purposes instead.
57 const std::string label_parent = "..";
58 
59 const std::string CURRENT_DIR = ".";
60 const std::string PARENT_DIR = "..";
61 
62 const int FILE_DIALOG_ITEM_RETVAL = 9001;
63 const int FILE_DIALOG_MAX_ENTRY_LENGTH = 42;
64 
65 inline std::string concat_path(const std::string& a, const std::string& b)
66 {
67  //
68  // As of Boost 1.61, normalize_path() displays unusual behavior when passing
69  // it paths with extra path separators (e.g. //opt becomes
70  // //opt/home/shadowm/src/wesnoth, where the extraneous sequence is probably
71  // the current working dir), so avoid leaving those around.
72  //
73  // TODO: Maybe handle this corner case in filesystem::normalize_path()
74  // instead, really.
75  //
76  if((a.empty() || !fs::is_path_sep(a.back())) && (b.empty() || !fs::is_path_sep(b.front()))) {
77  return a + fs::path_separator() + b;
78  } else {
79  return a + b;
80  }
81 }
82 
83 inline std::string filesystem_root()
84 {
85  // TODO: Multiple drives support (may require cooperation from the caller).
86  return std::string(1, fs::path_separator());
87 }
88 
89 inline void isort_dir_entries(std::vector<std::string>& entries)
90 {
91  // Yes, this uses Wesnoth's locale and not the filesystem/OS locale. Yes, this
92  // isn't ideal. No, we don't really need to worry about it. It's just a
93  // cosmetic procedure anyway.
94  std::sort(entries.begin(), entries.end(),
95  [](const std::string& a, const std::string& b) { return translation::icompare(a, b) < 0; });
96 }
97 
98 } // unnamed namespace
99 
100 namespace gui2::dialogs
101 {
102 
103 REGISTER_DIALOG(file_dialog)
104 
106  : modal_dialog(window_id())
107  , title_(_("Find File"))
108  , msg_()
109  , ok_label_()
110  , extension_()
111  , current_entry_()
112  , current_dir_()
113  , read_only_(false)
114  , save_mode_(false)
115  , dir_files_()
116  , dir_subdirs_()
117  , bookmark_paths_()
118  , current_bookmark_()
119  , user_bookmarks_begin_()
120  , extra_paths_()
121 {
122 }
123 
124 std::string file_dialog::path() const
125 {
126  const std::string& dir_norm = fs::normalize_path(current_dir_, true);
127 
128  if(current_entry_.empty() || current_entry_ == CURRENT_DIR) {
129  return dir_norm;
130  } else if(current_entry_ == PARENT_DIR) {
131  return fs::directory_name(dir_norm);
132  }
133 
134  return concat_path(dir_norm, current_entry_);
135 }
136 
137 file_dialog& file_dialog::set_path(const std::string& value)
138 {
139  if(value.empty()) {
140  current_dir_ = filesystem_root();
141  }
142 
143  const std::string& norm = fs::normalize_path(value, true);
144 
145  if(fs::is_directory(norm)) {
146  current_dir_ = norm;
147  } else {
149  if(current_dir_.empty()) {
150  current_dir_ = filesystem_root();
151  }
152  // The file may or may not exist. We'll find out eventually when setting up
153  // the dialog.
155  }
156 
157  return *this;
158 }
159 
160 file_dialog& file_dialog::set_filename(const std::string& value)
161 {
162  current_entry_ = value;
163 
164  return *this;
165 }
166 
168 {
169  styled_widget& title = find_widget<styled_widget>(&window, "title", false);
170  styled_widget& message = find_widget<styled_widget>(&window, "message", false);
171  styled_widget& ok = find_widget<styled_widget>(&window, "ok", false);
172 
173  title.set_label(title_);
174 
175  if(msg_.empty()) {
177  } else {
179  message.set_use_markup(true);
180  }
181 
182  if(ok_label_.empty()) {
183  ok.set_label(save_mode_ ? _("Save") : _("Open"));
184  } else {
185  ok.set_label(ok_label_);
186  }
187 
188  listbox& bookmarks_bar = find_widget<listbox>(&window, "bookmarks", false);
189 
190  find_widget<styled_widget>(&window, "current_dir", false).set_text_ellipse_mode(PANGO_ELLIPSIZE_START);
191 
192  //
193  // Push hard-coded bookmarks.
194  //
195 
198  std::vector<desktop::path_info> bookmarks = desktop::game_paths(extra_paths_);
200  bookmarks.insert(bookmarks.end(), sys_paths.begin(), sys_paths.end());
201 
202  bookmark_paths_.clear();
204 
206 
207  for(const auto& pinfo : bookmarks) {
208  bookmark_paths_.push_back(pinfo.path);
209  data["bookmark"]["label"] = pinfo.display_name();
210  bookmarks_bar.add_row(data);
211  }
212 
213  //
214  // Push user-defined bookmarks.
215  //
216 
217  const std::vector<desktop::bookmark_info>& user_bookmarks = desktop::user_bookmarks();
218 
219  if(!user_bookmarks.empty()) {
221  }
222 
223  for(const auto& bookmark : user_bookmarks) {
224  bookmark_paths_.push_back(bookmark.path);
225  data["bookmark"]["label"] = bookmark.label;
226  bookmarks_bar.add_row(data);
227  }
228 
230 
231  listbox& filelist = find_widget<listbox>(&window, "filelist", false);
232 
234  std::bind(&file_dialog::on_row_selected, this));
235  connect_signal_notify_modified(bookmarks_bar,
236  std::bind(&file_dialog::on_bookmark_selected, this));
237 
238  button& mkdir_button = find_widget<button>(&window, "new_dir", false);
239  button& rm_button = find_widget<button>(&window, "delete_file", false);
240  button& bookmark_add_button = find_widget<button>(&window, "add_bookmark", false);
241  button& bookmark_del_button = find_widget<button>(&window, "remove_bookmark", false);
242 
243  connect_signal_mouse_left_click(mkdir_button,
244  std::bind(&file_dialog::on_dir_create_cmd, this));
246  std::bind(&file_dialog::on_file_delete_cmd, this));
247  connect_signal_mouse_left_click(bookmark_add_button,
248  std::bind(&file_dialog::on_bookmark_add_cmd, this));
249  connect_signal_mouse_left_click(bookmark_del_button,
250  std::bind(&file_dialog::on_bookmark_del_cmd, this));
251 
252  if(read_only_) {
253  mkdir_button.set_active(false);
254  rm_button.set_active(false);
255 
258  }
259 
261 
262  window.keyboard_capture(find_widget<text_box>(&window, "filename", false, true));
263  window.add_to_keyboard_chain(&filelist);
264  window.set_exit_hook(window::exit_hook::on_all, std::bind(&file_dialog::on_exit, this, std::placeholders::_1));
265 }
266 
268 {
269  if(window.get_retval() == FILE_DIALOG_ITEM_RETVAL) {
270  // Attempting to exit by double clicking items -- only proceeds if the item
271  // was a file.
273  window.set_retval(retval::OK, false);
274  return true;
275  } else {
276  return false;
277  }
278  }
279 
280  if(window.get_retval() == retval::OK) {
281  // Attempting to exit by pressing Enter/clicking OK -- only proceeds if the
282  // textbox was not altered by the user to point to a different directory.
283  return process_textbox_submit();
284  }
285 
286  return true;
287 }
288 
290 {
291  // TODO: Adapt for implementing directory selection mode.
292  return save_mode_
293  ? stype != SELECTION_IS_DIR && stype != SELECTION_PARENT_NOT_FOUND
294  : stype == SELECTION_IS_FILE;
295 }
296 
298 {
299  // TODO: Adapt for implementing directory selection mode.
300  if(stype != SELECTION_IS_FILE) {
301  return true;
302  }
303 
304  const std::string& message
305  = _("The file already exists. Do you wish to overwrite it?");
307 }
308 
309 bool file_dialog::process_submit_common(const std::string& name)
310 {
311  const auto stype = register_new_selection(name);
312 
313  //DBG_FILEDLG << "current_dir_=" << current_dir_ << " current_entry_=" << current_entry_;
314 
315  if(is_selection_type_acceptable(stype)) {
316  return save_mode_ ? confirm_overwrite(stype) : true;
317  }
318 
319  switch(stype) {
320  case SELECTION_IS_DIR:
321  // TODO: Adapt for implementing directory selection mode.
324  break;
326  // We get here in save mode or not. Use the file creation language only in
327  // save mode.
328  if(save_mode_) {
329  show_transient_error_message(VGETTEXT("The file or folder $path cannot be created.", {{"path", name}}));
330  break;
331  }
332  [[fallthrough]];
333  case SELECTION_NOT_FOUND:
334  // We only get here if we aren't in save mode.
335  show_transient_error_message(VGETTEXT("The file or folder $path does not exist.", {{"path", name}}));
336  break;
337  case SELECTION_IS_FILE:
338  // TODO: Adapt for implementing directory selection mode.
339  default:
340  assert(false && "Unimplemented selection mode or semantics");
341  }
342 
343  return false;
344 }
345 
347 {
348  listbox& filelist = find_widget<listbox>(get_window(), "filelist", false);
349  const std::string& selected_name = get_filelist_selection(filelist);
350  return process_submit_common(selected_name);
351 }
352 
354 {
355  text_box& file_textbox = find_widget<text_box>(get_window(), "filename", false);
356  const std::string& input_name = file_textbox.get_value();
357  return !input_name.empty() && process_submit_common(input_name);
358 }
359 
361 {
362  const int row = filelist.get_selected_row();
363 
364  if(row == -1) {
365  // Shouldn't happen...
366  return "";
367  }
368 
369  const bool i_am_root = fs::is_root(current_dir_);
370 
371  if(row == 0 && !i_am_root) {
372  return PARENT_DIR;
373  } else {
374  std::size_t n = i_am_root ? row : row - 1;
375 
376  if(n < dir_subdirs_.size()) {
377  return dir_subdirs_[n];
378  } else {
379  n -= dir_subdirs_.size();
380 
381  if(n < dir_files_.size()) {
382  return dir_files_[n];
383  } else {
384  assert(false && "File list selection is out of range!");
385  }
386  }
387  }
388 
389  return "";
390 }
391 
393 {
394  std::string new_path, new_parent;
395 
396  if(fs::is_relative(name)) {
397  // On Windows, \ represents a path relative to the root of the process'
398  // current working drive specified by the current working dir, so we get
399  // here. This makes it the only platform where is_relative() and is_root()
400  // aren't mutually exclusive.
401  if(fs::is_root(name)) {
402  DBG_FILEDLG << "register_new_selection(): new selection '" << name << "' is relative to a root resource";
403  // Using the browsed dir's root drive instead of the cwd's makes the most
404  // sense for users.
405  new_parent = fs::root_name(current_dir_);
406  new_path = fs::normalize_path(concat_path(new_parent, name), true, true);
407  } else {
408  DBG_FILEDLG << "register_new_selection(): new selection '" << name << "' seems relative";
409  new_parent = current_dir_;
410  new_path = fs::normalize_path(concat_path(current_dir_, name), true, true);
411  }
412  } else {
413  DBG_FILEDLG << "register_new_selection(): new selection '" << name << "' seems absolute";
414  new_parent = fs::directory_name(name);
415  new_path = fs::normalize_path(name, true, true);
416  DBG_FILEDLG << "register_new_selection(): new selection is " << new_path;
417  }
418 
419  if(!new_path.empty()) {
420  if(fs::is_directory(new_path)) {
421  DBG_FILEDLG << "register_new_selection(): new selection '" << name << "' is a directory: " << new_path;
422  current_dir_ = new_path;
423  current_entry_.clear();
424  return SELECTION_IS_DIR;
425  } else if(fs::file_exists(new_path)) {
426  // FIXME: Perhaps redundant since the three-params call to normalize_path()
427  // above necessarily validates existence.
428  DBG_FILEDLG << "register_new_selection(): new selection '" << name << "' is a file, symbolic link, or special: " << new_path;
429  current_dir_ = fs::directory_name(new_path);
430  current_entry_ = fs::base_name(new_path);
431  return SELECTION_IS_FILE;
432  }
433  }
434 
435  // The path does not exist, at least not entirely. See if the parent does
436  // (in save mode non-existent files are accepted as long as the parent dir
437  // exists).
438  const std::string& absolute_parent = fs::normalize_path(new_parent, true, true);
439  if(!absolute_parent.empty()) {
440  DBG_FILEDLG << "register_new_selection(): new selection '" << name << "' does not exist or is not accessible, but parent exists";
441  current_dir_ = absolute_parent;
443  return SELECTION_NOT_FOUND;
444  }
445 
446  DBG_FILEDLG << "register_new_selection(): new selection '" << name << "' does not exist or is not accessible";
448 }
449 
450 void file_dialog::set_input_text(text_box& t, const std::string& value)
451 {
452  if(value.empty()) {
454  return;
455  }
456 
457  t.set_value(value);
458 
459  const std::size_t vallen = t.get_length();
460  const std::size_t extlen = utf8::size(extension_);
461 
462  if(save_mode_ && extlen && vallen > extlen) {
463  // Highlight everything but the extension if it matches
464  if(value.substr(vallen - extlen) == extension_) {
465  t.set_selection(0, vallen - extlen);
466  }
467  }
468 }
469 
471 {
472  if(save_mode_ && !extension_.empty()) {
473  t.set_value(extension_);
474  t.set_selection(0, 0);
475  } else {
476  t.clear();
477  }
478 }
479 
481 {
483 
484  dir_files_.clear();
485  dir_subdirs_.clear();
486 
487  // TODO: Need to detect and handle cases where we don't have search permission
488  // on current_dir_, otherwise things may get weird.
490  isort_dir_entries(dir_files_);
491  isort_dir_entries(dir_subdirs_);
492 
493  //
494  // Clear and refill the filelist box.
495  //
496 
497  listbox& filelist = find_widget<listbox>(get_window(), "filelist", false);
498  button& rm_button = find_widget<button>(get_window(), "delete_file", false);
499 
500  filelist.clear();
501 
502  // Parent entry
503  if(!fs::is_root(current_dir_)) {
504  // label_parent may not necessarily be always ".." in the future, so push
505  // with check_selection = false and check the selection ourselves here.
506  push_fileview_row(filelist, label_parent, icon_parent, false);
507  if(current_entry_ == PARENT_DIR || current_entry_.empty()) {
508  filelist.select_row(0, true);
509  rm_button.set_active(false);
510  } else {
511  rm_button.set_active(true);
512  }
513  }
514 
515  for(const auto& dir : dir_subdirs_) {
516  push_fileview_row(filelist, dir, icon_dir);
517  }
518 
519  for(const auto& file : dir_files_) {
520  push_fileview_row(filelist, file, icon_file);
521  }
522 
523  find_widget<styled_widget>(get_window(), "current_dir", false).set_label(current_dir_);
524  set_input_text(find_widget<text_box>(get_window(), "filename", false), current_entry_);
525 
526  on_row_selected();
527 }
528 
529 void file_dialog::push_fileview_row(listbox& filelist, const std::string& name, const std::string& icon, bool check_selection)
530 {
531  // TODO: Hopefully some day GUI2 will allow us to make labels be ellipsized
532  // dynamically at layout/rendering time.
533  std::string label = name;
534  utils::ellipsis_truncate(label, FILE_DIALOG_MAX_ENTRY_LENGTH);
535 
537  data["icon"]["label"] = icon;
538  data["file"]["label"] = label;
539 
540  grid& last_grid = filelist.add_row(data);
541 
542  //
543  // Crummy hack around the lack of an option to hook into row double click
544  // events for all rows using the GUI2 listbox API. Assign a special retval to
545  // each row that triggers a special check during dialog exit.
546  //
547  find_widget<toggle_panel>(&last_grid, "item_panel", false)
548  .set_retval(FILE_DIALOG_ITEM_RETVAL);
549 
550  if(check_selection && name == current_entry_) {
551  filelist.select_last_row(true);
552  }
553 }
554 
556 {
557  listbox& bookmarks_bar = find_widget<listbox>(get_window(), "bookmarks", false);
558 
559  // Internal state has normalized path delimiters but dot entries aren't
560  // resolved after callers call set_path(), so compare against a canonical
561  // version. The bookmark paths are already canonical, though.
562  const std::string& canon_current_dir = fs::normalize_path(current_dir_, true, true);
563 
564  // Go backwards so we can match user-defined bookmarks first (otherwise it may
565  // become impossible for the user to delete them if they match any of the
566  // predefined paths).
567  auto it = std::find(bookmark_paths_.rbegin(), bookmark_paths_.rend(), canon_current_dir);
568 
569  if(it == bookmark_paths_.rend()) {
570  if(current_bookmark_ >= 0) {
571  bookmarks_bar.select_row(static_cast<unsigned>(current_bookmark_), false);
572  }
573  current_bookmark_ = -1;
574  } else {
575  const int new_selection = static_cast<int>(std::distance(bookmark_paths_.begin(), it.base()) - 1);
576  if(new_selection != current_bookmark_) {
577  assert(static_cast<unsigned>(new_selection) < bookmarks_bar.get_item_count());
578  if(current_bookmark_ >= 0) {
579  bookmarks_bar.select_row(static_cast<unsigned>(current_bookmark_), false);
580  }
581  bookmarks_bar.select_row(static_cast<unsigned>(new_selection), true);
582  current_bookmark_ = new_selection;
583  }
584  }
585 
586  // Update bookmark edit controls.
587  button& del_button = find_widget<button>(get_window(), "remove_bookmark", false);
588 
589  if(user_bookmarks_begin_ == -1) {
590  del_button.set_active(false);
591  } else {
593  }
594 }
595 
597 {
598  listbox& filelist = find_widget<listbox>(get_window(), "filelist", false);
599  text_box& file_textbox = find_widget<text_box>(get_window(), "filename", false);
600  button& rm_button = find_widget<button>(get_window(), "delete_file", false);
601 
602  // Don't use register_new_selection() here, we don't want any parsing to be
603  // performed at this point.
605 
606  // Clear the textbox when selecting ..
607  if(current_entry_ != PARENT_DIR) {
608  set_input_text(file_textbox, current_entry_);
609  rm_button.set_active(true);
610  } else {
611  clear_input_text(file_textbox);
612  rm_button.set_active(false);
613  }
614 
615  // Need to do this every time so that input can still be sent to the
616  // textbox without clicking on it.
617  get_window()->keyboard_capture(&file_textbox);
618 }
619 
621 {
622  // Don't let us steal the focus from the primary widgets.
623  text_box& file_textbox = find_widget<text_box>(get_window(), "filename", false);
624  get_window()->keyboard_capture(&file_textbox);
625 
626  listbox& bookmarks_bar = find_widget<listbox>(get_window(), "bookmarks", false);
627  const int new_selection = bookmarks_bar.get_selected_row();
628 
629  if(new_selection < 0) {
630  if(current_bookmark_ >= 0) {
631  // Don't allow the user to deselect the selected bookmark. That wouldn't
632  // make any sense.
633  bookmarks_bar.select_row(static_cast<unsigned>(current_bookmark_));
634  }
635 
636  return;
637  }
638 
639  assert(static_cast<unsigned>(new_selection) < bookmark_paths_.size());
640  current_bookmark_ = new_selection;
641  set_path(bookmark_paths_[new_selection]);
643 
644  // Update bookmark edit controls.
645  button& del_button = find_widget<button>(get_window(), "remove_bookmark", false);
646  del_button.set_active(user_bookmarks_begin_ >= 0
648 }
649 
651 {
652  const std::string& default_label = fs::base_name(current_dir_);
653 
654  std::string label = default_label;
655 
656  const bool confirm = bookmark_create::execute(label);
657  if(!confirm) {
658  return;
659  }
660 
661  if(label.empty()) {
662  label = default_label;
663  }
664 
665  listbox& bookmarks_bar = find_widget<listbox>(get_window(), "bookmarks", false);
666 
668  bookmark_paths_.push_back(current_dir_);
669  const unsigned top_bookmark = bookmark_paths_.size() - 1;
670 
671  if(user_bookmarks_begin_ == -1) {
672  user_bookmarks_begin_ = top_bookmark;
673  }
674 
676  data["bookmark"]["label"] = label;
677  bookmarks_bar.add_row(data);
678 
679  current_bookmark_ = -1;
680 
682 }
683 
685 {
686  assert(user_bookmarks_begin_ >= 0
687  && current_bookmark_ >= 0
689  && current_bookmark_ < static_cast<int>(bookmark_paths_.size()));
690 
691  listbox& bookmarks_bar = find_widget<listbox>(get_window(), "bookmarks", false);
694  bookmarks_bar.remove_row(current_bookmark_);
695 
696  current_bookmark_ = -1;
697 
699 }
700 
702 {
703  std::string new_dir_name;
704 
705  if(folder_create::execute(new_dir_name)) {
706  const std::string& new_path = concat_path(current_dir_, new_dir_name);
707 
708  if(!fs::make_directory(new_path)) {
710  VGETTEXT("Could not create a new folder at $path|. Make sure you have the appropriate permissions to write to this location.",
711  {{"path", new_path}}));
712  } else {
714  }
715  }
716 }
717 
719 {
720  if(current_entry_.empty()) {
721  return;
722  }
723 
724  const std::string& selection = concat_path(current_dir_, current_entry_);
725  const bool is_dir = fs::is_directory(selection);
726 
727  const std::string& message = (is_dir
728  ? _("The following folder and its contents will be permanently deleted:")
729  : _("The following file will be permanently deleted:"))
730  + "\n\n" + selection + "\n\n" + _("Do you wish to continue?");
731 
733  return;
734  }
735 
736  const bool result = is_dir
737  ? fs::delete_directory(selection)
738  : fs::delete_file(selection);
739 
740  if(!result) {
742  VGETTEXT("Could not delete $path|. Make sure you have the appropriate permissions to write to this location.",
743  {{"path", selection}}));
744  } else {
746  }
747 }
748 
749 } // namespace dialogs
double t
Definition: astarsearch.cpp:65
Simple push button.
Definition: button.hpp:37
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: button.cpp:65
static bool execute(std::string &bookmark_name)
The execute function; see modal_dialog for more information.
void sync_bookmarks_bar()
Updates the bookmarks bar state to reflect the internal state.
bool process_submit_common(const std::string &name)
file_dialog & set_path(const std::string &value)
Sets the initial file selection.
const std::string & title() const
Gets the current dialog title text.
Definition: file_dialog.hpp:51
void on_bookmark_add_cmd()
Handles Add Bookmark button press events.
std::vector< std::string > bookmark_paths_
SELECTION_TYPE register_new_selection(const std::string &name)
Updates the internal state and returns the type of the selection.
void set_input_text(class text_box &t, const std::string &value)
void on_file_delete_cmd()
Handles Delete button press events.
std::string path() const
Gets the current file selection.
file_dialog & set_filename(const std::string &value)
Sets the initial file name input but not the path.
std::vector< std::string > dir_files_
void on_bookmark_selected()
Handles selection or deselection of bookmarks.
void clear_input_text(class text_box &t)
bool is_selection_type_acceptable(SELECTION_TYPE stype) const
Returns whether the given selection type is acceptable for closing the dialog.
bool process_fileview_submit()
Processes file view selection in reaction to row double-click events.
bool process_textbox_submit()
Processes textbox input in reaction to OK button/Enter key events.
void on_dir_create_cmd()
Handles New Folder button press events.
std::set< desktop::GAME_PATH_TYPES > extra_paths_
void on_row_selected()
Handles file/directory selection on single-click.
void push_fileview_row(class listbox &filelist, const std::string &name, const std::string &icon, bool check_selection=true)
Row building helper for refresh_fileview().
bool confirm_overwrite(SELECTION_TYPE stype)
Prompts the user before overwriting an existing file.
void refresh_fileview()
Updates the dialog contents to match the internal state.
std::string get_filelist_selection(class listbox &filelist)
virtual void pre_show(window &window) override
Actions to be taken before showing the window.
bool on_exit(window &window)
Handles dialog exit events and decides whether to proceed or not.
std::vector< std::string > dir_subdirs_
void on_bookmark_del_cmd()
Handles Remove Bookmark button press events.
Main class to show messages to the user.
Definition: message.hpp:36
@ yes_no_buttons
Shows a yes and no button.
Definition: message.hpp:81
Abstract base class for all modal dialogs.
window * get_window()
Returns a pointer to the dialog's window.
Base container class.
Definition: grid.hpp:32
A label displays text that can be wrapped but no scrollbars are provided.
Definition: label.hpp:57
The listbox class.
Definition: listbox.hpp:46
bool select_last_row(const bool select=true)
Does exactly as advertised: selects the list's last row.
Definition: listbox.hpp:192
grid & add_row(const widget_item &item, const int index=-1)
When an item in the list is selected by the user we need to update the state.
Definition: listbox.cpp:62
bool select_row(const unsigned row, const bool select=true)
Selects a row.
Definition: listbox.cpp:246
void remove_row(const unsigned row, unsigned count=1)
Removes a row in the listbox.
Definition: listbox.cpp:82
void clear()
Removes all the rows in the listbox, clearing it.
Definition: listbox.cpp:121
int get_selected_row() const
Returns the first selected row.
Definition: listbox.cpp:271
unsigned get_item_count() const
Returns the number of items in the listbox.
Definition: listbox.cpp:127
Base class for all visible items.
virtual void set_label(const t_string &label)
virtual void set_use_markup(bool use_markup)
std::string get_value() const
Class for a single line text area.
Definition: text_box.hpp:142
void set_visible(const visibility visible)
Definition: widget.cpp:471
@ invisible
The user set the widget invisible, that means:
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition: window.hpp:67
void set_retval(const int retval, const bool close_window=true)
Sets there return value of the window.
Definition: window.hpp:403
void keyboard_capture(widget *widget)
Definition: window.cpp:1224
void add_to_keyboard_chain(widget *widget)
Adds the widget to the keyboard chain.
Definition: window.cpp:1230
void set_exit_hook(exit_hook mode, std::function< bool(window &)> func)
Sets the window's exit hook.
Definition: window.hpp:457
int get_retval()
Definition: window.hpp:410
@ on_all
Always run hook.
#define DBG_FILEDLG
Definition: file_dialog.cpp:44
static lg::log_domain log_filedlg
Definition: file_dialog.cpp:40
Declarations for File-IO.
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
static std::string _(const char *str)
Definition: gettext.hpp:93
std::string label
What to show in the filter's drop-down list.
Definition: manager.cpp:217
This file contains the window object, this object is a top level container which has the event manage...
Standard logging facilities (interface).
#define REGISTER_DIALOG(window_id)
Wrapper for REGISTER_DIALOG2.
@ WAIT
Definition: cursor.hpp:29
std::vector< bookmark_info > user_bookmarks()
Definition: paths.cpp:281
unsigned add_user_bookmark(const std::string &label, const std::string &path)
Definition: paths.cpp:256
@ GAME_USER_DATA_DIR
User data dir.
Definition: paths.hpp:61
@ GAME_CORE_DATA_DIR
Game data dir.
Definition: paths.hpp:59
void remove_user_bookmark(unsigned index)
Definition: paths.cpp:269
std::vector< path_info > system_paths(std::set< SYSTEM_PATH_TYPES > paths)
Returns a list of system-defined paths.
Definition: paths.cpp:233
@ SYSTEM_USER_PROFILE
Path to the user's profile dir (e.g.
Definition: paths.hpp:68
@ SYSTEM_ALL_DRIVES
Paths for each storage media found (Windows), /media and/or /mnt (X11, if non-empty).
Definition: paths.hpp:67
@ SYSTEM_ROOTFS
Path to the root of the filesystem hierarchy (ignored on Windows).
Definition: paths.hpp:69
std::vector< path_info > game_paths(std::set< GAME_PATH_TYPES > paths)
Returns a list of game-related paths.
Definition: paths.cpp:200
bool is_relative(const std::string &path)
Returns whether the path seems to be relative.
bool is_root(const std::string &path)
Returns whether the path is the root of the file hierarchy.
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs, name_mode mode, filter_mode filter, reorder_mode reorder, file_tree_checksum *checksum)
Get a list of all files and/or directories in a given directory.
Definition: filesystem.cpp:407
std::string base_name(const std::string &file, const bool remove_extension)
Returns the base filename of a file, with directory name stripped.
bool delete_file(const std::string &filename)
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:321
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
bool delete_directory(const std::string &dirname, const bool keep_pbl)
std::string root_name(const std::string &path)
Returns the name of the root device if included in the given path.
std::string directory_name(const std::string &file)
Returns the directory name of a file, with filename stripped.
std::string nearest_extant_parent(const std::string &file)
Finds the nearest parent in existence for a file or directory.
char path_separator()
Returns the standard path separator for the current platform.
bool make_directory(const std::string &dirname)
bool is_path_sep(char c)
Returns whether c is a path separator.
std::string normalize_path(const std::string &fpath, bool normalize_separators, bool resolve_dot_entries)
Returns the absolute path of a file.
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:205
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:179
std::map< std::string, widget_item > widget_data
Definition: widget.hpp:35
void show_transient_error_message(const std::string &message, const std::string &image, const bool message_use_markup)
Shows a transient error message to the user.
void show_message(const std::string &title, const std::string &msg, const std::string &button_caption, const bool auto_close, const bool message_use_markup, const bool title_use_markup)
Shows a message to the user.
Definition: message.cpp:151
@ OK
Dialog was closed with the OK button.
Definition: retval.hpp:35
@ CANCEL
Dialog was closed with the CANCEL button.
Definition: retval.hpp:38
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:87
void ellipsis_truncate(std::string &str, const std::size_t size)
Truncates a string to a given utf-8 character count and then appends an ellipsis.
Desktop paths, storage media and bookmark functions.
std::string_view data
Definition: picture.cpp:199
This file contains the settings handling of the widget library.
static map_location::DIRECTION n
#define a
#define b