File chooser widget added. Now used in the editor for save as and load.

This commit is contained in:
Kristoffer Erlandsson 2004-05-23 22:23:10 +00:00
parent 0bdf49c8df
commit 6fab335aa4
11 changed files with 645 additions and 16 deletions

View file

@ -79,6 +79,7 @@ wesnoth_SOURCES = about.cpp \
unit_types.cpp \
video.cpp \
widgets/button.cpp \
widgets/file_chooser.cpp \
widgets/combo.cpp \
widgets/menu.cpp \
widgets/scrollbar.cpp \
@ -142,6 +143,7 @@ wesnoth_SOURCES = about.cpp \
util.hpp \
video.hpp \
widgets/button.hpp \
widgets/file_chooser.hpp \
widgets/combo.hpp \
widgets/menu.hpp \
widgets/scrollbar.hpp \
@ -208,6 +210,7 @@ wesnoth_editor_SOURCES = editor/editor.cpp \
unit_types.cpp \
video.cpp \
widgets/button.cpp \
widgets/file_chooser.cpp \
widgets/menu.cpp \
widgets/textbox.cpp \
widgets/scrollbar.cpp \
@ -267,6 +270,7 @@ wesnoth_editor_SOURCES = editor/editor.cpp \
unit_types.hpp \
video.hpp \
widgets/button.hpp \
widgets/file_chooser.hpp \
widgets/menu.hpp \
widgets/textbox.hpp \
widgets/scrollbar.hpp \

View file

