123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467 |
- /* GCSx
- ** FILEDIALOG.CPP
- **
- ** Standard file open/save/save as dialogs
- */
- /*****************************************************************************
- ** Copyright (C) 2003-2006 Janson
- **
- ** This program is free software; you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation; either version 2 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program; if not, write to the Free Software
- ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
- *****************************************************************************/
- #include "all.h"
- // Last-used directories
- string* previousDirectories = NULL;
- // File type associated extensions for saving, and some for loading
- vector<string>* fileExtensions = NULL;
- // Standard file dialog
- class FileDirDialog : public Dialog {
- private:
- int initialized;
- int initializedH;
- int initializedW;
- int selection;
- int saveAs;
- int openForWrite;
- string file;
- DirectoryView* tree;
- WListBox* list;
- WTextBox* filename;
- const vector<string>* currentExtensions;
- int treeviewEvent(int code, int command, int check);
- static int treeviewEventWrap(void* ptr, int code, int command, int check);
- void refillList(const string* itemToSelect = NULL);
- #if !FILESYSTEM_CASE_SENSITIVE
- struct stringCmpI : public binary_function<string, string, bool> {
- bool operator()(const string& x, const string& y) { return myStricmp(x.c_str(), y.c_str()) < 0; }
- };
- #endif
- public:
- enum {
- ID_TREE = 1,
- ID_LIST,
- ID_LABEL,
- ID_ENTRY,
- ID_OK,
- ID_CANCEL,
- };
- FileDirDialog();
- virtual ~FileDirDialog();
- void childModified(Window* modified);
- virtual ButtonAction verifyEntry(int buttonId, ButtonAction buttonType);
- void firstControl();
- int run(int isSaveAs, int isOpenForWrite, const vector<string>& extensions, string& storeFilename);
- } *fileDialog = NULL;
- // Initializes extensions, last-used directories and dialog
- void initFileSystems() { start_func
- if (!fileExtensions) {
- previousDirectories = new string[FILETYPE_COUNT];
- fileExtensions = new vector<string>[FILETYPE_COUNT];
- fileDialog = new FileDirDialog;
-
- char cwd[PATH_MAX];
- getcwd(cwd, PATH_MAX);
-
- for (int pos = 0; pos < FILETYPE_COUNT; ++pos) {
- previousDirectories[pos] = cwd;
- }
- initFileExtensions(fileExtensions);
- }
- }
- // Cleanup
- void destroyFileDialogGlobals() { start_func
- if (previousDirectories) delete[] previousDirectories;
- if (fileExtensions) delete[] fileExtensions;
- if (fileDialog) delete fileDialog;
- previousDirectories = NULL;
- fileExtensions = NULL;
- fileDialog = NULL;
- }
- // Directory treeview
- DirectoryView::DirectoryView(const string& name, const char* fullpath, void* tPtr, int (*tAction)(void* ptr, int code, int command, int check), int topLevel) : TreeView(name, tPtr, 0, tAction, topLevel, FILESYSTEM_CASE_SENSITIVE), path(blankString) { start_func
- path = fullpath;
- hasSubDirs = existFiles(fullpath, NULL);
- if ((FILESYSTEM_DRIVE_SEP) && (name[1] == FILESYSTEM_DRIVE_SEP) && (name.length() == 2)) {
- iconOpen = 2;
- iconClosed = 2;
- }
- else {
- iconOpen = 0;
- iconClosed = 1;
- }
- }
- DirectoryView::~DirectoryView() { start_func
- }
- void DirectoryView::redo() { start_func
- removeAll();
- hasSubDirs = existFiles(path.c_str(), NULL);
- expand(1);
- if (hasSubItems()) {
- subtree[0]->selectMe(); // (start with top item selected)
- }
- else {
- selectMe(); // (start with self selected)
- }
- }
- int DirectoryView::hasSubItems() const { start_func
- return (subitems > 0) || (hasSubDirs);
- }
- void DirectoryView::expand(int state) { start_func
- if ((hasSubDirs) && (state)) {
- vector<string> subdirs;
-
- if (findFiles(path.c_str(), NULL, subdirs)) {
- vector<string>::iterator end = subdirs.end();
- DirectoryView* subdir = NULL;
-
- for (vector<string>::iterator pos = subdirs.begin(); pos != end; ++pos) {
- string newpath;
- createDirname(path.c_str(), (*pos).c_str(), newpath);
- subdir = new DirectoryView(*pos, newpath.c_str(), ptr, action);
- insert(subdir, 0);
- subdir = NULL;
- }
- hasSubDirs = 0;
- }
- else {
- hasSubDirs = 0;
- // We thought we had subdirs, but we didn't; however
- // we're dirty as our icon needs to vanish
- setDirty();
- return;
- }
- }
- TreeView::expand(state);
- }
- // Standard file dialog
- FileDirDialog::FileDirDialog() : Dialog(blankString), file(blankString) { start_func
- initialized = 0;
- tree = NULL;
- list = NULL;
- filename = NULL;
- currentExtensions = NULL;
- }
- FileDirDialog::~FileDirDialog() { start_func
- // (dialog/wscroll will not delete tree for us)
- // (we must clear widgets first, so our tree doesn't get referenced in ~Dialog())
- filename = NULL;
- list = NULL;
- clearWidgets();
- delete tree;
- }
- void FileDirDialog::firstControl() { start_func
- changeInputFocus(NULL);
- nextControl();
- if (saveAs) {
- nextControl();
- nextControl();
- }
- }
- void FileDirDialog::childModified(Window* modified) { start_func
- Dialog::childModified(modified);
- if ((modified == list) && (filename)) {
- // Refill textbox
- list->apply();
- const ListEntry* entry = list->findEntry(selection);
-
- if (entry) filename->state(entry->label);
- }
- }
- void FileDirDialog::refillList(const string* itemToSelect) { start_func
- DirectoryView* sel = dynamic_cast<DirectoryView*>(tree->findSelected());
- list->clear();
-
- if (sel) {
- // Fill listbox
- vector<string> subfiles;
- const char* path = sel->getFullpath();
- if (currentExtensions) {
- vector<string>::const_iterator extEnd = currentExtensions->end();
- for (vector<string>::const_iterator extPos = currentExtensions->begin(); extPos != extEnd; ++extPos) {
- string extMask(FILESYSTEM_EXTENSIONSEP);
- extMask += *extPos;
- findFiles(path, extMask.c_str(), subfiles);
- }
- }
-
- if (!subfiles.empty()) {
- #if FILESYSTEM_CASE_SENSITIVE
- sort(subfiles.begin(), subfiles.end());
- #else
- sort(subfiles.begin(), subfiles.end(), stringCmpI());
- #endif
- vector<string>::iterator end = subfiles.end();
-
- int id = 0;
- for (vector<string>::iterator pos = subfiles.begin(); pos != end; ++pos) {
- list->addItem(ListEntry(*pos, ++id));
- #if FILESYSTEM_CASE_SENSITIVE
- if ((itemToSelect) && (!strcmp(itemToSelect->c_str(), (*pos).c_str()))) selection = id;
- #else
- if ((itemToSelect) && (!myStricmp(itemToSelect->c_str(), (*pos).c_str()))) selection = id;
- #endif
- }
- }
-
- // Empty textbox
- if (filename) filename->state(blankString);
- }
- }
- int FileDirDialog::treeviewEvent(int code, int command, int check) { start_func
- if (check) return Window::COMMAND_HIDE;
- if (command == LV_MOVE) {
- refillList();
- return 1;
- }
-
- return 0;
- }
- int FileDirDialog::treeviewEventWrap(void* ptr, int code, int command, int check) { start_func
- return ((FileDirDialog*)ptr)->treeviewEvent(code, command, check);
- }
- Dialog::ButtonAction FileDirDialog::verifyEntry(int buttonId, ButtonAction buttonType) { start_func
- if ((buttonType != BUTTON_APPLY) && (buttonType != BUTTON_OK)) return BUTTON_DEFAULT;
- DirectoryView* sel = dynamic_cast<DirectoryView*>(tree->findSelected());
- const char* path = sel->getFullpath();
- // Allows entry of name by itself or a relative pathname or an absolute pathname
- string fullname;
- createFilename(path, filename->state().c_str(), fullname);
-
- // Add extension if none given and saveas
- if ((currentExtensions) && (saveAs)) {
- string::size_type found = filename->state().find_last_of(FILESYSTEM_SEPARATORS);
- if (found >= string::npos) found = 0;
- if (filename->state().find_first_of(FILESYSTEM_EXTENSIONSEP) >= string::npos) {
- fullname += FILESYSTEM_EXTENSIONSEP;
- fullname += (*currentExtensions)[0];
- }
- }
-
- // Store entry back in textbox
- filename->state(fullname);
- if (saveAs) {
- // Does file exist?
- FILE* exist = fopen(fullname.c_str(), "rb");
- if (exist) {
- int result = guiConfirmBox("File already exists- Overwrite?", "Confirm Overwrite");
- fclose(exist);
-
- if (!result) return BUTTON_NOTHING;
-
- // Attempt to write to file
- FILE* attempt = fopen(fullname.c_str(), "rb+");
- if (!attempt) {
- guiErrorBox(formatString("Error writing to file- %s", strerror(errno)), errorTitleException);
- return BUTTON_NOTHING;
- }
- fclose(attempt);
- }
- else {
- // Attempt to create file
- FILE* attempt = fopen(fullname.c_str(), "wb+");
- if (!attempt) {
- guiErrorBox(formatString("Error creating file- %s", strerror(errno)), errorTitleException);
- return BUTTON_NOTHING;
- }
- fclose(attempt);
- }
- }
- else {
- // Ensure file exists, and if applicable, can be written to
- FILE* exist = fopen(fullname.c_str(), openForWrite ? "rb+" : "rb");
- if (!exist) {
- guiErrorBox(formatString("Error opening file- %s", strerror(errno)), errorTitleException);
- return BUTTON_NOTHING;
- }
- fclose(exist);
- }
- return Dialog::verifyEntry(buttonId, buttonType);
- }
- int FileDirDialog::run(int isSaveAs, int isOpenForWrite, const vector<string>& extensions, string& storeFilename) { start_func
- // Different screen height?
- if ((initialized) && ((initializedH != screenHeight) || (initializedW != screenWidth))) {
- filename = NULL;
- list = NULL;
- clearWidgets();
- delete tree;
- tree = NULL;
- initialized = 0;
- }
- if (!initialized) {
- initializedH = screenHeight;
- initializedW = screenWidth;
- Widget* w = NULL;
-
- int innerHeight = initializedH - (fontHeight() * 10);
- if (innerHeight > 400) innerHeight = 400;
-
- int innerWidth = initializedW - 40;
- if (innerWidth > 400) innerWidth = 400;
- w = new WStatic(ID_LABEL, "Directory:");
- w->addTo(this);
- w = new WStatic(ID_LABEL, "Files:");
- w->addTo(this);
- // w contains pointer so it gets deleted if it doesn't get added
- // Must create list here because creating directoryview accesses it
- // (in resulting treeview events)
- w = list = new WListBox(ID_LIST, &selection, 1, innerWidth / 2, innerHeight / fontHeight());
- tree = new DirectoryView(blankString, topDir(), this, treeviewEventWrap, 1);
- tree->addTo(this, innerWidth / 2, innerHeight / tree->getItemHeight());
- // (wscroll will not delete tree for us)
-
- w->addTo(this);
-
- makePretty(2, 1, 1, 0);
- w = new WStatic(ID_LABEL, "Filename:");
- innerWidth -= w->getWidth();
- w->addTo(this);
- w = filename = new WTextBox(ID_ENTRY, &file, 0, innerWidth);
- w->addTo(this);
- w = new WButton(ID_OK, isSaveAs ? "Save" : "Open", BUTTON_OK);
- w->addTo(this);
-
- w = new WButton(ID_CANCEL, messageBoxCancel, BUTTON_CANCEL);
- w->addTo(this);
-
- makePretty();
-
- initialized = 1;
- }
- else {
- // Redo treeview
- assert(tree);
- tree->redo();
-
- // Redo buttons
- dynamic_cast<WButton*>(findWidget(ID_OK))->changeText(isSaveAs ? "Save" : "Open");
- }
-
- setTitle(isSaveAs ? "Save As" : "Open File");
- selection = 0;
- file = blankString;
- saveAs = isSaveAs;
- openForWrite = isOpenForWrite;
- currentExtensions = &extensions;
- // Split filename and select within tree
- vector<string> split;
- splitFilename(storeFilename.c_str(), split);
-
- vector<string>::iterator end = split.end();
- DirectoryView* currentTree = tree;
- int refilledList = 0;
- for (vector<string>::iterator pos = split.begin(); pos != end; ++pos) {
- currentTree->expand(1);
- currentTree = dynamic_cast<DirectoryView*>(currentTree->find(*pos));
- if (currentTree) {
- currentTree->selectMe();
- }
- else {
- // This will be a final item, check as a filename
- refillList(&(*pos));
- refilledList = 1;
- break;
- }
- }
-
- // List should be filled if it wasn't during filename scan
- if (!refilledList) refillList();
- if (runModal() == ID_OK) {
- storeFilename = file;
- currentExtensions = NULL;
- return 1;
- }
- currentExtensions = NULL;
- return 0;
- }
- int fileOpen(int type, int requireWriteAccess, string& filename) { start_func
- assert(type >= 0);
- assert(type < FILETYPE_COUNT);
- initFileSystems();
-
- if (filename.empty()) filename = previousDirectories[type];
- if (fileDialog->run(0, requireWriteAccess, fileExtensions[type], filename)) {
- getPathname(filename.c_str(), previousDirectories[type]);
- return 1;
- }
- return 0;
- }
- int fileSaveAs(int type, string& filename) { start_func
- assert(type >= 0);
- assert(type < FILETYPE_COUNT);
- initFileSystems();
-
- if (filename.empty()) filename = previousDirectories[type];
- if (fileDialog->run(1, 1, fileExtensions[type], filename)) {
- getPathname(filename.c_str(), previousDirectories[type]);
- return 1;
- }
- return 0;
- }
|