(C++) File Selector/Dialog v1.1

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

(C++) File Selector/Dialog v1.1

Post by JP »

So the standard Irrlicht file selector class doesn't have much of the required features you'd need for a file selector, it's really only aimed at easily opening files, rather than saving them too.

So i decided to make an extended version with the usual things you require:

- File filters, any number you like (ignores whether it's upper/lower case)
- Drive switching
- Leaves the working directory unaffected once closed
- Saving files and specifying the filename to use
- File/Directory icons

The drive switching is done similarly to how you might do it in a standard OS file selector; type in the drive you want (i.e. 'D:') and then press enter and it will switch to that drive. There's a bit of a bug which may a bug in IGUIEditBox or setWorkingDirectory() that if you're already on a drive, but not in the root and want to get back to the root by typing in the drive letter it won't work if you select the contents of the file name box and overwrite it with the drive letter but it will if you just delete the extra directories after the drive letter and then press enter... a strange one for sure....

In the sample binary i've provided you can test it out with some fake audio files in the bin directory. There's a .mp3, a .WMA and a .wavy. Selecting the MP3 file filter will show the .mp3 (which is lowercase), selecting the WMA file filter will show the .WMA (which is uppercase) and selecting the WAV file filter will show nothing as .wavy isn't a .wav! There's also an 'All files' filter which doesn't do any filtering so you can see all the files in the directories.

Here's a download link with a compiled binary and the source:
http://www.mediafire.com/?jmdw1kin14y

It works with Irrlicht 1.4.2

In the following post i'll give the entire source in case the download link dies.

Use this code as you wish, you don't need to mention my name at all as it's a pretty small bit of code that i've added.

If you've got any suggestions for improvement feel free to let me know (by PM is probably best as i don't check the code snippets forum much).

Here's some screenshots:

What windows shows to be in my directory:
Image

MP3 filter successfully showing lower case extension:
Image

WMA filter successfully showing upper case extension:
Image

WAV filter successfully ignoring .wavy file
Image

No filter successfully showing all files:
Image
Last edited by JP on Thu Sep 25, 2008 10:22 am, edited 6 times in total.
Image Image Image
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

CGUIFileSelector.h

Code: Select all

#ifndef INC_CGUIFILESELECTOR_H
#define INC_CGUIFILESELECTOR_H

#include <irrlicht.h>

using namespace irr;
using namespace gui;

/** Class for opening/saving files. */
class CGUIFileSelector : public IGUIFileOpenDialog {
      
	public:
        /** Enum to specify the usage of the instance of the class */   
        enum E_FILESELECTOR_TYPE {
          EFST_OPEN_DIALOG, //<! For opening files
          EFST_SAVE_DIALOG, //<! For saving files
          EFST_NUM_TYPES    //<! Not used, just specifies how many possible types there are
        };
                  
        /** 
		\brief Constructor
		\param title - The title of the dialog
		\pararm environment - The GUI environment to be used
		\param parent - The parent of the dialog
		\param id - The ID of the dialog
		\param type - The type of dialog
		*/
		CGUIFileSelector(const wchar_t* title, IGUIEnvironment* environment, IGUIElement* parent, s32 id, E_FILESELECTOR_TYPE type);

		/**
        \brief Destructor
        */
		virtual ~CGUIFileSelector();
		
		/**
        \brief Returns the filename of the selected file. Returns NULL, if no file was selected.
        \return a const wchar_t*
        */
		virtual const wchar_t* getFileName() const;

		/**
        \brief called if an event happened.
        \param even - the event that happened
        \return a bool
        */
		virtual bool OnEvent(const SEvent& event);

		/**
		\brief Render function
		*/
		virtual void draw();
		
		/**
		\brief Returns the current file filter selected or "All Files" if no filter is applied
		\return a stringw
		*/
		inline core::stringw getFileFilter() const {
               if (FilterComboBox->getSelected() >= FileFilters.size()) return core::stringw("All Files"); 
               else return FileFilters[FilterComboBox->getSelected()].FileExtension;
        }
        
        /**
        \brief Returns the type of the dialog
        \return a E_FILESELECTOR_TYPE
        */
        inline E_FILESELECTOR_TYPE getDialogType() { return DialogType; }
        