@ -221,6 +221,7 @@ wesnoth_SOURCES = about.cpp \
unit_types.cpp \
video.cpp \
widgets/button.cpp \
widgets/file_chooser.cpp \
widgets/combo.cpp \
widgets/menu.cpp \
widgets/scrollbar.cpp \
@ -284,6 +285,7 @@ wesnoth_SOURCES = about.cpp \
util.hpp \
video.hpp \
widgets/button.hpp \
widgets/file_chooser.hpp \
widgets/combo.hpp \
widgets/menu.hpp \
widgets/scrollbar.hpp \
@ -351,6 +353,7 @@ wesnoth_editor_SOURCES = editor/editor.cpp \
unit_types.cpp \
video.cpp \
widgets/button.cpp \
widgets/file_chooser.cpp \
widgets/menu.cpp \
widgets/textbox.cpp \
widgets/scrollbar.cpp \
@ -410,6 +413,7 @@ wesnoth_editor_SOURCES = editor/editor.cpp \
unit_types.hpp \
video.hpp \
widgets/button.hpp \
widgets/file_chooser.hpp \
widgets/menu.hpp \
widgets/textbox.hpp \
widgets/scrollbar.hpp \
@ -450,8 +454,9 @@ am_wesnoth_OBJECTS = about.$(OBJEXT) actions.$(OBJEXT) ai.$(OBJEXT) \
terrain.$(OBJEXT) theme.$(OBJEXT) titlescreen.$(OBJEXT) \
tooltips.$(OBJEXT) unit.$(OBJEXT) unit_display.$(OBJEXT) \
unit_types.$(OBJEXT) video.$(OBJEXT) button.$(OBJEXT) \
combo.$(OBJEXT) menu.$(OBJEXT) scrollbar.$(OBJEXT) \
slider.$(OBJEXT) textbox.$(OBJEXT) widget.$(OBJEXT)
file_chooser.$(OBJEXT) combo.$(OBJEXT) menu.$(OBJEXT) \
scrollbar.$(OBJEXT) slider.$(OBJEXT) textbox.$(OBJEXT) \
widget.$(OBJEXT)
wesnoth_OBJECTS = $(am_wesnoth_OBJECTS)
wesnoth_LDADD = $(LDADD)
wesnoth_DEPENDENCIES =
@ -475,8 +480,9 @@ am_wesnoth_editor_OBJECTS = editor.$(OBJEXT) editor_layout.$(OBJEXT) \
statistics.$(OBJEXT) team.$(OBJEXT) terrain.$(OBJEXT) \
theme.$(OBJEXT) tooltips.$(OBJEXT) unit.$(OBJEXT) \
unit_display.$(OBJEXT) unit_types.$(OBJEXT) video.$(OBJEXT) \
button.$(OBJEXT) menu.$(OBJEXT) textbox.$(OBJEXT) \
scrollbar.$(OBJEXT) slider.$(OBJEXT) widget.$(OBJEXT)
button.$(OBJEXT) file_chooser.$(OBJEXT) menu.$(OBJEXT) \
textbox.$(OBJEXT) scrollbar.$(OBJEXT) slider.$(OBJEXT) \
widget.$(OBJEXT)
wesnoth_editor_OBJECTS = $(am_wesnoth_editor_OBJECTS)
wesnoth_editor_LDADD = $(LDADD)
wesnoth_editor_DEPENDENCIES =
@ -497,6 +503,7 @@ am__depfiles_maybe = depfiles
@AMDEP_TRUE@ ./$(DEPDIR)/editor_main.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/editor_palettes.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/editor_undo.Po ./$(DEPDIR)/events.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/file_chooser.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/filesystem.Po ./$(DEPDIR)/font.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/game.Po ./$(DEPDIR)/game_config.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/game_events.Po \
@ -610,6 +617,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/editor_palettes.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/editor_undo.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/events.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_chooser.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filesystem.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/font.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/game.Po@am__quote@
@ -703,6 +711,28 @@ button.obj: widgets/button.cpp
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o button.obj `if test -f 'widgets/button.cpp'; then $(CYGPATH_W) 'widgets/button.cpp'; else $(CYGPATH_W) '$(srcdir)/widgets/button.cpp'; fi`
file_chooser.o: widgets/file_chooser.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT file_chooser.o -MD -MP -MF "$(DEPDIR)/file_chooser.Tpo" \
@am__fastdepCXX_TRUE@ -c -o file_chooser.o `test -f 'widgets/file_chooser.cpp' || echo '$(srcdir)/'`widgets/file_chooser.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/file_chooser.Tpo" "$(DEPDIR)/file_chooser.Po"; \
@am__fastdepCXX_TRUE@ else rm -f "$(DEPDIR)/file_chooser.Tpo"; exit 1; \
@am__fastdepCXX_TRUE@ fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='widgets/file_chooser.cpp' object='file_chooser.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ depfile='$(DEPDIR)/file_chooser.Po' tmpdepfile='$(DEPDIR)/file_chooser.TPo' @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o file_chooser.o `test -f 'widgets/file_chooser.cpp' || echo '$(srcdir)/'`widgets/file_chooser.cpp
file_chooser.obj: widgets/file_chooser.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT file_chooser.obj -MD -MP -MF "$(DEPDIR)/file_chooser.Tpo" \
@am__fastdepCXX_TRUE@ -c -o file_chooser.obj `if test -f 'widgets/file_chooser.cpp'; then $(CYGPATH_W) 'widgets/file_chooser.cpp'; else $(CYGPATH_W) '$(srcdir)/widgets/file_chooser.cpp'; fi`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/file_chooser.Tpo" "$(DEPDIR)/file_chooser.Po"; \
@am__fastdepCXX_TRUE@ else rm -f "$(DEPDIR)/file_chooser.Tpo"; exit 1; \
@am__fastdepCXX_TRUE@ fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='widgets/file_chooser.cpp' object='file_chooser.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ depfile='$(DEPDIR)/file_chooser.Po' tmpdepfile='$(DEPDIR)/file_chooser.TPo' @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o file_chooser.obj `if test -f 'widgets/file_chooser.cpp'; then $(CYGPATH_W) 'widgets/file_chooser.cpp'; else $(CYGPATH_W) '$(srcdir)/widgets/file_chooser.cpp'; fi`
combo.o: widgets/combo.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT combo.o -MD -MP -MF "$(DEPDIR)/combo.Tpo" \
@am__fastdepCXX_TRUE@ -c -o combo.o `test -f 'widgets/combo.cpp' || echo '$(srcdir)/'`widgets/combo.cpp; \

View file

