aseprite/src/file_system.cpp
David Capello 7d1df59370 Win32: Show filename with extension instead of display name for files.
If the user has the "hide extensions for known file types" option
selected in Windows Explorer, the display name will not contain the
file extension, so it's better to use the filename (with extension)
directly.
2012-07-09 13:49:08 -03:00

1119 lines
28 KiB
C++

/* ASEPRITE
* Copyright (C) 2001-2012 David Capello
*
* 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-1307 USA
*/
/* Some of the original code to handle PIDLs come from the
MiniExplorer example of the Vaca library:
http://vaca.sourceforge.net/
*/
#include "config.h"
#include <cstdio>
#include <vector>
#include <map>
#include <algorithm>
#include <utility>
#include <allegro.h>
// define this macro to solve the problem of for_each_file Allegro
// routine which does not support to wrap 64-bits pointers in its
// user-data parameter
#define WORKAROUND_64BITS_SUPPORT
// in Windows we can use PIDLS
#if defined ALLEGRO_WINDOWS
// uncomment this if you don't want to use PIDLs in windows
#define USE_PIDLS
#endif
#if defined ALLEGRO_UNIX || defined ALLEGRO_MACOSX || defined ALLEGRO_DJGPP || defined ALLEGRO_MINGW32
#include <sys/stat.h>
#endif
#if defined ALLEGRO_UNIX || defined ALLEGRO_MACOSX || defined ALLEGRO_MINGW32
#include <sys/unistd.h>
#endif
#if defined USE_PIDLS
#include <winalleg.h>
#include <shlobj.h>
#include <shlwapi.h>
#endif
#include "base/path.h"
#include "file_system.h"
//////////////////////////////////////////////////////////////////////
#ifdef USE_PIDLS
// ..using Win32 Shell (PIDLs)
#define IS_FOLDER(fi) \
(((fi)->attrib & SFGAO_FOLDER) == SFGAO_FOLDER)
#define MYPC_CSLID "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}"
#else
// ..using Allegro (for_each_file)
#define IS_FOLDER(fi) \
(((fi)->attrib & FA_DIREC) == FA_DIREC)
#if (DEVICE_SEPARATOR != 0) && (DEVICE_SEPARATOR != '\0')
#define HAVE_DRIVES
#else
#define CASE_SENSITIVE
#endif
#ifndef FA_ALL
#define FA_ALL FA_RDONLY | FA_DIREC | FA_ARCH | FA_HIDDEN | FA_SYSTEM
#endif
#define FA_TO_SHOW FA_RDONLY | FA_DIREC | FA_ARCH | FA_SYSTEM
#endif
//////////////////////////////////////////////////////////////////////
#ifndef MAX_PATH
#define MAX_PATH 4096
#endif
#define NOTINITIALIZED "{__not_initialized_path__}"
// a position in the file-system
class FileItem : public IFileItem
{
public:
base::string keyname;
base::string filename;
base::string displayname;
FileItem* parent;
FileItemList children;
unsigned int version;
bool removed;
#ifdef USE_PIDLS
LPITEMIDLIST pidl; // relative to parent
LPITEMIDLIST fullpidl; // relative to the Desktop folder
// (like a full path-name, because the
// desktop is the root on Windows)
SFGAOF attrib;
#else
int attrib;
#endif
FileItem(FileItem* parent);
~FileItem();
void insertChildSorted(FileItem* child);
int compare(const FileItem& that) const;
bool operator<(const FileItem& that) const { return compare(that) < 0; }
bool operator>(const FileItem& that) const { return compare(that) > 0; }
bool operator==(const FileItem& that) const { return compare(that) == 0; }
bool operator!=(const FileItem& that) const { return compare(that) != 0; }
// IFileItem interface
bool isFolder() const;
bool isBrowsable() const;
base::string getKeyName() const;
base::string getFileName() const;
base::string getDisplayName() const;
IFileItem* getParent() const;
const FileItemList& getChildren();
bool hasExtension(const base::string& csv_extensions);
BITMAP* getThumbnail();
void setThumbnail(BITMAP* thumbnail);
};
typedef std::map<base::string, FileItem*> FileItemMap;
typedef std::map<base::string, BITMAP*> ThumbnailMap;
// the root of the file-system
static FileItem* rootitem = NULL;
static FileItemMap* fileitems_map;
static ThumbnailMap* thumbnail_map;
static unsigned int current_file_system_version = 0;
#ifdef USE_PIDLS
static IMalloc* shl_imalloc = NULL;
static IShellFolder* shl_idesktop = NULL;
#else
#ifdef WORKAROUND_64BITS_SUPPORT
static FileItem* for_each_child_callback_param;
#endif
#endif
/* a more easy PIDLs interface (without using the SH* & IL* routines of W2K) */
#ifdef USE_PIDLS
static void update_by_pidl(FileItem* fileitem);
static LPITEMIDLIST concat_pidl(LPITEMIDLIST pidlHead, LPITEMIDLIST pidlTail);
static UINT get_pidl_size(LPITEMIDLIST pidl);
static LPITEMIDLIST get_next_pidl(LPITEMIDLIST pidl);
static LPITEMIDLIST get_last_pidl(LPITEMIDLIST pidl);
static LPITEMIDLIST clone_pidl(LPITEMIDLIST pidl);
static LPITEMIDLIST remove_last_pidl(LPITEMIDLIST pidl);
static void free_pidl(LPITEMIDLIST pidl);
static base::string get_key_for_pidl(LPITEMIDLIST pidl);
static FileItem* get_fileitem_by_fullpidl(LPITEMIDLIST pidl, bool create_if_not);
static void put_fileitem(FileItem* fileitem);
#else
static FileItem* get_fileitem_by_path(const base::string& path, bool create_if_not);
static void for_each_child_callback(const char *filename, int attrib, int param);
static base::string remove_backslash_if_needed(const base::string& filename);
static base::string get_key_for_filename(const base::string& filename);
static void put_fileitem(FileItem* fileitem);
#endif
FileSystemModule* FileSystemModule::m_instance = NULL;
/**
* Initializes the file-system module to navigate the file-system.
*/
FileSystemModule::FileSystemModule()
{
ASSERT(m_instance == NULL);
m_instance = this;
fileitems_map = new FileItemMap;
thumbnail_map = new ThumbnailMap;
#ifdef USE_PIDLS
/* get the IMalloc interface */
SHGetMalloc(&shl_imalloc);
/* get desktop IShellFolder interface */
SHGetDesktopFolder(&shl_idesktop);
#endif
// first version of the file system
++current_file_system_version;
// get the root element of the file system (this will create
// the 'rootitem' FileItem)
getRootFileItem();
PRINTF("File system module installed\n");
}
/**
* Shutdowns the file-system module.
*/
FileSystemModule::~FileSystemModule()
{
PRINTF("File system module: uninstalling\n");
ASSERT(m_instance == this);
for (FileItemMap::iterator
it=fileitems_map->begin(); it!=fileitems_map->end(); ++it) {
delete it->second;
}
fileitems_map->clear();
for (ThumbnailMap::iterator
it=thumbnail_map->begin(); it!=thumbnail_map->end(); ++it) {
destroy_bitmap(it->second);
}
thumbnail_map->clear();
#ifdef USE_PIDLS
// relase desktop IShellFolder interface
shl_idesktop->Release();
// release IMalloc interface
shl_imalloc->Release();
shl_imalloc = NULL;
#endif
delete fileitems_map;
delete thumbnail_map;
PRINTF("File system module: uninstalled\n");
m_instance = NULL;
}
FileSystemModule* FileSystemModule::instance()
{
return m_instance;
}
/**
* Marks all FileItems as deprecated to be refresh the next time they
* are queried through @ref FileItem#getChildren.
*
* @see FileItem#getChildren
*/
void FileSystemModule::refresh()
{
++current_file_system_version;
}
IFileItem* FileSystemModule::getRootFileItem()
{
FileItem* fileitem;
if (rootitem)
return rootitem;
fileitem = new FileItem(NULL);
rootitem = fileitem;
//PRINTF("FS: Creating root fileitem %p\n", rootitem);
#ifdef USE_PIDLS
{
// get the desktop PIDL
LPITEMIDLIST pidl = NULL;
if (SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl) != S_OK) {
// TODO do something better
ASSERT(false);
exit(1);
}
fileitem->pidl = pidl;
fileitem->fullpidl = pidl;
fileitem->attrib = SFGAO_FOLDER;
shl_idesktop->GetAttributesOf(1, (LPCITEMIDLIST *)&pidl, &fileitem->attrib);
update_by_pidl(fileitem);
}
#else
{
const char* root;
#if defined HAVE_DRIVES
root = "C:\\";
#else
root = "/";
#endif
fileitem->filename = root;
fileitem->displayname = root;
fileitem->attrib = FA_DIREC;
}
#endif
// insert the file-item in the hash-table
put_fileitem(fileitem);
return fileitem;
}
/**
* Returns the FileItem through the specified @a path.
*
* @warning You have to call path.fix_separators() before.
*/
IFileItem* FileSystemModule::getFileItemFromPath(const base::string& path)
{
IFileItem* fileitem = NULL;
//PRINTF("FS: get_fileitem_from_path(%s)\n", path.c_str());
#ifdef USE_PIDLS
{
ULONG cbEaten;
WCHAR wStr[MAX_PATH];
LPITEMIDLIST fullpidl = NULL;
SFGAOF attrib = SFGAO_FOLDER;
if (path.empty()) {
fileitem = getRootFileItem();
//PRINTF("FS: > %p (root)\n", fileitem);
return fileitem;
}
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
path.c_str(), path.size()+1, wStr, MAX_PATH);
if (shl_idesktop->ParseDisplayName(NULL, NULL,
wStr, &cbEaten,
&fullpidl,
&attrib) != S_OK) {
//PRINTF("FS: > (null)\n");
return NULL;
}
fileitem = get_fileitem_by_fullpidl(fullpidl, true);
free_pidl(fullpidl);
}
#else
{
base::string buf = remove_backslash_if_needed(path);
fileitem = get_fileitem_by_path(buf, true);
}
#endif
//PRINTF("FS: get_fileitem_from_path(%s) -> %p\n", path.c_str(), fileitem);
return fileitem;
}
bool FileSystemModule::dirExists(const base::string& path)
{
struct al_ffblk info;
int ret;
base::string path2 = base::join_path(path, "*.*");
ret = al_findfirst(path2.c_str(), &info, FA_ALL);
al_findclose(&info);
return (ret == 0);
}
// ======================================================================
// FileItem class (IFileItem implementation)
// ======================================================================
bool FileItem::isFolder() const
{
return IS_FOLDER(this);
}
bool FileItem::isBrowsable() const
{
ASSERT(this->filename != NOTINITIALIZED);
#ifdef USE_PIDLS
return IS_FOLDER(this)
&& (base::get_file_extension(this->filename) != "zip")
&& ((!this->filename.empty() && (*this->filename.begin()) != ':') ||
(this->filename == MYPC_CSLID));
#else
return IS_FOLDER(this);
#endif
}
base::string FileItem::getKeyName() const
{
ASSERT(this->keyname != NOTINITIALIZED);
return this->keyname;
}
base::string FileItem::getFileName() const
{
ASSERT(this->filename != NOTINITIALIZED);
return this->filename;
}
base::string FileItem::getDisplayName() const
{
ASSERT(this->displayname != NOTINITIALIZED);
return this->displayname;
}
IFileItem* FileItem::getParent() const
{
if (this == rootitem)
return NULL;
else {
ASSERT(this->parent);
return this->parent;
}
}
const FileItemList& FileItem::getChildren()
{
// Is the file-item a folder?
if (IS_FOLDER(this) &&
// if the children list is empty, or the file-system version
// change (it's like to say: the current this->children list
// is outdated)...
(this->children.empty() ||
current_file_system_version > this->version)) {
FileItemList::iterator it;
FileItem* child;
// we have to mark current items as deprecated
for (it=this->children.begin();
it!=this->children.end(); ++it) {
child = static_cast<FileItem*>(*it);
child->removed = true;
}
//PRINTF("FS: Loading files for %p (%s)\n", fileitem, fileitem->displayname);
#ifdef USE_PIDLS
{
IShellFolder* pFolder = NULL;
if (this == rootitem)
pFolder = shl_idesktop;
else
shl_idesktop->BindToObject(this->fullpidl,
NULL,
IID_IShellFolder,
(LPVOID *)&pFolder);
if (pFolder != NULL) {
IEnumIDList *pEnum = NULL;
ULONG c, fetched;
/* get the interface to enumerate subitems */
pFolder->EnumObjects(win_get_window(),
SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &pEnum);
if (pEnum != NULL) {
LPITEMIDLIST itempidl[256];
SFGAOF attribs[256];
/* enumerate the items in the folder */
while (pEnum->Next(256, itempidl, &fetched) == S_OK && fetched > 0) {
/* request the SFGAO_FOLDER attribute to know what of the
item is a folder */
for (c=0; c<fetched; ++c) {
attribs[c] = SFGAO_FOLDER;
pFolder->GetAttributesOf(1, (LPCITEMIDLIST *)itempidl, attribs+c);
}
/* generate the FileItems */
for (c=0; c<fetched; ++c) {
LPITEMIDLIST fullpidl = concat_pidl(this->fullpidl,
itempidl[c]);
child = get_fileitem_by_fullpidl(fullpidl, false);
if (!child) {
child = new FileItem(this);
child->pidl = itempidl[c];
child->fullpidl = fullpidl;
child->attrib = attribs[c];
update_by_pidl(child);
put_fileitem(child);
}
else {
ASSERT(child->parent == this);
free_pidl(fullpidl);
free_pidl(itempidl[c]);
}
this->insertChildSorted(child);
}
}
pEnum->Release();
}
if (pFolder != shl_idesktop)
pFolder->Release();
}
}
#else
{
char buf[MAX_PATH], path[MAX_PATH], tmp[32];
ustrcpy(path, this->filename.c_str());
put_backslash(path);
replace_filename(buf,
path,
uconvert_ascii("*.*", tmp),
sizeof(buf));
#ifdef WORKAROUND_64BITS_SUPPORT
// we cannot use the for_each_file's 'param' to wrap a 64-bits pointer
for_each_child_callback_param = this;
for_each_file(buf, FA_TO_SHOW, for_each_child_callback, 0);
#else
for_each_file(buf, FA_TO_SHOW,
for_each_child_callback,
(int)this);
#endif
}
#endif
// check old file-items (maybe removed directories or file-items)
for (it=this->children.begin();
it!=this->children.end(); ) {
child = static_cast<FileItem*>(*it);
if (child->removed) {
it = this->children.erase(it);
fileitems_map->erase(fileitems_map->find(child->keyname));
delete child;
}
else
++it;
}
// now this file-item is updated
this->version = current_file_system_version;
}
return this->children;
}
bool FileItem::hasExtension(const base::string& csv_extensions)
{
ASSERT(this->filename != NOTINITIALIZED);
return base::has_file_extension(this->filename, csv_extensions);
}
BITMAP* FileItem::getThumbnail()
{
ThumbnailMap::iterator it = thumbnail_map->find(this->filename);
if (it != thumbnail_map->end())
return it->second;
else
return NULL;
}
void FileItem::setThumbnail(BITMAP* thumbnail)
{
// destroy the current thumbnail of the file (if exists)
ThumbnailMap::iterator it = thumbnail_map->find(this->filename);
if (it != thumbnail_map->end()) {
destroy_bitmap(it->second);
thumbnail_map->erase(it);
}
// insert the new one in the map
thumbnail_map->insert(std::make_pair(this->filename, thumbnail));
}
FileItem::FileItem(FileItem* parent)
{
//PRINTF("FS: Creating %p fileitem with parent %p\n", this, parent);
this->keyname = NOTINITIALIZED;
this->filename = NOTINITIALIZED;
this->displayname = NOTINITIALIZED;
this->parent = parent;
this->version = current_file_system_version;
this->removed = false;
#ifdef USE_PIDLS
this->pidl = NULL;
this->fullpidl = NULL;
this->attrib = 0;
#else
this->attrib = 0;
#endif
}
FileItem::~FileItem()
{
PRINTF("FS: Destroying FileItem() with parent %p\n", parent);
#ifdef USE_PIDLS
if (this->fullpidl && this->fullpidl != this->pidl) {
free_pidl(this->fullpidl);
this->fullpidl = NULL;
}
if (this->pidl) {
free_pidl(this->pidl);
this->pidl = NULL;
}
#endif
}
void FileItem::insertChildSorted(FileItem* child)
{
// this file-item wasn't removed from the last lookup
child->removed = false;
// if the fileitem is already in the list we can go back
if (std::find(children.begin(), children.end(), child) != children.end())
return;
bool inserted = false;
for (FileItemList::iterator
it=children.begin(); it!=children.end(); ++it) {
if (*static_cast<FileItem*>(*it) > *child) {
children.insert(it, child);
inserted = true;
break;
}
}
if (!inserted)
children.push_back(child);
}
/**
* Compares two FileItems.
*
* Based on 'ustricmp' of Allegro. It makes sure that eg "foo.bar"
* comes before "foo-1.bar", and also that "foo9.bar" comes before
* "foo10.bar".
*/
int FileItem::compare(const FileItem& that) const
{
if (IS_FOLDER(this)) {
if (!IS_FOLDER(&that))
return -1;
}
else if (IS_FOLDER(&that))
return 1;
{
int c1, c2;
int x1, x2;
char *t1, *t2;
const char* s1 = this->displayname.c_str();
const char* s2 = that.displayname.c_str();
for (;;) {
c1 = utolower(ugetxc(&s1));
c2 = utolower(ugetxc(&s2));
if ((c1 >= '0') && (c1 <= '9') && (c2 >= '0') && (c2 <= '9')) {
x1 = ustrtol(s1 - ucwidth(c1), &t1, 10);
x2 = ustrtol(s2 - ucwidth(c2), &t2, 10);
if (x1 != x2)
return x1 - x2;
else if (t1 - s1 != t2 - s2)
return (t2 - s2) - (t1 - s1);
s1 = t1;
s2 = t2;
}
else if (c1 != c2) {
if (!c1)
return -1;
else if (!c2)
return 1;
else if (c1 == '.')
return -1;
else if (c2 == '.')
return 1;
return c1 - c2;
}
if (!c1)
return 0;
}
}
return -1;
}
//////////////////////////////////////////////////////////////////////
// PIDLS: Only for Win32
//////////////////////////////////////////////////////////////////////
#ifdef USE_PIDLS
/* updates the names of the file-item through its PIDL */
static void update_by_pidl(FileItem* fileitem)
{
STRRET strret;
TCHAR pszName[MAX_PATH];
IShellFolder *pFolder = NULL;
if (fileitem == rootitem)
pFolder = shl_idesktop;
else {
ASSERT(fileitem->parent);
shl_idesktop->BindToObject(fileitem->parent->fullpidl,
NULL,
IID_IShellFolder,
(LPVOID *)&pFolder);
}
/****************************************/
/* get the file name */
if (pFolder != NULL &&
pFolder->GetDisplayNameOf(fileitem->pidl,
SHGDN_NORMAL | SHGDN_FORPARSING,
&strret) == S_OK) {
StrRetToBuf(&strret, fileitem->pidl, pszName, MAX_PATH);
fileitem->filename = pszName;
}
else if (shl_idesktop->GetDisplayNameOf(fileitem->fullpidl,
SHGDN_NORMAL | SHGDN_FORPARSING,
&strret) == S_OK) {
StrRetToBuf(&strret, fileitem->fullpidl, pszName, MAX_PATH);
fileitem->filename = pszName;
}
else
fileitem->filename = "ERR";
/****************************************/
/* get the name to display */
if (fileitem->isFolder() &&
pFolder &&
pFolder->GetDisplayNameOf(fileitem->pidl,
SHGDN_INFOLDER,
&strret) == S_OK) {
StrRetToBuf(&strret, fileitem->pidl, pszName, MAX_PATH);
fileitem->displayname = pszName;
}
else if (fileitem->isFolder() &&
shl_idesktop->GetDisplayNameOf(fileitem->fullpidl,
SHGDN_INFOLDER,
&strret) == S_OK) {
StrRetToBuf(&strret, fileitem->fullpidl, pszName, MAX_PATH);
fileitem->displayname = pszName;
}
else {
fileitem->displayname = base::get_file_name(fileitem->filename);
}
if (pFolder != NULL && pFolder != shl_idesktop) {
pFolder->Release();
}
}
static LPITEMIDLIST concat_pidl(LPITEMIDLIST pidlHead, LPITEMIDLIST pidlTail)
{
LPITEMIDLIST pidlNew;
UINT cb1, cb2;
ASSERT(pidlHead);
ASSERT(pidlTail);
cb1 = get_pidl_size(pidlHead) - sizeof(pidlHead->mkid.cb);
cb2 = get_pidl_size(pidlTail);
pidlNew = (LPITEMIDLIST)shl_imalloc->Alloc(cb1 + cb2);
if (pidlNew) {
CopyMemory(pidlNew, pidlHead, cb1);
CopyMemory(((LPSTR)pidlNew) + cb1, pidlTail, cb2);
}
return pidlNew;
}
static UINT get_pidl_size(LPITEMIDLIST pidl)
{
UINT cbTotal = 0;
if (pidl) {
cbTotal += sizeof(pidl->mkid.cb); /* null terminator */
while (pidl) {
cbTotal += pidl->mkid.cb;
pidl = get_next_pidl(pidl);
}
}
return cbTotal;
}
static LPITEMIDLIST get_next_pidl(LPITEMIDLIST pidl)
{
if (pidl != NULL && pidl->mkid.cb > 0) {
pidl = (LPITEMIDLIST)(((LPBYTE)(pidl)) + pidl->mkid.cb);
if (pidl->mkid.cb > 0)
return pidl;
}
return NULL;
}
static LPITEMIDLIST get_last_pidl(LPITEMIDLIST pidl)
{
LPITEMIDLIST pidlLast = pidl;
LPITEMIDLIST pidlNew = NULL;
while (pidl) {
pidlLast = pidl;
pidl = get_next_pidl(pidl);
}
if (pidlLast) {
ULONG sz = get_pidl_size(pidlLast);
pidlNew = (LPITEMIDLIST)shl_imalloc->Alloc(sz);
CopyMemory(pidlNew, pidlLast, sz);
}
return pidlNew;
}
static LPITEMIDLIST clone_pidl(LPITEMIDLIST pidl)
{
ULONG sz = get_pidl_size(pidl);
LPITEMIDLIST pidlNew = (LPITEMIDLIST)shl_imalloc->Alloc(sz);
CopyMemory(pidlNew, pidl, sz);
return pidlNew;
}
static LPITEMIDLIST remove_last_pidl(LPITEMIDLIST pidl)
{
LPITEMIDLIST pidlFirst = pidl;
LPITEMIDLIST pidlLast = pidl;
while (pidl) {
pidlLast = pidl;
pidl = get_next_pidl(pidl);
}
if (pidlLast)
pidlLast->mkid.cb = 0;
return pidlFirst;
}
static void free_pidl(LPITEMIDLIST pidl)
{
shl_imalloc->Free(pidl);
}
static base::string get_key_for_pidl(LPITEMIDLIST pidl)
{
#if 0
char *key = base_malloc(get_pidl_size(pidl)+1);
UINT c, i = 0;
while (pidl) {
for (c=0; c<pidl->mkid.cb; ++c) {
if (pidl->mkid.abID[c])
key[i++] = pidl->mkid.abID[c];
else
key[i++] = 1;
}
pidl = get_next_pidl(pidl);
}
key[i] = 0;
return key;
#else
STRRET strret;
TCHAR pszName[MAX_PATH];
char key[4096];
int len;
ustrcpy(key, empty_string);
// Go pidl by pidl from the fullpidl to the root (desktop)
//PRINTF("FS: ***\n");
pidl = clone_pidl(pidl);
while (pidl->mkid.cb > 0) {
if (shl_idesktop->GetDisplayNameOf(pidl,
SHGDN_INFOLDER | SHGDN_FORPARSING,
&strret) == S_OK) {
StrRetToBuf(&strret, pidl, pszName, MAX_PATH);
//PRINTF("FS: + %s\n", pszName);
len = ustrlen(pszName);
if (len > 0) {
if (*key) {
if (pszName[len-1] != '\\') {
memmove(key+len+1, key, ustrlen(key)+1);
key[len] = '\\';
}
else
memmove(key+len, key, ustrlen(key)+1);
}
else
key[len] = 0;
memcpy(key, pszName, len);
}
}
remove_last_pidl(pidl);
}
free_pidl(pidl);
//PRINTF("FS: =%s\n***\n", key);
return key;
#endif
}
static FileItem* get_fileitem_by_fullpidl(LPITEMIDLIST fullpidl, bool create_if_not)
{
FileItemMap::iterator it = fileitems_map->find(get_key_for_pidl(fullpidl));
if (it != fileitems_map->end())
return it->second;
if (!create_if_not)
return NULL;
// new file-item
FileItem* fileitem = new FileItem(NULL);
fileitem->fullpidl = clone_pidl(fullpidl);
fileitem->attrib = SFGAO_FOLDER;
HRESULT hr = shl_idesktop->GetAttributesOf(1, (LPCITEMIDLIST *)&fileitem->fullpidl,
&fileitem->attrib);
if (hr == S_OK) {
LPITEMIDLIST parent_fullpidl = clone_pidl(fileitem->fullpidl);
remove_last_pidl(parent_fullpidl);
fileitem->pidl = get_last_pidl(fileitem->fullpidl);
fileitem->parent = get_fileitem_by_fullpidl(parent_fullpidl, true);
free_pidl(parent_fullpidl);
}
update_by_pidl(fileitem);
put_fileitem(fileitem);
//PRINTF("FS: fileitem %p created %s with parent %p\n", fileitem, fileitem->keyname.c_str(), fileitem->parent);
return fileitem;
}
/**
* Inserts the @a fileitem in the hash map of items.
*/
static void put_fileitem(FileItem* fileitem)
{
ASSERT(fileitem->filename != NOTINITIALIZED);
ASSERT(fileitem->keyname == NOTINITIALIZED);
fileitem->keyname = get_key_for_pidl(fileitem->fullpidl);
ASSERT(fileitem->keyname != NOTINITIALIZED);
#ifdef DEBUGMODE
FileItemMap::iterator it = fileitems_map->find(get_key_for_pidl(fileitem->fullpidl));
ASSERT(it == fileitems_map->end());
#endif
// insert this file-item in the hash-table
fileitems_map->insert(std::make_pair(fileitem->keyname, fileitem));
}
#else
//////////////////////////////////////////////////////////////////////
// Allegro for_each_file: Portable
//////////////////////////////////////////////////////////////////////
static FileItem* get_fileitem_by_path(const base::string& path, bool create_if_not)
{
if (path.empty())
return rootitem;
FileItemMap::iterator it = fileitems_map->find(get_key_for_filename(path));
if (it != fileitems_map->end())
return it->second;
if (!create_if_not)
return NULL;
// get the attributes of the file
int attrib = 0;
if (!file_exists(path.c_str(), FA_ALL, &attrib)) {
if (!FileSystemModule::instance()->dirExists(path))
return NULL;
attrib = FA_DIREC;
}
// new file-item
FileItem* fileitem = new FileItem(NULL);
fileitem->filename = path;
fileitem->displayname = base::get_file_name(path);
fileitem->attrib = attrib;
// get the parent
{
base::string parent_path = remove_backslash_if_needed(base::join_path(base::get_file_path(path), ""));
fileitem->parent = get_fileitem_by_path(parent_path, true);
}
put_fileitem(fileitem);
return fileitem;
}
static void for_each_child_callback(const char *filename, int attrib, int param)
{
#ifdef WORKAROUND_64BITS_SUPPORT
FileItem* fileitem = for_each_child_callback_param;
#else
FileItem* fileitem = (FileItem*)param;
#endif
FileItem* child;
const char *filename_without_path = get_filename(filename);
if (*filename_without_path == '.' &&
(ustrcmp(filename_without_path, ".") == 0 ||
ustrcmp(filename_without_path, "..") == 0))
return;
child = get_fileitem_by_path(filename, false);
if (!child) {
ASSERT(fileitem != NULL);
child = new FileItem(fileitem);
child->filename = filename;
child->displayname = filename_without_path;
child->attrib = attrib;
put_fileitem(child);
}
else {
ASSERT(child->parent == fileitem);
}
fileitem->insertChildSorted(child);
}
static base::string remove_backslash_if_needed(const base::string& filename)
{
if (!filename.empty() && base::is_path_separator(*(filename.end()-1))) {
int len = filename.size();
#ifdef HAVE_DRIVES
// if the name is C:\ or something like that, the backslash isn't
// removed
if (len == 3 && filename[1] == ':')
return filename;
#else
// this is just the root '/' slash
if (len == 1)
return filename;
#endif
return base::remove_path_separator(filename);
}
return filename;
}
static base::string get_key_for_filename(const base::string& filename)
{
base::string buf(filename);
#if !defined CASE_SENSITIVE
buf.tolower();
#endif
buf = base::fix_path_separators(buf);
return buf;
}
static void put_fileitem(FileItem* fileitem)
{
ASSERT(fileitem->filename != NOTINITIALIZED);
ASSERT(fileitem->keyname == NOTINITIALIZED);
fileitem->keyname = get_key_for_filename(fileitem->filename);
ASSERT(fileitem->keyname != NOTINITIALIZED);
// insert this file-item in the hash-table
fileitems_map->insert(std::make_pair(fileitem->keyname, fileitem));
}
#endif