        /**
        \brief Add a file filter
        \param name - The description of the file type
        \param ext - The file's extension
        \param texture - The icon texture
        */
        void addFileFilter(wchar_t* name, wchar_t* ext, video::ITexture* texture);
        
        /**
        \brief Set an icon to use to display unknown files
        \param texture - the 16x16 icon to use
        */
        inline void setCustomFileIcon(video::ITexture* texture) {
          if (texture) FileIconIdx = addIcon(texture);
          else FileIconIdx = -1;
          fillListBox();
        }
        /**
        \brief Set an icon to use to display directories
        \param texture - the 16x16 icon to use
        */
        inline void setCustomDirectoryIcon(video::ITexture* texture) {
          if (texture) DirectoryIconIdx = addIcon(texture);
          else DirectoryIconIdx = -1;
          fillListBox();
        }
        
        /**
        \brief Sets whether directories can be chosen as the 'file' to open
        \param choosable - Whether the directory can be chosen
        */
        inline void setDirectoryChoosable(bool choosable) { IsDirectoryChoosable = choosable; }

	protected:

        /**
        \brief Returns true if the file extension is one of the registered filters
        \param s - the string to be checked
        \return a bool
        */
        bool matchesFileFilter(core::stringw s);
        /**
        \brief Returns true if the file extension matches the specified filter
        \param s - the string to be checked
        \param f - the filter to check for
        \return a bool
        */
        bool matchesFileFilter(core::stringw s, core::stringw f);
        
		/**
        \brief Fills the listbox with files.
        */
		void fillListBox();

		/**
        \brief Sends the event that the file has been selected.
        */
		void sendSelectedEvent();

		/**
        \brief Sends the event that the file choose process has been canceld
        */
		void sendCancelEvent();
		
		u32 addIcon(video::ITexture* texture);
				
		/** Struct to describe file filters to use when displaying files in directories */
        struct SFileFilter {
          /*
          \brief Constructor
          \param name - The name/description of the filter
          \param filter - The file extension required
          \param texture - The texture to use as an icon for the file type
          */
          SFileFilter(wchar_t* name, wchar_t* filter, video::ITexture* texture) {
            FilterName = name;
            FileExtension = filter;
            FileIcon = texture;
            FileIconIdx = 0;
          }
          void operator=(const SFileFilter& other) {
            FilterName = other.FilterName;
            FileExtension = other.FileExtension;
            FileIcon = other.FileIcon;
            FileIconIdx = other.FileIconIdx;
          }
          core::stringw FilterName;
          core::stringw FileExtension;     
          video::ITexture* FileIcon;
          u32 FileIconIdx;
        };

		core::position2d<s32> DragStart;
		bool Dragging;
		bool IsDirectoryChoosable;
		s32 FileIconIdx;
		s32 DirectoryIconIdx;
		IGUIButton* CloseButton;
		IGUIButton* OKButton;
		IGUIButton* CancelButton;
		IGUIEditBox* FileNameText;
		IGUIListBox* FileBox;
		IGUIComboBox* FilterComboBox;
		IGUIElement* EventParent;
		IGUISpriteBank* SpriteBank;
		io::IFileSystem* FileSystem;
		io::IFileList* FileList;
		core::array<SFileFilter> FileFilters;
		E_FILESELECTOR_TYPE DialogType;
		core::stringc prev_working_dir;
		
		static s32 numFileSelectors;
			
};

#endif /* INC_CGUIFILESELECTOR_H */

CGUIFileSelector.cpp

Code: Select all

#include "CGUIFileSelector.h"


const s32 FOD_WIDTH = 350;
const s32 FOD_HEIGHT = 265;

s32 CGUIFileSelector::numFileSelectors = 0;

