File Dialog using MyGUI and boost

Jad

22-06-2011 22:02:56

I decided to write my own instead of copying the one in the LayoutEditor (although i did copy the layout of the dialog ;)). Just wanted to share it with you :)

Note that you need to build a couple of boost libraries (filesystem and system) to use the code. More info here

Dialog.h
#ifndef DIALOG_H
#define DIALOG_H

#include <boost\filesystem.hpp>
#include <boost\function.hpp>
#include <MyGUI.h>

class Dialog
{
public:
Dialog(const std::string& name);

const std::string& getName();

void update();
void setVisible(bool set);
void setDialogTitle(const std::string& title);
void setButtonCaption(const std::string& caption);
void addFileExtension(const std::string& ext);
void setDefaultExtension(int index);
void setUserString(const std::string& value);
const std::string& getUserString();
void eventEndDialog(boost::function<void(Dialog*, const std::string&)> method);

private:
void windowCloseCallback(MyGUI::Window* _sender, const std::string& _name);
void itemAcceptedCallback(MyGUI::ListBox* _sender, size_t _index);
void itemSelectedCallback(MyGUI::ListBox* _sender, size_t _index);
void recentPathSelectedCallback(MyGUI::ComboBox* _sender, size_t _index);
void endDialogCallback(MyGUI::Widget* _sender);
void windowResizeCallback(MyGUI::Widget* _sender, const std::string& _key, const std::string& _value);
void extensionChangeCallback(MyGUI::ComboBox* _sender, size_t _index);

void updatePathList(boost::filesystem::path my_path);

//static int nDialogs;
//int mDialogIndex;
//void (GUIManager::*mUserMethod)(const std::string&, const std::string&);

std::string mName;
std::string mUserString;

// user callback
boost::function<void(Dialog*, const std::string&)> mUserMethod;

// widgets
MyGUI::Window* mWindow;
MyGUI::List* mPathList;
MyGUI::Edit* mFileEdit;
MyGUI::Button* mButton;
MyGUI::ComboBox* mRecentCombo;
MyGUI::ComboBox* mExtensionCombo;

boost::filesystem::path mCurrentPath; // current viewed directory
std::vector<boost::filesystem::path> mPath_set; // list of paths in the viewed directory
boost::filesystem::path* mSelectedFile;
std::vector<boost::filesystem::path> mRecentPaths;
};

#endif // DIALOG_H


Dialog.cpp
#include "Dialog.h"
using namespace boost::filesystem;

//int Dialog::nDialogs = 0;

Dialog::Dialog(const std::string& name) :
mName(name),
mWindow(0),
mPathList(0),
mFileEdit(0),
mButton(0),
mRecentCombo(0),
mExtensionCombo(0),
mSelectedFile(0),
mUserMethod(0)
{

// load dialog layout
MyGUI::VectorWidgetPtr widgets = MyGUI::LayoutManager::getInstance().loadLayout("Dialog.layout");

mWindow = (MyGUI::Window*)*widgets.begin();
mWindow->eventWindowButtonPressed += MyGUI::newDelegate(this, &Dialog::windowCloseCallback);
mWindow->setVisible(false);

mPathList = (MyGUI::List*)mWindow->findWidget("List");
mPathList->eventListSelectAccept += MyGUI::newDelegate(this, &Dialog::itemAcceptedCallback);
mPathList->eventListChangePosition += MyGUI::newDelegate(this, &Dialog::itemSelectedCallback);

mFileEdit = (MyGUI::Edit*)mWindow->findWidget("Edit");

mButton = (MyGUI::Button*)mWindow->findWidget("Confirm");
mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &Dialog::endDialogCallback);

mRecentCombo = (MyGUI::ComboBox*)mWindow->findWidget("Recent");
mRecentCombo->eventComboChangePosition += MyGUI::newDelegate(this, &Dialog::recentPathSelectedCallback);

