Page 1 of 1

IGUIFileOpenDialog and changeWorkingDirectoryTo

Posted: Mon Mar 31, 2008 2:11 am
by monkeycracks
Well at least this time I searched through the docs...
How do I set file extension limitations on what shows up in the dialog?
If this isn't possible does anyone have any theories on what could be done?

Otherwise, how could I go about using the Windows file open dialog with Irrlicht using the IEventReceiver? (I think this is impossible.)

As for the changeWorking.. etc. etc.

For some reason if the folder that I want to change to is not found, it doesn't seem to return false. It still moves the working directory to that folder. This is kinda irritating as I was using that as the only way I could tell if the directory existed or not.

I'm using the latest SVN and D3D9. The tests were on XP and on Vista. The Documents and Settings folder wasn't on the Vista computer (Two different computers.) yet it still changed the working directory to it.

Re: IGUIFileOpenDialog and changeWorkingDirectoryTo

Posted: Mon Mar 31, 2008 3:23 am
by vitek
monkeycracks wrote:Well at least this time I searched through the docs...
How do I set file extension limitations on what shows up in the dialog?
If this isn't possible does anyone have any theories on what could be done?
Write a function that tells you if a filename matches a given pattern. I wrote one a long time ago that supports '*' and '?' like windows does. In the unix world there is a POSIX function named fnmatch() that you could use. You could write your own version of it in a hundred lines or so.
moneycracks wrote:Otherwise, how could I go about using the Windows file open dialog with Irrlicht using the IEventReceiver? (I think this is impossible.)
It is possible, but I wouldn't recommend it. I worked on an application that used CFileOpenDialog windows on top of a D3D9 application. There were often problems that would come up. The dialog would disappear, it would cause problems with the application window... It was a mess.
moneycracks wrote:For some reason if the folder that I want to change to is not found, it doesn't seem to return false. It still moves the working directory to that folder. This is kinda irritating as I was using that as the only way I could tell if the directory existed or not.
You can see if a directory exists using the IFileList interface, or you could go down a level and use stat().

Travis

Posted: Mon Mar 31, 2008 3:29 am
by vitek
Here is the code that I wrote for file pattern matching. It _should_ work for expressions with * and ?.

Code: Select all

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

bool
simple_filename_match(const char* pat, const char* str)
{
	// may recursively call self to do pattern matching
	while (*pat && *str)
	{
		switch (*pat)
		{
		// match 0 or more characters
		case '*':
			while (*pat == '*')
				++pat; // additional stars mean nothing
			while ((str = strchr(str, *pat)) != 0)
			{
				if (simple_filename_match(pat, str++))
					return true;
			}

			if (!str)
				return false;
			break;

		// match any one character
		case '?':
			pat += 1;
			str += 1;
			break;

		// match a character
		default:
			if (*pat++ != *str++)
				return false;
			break;
		}
	}
	
	return !(*pat || *str);
}
The documentation for fnmatch() can be found here.

Travis

Posted: Mon Mar 31, 2008 4:09 am
by monkeycracks
Lost me a bit with that.. let me see if I get it..

const char* pat would be "*.jpg" and str would be the filepath?
if the filepath is a .jpg file, the bool returns true?


Orrr did I just miss that point completely..

Also, I'm targeting the Windows platform as I'm using some wininet and windows things.

Also, could you expound a bit on the IFileList thing, I understand that I'll want to use isDirectory, but how would I find the index number of said directory

Posted: Mon Mar 31, 2008 9:51 am
by vitek
monkeycracks wrote:const char* pat would be "*.jpg" and str would be the filepath?if the filepath is a .jpg file, the bool returns true?
Yes. It just tells you if the string str matches the pattern pat. If pat is "*.jpg", and str is "image.jpg", the function would return true. A quick change to the file dialog and its interface and you have file name filtering.
moneycracks wrote:Also, could you expound a bit on the IFileList thing, I understand that I'll want to use isDirectory, but how would I find the index number of said directory
I looked at the implementation of changeWorkingDirectoryTo(), and it uses chdir(). That function is documented to fail if the specified directory does not exist. So, you shouldn't need to do any workarounds. It should just work. Maybe you are running into a special case on Windows?

Travis

Posted: Mon Mar 31, 2008 10:53 am
by monkeycracks
Well I can't be too sure on the Vista computer as I can't have a look myself, but the tester said he was pretty sure that he did not have a C:\Documents and Settings.

Thanks for clarifying on that, I'll try to get it implemented sometime later today. Do you think that something like "*.jpg\0 *.bmp\0 *.png" (similar to the windows file open dialog) would be possible or will I need to modify the code for that?

Edit: I just failed at attempting to modify the engine, can't for the life of me figure out what I've done wrong. I added setPattern(char* pattern) and a stringc inside CFileOpenDialog to hold it. I changed the code at Ln 326-336
from

Code: Select all

	for (u32 i=0; i<FileList->getFileCount(); ++i)
	{
		s = FileList->getFileName(i);
		FileBox->addItem(s.c_str(), skin->getIcon(FileList->isDirectory(i) ? EGDI_DIRECTORY : EGDI_FILE));
	}

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

Code: Select all

	for (u32 i=0; i<FileList->getFileCount(); ++i)
	{
		s = FileList->getFileName(i);
		if(simple_filename_match(pattern.c_str(), stringc(s.c_str()).c_str()))
		{
			FileBox->addItem(s.c_str(), skin->getIcon(FileList->isDirectory(i) ? EGDI_DIRECTORY : EGDI_FILE));
		}
	}

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

It throws no compile errors but when I use d->addPattern("*.jpg");, nothing in the file list shows up at all. Not even the . and .. normally located to navigate through dirs.