//! constructor
CGUIFileSelector::CGUIFileSelector(const wchar_t* title, IGUIEnvironment* environment, IGUIElement* parent, s32 id, E_FILESELECTOR_TYPE type)
: IGUIFileOpenDialog(environment, parent, id,
 core::rect<s32>((parent->getAbsolutePosition().getWidth()-FOD_WIDTH)/2,
					(parent->getAbsolutePosition().getHeight()-FOD_HEIGHT)/2,	
					(parent->getAbsolutePosition().getWidth()-FOD_WIDTH)/2+FOD_WIDTH,
					(parent->getAbsolutePosition().getHeight()-FOD_HEIGHT)/2+FOD_HEIGHT)),	
  Dragging(false), FileNameText(0), FileList(0), DialogType(type)
{   
    #ifdef _DEBUG
	  IGUIElement::setDebugName("CGUIFileSelector");
	#endif    
    
    Text = title;
    IsDirectoryChoosable = false;
    
	IGUISkin* skin = Environment->getSkin();
	IGUISpriteBank* sprites = 0;
	video::SColor color(255,255,255,255);
	if (skin)
	{
		sprites = skin->getSpriteBank();
		color = skin->getColor(EGDC_WINDOW_SYMBOL);
	}

	s32 buttonw = Environment->getSkin()->getSize(EGDS_WINDOW_BUTTON_WIDTH);
	s32 posx = RelativeRect.getWidth() - buttonw - 4;

	CloseButton = Environment->addButton(core::rect<s32>(posx, 3, posx + buttonw, 3 + buttonw), this, -1, 
		L"", L"Close");
	CloseButton->setSubElement(true);
	if (sprites)
	{
		CloseButton->setSpriteBank(sprites);
		CloseButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_WINDOW_CLOSE), color);
		CloseButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_WINDOW_CLOSE), color);
	}
	CloseButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
	CloseButton->grab();

	OKButton = Environment->addButton(
		core::rect<s32>(RelativeRect.getWidth()-80, 30, RelativeRect.getWidth()-10, 50), 
		this, -1, (DialogType==EFST_OPEN_DIALOG?L"Open":L"Save"));
	OKButton->setSubElement(true);
	OKButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
	OKButton->grab();

	CancelButton = Environment->addButton(
		core::rect<s32>(RelativeRect.getWidth()-80, 55, RelativeRect.getWidth()-10, 75), 
		this, -1, skin ? skin->getDefaultText(EGDT_MSG_BOX_CANCEL) : L"Cancel");
	CancelButton->setSubElement(true);
	CancelButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
	CancelButton->grab();

	FileBox = Environment->addListBox(core::rect<s32>(10, 55, RelativeRect.getWidth()-90, 230), this, -1, true);
	FileBox->setSubElement(true);
	FileBox->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
	FileBox->grab();

	FileNameText = Environment->addEditBox(0, core::rect<s32>(10, 30, RelativeRect.getWidth()-90, 50), true, this, -1);
	FileNameText->setSubElement(true);
	FileNameText->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
	FileNameText->setTextAlignment(EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT);
	FileNameText->grab();
	
	FilterComboBox = Environment->addComboBox(core::rect<s32>(10, RelativeRect.getHeight()-30, RelativeRect.getWidth()-90, RelativeRect.getHeight()-10), this, -1);
	FilterComboBox->setSubElement(true);
	FilterComboBox->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
	FilterComboBox->grab();
    FilterComboBox->addItem(L"All Files");
   
    core::stringc str = "FileSelectorIcons";
    str += numFileSelectors++;
    SpriteBank = Environment->addEmptySpriteBank(str.c_str());
    if (SpriteBank) {
      SpriteBank->grab();
      FileBox->setSpriteBank(SpriteBank);
    }
    DirectoryIconIdx = -1;
    FileIconIdx = -1;
	
	FileSystem = Environment->getFileSystem();
	
	if (FileSystem) {
		FileSystem->grab();
	    prev_working_dir = FileSystem->getWorkingDirectory();
		//printf("working directory saved: %s\n", prev_working_dir.c_str());
	}

	fillListBox();
}

//! destructor
CGUIFileSelector::~CGUIFileSelector()
{
	if (CloseButton)
		CloseButton->drop();

	if (OKButton)
		OKButton->drop();

	if (CancelButton)
		CancelButton->drop();

	if (FileBox)
		FileBox->drop();

	if (FileNameText)
		FileNameText->drop();

	if (FileSystem)
		FileSystem->drop();

	if (FileList)
		FileList->drop();
		
	if (FilterComboBox)
	    FilterComboBox->drop();
	    
    if (SpriteBank)
        SpriteBank->drop();
	    
}