@ -19,6 +19,7 @@
#include "replay.hpp"
#include "show_dialog.hpp"
#include "util.hpp"
#include "widgets/file_chooser.hpp"
#include <cstdio>
#include <map>
@ -262,4 +263,74 @@ void unit_speak(const config& message_info, display& disp, const unit_map& units
}
}
int show_file_chooser_dialog(display &disp, std::string &filename,
const std::string title,
int xloc, int yloc) {
const events::event_context dialog_events_context;
const gui::dialog_manager manager;
const events::resize_lock prevent_resizing;
CVideo& screen = disp.video();
SDL_Surface* const scr = screen.getSurface();
SDL_Rect clipRect = disp.screen_area();
const int width = 400;
const int height = 400;
const int left_padding = 10;
const int right_padding = 10;
const int top_padding = 10;
const int bot_padding = 10;
// If not both locations were supplied, put the dialog in the middle
// of the screen.
if (yloc <= -1 || xloc <= -1) {
xloc = scr->w / 2 - width / 2;
yloc = scr->h / 2 - height / 2;
}
std::vector<gui::button*> buttons_ptr;
gui::button ok_button_(disp, string_table["ok_button"]);
gui::button cancel_button_(disp, string_table["cancel_button"]);
buttons_ptr.push_back(&ok_button_);
buttons_ptr.push_back(&cancel_button_);
surface_restorer restorer;
gui::draw_dialog(xloc, yloc, width, height, disp, title, NULL, &buttons_ptr, &restorer);
gui::file_chooser fc(disp, filename);
fc.set_location(xloc + left_padding, yloc + top_padding);
fc.set_width(width - left_padding - right_padding);
fc.set_height(height - top_padding - bot_padding);
fc.set_dirty(true);
events::raise_draw_event();
screen.flip();
disp.invalidate_all();
CKey key;
for (;;) {
events::pump();
events::raise_process_event();
events::raise_draw_event();
if (fc.choice_made()) {
filename = fc.get_choice();
return 0; // We know that the OK button is on index 0.
}
if (key[SDLK_ESCAPE]) {
// Escape quits from the dialog.
return -1;
}
for (std::vector<gui::button*>::iterator button_it = buttons_ptr.begin();
button_it != buttons_ptr.end(); button_it++) {
if ((*button_it)->pressed()) {
// Return the index of the pressed button.
filename = fc.get_choice();
return button_it - buttons_ptr.begin();
}
}
screen.flip();
SDL_Delay(10);
}
}
} //end namespace dialogs

View file

@ -49,6 +49,15 @@ std::string load_game_dialog(display& disp, bool* show_replay);
void unit_speak(const config& message_info, display& disp, const unit_map& units);
/// Show a dialog where the user can navigate through files and select a
/// file. The filename is used as a starting point in the navigation and
/// contains the chosen file when the function returns. Return the
/// index of the button pressed, or -1 if the dialog was canceled
/// through keypress.
int show_file_chooser_dialog(display &displ, std::string &filename,
const std::string title="Choose a File",
int xloc=-1, int yloc=-1);
}
#endif

View file