mExtensionCombo = (MyGUI::ComboBox*)mWindow->findWidget("Extension");
mExtensionCombo->eventComboChangePosition += MyGUI::newDelegate(this, &Dialog::extensionChangeCallback);
mExtensionCombo->addItem("All files \".\"");
mExtensionCombo->setIndexSelected(0);
}

//==========================================================================
// User closed Dialog window
//==========================================================================
void Dialog::windowCloseCallback(MyGUI::Window* _sender, const std::string& _name)
{
mWindow->setVisible(false);
}

//==========================================================================
// User selected a path (double-clicked)
//==========================================================================
void Dialog::itemAcceptedCallback(MyGUI::ListBox* _sender, size_t _index)
{
if (_index == 0) // user clicked on the "[...]", so back up a directory
{
updatePathList(mCurrentPath.parent_path().string());
}

// it's a folder, open it
else if (is_directory(mPath_set[_index-1]))
{
updatePathList(mPath_set[_index-1]);
}

// it's a file, notify user and quit
else
{
endDialogCallback(0);
}
}

//==========================================================================
// User selected a path (single click)
//==========================================================================
void Dialog::itemSelectedCallback(MyGUI::ListBox* _sender, size_t _index)
{
// make sure it's a file
if (_index != 0 && !is_directory(mPath_set[_index-1]))
{
// mark as selected
mSelectedFile = &mPath_set[_index-1];
mFileEdit->setCaption(mPathList->getItemNameAt(_index));
}
}

//==========================================================================
// User re-opened a recently visited folder
//==========================================================================
void Dialog::recentPathSelectedCallback(MyGUI::ComboBox* _sender, size_t _index)
{
updatePathList(mRecentPaths[_index].parent_path());
}

//==========================================================================
// Dialog's closed
//==========================================================================
void Dialog::endDialogCallback(MyGUI::Widget* _sender)
{
// last minute fix --forgot to account for the user entering the filename himself
// Note that since the Dialog has no idea if the user should be creating a new file, it doesn't check if the
// file exists or not. That's your job.
std::string filename = mFileEdit->getCaption();

// make sure it has an extension
if (mExtensionCombo->getIndexSelected() != 0)
{
std::string extension = mExtensionCombo->getCaption();
if (filename.length() > extension.length())
{
if(!filename.compare(filename.length() - extension.length(), extension.length(), extension))
filename.append(extension);
}

else
filename.append(extension);
}

mSelectedFile = new path(mPath_set[0].parent_path().append(filename.begin(), filename.end()));
//--

if (mSelectedFile)
{
// notify user of selected file (empty string if none)
if (mUserMethod)
{
mUserMethod(this, mSelectedFile->string());
}

// update recently visited paths
bool newEntry = true;
for (size_t i = 0; i < mRecentPaths.size(); i++)
{
if (*mSelectedFile == mRecentPaths)
{
newEntry = false;
break;
}
}

if (newEntry)
{
mRecentPaths.push_back(*mSelectedFile);
mRecentCombo->addItem(mSelectedFile->parent_path().string());
}

// close dialog
mWindow->setVisible(false);
}

// follow up of the fix
delete mSelectedFile;
mSelectedFile = 0;
}

//==========================================================================
// called when a new extension is set
//==========================================================================
void Dialog::extensionChangeCallback(MyGUI::ComboBox* _sender, size_t _index)
{
update();
}

//==========================================================================
// Set callback for when a file is selected
//==========================================================================
void Dialog::eventEndDialog(boost::function<void(Dialog*, const std::string&)> method)
{
mUserMethod = method;
}

//==========================================================================
// get dialog name
//==========================================================================
const std::string& Dialog::getName()
{
return mName;
}

//==========================================================================
// Must be called once dialog is initialized with the functions below
//==========================================================================
void Dialog::update()
{
updatePathList(current_path());
}

//==========================================================================
// show/hide dialog
//==========================================================================
void Dialog::setVisible(bool set)
{
mWindow->setVisible(set);
}

//==========================================================================
// set dialog window title
//==========================================================================
void Dialog::setDialogTitle(const std::string& title)
{
mWindow->setCaption(title);
}