//! returns the filename of the selected file. Returns NULL, if no file was selected.
const wchar_t* CGUIFileSelector::getFileName() const
{
	return FileNameText->getText();
}



//! called if an event happened.
bool CGUIFileSelector::OnEvent(const SEvent& event)
{
	switch(event.EventType)
	{
    case EET_KEY_INPUT_EVENT:
         switch (event.KeyInput.Key) {
           case KEY_RETURN:
             if (FileSystem) {
			   FileSystem->changeWorkingDirectoryTo(core::stringc(FileNameText->getText()).c_str());
			   fillListBox();
			   FileNameText->setText(core::stringw(FileSystem->getWorkingDirectory()).c_str());
	         }
             return true;
         }
         break;
	case EET_GUI_EVENT:
		switch(event.GUIEvent.EventType)
		{
        case EGET_COMBO_BOX_CHANGED:
             fillListBox();
             break;
		case EGET_ELEMENT_FOCUS_LOST:
			Dragging = false;
			break;
		case EGET_BUTTON_CLICKED:
			if (event.GUIEvent.Caller == CloseButton ||
				event.GUIEvent.Caller == CancelButton)
			{
                if (FileSystem) {
				  FileSystem->changeWorkingDirectoryTo(prev_working_dir.c_str());
				  //printf("working directory reset to: %s\n", prev_working_dir.c_str());
				}
				sendCancelEvent();
				remove();
				return true;
			}
			else
			if (event.GUIEvent.Caller == OKButton && (IsDirectoryChoosable || matchesFileFilter(FileNameText->getText())))
			{
				if (FileSystem) {
				  FileSystem->changeWorkingDirectoryTo(prev_working_dir.c_str());
				  //printf("working directory reset to: %s\n", prev_working_dir.c_str());
				}
				sendSelectedEvent();
				remove();
				return true;
			}
			break;

		case EGET_LISTBOX_CHANGED:
			{
				s32 selected = FileBox->getSelected();
				if (FileList && FileSystem)
				{
					core::stringw strw;
			        strw = FileSystem->getWorkingDirectory();
			        if (strw[strw.size()-1] != '\\')
					  strw += "\\";
				    strw += FileBox->getListItem(selected);
					FileNameText->setText(strw.c_str());
				}
			}
			break;

		case EGET_LISTBOX_SELECTED_AGAIN:
			{
				s32 selected = FileBox->getSelected();
				if (FileList && FileSystem)
				{
					if (FileList->isDirectory(selected))
					{
						FileSystem->changeWorkingDirectoryTo(FileList->getFileName(selected));
						fillListBox();
						FileNameText->setText(core::stringw(FileSystem->getWorkingDirectory()).c_str());
					}
					else
					{
						core::stringw strw;
			            strw = FileSystem->getWorkingDirectory();
				    	if (strw[strw.size()-1] != '\\')
					      strw += "\\";
				        strw += FileBox->getListItem(selected);
					    FileNameText->setText(strw.c_str());
						return true;
					}
				}
			}
			break;
		}
		break;
	case EET_MOUSE_INPUT_EVENT:
		switch(event.MouseInput.Event)
		{
		case EMIE_LMOUSE_PRESSED_DOWN:
			DragStart.X = event.MouseInput.X;
			DragStart.Y = event.MouseInput.Y;
			Dragging = true;
			Environment->setFocus(this);
			return true;
		case EMIE_LMOUSE_LEFT_UP:
			Dragging = false;
			Environment->removeFocus(this);
			return true;
		case EMIE_MOUSE_MOVED:
			if (Dragging)
			{
				// gui window should not be dragged outside its parent
				if (Parent)
					if (event.MouseInput.X < Parent->getAbsolutePosition().UpperLeftCorner.X +1 ||
						event.MouseInput.Y < Parent->getAbsolutePosition().UpperLeftCorner.Y +1 ||
						event.MouseInput.X > Parent->getAbsolutePosition().LowerRightCorner.X -1 ||
						event.MouseInput.Y > Parent->getAbsolutePosition().LowerRightCorner.Y -1)

						return true;

				move(core::position2d<s32>(event.MouseInput.X - DragStart.X, event.MouseInput.Y - DragStart.Y));
				DragStart.X = event.MouseInput.X;
				DragStart.Y = event.MouseInput.Y;
				return true;
			}
			break;
		}
	}

	return Parent ? Parent->OnEvent(event) : false;
}


