1 #include <CtrlLib/CtrlLib.h>
2 
3 namespace Upp {
4 
5 #ifdef GUI_WIN
6 void AvoidPaintingCheck__();
7 
ProcessSHIcon(HICON hIcon)8 Image ProcessSHIcon(HICON hIcon)
9 {
10 	AvoidPaintingCheck__();
11 	Color c = White();
12 	Image m[2];
13 	for(int i = 0; i < 2; i++) {
14 		ICONINFO iconinfo;
15 		if(!hIcon || !GetIconInfo(hIcon, &iconinfo))
16 			return Image();
17 		BITMAP bm;
18 		::GetObject((HGDIOBJ)iconinfo.hbmMask, sizeof(BITMAP), (LPVOID)&bm);
19 		Size sz(bm.bmWidth, bm.bmHeight);
20 		ImageDraw iw(sz);
21 		iw.DrawRect(sz, c);
22 		::DrawIconEx(iw.GetHandle(), 0, 0, hIcon, 0, 0, 0, NULL, DI_NORMAL|DI_COMPAT);
23 		::DeleteObject(iconinfo.hbmColor);
24 		::DeleteObject(iconinfo.hbmMask);
25 		c = Black();
26 		m[i] = iw;
27 	}
28 	::DestroyIcon(hIcon);
29 	return RecreateAlpha(m[0], m[1]);
30 }
31 
32 struct FileIconMaker : ImageMaker {
33 	String file;
34 	bool   exe;
35 	bool   dir;
36 	bool   large;
37 
KeyUpp::FileIconMaker38 	virtual String Key() const {
39 		return file + (exe ? "1" : "0") + (dir ? "1" : "0");
40 	}
41 
MakeUpp::FileIconMaker42 	virtual Image Make() const {
43 		SHFILEINFO info;
44 		AvoidPaintingCheck__();
45 		SHGetFileInfo(ToSystemCharset(file), dir ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL,
46 		              &info, sizeof(info),
47 		              SHGFI_ICON|(large ? SHGFI_LARGEICON : SHGFI_SMALLICON)|(exe ? 0 : SHGFI_USEFILEATTRIBUTES));
48 		return ProcessSHIcon(info.hIcon);
49 	}
50 };
51 
52 
GetFileIcon(const char * path,bool dir,bool force,bool large,bool quick=false)53 Image GetFileIcon(const char *path, bool dir, bool force, bool large, bool quick = false)
54 {
55 	FileIconMaker m;
56 	String ext = GetFileExt(path);
57 	m.exe = false;
58 	m.dir = false;
59 	m.file = path;
60 	m.large = large;
61 	if(force)
62 		m.exe = true;
63 	else
64 	if(dir) {
65 		m.dir = true;
66 		m.exe = true;
67 	}
68 	else
69 	if(findarg(ext, ".exe", ".lnk") >= 0)
70 		m.exe = true;
71 	else
72 		m.file = "x" + ext;
73 	if(quick) {
74 		m.exe = false;
75 		m.file = "x" + ext;
76 	}
77 	return MakeImage(m);
78 }
79 
80 #define GETFILEICON_DEFINED
81 
82 #endif
83 
84 #if defined(GUI_GTK)
85 
86 Image GtkThemeIcon(const char *name, int sz);
87 
GnomeImage(const char * s,bool large=false)88 Image GnomeImage(const char *s, bool large = false)
89 {
90 	return GtkThemeIcon(String("gnome-") + s, DPI(large ? 48 : 16));
91 }
92 
SystemImage(const char * s,bool large=false)93 Image SystemImage(const char *s, bool large = false)
94 {
95 	return GtkThemeIcon(s, DPI(large ? 48 : 16));
96 }
97 
98 struct ExtToMime {
99 	Index<String> major;
100 	Index<String> minor;
101 	VectorMap<String, dword> map;
102 
103 	void Load(const char *file);
104 	void Refresh();
105 	bool GetMime(const String& ext, String& maj, String& min);
106 };
107 
Load(const char * fn)108 void ExtToMime::Load(const char *fn)
109 {
110 	FileIn in(fn);
111 	if(in)
112 		while(!in.IsEof()) {
113 			String ln = TrimLeft(TrimRight(in.GetLine()));
114 			if(ln[0] != '#') {
115 				int q = ln.Find(':');
116 				if(q >= 0) {
117 					String h = ln.Mid(0, q);
118 					int w = h.Find('/');
119 					if(w >= 0) {
120 						int x = ln.Find("*.", q);
121 						if(x >= 0) {
122 							String ext = ln.Mid(x + 2);
123 							if(ext.GetCount() && map.Find(ext) < 0)
124 								map.Add(ext, MAKELONG(minor.FindAdd(h.Mid(w + 1)), major.FindAdd(h.Mid(0, w))));
125 						}
126 					}
127 				}
128 			}
129 		}
130 }
131 
Refresh()132 void ExtToMime::Refresh()
133 {
134 	major.Clear();
135 	minor.Clear();
136 	map.Clear();
137 	Load("/usr/local/share/mime/globs");
138 	Load("/usr/share/mime/globs");
139 }
140 
GetMime(const String & ext,String & maj,String & min)141 bool ExtToMime::GetMime(const String& ext, String& maj, String& min)
142 {
143 	ONCELOCK {
144 		Refresh();
145 	}
146 	int q = map.Find(ext);
147 	if(q < 0)
148 		return false;
149 	dword x = map[q];
150 	maj = major[HIWORD(x)];
151 	min = minor[LOWORD(x)];
152 	return true;
153 }
154 
155 struct FileExtMaker : ImageMaker {
156 	String ext;
157 	bool   large;
158 
KeyUpp::FileExtMaker159 	virtual String Key() const {
160 		return ext;
161 	}
162 
MakeUpp::FileExtMaker163 	virtual Image Make() const {
164 		String major;
165 		String minor;
166 		if(!Single<ExtToMime>().GetMime(ext, major, minor))
167 			return Null;
168 		Image img = SystemImage(major + '-' + minor, large);
169 		return IsNull(img) ? SystemImage(major) : img;
170 	}
171 };
172 
PosixGetDriveImage(String dir,bool large)173 Image PosixGetDriveImage(String dir, bool large)
174 {
175 	static bool init = false;
176 	static Image cdrom;
177 	static Image lcdrom;
178 	static Image harddisk;
179 	static Image lharddisk;
180 	static Image floppy;
181 	static Image lfloppy;
182 	static Image computer;
183 	static Image lcomputer;
184 
185 	if (!init) {
186 		bool KDE = Environment().Get("KDE_FULL_SESSION", String()) == "true";
187 		if (KDE) {
188 			cdrom     = SystemImage("media-optical");
189 			lcdrom    = SystemImage("media-optical", true);
190 			harddisk  = SystemImage("drive-harddisk");
191 			lharddisk = SystemImage("drive-harddisk", true);
192 			floppy    = SystemImage("media-floppy");
193 			lfloppy   = SystemImage("media-floppy", true);
194 			computer  = SystemImage("computer");
195 			lcomputer = SystemImage("computer", true);
196 		}
197 		else {
198 			cdrom     = GnomeImage("dev-cdrom");
199 			lcdrom    = GnomeImage("dev-cdrom", true);
200 			harddisk  = GnomeImage("dev-harddisk");
201 			lharddisk = GnomeImage("dev-harddisk", true);
202 			floppy    = GnomeImage("dev-floppy");
203 			lfloppy   = GnomeImage("dev-floppy", true);
204 			computer  = GnomeImage("dev-computer");
205 			lcomputer = GnomeImage("dev-computer", true);
206 		}
207 
208 		init = true;
209 	}
210 	if(dir.GetCount() == 0 || dir == "/") {
211 		Image m = large ? lcomputer : computer;
212 		return IsNull(m) ? CtrlImg::Computer() : m;
213 	}
214 	if(dir.Find("cdrom") == 0 || dir.Find("cdrecorder") == 0) {
215 		Image m = large ? lcdrom : cdrom;
216 		return IsNull(m) ? CtrlImg::CdRom() : m;
217 	}
218 	if(dir.Find("floppy") == 0 || dir.Find("zip") == 0) {
219 		Image m = large ? lfloppy : floppy;
220 		return IsNull(m) ? CtrlImg::Diskette() : m;
221 	}
222 	Image m = large ? lharddisk : harddisk;
223 	return IsNull(m) ? CtrlImg::Hd() : m;
224 }
225 
GetFileIcon(const String & folder,const String & filename,bool isdir,bool isexe,bool large)226 Image GetFileIcon(const String& folder, const String& filename, bool isdir, bool isexe, bool large)
227 {
228 	static bool init = false;
229 	static bool KDE  = Environment().Get("KDE_FULL_SESSION", String()) == "true";
230 
231 	static Image file;
232 	static Image lfile;
233 	static Image dir;
234 	static Image ldir;
235 	static Image exe;
236 	static Image lexe;
237 	static Image home;
238 	static Image lhome;
239 	static Image desktop;
240 	static Image ldesktop;
241 	static Image music;
242 	static Image lmusic;
243 	static Image pictures;
244 	static Image lpictures;
245 	static Image video;
246 	static Image lvideo;
247 	static Image documents;
248 	static Image ldocuments;
249 	static Image download;
250 	static Image ldownload;
251 	static Image help;
252 	static Image lhelp;
253 	static Image translation;
254 	static Image ltranslation;
255 	static Image layout;
256 	static Image llayout;
257 
258 	static Image fileImage;
259 	static Image fileMusic  = SystemImage("audio-x-generic");
260 	static Image fileScript = SystemImage("text-x-script");
261 
262 	if (!init) {
263 		if (KDE) {
264 			file         = SystemImage("text-plain");
265 			lfile        = SystemImage("text-plain", true);
266 			dir          = SystemImage("folder");
267 			ldir         = SystemImage("folder", true);
268 			exe          = SystemImage("application-x-executable");
269 			lexe         = SystemImage("application-x-executable", true);
270 			home         = SystemImage("user-home");
271 			lhome        = SystemImage("user-home", true);
272 			desktop      = SystemImage("user-desktop");
273 			ldesktop     = SystemImage("user-desktop", true);
274 			music        = SystemImage("folder-sound");
275 			lmusic       = SystemImage("folder-sound", true);
276 			pictures     = SystemImage("folder-image");
277 			lpictures    = SystemImage("folder-image", true);
278 			video        = SystemImage("folder-video");
279 			lvideo       = SystemImage("folder-video", true);
280 			documents    = SystemImage("folder-documents");
281 			ldocuments   = SystemImage("folder-documents", true);
282 			download     = SystemImage("folder-downloads");
283 			ldownload    = SystemImage("folder-downloads", true);
284 			help         = SystemImage("help-contents");
285 			lhelp        = SystemImage("help-contents", true);
286 			translation  = SystemImage("applications-education-language");
287 			ltranslation = SystemImage("applications-education-language", true);
288 			layout       = SystemImage("applications-development");
289 			llayout      = SystemImage("applications-development", true);
290 
291 			fileImage    = SystemImage("application-x-egon");
292 		}
293 		else {
294 			file         = GnomeImage("fs-regular");
295 			lfile        = GnomeImage("fs-regular", true);
296 			dir          = GnomeImage("fs-directory");
297 			ldir         = GnomeImage("fs-directory", true);
298 			exe          = GnomeImage("fs-executable");
299 			lexe         = GnomeImage("fs-executable", true);
300 			home         = GnomeImage("fs-home");
301 			lhome        = GnomeImage("fs-home", true);
302 			desktop      = GnomeImage("fs-desktop");
303 			ldesktop     = GnomeImage("fs-desktop", true);
304 			music        = SystemImage("folder-music");
305 			lmusic       = SystemImage("folder-music", true);
306 			pictures     = SystemImage("folder-pictures");
307 			lpictures    = SystemImage("folder-pictures", true);
308 			video        = SystemImage("folder-video");
309 			lvideo       = SystemImage("folder-video", true);
310 			documents    = SystemImage("folder-documents");
311 			ldocuments   = SystemImage("folder-documents", true);
312 			download     = SystemImage("folder-downloads");
313 			ldownload    = SystemImage("folder-downloads", true);
314 			help         = SystemImage("help");
315 			lhelp        = SystemImage("help", true);
316 			translation  = SystemImage("preferences-desktop-locale");
317 			ltranslation = SystemImage("preferences-desktop-locale", true);
318 			layout       = SystemImage("applications-development");
319 			llayout      = SystemImage("applications-development", true);
320 
321 			fileImage    = SystemImage("image-x-generic");
322 		}
323 
324 		init = true;
325 	}
326 	if (filename == "Help Topics")
327 		return large ? lhelp : help;
328 	if(isdir) {
329 		Image img = dir;
330 		if(AppendFileName(folder, filename) == GetHomeDirectory())
331 			return large ? lhome : home;
332 		else
333 		if(AppendFileName(folder, filename) ==  GetDesktopFolder ())
334 			return large ? ldesktop : desktop;
335 		else
336 		if(AppendFileName(folder, filename) == GetMusicFolder ())
337 			return large ? lmusic : music;
338 		else
339 		if(AppendFileName(folder, filename) == GetPicturesFolder())
340 			return large ? lpictures : pictures;
341 		else
342 		if(AppendFileName(folder, filename) == GetVideoFolder())
343 			return large ? lvideo : video;
344 		else
345 		if(AppendFileName(folder, filename) == GetDocumentsFolder())
346 			return large ? ldocuments : documents;
347 		else
348 		if(AppendFileName(folder, filename) == GetDownloadFolder())
349 			return large ? ldownload : download;
350 		else
351 		if(folder == "/media" || filename.GetCount() == 0)
352 			return PosixGetDriveImage(filename, large);
353 		return dir;
354 	}
355 	FileExtMaker m;
356 	m.ext = GetFileExt(filename);
357 	for (int i = 1; i < m.ext.GetCount(); ++i)
358 		m.ext.Set (i, ToLower(m.ext[i]));
359 
360 	// Fixing format problems
361 	if (m.ext == ".gz") m.ext = ".tar.gz";
362 
363 	// Ultimate++ - files extensions
364 	if (m.ext == ".t" || m.ext == ".lng") return large ? ltranslation : translation;
365 	if (m.ext == ".lay") return large ? llayout : layout;
366 	if (m.ext == ".iml") return fileImage;
367 	if (m.ext == ".usc") return fileScript;
368 
369 	// Binary - files extensions (It seems that KDE has problem with multimedia MIME icons handling)
370 	if (KDE) {
371 		if (m.ext == ".bmp" || m.ext == ".dib" ||
372 			m.ext == ".gif" ||
373 			m.ext == ".jpg" || m.ext == ".jpeg" || m.ext == ".jpe" ||
374 			m.ext == ".png" ||
375 			m.ext == ".tif" || m.ext == ".tiff" ||
376 			m.ext == ".svg" ||
377 			m.ext == ".ico" ||
378 			m.ext == ".xcf")
379 			return fileImage;
380 		if (m.ext == ".aac" || m.ext == ".ogg" || m.ext == ".mp3")  return fileMusic;
381 	}
382 
383 	Image img;
384 	if(m.ext.GetCount()) {
385 		m.ext = m.ext.Mid(1);
386 		m.large = large;
387 		img = MakeImage(m);
388 		isexe = false;
389 	}
390 	return IsNull(img) ? isexe ? (large ? lexe : exe) : (large ? lfile : file) : img;
391 }
392 
393 #define GETFILEICON_DEFINED
394 
395 #endif
396 
397 #ifdef PLATFORM_COCOA
398 struct FileIconMaker : ImageMaker {
399 	String file;
400 	bool   exe;
401 	bool   dir;
402 	bool   large;
403 
KeyUpp::FileIconMaker404 	virtual String Key() const {
405 		return file + (exe ? "1" : "0") + (dir ? "1" : "0");
406 	}
407 
MakeUpp::FileIconMaker408 	virtual Image Make() const {
409 		return GetIconForFile(file);
410 	}
411 };
412 
413 #define GETFILEICON_DEFINED
414 
GetFileIcon(const char * path,bool dir,bool exe,bool large,bool quick=false)415 Image GetFileIcon(const char *path, bool dir, bool exe, bool large, bool quick = false)
416 {
417 	FileIconMaker m;
418 	m.exe = exe;
419 	m.dir = dir;
420 	m.file = path;
421 	m.large = large;
422 	return MakeImage(m);
423 }
424 #endif
425 
426 #ifndef GETFILEICON_DEFINED
PosixGetDriveImage(String dir,bool)427 Image PosixGetDriveImage(String dir, bool)
428 {
429 	if(dir.GetCount() == 0 || dir == "/")
430 		return CtrlImg::Computer();
431 	if(dir.Find("cdrom") == 0 || dir.Find("cdrecorder") == 0)
432 		return CtrlImg::CdRom();
433 	if(dir.Find("floppy") == 0 || dir.Find("zip") == 0)
434 		return CtrlImg::Diskette();
435 	return CtrlImg::Hd();
436 }
437 
438 #ifdef PLATFORM_WIN32
GetFileIcon(const String & folder,bool,bool,bool,bool=false)439 Image GetFileIcon(const String& folder, bool, bool, bool, bool = false)
440 {
441 	return CtrlImg::File();
442 }
443 #else
GetFileIcon(const String & folder,const String & filename,bool isdir,bool isexe,bool=false)444 Image GetFileIcon(const String& folder, const String& filename, bool isdir, bool isexe, bool = false)
445 {
446 	return isdir ? CtrlImg::Dir() : CtrlImg::File();
447 }
448 
449 #endif
450 #endif
451 
NativePathIcon0(const char * path,bool folder,bool large)452 Image NativePathIcon0(const char *path, bool folder, bool large)
453 {
454 #if defined(PLATFORM_WIN32)
455 	if (folder)
456 		return GetFileIcon(path, true, true, large);
457 	else
458 		return GetFileIcon(path, false, false, large);
459 #endif
460 #ifdef PLATFORM_POSIX
461 	String p = path;
462 	FindFile ff(path);
463 #ifdef PLATFORM_COCOA
464 	return GetFileIcon(path, folder, ff.GetMode() & 0111, large);
465 #else
466 	bool isdrive = folder && ((p == "/media") || (p == "/mnt"));
467 	return isdrive ? PosixGetDriveImage(GetFileName(path), large)
468 				   : GetFileIcon(path, GetFileName(path), folder, ff.GetMode() & 0111, large);
469 #endif
470 #endif
471 }
472 
NativePathIcon(const char * path,bool folder)473 Image NativePathIcon(const char *path, bool folder)
474 {
475 	return NativePathIcon0(path, folder, false);
476 }
477 
NativePathIcon(const char * path)478 Image NativePathIcon(const char *path)
479 {
480 	FindFile ff(path);
481 	return NativePathIcon(path, ff.IsFolder());
482 }
483 
NativePathLargeIcon(const char * path,bool folder)484 Image NativePathLargeIcon(const char *path, bool folder)
485 {
486 	return NativePathIcon0(path, folder, true);
487 }
488 
NativePathLargeIcon(const char * path)489 Image NativePathLargeIcon(const char *path)
490 {
491 	FindFile ff(path);
492 	return NativePathLargeIcon(path, ff.IsFolder());
493 }
494 
MatchSearch(const String & filename,const String & search)495 bool MatchSearch(const String& filename, const String& search)
496 {
497 	return search.GetCount() ? Filter(filename, CharFilterDefaultToUpperAscii).Find(search) >= 0 : true;
498 }
499 
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)500 bool Load(FileList& list, const String& dir, const char *patterns, bool dirs,
501           Event<bool, const String&, Image&> WhenIcon, FileSystemInfo& filesystem,
502           const String& search, bool hidden, bool hiddenfiles, bool lazyicons)
503 {
504 	if(dir.IsEmpty()) {
505 		Array<FileSystemInfo::FileInfo> root = filesystem.Find(Null);
506 		for(int i = 0; i < root.GetCount(); i++)
507 			if(MatchSearch(root[i].filename, search))
508 				list.Add(root[i].filename,
509 					GetDriveImage(root[i].root_style),
510 					StdFont().Bold(), SColorText, true, -1, Null, SColorDisabled,
511 			#ifdef PLATFORM_WIN32
512 					Nvl(root[i].root_desc, String(" ") + t_("Local Disk")),
513 			#else
514 					root[i].root_desc,
515 			#endif
516 					StdFont()
517 				);
518 		#ifdef PLATFORM_WIN32
519 			list.Add(t_("Network"), CtrlImg::Network(), StdFont().Bold(), SColorText,
520 			         true, -1, Null, SColorDisabled, Null, StdFont());
521 		#endif
522 	}
523 	else {
524 		Array<FileSystemInfo::FileInfo> ffi =
525 			filesystem.Find(AppendFileName(dir, filesystem.IsWin32() ? "*.*" : "*"));
526 		if(ffi.IsEmpty())
527 			return false;
528 	#if defined(PLATFORM_POSIX) && !defined(PLATFORM_COCOA)
529 		bool isdrive = dir == "/media" || dir == "/mnt";
530 	#endif
531 		for(int t = 0; t < ffi.GetCount(); t++) {
532 			const FileSystemInfo::FileInfo& fi = ffi[t];
533 			bool nd = dirs && !fi.is_directory;
534 			bool show = hidden;
535 			if(!show && filesystem.IsWin32() ? !fi.is_hidden : fi.filename[0] != '.')
536 				show = true;
537 			if(!show && hiddenfiles && fi.is_file)
538 				show = true;
539 			if(fi.filename != "." && fi.filename != ".." &&
540 			#ifdef PLATFORM_WIN32
541 			   (fi.is_directory || FileSel::IsLnkFile(fi.filename) || PatternMatchMulti(patterns, fi.filename)) &&
542 			#else
543 			   (fi.is_directory || PatternMatchMulti(patterns, fi.filename)) &&
544 			#endif
545 			   MatchSearch(fi.filename, search) && show) {
546 				Image img;
547 			#ifdef PLATFORM_POSIX
548 			#ifdef PLATFORM_COCOA
549 				img = GetFileIcon(AppendFileName(dir, fi.filename), fi.is_directory, fi.unix_mode & 0111, false, lazyicons);
550 			#else
551 				img = isdrive ? PosixGetDriveImage(fi.filename, false)
552 				              : GetFileIcon(dir, fi.filename, fi.is_directory, fi.unix_mode & 0111, false);
553 			#endif
554 			#endif
555 			#ifdef GUI_WIN
556 				img = GetFileIcon(AppendFileName(dir, fi.filename), fi.is_directory, false, false, lazyicons);
557 			#endif
558 				if(IsNull(img))
559 					img = fi.is_directory ? CtrlImg::Dir() : CtrlImg::File();
560 				WhenIcon(fi.is_directory, fi.filename, img);
561 				list.Add(fi.filename, fi.is_hidden ? Contrast(img, 200) : img,
562 						 StdFont().Bold(fi.is_directory),
563 						 nd ? SColorDisabled : fi.is_hidden ? Blend(SColorText, Gray, 200) : SColorText, fi.is_directory,
564 						 fi.is_directory ? -1 : fi.length,
565 						 fi.last_write_time,
566 						 nd ? SColorDisabled
567 						    : fi.is_directory ? SColorText
568 						                      : fi.is_hidden ? Blend(SColorMark, Gray, 200)
569 						                                     : SColorMark,
570 				         Null, Null, Null, Null,
571 #ifdef PLATFORM_WIN32
572                          false,
573 #else
574 						 fi.unix_mode & 0111,
575 #endif
576 				         fi.is_hidden
577 				);
578 			}
579 		}
580 	}
581 	return true;
582 }
583 
584 #ifdef GUI_WIN
585 static Mutex       sExeMutex;
586 static wchar       sExePath[1025];
587 static bool        sExeRunning;
588 static SHFILEINFOW sExeInfo;
589 
sExeIconThread(void *)590 static auxthread_t auxthread__ sExeIconThread(void *)
591 {
592 	SHFILEINFOW info;
593 	wchar path[1025];
594 	CoInitialize(NULL);
595 	sExeMutex.Enter();
596 	wcscpy(path, sExePath);
597 	sExeMutex.Leave();
598 	AvoidPaintingCheck__();
599 	SHGetFileInfoW(path, FILE_ATTRIBUTE_NORMAL, &info, sizeof(info), SHGFI_ICON|SHGFI_SMALLICON);
600 	sExeMutex.Enter();
601 	memcpy(&sExeInfo, &info, sizeof(info));
602 	sExeRunning = false;
603 	sExeMutex.Leave();
604 	return 0;
605 }
606 
Done(Image img)607 void LazyExeFileIcons::Done(Image img)
608 {
609 	if(pos >= ndx.GetCount())
610 		return;
611 	int ii = ndx[pos];
612 	if(ii < 0 || ii >= list->GetCount())
613 		return;
614 	const FileList::File& f = list->Get(ii);
615 	WhenIcon(false, f.name, img);
616 	if(!IsNull(img)) {
617 		if(f.hidden)
618 			img = Contrast(img, 200);
619 		list->SetIcon(ii, img);
620 	}
621 	pos++;
622 }
623 
Path()624 WString LazyExeFileIcons::Path()
625 {
626 	if(pos >= ndx.GetCount())
627 		return Null;
628 	int ii = ndx[pos];
629 	if(ii < 0 || ii >= list->GetCount())
630 		return Null;
631 	const FileList::File& f = list->Get(ii);
632 	return NormalizePath(AppendFileName(dir, f.name)).ToWString();
633 }
634 
Do()635 void LazyExeFileIcons::Do()
636 {
637 	int start = msecs();
638 	for(;;) {
639 		for(;;) {
640 			SHFILEINFOW info;
641 			bool done = false;
642 			WString path = Path();
643 			if(IsNull(path))
644 				return;
645 			sExeMutex.Enter();
646 			bool running = sExeRunning;
647 			if(!running) {
648 				done = path == sExePath;
649 				memcpy(&info, &sExeInfo, sizeof(info));
650 				*sExePath = '\0';
651 				memset(&sExeInfo, 0, sizeof(sExeInfo));
652 			}
653 			sExeMutex.Leave();
654 			Image img = ProcessSHIcon(info.hIcon);
655 			if(done)
656 				Done(img);
657 			if(!running)
658 				break;
659 			Sleep(0);
660 			if(msecs(start) > 10 || Ctrl::IsWaitingEvent()) {
661 				Restart(0);
662 				return;
663 			}
664 		}
665 
666 		WString path = Path();
667 		if(IsNull(path))
668 			return;
669 		sExeMutex.Enter();
670 		memcpy(sExePath, ~path, 2 * min(1024, path.GetCount() + 1));
671 		sExeRunning = true;
672 		StartAuxThread(sExeIconThread, NULL);
673 		sExeMutex.Leave();
674 	}
675 }
676 
ReOrder()677 void LazyExeFileIcons::ReOrder()
678 { // gather .exe and .lnk files; sort based on length so that small .exe get resolved first
679 	ndx.Clear();
680 	Vector<int> len;
681 	for(int i = 0; i < list->GetCount(); i++) {
682 		const FileList::File& f = list->Get(i);
683 		if(findarg(ToLower(GetFileExt(f.name)), ".exe", ".lnk") >= 0 && !f.isdir) {
684 			ndx.Add(i);
685 			len.Add((int)min((int64)INT_MAX, f.length));
686 		}
687 	}
688 	IndexSort(len, ndx);
689 	Restart(0);
690 }
691 
Start(FileList & list_,const String & dir_,Event<bool,const String &,Image &> WhenIcon_)692 void LazyExeFileIcons::Start(FileList& list_, const String& dir_, Event<bool, const String&, Image&> WhenIcon_)
693 {
694 	list = &list_;
695 	dir = dir_;
696 	WhenIcon = WhenIcon_;
697 	pos = 0;
698 	ReOrder();
699 }
700 #endif
701 
GetDir() const702 String FileSel::GetDir() const
703 {
704 	String s = ~dir;
705 	if(s.IsEmpty()) return basedir;
706 	if(basedir.IsEmpty()) return s;
707 	return AppendFileName(basedir, s);
708 }
709 
SetDir(const String & _dir)710 void FileSel::SetDir(const String& _dir) {
711 #ifdef PLATFORM_WIN32
712 	netstack.Clear();
713 	netnode.Clear();
714 #endif
715 	dir <<= _dir;
716 	Load();
717 	Update();
718 }
719 
FilePath(const String & fn)720 String FileSel::FilePath(const String& fn) {
721 	return AppendFileName(GetDir(), fn);
722 }
723 
GetDriveImage(char drive_style)724 Image GetDriveImage(char drive_style)
725 {
726 	switch(drive_style)
727 	{
728 	case FileSystemInfo::ROOT_NO_ROOT_DIR: return Null;
729 	case FileSystemInfo::ROOT_REMOTE:
730 	case FileSystemInfo::ROOT_NETWORK:   return CtrlImg::Share();
731 	case FileSystemInfo::ROOT_COMPUTER:  return CtrlImg::Computer();
732 	case FileSystemInfo::ROOT_REMOVABLE: return CtrlImg::Flash();
733 	case FileSystemInfo::ROOT_CDROM:     return CtrlImg::CdRom();
734 	default:                             return CtrlImg::Hd();
735 	}
736 }
737 
GetMask()738 String FileSel::GetMask()
739 {
740 	String emask = "*";
741 	if(!IsNull(type)) {
742 		if(IsString(~type))
743 			emask = ~type;
744 		else {
745 			int q = ~type;
746 			if(q >= 0 && q < mask.GetCount())
747 				emask = mask[q];
748 		}
749 	}
750 	return emask;
751 }
752 
Load()753 void FileSel::Load()
754 {
755 	search <<= Null;
756 	SearchLoad();
757 }
758 
LoadNet()759 void FileSel::LoadNet()
760 {
761 #ifdef PLATFORM_WIN32
762 	list.Clear();
763 	for(int i = 0; i < netnode.GetCount(); i++) {
764 		Image m = CtrlImg::Group();
765 		switch(netnode[i].GetDisplayType()) {
766 		case NetNode::NETWORK:
767 			m = CtrlImg::Network();
768 			break;
769 		case NetNode::SHARE:
770 			m = CtrlImg::Share();
771 			break;
772 		case NetNode::SERVER:
773 			m = CtrlImg::Computer();
774 			break;
775 		}
776 		list.Add(netnode[i].GetName(), m);
777 	}
778 	places.FindSetCursor("\\");
779 #endif
780 }
781 
SelectNet()782 void FileSel::SelectNet()
783 {
784 #ifdef PLATFORM_WIN32
785 	int q = list.GetCursor();
786 	if(q >= 0 && q < netnode.GetCount()) {
787 		NetNode& n = netnode[q];
788 		String p = n.GetPath();
789 		if(p.GetCount())
790 			SetDir(p);
791 		else {
792 			netstack.Add() = netnode[q];
793 			netnode = netstack.Top().Enum();
794 			LoadNet();
795 		}
796 	}
797 #endif
798 }
799 
SearchLoad()800 void FileSel::SearchLoad()
801 {
802 	loaded = true;
803 	list.EndEdit();
804 	list.Clear();
805 	String d = GetDir();
806 #ifdef PLATFORM_WIN32
807 	if(d == "\\") {
808 		netnode = NetNode::EnumRoot();
809 		netnode.Append(NetNode::EnumRemembered());
810 		LoadNet();
811 		return;
812 	}
813 #endif
814 	String emask = GetMask();
815 	if(!UPP::Load(list, d, emask, mode == SELECTDIR, WhenIcon, *filesystem, ~search, ~hidden, ~hiddenfiles, true)) {
816 		loaded = false;
817 		Exclamation(t_("[A3* Unable to read the directory !]&&") + DeQtf((String)~dir) + "&&" +
818 		            GetErrorMessage(GetLastError()));
819 		if(!basedir.IsEmpty() && String(~dir).IsEmpty()) {
820 			Break(IDCANCEL);
821 			return;
822 		}
823 		dir <<= olddir;
824 		olddir = Null;
825 		SearchLoad();
826 	}
827 
828 	places.KillCursor();
829 	if(d.GetCount())
830 		places.FindSetCursor(d);
831 	hiddenfiles.Enable(!hidden);
832 	if(d.IsEmpty()) {
833 		if(filesystem->IsWin32()) {
834 			mkdir.Disable();
835 			plus.Disable();
836 			minus.Disable();
837 			toggle.Disable();
838 			list.Renaming(false);
839 		}
840 		dir <<= d;
841 		dirup.Disable();
842 	}
843 	else {
844 		dirup.Enable();
845 		mkdir.Enable();
846 		plus.Enable();
847 		minus.Enable();
848 		toggle.Enable();
849 		list.Renaming(true);
850 	}
851 	if(filesystem->IsPosix())
852 		if(d == "/" || !IsEmpty(basedir) && String(~dir).IsEmpty())
853 			dirup.Disable();
854 	if(filesystem->IsWin32())
855 		if(!IsEmpty(basedir) && String(~dir).IsEmpty())
856 			dirup.Disable();
857 	olddir = ~dir;
858 	if(olddir.GetCount() || basedir.GetCount())
859 		SortBy(list, ~sortby);
860 	Update();
861 #ifdef GUI_WIN
862 	lazyicons.Start(list, d, WhenIcon);
863 #endif
864 	StartLI();
865 }
866 
867 StaticMutex FileSel::li_mutex;
868 void      (*FileSel::li_current)(const String& path, Image& result);
869 String      FileSel::li_path;
870 Image       FileSel::li_result;
871 bool        FileSel::li_running;
872 int         FileSel::li_pos;
873 
LIThread()874 void FileSel::LIThread()
875 {
876 	String path;
877 	void (*li)(const String& path, Image& result);
878 	{
879 		Mutex::Lock __(li_mutex);
880 		path = li_path;
881 		li = li_current;
882 	}
883 	Image result;
884 	if(path.GetCount())
885 		li(path, result);
886 	if(!IsNull(result) && max(result.GetWidth(), result.GetHeight()) > DPI(16))
887 		result = Rescale(result, DPI(16), DPI(16));
888 	{
889 		Mutex::Lock __(li_mutex);
890 		li_result = result;
891 		li_running = false;
892 	}
893 }
894 
LIPath()895 String FileSel::LIPath()
896 {
897 	return li_pos >= 0 && li_pos < list.GetCount() ? FilePath(list.Get(li_pos).name) : Null;
898 }
899 
DoLI()900 void FileSel::DoLI()
901 {
902 	int start = msecs();
903 	for(;;) {
904 		for(;;) {
905 			bool done = false;
906 			String path = LIPath();
907 			if(IsNull(path))
908 				return;
909 			bool running;
910 			Image img;
911 			{
912 				Mutex::Lock __(li_mutex);
913 				running = li_running;
914 				if(!running) {
915 					done = li_path == path && li_current == WhenIconLazy;
916 					img = li_result;
917 				}
918 			}
919 			if(done) {
920 				if(li_pos < 0 || li_pos >= list.GetCount())
921 					return;
922 				if(!IsNull(img)) {
923 					const FileList::File& f = list.Get(li_pos);
924 					WhenIcon(f.isdir, f.name, img);
925 					if(f.hidden)
926 						img = Contrast(img, 200);
927 					list.SetIcon(li_pos, img);
928 				}
929 				li_pos++;
930 			}
931 			if(!running)
932 				break;
933 			Sleep(0);
934 			if(msecs(start) > 10 || Ctrl::IsWaitingEvent()) {
935 				ScheduleLI();
936 				return;
937 			}
938 		}
939 
940 		String path = LIPath();
941 		if(IsNull(path))
942 			return;
943 		{
944 			Mutex::Lock __(li_mutex);
945 			if(!li_running) {
946 				li_current = WhenIconLazy;
947 				li_path = path;
948 				li_running = true;
949 				Thread::Start(callback(LIThread));
950 			}
951 		}
952 	}
953 }
954 
StartLI()955 void FileSel::StartLI()
956 {
957 	if(WhenIconLazy) {
958 		li_pos = 0;
959 		ScheduleLI();
960 	}
961 }
962 
TrimDot(String f)963 String TrimDot(String f) {
964 	int i = f.Find('.');
965 	if(i >= 0 && i == f.GetLength() - 1)
966 		f.Trim(i);
967 	return f;
968 }
969 
AddName(Vector<String> & fn,String & f)970 void FileSel::AddName(Vector<String>& fn, String& f) {
971 	if(!f.IsEmpty()) {
972 		f = TrimDot(f);
973 		if(f[0] == '\"' && f.GetCount() > 2)
974 			f = f.Mid(1, f.GetCount() - 2);
975 		if(f.Find('.') < 0) {
976 			String t = GetMask();
977 			int q = t.Find('.');
978 			if(q >= 0 && IsAlNum(t[q + 1])) {
979 				int w = q + 2;
980 				while(IsAlNum(t[w]))
981 					w++;
982 				f << t.Mid(q, w - q);
983 			}
984 			else
985 			if(defext.GetCount())
986 				f << '.' << defext;
987 		}
988 		fn.Add(f);
989 	}
990 	f.Clear();
991 }
992 
IsLnkFile(const String & p)993 bool FileSel::IsLnkFile(const String& p)
994 {
995 	int l = p.GetLength() - 4;
996 	return l >= 0 && p[l] == '.' && ToLower(p[l + 1]) == 'l' && ToLower(p[l + 2]) == 'n' && ToLower(p[l + 3]) == 'k';
997 }
998 
ResolveLnk(const String & name) const999 String FileSel::ResolveLnk(const String& name) const
1000 {
1001 #ifdef PLATFORM_WIN32
1002 	if(IsLnkFile(name))
1003 		return GetSymLinkPath(AppendFileName(GetDir(), name));
1004 #endif
1005 	return Null;
1006 }
1007 
ResolveLnkDir(const String & name) const1008 String FileSel::ResolveLnkDir(const String& name) const
1009 {
1010 #ifdef PLATFORM_WIN32
1011 	String p = ResolveLnk(name);
1012 	if(p.GetCount() && DirectoryExists(p))
1013 		return p;
1014 #endif
1015 	return Null;
1016 }
1017 
ResolveLnkFile(const String & name) const1018 String FileSel::ResolveLnkFile(const String& name) const
1019 {
1020 #ifdef PLATFORM_WIN32
1021 	String p = ResolveLnk(name);
1022 	if(p.GetCount() && FileExists(p))
1023 		return p;
1024 #endif
1025 	return Null;
1026 }
1027 
Finish()1028 void FileSel::Finish() {
1029 	if(filesystem->IsWin32())
1030 		if(GetDir().IsEmpty()) {
1031 			file.Clear();
1032 			return;
1033 		}
1034 	fn.Clear();
1035 	if(mode == SELECTDIR) {
1036 		String p = GetDir();
1037 		if(list.IsSelection() && multi) {
1038 			for(int i = 0; i < list.GetCount(); i++)
1039 				if(list.IsSelected(i)) {
1040 					const FileList::File& m = list[i];
1041 					if(m.isdir)
1042 						fn.Add(AppendFileName(p, m.name));
1043 				#ifdef PLATFORM_WIN32
1044 					else {
1045 						String p = ResolveLnkDir(m.name);
1046 						if(p.GetCount())
1047 							fn.Add(p);
1048 					}
1049 				#endif
1050 				}
1051 		}
1052 		else {
1053 			String p = GetDir();
1054 			if(list.GetCursor() >= 0) {
1055 				const FileList::File& m = list[list.GetCursor()];
1056 				if(m.isdir)
1057 					p = AppendFileName(p, m.name);
1058 			#ifdef PLATFORM_WIN32
1059 				else {
1060 					String pp = ResolveLnkDir(m.name);
1061 					if(p.GetCount())
1062 						p = pp;
1063 				}
1064 			#endif
1065 			}
1066 			fn.Add(p);
1067 		}
1068 		Break(IDOK);
1069 		return;
1070 	}
1071 	String f = file.GetText().ToString();
1072 	if(f.IsEmpty()) return;
1073 	String o;
1074 	if(mode == OPEN && IsMulti()) {
1075 		for(const char *s = f; *s; s++) {
1076 			if(*s == ' ')
1077 				AddName(fn, o);
1078 			else
1079 			if(*s == '\"') {
1080 				AddName(fn, o);
1081 				s++;
1082 				for(;;) {
1083 					if(*s == '\0' || *s == '\"') {
1084 						AddName(fn, o);
1085 						break;
1086 					}
1087 					o.Cat(*s++);
1088 				}
1089 			}
1090 			else
1091 				o.Cat(*s);
1092 		}
1093 		AddName(fn, o);
1094 	}
1095 	else {
1096 		o = f;
1097 		AddName(fn, o);
1098 	}
1099 	if(!IsMulti() && fn.GetCount())
1100 		fn.SetCount(1);
1101 	String d = GetDir();
1102 	String nonexist;
1103 	int ne = 0;
1104 	for(int i = 0; i < fn.GetCount(); i++) {
1105 		String p = fn[i];
1106 		if(!IsFullPath(p))
1107 			p = NormalizePath(AppendFileName(d, fn[i]));
1108 		Array<FileSystemInfo::FileInfo> ff = filesystem->Find(p, 1);
1109 		p = DeQtf(p);
1110 		if(!ff.IsEmpty() && ff[0].is_directory) {
1111 			Exclamation(p + t_(" is directory."));
1112 			return;
1113 		}
1114 		if(asking) {
1115 			if(mode == SAVEAS) {
1116 				if(!ff.IsEmpty() && !PromptOKCancel(p + t_(" already exists.&Do you want to continue ?")))
1117 					return;
1118 			}
1119 			else
1120 			if(ff.IsEmpty()) {
1121 				if(ne) nonexist << '&';
1122 				nonexist << p;
1123 				ne++;
1124 			}
1125 		}
1126 	}
1127 	if(ne) {
1128 		nonexist << (ne == 1 ? t_(" does not exist.") : t_("&do not exist."));
1129 		if(!PromptOKCancel(nonexist + t_("&Do you want to continue ?")))
1130 			return;
1131 	}
1132 	Break(IDOK);
1133 }
1134 
OpenItem()1135 bool FileSel::OpenItem() {
1136 	if(list.IsCursor()) {
1137 	#ifdef PLATFORM_WIN32
1138 		if(netnode.GetCount()) {
1139 			SelectNet();
1140 			return true;
1141 		}
1142 	#endif
1143 		const FileList::File& m = list.Get(list.GetCursor());
1144 		String path = AppendFileName(~dir, m.name);
1145 	#ifdef PLATFORM_WIN32
1146 		if(IsNull(dir) && m.name == t_("Network")) {
1147 			netnode = NetNode::EnumRoot();
1148 			netnode.Append(NetNode::EnumRemembered());
1149 			LoadNet();
1150 			return true;
1151 		}
1152 		String p = ResolveLnkDir(m.name);
1153 		if(p.GetCount()) {
1154 			SetDir(p);
1155 			return true;
1156 		}
1157 	#endif
1158 		if(m.isdir) {
1159 			SetDir(path);
1160 			return true;
1161 		}
1162 	}
1163 	if(mode != SELECTDIR)
1164 		Finish();
1165 	return false;
1166 }
1167 
Open()1168 void FileSel::Open() {
1169 	if(mode == SELECTDIR) {
1170 	#ifdef PLATFORM_WIN32
1171 		if(netnode.GetCount())
1172 			return;
1173 	#endif
1174 		Finish();
1175 		return;
1176 	}
1177 	if(list.HasFocus() || type.HasFocus()) {
1178 		if(OpenItem()) list.SetCursor(0);
1179 	}
1180 	else
1181 	if(list.IsSelection())
1182 		for(int i = 0; i < list.GetCount(); i++) {
1183 			const FileList::File& m = list[i];
1184 			if(!m.isdir) Finish();
1185 		}
1186 	else
1187 	if(file.HasFocus()) {
1188 		String fn = file.GetText().ToString();
1189 	#ifdef PLATFORM_WIN32
1190 		if(fn[0] == '\\' && fn[1] == '\\') {
1191 			FindFile ff(AppendFileName(fn, "*.*"));
1192 			if(ff)
1193 				SetDir(TrimDot(fn));
1194 			return;
1195 		}
1196 	#endif
1197 		if(fn == "." || fn == "..") {
1198 			DirUp();
1199 			return;
1200 		}
1201 		if(HasWildcards(fn)) {
1202 			file.Clear();
1203 			int q = FindIndex(mask, fn);
1204 			if(q >= 0)
1205 				type.SetIndex(q);
1206 			else {
1207 				type.Add(fn, t_("Custom file type (") + fn + ')');
1208 				type.SetIndex(type.GetCount() - 1);
1209 			}
1210 			Load();
1211 			return;
1212 		}
1213 		if(fn.Find('\"') < 0) {
1214 			if(filesystem->IsWin32())
1215 			{
1216 				if(fn.GetLength() >= 2 && fn[1] == ':' && fn.GetLength() <= 3) {
1217 					fn.Set(0, ToUpper(fn[0]));
1218 					if(fn.GetLength() == 2)
1219 						fn.Cat('\\');
1220 					SetDir(fn);
1221 					return;
1222 				}
1223 			}
1224 			if(!IsFullPath(fn))
1225 				fn = AppendFileName(~dir, fn);
1226 			if(filesystem->IsWin32() && (!fn.IsEmpty() && (*fn.Last() == '\\' || *fn.Last() == '/'))
1227 			|| filesystem->IsPosix() && (fn != "/" && (*fn.Last() == '\\' || *fn.Last() == '/')))
1228 			{
1229 				fn.Trim(fn.GetLength() - 1);
1230 				SetDir(TrimDot(fn));
1231 				return;
1232 			}
1233 			Array<FileSystemInfo::FileInfo> ff = filesystem->Find(fn, 1);
1234 			if(!ff.IsEmpty()) {
1235 				if(ff[0].is_directory) {
1236 					SetDir(TrimDot(fn));
1237 					return;
1238 				}
1239 				else {
1240 					SetDir(TrimDot(GetFileFolder(fn)));
1241 					file.SetText(GetFileName(fn).ToWString());
1242 				}
1243 			}
1244 		}
1245 		if(mode != SELECTDIR) Finish();
1246 	}
1247 }
1248 
DirectoryUp(String & dir,bool basedir)1249 String DirectoryUp(String& dir, bool basedir)
1250 {
1251 	while(*dir.Last() == '\\' || *dir.Last() == '/')
1252 		dir.Trim(dir.GetCount() - 1);
1253 	String s = dir;
1254 	String name;
1255 #ifdef PLATFORM_WIN32
1256 	if(s.GetLength() < 3 || s.GetLength() == 3 && s[1] == ':') {
1257 		dir.Clear();
1258 		name = s;
1259 	}
1260 	else
1261 #endif
1262 #ifdef PLATFORM_POSIX
1263 	if(s != "/")
1264 #endif
1265 	{
1266 #ifdef PLATFORM_WIN32
1267 		int i = max(s.ReverseFind('/'), s.ReverseFind('\\'));
1268 #endif
1269 #ifdef PLATFORM_POSIX
1270 		int i = s.ReverseFind('/');
1271 #endif
1272 		if(basedir)
1273 			if(i < 0)
1274 				dir.Clear();
1275 			else {
1276 				dir = s.Mid(0, i);
1277 				name = s.Mid(i + 1);
1278 			}
1279 		else {
1280 #ifdef PLATFORM_WIN32
1281 			if(s.GetLength() && s[1] == ':') {
1282 				if(i > 3) {
1283 					dir = s.Mid(0, i);
1284 					name = s.Mid(i + 1);
1285 				}
1286 				else {
1287 					dir = s.Mid(0, 3);
1288 					name = s.Mid(3);
1289 				}
1290 			}
1291 			if(s.GetLength() && s[0] == DIR_SEP && s[1] == DIR_SEP) {
1292 				if(i > 2) {
1293 					dir = s.Mid(0, i);
1294 					name = s.Mid(i + 1);
1295 				}
1296 				else {
1297 					dir.Clear();
1298 					name = s;
1299 				}
1300 			}
1301 #endif
1302 #ifdef PLATFORM_POSIX
1303 			if(i == 0 && s.GetLength() > 1) {
1304 				dir = "/";
1305 				name = s.Mid(1);
1306 			}
1307 			else
1308 			if(s.GetLength() && s[0] == '/' && s[1] != '/') {
1309 				dir = s.Mid(0, i);
1310 				name = s.Mid(i + 1);
1311 			}
1312 #endif
1313 		}
1314 	}
1315 	return name;
1316 }
1317 
DirUp()1318 void FileSel::DirUp() {
1319 #ifdef PLATFORM_WIN32
1320 	if(netstack.GetCount()) {
1321 		netstack.Drop();
1322 		if(netstack.GetCount()) {
1323 			netnode = netstack.Top().Enum();
1324 			LoadNet();
1325 		}
1326 		netnode = NetNode::EnumRoot();
1327 		return;
1328 	}
1329 	if(netnode.GetCount()) {
1330 		netnode.Clear();
1331 		SetDir("");
1332 		return;
1333 	}
1334 #endif
1335 	String s = ~dir;
1336 	String name = DirectoryUp(s, !basedir.IsEmpty());
1337 #ifdef PLATFORM_WIN32
1338 	if(s[0] == '\\' && s[1] == '\\' && s.Find('\\', 2) < 0) {
1339 		s.Clear();
1340 		name.Clear();
1341 	}
1342 #endif
1343 	SetDir(s);
1344 	if(list.HasFocus())
1345 		list.FindSetCursor(name);
1346 }
1347 
MkDir()1348 void FileSel::MkDir() {
1349 	if(String(~dir).IsEmpty() && basedir.IsEmpty()) return;
1350 	String name, error;
1351 	if(EditText(name, t_("New directory"), t_("Name")) && !name.IsEmpty()) {
1352 		if(filesystem->CreateFolder(FilePath(name), error)) {
1353 			Load();
1354 			list.FindSetCursor(name);
1355 		}
1356 		else
1357 			Exclamation(t_("[A3* Creating directory failed !&&]") + error);
1358 	}
1359 }
1360 
PlusMinus(const char * title,bool sel)1361 void FileSel::PlusMinus(const char *title, bool sel) {
1362 	String pattern;
1363 	if(EditText(pattern, title, t_("Mask")) && !pattern.IsEmpty())
1364 		for(int i = 0; i < list.GetCount(); i++)
1365 			if(!list.Get(i).isdir)
1366 				if(PatternMatchMulti(pattern, list.Get(i).name))
1367 					list.SelectOne(i, sel);
1368 }
1369 
Plus()1370 void FileSel::Plus() {
1371 	PlusMinus(t_("Add to selection"), true);
1372 }
1373 
Minus()1374 void FileSel::Minus() {
1375 	PlusMinus(t_("Remove from selection"), false);
1376 }
1377 
Toggle()1378 void FileSel::Toggle() {
1379 	for(int i = 0; i < list.GetCount(); i++)
1380 		if(!list.Get(i).isdir)
1381 			list.SelectOne(i, !list.IsSelected(i));
1382 }
1383 
Reload()1384 void FileSel::Reload()
1385 {
1386 	String fn = list.GetCurrentName();
1387 	int a = list.GetScroll();
1388 	SearchLoad();
1389 	list.ScrollTo(a);
1390 	list.FindSetCursor(fn);
1391 }
1392 
Activate()1393 void FileSel::Activate()
1394 {
1395 	if(loaded)
1396 		Reload();
1397 	TopWindow::Activate();
1398 }
1399 
Key(dword key,int count)1400 bool FileSel::Key(dword key, int count) {
1401 	switch(key) {
1402 	case '.':
1403 	case K_CTRL_UP:
1404 		list.SetFocus();
1405 		dirup.PseudoPush();
1406 		return true;
1407 	case '+':
1408 		plus.PseudoPush();
1409 		return true;
1410 	case '-':
1411 		minus.PseudoPush();
1412 		return true;
1413 	case '*':
1414 		toggle.PseudoPush();
1415 		return true;
1416 	case K_F5:
1417 		Reload();
1418 		return true;
1419 	case K_F6:
1420 		list.StartEdit();
1421 		return true;
1422 	case K_F7:
1423 		mkdir.PseudoPush();
1424 		return true;
1425 	case K_ENTER:
1426 		if(mode == SELECTDIR && OpenItem())
1427 			return true;
1428 		break;
1429 	case K_UP:
1430 	case K_DOWN:
1431 	case K_PAGEUP:
1432 	case K_PAGEDOWN:
1433 		list.SetFocus();
1434 		return list.Key(key, count);
1435 	}
1436 	if(CharFilterDefaultToUpperAscii(key) || key == K_BACKSPACE)
1437 		return search.Key(key, count);
1438 	return TopWindow::Key(key, count);
1439 }
1440 
Catq(String & s,const String & fn)1441 void Catq(String& s, const String& fn) {
1442 	if(!s.IsEmpty())
1443 		s << ' ';
1444 	if(fn.Find(' ') >= 0)
1445 		s << '"' << fn << '"';
1446 	else
1447 		s << fn;
1448 }
1449 
FormatFileSize(int64 n)1450 String FormatFileSize(int64 n)
1451 {
1452 	if(n < 10000)
1453 		return Format("%d B  ", n);
1454 	else
1455 	if(n < 10000 * 1024)
1456 		return Format("%d.%d K  ", n >> 10, (n & 1023) / 103);
1457 	else
1458 	if(n < I64(10000000) * 1024)
1459 		return Format("%d.%d M  ", n >> 20, (n & 1023) / 103);
1460 	else
1461 		return Format("%d.%d G  ", n >> 30, (n & 1023) / 103);
1462 }
1463 
Update()1464 void FileSel::Update() {
1465 	String fn;
1466 	if(list.IsSelection()) {
1467 		for(int i = 0; i < list.GetCount(); i++)
1468 			if(list.IsSelected(i))
1469 				Catq(fn, list[i].name);
1470 	}
1471 	else
1472 	if(list.IsCursor()) {
1473 		const FileList::File& m = list[list.GetCursor()];
1474 		if(!m.isdir)
1475 			Catq(fn, m.name);
1476 	}
1477 	if(mode == OPEN)
1478 		file <<= fn;
1479 	filename = String();
1480 	filesize = String();
1481 	filetime = String();
1482 	if(preview)
1483 		*preview <<= Null;
1484 	if(list.IsCursor()) {
1485 		fn = list[list.GetCursor()].name;
1486 		if(fn[1] == ':' && fn.GetLength() <= 3)
1487 			filename = t_("  Drive");
1488 		else {
1489 			String path = FilePath(fn);
1490 			Array<FileSystemInfo::FileInfo> ff = filesystem->Find(path, 1);
1491 			if(!ff.IsEmpty()) {
1492 				filename = "  " + fn;
1493 				if(ff[0].is_directory)
1494 					filesize = t_("Directory  ");
1495 				else {
1496 					if(mode == SAVEAS)
1497 						file <<= fn;
1498 					filesize = FormatFileSize(ff[0].length);
1499 					if(preview)
1500 						*preview <<= path;
1501 				}
1502 				Time tm = ff[0].last_write_time;
1503 				filetime = "     " + Format(tm);
1504 			}
1505 		}
1506 	}
1507 	else {
1508 		int drives = 0;
1509 		int dirs = 0;
1510 		int files = 0;
1511 		int64 length = 0;
1512 		for(int i = 0; i < list.GetCount(); i++)
1513 			if(!list.IsSelection() || list.IsSelected(i)) {
1514 				const FileList::File& f = list[i];
1515 				if(f.isdir)
1516 #ifdef PLATFORM_WIN32
1517 					(*f.name.Last() == ':' ? drives : dirs)++;
1518 #else
1519 					dirs++;
1520 #endif
1521 				else {
1522 					files++;
1523 					length += f.length;
1524 				}
1525 			}
1526 		String s;
1527 		if(drives)
1528 			s << drives << t_(" drive(s)");
1529 		else {
1530 			if(dirs)
1531 				s << dirs << t_(" folder(s)");
1532 			if(files) {
1533 				if(s.GetCount())
1534 					s << ", ";
1535 				s << files << t_(" file(s)");
1536 			}
1537 		}
1538 		filename = "  " + s;
1539 		if(length >= 0)
1540 			filesize = FormatFileSize(length);
1541 	}
1542 	FileUpdate();
1543 }
1544 
FileUpdate()1545 void FileSel::FileUpdate() {
1546 	if(mode == SELECTDIR) {
1547 		ok.Enable(!IsNull(~dir));
1548 		return;
1549 	}
1550 	bool b = list.IsCursor() || !String(~file).IsEmpty();
1551 	ok.Enable(b);
1552 	if(mode != SAVEAS || list.IsCursor() && list[list.GetCursor()].isdir)
1553 		ok.SetLabel(t_("Open"));
1554 	else
1555 		ok.SetLabel(t_("Save"));
1556 }
1557 
Rename(const String & on,const String & nn)1558 void FileSel::Rename(const String& on, const String& nn) {
1559 	if(on == nn) return;
1560 #ifdef PLATFORM_WIN32
1561 	if(FileMove(FilePath(on), FilePath(nn)))
1562 #else
1563 	if(rename(FilePath(on), FilePath(nn)) == 0)
1564 #endif
1565 	{
1566 		Load();
1567 		list.FindSetCursor(nn);
1568 	}
1569 	else
1570 		Exclamation(t_("[A3* Renaming of file failed !&&]") + GetErrorMessage(GetLastError()));
1571 }
1572 
Choice()1573 void FileSel::Choice() {
1574 	Load();
1575 }
1576 
Type(const char * name,const char * ext)1577 FileSel& FileSel::Type(const char *name, const char *ext) {
1578 	type.Add(type.GetCount(), name);
1579 	mask.Add(ext);
1580 	if(IsNull(type))
1581 		type.SetIndex(0);
1582 	return *this;
1583 }
1584 
Types(const char * d)1585 FileSel& FileSel::Types(const char *d) {
1586 	Vector<String> s = Split(d, '\n');
1587 	for(int i = 0; i < s.GetCount(); i++) {
1588 		Vector<String> h = Split(s[i], '\t');
1589 		if(h.GetCount() == 2)
1590 			Type(h[0], h[1]);
1591 		if(h.GetCount() == 1)
1592 			Type(h[0], h[0]);
1593 	}
1594 	return *this;
1595 }
1596 
ClearTypes()1597 FileSel& FileSel::ClearTypes()
1598 {
1599 	type.Clear();
1600 	mask.Clear();
1601 	return *this;
1602 }
1603 
ActiveType(int i)1604 FileSel& FileSel::ActiveType(int i)
1605 {
1606 	activetype.Clear();
1607 	if(i >= 0 && i < type.GetCount())
1608 		activetype = type.GetValue(i);
1609 	return *this;
1610 }
1611 
AllFilesType()1612 FileSel& FileSel::AllFilesType() {
1613 	return Type(t_("All files"), "*.*");
1614 }
1615 
1616 struct FolderDisplay : public Display {
1617 	virtual void Paint(Draw& w, const Rect& r, const Value& q,
1618 	                   Color ink, Color paper, dword style) const;
1619 };
1620 
GetDirIcon(const String & s)1621 Image GetDirIcon(const String& s)
1622 {
1623 	Image img;
1624 #ifdef PLATFORM_X11
1625 	img = GetFileIcon(GetFileFolder(s), GetFileName(s), true, false, false);
1626 #endif
1627 #ifdef PLATFORM_WIN32
1628 	if((byte)*s.Last() == 255)
1629 		img = CtrlImg::Network();
1630 	else {
1631 		int q = s.Find(0);
1632 		if(q >= 0 && q + 1 < s.GetCount())
1633 			img = GetDriveImage(s[q + 1]);
1634 		else
1635 			img = s.GetCount() ? GetFileIcon(s, false, true, false) : CtrlImg::Computer();
1636 	}
1637 #endif
1638 	if(IsNull(img))
1639 		img = CtrlImg::Dir();
1640 	return DPI(img);
1641 }
1642 
Paint(Draw & w,const Rect & r,const Value & q,Color ink,Color paper,dword style) const1643 void FolderDisplay::Paint(Draw& w, const Rect& r, const Value& q,
1644                           Color ink, Color paper, dword style) const
1645 {
1646 	String s = q;
1647 	w.DrawRect(r, paper);
1648 	Image img = GetDirIcon(s);
1649 	w.DrawImage(r.left, r.top + (r.Height() - img.GetSize().cx) / 2, img);
1650 	w.DrawText(r.left + Zx(20),
1651 	           r.top + (r.Height() - StdFont().Bold().Info().GetHeight()) / 2,
1652 			   ~s, StdFont().Bold(), ink);
1653 }
1654 
1655 struct HomeDisplay : public Display {
PaintUpp::HomeDisplay1656 	virtual void Paint(Draw& w, const Rect& r, const Value& q,
1657 	                   Color ink, Color paper, dword style) const {
1658 		w.DrawRect(r, paper);
1659 		Image img = CtrlImg::Home();
1660 		w.DrawImage(r.left, r.top + (r.Height() - img.GetSize().cx) / 2,
1661 			        CtrlImg::Home());
1662 		w.DrawText(r.left + Zx(20),
1663 		           r.top + (r.Height() - StdFont().Bold().Info().GetHeight()) / 2,
1664 				   String(q), StdFont().Bold(), ink);
1665 	}
1666 };
1667 
Set(const String & s)1668 void FileSel::Set(const String& s)
1669 {
1670 	fn.Clear();
1671 	if(IsFullPath(s)) {
1672 		ActiveDir(GetFileFolder(s));
1673 		fn.Add(GetFileName(s));
1674 	}
1675 	else
1676 		fn.Add(s);
1677 	bidname = true;
1678 }
1679 
GoToPlace()1680 void FileSel::GoToPlace()
1681 {
1682 	if(places.IsCursor()) {
1683 #ifdef PLATFORM_WIN32
1684 		netnode.Clear();
1685 #endif
1686 		dir <<= places.GetKey();
1687 		Load();
1688 	}
1689 }
1690 
Execute(int _mode)1691 bool FileSel::Execute(int _mode) {
1692 	mode = _mode;
1693 
1694 	int system_row = -1;
1695 	for(int i = places.GetCount() - 1; i >= 0; i--) {
1696 		if(places.Get(i, 3) == "PLACES:SYSTEM") {
1697 			system_row = i;
1698 			places.Remove(i);
1699 		}
1700 	}
1701 	AddSystemPlaces(system_row);
1702 
1703 	if(mode == SELECTDIR) {
1704 		if(!fn.IsEmpty()) {
1705 			String h = ~dir;
1706 			dir <<= NormalizePath(fn[0]);
1707 			if(!DirectoryExists(~~dir))
1708 				dir <<= h;
1709 		}
1710 		type.Hide();
1711 		type_lbl.Hide();
1712 		file.Hide();
1713 		file_lbl.Hide();
1714 		sortby.Hide();
1715 		sort_lbl.Hide();
1716 		ok.SetLabel(t_("&Select"));
1717 		Logc p = filename.GetPos().y;
1718 		int q = ok.GetPos().y.GetA() + ok.GetPos().y.GetB() + Zy(16);
1719 		p.SetA(q);
1720 		filename.SetPosY(p);
1721 		filesize.SetPosY(p);
1722 		filetime.SetPosY(p);
1723 		p = splitter.Ctrl::GetPos().y;
1724 		p.SetB(q + Zy(28));
1725 		splitter.SetPosY(p);
1726 		LogPos ps = search.GetPos();
1727 		LogPos pl = sort_lbl.GetPos();
1728 		pl.x.SetB(ps.x.GetB());
1729 		pl.y.SetA(ok.GetPos().y.GetA());
1730 		pl.y.SetB(ps.y.GetB());
1731 		search.SetPos(pl);
1732 		bidname = false;
1733 	}
1734 	else {
1735 		for(Ctrl *q = GetFirstChild(); q; q = q->GetNext())
1736 			if(q != &mkdir)
1737 				q->Show();
1738 		Rect r = GetRect();
1739 		CtrlLayout(*this);
1740 		ArrangeOKCancel(ok, cancel);
1741 		SetRect(r);
1742 	}
1743 
1744 	if(file_ctrl) {
1745 		LogPos sp = search.GetPos();
1746 		LogPos fp = file.GetPos();
1747 		file.HSizePos(fp.x.GetA(), 2 * sp.x.GetA() + file_ctrl_cx);
1748 		AddChild(file_ctrl, &file);
1749 		file_ctrl->BottomPos(fp.y.GetA(), fp.y.GetB()).RightPos(sp.x.GetA(), file_ctrl_cx);
1750 	}
1751 
1752 	readonly.Show(rdonly && mode == OPEN);
1753 	list.Multi(multi && (mode == OPEN || mode == SELECTDIR));
1754 	list.SelectDir(multi && mode == SELECTDIR);
1755 	dir.ClearList();
1756 	file <<= Null;
1757 	if(basedir.IsEmpty()) {
1758 		dir.Add(GetHomeDirectory());
1759 	#ifdef PLATFORM_POSIX
1760 		Array<FileSystemInfo::FileInfo> root = filesystem->Find("/media/*");
1761 		dir.Add(GetDesktopFolder());
1762 		dir.Add("/");
1763 		for(int i = 0; i < root.GetCount(); i++) {
1764 			String ugly = root[i].filename;
1765 			if(ugly[0] != '.') {
1766 				dir.Add("/media/" + root[i].filename);
1767 			}
1768 		}
1769 	#else
1770 		dir.Add(GetDesktopFolder());
1771 		Array<FileSystemInfo::FileInfo> root = filesystem->Find(Null);
1772 		for(int i = 0; i < root.GetCount(); i++) {
1773 			String ugly = root[i].filename;
1774 			if(ugly != "A:\\" && ugly != "B:\\") {
1775 				ugly.Cat('\0');
1776 				ugly.Cat(root[i].root_style);
1777 				dir.Add(root[i].filename, ugly);
1778 			}
1779 		}
1780 		if(filesystem == &StdFileSystemInfo())
1781 			dir.Add("\\", String(t_("Network")) + String(0, 1) + "\xff");
1782 	#endif
1783 		if(filesystem->IsPosix() && String(~dir).IsEmpty())
1784 			dir <<= GetHomeDirectory();
1785 		if(lru.GetCount())
1786 			dir.AddSeparator();
1787 		for(int i = 0; i < lru.GetCount(); i++)
1788 			if(IsFullPath(lru[i]) && filesystem->FolderExists(lru[i]))
1789 				dir.Add(lru[i]);
1790 		dir.SetDisplay(Single<FolderDisplay>(), max(16, Draw::GetStdFontCy()));
1791 	}
1792 	else {
1793 		dir.SetDisplay(Single<HomeDisplay>(), max(16, Draw::GetStdFontCy()));
1794 		if(filesystem->IsPosix()) {
1795 			if(String(~dir)[0] == '/')
1796 				dir <<= "";
1797 		}
1798 	}
1799 	Rect lr = splitter.GetRect();
1800 	Rect dr = dir.GetRect();
1801 	int dp = max(20, dir.Ctrl::GetPos().y.GetB());
1802 	int px = GetSize().cx - lr.right;
1803 /*	if(IsMulti()) { // Cxl: Have we ever used these?!
1804 		toggle.RightPos(px, dp).TopPos(dr.top, dp);
1805 		minus.RightPos(px + 2 * dp, dp).TopPos(dr.top, dp);
1806 		plus.RightPos(px + 3 * dp, dp).TopPos(dr.top, dp);
1807 		px += 3 * dp;
1808 		toggle.Show();
1809 		minus.Show();
1810 		plus.Show();
1811 	}
1812 	else {*/
1813 		toggle.Hide();
1814 		minus.Hide();
1815 		plus.Hide();
1816 //	}
1817 	if(mkdir.IsShown()) {
1818 		mkdir.RightPos(px, dp).TopPos(dr.top, dp);
1819 		dirup.RightPos(px + dp, dp).TopPos(dr.top, dp);
1820 		px += 2 * dp;
1821 	}
1822 	else {
1823 		dirup.RightPos(px, dp).TopPos(dr.top, dp);
1824 		px += dp;
1825 	}
1826 	dir.HSizePos(dr.left, px + 4);
1827 	if(preselect.GetCount()) {
1828 		for(int i = 0; i < mask.GetCount(); i++) {
1829 			if(PatternMatchMulti(mask[i], preselect)) {
1830 				ActiveType(i);
1831 				break;
1832 			}
1833 		}
1834 	}
1835 	int q = type.FindValue(activetype);
1836 	if(q >= 0)
1837 		type <<= q;
1838 	else
1839 	if(type.GetCount())
1840 		type.SetIndex(0);
1841 	int dlc = type.GetCount();
1842 	Load();
1843 	ActiveFocus(file.IsEditable() ? (Ctrl&)file : (Ctrl&)list);
1844 	if(bidname) {
1845 		String s;
1846 		for(int i = 0; i < fn.GetCount(); i++)
1847 			Catq(s, fn[i]);
1848 		file <<= s;
1849 		ActiveFocus(file);
1850 		bidname = false;
1851 	}
1852 	list.SetSbPos(lastsby);
1853 	if(preselect.GetCount()) {
1854 		if(mode == SAVEAS)
1855 			file <<= preselect;
1856 		else
1857 			for(int i = 0; i < list.GetCount(); i++)
1858 			    if(list[i].name == preselect) {
1859 					list.SetCursor(i);
1860 					ActiveFocus(list);
1861 					break;
1862 				}
1863 		preselect.Clear();
1864 	}
1865 	FileUpdate();
1866 	Update();
1867 	int c = TopWindow::Run(appmodal);
1868 	TopWindow::Close();
1869 	lastsby = list.GetSbPos();
1870 	if(IsNumber(~type)) {
1871 		int ti = ~type;
1872 		type.Trim(dlc);
1873 		if(ti >= 0 && ti < type.GetCount())
1874 			activetype = type.GetValue(ti);
1875 	}
1876 	else
1877 		type.Trim(dlc);
1878 	if(c == IDOK) {
1879 		String d = ~dir;
1880 		if(filesystem->IsWin32())
1881 			if(d.GetLength() == 3 && d[1] == ':') return true;
1882 		if(filesystem->IsPosix())
1883 			if(d == "/") return true;
1884 		if(!IsFullPath(d)) return true;
1885 		LruAdd(lru, d, 8);
1886 		return true;
1887 	}
1888 	return false;
1889 }
1890 
ExecuteOpen(const char * title)1891 bool FileSel::ExecuteOpen(const char *title) {
1892 	Title(title ? title : t_("Open"));
1893 	return Execute(OPEN);
1894 }
1895 
ExecuteSaveAs(const char * title)1896 bool FileSel::ExecuteSaveAs(const char *title) {
1897 	Title(title ? title : t_("Save as"));
1898 	ok.SetLabel(t_("Save"));
1899 	return Execute(SAVEAS);
1900 }
1901 
ExecuteSelectDir(const char * title)1902 bool FileSel::ExecuteSelectDir(const char *title)
1903 {
1904 	Title(title ? title : t_("Select directory"));
1905 	return Execute(SELECTDIR);
1906 }
1907 
Serialize(Stream & s)1908 void FileSel::Serialize(Stream& s) {
1909 #ifdef PLATFORM_WIN32
1910 	if(s.IsLoading()) {
1911 		netnode.Clear();
1912 		netstack.Clear();
1913 	}
1914 #endif
1915 	int version = 10;
1916 	s / version;
1917 	String ad = ~dir;
1918 	int dummy = 0;
1919 	if(version < 10)
1920 		s / dummy;
1921 	else
1922 		s % activetype;
1923 	s % ad;
1924 	dir <<= ad;
1925 	if(version < 1) {
1926 		String n = fn.At(0);
1927 		s % n;
1928 		fn.At(0) = n;
1929 	}
1930 	else {
1931 		if(version < 4)
1932 			s % fn;
1933 		else {
1934 			Vector<String> __;
1935 			s % __;
1936 		}
1937 	}
1938 	if(version >= 2) {
1939 		SerializePlacement(s);
1940 		list.SerializeSettings(s);
1941 	}
1942 	if(version >= 3) {
1943 		s % lastsby;
1944 	}
1945 	if(version >= 4) {
1946 		s % lru;
1947 	}
1948 	if(version >= 5) {
1949 		s % sortby;
1950 	}
1951 	if(version >= 6) {
1952 		if(version >= 9)
1953 			s % splitter;
1954 		else {
1955 			Splitter dummy;
1956 			s % dummy;
1957 		}
1958 	}
1959 	if(version >= 7) {
1960 		s % hidden;
1961 	}
1962 	if(version >= 8) {
1963 		s % hiddenfiles;
1964 	}
1965 }
1966 
GetFile(int i) const1967 String FileSel::GetFile(int i) const {
1968 	String p;
1969 	if(i >= 0 && i < fn.GetCount()) {
1970 		p = fn[i];
1971 		if(!IsFullPath(p))
1972 			p = AppendFileName(dir.GetData(), p);
1973 	}
1974 #ifdef PLATFORM_WIN32
1975 	if(IsLnkFile(p))
1976 		p = Nvl(GetSymLinkPath(p), p);
1977 #endif
1978 	return p;
1979 }
1980 
SyncSplitter()1981 void FileSel::SyncSplitter()
1982 {
1983 	splitter.Clear();
1984 	if(places.GetCount() && basedir.IsEmpty())
1985 		splitter.Add(places);
1986 	splitter.Add(list);
1987 	if(preview)
1988 		splitter.Add(*preview);
1989 }
1990 
PreSelect(const String & path)1991 FileSel& FileSel::PreSelect(const String& path)
1992 {
1993 	ActiveDir(GetFileFolder(path));
1994 	preselect = GetFileName(path);
1995 	return *this;
1996 }
1997 
InitSplitter()1998 void FileSel::InitSplitter()
1999 {
2000 	int n = splitter.GetCount();
2001 	int i = 0;
2002 	if(places.GetCount())
2003 		splitter.SetPos(2000, i++);
2004 	splitter.SetPos(10000 - 2000 * (n - 1), i);
2005 }
2006 
Preview(Ctrl & ctrl)2007 FileSel& FileSel::Preview(Ctrl& ctrl)
2008 {
2009 	if(!preview) {
2010 		Size sz = GetRect().GetSize();
2011 		sz.cx = 5 * sz.cx / 3;
2012 		SetRect(sz);
2013 	}
2014 	preview = &ctrl;
2015 	SyncSplitter();
2016 	InitSplitter();
2017 	return *this;
2018 }
2019 
Preview(const Display & d)2020 FileSel& FileSel::Preview(const Display& d)
2021 {
2022 	preview_display.SetDisplay(d);
2023 	return Preview(preview_display);
2024 }
2025 
AddPlaceRaw(const String & path,const Image & m,const String & name,const char * group,int row)2026 void FileSel::AddPlaceRaw(const String& path, const Image& m, const String& name, const char* group, int row)
2027 {
2028 	if(path.GetCount()) {
2029 		row = row < 0 ? places.GetCount() : row;
2030 		places.Insert(row);
2031 		places.Set(row, 0, path);
2032 		places.Set(row, 1, DPI(m));
2033 		places.Set(row, 2, name);
2034 		places.Set(row, 3, group);
2035 		places.SetLineCy(row, max(m.GetSize().cy + 4, GetStdFontCy() + 4));
2036 		SyncSplitter();
2037 		InitSplitter();
2038 	}
2039 }
2040 
AddPlace(const String & path,const Image & m,const String & name,const char * group,int row)2041 FileSel& FileSel::AddPlace(const String& path, const Image& m, const String& name, const char* group, int row)
2042 {
2043 	if(path.GetCount())
2044 		AddPlaceRaw(NormalizePath(path), DPI(m), name, group, row);
2045 	return *this;
2046 }
2047 
AddPlace(const String & path,const String & name,const char * group,int row)2048 FileSel& FileSel::AddPlace(const String& path, const String& name, const char* group, int row)
2049 {
2050 #ifdef PLATFORM_COCOA
2051 	return AddPlace(path, GetFileIcon(NormalizePath(path), true, false, false), name, group, row);
2052 #else
2053 	return AddPlace(path, GetDirIcon(NormalizePath(path)), name, group, row);
2054 #endif
2055 }
2056 
AddPlace(const String & path,const char * group,int row)2057 FileSel& FileSel::AddPlace(const String& path, const char* group, int row)
2058 {
2059 	return AddPlace(path, GetFileTitle(path), group, row);
2060 }
2061 
AddPlaceSeparator()2062 FileSel& FileSel::AddPlaceSeparator()
2063 {
2064 	places.AddSeparator();
2065 	SyncSplitter();
2066 	InitSplitter();
2067 	return *this;
2068 }
2069 
ClearPlaces()2070 FileSel& FileSel::ClearPlaces()
2071 {
2072 	places.Clear();
2073 	SyncSplitter();
2074 	return *this;
2075 }
2076 
AddSystemPlaces(int row)2077 void FileSel::AddSystemPlaces(int row)
2078 {
2079 	row = row < 0 ? places.GetCount() : row;
2080 	Array<FileSystemInfo::FileInfo> root;
2081 #ifdef PLATFORM_WIN32
2082 	root = filesystem->Find(Null);
2083 	for(int i = 0; i < root.GetCount(); i++) {
2084 		String desc = root[i].root_desc;
2085 		String n = root[i].filename;
2086 		if(n != "A:\\" && n != "B:\\") {
2087 		#ifdef PLATFORM_WIN32
2088 			if(*n.Last() == '\\')
2089 				n.Trim(n.GetCount() - 1);
2090 		#endif
2091 			if(desc.GetCount() == 0)
2092 			    desc << " " << t_("Local Disk");
2093 			desc << " (" << n << ")";
2094 			AddPlace(root[i].filename, GetDriveImage(root[i].root_style), desc, "PLACES:SYSTEM", row++);
2095 		}
2096 	}
2097 
2098 	if(GetSystemMetrics(SM_REMOTESESSION))
2099 		for(int drive = 'A'; drive < 'Z'; drive++) {
2100 			String path = Format("\\\\tsclient\\%c", drive);
2101 			if(FindFile(path + "\\*.*"))
2102 				AddPlace(path, Format(t_("%c on client"), drive), "PLACES:SYSTEM", row++);
2103 		}
2104 #endif
2105 
2106 #ifdef PLATFORM_POSIX
2107 	root = filesystem->Find("/media/*");
2108 	for(int i = 0; i < root.GetCount(); i++) {
2109 		String fn = root[i].filename;
2110 		if(*fn != '.' && fn.Find("floppy") < 0)
2111 			AddPlace("/media/" + fn, fn, "PLACES:SYSTEM", row++);
2112 	}
2113 	AddPlace("/", t_("Computer"), "PLACES:SYSTEM", row++);
2114 #endif
2115 }
2116 
AddStandardPlaces()2117 FileSel& FileSel::AddStandardPlaces()
2118 {
2119 	AddPlace(GetHomeDirectory(), t_("Home"), "PLACES:FOLDER");
2120 #ifdef PLATFORM_COCOA
2121 	AddPlace(GetSpecialDirectory(SF_NSDesktopDirectory), t_("Desktop"), "PLACES:FOLDER");
2122 	AddPlace(GetSpecialDirectory(SF_NSMusicDirectory), t_("Music"), "PLACES:FOLDER");
2123 	AddPlace(GetSpecialDirectory(SF_NSPicturesDirectory), t_("Pictures"), "PLACES:FOLDER");
2124 	AddPlace(GetSpecialDirectory(SF_NSMoviesDirectory), t_("Videos"), "PLACES:FOLDER");
2125 	AddPlace(GetSpecialDirectory(SF_NSDocumentDirectory), t_("Documents"), "PLACES:FOLDER");
2126 	AddPlace(GetSpecialDirectory(SF_NSDownloadsDirectory), t_("Downloads"), "PLACES:FOLDER");
2127 #else
2128 	AddPlace(GetDesktopFolder(), t_("Desktop"), "PLACES:FOLDER");
2129 	AddPlace(GetMusicFolder(), t_("Music"), "PLACES:FOLDER");
2130 	AddPlace(GetPicturesFolder(), t_("Pictures"), "PLACES:FOLDER");
2131 	AddPlace(GetVideoFolder(), t_("Videos"), "PLACES:FOLDER");
2132 	AddPlace(GetDocumentsFolder(), t_("Documents"), "PLACES:FOLDER");
2133 	AddPlace(GetDownloadFolder(), t_("Downloads"), "PLACES:FOLDER");
2134 #endif
2135 	AddPlaceSeparator();
2136 	AddSystemPlaces();
2137 #ifdef PLATFORM_WIN32
2138 	AddPlaceSeparator();
2139 	AddPlaceRaw("\\", CtrlImg::Network(), t_("Network"), "PLACES:NETWORK");
2140 #endif
2141 	return *this;
2142 }
2143 
2144 struct DisplayPlace : Display {
PaintUpp::DisplayPlace2145 	virtual void Paint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper,
2146 	                   dword style) const
2147 	{
2148 		w.DrawRect(r, paper);
2149 		ValueArray va = q;
2150 		Image m = va[0];
2151 		String txt = va[1];
2152 		Size isz = m.GetSize();
2153 		w.DrawImage(r.left, r.top + (r.Height() - isz.cy) / 2, m);
2154 		w.DrawText(r.left + isz.cx + 2, r.top + (r.Height() - GetStdFontCy()) / 2, txt,
2155 		           StdFont(), ink);
2156 	}
GetStdSizeUpp::DisplayPlace2157 	virtual Size GetStdSize(const Value& q) const {
2158 		ValueArray va = q;
2159 		Image m = va[0];
2160 		String txt = va[1];
2161 		Size isz = m.GetSize();
2162 		return Size(isz.cx + GetTextSize(txt, StdFont()).cx + 2, max(isz.cy, GetStdFontCy()));
2163 	}
2164 };
2165 
FileSel()2166 FileSel::FileSel()
2167 {
2168 	loaded = false;
2169 	filesystem = &StdFileSystemInfo();
2170 	CtrlLayout(*this);
2171 	ArrangeOKCancel(ok, cancel);
2172 	Acceptor(ok, IDOK); ok.Ok();
2173 	Rejector(cancel, IDCANCEL); cancel.Cancel();
2174 	list.IconWidth(DPI(16)).Renaming().Columns(3).ClickKill();
2175 	list.WhenLeftDouble = THISBACK(OpenItem2);
2176 	dirup <<= THISBACK(DirUp);
2177 	Add(dirup);
2178 	sortby <<= THISBACK(SearchLoad);
2179 	Add(sortby);
2180 	hidden <<= THISBACK(SearchLoad);
2181 	Add(hidden);
2182 	hiddenfiles <<= THISBACK(SearchLoad);
2183 	Add(hiddenfiles);
2184 	mkdir <<= THISBACK(MkDir);
2185 	Add(mkdir);
2186 	plus <<= THISBACK(Plus);
2187 	Add(plus);
2188 	minus <<= THISBACK(Minus);
2189 	Add(minus);
2190 	toggle <<= THISBACK(Toggle);
2191 	Add(toggle);
2192 
2193 	ok <<= THISBACK(Open);
2194 	list <<= THISBACK(Update);
2195 	file <<= THISBACK(FileUpdate);
2196 	list.WhenRename = THISBACK(Rename);
2197 	Sizeable();
2198 	dirup.SetImage(CtrlImg::DirUp()).NoWantFocus();
2199 	dirup.Tip(t_("Dir up") + String(" (Ctrl+Up)"));
2200 	mkdir.SetImage(CtrlImg::MkDir()).NoWantFocus();
2201 	mkdir.Tip(t_("Create directory") + String(" (F7)"));
2202 	plus.SetImage(CtrlImg::Plus()).NoWantFocus();
2203 	plus.Tip(t_("Select files"));
2204 	minus.SetImage(CtrlImg::Minus()).NoWantFocus();
2205 	minus.Tip(t_("Unselect files"));
2206 	toggle.SetImage(CtrlImg::Toggle()).NoWantFocus();
2207 	toggle.Tip(t_("Toggle files"));
2208 	type <<= THISBACK(Load);
2209 	for(int pass = 0; pass < 2; pass++) {
2210 		int k = pass * FILELISTSORT_DESCENDING;
2211 		String d = pass ? t_(" descending") : "";
2212 		sortby.Add(FILELISTSORT_NAME|k, t_("Name") + d);
2213 		sortby.Add(FILELISTSORT_EXT|k, t_("Extension") + d);
2214 		sortby.Add(FILELISTSORT_TIME|k, t_("Last change") + d);
2215 		sortby.Add(FILELISTSORT_SIZE|k, t_("Size") + d);
2216 	}
2217 	sortby <<= FILELISTSORT_NAME;
2218 
2219 	search.NullText(t_("Search"), StdFont().Italic(), SColorDisabled());
2220 	search.SetFilter(CharFilterDefaultToUpperAscii);
2221 	search <<= THISBACK(SearchLoad);
2222 
2223 	filename.SetFont(StdFont());
2224 	filename.SetFrame(ViewFrame());
2225 	filesize.SetFont(StdFont()).SetAlign(ALIGN_RIGHT);
2226 	filesize.SetFrame(ViewFrame());
2227 	filetime.SetFont(StdFont());
2228 	filetime.SetFrame(ViewFrame());
2229 
2230 	dir <<= THISBACK(Choice);
2231 	dir.DisplayAll();
2232 	dir.SetDropLines(24);
2233 
2234 	readonly.Hide();
2235 
2236 	lastsby = 0;
2237 
2238 	asking = true;
2239 	rdonly = false;
2240 	multi = false;
2241 	bidname = false;
2242 	appmodal = true;
2243 
2244 	AddChildBefore(GetFirstChild(), &sizegrip);
2245 
2246 	preview = NULL;
2247 	preview_display.SetFrame(FieldFrame());
2248 
2249 	SyncSplitter();
2250 
2251 	BackPaintHint();
2252 
2253 	places.AddKey();
2254 	places.AddColumn().AddIndex().SetDisplay(Single<DisplayPlace>());
2255 	places.AddIndex();
2256 	places.NoHeader().NoGrid();
2257 	places.WhenLeftClick = THISBACK(GoToPlace);
2258 	places.NoWantFocus();
2259 
2260 #ifdef PLATFORM_WIN32
2261 	list.IconWidth(GetFileIcon(GetHomeDirectory(), true, false, false).GetSize().cx);
2262 #endif
2263 
2264 	AddStandardPlaces();
2265 
2266 	list.AutoHideSb();
2267 	places.AutoHideSb();
2268 
2269 	WhenIconLazy = NULL;
2270 }
2271 
~FileSel()2272 FileSel::~FileSel() {}
2273 
2274 }
2275