//==========================================================================
// set button caption (ie: save, load, save as..)
//==========================================================================
void Dialog::setButtonCaption(const std::string& caption)
{
mButton->setCaption(caption);
}

//==========================================================================
// Attaches a string to this dialog that can be retrieved later.
// Handy for using the same Dialog for different things
//==========================================================================
void Dialog::setUserString(const std::string& value)
{
mUserString = value;
}

//==========================================================================
// Return user string
//==========================================================================
const std::string& Dialog::getUserString()
{
return mUserString;
}

//==========================================================================
// Add an extension to the extension combobox
//==========================================================================
void Dialog::addFileExtension(const std::string& ext)
{
mExtensionCombo->addItem(ext);
mExtensionCombo->setIndexSelected(0); // MyGUI appears to reset
// the selected item to ITEM_NONE
// whenever a new item is added
}

//==========================================================================
// If this isn't called, the default is show all files.
//==========================================================================
void Dialog::setDefaultExtension(int index)
{
mExtensionCombo->setIndexSelected(index + 1);
}

//==========================================================================
// Sort paths alphabetically (folders first, files last)
//==========================================================================
bool sortFunction(boost::filesystem::path p1, boost::filesystem::path p2)
{
bool d1 = is_directory(p1);
bool d2 = is_directory(p2);

if (d1 && d2)
return (p1.filename() < p2.filename());
else if (d1)
return true;
else if (d2)
return false;
else
return (p1.filename() < p2.filename());
}

//==========================================================================
// Update the paths in the dialog
// my_path; parent directory
// Note: this function is slow. Mainly because i'm lazy.
//==========================================================================
void Dialog::updatePathList(path my_path)
{
mCurrentPath = my_path;

mPathList->removeAllItems();
mPath_set.clear();

mPathList->addItem("[...]");

std::vector<path> temp_paths;

try
{
copy(directory_iterator(mCurrentPath), directory_iterator(), back_inserter(temp_paths));
}

catch(const filesystem_error& ex)
{
// the user probably backed up too far. Jackass.
mCurrentPath = current_path().parent_path();
copy(directory_iterator(mCurrentPath), directory_iterator(), back_inserter(temp_paths));
}

// some shitty OSs don't sort paths..
sort(temp_paths.begin(), temp_paths.end(), sortFunction);

// extension
std::string ext = mExtensionCombo->getCaption();

for (size_t i = 0; i < temp_paths.size(); i++)
{
// folder
if (is_directory(temp_paths))
{
mPath_set.push_back(temp_paths);
mPathList->addItem('[' + temp_paths.filename() + ']');
}

// file
else
{
// filter to extension (if any)
if (mExtensionCombo->getIndexSelected() != 0)
{
if (temp_paths.extension() == ext)
{
mPathList->addItem(temp_paths.filename());
mPath_set.push_back(temp_paths);
}
}

else
{
mPathList->addItem(temp_paths.filename());
mPath_set.push_back(temp_paths);
}
}
}
}


Dialog.layout
<?xml version="1.0" encoding="UTF-8"?>
<MyGUI type="Layout" version="3.2.0">
<Widget type="Window" skin="WindowCSX" position="40 44 505 351" layer="Main" name="DialogWindow">
<Widget type="ListBox" skin="ListBox" position="5 35 485 246" align="Stretch" name="List"/>
<Widget type="ComboBox" skin="ComboBox" position="5 3 485 30" align="HStretch Top" name="Recent"/>
<Widget type="Button" skin="Button" position="400 285 90 26" align="Right Bottom" name="Confirm"/>
<Widget type="EditBox" skin="EditBox" position="5 285 285 26" align="HStretch Bottom" name="Edit"/>
<Widget type="ComboBox" skin="ComboBox" position="300 285 95 26" align="Right Bottom" name="Extension"/>
</Widget>
</MyGUI>


And finally an example of using it:

mDialog = new Dialog;
mDialog->setDialogTitle("Save file");
mDialog->setButtonCaption("Save");
mDialog->addFileExtension(".h");
mDialog->addFileExtension(".cpp");
// callback
boost::function<void(Dialog*, const std::string&)> my_function = boost::bind(&My_Class::openSaveDialogCallback, this, _1, _2);
mDialog->eventEndDialog(my_function);
mDialog->update();

Sord

14-03-2012 10:27:24

Thanks from share. I fixed few bugs.

.h

#ifndef DIALOG_H
#define DIALOG_H

#include <boost\filesystem.hpp>
#include <boost\function.hpp>
#include <MyGUI.h>

class FileSelectionDialog
{
public:
FileSelectionDialog(const std::string& name);

const std::string& getName();

void update();
void setVisible(bool set);
void setDialogTitle(const std::string& title);
void setButtonCaption(const std::string& caption);
void addFileExtension(const std::string& ext);
void setDefaultExtension(int index);
void setUserString(const std::string& value);
void buildGUI();
const std::string& getUserString();
void eventEndDialog(boost::function<void(FileSelectionDialog*, const std::string&)> method);

private:
void windowCloseCallback(MyGUI::Window* _sender, const std::string& _name);
void itemAcceptedCallback(MyGUI::ListBox* _sender, size_t _index);
void itemSelectedCallback(MyGUI::ListBox* _sender, size_t _index);
void recentPathSelectedCallback(MyGUI::ComboBox* _sender, size_t _index);
void endDialogCallback(MyGUI::Widget* _sender);
void windowResizeCallback(MyGUI::Widget* _sender, const std::string& _key, const std::string& _value);
void extensionChangeCallback(MyGUI::ComboBox* _sender, size_t _index);


void updatePathList(boost::filesystem::path my_path);

//static int nDialogs;
//int mDialogIndex;
//void (GUIManager::*mUserMethod)(const std::string&, const std::string&);

std::string mName;
std::string mUserString;

// user callback
boost::function<void(FileSelectionDialog*, const std::string&)> mUserMethod;

// widgets
MyGUI::Window* mWindow;
MyGUI::List* mPathList;
MyGUI::Edit* mFileEdit;
MyGUI::Button* mButton;
MyGUI::ComboBox* mRecentCombo;
MyGUI::ComboBox* mExtensionCombo;

boost::filesystem::path mCurrentPath; // current viewed directory
std::vector<boost::filesystem::path> mPath_set; // list of paths in the viewed directory
boost::filesystem::path* mSelectedFile;
std::vector<boost::filesystem::path> mRecentPaths;
};

#endif // DIALOG_H



.cpp

#include "FileSelectionDialog.h"
using namespace boost::filesystem;

//int Dialog::nDialogs = 0;

FileSelectionDialog::FileSelectionDialog(const std::string& name) :
mName(name),
mWindow(0),
mPathList(0),
mFileEdit(0),
mButton(0),
mRecentCombo(0),
mExtensionCombo(0),
mSelectedFile(0),
mUserMethod(0)
{
buildGUI();
}

//==========================================================================
// Useful after after deleted all widgets from scene
//==========================================================================
void FileSelectionDialog::buildGUI(){
// load dialog layout
MyGUI::VectorWidgetPtr widgets = MyGUI::LayoutManager::getInstance().loadLayout("FileDialog.layout");

mWindow = (MyGUI::Window*)*widgets.begin();
mWindow->eventWindowButtonPressed += MyGUI::newDelegate(this, &FileSelectionDialog::windowCloseCallback);
mWindow->setVisible(false);

mPathList = (MyGUI::List*)mWindow->findWidget("List");
mPathList->eventListSelectAccept += MyGUI::newDelegate(this, &FileSelectionDialog::itemAcceptedCallback);
mPathList->eventListChangePosition += MyGUI::newDelegate(this, &FileSelectionDialog::itemSelectedCallback);

mFileEdit = (MyGUI::Edit*)mWindow->findWidget("Edit");

mButton = (MyGUI::Button*)mWindow->findWidget("Confirm");
mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &FileSelectionDialog::endDialogCallback);