//! draws the element and its children
void CGUIFileSelector::draw()
{
	if (!IsVisible)
		return;

	IGUISkin* skin = Environment->getSkin();

	core::rect<s32> rect = AbsoluteRect;

	rect = skin->draw3DWindowBackground(this, true, skin->getColor(EGDC_ACTIVE_BORDER), 
		rect, &AbsoluteClippingRect);

	if (Text.size())
	{
		rect.UpperLeftCorner.X += 2;
		rect.LowerRightCorner.X -= skin->getSize(EGDS_WINDOW_BUTTON_WIDTH) + 5;

		IGUIFont* font = skin->getFont(EGDF_WINDOW);
		if (font)
			font->draw(Text.c_str(), rect, skin->getColor(EGDC_ACTIVE_CAPTION), false, true, 
			&AbsoluteClippingRect);
	}

	IGUIElement::draw();
}

bool CGUIFileSelector::matchesFileFilter(core::stringw s) {
  
  if (FilterComboBox->getSelected() >= FileFilters.size()) return true; // 'All Files' selectable
  else {
    s32 pos = s.findLast('.'); // Find the last '.' so we can check the file extension
    return FileFilters[FilterComboBox->getSelected()].FileExtension.equals_ignore_case(core::stringw(&s.c_str()[pos+1])); 
  }
  
}

bool CGUIFileSelector::matchesFileFilter(core::stringw s, core::stringw f) {
  
  s32 pos = s.findLast('.'); // Find the last '.' so we can check the file extension
  return f.equals_ignore_case(core::stringw(&s.c_str()[pos+1])); 
    
}

//! fills the listbox with files.
void CGUIFileSelector::fillListBox()
{
	IGUISkin *skin = Environment->getSkin();

	if (!FileSystem || !FileBox || !skin)
		return;

	if (FileList)
		FileList->drop();

	FileBox->clear();

	FileList = FileSystem->createFileList();
	core::stringw s;

	for (u32 i=0; i<FileList->getFileCount(); ++i)
	{
		s = FileList->getFileName(i);
		// We just want a list of directories and those matching the file filter
		if (FileList->isDirectory(i))
          if (DirectoryIconIdx != -1) FileBox->addItem(s.c_str(), DirectoryIconIdx);
          else                        FileBox->addItem(s.c_str());
		else if (matchesFileFilter(s))
		  if (FilterComboBox->getSelected() >= FileFilters.size())
            if (FileIconIdx != -1) {
              s32 iconIdx = FileIconIdx;
              for (u32 i = 0 ; i < FileFilters.size() ; i++) 
                if (matchesFileFilter(s, FileFilters[i].FileExtension))
                  iconIdx = FileFilters[i].FileIconIdx;
              FileBox->addItem(s.c_str(), iconIdx);
            } else  FileBox->addItem(s.c_str());
		  else FileBox->addItem(s.c_str(), FileFilters[FilterComboBox->getSelected()].FileIconIdx);        

	}

	if (FileNameText)
	{
		s = FileSystem->getWorkingDirectory();
		FileNameText->setText(s.c_str());
	}
}


//! sends the event that the file has been selected.
void CGUIFileSelector::sendSelectedEvent()
{
	SEvent event;
	event.EventType = EET_GUI_EVENT;
	event.GUIEvent.Caller = this;
	event.GUIEvent.EventType = EGET_FILE_SELECTED;
	Parent->OnEvent(event);
}

//! sends the event that the file choose process has been canceld
void CGUIFileSelector::sendCancelEvent()
{
	SEvent event;
	event.EventType = EET_GUI_EVENT;
	event.GUIEvent.Caller = this;
	event.GUIEvent.EventType = EGET_FILE_CHOOSE_DIALOG_CANCELLED;
	Parent->OnEvent(event);
}

