The Battle for Wesnoth  1.15.2+dev
file_dialog.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2011, 2018 by Iris Morelle <shadowm2006@gmail.com>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
18 
19 #include "cursor.hpp"
20 #include "desktop/paths.hpp"
21 #include "filesystem.hpp"
22 #include "formula/string_utils.hpp"
25 #include "gui/dialogs/message.hpp"
27 #include "gui/widgets/button.hpp"
28 #include "gui/widgets/listbox.hpp"
29 #include "gui/widgets/settings.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 const std::string icon_file = "";
51 const std::string icon_parent = "";
52 // NOTE: Does not need to be the same as PARENT_DIR! Use PARENT_DIR to build
53 // relative paths for non-presentational purposes instead.
54 const std::string label_parent = "..";
55 
56 const std::string CURRENT_DIR = ".";
57 const std::string PARENT_DIR = "..";
58 
59 const int FILE_DIALOG_ITEM_RETVAL = 9001;
60 const int FILE_DIALOG_MAX_ENTRY_LENGTH = 42;
61 
62 inline std::string concat_path(const std::string& a, const std::string& b)
63 {
64  //
65  // As of Boost 1.61, normalize_path() displays unusual behavior when passing
66  // it paths with extra path separators (e.g. //opt becomes
67  // //opt/home/shadowm/src/wesnoth, where the extraneous sequence is probably
68  // the current working dir), so avoid leaving those around.
69  //
70  // TODO: Maybe handle this corner case in filesystem::normalize_path()
71  // instead, really.
72  //
73  if((a.empty() || !fs::is_path_sep(a.back())) && (b.empty() || !fs::is_path_sep(b.front()))) {
74  return a + fs::path_separator() + b;
75  } else {
76  return a + b;
77  }
78 }
79 
80 inline std::string filesystem_root()
81 {
82  // TODO: Multiple drives support (may require cooperation from the caller).
83  return std::string(1, fs::path_separator());
84 }
85 
86 inline void isort_dir_entries(std::vector<std::string>& entries)
87 {
88  // Yes, this uses Wesnoth's locale and not the filesystem/OS locale. Yes, this
89  // isn't ideal. No, we don't really need to worry about it. It's just a
90  // cosmetic procedure anyway.
91  std::sort(entries.begin(), entries.end(),
92  [](const std::string& a, const std::string& b) { return translation::icompare(a, b) < 0; });
93 }
94 
95 } // unnamed namespace
96 
97 namespace gui2
98 {
99 namespace dialogs
100 {
101 
102 REGISTER_DIALOG(file_dialog)
103 
105  : title_(_("Find File"))
106  , msg_()
107  , ok_label_()
108  , extension_()
109  , current_entry_()
110  , current_dir_()
111  , read_only_(false)
112  , save_mode_(false)
113  , dir_files_()
114  , dir_subdirs_()
115  , bookmark_paths_()
116  , current_bookmark_()
117  , user_bookmarks_begin_()
118 {
119  set_restore(true);
120 }
121 
122 std::string file_dialog::path() const
123 {
124  const std::string& dir_norm = fs::normalize_path(current_dir_, true);
125 
126  if(current_entry_.empty() || current_entry_ == CURRENT_DIR) {
127  return dir_norm;
128  } else if(current_entry_ == PARENT_DIR) {
129  return fs::directory_name(dir_norm);
130  }
131 
132  return concat_path(dir_norm, current_entry_);
133 }
134 
135 file_dialog& file_dialog::set_path(const std::string& value)
136 {
137  if(value.empty()) {
138  current_dir_ = filesystem_root();
139  }
140 
141  const std::string& norm = fs::normalize_path(value, true);
142 
143  if(fs::is_directory(norm)) {
144  current_dir_ = norm;
145  } else {
147  if(current_dir_.empty()) {
148  current_dir_ = filesystem_root();
149  }
150  // The file may or may not exist. We'll find out eventually when setting up
151  // the dialog.
153  }
154 
155  return *this;
156 }
157 
158 file_dialog& file_dialog::set_filename(const std::string& value)
159 {
160  current_entry_ = value;
161 
162  return *this;
163 }
164 
166 {
167  styled_widget& title = find_widget<styled_widget>(&window, "title", false);
168  styled_widget& message = find_widget<styled_widget>(&window, "message", false);
169  styled_widget& ok = find_widget<styled_widget>(&window, "ok", false);
170 
171  title.set_label(title_);
172 
173  if(msg_.empty()) {
174  message.set_visible(gui2::widget::visibility::invisible);
175  } else {
176  message.set_label(msg_);
177  message.set_use_markup(true);
178  }
179 
180  if(ok_label_.empty()) {
181  ok.set_label(save_mode_ ? _("Save") : _("Open"));
182  } else {
183  ok.set_label(ok_label_);
184  }
185 
186  listbox& bookmarks_bar = find_widget<listbox>(&window, "bookmarks", false);
187 
188  find_widget<styled_widget>(&window, "current_dir", false).set_text_ellipse_mode(PANGO_ELLIPSIZE_START);
189 
190  //
191  // Push hard-coded bookmarks.
192  //
193 
194  std::vector<desktop::path_info> bookmarks = desktop::game_paths();
195  const auto& sys_paths = desktop::system_paths();
196  bookmarks.insert(bookmarks.end(), sys_paths.begin(), sys_paths.end());
197 
198  bookmark_paths_.clear();
200 
201  std::map<std::string, string_map> data;
202 
203  for(const auto& pinfo : bookmarks) {
204  bookmark_paths_.push_back(pinfo.path);
205  data["bookmark"]["label"] = pinfo.display_name();
206  bookmarks_bar.add_row(data);
207  }
208 
209  //
210  // Push user-defined bookmarks.
211  //
212 
213  const std::vector<desktop::bookmark_info>& user_bookmarks = desktop::user_bookmarks();
214 
215  if(!user_bookmarks.empty()) {
217  }
218 
219  for(const auto& bookmark : user_bookmarks) {
220  bookmark_paths_.push_back(bookmark.path);
221  data["bookmark"]["label"] = bookmark.label;
222  bookmarks_bar.add_row(data);
223  }
224 
225  sync_bookmarks_bar(window);
226 
227  listbox& filelist = find_widget<listbox>(&window, "filelist", false);
228 
230  std::bind(&file_dialog::on_row_selected, this, std::ref(window)));
231  connect_signal_notify_modified(bookmarks_bar,
232  std::bind(&file_dialog::on_bookmark_selected, this, std::ref(window)));
233 
234  button& mkdir_button = find_widget<button>(&window, "new_dir", false);
235  button& rm_button = find_widget<button>(&window, "delete_file", false);
236  button& bookmark_add_button = find_widget<button>(&window, "add_bookmark", false);
237  button& bookmark_del_button = find_widget<button>(&window, "remove_bookmark", false);
238 
239  connect_signal_mouse_left_click(mkdir_button,
240  std::bind(&file_dialog::on_dir_create_cmd, this, std::ref(window)));
242  std::bind(&file_dialog::on_file_delete_cmd, this, std::ref(window)));
243  connect_signal_mouse_left_click(bookmark_add_button,
244  std::bind(&file_dialog::on_bookmark_add_cmd, this, std::ref(window)));
245  connect_signal_mouse_left_click(bookmark_del_button,
246  std::bind(&file_dialog::on_bookmark_del_cmd, this, std::ref(window)));
247 
248  if(read_only_) {
249  mkdir_button.set_active(false);
250  rm_button.set_active(false);
251 
253  rm_button.set_visible(widget::visibility::invisible);
254  }
255 
256  refresh_fileview(window);
257 
258  window.keyboard_capture(find_widget<text_box>(&window, "filename", false, true));
259  window.add_to_keyboard_chain(&filelist);
260  window.set_exit_hook(std::bind(&file_dialog::on_exit, this, std::ref(window)));
261 }
262 
264 {
265  if(window.get_retval() == FILE_DIALOG_ITEM_RETVAL) {
266  // Attempting to exit by double clicking items -- only proceeds if the item
267  // was a file.
268  if(process_fileview_submit(window)) {
269  window.set_retval(retval::OK, false);
270  return true;
271  } else {
272  return false;
273  }
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(window);
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?");
303  return gui2::show_message(_("Confirm"), message, message::yes_no_buttons) != gui2::retval::CANCEL;
304 }
305 
306 bool file_dialog::process_submit_common(window& window, 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_ << '\n';
311 
312  if(is_selection_type_acceptable(stype)) {
313  return save_mode_ ? confirm_overwrite(window, stype) : true;
314  }
315 
316  switch(stype) {
317  case SELECTION_IS_DIR:
318  // TODO: Adapt for implementing directory selection mode.
319  sync_bookmarks_bar(window);
320  refresh_fileview(window);
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>(&window, "filelist", false);
346  const std::string& selected_name = get_filelist_selection(filelist);
347  return process_submit_common(window, selected_name);
348 }
349 
351 {
352  text_box& file_textbox = find_widget<text_box>(&window, "filename", false);
353  const std::string& input_name = file_textbox.get_value();
354  return !input_name.empty() && process_submit_common(window, 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\n";
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\n";
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\n";
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 << '\n';
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 << '\n';
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 << '\n';
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\n";
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\n";
445 }
446 
447 void file_dialog::set_input_text(text_box& t, const std::string& value)
448 {
449  if(value.empty()) {
450  clear_input_text(t);
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()) {
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>(&window, "filelist", false);
495  button& rm_button = find_widget<button>(&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>(&window, "current_dir", false).set_label(current_dir_);
521  set_input_text(find_widget<text_box>(&window, "filename", false), current_entry_);
522 
523  on_row_selected(window);
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 
533  std::map<std::string, string_map> data;
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>(&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>(&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>(&window, "filelist", false);
596  text_box& file_textbox = find_widget<text_box>(&window, "filename", false);
597  button& rm_button = find_widget<button>(&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  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>(&window, "filename", false);
621  window.keyboard_capture(&file_textbox);
622 
623  listbox& bookmarks_bar = find_widget<listbox>(&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]);
639  refresh_fileview(window);
640 
641  // Update bookmark edit controls.
642  button& del_button = find_widget<button>(&window, "remove_bookmark", false);
643  del_button.set_active(user_bookmarks_begin_ >= 0
644  && current_bookmark_ >= user_bookmarks_begin_);
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>(&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 
672  std::map<std::string, string_map> data;
673  data["bookmark"]["label"] = label;
674  bookmarks_bar.add_row(data);
675 
676  current_bookmark_ = -1;
677 
678  sync_bookmarks_bar(window);
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>(&window, "bookmarks", false);
691  bookmarks_bar.remove_row(current_bookmark_);
692 
693  current_bookmark_ = -1;
694 
695  sync_bookmarks_bar(window);
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 {
710  refresh_fileview(window);
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 {
742  refresh_fileview(window);
743  }
744 }
745 
746 } // namespace dialogs
747 } // namespace gui2
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().
Dialog was closed with the CANCEL button.
Definition: retval.hpp:37
bool delete_directory(const std::string &dirname, const bool keep_pbl)
Definition: filesystem.cpp:934
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:152
void set_selection(std::size_t start, int length)
Sets or clears the text selection.
void set_input_text(class text_box &t, const std::string &value)
std::vector< std::string > dir_subdirs_
std::string get_filelist_selection(class listbox &filelist)
void remove_user_bookmark(unsigned index)
Definition: paths.cpp:254
bool delete_file(const std::string &filename)
Definition: filesystem.cpp:973
file_dialog & set_path(const std::string &value)
Sets the initial file selection.
Main class to show messages to the user.
Definition: message.hpp:34
void clear_input_text(class text_box &t)
#define a
void on_file_delete_cmd(window &window)
Handles Delete button press events.
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:266
std::string get_value() const
This file contains the window object, this object is a top level container which has the event manage...
bool process_fileview_submit(window &window)
Processes file view selection in reaction to row double-click events.
static bool execute(std::string &bookmark_name)
The execute function; see modal_dialog for more information.
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. ...
std::vector< bookmark_info > user_bookmarks()
Definition: paths.cpp:266
void on_bookmark_add_cmd(window &window)
Handles Add Bookmark button press events.
bool select_last_row(const bool select=true)
Does exactly as advertised: selects the list&#39;s last row.
Definition: listbox.hpp:192
bool process_submit_common(window &window, const std::string &name)
Label showing a text.
Definition: label.hpp:32
int get_selected_row() const
Returns the first selected row.
Definition: listbox.cpp:272
std::string normalize_path(const std::string &fpath, bool normalize_separators, bool resolve_dot_entries)
Returns the absolute path of a file.
void on_bookmark_selected(window &window)
Handles selection or deselection of bookmarks.
static lg::log_domain log_filedlg
Definition: file_dialog.cpp:39
bool select_row(const unsigned row, const bool select=true)
Selects a row.
Definition: listbox.cpp:250
Class for a single line text area.
Definition: text_box.hpp:121
std::vector< path_info > game_paths(unsigned path_types)
Returns a list of game-related paths.
Definition: paths.cpp:190
Generic file dialog.
Definition: field-fwd.hpp:22
void sync_bookmarks_bar(window &window)
Updates the bookmarks bar state to reflect the internal state.
#define b
std::string path() const
Gets the current file selection.
virtual void set_label(const t_string &label)
The listbox class.
Definition: listbox.hpp:40
Base container class.
Definition: grid.hpp:30
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:91
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification_function &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:248
This file contains the settings handling of the widget library.
void clear()
Removes all the rows in the listbox, clearing it.
Definition: listbox.cpp:125
void set_visible(const visibility visible)
Definition: widget.cpp:473
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal_function &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:233
std::vector< path_info > system_paths(unsigned path_types)
Returns a list of system-defined paths.
Definition: paths.cpp:218
const std::string & title() const
Gets the current dialog title text.
Definition: file_dialog.hpp:48
std::string root_name(const std::string &path)
Returns the name of the root device if included in the given path.
file_dialog & set_filename(const std::string &value)
Sets the initial file name input but not the path.
std::string nearest_extant_parent(const std::string &file)
Finds the nearest parent in existence for a file or directory.
std::vector< std::string > dir_files_
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
#define DBG_FILEDLG
Definition: file_dialog.cpp:43
unsigned get_item_count() const
Returns the number of items in the listbox.
Definition: listbox.cpp:131
Shows a yes and no button.
Definition: message.hpp:79
Desktop paths, storage media and bookmark functions.
SELECTION_TYPE register_new_selection(const std::string &name)
Updates the internal state and returns the type of the selection.
bool confirm_overwrite(window &window, SELECTION_TYPE stype)
Prompts the user before overwriting an existing file.
void refresh_fileview(window &window)
Updates the dialog contents to match the internal state.
Various uncategorised dialogs.
int get_retval()
Definition: window.hpp:370
bool is_path_sep(char c)
Returns whether c is a path separator.
std::size_t get_length() const
void on_row_selected(window &window)
Handles file/directory selection on single-click.
bool on_exit(window &window)
Handles dialog exit events and decides whether to proceed or not.
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs, file_name_option mode, file_filter_option filter, file_reorder_option reorder, file_tree_checksum *checksum)
Populates &#39;files&#39; with all the files and &#39;dirs&#39; with all the directories in dir.
Definition: filesystem.cpp:352
bool is_relative(const std::string &path)
Returns whether the path seems to be relative.
void on_bookmark_del_cmd(window &window)
Handles Remove Bookmark button press events.
The user set the widget invisible, that means:
unsigned add_user_bookmark(const std::string &label, const std::string &path)
Definition: paths.cpp:241
bool make_directory(const std::string &dirname)
Definition: filesystem.cpp:923
bool is_root(const std::string &path)
Returns whether the path is the root of the file hierarchy.
std::vector< std::string > bookmark_paths_
grid & add_row(const string_map &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:66
Declarations for File-IO.
void on_dir_create_cmd(window &window)
Handles New Folder button press events.
void set_retval(const int retval, const bool close_window=true)
Sets there return value of the window.
Definition: window.hpp:363
Base class for all visible items.
static int sort(lua_State *L)
Definition: ltablib.cpp:411
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
char path_separator()
Returns the standard path separator for the current platform.
std::string base_name(const std::string &file, const bool remove_extension)
Returns the base filename of a file, with directory name stripped.
virtual void set_value(const std::string &text)
The set_value is virtual for the password_box class.
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.
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: button.cpp:62
double t
Definition: astarsearch.cpp:64
bool find(E event, F functor)
Tests whether an event handler is available.
int icompare(const std::string &s1, const std::string &s2)
Case-insensitive lexicographical comparison.
Definition: gettext.cpp:476
Simple push button.
Definition: button.hpp:35
Standard logging facilities (interface).
void remove_row(const unsigned row, unsigned count=1)
Removes a row in the listbox.
Definition: listbox.cpp:86
Dialog was closed with the OK button.
Definition: retval.hpp:34
static map_location::DIRECTION n
base class of top level items, the only item which needs to store the final canvases to draw on ...
Definition: window.hpp:62
virtual void pre_show(window &window) override
Inherited from modal_dialog.
bool is_selection_type_acceptable(SELECTION_TYPE stype) const
Returns whether the given selection type is acceptable for closing the dialog.
std::string directory_name(const std::string &file)
Returns the directory name of a file, with filename stripped.
bool process_textbox_submit(window &window)
Processes textbox input in reaction to OK button/Enter key events.