mRecentCombo = (MyGUI::ComboBox*)mWindow->findWidget("Recent");
mRecentCombo->eventComboChangePosition += MyGUI::newDelegate(this, &FileSelectionDialog::recentPathSelectedCallback);

mExtensionCombo = (MyGUI::ComboBox*)mWindow->findWidget("Extension");
mExtensionCombo->eventComboChangePosition += MyGUI::newDelegate(this, &FileSelectionDialog::extensionChangeCallback);
mExtensionCombo->addItem("All files \".\"");
mExtensionCombo->setIndexSelected(0);
}


//==========================================================================
// User closed Dialog window
//==========================================================================
void FileSelectionDialog::windowCloseCallback(MyGUI::Window* _sender, const std::string& _name)
{
mWindow->setVisible(false);
}

//==========================================================================
// User selected a path (double-clicked)
//==========================================================================
void FileSelectionDialog::itemAcceptedCallback(MyGUI::ListBox* _sender, size_t _index)
{
if (_index == 0) // user clicked on the "[...]", so back up a directory
{
updatePathList(mCurrentPath.parent_path().string());
}

// it's a folder, open it
else if (is_directory(mPath_set[_index-1]))
{
updatePathList(mPath_set[_index-1]);
}

// it's a file, notify user and quit
else
{
endDialogCallback(0);
}
}

//==========================================================================
// User selected a path (single click)
//==========================================================================
void FileSelectionDialog::itemSelectedCallback(MyGUI::ListBox* _sender, size_t _index)
{
//TODO fix bug?: if in filelist, file / folder selected and then click to list box to empty line, calls this with _index = int.max(?)
if (_index > mPath_set.size())
return;

// make sure it's a file
if (_index != 0 && !is_directory(mPath_set[_index-1]))
{
// mark as selected
mSelectedFile = &mPath_set[_index-1];
mFileEdit->setCaption(mPathList->getItemNameAt(_index));
}
}

//==========================================================================
// User re-opened a recently visited folder
//==========================================================================
void FileSelectionDialog::recentPathSelectedCallback(MyGUI::ComboBox* _sender, size_t _index)
{
updatePathList(mRecentPaths[_index].parent_path());
}

//==========================================================================
// Dialog's closed
//==========================================================================
void FileSelectionDialog::endDialogCallback(MyGUI::Widget* _sender)
{
// last minute fix --forgot to account for the user entering the filename himself
// Note that since the Dialog has no idea if the user should be creating a new file, it doesn't check if the
// file exists or not. That's your job.

std::string filename = mFileEdit->getCaption();

// make sure it has an extension
if (mExtensionCombo->getIndexSelected() != 0)
{
std::string extension = mExtensionCombo->getCaption();
if (filename.length() > extension.length())
{
if(filename.compare(filename.length() - extension.length(), extension.length(), extension))
filename.append(extension);
}
else
filename.append(extension);
}

mSelectedFile = new path(mPath_set[0].parent_path().append(filename.begin(), filename.end()));
//--

if (mSelectedFile)
{
// notify user of selected file (empty string if none)
if (mUserMethod)
{
mUserMethod(this, mSelectedFile->string());
}

// update recently visited paths
bool newEntry = true;
for (size_t i = 0; i < mRecentPaths.size(); i++)
{
if (*mSelectedFile == mRecentPaths)
{
newEntry = false;
break;
}
}

if (newEntry)
{
mRecentPaths.push_back(*mSelectedFile);
mRecentCombo->addItem(mSelectedFile->parent_path().string());
}

// close dialog
mWindow->setVisible(false);
}

// follow up of the fix
delete mSelectedFile;
mSelectedFile = 0;
}

//==========================================================================
// called when a new extension is set
//==========================================================================
void FileSelectionDialog::extensionChangeCallback(MyGUI::ComboBox* _sender, size_t _index)
{
update();
}

//==========================================================================
// Set callback for when a file is selected
//==========================================================================
void FileSelectionDialog::eventEndDialog(boost::function<void(FileSelectionDialog*, const std::string&)> method)
{
mUserMethod = method;
}