void CGUIFileSelector::addFileFilter(wchar_t* name, wchar_t* ext, video::ITexture* texture) {

  SFileFilter filter(name, ext, texture);
  
  filter.FileIconIdx = addIcon(texture);
  
  FileFilters.push_back(filter);  
  
  FilterComboBox->clear();
  core::stringw strw;
  for (u32 i = 0 ; i < FileFilters.size() ; i++) {
    strw = FileFilters[i].FilterName;
    strw += " (*.";
    strw += FileFilters[i].FileExtension;
    strw += ")";
    FilterComboBox->addItem(strw.c_str());
  }
  FilterComboBox->addItem(L"All Files");
  
  fillListBox();

}

u32 CGUIFileSelector::addIcon(video::ITexture* texture) {
  
  if (!SpriteBank || !texture) return 0;
    
  // load and add the texture to the bank     
         SpriteBank->addTexture(texture); 
         u32 textureIndex = SpriteBank->getTextureCount() - 1; 
         // now lets get the sprite bank's rectangles and add some for our animation 
         core::array<core::rect<s32> >& rectangles = SpriteBank->getPositions(); 
         u32 firstRect = rectangles.size(); 
         // remember that rectangles are not in pixels, they enclose pixels! 
         // to draw a rectangle around the pixel at 0,0, it would rect<s32>(0,0, 1,1) 
         rectangles.push_back(core::rect<s32>(0,0, 16,16)); 
   

         // now we make a sprite.. 
         SGUISprite sprite; 
         sprite.frameTime = 30; 
         // add some frames of animation. 
         SGUISpriteFrame frame; 
         frame.rectNumber = firstRect; 
         frame.textureNumber = textureIndex; 

         // add this frame 
         sprite.Frames.push_back(frame); 
         // add the sprite 
         u32 spriteIndex = SpriteBank->getSprites().size(); 
         SpriteBank->getSprites().push_back(sprite);  
         
         return textureIndex;
    
}
Usage is as simple as this:

Code: Select all

CGUIFileSelector* selector = new CGUIFileSelector(L"File Selector", env, env->getRootGUIElement(), 1, CGUIFileSelector::EFST_OPEN_DIALOG);
    if (selector) {
      // Add some file filters and custom icons for unknown files and directories
      selector->setCustomFileIcon(driver->getTexture("file.png"));
      selector->setCustomDirectoryIcon(driver->getTexture("folder.png"));
      selector->addFileFilter(L"MP3", L"mp3", driver->getTexture("mp3.png"));
      selector->addFileFilter(L"PCM WAV", L"wav", driver->getTexture("wav.png"));
      selector->addFileFilter(L"Windows Media Audio", L"wma", driver->getTexture("wma.png"));
      selector->drop();
    }
That gives it 3 file filters for common audio files, if you don't care about file filters and just want to see all the files in a directory then do this:

Code: Select all

CGUIFileSelector* selector = new CGUIFileSelector(L"File Selector", env, env->getRootGUIElement(), 1, CGUIFileSelector::EFST_OPEN_DIALOG);
And to know what file has been selected you have this in your event receiver:

Code: Select all

    if (event.EventType == EET_GUI_EVENT) {
        switch(event.GUIEvent.EventType) {
          case EGET_FILE_SELECTED: {
		    CGUIFileSelector* dialog = (CGUIFileSelector*)event.GUIEvent.Caller;
			core::stringc fileName = dialog->getFileName();
			if (dialog->getDialogType() == CGUIFileSelector::EFST_OPEN_DIALOG)
              openFile(fileName.c_str());
            return true;
          }
	    }
	  }
Image Image Image
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

Updated to Irrlicht 1.4.2 and also added in sprite bank support to give the files and directories their own icons.
Image Image Image
BlindSide
Admin
Posts: 2821
Joined: Thu Dec 08, 2005 9:09 am
Location: NZ!

Post by BlindSide »

Nice, this is pretty sexy. Is it possible to get that address bar text to align to the left? Theres a bit too much space on the right side in your screenshots.

