mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 14:16:07 -06:00
This might bring some incompatibilities in the code that expects wchar to be 16 bit, which escpecially involves dealing with Win32 (and to lesser extend MacOS) APIs, so if your application is doing that, please check all instances of WCHAR (UniChar on MacOS) or even wchar especially type casts. To support host APIs, char16 is introduced (but there is no 16-bit String varian). Use ToSystemCharsetW, FromSystemCharsetW to convert texts to Win32 API. - Support of drawing non-BMP characters in GUI - Vastly improved character font replacement code (when drawing characters missing with requested font, replacement font is used) - Last instances of Win32 ANSI calls (those ending with A) are removed - UTF handling routines are refactored and their's naming is unified - RTF is now being able to handle non-BMP characters (RTF is used as clipboard format for RichText) Other minor changes: - fixed TryRealloc issue - improved MemoryCheck - Removed MemoryAlloc48/MemoryFree48 - In theide Background parsing should less often cause delays in the main thread
2407 lines
55 KiB
C++
2407 lines
55 KiB
C++
#include <CtrlLib/CtrlLib.h>
|
|
|
|
namespace Upp {
|
|
|
|
INITBLOCK {
|
|
RegisterGlobalConfig("GlobalFileSelectorLRU");
|
|
}
|
|
|
|
#ifdef GUI_WIN
|
|
void AvoidPaintingCheck__();
|
|
|
|
Image ProcessSHIcon(HICON hIcon)
|
|
{
|
|
AvoidPaintingCheck__();
|
|
Color c = White();
|
|
Image m[2];
|
|
for(int i = 0; i < 2; i++) {
|
|
ICONINFO iconinfo;
|
|
if(!hIcon || !GetIconInfo(hIcon, &iconinfo))
|
|
return Image();
|
|
BITMAP bm;
|
|
::GetObject((HGDIOBJ)iconinfo.hbmMask, sizeof(BITMAP), (LPVOID)&bm);
|
|
Size sz(bm.bmWidth, bm.bmHeight);
|
|
ImageDraw iw(sz);
|
|
iw.DrawRect(sz, c);
|
|
::DrawIconEx(iw.GetHandle(), 0, 0, hIcon, 0, 0, 0, NULL, DI_NORMAL|DI_COMPAT);
|
|
::DeleteObject(iconinfo.hbmColor);
|
|
::DeleteObject(iconinfo.hbmMask);
|
|
c = Black();
|
|
m[i] = iw;
|
|
}
|
|
::DestroyIcon(hIcon);
|
|
return RecreateAlpha(m[0], m[1]);
|
|
}
|
|
|
|
struct FileIconMaker : ImageMaker {
|
|
String file;
|
|
bool exe;
|
|
bool dir;
|
|
bool large;
|
|
|
|
virtual String Key() const {
|
|
return file + (exe ? "1" : "0") + (dir ? "1" : "0");
|
|
}
|
|
|
|
virtual Image Make() const {
|
|
SHFILEINFOW info;
|
|
AvoidPaintingCheck__();
|
|
SHGetFileInfoW(ToSystemCharsetW(file), dir ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL,
|
|
&info, sizeof(info),
|
|
SHGFI_ICON|(large ? SHGFI_LARGEICON : SHGFI_SMALLICON)|(exe ? 0 : SHGFI_USEFILEATTRIBUTES));
|
|
return ProcessSHIcon(info.hIcon);
|
|
}
|
|
};
|
|
|
|
|
|
Image GetFileIcon(const char *path, bool dir, bool force, bool large, bool quick = false)
|
|
{
|
|
FileIconMaker m;
|
|
String ext = GetFileExt(path);
|
|
m.exe = false;
|
|
m.dir = false;
|
|
m.file = path;
|
|
m.large = large;
|
|
if(force)
|
|
m.exe = true;
|
|
else
|
|
if(dir) {
|
|
m.dir = true;
|
|
m.exe = true;
|
|
}
|
|
else
|
|
if(findarg(ext, ".exe", ".lnk") >= 0)
|
|
m.exe = true;
|
|
else
|
|
m.file = "x" + ext;
|
|
if(quick) {
|
|
m.exe = false;
|
|
m.file = "x" + ext;
|
|
}
|
|
return MakeImage(m);
|
|
}
|
|
|
|
#define GETFILEICON_DEFINED
|
|
|
|
#endif
|
|
|
|
#if defined(GUI_GTK)
|
|
|
|
Image GtkThemeIcon(const char *name, int sz);
|
|
|
|
Image GnomeImage(const char *s, bool large = false)
|
|
{
|
|
return GtkThemeIcon(String("gnome-") + s, DPI(large ? 48 : 16));
|
|
}
|
|
|
|
Image SystemImage(const char *s, bool large = false)
|
|
{
|
|
return GtkThemeIcon(s, DPI(large ? 48 : 16));
|
|
}
|
|
|
|
struct ExtToMime {
|
|
Index<String> major;
|
|
Index<String> minor;
|
|
VectorMap<String, dword> map;
|
|
|
|
void Load(const char *file);
|
|
void Refresh();
|
|
bool GetMime(const String& ext, String& maj, String& min);
|
|
};
|
|
|
|
void ExtToMime::Load(const char *fn)
|
|
{
|
|
FileIn in(fn);
|
|
if(in)
|
|
while(!in.IsEof()) {
|
|
String ln = TrimLeft(TrimRight(in.GetLine()));
|
|
if(ln[0] != '#') {
|
|
int q = ln.Find(':');
|
|
if(q >= 0) {
|
|
String h = ln.Mid(0, q);
|
|
int w = h.Find('/');
|
|
if(w >= 0) {
|
|
int x = ln.Find("*.", q);
|
|
if(x >= 0) {
|
|
String ext = ln.Mid(x + 2);
|
|
if(ext.GetCount() && map.Find(ext) < 0)
|
|
map.Add(ext, MAKELONG(minor.FindAdd(h.Mid(w + 1)), major.FindAdd(h.Mid(0, w))));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ExtToMime::Refresh()
|
|
{
|
|
major.Clear();
|
|
minor.Clear();
|
|
map.Clear();
|
|
Load("/usr/local/share/mime/globs");
|
|
Load("/usr/share/mime/globs");
|
|
}
|
|
|
|
bool ExtToMime::GetMime(const String& ext, String& maj, String& min)
|
|
{
|
|
ONCELOCK {
|
|
Refresh();
|
|
}
|
|
int q = map.Find(ext);
|
|
if(q < 0)
|
|
return false;
|
|
dword x = map[q];
|
|
maj = major[HIWORD(x)];
|
|
min = minor[LOWORD(x)];
|
|
return true;
|
|
}
|
|
|
|
struct FileExtMaker : ImageMaker {
|
|
String ext;
|
|
bool large;
|
|
|
|
virtual String Key() const {
|
|
return ext;
|
|
}
|
|
|
|
virtual Image Make() const {
|
|
String major;
|
|
String minor;
|
|
if(!Single<ExtToMime>().GetMime(ext, major, minor))
|
|
return Null;
|
|
Image img = SystemImage(major + '-' + minor, large);
|
|
return IsNull(img) ? SystemImage(major) : img;
|
|
}
|
|
};
|
|
|
|
Image PosixGetDriveImage(String dir, bool large)
|
|
{
|
|
static bool init = false;
|
|
static Image cdrom;
|
|
static Image lcdrom;
|
|
static Image harddisk;
|
|
static Image lharddisk;
|
|
static Image floppy;
|
|
static Image lfloppy;
|
|
static Image computer;
|
|
static Image lcomputer;
|
|
|
|
if (!init) {
|
|
bool KDE = Environment().Get("KDE_FULL_SESSION", String()) == "true";
|
|
if (KDE) {
|
|
cdrom = SystemImage("media-optical");
|
|
lcdrom = SystemImage("media-optical", true);
|
|
harddisk = SystemImage("drive-harddisk");
|
|
lharddisk = SystemImage("drive-harddisk", true);
|
|
floppy = SystemImage("media-floppy");
|
|
lfloppy = SystemImage("media-floppy", true);
|
|
computer = SystemImage("computer");
|
|
lcomputer = SystemImage("computer", true);
|
|
}
|
|
else {
|
|
cdrom = GnomeImage("dev-cdrom");
|
|
lcdrom = GnomeImage("dev-cdrom", true);
|
|
harddisk = GnomeImage("dev-harddisk");
|
|
lharddisk = GnomeImage("dev-harddisk", true);
|
|
floppy = GnomeImage("dev-floppy");
|
|
lfloppy = GnomeImage("dev-floppy", true);
|
|
computer = GnomeImage("dev-computer");
|
|
lcomputer = GnomeImage("dev-computer", true);
|
|
}
|
|
|
|
init = true;
|
|
}
|
|
if(dir.GetCount() == 0 || dir == "/") {
|
|
Image m = large ? lcomputer : computer;
|
|
return IsNull(m) ? CtrlImg::Computer() : m;
|
|
}
|
|
if(dir.Find("cdrom") == 0 || dir.Find("cdrecorder") == 0) {
|
|
Image m = large ? lcdrom : cdrom;
|
|
return IsNull(m) ? CtrlImg::CdRom() : m;
|
|
}
|
|
if(dir.Find("floppy") == 0 || dir.Find("zip") == 0) {
|
|
Image m = large ? lfloppy : floppy;
|
|
return IsNull(m) ? CtrlImg::Diskette() : m;
|
|
}
|
|
Image m = large ? lharddisk : harddisk;
|
|
return IsNull(m) ? CtrlImg::Hd() : m;
|
|
}
|
|
|
|
Image GetFileIcon(const String& folder, const String& filename, bool isdir, bool isexe, bool large)
|
|
{
|
|
static bool init = false;
|
|
static bool KDE = Environment().Get("KDE_FULL_SESSION", String()) == "true";
|
|
|
|
static Image file;
|
|
static Image lfile;
|
|
static Image dir;
|
|
static Image ldir;
|
|
static Image exe;
|
|
static Image lexe;
|
|
static Image home;
|
|
static Image lhome;
|
|
static Image desktop;
|
|
static Image ldesktop;
|
|
static Image music;
|
|
static Image lmusic;
|
|
static Image pictures;
|
|
static Image lpictures;
|
|
static Image video;
|
|
static Image lvideo;
|
|
static Image documents;
|
|
static Image ldocuments;
|
|
static Image download;
|
|
static Image ldownload;
|
|
static Image help;
|
|
static Image lhelp;
|
|
static Image translation;
|
|
static Image ltranslation;
|
|
static Image layout;
|
|
static Image llayout;
|
|
|
|
static Image fileImage;
|
|
static Image fileMusic = SystemImage("audio-x-generic");
|
|
static Image fileScript = SystemImage("text-x-script");
|
|
|
|
if (!init) {
|
|
if (KDE) {
|
|
file = SystemImage("text-plain");
|
|
lfile = SystemImage("text-plain", true);
|
|
dir = SystemImage("folder");
|
|
ldir = SystemImage("folder", true);
|
|
exe = SystemImage("application-x-executable");
|
|
lexe = SystemImage("application-x-executable", true);
|
|
home = SystemImage("user-home");
|
|
lhome = SystemImage("user-home", true);
|
|
desktop = SystemImage("user-desktop");
|
|
ldesktop = SystemImage("user-desktop", true);
|
|
music = SystemImage("folder-sound");
|
|
lmusic = SystemImage("folder-sound", true);
|
|
pictures = SystemImage("folder-image");
|
|
lpictures = SystemImage("folder-image", true);
|
|
video = SystemImage("folder-video");
|
|
lvideo = SystemImage("folder-video", true);
|
|
documents = SystemImage("folder-documents");
|
|
ldocuments = SystemImage("folder-documents", true);
|
|
download = SystemImage("folder-downloads");
|
|
ldownload = SystemImage("folder-downloads", true);
|
|
help = SystemImage("help-contents");
|
|
lhelp = SystemImage("help-contents", true);
|
|
translation = SystemImage("applications-education-language");
|
|
ltranslation = SystemImage("applications-education-language", true);
|
|
layout = SystemImage("applications-development");
|
|
llayout = SystemImage("applications-development", true);
|
|
|
|
fileImage = SystemImage("application-x-egon");
|
|
}
|
|
else {
|
|
file = GnomeImage("fs-regular");
|
|
lfile = GnomeImage("fs-regular", true);
|
|
dir = GnomeImage("fs-directory");
|
|
ldir = GnomeImage("fs-directory", true);
|
|
exe = GnomeImage("fs-executable");
|
|
lexe = GnomeImage("fs-executable", true);
|
|
home = GnomeImage("fs-home");
|
|
lhome = GnomeImage("fs-home", true);
|
|
desktop = GnomeImage("fs-desktop");
|
|
ldesktop = GnomeImage("fs-desktop", true);
|
|
music = SystemImage("folder-music");
|
|
lmusic = SystemImage("folder-music", true);
|
|
pictures = SystemImage("folder-pictures");
|
|
lpictures = SystemImage("folder-pictures", true);
|
|
video = SystemImage("folder-video");
|
|
lvideo = SystemImage("folder-video", true);
|
|
documents = SystemImage("folder-documents");
|
|
ldocuments = SystemImage("folder-documents", true);
|
|
download = SystemImage("folder-downloads");
|
|
ldownload = SystemImage("folder-downloads", true);
|
|
help = SystemImage("help");
|
|
lhelp = SystemImage("help", true);
|
|
translation = SystemImage("preferences-desktop-locale");
|
|
ltranslation = SystemImage("preferences-desktop-locale", true);
|
|
layout = SystemImage("applications-development");
|
|
llayout = SystemImage("applications-development", true);
|
|
|
|
fileImage = SystemImage("image-x-generic");
|
|
}
|
|
|
|
init = true;
|
|
}
|
|
if (filename == "Help Topics")
|
|
return large ? lhelp : help;
|
|
if(isdir) {
|
|
Image img = dir;
|
|
if(AppendFileName(folder, filename) == GetHomeDirectory())
|
|
return large ? lhome : home;
|
|
else
|
|
if(AppendFileName(folder, filename) == GetDesktopFolder ())
|
|
return large ? ldesktop : desktop;
|
|
else
|
|
if(AppendFileName(folder, filename) == GetMusicFolder ())
|
|
return large ? lmusic : music;
|
|
else
|
|
if(AppendFileName(folder, filename) == GetPicturesFolder())
|
|
return large ? lpictures : pictures;
|
|
else
|
|
if(AppendFileName(folder, filename) == GetVideoFolder())
|
|
return large ? lvideo : video;
|
|
else
|
|
if(AppendFileName(folder, filename) == GetDocumentsFolder())
|
|
return large ? ldocuments : documents;
|
|
else
|
|
if(AppendFileName(folder, filename) == GetDownloadFolder())
|
|
return large ? ldownload : download;
|
|
else
|
|
if(folder == "/media" || filename.GetCount() == 0)
|
|
return PosixGetDriveImage(filename, large);
|
|
return dir;
|
|
}
|
|
FileExtMaker m;
|
|
m.ext = GetFileExt(filename);
|
|
for (int i = 1; i < m.ext.GetCount(); ++i)
|
|
m.ext.Set (i, ToLower(m.ext[i]));
|
|
|
|
// Fixing format problems
|
|
if (m.ext == ".gz") m.ext = ".tar.gz";
|
|
|
|
// Ultimate++ - files extensions
|
|
if (m.ext == ".t" || m.ext == ".lng") return large ? ltranslation : translation;
|
|
if (m.ext == ".lay") return large ? llayout : layout;
|
|
if (m.ext == ".iml") return fileImage;
|
|
if (m.ext == ".usc") return fileScript;
|
|
|
|
// Binary - files extensions (It seems that KDE has problem with multimedia MIME icons handling)
|
|
if (KDE) {
|
|
if (m.ext == ".bmp" || m.ext == ".dib" ||
|
|
m.ext == ".gif" ||
|
|
m.ext == ".jpg" || m.ext == ".jpeg" || m.ext == ".jpe" ||
|
|
m.ext == ".png" ||
|
|
m.ext == ".tif" || m.ext == ".tiff" ||
|
|
m.ext == ".svg" ||
|
|
m.ext == ".ico" ||
|
|
m.ext == ".xcf")
|
|
return fileImage;
|
|
if (m.ext == ".aac" || m.ext == ".ogg" || m.ext == ".mp3") return fileMusic;
|
|
}
|
|
|
|
Image img;
|
|
if(m.ext.GetCount()) {
|
|
m.ext = m.ext.Mid(1);
|
|
m.large = large;
|
|
img = MakeImage(m);
|
|
isexe = false;
|
|
}
|
|
return IsNull(img) ? isexe ? (large ? lexe : exe) : (large ? lfile : file) : img;
|
|
}
|
|
|
|
#define GETFILEICON_DEFINED
|
|
|
|
#endif
|
|
|
|
#ifdef GUI_COCOA
|
|
struct FileIconMaker : ImageMaker {
|
|
String file;
|
|
bool exe;
|
|
bool dir;
|
|
bool large;
|
|
|
|
virtual String Key() const {
|
|
return file + (exe ? "1" : "0") + (dir ? "1" : "0");
|
|
}
|
|
|
|
virtual Image Make() const {
|
|
return GetIconForFile(file);
|
|
}
|
|
};
|
|
|
|
#define GETFILEICON_DEFINED
|
|
|
|
Image GetFileIcon(const char *path, bool dir, bool exe, bool large, bool quick = false)
|
|
{
|
|
FileIconMaker m;
|
|
m.exe = exe;
|
|
m.dir = dir;
|
|
m.file = path;
|
|
m.large = large;
|
|
return MakeImage(m);
|
|
}
|
|
#endif
|
|
|
|
#ifndef GETFILEICON_DEFINED
|
|
Image PosixGetDriveImage(String dir, bool)
|
|
{
|
|
if(dir.GetCount() == 0 || dir == "/")
|
|
return CtrlImg::Computer();
|
|
if(dir.Find("cdrom") == 0 || dir.Find("cdrecorder") == 0)
|
|
return CtrlImg::CdRom();
|
|
if(dir.Find("floppy") == 0 || dir.Find("zip") == 0)
|
|
return CtrlImg::Diskette();
|
|
return CtrlImg::Hd();
|
|
}
|
|
|
|
#ifdef PLATFORM_WIN32
|
|
Image GetFileIcon(const String& folder, bool, bool, bool, bool = false)
|
|
{
|
|
return CtrlImg::File();
|
|
}
|
|
#else
|
|
Image GetFileIcon(const String& folder, const String& filename, bool isdir, bool isexe, bool = false)
|
|
{
|
|
return isdir ? CtrlImg::Dir() : CtrlImg::File();
|
|
}
|
|
|
|
#endif
|
|
#endif
|
|
|
|
Image NativePathIcon0(const char *path, bool folder, bool large)
|
|
{
|
|
#if defined(PLATFORM_WIN32)
|
|
if (folder)
|
|
return GetFileIcon(path, true, true, large);
|
|
else
|
|
return GetFileIcon(path, false, false, large);
|
|
#endif
|
|
#ifdef PLATFORM_POSIX
|
|
String p = path;
|
|
FindFile ff(path);
|
|
#ifdef GUI_COCOA
|
|
return GetFileIcon(path, folder, ff.GetMode() & 0111, large);
|
|
#else
|
|
bool isdrive = folder && ((p == "/media") || (p == "/mnt"));
|
|
return isdrive ? PosixGetDriveImage(GetFileName(path), large)
|
|
: GetFileIcon(path, GetFileName(path), folder, ff.GetMode() & 0111, large);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
Image NativePathIcon(const char *path, bool folder)
|
|
{
|
|
return NativePathIcon0(path, folder, false);
|
|
}
|
|
|
|
Image NativePathIcon(const char *path)
|
|
{
|
|
FindFile ff(path);
|
|
return NativePathIcon(path, ff.IsFolder());
|
|
}
|
|
|
|
Image NativePathLargeIcon(const char *path, bool folder)
|
|
{
|
|
return NativePathIcon0(path, folder, true);
|
|
}
|
|
|
|
Image NativePathLargeIcon(const char *path)
|
|
{
|
|
FindFile ff(path);
|
|
return NativePathLargeIcon(path, ff.IsFolder());
|
|
}
|
|
|
|
bool MatchSearch(const String& filename, const String& search)
|
|
{
|
|
return search.GetCount() ? Filter(filename, CharFilterDefaultToUpperAscii).Find(search) >= 0 : true;
|
|
}
|
|
|
|
bool Load(FileList& list, const String& dir, const char *patterns, bool dirs,
|
|
Event<bool, const String&, Image&> WhenIcon, FileSystemInfo& filesystem,
|
|
const String& search, bool hidden, bool hiddenfiles, bool lazyicons)
|
|
{
|
|
if(dir.IsEmpty()) {
|
|
Array<FileSystemInfo::FileInfo> root = filesystem.Find(Null);
|
|
for(int i = 0; i < root.GetCount(); i++)
|
|
if(MatchSearch(root[i].filename, search))
|
|
list.Add(root[i].filename,
|
|
GetDriveImage(root[i].root_style),
|
|
StdFont().Bold(), SColorText, true, -1, Null, SColorDisabled,
|
|
#ifdef PLATFORM_WIN32
|
|
Nvl(root[i].root_desc, String(" ") + t_("Local Disk")),
|
|
#else
|
|
root[i].root_desc,
|
|
#endif
|
|
StdFont()
|
|
);
|
|
#ifdef PLATFORM_WIN32
|
|
list.Add(t_("Network"), CtrlImg::Network(), StdFont().Bold(), SColorText,
|
|
true, -1, Null, SColorDisabled, Null, StdFont());
|
|
#endif
|
|
}
|
|
else {
|
|
Array<FileSystemInfo::FileInfo> ffi =
|
|
filesystem.Find(AppendFileName(dir, filesystem.IsWin32() ? "*.*" : "*"));
|
|
if(ffi.IsEmpty())
|
|
return false;
|
|
#if defined(PLATFORM_POSIX) && !defined(GUI_COCOA)
|
|
bool isdrive = dir == "/media" || dir == "/mnt";
|
|
#endif
|
|
for(int t = 0; t < ffi.GetCount(); t++) {
|
|
const FileSystemInfo::FileInfo& fi = ffi[t];
|
|
bool nd = dirs && !fi.is_directory;
|
|
bool show = hidden;
|
|
if(!show && filesystem.IsWin32() ? !fi.is_hidden : fi.filename[0] != '.')
|
|
show = true;
|
|
if(!show && hiddenfiles && fi.is_file)
|
|
show = true;
|
|
if(fi.filename != "." && fi.filename != ".." &&
|
|
#ifdef PLATFORM_WIN32
|
|
(fi.is_directory || FileSel::IsLnkFile(fi.filename) || PatternMatchMulti(patterns, fi.filename)) &&
|
|
#else
|
|
(fi.is_directory || PatternMatchMulti(patterns, fi.filename)) &&
|
|
#endif
|
|
MatchSearch(fi.filename, search) && show) {
|
|
Image img;
|
|
#ifdef PLATFORM_POSIX
|
|
#ifdef GUI_COCOA
|
|
img = GetFileIcon(AppendFileName(dir, fi.filename), fi.is_directory, fi.unix_mode & 0111, false, lazyicons);
|
|
#else
|
|
img = isdrive ? PosixGetDriveImage(fi.filename, false)
|
|
: GetFileIcon(dir, fi.filename, fi.is_directory, fi.unix_mode & 0111, false);
|
|
#endif
|
|
#endif
|
|
#ifdef GUI_WIN
|
|
img = GetFileIcon(AppendFileName(dir, fi.filename), fi.is_directory, false, false, lazyicons);
|
|
#endif
|
|
if(IsNull(img))
|
|
img = fi.is_directory ? CtrlImg::Dir() : CtrlImg::File();
|
|
WhenIcon(fi.is_directory, fi.filename, img);
|
|
list.Add(fi.filename, fi.is_hidden ? Contrast(img, 200) : img,
|
|
StdFont().Bold(fi.is_directory),
|
|
nd ? SColorDisabled : fi.is_hidden ? Blend(SColorText, Gray, 200) : SColorText, fi.is_directory,
|
|
fi.is_directory ? -1 : fi.length,
|
|
fi.last_write_time,
|
|
nd ? SColorDisabled
|
|
: fi.is_directory ? SColorText
|
|
: fi.is_hidden ? Blend(SColorMark, Gray, 200)
|
|
: SColorMark,
|
|
Null, Null, Null, Null,
|
|
#ifdef PLATFORM_WIN32
|
|
false,
|
|
#else
|
|
fi.unix_mode & 0111,
|
|
#endif
|
|
fi.is_hidden
|
|
);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#ifdef GUI_WIN
|
|
static Mutex sExeMutex;
|
|
static WCHAR sExePath[1025];
|
|
static bool sExeRunning;
|
|
static SHFILEINFOW sExeInfo;
|
|
|
|
static auxthread_t auxthread__ sExeIconThread(void *)
|
|
{
|
|
SHFILEINFOW info;
|
|
WCHAR path[1025];
|
|
CoInitialize(NULL);
|
|
sExeMutex.Enter();
|
|
wcscpy(path, sExePath);
|
|
sExeMutex.Leave();
|
|
AvoidPaintingCheck__();
|
|
SHGetFileInfoW(path, FILE_ATTRIBUTE_NORMAL, &info, sizeof(info), SHGFI_ICON|SHGFI_SMALLICON);
|
|
sExeMutex.Enter();
|
|
memcpy(&sExeInfo, &info, sizeof(info));
|
|
sExeRunning = false;
|
|
sExeMutex.Leave();
|
|
return 0;
|
|
}
|
|
|
|
void LazyExeFileIcons::Done(Image img)
|
|
{
|
|
if(pos >= ndx.GetCount())
|
|
return;
|
|
int ii = ndx[pos];
|
|
if(ii < 0 || ii >= list->GetCount())
|
|
return;
|
|
const FileList::File& f = list->Get(ii);
|
|
WhenIcon(false, f.name, img);
|
|
if(!IsNull(img)) {
|
|
if(f.hidden)
|
|
img = Contrast(img, 200);
|
|
list->SetIcon(ii, img);
|
|
}
|
|
pos++;
|
|
}
|
|
|
|
WString LazyExeFileIcons::Path()
|
|
{
|
|
if(pos >= ndx.GetCount())
|
|
return Null;
|
|
int ii = ndx[pos];
|
|
if(ii < 0 || ii >= list->GetCount())
|
|
return Null;
|
|
const FileList::File& f = list->Get(ii);
|
|
return NormalizePath(AppendFileName(dir, f.name)).ToWString();
|
|
}
|
|
|
|
void LazyExeFileIcons::Do()
|
|
{
|
|
int start = msecs();
|
|
for(;;) {
|
|
for(;;) {
|
|
SHFILEINFOW info;
|
|
bool done = false;
|
|
WString path = Path();
|
|
if(IsNull(path))
|
|
return;
|
|
sExeMutex.Enter();
|
|
bool running = sExeRunning;
|
|
if(!running) {
|
|
done = path == ToUtf32(sExePath);
|
|
memcpy(&info, &sExeInfo, sizeof(info));
|
|
*sExePath = '\0';
|
|
memset(&sExeInfo, 0, sizeof(sExeInfo));
|
|
}
|
|
sExeMutex.Leave();
|
|
Image img = ProcessSHIcon(info.hIcon);
|
|
if(done)
|
|
Done(img);
|
|
if(!running)
|
|
break;
|
|
Sleep(0);
|
|
if(msecs(start) > 10 || Ctrl::IsWaitingEvent()) {
|
|
Restart(0);
|
|
return;
|
|
}
|
|
}
|
|
|
|
WString path = Path();
|
|
if(IsNull(path))
|
|
return;
|
|
sExeMutex.Enter();
|
|
memcpy(sExePath, ~path, 2 * min(1024, path.GetCount() + 1));
|
|
sExeRunning = true;
|
|
StartAuxThread(sExeIconThread, NULL);
|
|
sExeMutex.Leave();
|
|
}
|
|
}
|
|
|
|
void LazyExeFileIcons::ReOrder()
|
|
{ // gather .exe and .lnk files; sort based on length so that small .exe get resolved first
|
|
ndx.Clear();
|
|
Vector<int> len;
|
|
for(int i = 0; i < list->GetCount(); i++) {
|
|
const FileList::File& f = list->Get(i);
|
|
if(findarg(ToLower(GetFileExt(f.name)), ".exe", ".lnk") >= 0 && !f.isdir) {
|
|
ndx.Add(i);
|
|
len.Add((int)min((int64)INT_MAX, f.length));
|
|
}
|
|
}
|
|
IndexSort(len, ndx);
|
|
Restart(0);
|
|
}
|
|
|
|
void LazyExeFileIcons::Start(FileList& list_, const String& dir_, Event<bool, const String&, Image&> WhenIcon_)
|
|
{
|
|
list = &list_;
|
|
dir = dir_;
|
|
WhenIcon = WhenIcon_;
|
|
pos = 0;
|
|
ReOrder();
|
|
}
|
|
#endif
|
|
|
|
String FileSel::GetDir() const
|
|
{
|
|
String s = ~dir;
|
|
if(s.IsEmpty()) return basedir;
|
|
if(basedir.IsEmpty()) return s;
|
|
return AppendFileName(basedir, s);
|
|
}
|
|
|
|
void FileSel::SetDir(const String& _dir) {
|
|
#ifdef PLATFORM_WIN32
|
|
netstack.Clear();
|
|
netnode.Clear();
|
|
#endif
|
|
dir <<= _dir;
|
|
Load();
|
|
Update();
|
|
}
|
|
|
|
String FileSel::FilePath(const String& fn) {
|
|
return AppendFileName(GetDir(), fn);
|
|
}
|
|
|
|
Image GetDriveImage(char drive_style)
|
|
{
|
|
switch(drive_style)
|
|
{
|
|
case FileSystemInfo::ROOT_NO_ROOT_DIR: return Null;
|
|
case FileSystemInfo::ROOT_REMOTE:
|
|
case FileSystemInfo::ROOT_NETWORK: return CtrlImg::Share();
|
|
case FileSystemInfo::ROOT_COMPUTER: return CtrlImg::Computer();
|
|
case FileSystemInfo::ROOT_REMOVABLE: return CtrlImg::Flash();
|
|
case FileSystemInfo::ROOT_CDROM: return CtrlImg::CdRom();
|
|
default: return CtrlImg::Hd();
|
|
}
|
|
}
|
|
|
|
String FileSel::GetMask()
|
|
{
|
|
String emask = "*";
|
|
if(!IsNull(type)) {
|
|
if(IsString(~type))
|
|
emask = ~type;
|
|
else {
|
|
int q = ~type;
|
|
if(q >= 0 && q < mask.GetCount())
|
|
emask = mask[q];
|
|
}
|
|
}
|
|
return emask;
|
|
}
|
|
|
|
void FileSel::Load()
|
|
{
|
|
search <<= Null;
|
|
SearchLoad();
|
|
}
|
|
|
|
void FileSel::LoadNet()
|
|
{
|
|
#ifdef PLATFORM_WIN32
|
|
list.Clear();
|
|
for(int i = 0; i < netnode.GetCount(); i++) {
|
|
Image m = CtrlImg::Group();
|
|
switch(netnode[i].GetDisplayType()) {
|
|
case NetNode::NETWORK:
|
|
m = CtrlImg::Network();
|
|
break;
|
|
case NetNode::SHARE:
|
|
m = CtrlImg::Share();
|
|
break;
|
|
case NetNode::SERVER:
|
|
m = CtrlImg::Computer();
|
|
break;
|
|
}
|
|
list.Add(netnode[i].GetName(), m);
|
|
}
|
|
places.FindSetCursor("\\");
|
|
#endif
|
|
}
|
|
|
|
void FileSel::SelectNet()
|
|
{
|
|
#ifdef PLATFORM_WIN32
|
|
int q = list.GetCursor();
|
|
if(q >= 0 && q < netnode.GetCount()) {
|
|
NetNode& n = netnode[q];
|
|
String p = n.GetPath();
|
|
if(p.GetCount())
|
|
SetDir(p);
|
|
else {
|
|
NetNode n = netnode[q];
|
|
netstack.Add() = n;
|
|
ScanNetwork([=] {
|
|
return n.Enum();
|
|
});
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef PLATFORM_WIN32
|
|
bool FileSel::ScanNetwork(Function<Array<NetNode> ()> fn)
|
|
{
|
|
Progress pi(t_("Scanning network.."));
|
|
loading_network = true;
|
|
for(;;) {
|
|
Ptr<FileSel> fs = this;
|
|
if(CoWork::TrySchedule([=] {
|
|
Array<NetNode> n = fn();
|
|
GuiLock __;
|
|
if(fs) {
|
|
fs->netnode = pick(n);
|
|
fs->loading_network = false;
|
|
}
|
|
}))
|
|
break;
|
|
if(pi.StepCanceled()) {
|
|
SetDir("");
|
|
return false;
|
|
}
|
|
Sleep(10);
|
|
}
|
|
for(;;) {
|
|
if(pi.StepCanceled()) {
|
|
SetDir("");
|
|
return false;
|
|
}
|
|
if(!loading_network) {
|
|
LoadNet();
|
|
break;
|
|
}
|
|
Sleep(10);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool FileSel::netroot_loaded;
|
|
Array<NetNode> FileSel::netroot;
|
|
|
|
void FileSel::ScanNetworkRoot()
|
|
{
|
|
if(netroot_loaded) {
|
|
netnode = clone(netroot);
|
|
LoadNet();
|
|
return;
|
|
}
|
|
netroot_loaded = ScanNetwork([] {
|
|
Array<NetNode> n;
|
|
n = NetNode::EnumRoot();
|
|
n.Append(NetNode::EnumRemembered());
|
|
return n;
|
|
});
|
|
netroot = clone(netnode);
|
|
}
|
|
#endif
|
|
|
|
void FileSel::SearchLoad()
|
|
{
|
|
loaded = true;
|
|
list.EndEdit();
|
|
list.Clear();
|
|
String d = GetDir();
|
|
#ifdef PLATFORM_WIN32
|
|
if(d == "\\") {
|
|
ScanNetworkRoot();
|
|
return;
|
|
}
|
|
#endif
|
|
String emask = GetMask();
|
|
if(!UPP::Load(list, d, emask, mode == SELECTDIR, WhenIcon, *filesystem, ~search, ~hidden, ~hiddenfiles, true)) {
|
|
loaded = false;
|
|
Exclamation(t_("[A3* Unable to read the directory !]&&") + DeQtf((String)~dir) + "&&" +
|
|
GetErrorMessage(GetLastError()));
|
|
if(!basedir.IsEmpty() && String(~dir).IsEmpty()) {
|
|
Break(IDCANCEL);
|
|
return;
|
|
}
|
|
dir <<= olddir;
|
|
olddir = Null;
|
|
SearchLoad();
|
|
}
|
|
|
|
places.KillCursor();
|
|
if(d.GetCount())
|
|
places.FindSetCursor(d);
|
|
hiddenfiles.Enable(!hidden);
|
|
if(d.IsEmpty()) {
|
|
if(filesystem->IsWin32()) {
|
|
mkdir.Disable();
|
|
plus.Disable();
|
|
minus.Disable();
|
|
toggle.Disable();
|
|
list.Renaming(false);
|
|
}
|
|
dir <<= d;
|
|
dirup.Disable();
|
|
}
|
|
else {
|
|
dirup.Enable();
|
|
mkdir.Enable();
|
|
plus.Enable();
|
|
minus.Enable();
|
|
toggle.Enable();
|
|
list.Renaming(true);
|
|
}
|
|
if(filesystem->IsPosix())
|
|
if(d == "/" || !IsEmpty(basedir) && String(~dir).IsEmpty())
|
|
dirup.Disable();
|
|
if(filesystem->IsWin32())
|
|
if(!IsEmpty(basedir) && String(~dir).IsEmpty())
|
|
dirup.Disable();
|
|
olddir = ~dir;
|
|
if(olddir.GetCount() || basedir.GetCount())
|
|
SortBy(list, ~sortby);
|
|
Update();
|
|
#ifdef GUI_WIN
|
|
lazyicons.Start(list, d, WhenIcon);
|
|
#endif
|
|
StartLI();
|
|
}
|
|
|
|
StaticMutex FileSel::li_mutex;
|
|
void (*FileSel::li_current)(const String& path, Image& result);
|
|
String FileSel::li_path;
|
|
Image FileSel::li_result;
|
|
bool FileSel::li_running;
|
|
int FileSel::li_pos;
|
|
|
|
void FileSel::LIThread()
|
|
{
|
|
String path;
|
|
void (*li)(const String& path, Image& result);
|
|
{
|
|
Mutex::Lock __(li_mutex);
|
|
path = li_path;
|
|
li = li_current;
|
|
}
|
|
Image result;
|
|
if(path.GetCount())
|
|
li(path, result);
|
|
if(!IsNull(result) && max(result.GetWidth(), result.GetHeight()) > DPI(16))
|
|
result = Rescale(result, DPI(16), DPI(16));
|
|
{
|
|
Mutex::Lock __(li_mutex);
|
|
li_result = result;
|
|
li_running = false;
|
|
}
|
|
}
|
|
|
|
String FileSel::LIPath()
|
|
{
|
|
return li_pos >= 0 && li_pos < list.GetCount() ? FilePath(list.Get(li_pos).name) : Null;
|
|
}
|
|
|
|
void FileSel::DoLI()
|
|
{
|
|
int start = msecs();
|
|
for(;;) {
|
|
for(;;) {
|
|
bool done = false;
|
|
String path = LIPath();
|
|
if(IsNull(path))
|
|
return;
|
|
bool running;
|
|
Image img;
|
|
{
|
|
Mutex::Lock __(li_mutex);
|
|
running = li_running;
|
|
if(!running) {
|
|
done = li_path == path && li_current == WhenIconLazy;
|
|
img = li_result;
|
|
}
|
|
}
|
|
if(done) {
|
|
if(li_pos < 0 || li_pos >= list.GetCount())
|
|
return;
|
|
if(!IsNull(img)) {
|
|
const FileList::File& f = list.Get(li_pos);
|
|
WhenIcon(f.isdir, f.name, img);
|
|
if(f.hidden)
|
|
img = Contrast(img, 200);
|
|
list.SetIcon(li_pos, img);
|
|
}
|
|
li_pos++;
|
|
}
|
|
if(!running)
|
|
break;
|
|
Sleep(0);
|
|
if(msecs(start) > 10 || Ctrl::IsWaitingEvent()) {
|
|
ScheduleLI();
|
|
return;
|
|
}
|
|
}
|
|
|
|
String path = LIPath();
|
|
if(IsNull(path))
|
|
return;
|
|
{
|
|
Mutex::Lock __(li_mutex);
|
|
if(!li_running) {
|
|
li_current = WhenIconLazy;
|
|
li_path = path;
|
|
li_running = true;
|
|
Thread::Start(callback(LIThread));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FileSel::StartLI()
|
|
{
|
|
if(WhenIconLazy) {
|
|
li_pos = 0;
|
|
ScheduleLI();
|
|
}
|
|
}
|
|
|
|
String TrimDot(String f) {
|
|
int i = f.Find('.');
|
|
if(i >= 0 && i == f.GetLength() - 1)
|
|
f.Trim(i);
|
|
return f;
|
|
}
|
|
|
|
void FileSel::AddName(Vector<String>& fn, String& f) {
|
|
if(!f.IsEmpty()) {
|
|
f = TrimDot(f);
|
|
if(f[0] == '\"' && f.GetCount() > 2)
|
|
f = f.Mid(1, f.GetCount() - 2);
|
|
if(f.Find('.') < 0) {
|
|
String t = GetMask();
|
|
int q = t.Find('.');
|
|
if(q >= 0 && IsAlNum(t[q + 1])) {
|
|
int w = q + 2;
|
|
while(IsAlNum(t[w]))
|
|
w++;
|
|
f << t.Mid(q, w - q);
|
|
}
|
|
else
|
|
if(defext.GetCount())
|
|
f << '.' << defext;
|
|
}
|
|
fn.Add(f);
|
|
}
|
|
f.Clear();
|
|
}
|
|
|
|
bool FileSel::IsLnkFile(const String& p)
|
|
{
|
|
int l = p.GetLength() - 4;
|
|
return l >= 0 && p[l] == '.' && ToLower(p[l + 1]) == 'l' && ToLower(p[l + 2]) == 'n' && ToLower(p[l + 3]) == 'k';
|
|
}
|
|
|
|
String FileSel::ResolveLnk(const String& name) const
|
|
{
|
|
#ifdef PLATFORM_WIN32
|
|
if(IsLnkFile(name))
|
|
return GetSymLinkPath(AppendFileName(GetDir(), name));
|
|
#endif
|
|
return Null;
|
|
}
|
|
|
|
String FileSel::ResolveLnkDir(const String& name) const
|
|
{
|
|
#ifdef PLATFORM_WIN32
|
|
String p = ResolveLnk(name);
|
|
if(p.GetCount() && DirectoryExists(p))
|
|
return p;
|
|
#endif
|
|
return Null;
|
|
}
|
|
|
|
String FileSel::ResolveLnkFile(const String& name) const
|
|
{
|
|
#ifdef PLATFORM_WIN32
|
|
String p = ResolveLnk(name);
|
|
if(p.GetCount() && FileExists(p))
|
|
return p;
|
|
#endif
|
|
return Null;
|
|
}
|
|
|
|
void FileSel::Finish() {
|
|
if(filesystem->IsWin32())
|
|
if(GetDir().IsEmpty()) {
|
|
file.Clear();
|
|
return;
|
|
}
|
|
fn.Clear();
|
|
if(mode == SELECTDIR) {
|
|
String p = GetDir();
|
|
if(list.IsSelection() && multi) {
|
|
for(int i = 0; i < list.GetCount(); i++)
|
|
if(list.IsSelected(i)) {
|
|
const FileList::File& m = list[i];
|
|
if(m.isdir)
|
|
fn.Add(AppendFileName(p, m.name));
|
|
#ifdef PLATFORM_WIN32
|
|
else {
|
|
String p = ResolveLnkDir(m.name);
|
|
if(p.GetCount())
|
|
fn.Add(p);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
else {
|
|
String p = GetDir();
|
|
if(list.GetCursor() >= 0) {
|
|
const FileList::File& m = list[list.GetCursor()];
|
|
if(m.isdir)
|
|
p = AppendFileName(p, m.name);
|
|
#ifdef PLATFORM_WIN32
|
|
else {
|
|
String pp = ResolveLnkDir(m.name);
|
|
if(p.GetCount())
|
|
p = pp;
|
|
}
|
|
#endif
|
|
}
|
|
fn.Add(p);
|
|
}
|
|
Break(IDOK);
|
|
return;
|
|
}
|
|
String f = file.GetText().ToString();
|
|
if(f.IsEmpty()) return;
|
|
String o;
|
|
if(mode == OPEN && IsMulti()) {
|
|
for(const char *s = f; *s; s++) {
|
|
if(*s == ' ')
|
|
AddName(fn, o);
|
|
else
|
|
if(*s == '\"') {
|
|
AddName(fn, o);
|
|
s++;
|
|
for(;;) {
|
|
if(*s == '\0' || *s == '\"') {
|
|
AddName(fn, o);
|
|
break;
|
|
}
|
|
o.Cat(*s++);
|
|
}
|
|
}
|
|
else
|
|
o.Cat(*s);
|
|
}
|
|
AddName(fn, o);
|
|
}
|
|
else {
|
|
o = f;
|
|
AddName(fn, o);
|
|
}
|
|
if(!IsMulti() && fn.GetCount())
|
|
fn.SetCount(1);
|
|
String d = GetDir();
|
|
String nonexist;
|
|
int ne = 0;
|
|
for(int i = 0; i < fn.GetCount(); i++) {
|
|
String p = fn[i];
|
|
if(!IsFullPath(p))
|
|
p = NormalizePath(AppendFileName(d, fn[i]));
|
|
Array<FileSystemInfo::FileInfo> ff = filesystem->Find(p, 1);
|
|
p = DeQtf(p);
|
|
if(!ff.IsEmpty() && ff[0].is_directory) {
|
|
Exclamation(p + t_(" is directory."));
|
|
return;
|
|
}
|
|
if(asking) {
|
|
if(mode == SAVEAS) {
|
|
if(!ff.IsEmpty() && !PromptOKCancel(p + t_(" already exists.&Do you want to continue ?")))
|
|
return;
|
|
}
|
|
else
|
|
if(ff.IsEmpty()) {
|
|
if(ne) nonexist << '&';
|
|
nonexist << p;
|
|
ne++;
|
|
}
|
|
}
|
|
}
|
|
if(ne) {
|
|
nonexist << (ne == 1 ? t_(" does not exist.") : t_("&do not exist."));
|
|
if(!PromptOKCancel(nonexist + t_("&Do you want to continue ?")))
|
|
return;
|
|
}
|
|
Break(IDOK);
|
|
}
|
|
|
|
bool FileSel::OpenItem() {
|
|
fw.Clear();
|
|
if(list.IsCursor()) {
|
|
#ifdef PLATFORM_WIN32
|
|
if(netnode.GetCount()) {
|
|
SelectNet();
|
|
return true;
|
|
}
|
|
#endif
|
|
const FileList::File& m = list.Get(list.GetCursor());
|
|
String path = AppendFileName(~dir, m.name);
|
|
#ifdef PLATFORM_WIN32
|
|
if(IsNull(dir) && m.name == t_("Network")) {
|
|
ScanNetworkRoot();
|
|
return true;
|
|
}
|
|
String p = ResolveLnkDir(m.name);
|
|
if(p.GetCount()) {
|
|
SetDir(p);
|
|
return true;
|
|
}
|
|
#endif
|
|
if(m.isdir) {
|
|
SetDir(path);
|
|
return true;
|
|
}
|
|
}
|
|
if(mode != SELECTDIR)
|
|
Finish();
|
|
return false;
|
|
}
|
|
|
|
void FileSel::Open() {
|
|
if(mode == SELECTDIR) {
|
|
#ifdef PLATFORM_WIN32
|
|
if(netnode.GetCount())
|
|
return;
|
|
#endif
|
|
Finish();
|
|
return;
|
|
}
|
|
if(list.HasFocus() || type.HasFocus()) {
|
|
if(OpenItem()) list.SetCursor(0);
|
|
}
|
|
else
|
|
if(list.IsSelection())
|
|
for(int i = 0; i < list.GetCount(); i++) {
|
|
const FileList::File& m = list[i];
|
|
if(!m.isdir) Finish();
|
|
}
|
|
else
|
|
if(file.HasFocus()) {
|
|
String fn = file.GetText().ToString();
|
|
#ifdef PLATFORM_WIN32
|
|
if(fn[0] == '\\' && fn[1] == '\\') {
|
|
FindFile ff(AppendFileName(fn, "*.*"));
|
|
if(ff)
|
|
SetDir(TrimDot(fn));
|
|
return;
|
|
}
|
|
#endif
|
|
if(fn == "." || fn == "..") {
|
|
DirUp();
|
|
return;
|
|
}
|
|
if(HasWildcards(fn)) {
|
|
file.Clear();
|
|
int q = FindIndex(mask, fn);
|
|
if(q >= 0)
|
|
type.SetIndex(q);
|
|
else {
|
|
type.Add(fn, t_("Custom file type (") + fn + ')');
|
|
type.SetIndex(type.GetCount() - 1);
|
|
}
|
|
Load();
|
|
return;
|
|
}
|
|
if(fn.Find('\"') < 0) {
|
|
if(filesystem->IsWin32())
|
|
{
|
|
if(fn.GetLength() >= 2 && fn[1] == ':' && fn.GetLength() <= 3) {
|
|
fn.Set(0, ToUpper(fn[0]));
|
|
if(fn.GetLength() == 2)
|
|
fn.Cat('\\');
|
|
SetDir(fn);
|
|
return;
|
|
}
|
|
}
|
|
if(!IsFullPath(fn))
|
|
fn = AppendFileName(~dir, fn);
|
|
if(filesystem->IsWin32() && (!fn.IsEmpty() && (*fn.Last() == '\\' || *fn.Last() == '/'))
|
|
|| filesystem->IsPosix() && (fn != "/" && (*fn.Last() == '\\' || *fn.Last() == '/')))
|
|
{
|
|
fn.Trim(fn.GetLength() - 1);
|
|
SetDir(TrimDot(fn));
|
|
return;
|
|
}
|
|
Array<FileSystemInfo::FileInfo> ff = filesystem->Find(fn, 1);
|
|
if(!ff.IsEmpty()) {
|
|
if(ff[0].is_directory) {
|
|
SetDir(TrimDot(fn));
|
|
return;
|
|
}
|
|
else {
|
|
SetDir(TrimDot(GetFileFolder(fn)));
|
|
file.SetText(GetFileName(fn).ToWString());
|
|
}
|
|
}
|
|
}
|
|
if(mode != SELECTDIR) Finish();
|
|
}
|
|
}
|
|
|
|
String DirectoryUp(String& dir, bool basedir)
|
|
{
|
|
while(*dir.Last() == '\\' || *dir.Last() == '/')
|
|
dir.Trim(dir.GetCount() - 1);
|
|
String s = dir;
|
|
String name;
|
|
#ifdef PLATFORM_WIN32
|
|
if(s.GetLength() < 3 || s.GetLength() == 3 && s[1] == ':') {
|
|
dir.Clear();
|
|
name = s;
|
|
}
|
|
else
|
|
#endif
|
|
#ifdef PLATFORM_POSIX
|
|
if(s != "/")
|
|
#endif
|
|
{
|
|
#ifdef PLATFORM_WIN32
|
|
int i = max(s.ReverseFind('/'), s.ReverseFind('\\'));
|
|
#endif
|
|
#ifdef PLATFORM_POSIX
|
|
int i = s.ReverseFind('/');
|
|
#endif
|
|
if(basedir)
|
|
if(i < 0)
|
|
dir.Clear();
|
|
else {
|
|
dir = s.Mid(0, i);
|
|
name = s.Mid(i + 1);
|
|
}
|
|
else {
|
|
#ifdef PLATFORM_WIN32
|
|
if(s.GetLength() && s[1] == ':') {
|
|
if(i > 3) {
|
|
dir = s.Mid(0, i);
|
|
name = s.Mid(i + 1);
|
|
}
|
|
else {
|
|
dir = s.Mid(0, 3);
|
|
name = s.Mid(3);
|
|
}
|
|
}
|
|
if(s.GetLength() && s[0] == DIR_SEP && s[1] == DIR_SEP) {
|
|
if(i > 2) {
|
|
dir = s.Mid(0, i);
|
|
name = s.Mid(i + 1);
|
|
}
|
|
else {
|
|
dir.Clear();
|
|
name = s;
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef PLATFORM_POSIX
|
|
if(i == 0 && s.GetLength() > 1) {
|
|
dir = "/";
|
|
name = s.Mid(1);
|
|
}
|
|
else
|
|
if(s.GetLength() && s[0] == '/' && s[1] != '/') {
|
|
dir = s.Mid(0, i);
|
|
name = s.Mid(i + 1);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
return name;
|
|
}
|
|
|
|
void FileSel::DirUp()
|
|
{
|
|
#ifdef PLATFORM_WIN32
|
|
if(netstack.GetCount()) {
|
|
netstack.Drop();
|
|
if(netstack.GetCount()) {
|
|
netnode = netstack.Top().Enum();
|
|
LoadNet();
|
|
}
|
|
netnode = NetNode::EnumRoot();
|
|
return;
|
|
}
|
|
if(netnode.GetCount()) {
|
|
netnode.Clear();
|
|
SetDir("");
|
|
return;
|
|
}
|
|
#endif
|
|
String s = ~dir;
|
|
fw.Add(s);
|
|
String name = DirectoryUp(s, !basedir.IsEmpty());
|
|
#ifdef PLATFORM_WIN32
|
|
if(s[0] == '\\' && s[1] == '\\' && s.Find('\\', 2) < 0) {
|
|
s.Clear();
|
|
name.Clear();
|
|
}
|
|
#endif
|
|
SetDir(s);
|
|
if(list.HasFocus())
|
|
list.FindSetCursor(name);
|
|
}
|
|
|
|
void FileSel::MkDir() {
|
|
if(String(~dir).IsEmpty() && basedir.IsEmpty()) return;
|
|
String name, error;
|
|
if(EditText(name, t_("New directory"), t_("Name")) && !name.IsEmpty()) {
|
|
if(filesystem->CreateFolder(FilePath(name), error)) {
|
|
Load();
|
|
list.FindSetCursor(name);
|
|
}
|
|
else
|
|
Exclamation(t_("[A3* Creating directory failed !&&]") + error);
|
|
}
|
|
}
|
|
|
|
void FileSel::PlusMinus(const char *title, bool sel) {
|
|
String pattern;
|
|
if(EditText(pattern, title, t_("Mask")) && !pattern.IsEmpty())
|
|
for(int i = 0; i < list.GetCount(); i++)
|
|
if(!list.Get(i).isdir)
|
|
if(PatternMatchMulti(pattern, list.Get(i).name))
|
|
list.SelectOne(i, sel);
|
|
}
|
|
|
|
void FileSel::Plus() {
|
|
PlusMinus(t_("Add to selection"), true);
|
|
}
|
|
|
|
void FileSel::Minus() {
|
|
PlusMinus(t_("Remove from selection"), false);
|
|
}
|
|
|
|
void FileSel::Toggle() {
|
|
for(int i = 0; i < list.GetCount(); i++)
|
|
if(!list.Get(i).isdir)
|
|
list.SelectOne(i, !list.IsSelected(i));
|
|
}
|
|
|
|
void FileSel::Reload()
|
|
{
|
|
String fn = list.GetCurrentName();
|
|
int a = list.GetScroll();
|
|
SearchLoad();
|
|
list.ScrollTo(a);
|
|
list.FindSetCursor(fn);
|
|
}
|
|
|
|
void FileSel::Activate()
|
|
{
|
|
if(loaded && !loading_network && GetDir() != "\\"
|
|
#ifdef PLATFORM_WIN32
|
|
&& netnode.GetCount() == 0
|
|
#endif
|
|
)
|
|
Reload();
|
|
TopWindow::Activate();
|
|
}
|
|
|
|
bool FileSel::Key(dword key, int count) {
|
|
switch(key) {
|
|
case K_F9:
|
|
#ifdef PLATFORM_WIN32
|
|
netroot_loaded = false;
|
|
#endif
|
|
Reload();
|
|
return true;
|
|
case K_MOUSE_FORWARD:
|
|
if(fw.GetCount())
|
|
SetDir(fw.Pop());
|
|
return true;
|
|
case K_MOUSE_BACKWARD:
|
|
case '.':
|
|
case K_CTRL_UP:
|
|
list.SetFocus();
|
|
dirup.PseudoPush();
|
|
return true;
|
|
case '+':
|
|
plus.PseudoPush();
|
|
return true;
|
|
case '-':
|
|
minus.PseudoPush();
|
|
return true;
|
|
case '*':
|
|
toggle.PseudoPush();
|
|
return true;
|
|
case K_F5:
|
|
Reload();
|
|
return true;
|
|
case K_F6:
|
|
list.StartEdit();
|
|
return true;
|
|
case K_F7:
|
|
mkdir.PseudoPush();
|
|
return true;
|
|
case K_ENTER:
|
|
if(mode == SELECTDIR && OpenItem())
|
|
return true;
|
|
break;
|
|
case K_UP:
|
|
case K_DOWN:
|
|
case K_PAGEUP:
|
|
case K_PAGEDOWN:
|
|
list.SetFocus();
|
|
return list.Key(key, count);
|
|
}
|
|
if(CharFilterDefaultToUpperAscii(key) || key == K_BACKSPACE)
|
|
return search.Key(key, count);
|
|
return TopWindow::Key(key, count);
|
|
}
|
|
|
|
void Catq(String& s, const String& fn) {
|
|
if(!s.IsEmpty())
|
|
s << ' ';
|
|
if(fn.Find(' ') >= 0)
|
|
s << '"' << fn << '"';
|
|
else
|
|
s << fn;
|
|
}
|
|
|
|
String FormatFileSize(int64 n)
|
|
{
|
|
if(n < 10000)
|
|
return Format("%d B ", n);
|
|
else
|
|
if(n < 10000 * 1024)
|
|
return Format("%d.%d K ", n >> 10, (n & 1023) / 103);
|
|
else
|
|
if(n < I64(10000000) * 1024)
|
|
return Format("%d.%d M ", n >> 20, (n & 1023) / 103);
|
|
else
|
|
return Format("%d.%d G ", n >> 30, (n & 1023) / 103);
|
|
}
|
|
|
|
void FileSel::Update() {
|
|
String fn;
|
|
if(list.IsSelection()) {
|
|
for(int i = 0; i < list.GetCount(); i++)
|
|
if(list.IsSelected(i))
|
|
Catq(fn, list[i].name);
|
|
}
|
|
else
|
|
if(list.IsCursor()) {
|
|
const FileList::File& m = list[list.GetCursor()];
|
|
if(!m.isdir)
|
|
Catq(fn, m.name);
|
|
}
|
|
if(mode == OPEN)
|
|
file <<= fn;
|
|
filename = String();
|
|
filesize = String();
|
|
filetime = String();
|
|
if(preview)
|
|
*preview <<= Null;
|
|
if(list.IsCursor()) {
|
|
fn = list[list.GetCursor()].name;
|
|
if(fn[1] == ':' && fn.GetLength() <= 3)
|
|
filename = t_(" Drive");
|
|
else {
|
|
String path = FilePath(fn);
|
|
Array<FileSystemInfo::FileInfo> ff = filesystem->Find(path, 1);
|
|
if(!ff.IsEmpty()) {
|
|
filename = " " + fn;
|
|
if(ff[0].is_directory)
|
|
filesize = t_("Directory ");
|
|
else {
|
|
if(mode == SAVEAS)
|
|
file <<= fn;
|
|
filesize = FormatFileSize(ff[0].length);
|
|
if(preview)
|
|
*preview <<= path;
|
|
}
|
|
Time tm = ff[0].last_write_time;
|
|
filetime = " " + Format(tm);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
int drives = 0;
|
|
int dirs = 0;
|
|
int files = 0;
|
|
int64 length = 0;
|
|
for(int i = 0; i < list.GetCount(); i++)
|
|
if(!list.IsSelection() || list.IsSelected(i)) {
|
|
const FileList::File& f = list[i];
|
|
if(f.isdir)
|
|
#ifdef PLATFORM_WIN32
|
|
(*f.name.Last() == ':' ? drives : dirs)++;
|
|
#else
|
|
dirs++;
|
|
#endif
|
|
else {
|
|
files++;
|
|
length += f.length;
|
|
}
|
|
}
|
|
String s;
|
|
if(drives)
|
|
s << drives << t_(" drive(s)");
|
|
else {
|
|
if(dirs)
|
|
s << dirs << t_(" folder(s)");
|
|
if(files) {
|
|
if(s.GetCount())
|
|
s << ", ";
|
|
s << files << t_(" file(s)");
|
|
}
|
|
}
|
|
filename = " " + s;
|
|
if(length >= 0)
|
|
filesize = FormatFileSize(length);
|
|
}
|
|
FileUpdate();
|
|
}
|
|
|
|
void FileSel::FileUpdate() {
|
|
if(mode == SELECTDIR) {
|
|
ok.Enable(!IsNull(~dir));
|
|
return;
|
|
}
|
|
bool b = list.IsCursor() || !String(~file).IsEmpty();
|
|
ok.Enable(b);
|
|
if(mode != SAVEAS || list.IsCursor() && list[list.GetCursor()].isdir)
|
|
ok.SetLabel(t_("Open"));
|
|
else
|
|
ok.SetLabel(t_("Save"));
|
|
}
|
|
|
|
void FileSel::Rename(const String& on, const String& nn) {
|
|
if(on == nn) return;
|
|
#ifdef PLATFORM_WIN32
|
|
if(FileMove(FilePath(on), FilePath(nn)))
|
|
#else
|
|
if(rename(FilePath(on), FilePath(nn)) == 0)
|
|
#endif
|
|
{
|
|
Load();
|
|
list.FindSetCursor(nn);
|
|
}
|
|
else
|
|
Exclamation(t_("[A3* Renaming of file failed !&&]") + GetErrorMessage(GetLastError()));
|
|
}
|
|
|
|
void FileSel::Choice() {
|
|
Load();
|
|
}
|
|
|
|
FileSel& FileSel::Type(const char *name, const char *ext) {
|
|
type.Add(type.GetCount(), name);
|
|
mask.Add(ext);
|
|
if(IsNull(type))
|
|
type.SetIndex(0);
|
|
return *this;
|
|
}
|
|
|
|
FileSel& FileSel::Types(const char *d) {
|
|
Vector<String> s = Split(d, '\n');
|
|
for(int i = 0; i < s.GetCount(); i++) {
|
|
Vector<String> h = Split(s[i], '\t');
|
|
if(h.GetCount() == 2)
|
|
Type(h[0], h[1]);
|
|
if(h.GetCount() == 1)
|
|
Type(h[0], h[0]);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
FileSel& FileSel::ClearTypes()
|
|
{
|
|
type.Clear();
|
|
mask.Clear();
|
|
return *this;
|
|
}
|
|
|
|
FileSel& FileSel::ActiveType(int i)
|
|
{
|
|
activetype.Clear();
|
|
if(i >= 0 && i < type.GetCount())
|
|
activetype = type.GetValue(i);
|
|
return *this;
|
|
}
|
|
|
|
FileSel& FileSel::AllFilesType() {
|
|
return Type(t_("All files"), "*.*");
|
|
}
|
|
|
|
struct FolderDisplay : public Display {
|
|
virtual void Paint(Draw& w, const Rect& r, const Value& q,
|
|
Color ink, Color paper, dword style) const;
|
|
};
|
|
|
|
Image GetDirIcon(const String& s)
|
|
{
|
|
Image img;
|
|
#ifdef PLATFORM_X11
|
|
img = GetFileIcon(GetFileFolder(s), GetFileName(s), true, false, false);
|
|
#endif
|
|
#ifdef PLATFORM_WIN32
|
|
if((byte)*s.Last() == 255)
|
|
img = CtrlImg::Network();
|
|
else {
|
|
int q = s.Find(0);
|
|
if(q >= 0 && q + 1 < s.GetCount())
|
|
img = GetDriveImage(s[q + 1]);
|
|
else
|
|
img = s.GetCount() ? GetFileIcon(s, false, true, false) : CtrlImg::Computer();
|
|
}
|
|
#endif
|
|
if(IsNull(img))
|
|
img = CtrlImg::Dir();
|
|
return DPI(img);
|
|
}
|
|
|
|
void FolderDisplay::Paint(Draw& w, const Rect& r, const Value& q,
|
|
Color ink, Color paper, dword style) const
|
|
{
|
|
String s = q;
|
|
w.DrawRect(r, paper);
|
|
Image img = GetDirIcon(s);
|
|
w.DrawImage(r.left, r.top + (r.Height() - img.GetSize().cx) / 2, img);
|
|
w.DrawText(r.left + Zx(20),
|
|
r.top + (r.Height() - StdFont().Bold().Info().GetHeight()) / 2,
|
|
~s, StdFont().Bold(), ink);
|
|
}
|
|
|
|
struct HomeDisplay : public Display {
|
|
virtual void Paint(Draw& w, const Rect& r, const Value& q,
|
|
Color ink, Color paper, dword style) const {
|
|
w.DrawRect(r, paper);
|
|
Image img = CtrlImg::Home();
|
|
w.DrawImage(r.left, r.top + (r.Height() - img.GetSize().cx) / 2,
|
|
CtrlImg::Home());
|
|
w.DrawText(r.left + Zx(20),
|
|
r.top + (r.Height() - StdFont().Bold().Info().GetHeight()) / 2,
|
|
String(q), StdFont().Bold(), ink);
|
|
}
|
|
};
|
|
|
|
void FileSel::Set(const String& s)
|
|
{
|
|
fn.Clear();
|
|
if(IsFullPath(s)) {
|
|
ActiveDir(GetFileFolder(s));
|
|
fn.Add(GetFileName(s));
|
|
}
|
|
else
|
|
fn.Add(s);
|
|
bidname = true;
|
|
}
|
|
|
|
void FileSel::GoToPlace()
|
|
{
|
|
if(places.IsCursor()) {
|
|
#ifdef PLATFORM_WIN32
|
|
netnode.Clear();
|
|
#endif
|
|
dir <<= places.GetKey();
|
|
Load();
|
|
}
|
|
}
|
|
|
|
Image SynthetisePathIcon(const String& path)
|
|
{
|
|
Size isz = DPI(16, 16);
|
|
ImagePainter iw(isz);
|
|
iw.Clear(RGBAZero());
|
|
int x = FoldHash(GetHashValue(path));
|
|
auto cl = [](int x) { return 128 + (x & 127); };
|
|
iw.Circle(DPI(8), DPI(8), DPI(7)).Fill(Color(cl(x), cl(x >> 7), cl(x >> 14))).Stroke(1, SBlack());
|
|
WString s = GetFileTitle(path).ToWString();
|
|
if(s.GetCount()) {
|
|
s = s.Mid(0, 1);
|
|
Font fnt = Serif(DPI(12));
|
|
Size tsz = GetTextSize(s, fnt);
|
|
iw.DrawText((isz.cx - tsz.cx) / 2, (isz.cy - tsz.cy) / 2, s, fnt, Black());
|
|
}
|
|
return iw;
|
|
}
|
|
|
|
String PathName(const String& path)
|
|
{
|
|
int cx = Zx(100);
|
|
String p = path;
|
|
p.Replace("\\", "/");
|
|
if(GetTextSize(p, StdFont()).cx < cx) return p;
|
|
cx -= GetTextSize("..", StdFont()).cx;
|
|
while(GetTextSize(p, StdFont()).cx > cx && p.GetCount() > 1)
|
|
p = p.Mid(1);
|
|
return ".." + p;
|
|
}
|
|
|
|
bool FileSel::Execute(int _mode) {
|
|
mode = _mode;
|
|
|
|
int fixed_places = places.GetCount();
|
|
|
|
Vector<String> glru;
|
|
LoadFromGlobal(glru, "GlobalFileSelectorLRU");
|
|
if(glru.GetCount()) {
|
|
AddPlaceSeparator();
|
|
for(const String& path : glru)
|
|
AddPlace(path, SynthetisePathIcon(path), PathName(path), "PLACES:FOLDER");
|
|
}
|
|
|
|
int system_row = -1;
|
|
for(int i = places.GetCount() - 1; i >= 0; i--) {
|
|
if(places.Get(i, 3) == "PLACES:SYSTEM") {
|
|
system_row = i;
|
|
places.Remove(i);
|
|
}
|
|
}
|
|
AddSystemPlaces(system_row);
|
|
|
|
if(mode == SELECTDIR) {
|
|
if(!fn.IsEmpty()) {
|
|
String h = ~dir;
|
|
dir <<= NormalizePath(fn[0]);
|
|
if(!DirectoryExists(~~dir))
|
|
dir <<= h;
|
|
}
|
|
type.Hide();
|
|
type_lbl.Hide();
|
|
file.Hide();
|
|
file_lbl.Hide();
|
|
sortby.Hide();
|
|
sort_lbl.Hide();
|
|
ok.SetLabel(t_("&Select"));
|
|
Logc p = filename.GetPos().y;
|
|
int q = ok.GetPos().y.GetA() + ok.GetPos().y.GetB() + Zy(16);
|
|
p.SetA(q);
|
|
filename.SetPosY(p);
|
|
filesize.SetPosY(p);
|
|
filetime.SetPosY(p);
|
|
p = splitter.Ctrl::GetPos().y;
|
|
p.SetB(q + Zy(28));
|
|
splitter.SetPosY(p);
|
|
LogPos ps = search.GetPos();
|
|
LogPos pl = sort_lbl.GetPos();
|
|
pl.x.SetB(ps.x.GetB());
|
|
pl.y.SetA(ok.GetPos().y.GetA());
|
|
pl.y.SetB(ps.y.GetB());
|
|
search.SetPos(pl);
|
|
bidname = false;
|
|
}
|
|
else {
|
|
for(Ctrl *q = GetFirstChild(); q; q = q->GetNext())
|
|
if(q != &mkdir)
|
|
q->Show();
|
|
Rect r = GetRect();
|
|
CtrlLayout(*this);
|
|
ArrangeOKCancel(ok, cancel);
|
|
SetRect(r);
|
|
}
|
|
|
|
if(file_ctrl) {
|
|
LogPos sp = search.GetPos();
|
|
LogPos fp = file.GetPos();
|
|
file.HSizePos(fp.x.GetA(), 2 * sp.x.GetA() + file_ctrl_cx);
|
|
AddChild(file_ctrl, &file);
|
|
file_ctrl->BottomPos(fp.y.GetA(), fp.y.GetB()).RightPos(sp.x.GetA(), file_ctrl_cx);
|
|
}
|
|
|
|
readonly.Show(rdonly && mode == OPEN);
|
|
list.Multi(multi && (mode == OPEN || mode == SELECTDIR));
|
|
list.SelectDir(multi && mode == SELECTDIR);
|
|
dir.ClearList();
|
|
file <<= Null;
|
|
if(basedir.IsEmpty()) {
|
|
dir.Add(GetHomeDirectory());
|
|
#ifdef PLATFORM_POSIX
|
|
Array<FileSystemInfo::FileInfo> root = filesystem->Find("/media/*");
|
|
dir.Add(GetDesktopFolder());
|
|
dir.Add("/");
|
|
for(int i = 0; i < root.GetCount(); i++) {
|
|
String ugly = root[i].filename;
|
|
if(ugly[0] != '.') {
|
|
dir.Add("/media/" + root[i].filename);
|
|
}
|
|
}
|
|
#else
|
|
dir.Add(GetDesktopFolder());
|
|
Array<FileSystemInfo::FileInfo> root = filesystem->Find(Null);
|
|
for(int i = 0; i < root.GetCount(); i++) {
|
|
String ugly = root[i].filename;
|
|
if(ugly != "A:\\" && ugly != "B:\\") {
|
|
ugly.Cat('\0');
|
|
ugly.Cat(root[i].root_style);
|
|
dir.Add(root[i].filename, ugly);
|
|
}
|
|
}
|
|
if(filesystem == &StdFileSystemInfo())
|
|
dir.Add("\\", String(t_("Network")) + String(0, 1) + "\xff");
|
|
#endif
|
|
if(filesystem->IsPosix() && String(~dir).IsEmpty())
|
|
dir <<= GetHomeDirectory();
|
|
if(lru.GetCount())
|
|
dir.AddSeparator();
|
|
for(int i = 0; i < lru.GetCount(); i++)
|
|
if(IsFullPath(lru[i]) && filesystem->FolderExists(lru[i]))
|
|
dir.Add(lru[i]);
|
|
dir.SetDisplay(Single<FolderDisplay>(), max(16, Draw::GetStdFontCy()));
|
|
}
|
|
else {
|
|
dir.SetDisplay(Single<HomeDisplay>(), max(16, Draw::GetStdFontCy()));
|
|
if(filesystem->IsPosix()) {
|
|
if(String(~dir)[0] == '/')
|
|
dir <<= "";
|
|
}
|
|
}
|
|
Rect lr = splitter.GetRect();
|
|
Rect dr = dir.GetRect();
|
|
int dp = max(20, dir.Ctrl::GetPos().y.GetB());
|
|
int px = GetSize().cx - lr.right;
|
|
/* if(IsMulti()) { // Cxl: Have we ever used these?!
|
|
toggle.RightPos(px, dp).TopPos(dr.top, dp);
|
|
minus.RightPos(px + 2 * dp, dp).TopPos(dr.top, dp);
|
|
plus.RightPos(px + 3 * dp, dp).TopPos(dr.top, dp);
|
|
px += 3 * dp;
|
|
toggle.Show();
|
|
minus.Show();
|
|
plus.Show();
|
|
}
|
|
else {*/
|
|
toggle.Hide();
|
|
minus.Hide();
|
|
plus.Hide();
|
|
// }
|
|
if(mkdir.IsShown()) {
|
|
mkdir.RightPos(px, dp).TopPos(dr.top, dp);
|
|
dirup.RightPos(px + dp, dp).TopPos(dr.top, dp);
|
|
px += 2 * dp;
|
|
}
|
|
else {
|
|
dirup.RightPos(px, dp).TopPos(dr.top, dp);
|
|
px += dp;
|
|
}
|
|
dir.HSizePos(dr.left, px + 4);
|
|
if(preselect.GetCount()) {
|
|
for(int i = 0; i < mask.GetCount(); i++) {
|
|
if(PatternMatchMulti(mask[i], preselect)) {
|
|
ActiveType(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
int q = type.FindValue(activetype);
|
|
if(q >= 0)
|
|
type <<= q;
|
|
else
|
|
if(type.GetCount())
|
|
type.SetIndex(0);
|
|
int dlc = type.GetCount();
|
|
Load();
|
|
ActiveFocus(file.IsEditable() ? (Ctrl&)file : (Ctrl&)list);
|
|
if(bidname) {
|
|
String s;
|
|
for(int i = 0; i < fn.GetCount(); i++)
|
|
Catq(s, fn[i]);
|
|
file <<= s;
|
|
ActiveFocus(file);
|
|
bidname = false;
|
|
}
|
|
list.SetSbPos(lastsby);
|
|
if(preselect.GetCount()) {
|
|
if(mode == SAVEAS)
|
|
file <<= preselect;
|
|
else
|
|
for(int i = 0; i < list.GetCount(); i++)
|
|
if(list[i].name == preselect) {
|
|
list.SetCursor(i);
|
|
ActiveFocus(list);
|
|
break;
|
|
}
|
|
preselect.Clear();
|
|
}
|
|
if(default_name.GetCount() && mode == SAVEAS)
|
|
file <<= default_name;
|
|
FileUpdate();
|
|
Update();
|
|
int c = TopWindow::Run(appmodal);
|
|
TopWindow::Close();
|
|
lastsby = list.GetSbPos();
|
|
if(IsNumber(~type)) {
|
|
int ti = ~type;
|
|
type.Trim(dlc);
|
|
if(ti >= 0 && ti < type.GetCount())
|
|
activetype = type.GetValue(ti);
|
|
}
|
|
else
|
|
type.Trim(dlc);
|
|
|
|
String d = ~dir;
|
|
if(filesystem->IsWin32() && d.GetLength() == 3 && d[1] == ':' ||
|
|
filesystem->IsPosix() && d == "/" ||
|
|
!IsFullPath(d))
|
|
d.Clear();
|
|
|
|
if(d.GetCount()) {
|
|
LruAdd(lru, d, 12);
|
|
Vector<String> glru;
|
|
d = NormalizePath(d);
|
|
Index<String> h;
|
|
for(int i = 0; i < fixed_places; i++)
|
|
h.Add(NormalizePath(~places.Get(i, 0)));
|
|
if(h.Find(d) < 0) {
|
|
LoadFromGlobal(glru, "GlobalFileSelectorLRU");
|
|
LruAdd(glru, d, 5);
|
|
StoreToGlobal(glru, "GlobalFileSelectorLRU");
|
|
}
|
|
}
|
|
|
|
places.SetCount(fixed_places);
|
|
|
|
return c == IDOK;
|
|
}
|
|
|
|
bool FileSel::ExecuteOpen(const char *title) {
|
|
Title(title ? title : t_("Open"));
|
|
return Execute(OPEN);
|
|
}
|
|
|
|
bool FileSel::ExecuteSaveAs(const char *title) {
|
|
Title(title ? title : t_("Save as"));
|
|
ok.SetLabel(t_("Save"));
|
|
return Execute(SAVEAS);
|
|
}
|
|
|
|
bool FileSel::ExecuteSelectDir(const char *title)
|
|
{
|
|
Title(title ? title : t_("Select directory"));
|
|
return Execute(SELECTDIR);
|
|
}
|
|
|
|
void FileSel::Serialize(Stream& s) {
|
|
#ifdef PLATFORM_WIN32
|
|
if(s.IsLoading()) {
|
|
netnode.Clear();
|
|
netstack.Clear();
|
|
}
|
|
#endif
|
|
int version = 10;
|
|
s / version;
|
|
String ad = ~dir;
|
|
int dummy = 0;
|
|
if(version < 10)
|
|
s / dummy;
|
|
else
|
|
s % activetype;
|
|
s % ad;
|
|
dir <<= ad;
|
|
if(version < 1) {
|
|
String n = fn.At(0);
|
|
s % n;
|
|
fn.At(0) = n;
|
|
}
|
|
else {
|
|
if(version < 4)
|
|
s % fn;
|
|
else {
|
|
Vector<String> __;
|
|
s % __;
|
|
}
|
|
}
|
|
if(version >= 2) {
|
|
SerializePlacement(s);
|
|
list.SerializeSettings(s);
|
|
}
|
|
if(version >= 3) {
|
|
s % lastsby;
|
|
}
|
|
if(version >= 4) {
|
|
s % lru;
|
|
}
|
|
if(version >= 5) {
|
|
s % sortby;
|
|
}
|
|
if(version >= 6) {
|
|
if(version >= 9)
|
|
s % splitter;
|
|
else {
|
|
Splitter dummy;
|
|
s % dummy;
|
|
}
|
|
}
|
|
if(version >= 7) {
|
|
s % hidden;
|
|
}
|
|
if(version >= 8) {
|
|
s % hiddenfiles;
|
|
}
|
|
}
|
|
|
|
String FileSel::GetFile(int i) const {
|
|
String p;
|
|
if(i >= 0 && i < fn.GetCount()) {
|
|
p = fn[i];
|
|
if(!IsFullPath(p))
|
|
p = AppendFileName(dir.GetData(), p);
|
|
}
|
|
#ifdef PLATFORM_WIN32
|
|
if(IsLnkFile(p))
|
|
p = Nvl(GetSymLinkPath(p), p);
|
|
#endif
|
|
return p;
|
|
}
|
|
|
|
void FileSel::SyncSplitter()
|
|
{
|
|
splitter.Clear();
|
|
if(places.GetCount() && basedir.IsEmpty())
|
|
splitter.Add(places);
|
|
splitter.Add(list);
|
|
if(preview)
|
|
splitter.Add(*preview);
|
|
}
|
|
|
|
FileSel& FileSel::PreSelect(const String& path)
|
|
{
|
|
ActiveDir(GetFileFolder(path));
|
|
preselect = GetFileName(path);
|
|
return *this;
|
|
}
|
|
|
|
void FileSel::InitSplitter()
|
|
{
|
|
int n = splitter.GetCount();
|
|
int i = 0;
|
|
if(places.GetCount())
|
|
splitter.SetPos(2000, i++);
|
|
splitter.SetPos(10000 - 2000 * (n - 1), i);
|
|
}
|
|
|
|
FileSel& FileSel::Preview(Ctrl& ctrl)
|
|
{
|
|
if(!preview) {
|
|
Size sz = GetRect().GetSize();
|
|
sz.cx = 5 * sz.cx / 3;
|
|
SetRect(sz);
|
|
}
|
|
preview = &ctrl;
|
|
SyncSplitter();
|
|
InitSplitter();
|
|
return *this;
|
|
}
|
|
|
|
FileSel& FileSel::Preview(const Display& d)
|
|
{
|
|
preview_display.SetDisplay(d);
|
|
return Preview(preview_display);
|
|
}
|
|
|
|
void FileSel::AddPlaceRaw(const String& path, const Image& m, const String& name, const char* group, int row)
|
|
{
|
|
if(path.GetCount()) {
|
|
row = row < 0 ? places.GetCount() : row;
|
|
places.Insert(row);
|
|
places.Set(row, 0, path);
|
|
places.Set(row, 1, DPI(m));
|
|
places.Set(row, 2, name);
|
|
places.Set(row, 3, group);
|
|
places.SetLineCy(row, max(m.GetSize().cy + 4, GetStdFontCy() + 4));
|
|
SyncSplitter();
|
|
InitSplitter();
|
|
}
|
|
}
|
|
|
|
FileSel& FileSel::AddPlace(const String& path, const Image& m, const String& name, const char* group, int row)
|
|
{
|
|
if(path.GetCount())
|
|
AddPlaceRaw(NormalizePath(path), DPI(m), name, group, row);
|
|
return *this;
|
|
}
|
|
|
|
FileSel& FileSel::AddPlace(const String& path, const String& name, const char* group, int row)
|
|
{
|
|
#ifdef GUI_COCOA
|
|
return AddPlace(path, GetFileIcon(NormalizePath(path), true, false, false), name, group, row);
|
|
#else
|
|
return AddPlace(path, GetDirIcon(NormalizePath(path)), name, group, row);
|
|
#endif
|
|
}
|
|
|
|
FileSel& FileSel::AddPlace(const String& path, const char* group, int row)
|
|
{
|
|
return AddPlace(path, GetFileTitle(path), group, row);
|
|
}
|
|
|
|
FileSel& FileSel::AddPlaceSeparator()
|
|
{
|
|
places.AddSeparator();
|
|
SyncSplitter();
|
|
InitSplitter();
|
|
return *this;
|
|
}
|
|
|
|
FileSel& FileSel::ClearPlaces()
|
|
{
|
|
places.Clear();
|
|
SyncSplitter();
|
|
return *this;
|
|
}
|
|
|
|
void FileSel::AddSystemPlaces(int row)
|
|
{
|
|
row = row < 0 ? places.GetCount() : row;
|
|
Array<FileSystemInfo::FileInfo> root;
|
|
#ifdef PLATFORM_WIN32
|
|
root = filesystem->Find(Null);
|
|
for(int i = 0; i < root.GetCount(); i++) {
|
|
String desc = root[i].root_desc;
|
|
String n = root[i].filename;
|
|
if(n != "A:\\" && n != "B:\\") {
|
|
#ifdef PLATFORM_WIN32
|
|
if(*n.Last() == '\\')
|
|
n.Trim(n.GetCount() - 1);
|
|
#endif
|
|
if(desc.GetCount() == 0)
|
|
desc << " " << t_("Local Disk");
|
|
desc << " (" << n << ")";
|
|
AddPlace(root[i].filename, GetDriveImage(root[i].root_style), desc, "PLACES:SYSTEM", row++);
|
|
}
|
|
}
|
|
|
|
if(GetSystemMetrics(SM_REMOTESESSION))
|
|
for(int drive = 'A'; drive < 'Z'; drive++) {
|
|
String path = Format("\\\\tsclient\\%c", drive);
|
|
if(FindFile(path + "\\*.*"))
|
|
AddPlace(path, Format(t_("%c on client"), drive), "PLACES:SYSTEM", row++);
|
|
}
|
|
#endif
|
|
|
|
#ifdef PLATFORM_POSIX
|
|
root = filesystem->Find("/media/*");
|
|
for(int i = 0; i < root.GetCount(); i++) {
|
|
String fn = root[i].filename;
|
|
if(*fn != '.' && fn.Find("floppy") < 0)
|
|
AddPlace("/media/" + fn, fn, "PLACES:SYSTEM", row++);
|
|
}
|
|
AddPlace("/", t_("Computer"), "PLACES:SYSTEM", row++);
|
|
#endif
|
|
}
|
|
|
|
FileSel& FileSel::AddStandardPlaces()
|
|
{
|
|
AddPlace(GetHomeDirectory(), t_("Home"), "PLACES:FOLDER");
|
|
#ifdef GUI_COCOA
|
|
AddPlace(GetSpecialDirectory(SF_NSDesktopDirectory), t_("Desktop"), "PLACES:FOLDER");
|
|
AddPlace(GetSpecialDirectory(SF_NSMusicDirectory), t_("Music"), "PLACES:FOLDER");
|
|
AddPlace(GetSpecialDirectory(SF_NSPicturesDirectory), t_("Pictures"), "PLACES:FOLDER");
|
|
AddPlace(GetSpecialDirectory(SF_NSMoviesDirectory), t_("Videos"), "PLACES:FOLDER");
|
|
AddPlace(GetSpecialDirectory(SF_NSDocumentDirectory), t_("Documents"), "PLACES:FOLDER");
|
|
AddPlace(GetSpecialDirectory(SF_NSDownloadsDirectory), t_("Downloads"), "PLACES:FOLDER");
|
|
#else
|
|
AddPlace(GetDesktopFolder(), t_("Desktop"), "PLACES:FOLDER");
|
|
AddPlace(GetMusicFolder(), t_("Music"), "PLACES:FOLDER");
|
|
AddPlace(GetPicturesFolder(), t_("Pictures"), "PLACES:FOLDER");
|
|
AddPlace(GetVideoFolder(), t_("Videos"), "PLACES:FOLDER");
|
|
AddPlace(GetDocumentsFolder(), t_("Documents"), "PLACES:FOLDER");
|
|
AddPlace(GetDownloadFolder(), t_("Downloads"), "PLACES:FOLDER");
|
|
#endif
|
|
AddPlaceSeparator();
|
|
AddSystemPlaces();
|
|
#ifdef PLATFORM_WIN32
|
|
AddPlaceSeparator();
|
|
AddPlaceRaw("\\", CtrlImg::Network(), t_("Network"), "PLACES:NETWORK");
|
|
#endif
|
|
return *this;
|
|
}
|
|
|
|
struct DisplayPlace : Display {
|
|
virtual void Paint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper,
|
|
dword style) const
|
|
{
|
|
w.DrawRect(r, paper);
|
|
ValueArray va = q;
|
|
Image m = va[0];
|
|
String txt = va[1];
|
|
Size isz = m.GetSize();
|
|
w.DrawImage(r.left, r.top + (r.Height() - isz.cy) / 2, m);
|
|
w.DrawText(r.left + isz.cx + 2, r.top + (r.Height() - GetStdFontCy()) / 2, txt,
|
|
StdFont(), ink);
|
|
}
|
|
virtual Size GetStdSize(const Value& q) const {
|
|
ValueArray va = q;
|
|
Image m = va[0];
|
|
String txt = va[1];
|
|
Size isz = m.GetSize();
|
|
return Size(isz.cx + GetTextSize(txt, StdFont()).cx + 2, max(isz.cy, GetStdFontCy()));
|
|
}
|
|
};
|
|
|
|
FileSel::FileSel()
|
|
{
|
|
loaded = false;
|
|
filesystem = &StdFileSystemInfo();
|
|
CtrlLayout(*this);
|
|
ArrangeOKCancel(ok, cancel);
|
|
Acceptor(ok, IDOK); ok.Ok();
|
|
Rejector(cancel, IDCANCEL); cancel.Cancel();
|
|
list.IconWidth(DPI(16)).Renaming().Columns(3).ClickKill();
|
|
list.WhenLeftDouble = THISBACK(OpenItem2);
|
|
dirup <<= THISBACK(DirUp);
|
|
Add(dirup);
|
|
sortby <<= THISBACK(SearchLoad);
|
|
Add(sortby);
|
|
hidden <<= THISBACK(SearchLoad);
|
|
Add(hidden);
|
|
hiddenfiles <<= THISBACK(SearchLoad);
|
|
Add(hiddenfiles);
|
|
mkdir <<= THISBACK(MkDir);
|
|
Add(mkdir);
|
|
plus <<= THISBACK(Plus);
|
|
Add(plus);
|
|
minus <<= THISBACK(Minus);
|
|
Add(minus);
|
|
toggle <<= THISBACK(Toggle);
|
|
Add(toggle);
|
|
|
|
ok <<= THISBACK(Open);
|
|
list <<= THISBACK(Update);
|
|
file <<= THISBACK(FileUpdate);
|
|
list.WhenRename = THISBACK(Rename);
|
|
Sizeable();
|
|
dirup.SetImage(CtrlImg::DirUp()).NoWantFocus();
|
|
dirup.Tip(t_("Dir up") + String(" (Ctrl+Up)"));
|
|
mkdir.SetImage(CtrlImg::MkDir()).NoWantFocus();
|
|
mkdir.Tip(t_("Create directory") + String(" (F7)"));
|
|
plus.SetImage(CtrlImg::Plus()).NoWantFocus();
|
|
plus.Tip(t_("Select files"));
|
|
minus.SetImage(CtrlImg::Minus()).NoWantFocus();
|
|
minus.Tip(t_("Unselect files"));
|
|
toggle.SetImage(CtrlImg::Toggle()).NoWantFocus();
|
|
toggle.Tip(t_("Toggle files"));
|
|
type <<= THISBACK(Load);
|
|
for(int pass = 0; pass < 2; pass++) {
|
|
int k = pass * FILELISTSORT_DESCENDING;
|
|
String d = pass ? t_(" descending") : "";
|
|
sortby.Add(FILELISTSORT_NAME|k, t_("Name") + d);
|
|
sortby.Add(FILELISTSORT_EXT|k, t_("Extension") + d);
|
|
sortby.Add(FILELISTSORT_TIME|k, t_("Last change") + d);
|
|
sortby.Add(FILELISTSORT_SIZE|k, t_("Size") + d);
|
|
}
|
|
sortby <<= FILELISTSORT_NAME;
|
|
|
|
search.NullText(t_("Search"), StdFont().Italic(), SColorDisabled());
|
|
search.SetFilter(CharFilterDefaultToUpperAscii);
|
|
search <<= THISBACK(SearchLoad);
|
|
|
|
filename.SetFont(StdFont());
|
|
filename.SetFrame(ViewFrame());
|
|
filesize.SetFont(StdFont()).SetAlign(ALIGN_RIGHT);
|
|
filesize.SetFrame(ViewFrame());
|
|
filetime.SetFont(StdFont());
|
|
filetime.SetFrame(ViewFrame());
|
|
|
|
dir <<= THISBACK(Choice);
|
|
dir.DisplayAll();
|
|
dir.SetDropLines(24);
|
|
|
|
readonly.Hide();
|
|
|
|
lastsby = 0;
|
|
|
|
asking = true;
|
|
rdonly = false;
|
|
multi = false;
|
|
bidname = false;
|
|
appmodal = true;
|
|
|
|
AddChildBefore(GetFirstChild(), &sizegrip);
|
|
|
|
preview = NULL;
|
|
preview_display.SetFrame(FieldFrame());
|
|
|
|
SyncSplitter();
|
|
|
|
BackPaintHint();
|
|
|
|
places.AddKey();
|
|
places.AddColumn().AddIndex().SetDisplay(Single<DisplayPlace>());
|
|
places.AddIndex();
|
|
places.NoHeader().NoGrid();
|
|
places.WhenLeftClick = THISBACK(GoToPlace);
|
|
places.NoWantFocus();
|
|
|
|
#ifdef PLATFORM_WIN32
|
|
int icx = GetFileIcon(GetHomeDirectory(), true, false, false).GetSize().cx;
|
|
if(icx)
|
|
list.IconWidth(icx);
|
|
#endif
|
|
|
|
AddStandardPlaces();
|
|
|
|
list.AutoHideSb();
|
|
places.AutoHideSb();
|
|
|
|
WhenIconLazy = NULL;
|
|
}
|
|
|
|
FileSel::~FileSel() {}
|
|
|
|
}
|