mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-09 18:44:46 +00:00
Add possibility to open multiple files at once (fix #1163)
This commit is contained in:
parent
eee87ed1ec
commit
1350d6d7f7
@ -448,12 +448,15 @@ private:
|
||||
void onImageFilename() {
|
||||
std::string exts = get_writable_extensions();
|
||||
|
||||
std::string newFilename = app::show_file_selector(
|
||||
"Save Sprite Sheet", m_filename, exts, FileSelectorType::Save);
|
||||
if (newFilename.empty())
|
||||
FileSelectorFiles newFilename;
|
||||
if (!app::show_file_selector(
|
||||
"Save Sprite Sheet", m_filename, exts,
|
||||
FileSelectorType::Save, newFilename))
|
||||
return;
|
||||
|
||||
m_filename = newFilename;
|
||||
ASSERT(!newFilename.empty());
|
||||
|
||||
m_filename = newFilename.front();
|
||||
m_filenameAskOverwrite = false; // Already asked in file selector
|
||||
onFileNamesChange();
|
||||
}
|
||||
@ -468,12 +471,15 @@ private:
|
||||
|
||||
void onDataFilename() {
|
||||
// TODO hardcoded "json" extension
|
||||
std::string newFilename = app::show_file_selector(
|
||||
"Save JSON Data", m_dataFilename, "json", FileSelectorType::Save);
|
||||
if (newFilename.empty())
|
||||
FileSelectorFiles newFilename;
|
||||
if (!app::show_file_selector(
|
||||
"Save JSON Data", m_dataFilename, "json",
|
||||
FileSelectorType::Save, newFilename))
|
||||
return;
|
||||
|
||||
m_dataFilename = newFilename;
|
||||
ASSERT(!newFilename.empty());
|
||||
|
||||
m_dataFilename = newFilename.front();
|
||||
m_dataFilenameAskOverwrite = false; // Already asked in file selector
|
||||
onFileNamesChange();
|
||||
}
|
||||
|
@ -556,24 +556,34 @@ private:
|
||||
}
|
||||
|
||||
void onImport() {
|
||||
std::string filename = app::show_file_selector("Import Keyboard Shortcuts", "",
|
||||
KEYBOARD_FILENAME_EXTENSION, FileSelectorType::Open);
|
||||
if (filename.empty())
|
||||
FileSelectorFiles filename;
|
||||
if (!app::show_file_selector(
|
||||
"Import Keyboard Shortcuts", "",
|
||||
KEYBOARD_FILENAME_EXTENSION,
|
||||
FileSelectorType::Open, filename))
|
||||
return;
|
||||
|
||||
app::KeyboardShortcuts::instance()->importFile(filename.c_str(), KeySource::UserDefined);
|
||||
ASSERT(!filename.empty());
|
||||
|
||||
app::KeyboardShortcuts::instance()->importFile(
|
||||
filename.front(), KeySource::UserDefined);
|
||||
|
||||
fillAllLists();
|
||||
layout();
|
||||
}
|
||||
|
||||
void onExport() {
|
||||
std::string filename = app::show_file_selector(
|
||||
FileSelectorFiles filename;
|
||||
|
||||
if (!app::show_file_selector(
|
||||
"Export Keyboard Shortcuts", "",
|
||||
KEYBOARD_FILENAME_EXTENSION, FileSelectorType::Save);
|
||||
if (filename.empty())
|
||||
KEYBOARD_FILENAME_EXTENSION,
|
||||
FileSelectorType::Save, filename))
|
||||
return;
|
||||
|
||||
app::KeyboardShortcuts::instance()->exportFile(filename.c_str());
|
||||
ASSERT(!filename.empty());
|
||||
|
||||
app::KeyboardShortcuts::instance()->exportFile(filename.front());
|
||||
}
|
||||
|
||||
void onReset() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -57,17 +57,14 @@ void LoadMaskCommand::onExecute(Context* context)
|
||||
{
|
||||
const ContextReader reader(context);
|
||||
|
||||
std::string filename = m_filename;
|
||||
|
||||
if (context->isUIAvailable()) {
|
||||
filename = app::show_file_selector(
|
||||
"Load .msk File", filename, "msk",
|
||||
FileSelectorType::Open);
|
||||
|
||||
if (filename.empty())
|
||||
FileSelectorFiles selectedFilename;
|
||||
if (!app::show_file_selector(
|
||||
"Load .msk File", m_filename, "msk",
|
||||
FileSelectorType::Open, selectedFilename))
|
||||
return;
|
||||
|
||||
m_filename = filename;
|
||||
m_filename = selectedFilename.front();
|
||||
}
|
||||
|
||||
base::UniquePtr<Mask> mask(load_msk_file(m_filename.c_str()));
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -65,8 +65,12 @@ void LoadPaletteCommand::onExecute(Context* context)
|
||||
}
|
||||
else {
|
||||
std::string exts = get_readable_palette_extensions();
|
||||
filename = app::show_file_selector("Load Palette", "", exts,
|
||||
FileSelectorType::Open);
|
||||
|
||||
FileSelectorFiles filenames;
|
||||
if (app::show_file_selector("Load Palette", "", exts,
|
||||
FileSelectorType::Open, filenames)) {
|
||||
filename = filenames.front();
|
||||
}
|
||||
}
|
||||
|
||||
// Do nothing
|
||||
|
@ -106,6 +106,8 @@ void OpenFileCommand::onExecute(Context* context)
|
||||
|
||||
m_usedFiles.clear();
|
||||
|
||||
FileSelectorFiles filenames;
|
||||
|
||||
// interactive
|
||||
if (context->isUIAvailable() && m_filename.empty()) {
|
||||
std::string exts = get_readable_extensions();
|
||||
@ -115,13 +117,18 @@ void OpenFileCommand::onExecute(Context* context)
|
||||
if (!m_folder.empty() && !base::is_path_separator(m_folder[m_folder.size()-1]))
|
||||
m_folder.push_back(base::path_separator);
|
||||
|
||||
m_filename = app::show_file_selector("Open", m_folder, exts,
|
||||
FileSelectorType::Open);
|
||||
if (!app::show_file_selector("Open", m_folder, exts,
|
||||
FileSelectorType::OpenMultiple,
|
||||
filenames)) {
|
||||
// The user cancelled the operation through UI
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (!m_filename.empty()) {
|
||||
filenames.push_back(m_filename);
|
||||
}
|
||||
|
||||
// The user cancelled the operation through UI or isn't a filename
|
||||
// specified in params.
|
||||
if (m_filename.empty())
|
||||
if (filenames.empty())
|
||||
return;
|
||||
|
||||
int flags =
|
||||
@ -143,9 +150,10 @@ void OpenFileCommand::onExecute(Context* context)
|
||||
if (m_oneFrame)
|
||||
flags |= FILE_LOAD_ONE_FRAME;
|
||||
|
||||
for (const auto& filename : filenames) {
|
||||
base::UniquePtr<FileOp> fop(
|
||||
FileOp::createLoadDocumentOperation(
|
||||
context, m_filename, flags));
|
||||
context, filename, flags));
|
||||
bool unrecent = false;
|
||||
|
||||
// Do nothing (the user cancelled or something like that)
|
||||
@ -200,6 +208,7 @@ void OpenFileCommand::onExecute(Context* context)
|
||||
App::instance()->recentFiles()->removeRecentFile(m_filename.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Command* CommandFactory::createOpenFileCommand()
|
||||
{
|
||||
|
@ -90,16 +90,16 @@ private:
|
||||
}
|
||||
|
||||
void onSelectFontFile() {
|
||||
std::string face = show_file_selector(
|
||||
FileSelectorFiles face;
|
||||
if (!show_file_selector(
|
||||
"Select a TrueType Font",
|
||||
m_face,
|
||||
"ttf,ttc,otf,dfont",
|
||||
FileSelectorType::Open,
|
||||
nullptr);
|
||||
FileSelectorType::Open, face))
|
||||
return;
|
||||
|
||||
if (!face.empty()) {
|
||||
setFontFace(face);
|
||||
}
|
||||
ASSERT(!face.empty());
|
||||
setFontFace(face.front());
|
||||
}
|
||||
|
||||
void setFontFace(const std::string& face) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -192,15 +192,14 @@ bool SaveFileBaseCommand::saveAsDialog(Context* context,
|
||||
std::string exts = get_writable_extensions();
|
||||
filename = document->filename();
|
||||
|
||||
std::string newfilename = app::show_file_selector(
|
||||
FileSelectorFiles newfilename;
|
||||
if (!app::show_file_selector(
|
||||
dlgTitle, filename, exts,
|
||||
FileSelectorType::Save,
|
||||
delegate);
|
||||
|
||||
if (newfilename.empty())
|
||||
FileSelectorType::Save, newfilename,
|
||||
delegate))
|
||||
return false;
|
||||
|
||||
filename = newfilename;
|
||||
filename = newfilename.front();
|
||||
if (delegate &&
|
||||
delegate->hasResizeCombobox()) {
|
||||
xscale = yscale = delegate->getResizeScale();
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -45,13 +45,15 @@ void SaveMaskCommand::onExecute(Context* context)
|
||||
{
|
||||
const ContextReader reader(context);
|
||||
const Document* document(reader.document());
|
||||
std::string filename = "default.msk";
|
||||
|
||||
filename = app::show_file_selector(
|
||||
"Save .msk File", filename, "msk", FileSelectorType::Save);
|
||||
if (filename.empty())
|
||||
FileSelectorFiles selFilename;
|
||||
if (!app::show_file_selector(
|
||||
"Save .msk File", "default.msk", "msk",
|
||||
FileSelectorType::Save, selFilename))
|
||||
return;
|
||||
|
||||
std::string filename = selFilename.front();
|
||||
|
||||
if (save_msk_file(document->mask(), filename.c_str()) != 0)
|
||||
ui::Alert::show("Error<<Error saving .msk file<<%s||&Close", filename.c_str());
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -58,10 +58,13 @@ void SavePaletteCommand::onExecute(Context* context)
|
||||
}
|
||||
else {
|
||||
std::string exts = get_writable_palette_extensions();
|
||||
filename = app::show_file_selector("Save Palette", "", exts,
|
||||
FileSelectorType::Save);
|
||||
if (filename.empty())
|
||||
FileSelectorFiles selFilename;
|
||||
if (!app::show_file_selector("Save Palette", "", exts,
|
||||
FileSelectorType::Save,
|
||||
selFilename))
|
||||
return;
|
||||
|
||||
filename = selFilename.front();
|
||||
}
|
||||
|
||||
if (!save_palette(filename.c_str(), palette, 16)) // TODO 16 should be configurable
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -20,11 +20,12 @@
|
||||
|
||||
namespace app {
|
||||
|
||||
std::string show_file_selector(
|
||||
bool show_file_selector(
|
||||
const std::string& title,
|
||||
const std::string& initialPath,
|
||||
const std::string& showExtensions,
|
||||
FileSelectorType type,
|
||||
FileSelectorFiles& output,
|
||||
FileSelectorDelegate* delegate)
|
||||
{
|
||||
if (Preferences::instance().experimental.useNativeFileDialog() &&
|
||||
@ -33,31 +34,40 @@ std::string show_file_selector(
|
||||
she::instance()->nativeDialogs()->createFileDialog();
|
||||
|
||||
if (dlg) {
|
||||
std::string res;
|
||||
|
||||
dlg->setTitle(title);
|
||||
dlg->setFileName(initialPath);
|
||||
|
||||
if (type == FileSelectorType::Save)
|
||||
dlg->toSaveFile();
|
||||
else
|
||||
switch (type) {
|
||||
case FileSelectorType::Open:
|
||||
case FileSelectorType::OpenMultiple:
|
||||
dlg->toOpenFile();
|
||||
if (type == FileSelectorType::OpenMultiple)
|
||||
dlg->setMultipleSelection(true);
|
||||
break;
|
||||
case FileSelectorType::Save:
|
||||
dlg->toSaveFile();
|
||||
break;
|
||||
}
|
||||
|
||||
std::vector<std::string> tokens;
|
||||
base::split_string(showExtensions, tokens, ",");
|
||||
for (const auto& tok : tokens)
|
||||
dlg->addFilter(tok, tok + " files (*." + tok + ")");
|
||||
|
||||
if (dlg->show(she::instance()->defaultDisplay()))
|
||||
res = dlg->fileName();
|
||||
|
||||
bool res = dlg->show(she::instance()->defaultDisplay());
|
||||
if (res) {
|
||||
if (type == FileSelectorType::OpenMultiple)
|
||||
dlg->getMultipleFileNames(output);
|
||||
else
|
||||
output.push_back(dlg->fileName());
|
||||
}
|
||||
dlg->dispose();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
FileSelector fileSelector(type, delegate);
|
||||
return fileSelector.show(title, initialPath, showExtensions);
|
||||
return fileSelector.show(title, initialPath, showExtensions, output);
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -8,17 +8,20 @@
|
||||
#define APP_FILE_SELECTOR_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "doc/pixel_ratio.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace ui {
|
||||
class ComboBox;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
|
||||
enum class FileSelectorType { Open, Save };
|
||||
enum class FileSelectorType { Open, OpenMultiple, Save };
|
||||
|
||||
typedef std::vector<std::string> FileSelectorFiles;
|
||||
|
||||
class FileSelectorDelegate {
|
||||
public:
|
||||
@ -41,11 +44,12 @@ namespace app {
|
||||
virtual doc::PixelRatio pixelRatio() = 0;
|
||||
};
|
||||
|
||||
std::string show_file_selector(
|
||||
bool show_file_selector(
|
||||
const std::string& title,
|
||||
const std::string& initialPath,
|
||||
const std::string& showExtensions,
|
||||
FileSelectorType type,
|
||||
FileSelectorFiles& output,
|
||||
FileSelectorDelegate* delegate = nullptr);
|
||||
|
||||
} // namespace app
|
||||
|
@ -33,20 +33,19 @@ using namespace ui;
|
||||
|
||||
FileList::FileList()
|
||||
: Widget(kGenericWidget)
|
||||
, m_currentFolder(FileSystemModule::instance()->getRootFileItem())
|
||||
, m_req_valid(false)
|
||||
, m_selected(nullptr)
|
||||
, m_isearchClock(0)
|
||||
, m_generateThumbnailTimer(200, this)
|
||||
, m_monitoringTimer(50, this)
|
||||
, m_itemToGenerateThumbnail(nullptr)
|
||||
, m_thumbnail(nullptr)
|
||||
, m_multiselect(false)
|
||||
{
|
||||
setFocusStop(true);
|
||||
setDoubleBuffered(true);
|
||||
|
||||
m_currentFolder = FileSystemModule::instance()->getRootFileItem();
|
||||
m_req_valid = false;
|
||||
m_selected = NULL;
|
||||
m_isearchClock = 0;
|
||||
|
||||
m_itemToGenerateThumbnail = NULL;
|
||||
|
||||
m_generateThumbnailTimer.Tick.connect(&FileList::onGenerateThumbnailTick, this);
|
||||
m_monitoringTimer.Tick.connect(&FileList::onMonitoringTick, this);
|
||||
m_monitoringTimer.start();
|
||||
@ -80,7 +79,7 @@ void FileList::setCurrentFolder(IFileItem* folder)
|
||||
|
||||
m_currentFolder = folder;
|
||||
m_req_valid = false;
|
||||
m_selected = NULL;
|
||||
m_selected = nullptr;
|
||||
|
||||
regenerateList();
|
||||
|
||||
@ -95,13 +94,56 @@ void FileList::setCurrentFolder(IFileItem* folder)
|
||||
View::getView(this)->updateView();
|
||||
}
|
||||
|
||||
FileItemList FileList::selectedFileItems() const
|
||||
{
|
||||
ASSERT(m_selectedItems.size() == m_list.size());
|
||||
|
||||
FileItemList result;
|
||||
for (int i=0; i<int(m_list.size()); ++i) {
|
||||
if (m_selectedItems[i])
|
||||
result.push_back(m_list[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void FileList::deselectedFileItems()
|
||||
{
|
||||
if (!m_multiselect)
|
||||
return;
|
||||
|
||||
bool redraw = false;
|
||||
|
||||
for (int i=0; i<int(m_list.size()); ++i) {
|
||||
if (m_selected == m_list[i]) {
|
||||
if (!m_selectedItems[i]) {
|
||||
m_selectedItems[i] = true;
|
||||
redraw = true;
|
||||
}
|
||||
}
|
||||
else if (m_selectedItems[i]) {
|
||||
m_selectedItems[i] = false;
|
||||
redraw = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (redraw)
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void FileList::setMultipleSelection(bool multiple)
|
||||
{
|
||||
m_multiselect = multiple;
|
||||
}
|
||||
|
||||
void FileList::goUp()
|
||||
{
|
||||
IFileItem* folder = m_currentFolder;
|
||||
IFileItem* parent = folder->parent();
|
||||
if (parent) {
|
||||
setCurrentFolder(parent);
|
||||
|
||||
m_selected = folder;
|
||||
deselectedFileItems();
|
||||
|
||||
// Make the selected item visible.
|
||||
makeSelectedFileitemVisible();
|
||||
@ -120,21 +162,38 @@ bool FileList::onProcessMessage(Message* msg)
|
||||
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
|
||||
int th = textHeight();
|
||||
int y = bounds().y;
|
||||
IFileItem* old_selected = m_selected;
|
||||
m_selected = NULL;
|
||||
IFileItem* oldSelected = m_selected;
|
||||
m_selected = nullptr;
|
||||
|
||||
// rows
|
||||
for (FileItemList::iterator
|
||||
it=m_list.begin();
|
||||
it!=m_list.end(); ++it) {
|
||||
int i = 0;
|
||||
for (auto begin=m_list.begin(), end=m_list.end(), it=begin;
|
||||
it!=end; ++it, ++i) {
|
||||
IFileItem* fi = *it;
|
||||
gfx::Size itemSize = getFileItemSize(fi);
|
||||
|
||||
if (((mouseMsg->position().y >= y) &&
|
||||
(mouseMsg->position().y < y+th+4*guiscale())) ||
|
||||
(it == m_list.begin() && mouseMsg->position().y < y) ||
|
||||
(it == m_list.end()-1 && mouseMsg->position().y >= y+th+4*guiscale())) {
|
||||
(it == begin && mouseMsg->position().y < y) ||
|
||||
(it == end-1 && mouseMsg->position().y >= y+th+4*guiscale())) {
|
||||
m_selected = fi;
|
||||
|
||||
if (m_multiselect &&
|
||||
oldSelected != fi &&
|
||||
m_selectedItems.size() == m_list.size()) {
|
||||
if (msg->shiftPressed() ||
|
||||
msg->ctrlPressed() ||
|
||||
msg->cmdPressed()) {
|
||||
m_selectedItems[i] = !m_selectedItems[i];
|
||||
}
|
||||
else {
|
||||
std::fill(m_selectedItems.begin(),
|
||||
m_selectedItems.end(), false);
|
||||
m_selectedItems[i] = true;
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
makeSelectedFileitemVisible();
|
||||
break;
|
||||
}
|
||||
@ -142,7 +201,7 @@ bool FileList::onProcessMessage(Message* msg)
|
||||
y += itemSize.h;
|
||||
}
|
||||
|
||||
if (old_selected != m_selected) {
|
||||
if (oldSelected != m_selected) {
|
||||
generatePreviewOfSelectedItem();
|
||||
|
||||
invalidate();
|
||||
@ -164,7 +223,7 @@ bool FileList::onProcessMessage(Message* msg)
|
||||
KeyMessage* keyMsg = static_cast<KeyMessage*>(msg);
|
||||
KeyScancode scancode = keyMsg->scancode();
|
||||
int unicodeChar = keyMsg->unicodeChar();
|
||||
int select = getSelectedIndex();
|
||||
int select = selectedIndex();
|
||||
View* view = View::getView(this);
|
||||
int bottom = m_list.size();
|
||||
|
||||
@ -328,7 +387,6 @@ void FileList::onPaint(ui::PaintEvent& ev)
|
||||
SkinTheme* theme = static_cast<SkinTheme*>(this->theme());
|
||||
gfx::Rect bounds = clientBounds();
|
||||
int x, y = bounds.y;
|
||||
int evenRow = 0;
|
||||
gfx::Color bgcolor;
|
||||
gfx::Color fgcolor;
|
||||
|
||||
@ -336,10 +394,15 @@ void FileList::onPaint(ui::PaintEvent& ev)
|
||||
|
||||
// rows
|
||||
m_thumbnail = nullptr;
|
||||
int i = 0;
|
||||
for (IFileItem* fi : m_list) {
|
||||
gfx::Size itemSize = getFileItemSize(fi);
|
||||
const bool evenRow = ((i & 1) == 0);
|
||||
|
||||
if (fi == m_selected) {
|
||||
if ((!m_multiselect && fi == m_selected) ||
|
||||
(m_multiselect &&
|
||||
m_selectedItems.size() == m_list.size() &&
|
||||
m_selectedItems[i])) {
|
||||
fgcolor = theme->colors.filelistSelectedRowText();
|
||||
bgcolor = theme->colors.filelistSelectedRowFace();
|
||||
}
|
||||
@ -392,7 +455,7 @@ void FileList::onPaint(ui::PaintEvent& ev)
|
||||
m_thumbnail = fi->getThumbnail();
|
||||
|
||||
y += itemSize.h;
|
||||
evenRow ^= 1;
|
||||
++i;
|
||||
}
|
||||
|
||||
// Draw the thumbnail
|
||||
@ -524,6 +587,7 @@ void FileList::regenerateList()
|
||||
it=m_list.begin();
|
||||
it!=m_list.end(); ) {
|
||||
IFileItem* fileitem = *it;
|
||||
|
||||
if (fileitem->isHidden())
|
||||
it = m_list.erase(it);
|
||||
else if (!fileitem->isFolder() &&
|
||||
@ -534,26 +598,33 @@ void FileList::regenerateList()
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_multiselect && !m_list.empty()) {
|
||||
m_selectedItems.resize(m_list.size());
|
||||
deselectedFileItems();
|
||||
}
|
||||
else
|
||||
m_selectedItems.clear();
|
||||
}
|
||||
|
||||
int FileList::getSelectedIndex()
|
||||
int FileList::selectedIndex() const
|
||||
{
|
||||
for (FileItemList::iterator
|
||||
it = m_list.begin();
|
||||
it != m_list.end(); ++it) {
|
||||
for (auto it = m_list.begin(), end = m_list.end();
|
||||
it != end; ++it) {
|
||||
if (*it == m_selected)
|
||||
return it - m_list.begin();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void FileList::selectIndex(int index)
|
||||
{
|
||||
IFileItem* old_selected = m_selected;
|
||||
IFileItem* oldSelected = m_selected;
|
||||
|
||||
m_selected = m_list.at(index);
|
||||
if (old_selected != m_selected) {
|
||||
deselectedFileItems();
|
||||
|
||||
if (oldSelected != m_selected) {
|
||||
makeSelectedFileitemVisible();
|
||||
|
||||
invalidate();
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -15,6 +15,7 @@
|
||||
#include "ui/widget.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace she {
|
||||
class Surface;
|
||||
@ -30,11 +31,16 @@ namespace app {
|
||||
const std::string& extensions() const { return m_exts; }
|
||||
void setExtensions(const char* extensions);
|
||||
|
||||
IFileItem* getCurrentFolder() const { return m_currentFolder; }
|
||||
IFileItem* currentFolder() const { return m_currentFolder; }
|
||||
void setCurrentFolder(IFileItem* folder);
|
||||
|
||||
IFileItem* getSelectedFileItem() const { return m_selected; }
|
||||
const FileItemList& getFileList() const { return m_list; }
|
||||
IFileItem* selectedFileItem() const { return m_selected; }
|
||||
const FileItemList& fileList() const { return m_list; }
|
||||
FileItemList selectedFileItems() const;
|
||||
void deselectedFileItems();
|
||||
|
||||
bool multipleSelection() { return m_multiselect; }
|
||||
void setMultipleSelection(bool multiple);
|
||||
|
||||
void goUp();
|
||||
|
||||
@ -58,16 +64,18 @@ namespace app {
|
||||
gfx::Size getFileItemSize(IFileItem* fi) const;
|
||||
void makeSelectedFileitemVisible();
|
||||
void regenerateList();
|
||||
int getSelectedIndex();
|
||||
int selectedIndex() const;
|
||||
void selectIndex(int index);
|
||||
void generatePreviewOfSelectedItem();
|
||||
int thumbnailY();
|
||||
|
||||
IFileItem* m_currentFolder;
|
||||
FileItemList m_list;
|
||||
|
||||
bool m_req_valid;
|
||||
int m_req_w, m_req_h;
|
||||
IFileItem* m_selected;
|
||||
std::vector<bool> m_selectedItems;
|
||||
std::string m_exts;
|
||||
|
||||
// Incremental-search
|
||||
@ -86,6 +94,10 @@ namespace app {
|
||||
IFileItem* m_itemToGenerateThumbnail;
|
||||
|
||||
she::Surface* m_thumbnail;
|
||||
|
||||
// True if this listbox accepts selecting multiple items at the
|
||||
// same time.
|
||||
bool m_multiselect;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
@ -109,6 +109,10 @@ public:
|
||||
protected:
|
||||
|
||||
void onEntryChange() {
|
||||
// Deselect multiple-selection
|
||||
if (m_fileList->multipleSelection())
|
||||
m_fileList->deselectedFileItems();
|
||||
|
||||
removeAllItems();
|
||||
|
||||
// String to be autocompleted
|
||||
@ -118,7 +122,7 @@ protected:
|
||||
if (left_part.empty())
|
||||
return;
|
||||
|
||||
for (const IFileItem* child : m_fileList->getFileList()) {
|
||||
for (const IFileItem* child : m_fileList->fileList()) {
|
||||
std::string child_name = child->displayName();
|
||||
std::string::const_iterator it1, it2;
|
||||
|
||||
@ -376,20 +380,19 @@ void FileSelector::goUp()
|
||||
|
||||
void FileSelector::goInsideFolder()
|
||||
{
|
||||
if (m_fileList->getSelectedFileItem() &&
|
||||
m_fileList->getSelectedFileItem()->isBrowsable()) {
|
||||
if (m_fileList->selectedFileItem() &&
|
||||
m_fileList->selectedFileItem()->isBrowsable()) {
|
||||
m_fileList->setCurrentFolder(
|
||||
m_fileList->getSelectedFileItem());
|
||||
m_fileList->selectedFileItem());
|
||||
}
|
||||
}
|
||||
|
||||
std::string FileSelector::show(
|
||||
bool FileSelector::show(
|
||||
const std::string& title,
|
||||
const std::string& initialPath,
|
||||
const std::string& showExtensions)
|
||||
const std::string& showExtensions,
|
||||
FileSelectorFiles& output)
|
||||
{
|
||||
std::string result;
|
||||
|
||||
FileSystemModule* fs = FileSystemModule::instance();
|
||||
LockFS lock(fs);
|
||||
|
||||
@ -432,7 +435,8 @@ std::string FileSelector::show(
|
||||
// Change the file formats/extensions to be shown
|
||||
std::string initialExtension = base::get_file_extension(initialPath);
|
||||
std::string exts = showExtensions;
|
||||
if (m_type == FileSelectorType::Open) {
|
||||
if (m_type == FileSelectorType::Open ||
|
||||
m_type == FileSelectorType::OpenMultiple) {
|
||||
auto it = preferred_open_extensions.find(exts);
|
||||
if (it == preferred_open_extensions.end())
|
||||
exts = showExtensions;
|
||||
@ -444,13 +448,14 @@ std::string FileSelector::show(
|
||||
if (!initialExtension.empty())
|
||||
exts = initialExtension;
|
||||
}
|
||||
m_fileList->setMultipleSelection(m_type == FileSelectorType::OpenMultiple);
|
||||
m_fileList->setExtensions(exts.c_str());
|
||||
if (start_folder)
|
||||
m_fileList->setCurrentFolder(start_folder);
|
||||
|
||||
// current location
|
||||
navigation_position.reset();
|
||||
addInNavigationHistory(m_fileList->getCurrentFolder());
|
||||
addInNavigationHistory(m_fileList->currentFolder());
|
||||
|
||||
// fill the location combo-box
|
||||
updateLocation();
|
||||
@ -501,18 +506,19 @@ std::string FileSelector::show(
|
||||
// update the view
|
||||
View::getView(m_fileList)->updateView();
|
||||
|
||||
// open the window and run... the user press ok?
|
||||
// TODO this loop needs a complete refactor
|
||||
// Open the window and run... the user press ok?
|
||||
again:
|
||||
openWindowInForeground();
|
||||
if (closer() == ok ||
|
||||
closer() == m_fileList) {
|
||||
// open the selected file
|
||||
IFileItem* folder = m_fileList->getCurrentFolder();
|
||||
IFileItem* folder = m_fileList->currentFolder();
|
||||
ASSERT(folder);
|
||||
|
||||
// File name in the text entry field/combobox
|
||||
std::string fn = m_fileName->getValue();
|
||||
std::string buf;
|
||||
IFileItem* enter_folder = NULL;
|
||||
IFileItem* enter_folder = nullptr;
|
||||
|
||||
// up a level?
|
||||
if (fn == "..") {
|
||||
@ -521,13 +527,15 @@ again:
|
||||
enter_folder = folder;
|
||||
}
|
||||
else if (fn.empty()) {
|
||||
// show the window again
|
||||
if (m_type != FileSelectorType::OpenMultiple) {
|
||||
// Show the window again
|
||||
setVisible(true);
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// check if the user specified in "fn" a item of "fileview"
|
||||
const FileItemList& children = m_fileList->getFileList();
|
||||
const FileItemList& children = m_fileList->fileList();
|
||||
|
||||
std::string fn2 = fn;
|
||||
#ifdef _WIN32
|
||||
@ -536,7 +544,6 @@ again:
|
||||
|
||||
for (IFileItem* child : children) {
|
||||
std::string child_name = child->displayName();
|
||||
|
||||
#ifdef _WIN32
|
||||
child_name = base::string_to_lower(child_name);
|
||||
#endif
|
||||
@ -604,7 +611,7 @@ again:
|
||||
|
||||
// does it not have extension? ...we should add the extension
|
||||
// selected in the filetype combo-box
|
||||
if (base::get_file_extension(buf).empty()) {
|
||||
if (!buf.empty() && base::get_file_extension(buf).empty()) {
|
||||
buf += '.';
|
||||
buf += getSelectedExtension();
|
||||
}
|
||||
@ -628,12 +635,17 @@ again:
|
||||
}
|
||||
// Cancel
|
||||
else if (ret != 1) {
|
||||
return "";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// duplicate the buffer to return a new string
|
||||
result = buf;
|
||||
// Put in output the selected filenames
|
||||
if (!buf.empty())
|
||||
output.push_back(buf);
|
||||
else if (m_type == FileSelectorType::OpenMultiple) {
|
||||
for (IFileItem* fi : m_fileList->selectedFileItems())
|
||||
output.push_back(fi->fileName());
|
||||
}
|
||||
|
||||
// save the path in the configuration file
|
||||
std::string lastpath = folder->keyName();
|
||||
@ -650,19 +662,19 @@ again:
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return (!output.empty());
|
||||
}
|
||||
|
||||
// Updates the content of the combo-box that shows the current
|
||||
// location in the file-system.
|
||||
void FileSelector::updateLocation()
|
||||
{
|
||||
IFileItem* currentFolder = m_fileList->getCurrentFolder();
|
||||
IFileItem* currentFolder = m_fileList->currentFolder();
|
||||
IFileItem* fileItem = currentFolder;
|
||||
std::list<IFileItem*> locations;
|
||||
int selected_index = -1;
|
||||
|
||||
while (fileItem != NULL) {
|
||||
while (fileItem) {
|
||||
locations.push_front(fileItem);
|
||||
fileItem = fileItem->parent();
|
||||
}
|
||||
@ -672,8 +684,7 @@ void FileSelector::updateLocation()
|
||||
|
||||
// Add item by item (from root to the specific current folder)
|
||||
int level = 0;
|
||||
for (std::list<IFileItem*>::iterator it=locations.begin(), end=locations.end();
|
||||
it != end; ++it) {
|
||||
for (auto it=locations.begin(), end=locations.end(); it!=end; ++it) {
|
||||
fileItem = *it;
|
||||
|
||||
// Indentation
|
||||
@ -729,7 +740,7 @@ void FileSelector::updateNavigationButtons()
|
||||
|
||||
// Update the state of the go up button: if the current-folder isn't
|
||||
// the root-item.
|
||||
IFileItem* currentFolder = m_fileList->getCurrentFolder();
|
||||
IFileItem* currentFolder = m_fileList->currentFolder();
|
||||
goUpButton()->setEnabled(currentFolder != FileSystemModule::instance()->getRootFileItem());
|
||||
}
|
||||
|
||||
@ -797,7 +808,7 @@ void FileSelector::onNewFolder()
|
||||
|
||||
window.openWindowInForeground();
|
||||
if (window.closer() == window.ok()) {
|
||||
IFileItem* currentFolder = m_fileList->getCurrentFolder();
|
||||
IFileItem* currentFolder = m_fileList->currentFolder();
|
||||
if (currentFolder) {
|
||||
std::string dirname = window.name()->text();
|
||||
|
||||
@ -858,7 +869,8 @@ void FileSelector::onFileTypeChange()
|
||||
m_fileList->setExtensions(exts.c_str());
|
||||
m_navigationLocked = false;
|
||||
|
||||
if (m_type == FileSelectorType::Open) {
|
||||
if (m_type == FileSelectorType::Open ||
|
||||
m_type == FileSelectorType::OpenMultiple) {
|
||||
std::string origShowExtensions = fileType()->getItem(0)->getValue();
|
||||
preferred_open_extensions[origShowExtensions] = fileType()->getValue();
|
||||
}
|
||||
@ -876,12 +888,16 @@ void FileSelector::onFileTypeChange()
|
||||
|
||||
void FileSelector::onFileListFileSelected()
|
||||
{
|
||||
IFileItem* fileitem = m_fileList->getSelectedFileItem();
|
||||
IFileItem* fileitem = m_fileList->selectedFileItem();
|
||||
|
||||
if (!fileitem->isFolder()) {
|
||||
std::string filename = base::get_file_name(fileitem->fileName());
|
||||
|
||||
if (m_type != FileSelectorType::OpenMultiple ||
|
||||
m_fileList->selectedFileItems().size() == 1)
|
||||
m_fileName->setValue(filename.c_str());
|
||||
else
|
||||
m_fileName->setValue(std::string());
|
||||
}
|
||||
}
|
||||
|
||||
@ -893,7 +909,7 @@ void FileSelector::onFileListFileAccepted()
|
||||
void FileSelector::onFileListCurrentFolderChanged()
|
||||
{
|
||||
if (!m_navigationLocked)
|
||||
addInNavigationHistory(m_fileList->getCurrentFolder());
|
||||
addInNavigationHistory(m_fileList->currentFolder());
|
||||
|
||||
updateLocation();
|
||||
updateNavigationButtons();
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -38,9 +38,10 @@ namespace app {
|
||||
void goInsideFolder();
|
||||
|
||||
// Shows the dialog to select a file in the program.
|
||||
std::string show(const std::string& title,
|
||||
bool show(const std::string& title,
|
||||
const std::string& initialPath,
|
||||
const std::string& showExtensions);
|
||||
const std::string& showExtensions,
|
||||
FileSelectorFiles& output);
|
||||
|
||||
private:
|
||||
void updateLocation();
|
||||
|
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2012-2016 David Capello
|
||||
Copyright (c) 2012-2017 David Capello
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
@ -1,5 +1,5 @@
|
||||
// SHE library
|
||||
// Copyright (C) 2015 David Capello
|
||||
// Copyright (C) 2015-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace she {
|
||||
class Display;
|
||||
@ -21,8 +22,10 @@ namespace she {
|
||||
virtual void toSaveFile() = 0; // Configure the dialog to save a file
|
||||
virtual void setTitle(const std::string& title) = 0;
|
||||
virtual void setDefaultExtension(const std::string& extension) = 0;
|
||||
virtual void setMultipleSelection(bool multiple) = 0;
|
||||
virtual void addFilter(const std::string& extension, const std::string& description) = 0;
|
||||
virtual std::string fileName() = 0;
|
||||
virtual void getMultipleFileNames(std::vector<std::string>& output) = 0;
|
||||
virtual void setFileName(const std::string& filename) = 0;
|
||||
virtual bool show(Display* parent) = 0;
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
// SHE library
|
||||
// Copyright (C) 2012-2016 David Capello
|
||||
// Copyright (C) 2012-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -82,6 +82,7 @@ class FileDialogOSX : public FileDialog {
|
||||
public:
|
||||
FileDialogOSX()
|
||||
: m_save(false)
|
||||
, m_multipleSelection(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -105,6 +106,10 @@ public:
|
||||
m_defExtension = extension;
|
||||
}
|
||||
|
||||
void setMultipleSelection(bool multiple) override {
|
||||
m_multipleSelection = multiple;
|
||||
}
|
||||
|
||||
void addFilter(const std::string& extension, const std::string& description) override {
|
||||
if (m_defExtension.empty())
|
||||
m_defExtension = extension;
|
||||
@ -116,6 +121,10 @@ public:
|
||||
return m_filename;
|
||||
}
|
||||
|
||||
void getMultipleFileNames(std::vector<std::string>& output) override {
|
||||
output = m_filenames;
|
||||
}
|
||||
|
||||
void setFileName(const std::string& filename) override {
|
||||
m_filename = filename;
|
||||
}
|
||||
@ -128,7 +137,7 @@ public:
|
||||
}
|
||||
else {
|
||||
panel = [NSOpenPanel openPanel];
|
||||
[(NSOpenPanel*)panel setAllowsMultipleSelection:NO];
|
||||
[(NSOpenPanel*)panel setAllowsMultipleSelection:(m_multipleSelection ? YES: NO)];
|
||||
[(NSOpenPanel*)panel setCanChooseDirectories:NO];
|
||||
}
|
||||
|
||||
@ -157,12 +166,22 @@ public:
|
||||
|
||||
bool retValue;
|
||||
if ([helper result] == NSFileHandlingPanelOKButton) {
|
||||
if (m_multipleSelection) {
|
||||
for (NSURL* url in [(NSOpenPanel*)panel URLs]) {
|
||||
m_filename = [[url path] UTF8String];
|
||||
m_filenames.push_back(m_filename);
|
||||
}
|
||||
}
|
||||
else {
|
||||
NSURL* url = [panel URL];
|
||||
m_filename = [[url path] UTF8String];
|
||||
m_filenames.push_back(m_filename);
|
||||
}
|
||||
retValue = true;
|
||||
}
|
||||
else
|
||||
else {
|
||||
retValue = false;
|
||||
}
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
[helper release];
|
||||
@ -176,8 +195,10 @@ private:
|
||||
std::vector<std::pair<std::string, std::string>> m_filters;
|
||||
std::string m_defExtension;
|
||||
std::string m_filename;
|
||||
std::vector<std::string> m_filenames;
|
||||
std::string m_title;
|
||||
bool m_save;
|
||||
bool m_multipleSelection;
|
||||
};
|
||||
|
||||
NativeDialogsOSX::NativeDialogsOSX()
|
||||
|
@ -1,5 +1,5 @@
|
||||
// SHE library
|
||||
// Copyright (C) 2015-2016 David Capello
|
||||
// Copyright (C) 2015-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -29,7 +29,8 @@ class FileDialogWin32 : public FileDialog {
|
||||
public:
|
||||
FileDialogWin32()
|
||||
: m_filename(FILENAME_BUFSIZE)
|
||||
, m_save(false) {
|
||||
, m_save(false)
|
||||
, m_multipleSelection(false) {
|
||||
}
|
||||
|
||||
void dispose() override {
|
||||
@ -52,6 +53,10 @@ public:
|
||||
m_defExtension = base::from_utf8(extension);
|
||||
}
|
||||
|
||||
void setMultipleSelection(bool multiple) override {
|
||||
m_multipleSelection = multiple;
|
||||
}
|
||||
|
||||
void addFilter(const std::string& extension, const std::string& description) override {
|
||||
if (m_defExtension.empty()) {
|
||||
m_defExtension = base::from_utf8(extension);
|
||||
@ -64,6 +69,10 @@ public:
|
||||
return base::to_utf8(&m_filename[0]);
|
||||
}
|
||||
|
||||
void getMultipleFileNames(std::vector<std::string>& output) override {
|
||||
output = m_filenames;
|
||||
}
|
||||
|
||||
void setFileName(const std::string& filename) override {
|
||||
wcscpy(&m_filename[0], base::from_utf8(base::get_file_name(filename)).c_str());
|
||||
m_initialDir = base::from_utf8(base::get_file_path(filename));
|
||||
@ -92,16 +101,41 @@ public:
|
||||
OFN_NOCHANGEDIR |
|
||||
OFN_PATHMUSTEXIST;
|
||||
|
||||
if (!m_save)
|
||||
if (!m_save) {
|
||||
ofn.Flags |= OFN_FILEMUSTEXIST;
|
||||
if (m_multipleSelection)
|
||||
ofn.Flags |= OFN_ALLOWMULTISELECT;
|
||||
}
|
||||
else
|
||||
ofn.Flags |= OFN_OVERWRITEPROMPT;
|
||||
|
||||
BOOL res;
|
||||
if (m_save)
|
||||
res = GetSaveFileName(&ofn);
|
||||
else
|
||||
else {
|
||||
res = GetOpenFileName(&ofn);
|
||||
if (res && m_multipleSelection) {
|
||||
WCHAR* p = &m_filename[0];
|
||||
std::string path = base::to_utf8(p);
|
||||
|
||||
for (p+=std::wcslen(p)+1; ; ++p) {
|
||||
if (*p) {
|
||||
WCHAR* q = p;
|
||||
for (++p; *p; ++p)
|
||||
;
|
||||
|
||||
m_filenames.push_back(
|
||||
base::join_path(path, base::to_utf8(q)));
|
||||
}
|
||||
else // Two null chars in a row
|
||||
break;
|
||||
}
|
||||
|
||||
// Just one filename was selected
|
||||
if (m_filenames.empty())
|
||||
m_filenames.push_back(path);
|
||||
}
|
||||
}
|
||||
|
||||
if (!res) {
|
||||
DWORD err = CommDlgExtendedError();
|
||||
@ -112,7 +146,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
return res != FALSE;
|
||||
return (res != FALSE);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -158,9 +192,11 @@ private:
|
||||
std::wstring m_defExtension;
|
||||
int m_defFilter;
|
||||
std::vector<WCHAR> m_filename;
|
||||
std::vector<std::string> m_filenames;
|
||||
std::wstring m_initialDir;
|
||||
std::wstring m_title;
|
||||
bool m_save;
|
||||
bool m_multipleSelection;
|
||||
};
|
||||
|
||||
NativeDialogsWin32::NativeDialogsWin32()
|
||||
|
Loading…
x
Reference in New Issue
Block a user