How do you decide what gets what icon, do you load it directly from the files, or have them custom set in the engine?
ShadowMapping for Irrlicht!: Get it here
Need help? Come on the IRC!: #irrlicht on irc://irc.freenode.net
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Post by Acki »

it seems there are 2 bugs:

1st, the listbox for possible extensions does not show up correctly !!!
see the pic:
Image

2nd, if you open/close this extensions listbox (combobox) a few times the app will crash with an Windows exception !!!

all with your (unmodified) demo...

BlindSide wrote:How do you decide what gets what icon, do you load it directly from the files, or have them custom set in the engine?
look at the demo:

Code: Select all

      selector->setCustomFileIcon(driver->getTexture("file.png"));
      selector->setCustomDirectoryIcon(driver->getTexture("folder.png"));
      selector->addFileFilter(L"MP3", L"mp3", driver->getTexture("mp3.png"));
      selector->addFileFilter(L"PCM WAV", L"wav", driver->getTexture("wav.png"));
      selector->addFileFilter(L"Windows Media Audio", L"wma", driver->getTexture("wma.png"));
while(!asleep) sheep++;
IrrExtensions:Image
http://abusoft.g0dsoft.com
try Stendhal a MORPG written in Java
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

Blindside, i aligned the text in the filename box to the right so that you can see the right extreme of it, so you can input a filename when saving or read which directory your're in. If it's aligned to the left then it just shows you as much as is possible and then you have to scroll to see the rest. Maybe it could be aligned to the left until it fills up the space and then it aligns to the right....

As Acki pointed out you set all the icons yourself when you create it. Every file filter you add has an icon, all other file types don't get an icon and use the default one you set.

Acki.... interesting bugs there.... not a clue about any of them, never seen those problems on my PC, i'll have to do some tests... Also not sure why the directory icons aren't showing up in your screenshot there...

One other 'bug' is the sprite bank bug i posted in the bugs forum. sprite banks don't get removed ever from the gui environment so i had to create a new sprite bank each time the file selector is created otherwise the sprite bank wouldnt be created as they need unique names. No one seems to have commented on the bug post though....
Image Image Image
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Post by Acki »

All righty then... :lol:
finaly it turned out that instead I'm helping you debugging your project you're helping me debugging my project !!! :oops:

!!! I'm realy sorry for bothering you !!! ;)
while(!asleep) sheep++;
IrrExtensions:Image
http://abusoft.g0dsoft.com
try Stendhal a MORPG written in Java
varmint
Posts: 46
Joined: Fri Oct 06, 2006 4:33 pm

Post by varmint »

Awesome code snippet! :D

I made a small change. I wanted to be able to see all filtered files at the same time, as well select them.

first change to addFileFilter

Code: Select all

void CGUIFileSelector::addFileFilter(wchar_t* name, wchar_t* ext, video::ITexture* texture) {

  SFileFilter filter(name, ext, texture);
 
  filter.FileIconIdx = addIcon(texture);
 
  FileFilters.push_back(filter); 
 
  FilterComboBox->clear();
  core::stringw strw;

  if (FileFilters.size() > 1) {
	  strw = "Supported ";
	  for (u32 i = 0 ; i < FileFilters.size() ; i++) {
		strw += ".";
		strw += FileFilters[i].FileExtension;
		strw += " ";
	  }
	  FilterComboBox->addItem(strw.c_str());
  }
  
  for (u32 i = 0 ; i < FileFilters.size() ; i++) {
    strw = FileFilters[i].FilterName;
    strw += " (*.";
    strw += FileFilters[i].FileExtension;
    strw += ")";
    FilterComboBox->addItem(strw.c_str());
  }
  FilterComboBox->addItem(L"All Files");
 
  fillListBox();

}
Then small change to matchesFileFilter(core::stringw s)

Code: Select all