//==========================================================================
// get dialog name
//==========================================================================
const std::string& FileSelectionDialog::getName()
{
return mName;
}

//==========================================================================
// Must be called once dialog is initialized with the functions below
//==========================================================================
void FileSelectionDialog::update()
{
updatePathList(current_path());
}

//==========================================================================
// show/hide dialog
//==========================================================================
void FileSelectionDialog::setVisible(bool set)
{
mWindow->setVisible(set);
}

//==========================================================================
// set dialog window title
//==========================================================================
void FileSelectionDialog::setDialogTitle(const std::string& title)
{
mWindow->setCaption(title);
}

//==========================================================================
// set button caption (ie: save, load, save as..)
//==========================================================================
void FileSelectionDialog::setButtonCaption(const std::string& caption)
{
mButton->setCaption(caption);
}

//==========================================================================
// Attaches a string to this dialog that can be retrieved later.
// Handy for using the same Dialog for different things
//==========================================================================
void FileSelectionDialog::setUserString(const std::string& value)
{
mUserString = value;
}

//==========================================================================
// Return user string
//==========================================================================
const std::string& FileSelectionDialog::getUserString()
{
return mUserString;
}

//==========================================================================
// Add an extension to the extension combobox
//==========================================================================
void FileSelectionDialog::addFileExtension(const std::string& ext)
{
mExtensionCombo->addItem(ext);
mExtensionCombo->setIndexSelected(0); // MyGUI appears to reset
// the selected item to ITEM_NONE
// whenever a new item is added
}

//==========================================================================
// If this isn't called, the default is show all files.
//==========================================================================
void FileSelectionDialog::setDefaultExtension(int index)
{
mExtensionCombo->setIndexSelected(index + 1);
}

//==========================================================================
// Sort paths alphabetically (folders first, files last)
//==========================================================================
bool sortFunction(boost::filesystem::path p1, boost::filesystem::path p2)
{
bool d1 = is_directory(p1);
bool d2 = is_directory(p2);

if (d1 && d2)
return (p1.filename() < p2.filename());
else if (d1)
return true;
else if (d2)
return false;
else
return (p1.filename() < p2.filename());
}

//==========================================================================
// Update the paths in the dialog
// my_path; parent directory
// Note: this function is slow. Mainly because i'm lazy.
//==========================================================================
void FileSelectionDialog::updatePathList(path my_path)
{
mCurrentPath = my_path;

mPathList->removeAllItems();
mPath_set.clear();

mPathList->addItem("[...]");

std::vector<path> temp_paths;

try
{
copy(directory_iterator(mCurrentPath), directory_iterator(), back_inserter(temp_paths));
}

catch(const filesystem_error& ex)
{
// the user probably backed up too far. Jackass.
mCurrentPath = current_path().parent_path();
copy(directory_iterator(mCurrentPath), directory_iterator(), back_inserter(temp_paths));
}

// some shitty OSs don't sort paths..
sort(temp_paths.begin(), temp_paths.end(), sortFunction);

// extension
std::string ext = mExtensionCombo->getItem(mExtensionCombo->getIndexSelected());

for (size_t i = 0; i < temp_paths.size(); i++)
{
// folder
if (is_directory(temp_paths))
{
mPath_set.push_back(temp_paths);
mPathList->addItem(MyGUI::UString("[") + MyGUI::UString(temp_paths.filename().c_str()) + ']');
}

// file
else
{
// filter to extension (if any)
if (mExtensionCombo->getIndexSelected() != 0)
{
if (temp_paths.extension() == ext)
{
MyGUI::UString path = temp_paths.filename().c_str();
mPathList->addItem(path);
mPath_set.push_back(temp_paths);
}
}

else
{
MyGUI::UString path = temp_paths.filename().c_str();
mPathList->addItem(path);
mPath_set.push_back(temp_paths);
}
}
}
}

Altren

14-03-2012 18:51:31

Could you post this as wiki page? I think this might be useful for other users, but in forum it is getting lost fast.