My theory is that I should be using the addPattern in the constructor and not after its made and fillFileList() (it's in the constructor) is called. I'll look into that when I get home.

Posted: Mon Mar 31, 2008 6:58 pm
by vitek
monkeycracks wrote:Do you think that something like "*.jpg\0 *.bmp\0 *.png" (similar to the windows file open dialog) would be possible or will I need to modify the code for that?
Yes, this could be done quite easily. You would need to write a short routine to check for matches on each of the patterns. This should do it.

Code: Select all

bool simple_filename_match_many (const char* pat, const char* str)
{
  // it is expected that pat is a null terminated string
  // of null terminated strings. i.e. it ends with two nulls.

  for (/**/; *pat; pat += strlen (pat) + 1)
  {
    if (simple_filename_match (pat, str))
      return true;
  }

  return false;
}
moneycracks wrote:I just failed at attempting to modify the engine, can't for the life of me figure out what I've done wrong...
Uh, well I think you want to filter filenames only. You probably don't want to filter directories.

Code: Select all

for (u32 i=0; i<FileList->getFileCount(); ++i)
{
    const core::stringw s = FileList->getFileName(i);

    if (FileList->isDirectory(i))
    {
      // always include directories
      FileBox->addItem(s.c_str(), skin->getIcon(EGDI_DIRECTORY));
    }
    else if (!pattern.c_str())
    {
      // no pattern string, so show all files
      FileBox->addItem(s.c_str(), skin->getIcon(EGDI_FILE));
    }
    else
    {
      // we have a pattern string, only include file if it matches one
      // of the patterns
      const core::stringc ss (s.c_str());

      if (simple_filename_match_many(pattern.c_str(), ss.c_str()))
      {
        // only include files if they match pattern
        FileBox->addItem(s.c_str(), skin->getIcon(EGDI_FILE));
      }
   }
}

If you want to do this, your setPattern() function has to be smart enough to capture the full null terminated list of null terminated strings, or the caller has to be smart enough to do the right thing to make sure that the embedded nulls are included in the length of the string. I would propose that you write one of the following. Remember that 's' must be double null terminated.

Code: Select all

void CGUIFileOpenDialog::setPattern (const c8* s, u32 n)
{
  pattern = core::stringc (s, n);
  fillListBox(); // refresh file list
}

void CGUIFileOpenDialog::setPattern (const core::stringc& s)
{
  pattern = s;
  fillListBox(); // refresh file list
}
moneycracks wrote:'s' is a stringw
You can easily modify the code I've provided to work with wchar_t so you don't have to do all of that conversion.
moneycracks wrote:My theory is that I should be using the addPattern in the constructor and not after its made and fillFileList() (it's in the constructor) is called. I'll look into that when I get home.
The code I've provided above should not have that problem. If there is no pattern string, then all files are selected. If you set the pattern after the dialog is constructed, then the list box will be refreshed.

Travis

Posted: Mon Mar 31, 2008 9:10 pm
by monkeycracks
Thanks vitek. I really appreciate all the help you're giving me on this. Everything works like a charm!

The only question I have remaining is
Also, could you expound a bit on the IFileList thing, I understand that I'll want to use isDirectory, but how would I find the index number of said directory
if anyone cares to answer it.

Thanks much again vitek. Strings always seem to give me troubles.


Edit:: sorry that may have not been clear enough, I want to get the index number based on the name of the directory if possible


Edit:: Sorry for bugging you again vitek, but would it be possible to have more than two patterns? As is I can only do "*.jpg\0*.bmp\0", anything past the second \0 gets ignored by the function and I'm not exactly sure how to make it consider more.

Posted: Mon Mar 31, 2008 9:19 pm
by vitek
Iterate through each of the entries in the file list. When you find the one that has a matching name, you have the index.

Travis

Posted: Mon Mar 31, 2008 9:29 pm
by monkeycracks
Vitek, Thanks again, I edited my post at the bottom right as you posted.

Posted: Tue Apr 01, 2008 12:18 am
by vitek
moneycracks wrote:I want to get the index number based on the name of the directory if possible
Yes, you do exactly like I said. You iterate through the file list from 0 to fileList->getFileCount(). For every name in the list, you compare it against the name you are looking for. If you find a name that matches, then you know the index of that name.

I've written enough code for you, so I'm not going to write this. It is a simple loop with a counter. It is no more than 5 lines of code.
moneycracks wrote:Sorry for bugging you again vitek, but would it be possible to have more than two patterns? As is I can only do "*.jpg\0*.bmp\0", anything past the second \0 gets ignored by the function and I'm not exactly sure how to make it consider more.
I don't know what you're doing wrong, but the code I posted does not have this problem. If I had to guess, I'd say that the pattern string was cut short because you didn't initialize it in such a way that the embeded nulls are kept.

Travis

Posted: Tue Apr 01, 2008 12:23 am
by monkeycracks
I've written enough code for you, so I'm not going to write this. It is a simple loop with a counter. It is no more than 5 lines of code.
Didn't mean that part, sorry ;). I've got that mostly figured out.


As for the initializing it in a way that the null embeds aren't kept : I think c_str(); in the stringc and stringw does it. I'm not for sure but I'll look for a workaround and see if I can't find a way to avoid using them.

Posted: Tue Apr 01, 2008 12:40 am
by monkeycracks
Yes I found the problem!


const core::stringw s(L"file.png");
const core::stringc ss(s.c_str());

If I change stringw s to stringc s, the embeds aren't lost. I'll have to make the workarounds now. Thanks for the kickoff vitek.