bool CGUIFileSelector::matchesFileFilter(core::stringw s) {
 
	if (FileFilters.size() > 1) {
		s32 selected = FilterComboBox->getSelected();
		if (selected == 0) {
			for (s32 i = 0; i < FileFilters.size(); i++) {
				s32 pos = s.findLast('.'); // Find the last '.' so we can check the file extension
				if (FileFilters[i].FileExtension.equals_ignore_case(core::stringw(&s.c_str()[pos+1])))
					return true;
			}
			return false;
		}
		selected--;
		if (selected >= FileFilters.size()) return true; // 'All Files' selectable
		else {
			s32 pos = s.findLast('.'); // Find the last '.' so we can check the file extension
			return FileFilters[selected].FileExtension.equals_ignore_case(core::stringw(&s.c_str()[pos+1]));
		}
	}
	if (FilterComboBox->getSelected() >= FileFilters.size()) return true; // 'All Files' selectable
	else {
		s32 pos = s.findLast('.'); // Find the last '.' so we can check the file extension
		return FileFilters[FilterComboBox->getSelected()].FileExtension.equals_ignore_case(core::stringw(&s.c_str()[pos+1]));
	}
}
Thx V
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

Good addition Varmint, i'll put it into my code. Bitplane said that this and my scrolling tab control could be added into Irrlicht (at some point) but it'll require me to do some extra work to it like making sure it's in Niko's coding style etc and i don't have a huge amount of time for that right now unfortunately, trying to get IrrAI 0.5 out, but as soon as that's done i'll get onto it!
Image Image Image
Shoozza
Posts: 3
Joined: Tue Sep 30, 2008 1:34 am

Post by Shoozza »

nice work!
Here my suggestions/bugs:
It's not possible to type \ on german keyboardlayout
(altgr + ß or ctrl+alt+'ß' where 'ß' is on the same key like '?')
I would expect the file chooser to hide when you doubleclick on a file.
Doubleclick the address bar text doesn't make it get selected.
The addressbar text moves a few pixels up when i click it for the first time.
What about drag and drop and making it resizeable? (maybe overkill :) )
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

I won't be doing drag/drop and resizable addons for it, they're useful things i guess but they're not key to its functioning. I guess the closest i'd come to making it resizable would be giving it a user specified rectangle to the constructor so the user could decide how big/small to make it.

Double clicking on a file could certainly auto-open that file, it's the usual thing that happens with windows file selectors... i think there's actually a little bug with some single clicks being detected as double clicks... might have to look into that before accepting double clicks to open files.

all your other bugs i think would be to do with limitations of IGUIEditBox possibly... Can you type German characters into other IGUIEditBoxs?

Thanks for the input Shoozza :)
Image Image Image
Shoozza
Posts: 3
Joined: Tue Sep 30, 2008 1:34 am

Post by Shoozza »

no problem JP ;)

For the German keylayout all keys seems to work except the ones who need to have altgr + 'thekey' or ctrl+alt + 'thekey' to be pressed.
These are following:
@ = altgr + 'q'
\ = altgr + 'ß'
~ = altgr + '+'
² = altgr + '2'
³ = altgr + '3'
€ = altgr + 'e'
| = altgr + '<'
µ = altgr + 'm'

I also found out that the titlebars gradient changes (gets smaller - i hope you understand what i mean ;) ) when some part of the window is outside the view.

When i make the window a EFST_SAVE_DIALOG I cannot close it for nonexisting files (with i type them in the address bar - okok its a fileselector but still weird when you cannot do that).
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

When you type in a new filename to the address bar and click ok it doesn't accept it? I'm fairly sure it's always let me do that... so long as the file extension is correct.. did you specify a file extension?
Image Image Image
Shoozza
Posts: 3
Joined: Tue Sep 30, 2008 1:34 am

Post by Shoozza »

Yeah when the file extension is mp3 and i add e.g. test.xml it will not save (has to be *.mp3 in that case).
It does close for :.mp3 which cannot be saved on windows.
(\/:?"<>| are not allowed to be in the filename)
Also when i press enter after i typed the name (even without filer) it changes the addressbar to the foldername and doesn't close.
fabietto
Posts: 93
Joined: Wed Sep 24, 2008 4:38 pm
Location: Plymouth, UK
Contact:

Post by fabietto »

Great code... I was looking for something like that, in order to filter the files "offered" to the user for opening... :)

I think it might be a good idea to extend this file selector with the ability of selecting directories as well.
Post Reply