The Battle for Wesnoth  1.17.17+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 {
121 }
122 
123 std::string file_dialog::path() const
124 {
125  const std::string& dir_norm = fs::normalize_path(current_dir_, true);
126 
127  if(current_entry_.empty() || current_entry_ == CURRENT_DIR) {
128  return dir_norm;
129  } else if(current_entry_ == PARENT_DIR) {
130  return fs::directory_name(dir_norm);
131  }
132 
133  return concat_path(dir_norm, current_entry_);
134 }
135 
136 file_dialog& file_dialog::set_path(const std::string& value)
137 {
138  if(value.empty()) {
139  current_dir_ = filesystem_root();
140  }
141 
142  const std::string& norm = fs::normalize_path(value, true);
143 
144  if(fs::is_directory(norm)) {
145  current_dir_ = norm;
146  } else {
148  if(current_dir_.empty()) {
149  current_dir_ = filesystem_root();
150  }
151  // The file may or may not exist. We'll find out eventually when setting up
152  // the dialog.
154  }
155 
156  return *this;
157 }
158 
159 file_dialog& file_dialog::set_filename(const std::string& value)
160 {
161  current_entry_ = value;
162 
163  return *this;
164 }
165 
167 {
168  styled_widget& title = find_widget<styled_widget>(&window, "title", false);
169  styled_widget& message = find_widget<styled_widget>(&window, "message", false);
170  styled_widget& ok = find_widget<styled_widget>(&window, "ok", false);
171 
172  title.set_label(title_);
173 
174  if(msg_.empty()) {
176  } else {
178  message.set_use_markup(true);
179  }
180 
181  if(ok_label_.empty()) {
182  ok.set_label(save_mode_ ? _("Save") : _("Open"));
183  } else {
184  ok.set_label(ok_label_);
185  }
186 
187  listbox& bookmarks_bar = find_widget<listbox>(&window, "bookmarks", false);
188 
189  find_widget<styled_widget>(&window, "current_dir", false).set_text_ellipse_mode(PANGO_ELLIPSIZE_START);
190 
191  //
192  // Push hard-coded bookmarks.
193  //
194 
195  std::vector<desktop::path_info> bookmarks = desktop::game_paths();
196  const auto& sys_paths = desktop::system_paths();
197  bookmarks.insert(bookmarks.end(), sys_paths.begin(), sys_paths.end());
198 
199  bookmark_paths_.clear();
201 
203 
204  for(const auto& pinfo : bookmarks) {
205  bookmark_paths_.push_back(pinfo.path);
206  data["bookmark"]["label"] = pinfo.display_name();
207  bookmarks_bar.add_row(data);
208  }
209 
210  //
211  // Push user-defined bookmarks.
212  //
213 
214  const std::vector<desktop::bookmark_info>& user_bookmarks = desktop::user_bookmarks();
215 
216  if(!user_bookmarks.empty()) {
218  }
219 
220  for(const auto& bookmark : user_bookmarks) {
221  bookmark_paths_.push_back(bookmark.path);
222  data["bookmark"]["label"] = bookmark.label;
223  bookmarks_bar.add_row(data);
224  }
225 
227 
228  listbox& filelist = find_widget<listbox>(&window, "filelist", false);
229 
231  std::bind(&file_dialog::on_row_selected, this));
232  connect_signal_notify_modified(bookmarks_bar,
233  std::bind(&file_dialog::on_bookmark_selected, this));
234 
235  button& mkdir_button = find_widget<button>(&window, "new_dir", false);
236  button& rm_button = find_widget<button>(&window, "delete_file", false);
237  button& bookmark_add_button = find_widget<button>(&window, "add_bookmark", false);
238  button& bookmark_del_button = find_widget<button>(&window, "remove_bookmark", false);
239 
240  connect_signal_mouse_left_click(mkdir_button,
241  std::bind(&file_dialog::on_dir_create_cmd, this));
243  std::bind(&file_dialog::on_file_delete_cmd, this));
244  connect_signal_mouse_left_click(bookmark_add_button,
245  std::bind(&file_dialog::on_bookmark_add_cmd, this));
246  connect_signal_mouse_left_click(bookmark_del_button,
247  std::bind(&file_dialog::on_bookmark_del_cmd, this));
248 
249  if(read_only_) {
250  mkdir_button.set_active(false);
251  rm_button.set_active(false);
252 
255  }
256 
258 
259  window.keyboard_capture(find_widget<text_box>(&window, "filename", false, true));
260  window.add_to_keyboard_chain(&filelist);
261  window.set_exit_hook(window::exit_hook::on_all, std::bind(&file_dialog::on_exit, this, std::placeholders::_1));
262 }
263 
265 {
266  if(window.get_retval() == FILE_DIALOG_ITEM_RETVAL) {
267  // Attempting to exit by double clicking items -- only proceeds if the item
268  // was a file.
270  window.set_retval(retval::OK, false);
271  return true;
272  } else {
273  return false;
274  }
275  }
276 
277  if(window.get_retval() == retval::OK) {
278  // Attempting to exit by pressing Enter/clicking OK -- only proceeds if the
279  // textbox was not altered by the user to point to a different directory.
280  return process_textbox_submit();
281  }
282 
283  return true;
284 }
285 
287 {
288  // TODO: Adapt for implementing directory selection mode.
289  return save_mode_
290  ? stype != SELECTION_IS_DIR && stype != SELECTION_PARENT_NOT_FOUND
291  : stype == SELECTION_IS_FILE;
292 }
293 
295 {
296  // TODO: Adapt for implementing directory selection mode.
297  if(stype != SELECTION_IS_FILE) {
298  return true;
299  }
300 
301  const std::string& message
302  = _("The file already exists. Do you wish to overwrite it?");
304 }
305 
306 bool file_dialog::process_submit_common(const std::string& name)
307 {
308  const auto stype = register_new_selection(name);
309 
310  //DBG_FILEDLG << "current_dir_=" << current_dir_ << " current_entry_=" << current_entry_;
311 
312  if(is_selection_type_acceptable(stype)) {
313  return save_mode_ ? confirm_overwrite(stype) : true;
314  }
315 
316  switch(stype) {
317  case SELECTION_IS_DIR:
318  // TODO: Adapt for implementing directory selection mode.
321  break;
323  // We get here in save mode or not. Use the file creation language only in
324  // save mode.
325  if(save_mode_) {
326  show_transient_error_message(VGETTEXT("The file or folder $path cannot be created.", {{"path", name}}));
327  break;
328  }
329  [[fallthrough]];
330  case SELECTION_NOT_FOUND:
331  // We only get here if we aren't in save mode.
332  show_transient_error_message(VGETTEXT("The file or folder $path does not exist.", {{"path", name}}));
333  break;
334  case SELECTION_IS_FILE:
335  // TODO: Adapt for implementing directory selection mode.
336  default:
337  assert(false && "Unimplemented selection mode or semantics");
338  }
339 
340  return false;
341 }
342 
344 {
345  listbox& filelist = find_widget<listbox>(get_window(), "filelist", false);
346  const std::string& selected_name = get_filelist_selection(filelist);
347  return process_submit_common(selected_name);
348 }
349 
351 {
352  text_box& file_textbox = find_widget<text_box>(get_window(), "filename", false);
353  const std::string& input_name = file_textbox.get_value();
354  return !input_name.empty() && process_submit_common(input_name);
355 }
356 
358 {
359  const int row = filelist.get_selected_row();
360 
361  if(row == -1) {
362  // Shouldn't happen...
363  return "";
364  }
365 
366  const bool i_am_root = fs::is_root(current_dir_);
367 
368  if(row == 0 && !i_am_root) {
369  return PARENT_DIR;
370  } else {
371  std::size_t n = i_am_root ? row : row - 1;
372 
373  if(n < dir_subdirs_.size()) {
374  return dir_subdirs_[n];
375  } else {
376  n -= dir_subdirs_.size();
377 
378  if(n < dir_files_.size()) {
379  return dir_files_[n];
380  } else {
381  assert(false && "File list selection is out of range!");
382  }
383  }
384  }
385 
386  return "";
387 }
388 
390 {
391  std::string new_path, new_parent;
392 
393  if(fs::is_relative(name)) {
394  // On Windows, \ represents a path relative to the root of the process'
395  // current working drive specified by the current working dir, so we get
396  // here. This makes it the only platform where is_relative() and is_root()
397  // aren't mutually exclusive.
398  if(fs::is_root(name)) {
399  DBG_FILEDLG << "register_new_selection(): new selection '" << name << "' is relative to a root resource";
400  // Using the browsed dir's root drive instead of the cwd's makes the most
401  // sense for users.
402  new_parent = fs::root_name(current_dir_);
403  new_path = fs::normalize_path(concat_path(new_parent, name), true, true);
404  } else {
405  DBG_FILEDLG << "register_new_selection(): new selection '" << name << "' seems relative";
406  new_parent = current_dir_;
407  new_path = fs::normalize_path(concat_path(current_dir_, name), true, true);
408  }
409  } else {
410  DBG_FILEDLG << "register_new_selection(): new selection '" << name << "' seems absolute";
411  new_parent = fs::directory_name(name);
412  new_path = fs::normalize_path(name, true, true);
413  DBG_FILEDLG << "register_new_selection(): new selection is " << new_path;
414  }
415 
416  if(!new_path.empty()) {
417  if(fs::is_directory(new_path)) {
418  DBG_FILEDLG << "register_new_selection(): new selection '" << name << "' is a directory: " << new_path;
419  current_dir_ = new_path;
420  current_entry_.clear();
421  return SELECTION_IS_DIR;
422  } else if(fs::file_exists(new_path)) {
423  // FIXME: Perhaps redundant since the three-params call to normalize_path()
424  // above necessarily validates existence.
425  DBG_FILEDLG << "register_new_selection(): new selection '" << name << "' is a file, symbolic link, or special: " << new_path;
426  current_dir_ = fs::directory_name(new_path);
427  current_entry_ = fs::base_name(new_path);
428  return SELECTION_IS_FILE;
429  }
430  }
431 
432  // The path does not exist, at least not entirely. See if the parent does
433  // (in save mode non-existent files are accepted as long as the parent dir
434  // exists).
435  const std::string& absolute_parent = fs::normalize_path(new_parent, true, true);
436  if(!absolute_parent.empty()) {
437  DBG_FILEDLG << "register_new_selection(): new selection '" << name << "' does not exist or is not accessible, but parent exists";
438  current_dir_ = absolute_parent;
440  return SELECTION_NOT_FOUND;
441  }
442 
443  DBG_FILEDLG << "register_new_selection(): new selection '" << name << "' does not exist or is not accessible";
445 }
446 
447 void file_dialog::set_input_text(text_box& t, const std::string& value)
448 {
449  if(value.empty()) {
451  return;
452  }
453 
454  t.set_value(value);
455 
456  const std::size_t vallen = t.get_length();
457  const std::size_t extlen = utf8::size(extension_);
458 
459  if(save_mode_ && extlen && vallen > extlen) {
460  // Highlight everything but the extension if it matches
461  if(value.substr(vallen - extlen) == extension_) {
462  t.set_selection(0, vallen - extlen);
463  }
464  }
465 }
466 
468 {
469  if(save_mode_ && !extension_.empty()) {
470  t.set_value(extension_);
471  t.set_selection(0, 0);
472  } else {
473  t.clear();
474  }
475 }
476 
478 {
480 
481  dir_files_.clear();
482  dir_subdirs_.clear();
483 
484  // TODO: Need to detect and handle cases where we don't have search permission
485  // on current_dir_, otherwise things may get weird.
487  isort_dir_entries(dir_files_);
488  isort_dir_entries(dir_subdirs_);
489 
490  //
491  // Clear and refill the filelist box.
492  //
493 
494  listbox& filelist = find_widget<listbox>(get_window(), "filelist", false);
495  button& rm_button = find_widget<button>(get_window(), "delete_file", false);
496 
497  filelist.clear();
498 
499  // Parent entry
500  if(!fs::is_root(current_dir_)) {
501  // label_parent may not necessarily be always ".." in the future, so push
502  // with check_selection = false and check the selection ourselves here.
503  push_fileview_row(filelist, label_parent, icon_parent, false);
504  if(current_entry_ == PARENT_DIR || current_entry_.empty()) {
505  filelist.select_row(0, true);
506  rm_button.set_active(false);
507  } else {
508  rm_button.set_active(true);
509  }
510  }
511 
512  for(const auto& dir : dir_subdirs_) {
513  push_fileview_row(filelist, dir, icon_dir);
514  }
515 
516  for(const auto& file : dir_files_) {
517  push_fileview_row(filelist, file, icon_file);
518  }
519 
520  find_widget<styled_widget>(get_window(), "current_dir", false).set_label(current_dir_);
521  set_input_text(find_widget<text_box>(get_window(), "filename", false), current_entry_);
522 
523  on_row_selected();
524 }
525 
526 void file_dialog::push_fileview_row(listbox& filelist, const std::string& name, const std::string& icon, bool check_selection)
527 {
528  // TODO: Hopefully some day GUI2 will allow us to make labels be ellipsized
529  // dynamically at layout/rendering time.
530  std::string label = name;
531  utils::ellipsis_truncate(label, FILE_DIALOG_MAX_ENTRY_LENGTH);
532 
534  data["icon"]["label"] = icon;
535  data["file"]["label"] = label;
536 
537  grid& last_grid = filelist.add_row(data);
538 
539  //
540  // Crummy hack around the lack of an option to hook into row double click
541  // events for all rows using the GUI2 listbox API. Assign a special retval to
542  // each row that triggers a special check during dialog exit.
543  //
544  find_widget<toggle_panel>(&last_grid, "item_panel", false)
545  .set_retval(FILE_DIALOG_ITEM_RETVAL);
546 
547  if(check_selection && name == current_entry_) {
548  filelist.select_last_row(true);
549  }
550 }
551 
553 {
554  listbox& bookmarks_bar = find_widget<listbox>(get_window(), "bookmarks", false);
555 
556  // Internal state has normalized path delimiters but dot entries aren't
557  // resolved after callers call set_path(), so compare against a canonical
558  // version. The bookmark paths are already canonical, though.
559  const std::string& canon_current_dir = fs::normalize_path(current_dir_, true, true);
560 
561  // Go backwards so we can match user-defined bookmarks first (otherwise it may
562  // become impossible for the user to delete them if they match any of the
563  // predefined paths).
564  auto it = std::find(bookmark_paths_.rbegin(), bookmark_paths_.rend(), canon_current_dir);
565 
566  if(it == bookmark_paths_.rend()) {
567  if(current_bookmark_ >= 0) {
568  bookmarks_bar.select_row(static_cast<unsigned>(current_bookmark_), false);
569  }
570  current_bookmark_ = -1;
571  } else {
572  const int new_selection = static_cast<int>(std::distance(bookmark_paths_.begin(), it.base()) - 1);
573  if(new_selection != current_bookmark_) {
574  assert(static_cast<unsigned>(new_selection) < bookmarks_bar.get_item_count());
575  if(current_bookmark_ >= 0) {
576  bookmarks_bar.select_row(static_cast<unsigned>(current_bookmark_), false);
577  }
578  bookmarks_bar.select_row(static_cast<unsigned>(new_selection), true);
579  current_bookmark_ = new_selection;
580  }
581  }
582 
583  // Update bookmark edit controls.
584  button& del_button = find_widget<button>(get_window(), "remove_bookmark", false);
585 
586  if(user_bookmarks_begin_ == -1) {
587  del_button.set_active(false);
588  } else {
590  }
591 }
592 
594 {
595  listbox& filelist = find_widget<listbox>(get_window(), "filelist", false);
596  text_box& file_textbox = find_widget<text_box>(get_window(), "filename", false);
597  button& rm_button = find_widget<button>(get_window(), "delete_file", false);
598 
599  // Don't use register_new_selection() here, we don't want any parsing to be
600  // performed at this point.
602 
603  // Clear the textbox when selecting ..
604  if(current_entry_ != PARENT_DIR) {
605  set_input_text(file_textbox, current_entry_);
606  rm_button.set_active(true);
607  } else {
608  clear_input_text(file_textbox);
609  rm_button.set_active(false);
610  }
611 
612  // Need to do this every time so that input can still be sent to the
613  // textbox without clicking on it.
614  get_window()->keyboard_capture(&file_textbox);
615 }
616 
618 {
619  // Don't let us steal the focus from the primary widgets.
620  text_box& file_textbox = find_widget<text_box>(get_window(), "filename", false);
621  get_window()->keyboard_capture(&file_textbox);
622 
623  listbox& bookmarks_bar = find_widget<listbox>(get_window(), "bookmarks", false);
624  const int new_selection = bookmarks_bar.get_selected_row();
625 
626  if(new_selection < 0) {
627  if(current_bookmark_ >= 0) {
628  // Don't allow the user to deselect the selected bookmark. That wouldn't
629  // make any sense.
630  bookmarks_bar.select_row(static_cast<unsigned>(current_bookmark_));
631  }
632 
633  return;
634  }
635 
636  assert(static_cast<unsigned>(new_selection) < bookmark_paths_.size());
637  current_bookmark_ = new_selection;
638  set_path(bookmark_paths_[new_selection]);
640 
641  // Update bookmark edit controls.
642  button& del_button = find_widget<button>(get_window(), "remove_bookmark", false);
643  del_button.set_active(user_bookmarks_begin_ >= 0
645 }
646 
648 {
649  const std::string& default_label = fs::base_name(current_dir_);
650 
651  std::string label = default_label;
652 
653  const bool confirm = bookmark_create::execute(label);
654  if(!confirm) {
655  return;
656  }
657 
658  if(label.empty()) {
659  label = default_label;
660  }
661 
662  listbox& bookmarks_bar = find_widget<listbox>(get_window(), "bookmarks", false);
663 
665  bookmark_paths_.push_back(current_dir_);
666  const unsigned top_bookmark = bookmark_paths_.size() - 1;
667 
668  if(user_bookmarks_begin_ == -1) {
669  user_bookmarks_begin_ = top_bookmark;
670  }
671 
673  data["bookmark"]["label"] = label;
674  bookmarks_bar.add_row(data);
675 
676  current_bookmark_ = -1;
677 
679 }
680 
682 {
683  assert(user_bookmarks_begin_ >= 0
684  && current_bookmark_ >= 0
686  && current_bookmark_ < static_cast<int>(bookmark_paths_.size()));
687 
688  listbox& bookmarks_bar = find_widget<listbox>(get_window(), "bookmarks", false);
691  bookmarks_bar.remove_row(current_bookmark_);
692 
693  current_bookmark_ = -1;
694 
696 }
697 
699 {
700  std::string new_dir_name;
701 
702  if(folder_create::execute(new_dir_name)) {
703  const std::string& new_path = concat_path(current_dir_, new_dir_name);
704 
705  if(!fs::make_directory(new_path)) {
707  VGETTEXT("Could not create a new folder at $path|. Make sure you have the appropriate permissions to write to this location.",
708  {{"path", new_path}}));
709  } else {
711  }
712  }
713 }
714 
716 {
717  if(current_entry_.empty()) {
718  return;
719  }
720 
721  const std::string& selection = concat_path(current_dir_, current_entry_);
722  const bool is_dir = fs::is_directory(selection);
723 
724  const std::string& message = (is_dir
725  ? _("The following folder and its contents will be permanently deleted:")
726  : _("The following file will be permanently deleted:"))
727  + "\n\n" + selection + "\n\n" + _("Do you wish to continue?");
728 
730  return;
731  }
732 
733  const bool result = is_dir
734  ? fs::delete_directory(selection)
735  : fs::delete_file(selection);
736 
737  if(!result) {
739  VGETTEXT("Could not delete $path|. Make sure you have the appropriate permissions to write to this location.",
740  {{"path", selection}}));
741  } else {
743  }
744 }
745 
746 } // 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:49
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.
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 a text, the text can be wrapped but no scrollbars are provided.
Definition: label.hpp:58
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:456
@ 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:358
void keyboard_capture(widget *widget)
Definition: window.cpp:1130
void add_to_keyboard_chain(widget *widget)
Adds the widget to the keyboard chain.
Definition: window.cpp:1136
void set_exit_hook(exit_hook mode, std::function< bool(window &)> func)
Sets the window's exit hook.
Definition: window.hpp:412
int get_retval()
Definition: window.hpp:365
@ 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:276
unsigned add_user_bookmark(const std::string &label, const std::string &path)
Definition: paths.cpp:251
void remove_user_bookmark(unsigned index)
Definition: paths.cpp:264
std::vector< path_info > system_paths(unsigned path_types)
Returns a list of system-defined paths.
Definition: paths.cpp:228
std::vector< path_info > game_paths(unsigned path_types)
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:406
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:320
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