The Battle for Wesnoth  1.19.0-dev
file_dialog.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2011 - 2024
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/text_box.hpp"
32 #include "gui/widgets/window.hpp"
33 #include "gettext.hpp"
34 #include "log.hpp"
36 
37 #include <functional>
38 
39 static lg::log_domain log_filedlg{"gui/dialogs/file_dialog"};
40 #define ERR_FILEDLG LOG_STREAM(err, log_filedlg)
41 #define WRN_FILEDLG LOG_STREAM(warn, log_filedlg)
42 #define LOG_FILEDLG LOG_STREAM(info, log_filedlg)
43 #define DBG_FILEDLG LOG_STREAM(debug, log_filedlg)
44 
45 namespace fs = filesystem;
46 
47 namespace
48 {
49 const std::string icon_dir = "misc/folder-icon.png";
50 // Empty icons with the same size as the above to force the icon column to have a
51 // specific size even when there are no folders in the list.
52 const std::string icon_file = icon_dir + "~O(0)";
53 const std::string icon_parent = icon_dir + "~O(0)";
54 // NOTE: Does not need to be the same as PARENT_DIR! Use PARENT_DIR to build
55 // relative paths for non-presentational purposes instead.
56 const std::string label_parent = "..";
57 
58 const std::string CURRENT_DIR = ".";
59 const std::string PARENT_DIR = "..";
60 
61 const int FILE_DIALOG_ITEM_RETVAL = 9001;
62 const int FILE_DIALOG_MAX_ENTRY_LENGTH = 42;
63 
64 inline std::string concat_path(const std::string& a, const std::string& b)
65 {
66  //
67  // As of Boost 1.61, normalize_path() displays unusual behavior when passing
68  // it paths with extra path separators (e.g. //opt becomes
69  // //opt/home/shadowm/src/wesnoth, where the extraneous sequence is probably
70  // the current working dir), so avoid leaving those around.
71  //
72  // TODO: Maybe handle this corner case in filesystem::normalize_path()
73  // instead, really.
74  //
75  if((a.empty() || !fs::is_path_sep(a.back())) && (b.empty() || !fs::is_path_sep(b.front()))) {
76  return a + fs::path_separator() + b;
77  } else {
78  return a + b;
79  }
80 }
81 
82 inline std::string filesystem_root()
83 {
84  // TODO: Multiple drives support (may require cooperation from the caller).
85  return std::string(1, fs::path_separator());
86 }
87 
88 inline void isort_dir_entries(std::vector<std::string>& entries)
89 {
90  // Yes, this uses Wesnoth's locale and not the filesystem/OS locale. Yes, this
91  // isn't ideal. No, we don't really need to worry about it. It's just a
92  // cosmetic procedure anyway.
93  std::sort(entries.begin(), entries.end(),
94  [](const std::string& a, const std::string& b) { return translation::icompare(a, b) < 0; });
95 }
96 
97 } // unnamed namespace
98 
99 namespace gui2::dialogs
100 {
101 
102 REGISTER_DIALOG(file_dialog)
103 
105  : modal_dialog(window_id())
106  , title_(_("Find File"))
107  , msg_()
108  , ok_label_()
109  , extension_()
110  , current_entry_()
111  , current_dir_()
112  , read_only_(false)
113  , save_mode_(false)
114  , dir_files_()
115  , dir_subdirs_()
116  , bookmark_paths_()
117  , current_bookmark_()
118  , user_bookmarks_begin_()
119  , extra_paths_()
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 
197  std::vector<desktop::path_info> bookmarks = desktop::game_paths(extra_paths_);
199  bookmarks.insert(bookmarks.end(), sys_paths.begin(), sys_paths.end());
200 
201  bookmark_paths_.clear();
203 
205 
206  for(const auto& pinfo : bookmarks) {
207  bookmark_paths_.push_back(pinfo.path);
208  data["bookmark"]["label"] = pinfo.display_name();
209  bookmarks_bar.add_row(data);
210  }
211 
212  //
213  // Push user-defined bookmarks.
214  //
215 
216  const std::vector<desktop::bookmark_info>& user_bookmarks = desktop::user_bookmarks();
217 
218  if(!user_bookmarks.empty()) {
220  }
221 
222  for(const auto& bookmark : user_bookmarks) {
223  bookmark_paths_.push_back(bookmark.path);
224  data["bookmark"]["label"] = bookmark.label;
225  bookmarks_bar.add_row(data);
226  }
227 
229 
230  listbox& filelist = find_widget<listbox>(&window, "filelist", false);
231 
233  std::bind(&file_dialog::on_row_selected, this));
234  connect_signal_notify_modified(bookmarks_bar,
235  std::bind(&file_dialog::on_bookmark_selected, this));
236 
237  button& mkdir_button = find_widget<button>(&window, "new_dir", false);
238  button& rm_button = find_widget<button>(&window, "delete_file", false);
239  button& bookmark_add_button = find_widget<button>(&window, "add_bookmark", false);
240  button& bookmark_del_button = find_widget<button>(&window, "remove_bookmark", false);
241 
242  connect_signal_mouse_left_click(mkdir_button,
243  std::bind(&file_dialog::on_dir_create_cmd, this));
245  std::bind(&file_dialog::on_file_delete_cmd, this));
246  connect_signal_mouse_left_click(bookmark_add_button,
247  std::bind(&file_dialog::on_bookmark_add_cmd, this));
248  connect_signal_mouse_left_click(bookmark_del_button,
249  std::bind(&file_dialog::on_bookmark_del_cmd, this));
250 
251  if(read_only_) {
252  mkdir_button.set_active(false);
253  rm_button.set_active(false);
254 
257  }
258 
260 
261  window.keyboard_capture(find_widget<text_box>(&window, "filename", false, true));
262  window.add_to_keyboard_chain(&filelist);
263  window.set_exit_hook(window::exit_hook::on_all, std::bind(&file_dialog::on_exit, this, std::placeholders::_1));
264 }
265 
267 {
268  if(window.get_retval() == FILE_DIALOG_ITEM_RETVAL) {
269  // Attempting to exit by double clicking items -- only proceeds if the item
270  // was a file.
272  window.set_retval(retval::OK, false);
273  return true;
274  } else {
275  return false;
276  }
277  }
278 
279  if(window.get_retval() == retval::OK) {
280  // Attempting to exit by pressing Enter/clicking OK -- only proceeds if the
281  // textbox was not altered by the user to point to a different directory.
282  return process_textbox_submit();
283  }
284 
285  return true;
286 }
287 
289 {
290  // TODO: Adapt for implementing directory selection mode.
291  return save_mode_
292  ? stype != SELECTION_IS_DIR && stype != SELECTION_PARENT_NOT_FOUND
293  : stype == SELECTION_IS_FILE;
294 }
295 
297 {
298  // TODO: Adapt for implementing directory selection mode.
299  if(stype != SELECTION_IS_FILE) {
300  return true;
301  }
302 
303  const std::string& message
304  = _("The file already exists. Do you wish to overwrite it?");
306 }
307 
308 bool file_dialog::process_submit_common(const std::string& name)
309 {
310  const auto stype = register_new_selection(name);
311 
312  //DBG_FILEDLG << "current_dir_=" << current_dir_ << " current_entry_=" << current_entry_;
313 
314  if(is_selection_type_acceptable(stype)) {
315  return save_mode_ ? confirm_overwrite(stype) : true;
316  }
317 
318  switch(stype) {
319  case SELECTION_IS_DIR:
320  // TODO: Adapt for implementing directory selection mode.
323  break;
325  // We get here in save mode or not. Use the file creation language only in
326  // save mode.
327  if(save_mode_) {
328  show_transient_error_message(VGETTEXT("The file or folder $path cannot be created.", {{"path", name}}));
329  break;
330  }
331  [[fallthrough]];
332  case SELECTION_NOT_FOUND:
333  // We only get here if we aren't in save mode.
334  show_transient_error_message(VGETTEXT("The file or folder $path does not exist.", {{"path", name}}));
335  break;
336  case SELECTION_IS_FILE:
337  // TODO: Adapt for implementing directory selection mode.
338  default:
339  assert(false && "Unimplemented selection mode or semantics");
340  }
341 
342  return false;
343 }
344 
346 {
347  listbox& filelist = find_widget<listbox>(get_window(), "filelist", false);
348  const std::string& selected_name = get_filelist_selection(filelist);
349  return process_submit_common(selected_name);
350 }
351 
353 {
354  text_box& file_textbox = find_widget<text_box>(get_window(), "filename", false);
355  const std::string& input_name = file_textbox.get_value();
356  return !input_name.empty() && process_submit_common(input_name);
357 }
358 
360 {
361  const int row = filelist.get_selected_row();
362 
363  if(row == -1) {
364  // Shouldn't happen...
365  return "";
366  }
367 
368  const bool i_am_root = fs::is_root(current_dir_);
369 
370  if(row == 0 && !i_am_root) {
371  return PARENT_DIR;
372  } else {
373  std::size_t n = i_am_root ? row : row - 1;
374 
375  if(n < dir_subdirs_.size()) {
376  return dir_subdirs_[n];
377  } else {
378  n -= dir_subdirs_.size();
379 
380  if(n < dir_files_.size()) {
381  return dir_files_[n];
382  } else {
383  assert(false && "File list selection is out of range!");
384  }
385  }
386  }
387 
388  return "";
389 }
390 
392 {
393  std::string new_path, new_parent;
394 
395  if(fs::is_relative(name)) {
396  // On Windows, \ represents a path relative to the root of the process'
397  // current working drive specified by the current working dir, so we get
398  // here. This makes it the only platform where is_relative() and is_root()
399  // aren't mutually exclusive.
400  if(fs::is_root(name)) {
401  DBG_FILEDLG << "register_new_selection(): new selection '" << name << "' is relative to a root resource";
402  // Using the browsed dir's root drive instead of the cwd's makes the most
403  // sense for users.
404  new_parent = fs::root_name(current_dir_);
405  new_path = fs::normalize_path(concat_path(new_parent, name), true, true);
406  } else {
407  DBG_FILEDLG << "register_new_selection(): new selection '" << name << "' seems relative";
408  new_parent = current_dir_;
409  new_path = fs::normalize_path(concat_path(current_dir_, name), true, true);
410  }
411  } else {
412  DBG_FILEDLG << "register_new_selection(): new selection '" << name << "' seems absolute";
413  new_parent = fs::directory_name(name);
414  new_path = fs::normalize_path(name, true, true);
415  DBG_FILEDLG << "register_new_selection(): new selection is " << new_path;
416  }
417 
418  if(!new_path.empty()) {
419  if(fs::is_directory(new_path)) {
420  DBG_FILEDLG << "register_new_selection(): new selection '" << name << "' is a directory: " << new_path;
421  current_dir_ = new_path;
422  current_entry_.clear();
423  return SELECTION_IS_DIR;
424  } else if(fs::file_exists(new_path)) {
425  // FIXME: Perhaps redundant since the three-params call to normalize_path()
426  // above necessarily validates existence.
427  DBG_FILEDLG << "register_new_selection(): new selection '" << name << "' is a file, symbolic link, or special: " << new_path;
428  current_dir_ = fs::directory_name(new_path);
429  current_entry_ = fs::base_name(new_path);
430  return SELECTION_IS_FILE;
431  }
432  }
433 
434  // The path does not exist, at least not entirely. See if the parent does
435  // (in save mode non-existent files are accepted as long as the parent dir
436  // exists).
437  const std::string& absolute_parent = fs::normalize_path(new_parent, true, true);
438  if(!absolute_parent.empty()) {
439  DBG_FILEDLG << "register_new_selection(): new selection '" << name << "' does not exist or is not accessible, but parent exists";
440  current_dir_ = absolute_parent;
442  return SELECTION_NOT_FOUND;
443  }
444 
445  DBG_FILEDLG << "register_new_selection(): new selection '" << name << "' does not exist or is not accessible";
447 }
448 
449 void file_dialog::set_input_text(text_box& t, const std::string& value)
450 {
451  if(value.empty()) {
453  return;
454  }
455 
456  t.set_value(value);
457 
458  const std::size_t vallen = t.get_length();
459  const std::size_t extlen = utf8::size(extension_);
460 
461  if(save_mode_ && extlen && vallen > extlen) {
462  // Highlight everything but the extension if it matches
463  if(value.substr(vallen - extlen) == extension_) {
464  t.set_selection(0, vallen - extlen);
465  }
466  }
467 }
468 
470 {
471  if(save_mode_ && !extension_.empty()) {
472  t.set_value(extension_);
473  t.set_selection(0, 0);
474  } else {
475  t.clear();
476  }
477 }
478 
480 {
482 
483  dir_files_.clear();
484  dir_subdirs_.clear();
485 
486  // TODO: Need to detect and handle cases where we don't have search permission
487  // on current_dir_, otherwise things may get weird.
489  isort_dir_entries(dir_files_);
490  isort_dir_entries(dir_subdirs_);
491 
492  //
493  // Clear and refill the filelist box.
494  //
495 
496  listbox& filelist = find_widget<listbox>(get_window(), "filelist", false);
497  button& rm_button = find_widget<button>(get_window(), "delete_file", false);
498 
499  filelist.clear();
500 
501  // Parent entry
502  if(!fs::is_root(current_dir_)) {
503  // label_parent may not necessarily be always ".." in the future, so push
504  // with check_selection = false and check the selection ourselves here.
505  push_fileview_row(filelist, label_parent, icon_parent, false);
506  if(current_entry_ == PARENT_DIR || current_entry_.empty()) {
507  filelist.select_row(0, true);
508  rm_button.set_active(false);
509  } else {
510  rm_button.set_active(true);
511  }
512  }
513 
514  for(const auto& dir : dir_subdirs_) {
515  push_fileview_row(filelist, dir, icon_dir);
516  }
517 
518  for(const auto& file : dir_files_) {
519  push_fileview_row(filelist, file, icon_file);
520  }
521 
522  find_widget<styled_widget>(get_window(), "current_dir", false).set_label(current_dir_);
523  set_input_text(find_widget<text_box>(get_window(), "filename", false), current_entry_);
524 
525  on_row_selected();
526 }
527 
528 void file_dialog::push_fileview_row(listbox& filelist, const std::string& name, const std::string& icon, bool check_selection)
529 {
530  // TODO: Hopefully some day GUI2 will allow us to make labels be ellipsized
531  // dynamically at layout/rendering time.
532  std::string label = name;
533  utils::ellipsis_truncate(label, FILE_DIALOG_MAX_ENTRY_LENGTH);
534 
536  data["icon"]["label"] = icon;
537  data["file"]["label"] = label;
538 
539  grid& last_grid = filelist.add_row(data);
540 
541  //
542  // Crummy hack around the lack of an option to hook into row double click
543  // events for all rows using the GUI2 listbox API. Assign a special retval to
544  // each row that triggers a special check during dialog exit.
545  //
546  find_widget<toggle_panel>(&last_grid, "item_panel", false)
547  .set_retval(FILE_DIALOG_ITEM_RETVAL);
548 
549  if(check_selection && name == current_entry_) {
550  filelist.select_last_row(true);
551  }
552 }
553 
555 {
556  listbox& bookmarks_bar = find_widget<listbox>(get_window(), "bookmarks", false);
557 
558  // Internal state has normalized path delimiters but dot entries aren't
559  // resolved after callers call set_path(), so compare against a canonical
560  // version. The bookmark paths are already canonical, though.
561  const std::string& canon_current_dir = fs::normalize_path(current_dir_, true, true);
562 
563  // Go backwards so we can match user-defined bookmarks first (otherwise it may
564  // become impossible for the user to delete them if they match any of the
565  // predefined paths).
566  auto it = std::find(bookmark_paths_.rbegin(), bookmark_paths_.rend(), canon_current_dir);
567 
568  if(it == bookmark_paths_.rend()) {
569  if(current_bookmark_ >= 0) {
570  bookmarks_bar.select_row(static_cast<unsigned>(current_bookmark_), false);
571  }
572  current_bookmark_ = -1;
573  } else {
574  const int new_selection = static_cast<int>(std::distance(bookmark_paths_.begin(), it.base()) - 1);
575  if(new_selection != current_bookmark_) {
576  assert(static_cast<unsigned>(new_selection) < bookmarks_bar.get_item_count());
577  if(current_bookmark_ >= 0) {
578  bookmarks_bar.select_row(static_cast<unsigned>(current_bookmark_), false);
579  }
580  bookmarks_bar.select_row(static_cast<unsigned>(new_selection), true);
581  current_bookmark_ = new_selection;
582  }
583  }
584 
585  // Update bookmark edit controls.
586  button& del_button = find_widget<button>(get_window(), "remove_bookmark", false);
587 
588  if(user_bookmarks_begin_ == -1) {
589  del_button.set_active(false);
590  } else {
592  }
593 }
594 
596 {
597  listbox& filelist = find_widget<listbox>(get_window(), "filelist", false);
598  text_box& file_textbox = find_widget<text_box>(get_window(), "filename", false);
599  button& rm_button = find_widget<button>(get_window(), "delete_file", false);
600 
601  // Don't use register_new_selection() here, we don't want any parsing to be
602  // performed at this point.
604 
605  // Clear the textbox when selecting ..
606  if(current_entry_ != PARENT_DIR) {
607  set_input_text(file_textbox, current_entry_);
608  rm_button.set_active(true);
609  } else {
610  clear_input_text(file_textbox);
611  rm_button.set_active(false);
612  }
613 
614  // Need to do this every time so that input can still be sent to the
615  // textbox without clicking on it.
616  get_window()->keyboard_capture(&file_textbox);
617 }
618 
620 {
621  // Don't let us steal the focus from the primary widgets.
622  text_box& file_textbox = find_widget<text_box>(get_window(), "filename", false);
623  get_window()->keyboard_capture(&file_textbox);
624 
625  listbox& bookmarks_bar = find_widget<listbox>(get_window(), "bookmarks", false);
626  const int new_selection = bookmarks_bar.get_selected_row();
627 
628  if(new_selection < 0) {
629  if(current_bookmark_ >= 0) {
630  // Don't allow the user to deselect the selected bookmark. That wouldn't
631  // make any sense.
632  bookmarks_bar.select_row(static_cast<unsigned>(current_bookmark_));
633  }
634 
635  return;
636  }
637 
638  assert(static_cast<unsigned>(new_selection) < bookmark_paths_.size());
639  current_bookmark_ = new_selection;
640  set_path(bookmark_paths_[new_selection]);
642 
643  // Update bookmark edit controls.
644  button& del_button = find_widget<button>(get_window(), "remove_bookmark", false);
645  del_button.set_active(user_bookmarks_begin_ >= 0
647 }
648 
650 {
651  const std::string& default_label = fs::base_name(current_dir_);
652 
653  std::string label = default_label;
654 
655  const bool confirm = bookmark_create::execute(label);
656  if(!confirm) {
657  return;
658  }
659 
660  if(label.empty()) {
661  label = default_label;
662  }
663 
664  listbox& bookmarks_bar = find_widget<listbox>(get_window(), "bookmarks", false);
665 
667  bookmark_paths_.push_back(current_dir_);
668  const unsigned top_bookmark = bookmark_paths_.size() - 1;
669 
670  if(user_bookmarks_begin_ == -1) {
671  user_bookmarks_begin_ = top_bookmark;
672  }
673 
675  data["bookmark"]["label"] = label;
676  bookmarks_bar.add_row(data);
677 
678  current_bookmark_ = -1;
679 
681 }
682 
684 {
685  assert(user_bookmarks_begin_ >= 0
686  && current_bookmark_ >= 0
688  && current_bookmark_ < static_cast<int>(bookmark_paths_.size()));
689 
690  listbox& bookmarks_bar = find_widget<listbox>(get_window(), "bookmarks", false);
693  bookmarks_bar.remove_row(current_bookmark_);
694 
695  current_bookmark_ = -1;
696 
698 }
699 
701 {
702  std::string new_dir_name;
703 
704  if(folder_create::execute(new_dir_name)) {
705  const std::string& new_path = concat_path(current_dir_, new_dir_name);
706 
707  if(!fs::make_directory(new_path)) {
709  VGETTEXT("Could not create a new folder at $path|. Make sure you have the appropriate permissions to write to this location.",
710  {{"path", new_path}}));
711  } else {
713  }
714  }
715 }
716 
718 {
719  if(current_entry_.empty()) {
720  return;
721  }
722 
723  const std::string& selection = concat_path(current_dir_, current_entry_);
724  const bool is_dir = fs::is_directory(selection);
725 
726  const std::string& message = (is_dir
727  ? _("The following folder and its contents will be permanently deleted:")
728  : _("The following file will be permanently deleted:"))
729  + "\n\n" + selection + "\n\n" + _("Do you wish to continue?");
730 
732  return;
733  }
734 
735  const bool result = is_dir
736  ? fs::delete_directory(selection)
737  : fs::delete_file(selection);
738 
739  if(!result) {
741  VGETTEXT("Could not delete $path|. Make sure you have the appropriate permissions to write to this location.",
742  {{"path", selection}}));
743  } else {
745  }
746 }
747 
748 } // namespace dialogs
double t
Definition: astarsearch.cpp:63
Simple push button.
Definition: button.hpp:36
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: button.cpp:63
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:56
The listbox class.
Definition: listbox.hpp:43
bool select_last_row(const bool select=true)
Does exactly as advertised: selects the list's last row.
Definition: listbox.hpp:189
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:59
bool select_row(const unsigned row, const bool select=true)
Selects a row.
Definition: listbox.cpp:243
void remove_row(const unsigned row, unsigned count=1)
Removes a row in the listbox.
Definition: listbox.cpp:79
void clear()
Removes all the rows in the listbox, clearing it.
Definition: listbox.cpp:118
int get_selected_row() const
Returns the first selected row.
Definition: listbox.cpp:268
unsigned get_item_count() const
Returns the number of items in the listbox.
Definition: listbox.cpp:124
Base class for all visible items.
virtual void set_label(const t_string &text)
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:470
@ 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:63
void set_retval(const int retval, const bool close_window=true)
Sets there return value of the window.
Definition: window.hpp:399
void keyboard_capture(widget *widget)
Definition: window.cpp:1215
void add_to_keyboard_chain(widget *widget)
Adds the widget to the keyboard chain.
Definition: window.cpp:1221
void set_exit_hook(exit_hook mode, std::function< bool(window &)> func)
Sets the window's exit hook.
Definition: window.hpp:453
int get_retval()
Definition: window.hpp:406
@ on_all
Always run hook.
#define DBG_FILEDLG
Definition: file_dialog.cpp:43
static lg::log_domain log_filedlg
Definition: file_dialog.cpp:39
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:209
This file contains the window object, this object is a top level container which has the event manage...
Standard logging facilities (interface).
@ WAIT
Definition: cursor.hpp:28
std::vector< bookmark_info > user_bookmarks()
Definition: paths.cpp:279
unsigned add_user_bookmark(const std::string &label, const std::string &path)
Definition: paths.cpp:254
@ 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:267
std::vector< path_info > system_paths(std::set< SYSTEM_PATH_TYPES > paths)
Returns a list of system-defined paths.
Definition: paths.cpp:231
@ 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:198
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:404
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:318
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.
REGISTER_DIALOG(tod_new_schedule)
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:203
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:177
std::map< std::string, widget_item > widget_data
Definition: widget.hpp:34
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:150
@ 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:85
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:194
static map_location::DIRECTION n
#define a
#define b