@ -213,13 +213,13 @@ void map_editor::handle_mouse_button_event(const SDL_MouseButtonEvent &event,
void map_editor::edit_save_as() {
std::string input_name = get_dir(get_dir(get_user_data_dir() + "/editor") + "/maps/");
const std::string default_dir =
get_dir(get_dir(get_user_data_dir() + "/editor") + "/maps/");
std::string input_name = filename_ == "" ? default_dir : filename_;
int res = 0;
int overwrite = 0;
do {
res = gui::show_dialog(gui_, NULL, "", "Save As ", gui::OK_CANCEL,
NULL, NULL, "", &input_name);
res = dialogs::show_file_chooser_dialog(gui_, input_name, "Choose a File to Save As");
if (res == 0) {
if (file_exists(input_name)) {
overwrite = gui::show_dialog(gui_, NULL, "Overwrite?",
@ -291,8 +291,10 @@ void map_editor::edit_new_map() {
}
void map_editor::edit_load_map() {
const std::string fn = load_map_dialog(gui_);
if (fn != "") {
std::string fn = filename_ == "" ?
get_dir(get_dir(get_user_data_dir() + "/editor") + "/maps/") : filename_;
int res = dialogs::show_file_chooser_dialog(gui_, fn, "Choose a Map to Load");
if (res == 0) {
const std::string new_map = load_map(fn);
if (new_map != "") {
if (!changed_since_save() || confirm_modification_disposal(gui_)) {

View file

@ -15,6 +15,7 @@
#include "../display.hpp"
#include "../show_dialog.hpp"
#include "../config.hpp"
#include "../events.hpp"
#include "../game_config.hpp"
#include "../mapgen.hpp"
#include "../filesystem.hpp"
@ -529,3 +530,5 @@ FLIP_AXIS flip_dialog(display &disp) {
}

View file

@ -54,7 +54,6 @@ resize_dialog(display &disp, const unsigned curr_w, const unsigned curr_h);
FLIP_AXIS flip_dialog(display &disp);
}

View file

@ -0,0 +1,324 @@
/*
Copyright (C) 2003 by David White <davidnwhite@optusnet.com.au>
Part of the Battle for Wesnoth Project http://wesnoth.whitevine.net
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#include "../font.hpp"
#include "../events.hpp"
#include "../display.hpp"
#include "file_chooser.hpp"
namespace gui {
file_chooser::file_chooser(display &disp, std::string start_file)
: widget(disp), disp_(disp), path_delim_('/'), current_dir_(get_path(start_file)),
chosen_file_(start_file), file_list_(disp, files_in_current_dir_, false),
filename_textbox_(disp, 100, start_file, true), choice_made_(false),
last_selection_(-1) {
// If the start file is not a file or directory, use the root.
if (!file_exists(chosen_file_) || !is_directory(current_dir_)) {
current_dir_ = path_delim_;
chosen_file_ = current_dir_;
}
// Set sizes to some default values.
set_location(1, 1);
set_width(400);
set_height(500);
update_file_lists();
}
void file_chooser::adjust_layout() {
const int current_path_y = location().y;
current_path_rect_.y = current_path_y;
current_path_rect_.w = width();
current_path_rect_.x = location().x;
current_path_rect_.h = 18;
const int file_list_y = current_path_y + current_path_rect_.h + 10;
const int filename_textbox_y = location().y + height() - filename_textbox_.height();
const int file_list_height = filename_textbox_y - file_list_y - 10;
file_list_.set_width(width());
filename_textbox_.set_width(width());
file_list_.set_loc(location().x, file_list_y);
filename_textbox_.set_location(location().x, filename_textbox_y);
file_list_.set_max_height(file_list_height);
// When the layout has changed we want to redisplay the files to
// make them fit into the newly adjusted widget.
set_dirty(true);
}
void file_chooser::display_current_files() {
bg_restore();
std::vector<std::string> to_show;
if (!is_root(current_dir_)) {
to_show.push_back("..");
}
std::copy(dirs_in_current_dir_.begin(), dirs_in_current_dir_.end(),
std::back_inserter(to_show));
std::vector<std::string>::iterator it;
for (it = to_show.begin(); it != to_show.end(); it++) {
// Add a delimiter to show that these are directories.
(*it) += path_delim_;
}
std::copy(files_in_current_dir_.begin(), files_in_current_dir_.end(),
std::back_inserter(to_show));
const int menu_font_size = 14; // Known from menu.cpp.
for (it = to_show.begin(); it != to_show.end(); it++) {
// Make sure that all lines fit.
// Guess the width of the scrollbar to be 30 since it is not accessible from here.
while (font::line_width(*it, menu_font_size) > file_list_.width() - 30) {
(*it).resize((*it).size() - 1);
}
}
file_list_.set_items(to_show);
// This will prevent the "box" with filenames from changing size on
// every redisplay, it looks better when it's static.
file_list_.set_width(width());
}
void file_chooser::display_chosen_file() {
// Clearing is not really necessary, but things end up nicer of we do.
filename_textbox_.clear();
if (is_directory(chosen_file_)) {
filename_textbox_.set_text(strip_last_delim(chosen_file_) + path_delim_);
}
else {
filename_textbox_.set_text(chosen_file_);
}
}
void file_chooser::draw() {
if (!dirty()) {
return;
}
display_current_files();
display_chosen_file();
font::draw_text(&disp_, current_path_rect_, 14, font::NORMAL_COLOUR,
current_dir_, current_path_rect_.x, current_path_rect_.y,
disp_.video().getSurface());
set_dirty(false);
}
void file_chooser::process() {
CKey key;
int mousex, mousey;
const int mouse_flags = SDL_GetMouseState(&mousex,&mousey);
// The menu does not implement focus functionality, so we fake
// it. We give the file list focus whenever the filename textbox
// does not have focus. Inflexible but easy solution.
if (!(mousex > location().x && (unsigned)mousex < location().x + width()
&& mousey > location().y
&& (unsigned)mousey < location().y + height() - filename_textbox_.height())) {
// Hmm, as I understand it this should happen automatically when
// the mouse is in the textbox again. However this is not the
// case so this is done explicitly here.
filename_textbox_.set_focus(true);
}
else {
filename_textbox_.set_focus(false);
}
if (!filename_textbox_.focus()) {
const bool new_left_button = mouse_flags&SDL_BUTTON_LMASK;
const bool new_up_arrow = key[SDLK_UP];
const bool new_down_arrow = key[SDLK_DOWN];
const bool new_page_up = key[SDLK_PAGEUP];
const bool new_page_down = key[SDLK_PAGEDOWN];
file_list_.process(mousex, mousey, new_left_button, new_up_arrow,
new_down_arrow, new_page_up, new_page_down, -1);
const int new_selection = file_list_.selection();
const bool double_click = file_list_.double_clicked();
if (double_click && new_selection >= 0) {
last_selection_ = new_selection;
entry_chosen(new_selection);
}
if (new_selection >= 0 && last_selection_ != new_selection) {
last_selection_ = new_selection;
entry_selected(new_selection);
}
}
}
void file_chooser::entry_selected(const unsigned entry) {
const int entry_index = entry - (is_root(current_dir_) ? 0 : 1);
if (entry_index >= 0) {
// Do not change the selection if the parent directory entry is selected.
std::string selected;
if ((unsigned)entry_index < dirs_in_current_dir_.size()) {
const int dir_index = entry_index;
selected = dirs_in_current_dir_[dir_index];
}
else {
const int file_index = entry_index - dirs_in_current_dir_.size();
selected = files_in_current_dir_[file_index];
}
chosen_file_ = add_path(current_dir_, selected);
display_chosen_file();
}
}
/// Enter the directory or choose the file.
void file_chooser::entry_chosen(const unsigned entry) {
const int entry_index = entry - (is_root(current_dir_) ? 0 : 1);
if (entry_index == -1) {
// Parent dir wanted.
if (!is_root(current_dir_)) {
current_dir_ = get_path_up(current_dir_);
update_file_lists();
chosen_file_ = current_dir_;
}
else {
return;
}
}
else {
if ((unsigned)entry_index < dirs_in_current_dir_.size()) {
// Descend into a directory.
const int dir_index = entry_index;
const std::string selected_dir = dirs_in_current_dir_[dir_index];
current_dir_ = add_path(current_dir_, selected_dir);
chosen_file_ = current_dir_;
update_file_lists();
}
else {
// Choose a file.
const int file_index = entry_index - dirs_in_current_dir_.size();
const std::string selected_file = files_in_current_dir_[file_index];
chosen_file_ = add_path(current_dir_, selected_file);
choice_made_ = true;
}
}
set_dirty(true);
}
bool file_chooser::choice_made() const {
return choice_made_;
}
std::string file_chooser::get_choice() const {
if (filename_textbox_.focus()) {
return filename_textbox_.text();
}
return chosen_file_;
}
void file_chooser::set_dirty(bool dirty) {
widget::set_dirty(dirty);
filename_textbox_.set_dirty(dirty);
}
void file_chooser::set_location(const SDL_Rect& rect) {
widget::set_location(rect);
adjust_layout();
}
void file_chooser::set_location(int x, int y) {
widget::set_location(x, y);
adjust_layout();
}
void file_chooser::set_width(int w) {
widget::set_width(w);
adjust_layout();
}
void file_chooser::set_height(int h) {
widget::set_height(h);
adjust_layout();
}
std::string file_chooser::get_path(const std::string file_or_dir) const {
std::string res_path = file_or_dir;
if (!is_directory(file_or_dir)) {
size_t index = file_or_dir.find_last_of(path_delim_);
if (index != std::string::npos) {
res_path = file_or_dir.substr(0, index);
}
}
return res_path;
}
std::string file_chooser::get_path_up(const std::string path, const unsigned levels) const {
std::string curr_path = get_path(path);
for (unsigned i = 0; i < levels; i++) {
if (is_root(curr_path)) {
break;
}
curr_path = strip_last_delim(curr_path);
size_t index = curr_path.find_last_of(path_delim_);
if (index != std::string::npos) {
curr_path = curr_path.substr(0, index);
}
else {
break;
}
}
if (curr_path.size() == 0) {
// The root was reached, represent this as one delimiter only.
curr_path = path_delim_;
}
return curr_path;
}
std::string file_chooser::strip_last_delim(const std::string path) const {
std::string res_string = path;
if (path[path.size() - 1] == path_delim_) {
res_string = path.substr(0, path.size() - 1);
}
return res_string;
}
bool file_chooser::is_root(const std::string path) const {
return path.size() == 0 || path.size() == 1 && path[0] == path_delim_;
}
std::string file_chooser::add_path(const std::string path, const std::string to_add) {
std::string joined_path = strip_last_delim(path);
if (to_add.size() > 0) {
if (to_add[0] == path_delim_) {
joined_path += to_add;
}
else {
joined_path += "/" + to_add;
}
}
return joined_path;
}
void file_chooser::update_file_lists() {
files_in_current_dir_.clear();
dirs_in_current_dir_.clear();
get_files_in_dir(current_dir_, &files_in_current_dir_,
&dirs_in_current_dir_, FILE_NAME_ONLY);
}
void file_chooser::handle_event(const SDL_Event& event) {
if (event.type == SDL_KEYDOWN) {
if (event.key.keysym.sym == SDLK_RETURN) {
if (filename_textbox_.focus()) {
chosen_file_ = filename_textbox_.text();
choice_made_ = true;
}
else {
const int selected = file_list_.selection();
if (selected >= 0) {
entry_chosen(selected);
}
}
}
}
}
}

View file

@ -0,0 +1,109 @@
/*
Copyright (C) 2003 by David White <davidnwhite@optusnet.com.au>
Part of the Battle for Wesnoth Project http://wesnoth.whitevine.net
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef FILE_CHOOSER_H_INCLUDED
#define FILE_CHOOSER_H_INCLUDED
#include "../display.hpp"
#include "widget.hpp"
#include "menu.hpp"
#include "textbox.hpp"
#include "../filesystem.hpp"
#include "../language.hpp"
namespace gui {
/// A widget where the user may navigate through directories and choose
/// a file.
class file_chooser : public gui::widget {
public:
/// Initialize the file chooser. start_file is the file that will be
/// selected when the dialog starts. The current directory will be
/// the one the file is in.
file_chooser(display &disp, std::string start_file="");
void draw();
void process();
void set_dirty(bool dirty=true);
void set_location(const SDL_Rect& rect);
void set_location(int x, int y);
void set_width(int w);
void set_height(int h);
/// Return true if the user is done making her choice.
bool choice_made() const;
/// Return the choosen file.
std::string get_choice() const;
protected:
virtual void handle_event(const SDL_Event& event);
private:
/// If file_or_dir is a file, return the directory the file is in,
/// if it is a directory, return the directory name. If no path
/// delimiters could be found, return the unchanged argument.
std::string get_path(const std::string file_or_dir) const;
/// Return the path that is the specified number of levels up from
/// the path. If the movement could not proceed due to being at the
/// root or having an invalid argument, return the path that the
/// movement ended on.
std::string get_path_up(const std::string path,
const unsigned levels=1) const;
/// Return path with to_add added, using a path delimiter between them.
std::string add_path(const std::string path, const std::string to_add);
/// Return the string with the last path delimiter removed, if one
/// was there.
std::string strip_last_delim(const std::string path) const;
/// Return true if the path is the root of the filesystem.
bool is_root(const std::string path) const;
/// Adjust the layout of the widget.
void adjust_layout();
/// Show the files in the current directory.
void display_current_files();
/// Display the currently selected file in the file text box.
void display_chosen_file();
/// Updated the locally maintained lists of files and directories in
/// the current directory.
void update_file_lists();
/// Set the textbox to reflect the selected file.
void entry_selected(const unsigned entry);
/// Enter the directory or choose the file.
void entry_chosen(const unsigned entry);
display &disp_;
const char path_delim_;
std::string current_dir_;
std::string chosen_file_;
std::vector<std::string> files_in_current_dir_, dirs_in_current_dir_;
menu file_list_;
textbox filename_textbox_;
SDL_Rect current_path_rect_;
bool choice_made_;
int last_selection_;
};
}
#endif // FILE_CHOOSER_H_INCLUDED

View file

@ -25,7 +25,9 @@ menu::menu(display& disp, const std::vector<std::string>& items,
uparrow_(disp,"",gui::button::TYPE_PRESS,"uparrow-button"),
downarrow_(disp,"",gui::button::TYPE_PRESS,"downarrow-button"),
scrollbar_(disp,this), scrollbar_height_(0),
double_clicked_(false), num_selects_(true)
double_clicked_(false), num_selects_(true),
ignore_next_doubleclick_(false),
last_was_doubleclick_(false)
{
for(std::vector<std::string>::const_iterator item = items.begin();
item != items.end(); ++item) {
@ -59,6 +61,8 @@ void menu::set_scrollbar_height()
if (scrollbar_height_ > height()) {
std::cerr << "Strange. For some reason I want my scrollbar" <<
" to be larger than me!\n\n";
std::cerr << "pos_percent=" << pos_percent << " height()=" << height()
<< std::endl;
scrollbar_height_ = height() - buttons_height;
}
@ -162,6 +166,47 @@ void menu::erase_item(size_t index)
}
}
void menu::set_items(const std::vector<std::string>& items) {
items_.clear();
itemRects_.clear();
column_widths_.clear();
undrawn_items_.clear();
height_ = -1; // Force recalculation of the height.
width_ = -1; // Force recalculation of the width.
max_items_ = -1; // Force recalculation of the max items.
// Scrollbar and buttons will be reanabled if they are needed.
scrollbar_.enable(false);
uparrow_.hide(true);
downarrow_.hide(true);
first_item_on_screen_ = 0;
selected_ = click_selects_ ? -1:0;
for (std::vector<std::string>::const_iterator item = items.begin();
item != items.end(); ++item) {
items_.push_back(config::quoted_split(*item,',',false));
//make sure there is always at least one item
if(items_.back().empty())
items_.back().push_back(" ");
//if the first character in an item is an asterisk,
//it means this item should be selected by default
std::string& first_item = items_.back().front();
if(first_item.empty() == false && first_item[0] == '*') {
selected_ = items_.size()-1;
first_item.erase(first_item.begin());
}
}
set_loc(x_, y_); // Force some more updating.
calculate_position();
drawn_ = false;
}
void menu::set_max_height(const int new_max_height) {
max_height_ = new_max_height;
}
size_t menu::max_items_onscreen() const
{
if(max_items_ != -1) {
@ -300,7 +345,25 @@ void menu::handle_event(const SDL_Event& event)
}
if(event.type == DOUBLE_CLICK_EVENT) {
double_clicked_ = true;
if (ignore_next_doubleclick_) {
ignore_next_doubleclick_ = false;
}
else {
double_clicked_ = true;
last_was_doubleclick_ = true;
}
}
else if (last_was_doubleclick_) {
// If we have a double click as the next event, it means
// this double click was generated from a click that
// already has helped in generating a double click.
SDL_Event ev;
SDL_PeepEvents(&ev, 1, SDL_PEEKEVENT,
SDL_EVENTMASK(DOUBLE_CLICK_EVENT));
if (ev.type == DOUBLE_CLICK_EVENT) {
ignore_next_doubleclick_ = true;
}
last_was_doubleclick_ = false;
}
}
} else if(event.type == SDL_MOUSEMOTION && click_selects_) {
@ -408,6 +471,7 @@ int menu::process(int x, int y, bool button,bool up_arrow,bool down_arrow,
}
if(show_result_) {
show_result_ = false;
return selected_;
} else {
return -1;
@ -419,9 +483,11 @@ bool menu::show_scrollbar() const
return items_.size() > max_items_onscreen();
}
bool menu::double_clicked() const
bool menu::double_clicked()
{
return double_clicked_;
bool old = double_clicked_;
double_clicked_ = false;
return old;
}
void menu::set_numeric_keypress_selection(bool value)

View file

@ -36,12 +36,19 @@ public:
void erase_item(size_t index);
void set_items(const std::vector<std::string>& items);
/// Set a new max height for this menu. Note that this does not take
/// effect immideately, only after certain operations that clear
/// everything, such as set_items().
void set_max_height(const int new_max_height);
size_t nitems() const { return items_.size(); }
int process(int x, int y, bool button,bool up_arrow,bool down_arrow,
bool page_up, bool page_down, int select_item=-1);
bool double_clicked() const;
bool double_clicked();
void set_numeric_keypress_selection(bool value);
@ -109,6 +116,11 @@ private:
///variable which determines whether a numeric keypress should
///select an item on the dialog
bool num_selects_;
// These two variables are used to get the correct double click
// behavior so that a click that causes one double click wont be
// counted as a first click in the "next" double click.
bool ignore_next_doubleclick_;
bool last_was_doubleclick_;
};
}