Merge pull request #110 from tesselode/fixes
[wdl/wdl-ol.git] / WDL / swell / swell-miscdlg-generic.cpp
blobdd1c20bc6fe6b99cdecc8b01585ec78c2c49d706
1 /* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX)
2 Copyright (C) 2006 and later, Cockos, Inc.
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
12 1. The origin of this software must not be misrepresented; you must not
13 claim that you wrote the original software. If you use this software
14 in a product, an acknowledgment in the product documentation would be
15 appreciated but is not required.
16 2. Altered source versions must be plainly marked as such, and must not be
17 misrepresented as being the original software.
18 3. This notice may not be removed or altered from any source distribution.
21 This file provides basic APIs for browsing for files, directories, and messageboxes.
23 These APIs don't all match the Windows equivelents, but are close enough to make it not too much trouble.
28 #ifndef SWELL_PROVIDED_BY_APP
30 #include "swell.h"
31 #include "swell-internal.h"
32 #include "swell-dlggen.h"
34 #include "../wdlcstring.h"
35 #include "../assocarray.h"
36 #include <dirent.h>
37 #include <time.h>
39 #include "../lineparse.h"
40 #define WDL_HASSTRINGS_EXPORT static
41 #include "../has_strings.h"
43 static const char *BFSF_Templ_dlgid;
44 static DLGPROC BFSF_Templ_dlgproc;
45 static struct SWELL_DialogResourceIndex *BFSF_Templ_reshead;
46 void BrowseFile_SetTemplate(const char *dlgid, DLGPROC dlgProc, struct SWELL_DialogResourceIndex *reshead)
48 BFSF_Templ_reshead=reshead;
49 BFSF_Templ_dlgid=dlgid;
50 BFSF_Templ_dlgproc=dlgProc;
53 class BrowseFile_State
55 public:
56 static char s_sortrev;
57 enum modeEnum { SAVE=0,OPEN, OPENMULTI, OPENDIR };
59 BrowseFile_State(const char *_cap, const char *_idir, const char *_ifile, const char *_el, modeEnum _mode, char *_fnout, int _fnout_sz) :
60 caption(_cap), initialdir(_idir), initialfile(_ifile), extlist(_el), mode(_mode),
61 sortcol(0), sortrev(0),
62 fnout(_fnout), fnout_sz(_fnout_sz), viewlist_store(16384), viewlist(4096)
65 ~BrowseFile_State()
67 viewlist_clear();
70 const char *caption;
71 const char *initialdir;
72 const char *initialfile;
73 const char *extlist;
75 modeEnum mode;
76 char sortcol, sortrev;
78 char *fnout; // if NULL this will be malloced by the window
79 int fnout_sz;
81 struct rec {
82 WDL_INT64 size;
83 time_t date;
84 char *name;
85 int type; // 1 = directory, 2 = file
87 void format_date(char *buf, int bufsz)
89 *buf=0;
90 if (date > 0 && date < WDL_INT64_CONST(0x793406fff))
92 struct tm *a=localtime(&date);
93 if (a) strftime(buf,bufsz,"%c",a);
97 void format_size(char *buf, int bufsz)
99 if (type == 1)
101 lstrcpyn_safe(buf,"<DIR>",bufsz);
103 else
105 static const char *tab[]={ "bytes","KB","MB","GB" };
106 int lf=0;
107 WDL_INT64 s=size;
108 if (s<1024)
110 snprintf(buf,bufsz,"%d %s",(int)s,tab[0]);
112 else
114 int w = 1;
115 do { w++; lf = (int)(s&1023); s/=1024; } while (s >= 1024 && w<4);
116 snprintf(buf,bufsz,"%d.%d %s",(int)s,(int)((lf*10.0)/1024.0+0.5),tab[w-1]);
121 char *format_all(char *buf, int bufsz)
123 char dstr[128],sstr[128];
124 format_date(dstr,sizeof(dstr));
125 format_size(sstr,sizeof(sstr));
126 snprintf(buf,bufsz,"%s\t%s\t%s",WDL_get_filepart(name),dstr,sstr);
127 return buf;
132 void viewlist_clear()
134 rec *l = viewlist_store.Get();
135 const int n = viewlist_store.GetSize();
136 for (int x = 0; x < n; x ++) free(l[x].name);
137 viewlist_store.Resize(0);
138 viewlist.Empty();
140 WDL_TypedBuf<rec> viewlist_store;
141 WDL_PtrList<rec> viewlist;
142 void viewlist_sort(const char *filter)
144 if (filter)
146 viewlist.Empty();
147 LineParser lp;
148 const bool no_filter = !*filter || !WDL_makeSearchFilter(filter,&lp);
149 for (int x=0;x<viewlist_store.GetSize();x++)
151 rec *r = viewlist_store.Get()+x;
152 char tmp[512];
153 if (no_filter || WDL_hasStrings(r->format_all(tmp,sizeof(tmp)),&lp))
154 viewlist.Add(r);
157 s_sortrev = sortrev;
158 if (viewlist.GetSize()>1)
159 qsort(viewlist.GetList(), viewlist.GetSize(),sizeof(rec*),
160 sortcol == 1 ? sortFunc_sz :
161 sortcol == 2 ? sortFunc_date :
162 sortFunc_fn);
164 static int sortFunc_fn(const void *_a, const void *_b)
166 const rec *a = *(const rec * const*)_a, *b = *(const rec * const*)_b;
167 int d = a->type - b->type;
168 if (d) return d;
169 d = stricmp(a->name,b->name);
170 return s_sortrev ? -d : d;
172 static int sortFunc_date(const void *_a, const void *_b)
174 const rec *a = *(const rec * const*)_a, *b = *(const rec * const*)_b;
175 if (a->date != b->date) return s_sortrev ? (a->date>b->date?-1:1) : (a->date>b->date?1:-1);
176 return stricmp(a->name,b->name);
178 static int sortFunc_sz(const void *_a, const void *_b)
180 const rec *a = *(const rec * const *)_a, *b = *(const rec * const *)_b;
181 int d = a->type - b->type;
182 if (d) return s_sortrev ? -d : d;
183 if (a->size != b->size) return s_sortrev ? (a->size>b->size?-1:1) : (a->size>b->size?1:-1);
184 return stricmp(a->name,b->name);
188 void scan_path(const char *path, const char *filterlist, bool dir_only)
190 viewlist_clear();
191 DIR *dir = opendir(path);
192 if (!dir) return;
193 char tmp[2048];
194 struct dirent *ent;
195 while (NULL != (ent = readdir(dir)))
197 if (ent->d_name[0] == '.') continue;
198 bool is_dir = (ent->d_type == DT_DIR);
199 if (ent->d_type == DT_UNKNOWN)
201 snprintf(tmp,sizeof(tmp),"%s/%s",path,ent->d_name);
202 DIR *d = opendir(tmp);
203 if (d) { is_dir = true; closedir(d); }
205 else if (ent->d_type == DT_LNK)
207 snprintf(tmp,sizeof(tmp),"%s/%s",path,ent->d_name);
208 char *rp = realpath(tmp,NULL);
209 if (rp)
211 DIR *d = opendir(rp);
212 if (d) { is_dir = true; closedir(d); }
213 free(rp);
216 if (!dir_only || is_dir)
218 if (filterlist && *filterlist && !is_dir)
220 const char *f = filterlist;
221 while (*f)
223 const char *nf = f;
224 while (*nf && *nf != ';') nf++;
225 if (*f != '*')
227 const char *nw = f;
228 while (nw < nf && *nw != '*') nw++;
230 if ((nw!=nf || f+strlen(ent->d_name) == nw) && !strncasecmp(ent->d_name,f,nw-f))
232 // matched leading text
233 if (nw == nf) break;
234 f = nw;
238 if (*f == '*')
240 f++;
241 if (!*f || *f == ';' || (*f == '.' && f[1] == '*')) break;
242 size_t l = strlen(ent->d_name);
243 if (f+l > nf && !strncasecmp(ent->d_name + l - (nf-f), f,nf-f)) break;
245 f = nf;
246 while (*f == ';') f++;
248 if (!*f) continue; // did not match
250 snprintf(tmp,sizeof(tmp),"%s/%s",path,ent->d_name);
251 struct stat64 st={0,};
252 stat64(tmp,&st);
254 rec r = { st.st_size, st.st_mtime, strdup(ent->d_name), is_dir?1:2 } ;
255 viewlist_store.Add(&r,1);
258 // sort viewlist
260 closedir(dir);
264 char BrowseFile_State::s_sortrev;
266 static void preprocess_user_path(char *buf, int bufsz)
268 if (buf[0] == '~')
270 char *tmp = strdup(buf+1);
271 if (buf[1] == '/' || !buf[1])
273 char *p = getenv("HOME");
274 if (p && *p) snprintf(buf,bufsz,"%s%s",p,tmp);
276 else
278 snprintf(buf,bufsz,"/home/%s",tmp); // if someone wants to write code to lookup homedirs, please, go right ahead!
280 free(tmp);
284 static LRESULT WINAPI swellFileSelectProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
286 enum { IDC_EDIT=0x100, IDC_LABEL, IDC_CHILD, IDC_DIR, IDC_LIST, IDC_EXT, IDC_PARENTBUTTON, IDC_FILTER };
287 enum { WM_UPD=WM_USER+100 };
288 const int maxPathLen = 2048;
289 const char *multiple_files = "(multiple files)";
290 switch (uMsg)
292 case WM_CREATE:
293 if (lParam) // swell-specific
295 SetWindowLong(hwnd,GWL_WNDPROC,(LPARAM)SwellDialogDefaultWindowProc);
296 SetWindowLong(hwnd,DWL_DLGPROC,(LPARAM)swellFileSelectProc);
298 SetWindowLong(hwnd,GWL_STYLE, GetWindowLong(hwnd,GWL_STYLE)|WS_THICKFRAME);
300 SetWindowLongPtr(hwnd,GWLP_USERDATA,lParam);
301 BrowseFile_State *parms = (BrowseFile_State *)lParam;
302 if (parms->caption) SetWindowText(hwnd,parms->caption);
304 SWELL_MakeSetCurParms(1,1,0,0,hwnd,false,false);
306 HWND edit = SWELL_MakeEditField(IDC_EDIT, 0,0,0,0, 0);
307 SWELL_MakeButton(0,
308 parms->mode == BrowseFile_State::OPENDIR ? "Choose directory" :
309 parms->mode == BrowseFile_State::SAVE ? "Save" : "Open",
310 IDOK,0,0,0,0, 0);
312 SWELL_MakeButton(0, "Cancel", IDCANCEL,0,0,0,0, 0);
313 HWND dir = SWELL_MakeCombo(IDC_DIR, 0,0,0,0, 0);
314 SWELL_MakeButton(0, "..", IDC_PARENTBUTTON, 0,0,0,0, 0);
315 SWELL_MakeEditField(IDC_FILTER, 0,0,0,0, 0);
317 const char *ent = parms->mode == BrowseFile_State::OPENDIR ? "dir_browser" : "file_browser";
318 char tmp[128];
319 GetPrivateProfileString(".swell",ent,"", tmp,sizeof(tmp),"");
320 int x=0,y=0,w=0,h=0, c1=0,c2=0,c3=0;
321 int flag = SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER;
322 if (tmp[0] &&
323 sscanf(tmp,"%d %d %d %d %d %d %d",&x,&y,&w,&h,&c1,&c2,&c3) >= 4)
324 flag &= ~SWP_NOMOVE;
325 if (w < 100) w=SWELL_UI_SCALE(600);
326 if (h < 100) h=SWELL_UI_SCALE(400);
328 if (c1 + c2 + c3 < w/2)
330 c1=SWELL_UI_SCALE(280);
331 c2=SWELL_UI_SCALE(120);
332 c3=SWELL_UI_SCALE(140);
335 HWND list = SWELL_MakeControl("",IDC_LIST,"SysListView32",LVS_REPORT|LVS_SHOWSELALWAYS|
336 (parms->mode == BrowseFile_State::OPENMULTI ? 0 : LVS_SINGLESEL)|
337 LVS_OWNERDATA|WS_BORDER|WS_TABSTOP,0,0,0,0,0);
338 if (list)
340 LVCOLUMN c={LVCF_TEXT|LVCF_WIDTH, 0, c1, (char*)"Filename" };
341 ListView_InsertColumn(list,0,&c);
342 c.cx = c2;
343 c.pszText = (char*) "Size";
344 ListView_InsertColumn(list,1,&c);
345 c.cx = c3;
346 c.pszText = (char*) "Date";
347 ListView_InsertColumn(list,2,&c);
348 HWND hdr = ListView_GetHeader(list);
349 HDITEM hi;
350 memset(&hi,0,sizeof(hi));
351 hi.mask = HDI_FORMAT;
352 hi.fmt = parms->sortrev ? HDF_SORTDOWN : HDF_SORTUP;
353 Header_SetItem(hdr,parms->sortcol,&hi);
355 HWND extlist = (parms->extlist && *parms->extlist) ? SWELL_MakeCombo(IDC_EXT, 0,0,0,0, CBS_DROPDOWNLIST) : NULL;
356 if (extlist)
358 const char *p = parms->extlist;
359 while (*p)
361 const char *rd=p;
362 p += strlen(p)+1;
363 if (!*p) break;
364 int a = SendMessage(extlist,CB_ADDSTRING,0,(LPARAM)rd);
365 SendMessage(extlist,CB_SETITEMDATA,a,(LPARAM)p);
366 p += strlen(p)+1;
368 SendMessage(extlist,CB_SETCURSEL,0,0);
371 SWELL_MakeLabel(-1,parms->mode == BrowseFile_State::OPENDIR ? "Directory: " : "File:",IDC_LABEL, 0,0,0,0, 0);
373 if (BFSF_Templ_dlgid && BFSF_Templ_dlgproc)
375 HWND dlg = SWELL_CreateDialog(BFSF_Templ_reshead, BFSF_Templ_dlgid, hwnd, BFSF_Templ_dlgproc, 0);
376 if (dlg) SetWindowLong(dlg,GWL_ID,IDC_CHILD);
377 BFSF_Templ_dlgproc=0;
378 BFSF_Templ_dlgid=0;
381 SWELL_MakeSetCurParms(1,1,0,0,NULL,false,false);
383 if (edit && dir)
385 char buf[maxPathLen];
386 const char *filepart = "";
387 if (parms->initialfile && *parms->initialfile && strcmp(parms->initialfile,"."))
389 lstrcpyn_safe(buf,parms->initialfile,sizeof(buf));
390 char *p = (char *)WDL_get_filepart(buf);
391 if (p > buf)
393 p[-1]=0;
394 filepart = p;
396 else
398 filepart = parms->initialfile;
399 goto get_dir;
402 else
404 get_dir:
405 if (parms->initialdir && *parms->initialdir && strcmp(parms->initialdir,"."))
407 lstrcpyn_safe(buf,parms->initialdir,sizeof(buf));
409 else getcwd(buf,sizeof(buf));
412 SetWindowText(edit,filepart);
413 SendMessage(hwnd, WM_UPD, IDC_DIR, (LPARAM)buf);
416 if (list) SetWindowPos(list,HWND_BOTTOM,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
417 SetWindowPos(hwnd,NULL,x,y, w,h, flag);
418 SendMessage(hwnd,WM_UPD,1,0);
419 SendMessage(edit,EM_SETSEL,0,(LPARAM)-1);
420 SetFocus(edit);
422 break;
423 case WM_DESTROY:
425 BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA);
426 if (parms)
428 RECT r;
429 GetWindowRect(hwnd,&r);
430 HWND list = GetDlgItem(hwnd,IDC_LIST);
431 const int c1 = ListView_GetColumnWidth(list,0);
432 const int c2 = ListView_GetColumnWidth(list,1);
433 const int c3 = ListView_GetColumnWidth(list,2);
434 char tmp[128];
435 snprintf(tmp,sizeof(tmp),"%d %d %d %d %d %d %d",r.left,r.top,r.right-r.left,r.bottom-r.top,c1,c2,c3);
436 const char *ent = parms->mode == BrowseFile_State::OPENDIR ? "dir_browser" : "file_browser";
437 WritePrivateProfileString(".swell",ent, tmp, "");
440 break;
441 case WM_UPD:
442 switch (wParam)
444 case IDC_DIR: // update directory combo box -- destroys buffer pointed to by lParam
445 if (lParam)
447 char *path = (char*)lParam;
448 HWND combo=GetDlgItem(hwnd,IDC_DIR);
449 SendMessage(combo,CB_RESETCONTENT,0,0);
450 WDL_remove_trailing_dirchars(path);
451 while (path[0])
453 SendMessage(combo,CB_ADDSTRING,0,(LPARAM)path);
454 WDL_remove_filepart(path);
455 WDL_remove_trailing_dirchars(path);
457 SendMessage(combo,CB_ADDSTRING,0,(LPARAM)"/");
458 SendMessage(combo,CB_SETCURSEL,0,0);
460 break;
461 case 1:
463 BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA);
464 if (parms)
466 SetDlgItemText(hwnd,IDC_FILTER,"");
467 KillTimer(hwnd,1);
469 char buf[maxPathLen];
470 const char *filt = NULL;
471 buf[0]=0;
472 int a = (int) SendDlgItemMessage(hwnd,IDC_EXT,CB_GETCURSEL,0,0);
473 if (a>=0) filt = (const char *)SendDlgItemMessage(hwnd,IDC_EXT,CB_GETITEMDATA,a,0);
475 GetDlgItemText(hwnd,IDC_DIR,buf,sizeof(buf));
476 preprocess_user_path(buf,sizeof(buf));
478 if (buf[0]) parms->scan_path(buf, filt, parms->mode == BrowseFile_State::OPENDIR);
479 else parms->viewlist_clear();
480 HWND list = GetDlgItem(hwnd,IDC_LIST);
481 ListView_SetItemCount(list, 0); // clear selection
483 parms->viewlist_sort("");
484 ListView_SetItemCount(list, parms->viewlist.GetSize());
485 ListView_RedrawItems(list,0, parms->viewlist.GetSize());
488 break;
490 break;
491 case WM_GETMINMAXINFO:
493 LPMINMAXINFO p=(LPMINMAXINFO)lParam;
494 p->ptMinTrackSize.x = 300;
495 p->ptMinTrackSize.y = 300;
497 break;
498 case WM_SIZE:
500 BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA);
501 // reposition controls
502 RECT r;
503 GetClientRect(hwnd,&r);
504 const int buth = SWELL_UI_SCALE(24), cancelbutw = SWELL_UI_SCALE(50), okbutw = SWELL_UI_SCALE(parms->mode == BrowseFile_State::OPENDIR ? 120 : 50);
505 const int xborder = SWELL_UI_SCALE(4), yborder=SWELL_UI_SCALE(8);
506 const int fnh = SWELL_UI_SCALE(20), fnlblw = SWELL_UI_SCALE(parms->mode == BrowseFile_State::OPENDIR ? 70 : 50);
507 const int ypad = SWELL_UI_SCALE(4);
509 int ypos = r.bottom - ypad - buth;
510 int xpos = r.right;
511 SetWindowPos(GetDlgItem(hwnd,IDCANCEL), NULL, xpos -= cancelbutw + xborder, ypos, cancelbutw,buth, SWP_NOZORDER|SWP_NOACTIVATE);
512 SetWindowPos(GetDlgItem(hwnd,IDOK), NULL, xpos -= okbutw + xborder, ypos, okbutw,buth, SWP_NOZORDER|SWP_NOACTIVATE);
514 HWND emb = GetDlgItem(hwnd,IDC_CHILD);
515 if (emb)
517 RECT sr;
518 GetClientRect(emb,&sr);
519 if (ypos > r.bottom-ypad-sr.bottom) ypos = r.bottom-ypad-sr.bottom;
520 SetWindowPos(emb,NULL, xborder,ypos, xpos - xborder*2, sr.bottom, SWP_NOZORDER|SWP_NOACTIVATE);
521 ShowWindow(emb,SW_SHOWNA);
524 HWND filt = GetDlgItem(hwnd,IDC_EXT);
525 if (filt)
527 SetWindowPos(filt, NULL, xborder*2 + fnlblw, ypos -= fnh + yborder, r.right-fnlblw-xborder*3, fnh, SWP_NOZORDER|SWP_NOACTIVATE);
530 SetWindowPos(GetDlgItem(hwnd,IDC_EDIT), NULL, xborder*2 + fnlblw, ypos -= fnh + yborder, r.right-fnlblw-xborder*3, fnh, SWP_NOZORDER|SWP_NOACTIVATE);
531 SetWindowPos(GetDlgItem(hwnd,IDC_LABEL), NULL, xborder, ypos, fnlblw, fnh, SWP_NOZORDER|SWP_NOACTIVATE);
532 const int comboh = g_swell_ctheme.combo_height;
533 const int filterw = wdl_max(r.right/8, SWELL_UI_SCALE(50));
534 SetWindowPos(GetDlgItem(hwnd,IDC_DIR), NULL, xborder, yborder/2,
535 r.right-xborder*4 - comboh - filterw, comboh, SWP_NOZORDER|SWP_NOACTIVATE);
537 SetWindowPos(GetDlgItem(hwnd,IDC_PARENTBUTTON),NULL,
538 r.right-xborder*2-comboh - filterw,yborder/2,
539 comboh,comboh,SWP_NOZORDER|SWP_NOACTIVATE);
541 SetWindowPos(GetDlgItem(hwnd,IDC_FILTER),NULL,
542 r.right-xborder-filterw,yborder/2 + (comboh-fnh)/2,
543 filterw,fnh,SWP_NOZORDER|SWP_NOACTIVATE);
545 SetWindowPos(GetDlgItem(hwnd,IDC_LIST), NULL, xborder, g_swell_ctheme.combo_height+yborder, r.right-xborder*2, ypos - (g_swell_ctheme.combo_height+yborder) - yborder, SWP_NOZORDER|SWP_NOACTIVATE);
547 break;
548 case WM_TIMER:
549 if (wParam == 1)
551 KillTimer(hwnd,1);
552 BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA);
553 if (parms)
555 char buf[128];
556 GetDlgItemText(hwnd,IDC_FILTER,buf,sizeof(buf));
557 parms->viewlist_sort(buf);
558 HWND list = GetDlgItem(hwnd,IDC_LIST);
559 ListView_SetItemCount(list, parms->viewlist.GetSize());
560 ListView_RedrawItems(list,0, parms->viewlist.GetSize());
563 break;
564 case WM_COMMAND:
565 switch (LOWORD(wParam))
567 case IDC_FILTER:
568 if (HIWORD(wParam) == EN_CHANGE)
570 KillTimer(hwnd,1);
571 SetTimer(hwnd,1,250,NULL);
573 return 0;
574 case IDC_EXT:
575 if (HIWORD(wParam) == CBN_SELCHANGE)
577 SendMessage(hwnd,WM_UPD,1,0);
579 return 0;
580 case IDC_PARENTBUTTON:
582 int a = (int) SendDlgItemMessage(hwnd,IDC_DIR,CB_GETCURSEL,0,0);
583 if (a>=0)
585 SendDlgItemMessage(hwnd,IDC_DIR,CB_SETCURSEL,a+1,0);
587 else
589 char buf[maxPathLen];
590 GetDlgItemText(hwnd,IDC_DIR,buf,sizeof(buf));
591 preprocess_user_path(buf,sizeof(buf));
592 WDL_remove_filepart(buf);
593 SetDlgItemText(hwnd,IDC_DIR,buf);
595 SendMessage(hwnd,WM_UPD,1,0);
597 return 0;
598 case IDC_DIR:
599 if (HIWORD(wParam) == CBN_SELCHANGE)
601 SendMessage(hwnd,WM_UPD,1,0);
603 return 0;
604 case IDCANCEL: EndDialog(hwnd,0); return 0;
605 case IDOK:
607 char buf[maxPathLen], msg[2048];
608 GetDlgItemText(hwnd,IDC_DIR,buf,sizeof(buf));
609 preprocess_user_path(buf,sizeof(buf));
611 if (GetFocus() == GetDlgItem(hwnd,IDC_DIR))
613 DIR *dir = opendir(buf);
614 if (!dir)
616 //snprintf(msg,sizeof(msg),"Path does not exist:\r\n\r\n%s",buf);
617 //MessageBox(hwnd,msg,"Path not found",MB_OK);
618 return 0;
620 closedir(dir);
622 SendMessage(hwnd,WM_UPD,1,0);
623 SendMessage(hwnd, WM_UPD, IDC_DIR, (LPARAM)buf);
624 HWND e = GetDlgItem(hwnd,IDC_EDIT);
625 SendMessage(e,EM_SETSEL,0,(LPARAM)-1);
626 SetFocus(e);
627 return 0;
630 size_t buflen = strlen(buf);
631 if (!buflen) strcpy(buf,"/");
632 else
634 if (buflen > sizeof(buf)-2) buflen = sizeof(buf)-2;
635 if (buf[buflen-1]!='/') { buf[buflen++] = '/'; buf[buflen]=0; }
637 GetDlgItemText(hwnd,IDC_EDIT,msg,sizeof(msg));
638 preprocess_user_path(msg,sizeof(msg));
640 BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA);
641 int cnt;
642 if (parms->mode == BrowseFile_State::OPENMULTI && (cnt=ListView_GetSelectedCount(GetDlgItem(hwnd,IDC_LIST)))>1 && (!*msg || !strcmp(msg,multiple_files)))
644 HWND list = GetDlgItem(hwnd,IDC_LIST);
645 WDL_TypedBuf<char> fs;
646 fs.Set(buf,strlen(buf)+1);
647 int a = ListView_GetNextItem(list,-1,LVNI_SELECTED);
648 while (a != -1 && fs.GetSize() < 4096*4096 && cnt--)
650 if (a < 0 || a >= parms->viewlist.GetSize()) break;
651 const struct BrowseFile_State::rec *rec = parms->viewlist.Get(a);
652 if (!rec) break;
654 fs.Add(rec->name,strlen(rec->name)+1);
655 a = ListView_GetNextItem(list,a,LVNI_SELECTED);
657 fs.Add("",1);
659 parms->fnout = (char*)malloc(fs.GetSize());
660 if (parms->fnout) memcpy(parms->fnout,fs.Get(),fs.GetSize());
662 EndDialog(hwnd,1);
663 return 0;
665 else
667 if (msg[0] == '.' && (msg[1] == '.' || msg[1] == 0))
669 if (msg[1] == '.')
671 int a = (int) SendDlgItemMessage(hwnd,IDC_DIR,CB_GETCURSEL,0,0);
672 if (a>=0) SendDlgItemMessage(hwnd,IDC_DIR,CB_SETCURSEL,a+1,0);
674 SetDlgItemText(hwnd,IDC_EDIT,"");
675 SendMessage(hwnd,WM_UPD,1,0);
676 return 0;
678 else if (msg[0] == '/') lstrcpyn_safe(buf,msg,sizeof(buf));
679 else lstrcatn(buf,msg,sizeof(buf));
682 switch (parms->mode)
684 case BrowseFile_State::OPENDIR:
685 if (!buf[0]) return 0;
686 else if (msg[0])
688 // navigate to directory if filepart set
689 treatAsDir:
690 DIR *dir = opendir(buf);
691 if (!dir)
693 snprintf(msg,sizeof(msg),"Error opening directory:\r\n\r\n%s\r\n\r\nCreate?",buf);
694 if (MessageBox(hwnd,msg,"Create directory?",MB_OKCANCEL)==IDCANCEL) return 0;
695 CreateDirectory(buf,NULL);
696 dir=opendir(buf);
698 if (!dir) { MessageBox(hwnd,"Error creating directory","Error",MB_OK); return 0; }
699 closedir(dir);
700 SendMessage(hwnd, WM_UPD, IDC_DIR, (LPARAM)buf);
701 SetDlgItemText(hwnd,IDC_EDIT,"");
702 SendMessage(hwnd,WM_UPD,1,0);
704 return 0;
706 else
708 DIR *dir = opendir(buf);
709 if (!dir) return 0;
710 closedir(dir);
712 break;
713 case BrowseFile_State::SAVE:
714 if (!buf[0]) return 0;
715 else
717 struct stat64 st={0,};
718 DIR *dir = opendir(buf);
719 if (dir)
721 closedir(dir);
722 SendMessage(hwnd, WM_UPD, IDC_DIR, (LPARAM)buf);
723 SetDlgItemText(hwnd,IDC_EDIT,"");
724 SendMessage(hwnd,WM_UPD,1,0);
725 return 0;
727 if (buf[strlen(buf)-1] == '/') goto treatAsDir;
728 if (!stat64(buf,&st))
730 snprintf(msg,sizeof(msg),"File exists:\r\n\r\n%s\r\n\r\nOverwrite?",buf);
731 if (MessageBox(hwnd,msg,"Overwrite file?",MB_OKCANCEL)==IDCANCEL) return 0;
734 break;
735 default:
736 if (!buf[0]) return 0;
737 else
739 struct stat64 st={0,};
740 DIR *dir = opendir(buf);
741 if (dir)
743 closedir(dir);
744 SendMessage(hwnd, WM_UPD, IDC_DIR, (LPARAM)buf);
745 SetDlgItemText(hwnd,IDC_EDIT,"");
746 SendMessage(hwnd,WM_UPD,1,0);
747 return 0;
749 if (stat64(buf,&st))
751 //snprintf(msg,sizeof(msg),"File does not exist:\r\n\r\n%s",buf);
752 //MessageBox(hwnd,msg,"File not found",MB_OK);
753 return 0;
756 break;
758 if (parms->fnout)
760 lstrcpyn_safe(parms->fnout,buf,parms->fnout_sz);
762 else
764 size_t l = strlen(buf);
765 parms->fnout = (char*)calloc(l+2,1);
766 memcpy(parms->fnout,buf,l);
769 EndDialog(hwnd,1);
770 return 0;
772 break;
773 case WM_NOTIFY:
775 LPNMHDR l=(LPNMHDR)lParam;
776 if (l->code == LVN_GETDISPINFO)
778 BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA);
779 NMLVDISPINFO *lpdi = (NMLVDISPINFO*) lParam;
780 const int idx=lpdi->item.iItem;
781 if (l->idFrom == IDC_LIST && parms)
783 struct BrowseFile_State::rec *rec = parms->viewlist.Get(idx);
784 if (rec && rec->name)
786 if (lpdi->item.mask&LVIF_TEXT)
788 switch (lpdi->item.iSubItem)
790 case 0:
791 lstrcpyn_safe(lpdi->item.pszText,rec->name,lpdi->item.cchTextMax);
792 break;
793 case 1:
794 rec->format_size(lpdi->item.pszText,lpdi->item.cchTextMax);
795 break;
796 case 2:
797 rec->format_date(lpdi->item.pszText,lpdi->item.cchTextMax);
798 break;
804 else if (l->code == LVN_ODFINDITEM)
807 else if (l->code == LVN_ITEMCHANGED)
809 const int selidx = ListView_GetNextItem(l->hwndFrom, -1, LVNI_SELECTED);
810 BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA);
811 if (selidx>=0 && parms)
813 if (parms->mode == BrowseFile_State::OPENMULTI && ListView_GetSelectedCount(l->hwndFrom)>1)
815 SetDlgItemText(hwnd,IDC_EDIT,multiple_files);
817 else
819 struct BrowseFile_State::rec *rec = parms->viewlist.Get(selidx);
820 if (rec)
822 SetDlgItemText(hwnd,IDC_EDIT,rec->name);
827 else if (l->code == NM_DBLCLK)
829 SendMessage(hwnd,WM_COMMAND,IDOK,0);
831 else if (l->code == LVN_COLUMNCLICK)
833 NMLISTVIEW* lv = (NMLISTVIEW*) l;
834 BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA);
835 parms->sortrev=!parms->sortrev;
836 char lcol = parms->sortcol;
837 if ((int)parms->sortcol != lv->iSubItem)
839 parms->sortcol = (char)lv->iSubItem;
840 parms->sortrev=0;
843 HWND hdr = ListView_GetHeader(l->hwndFrom);
844 HDITEM hi;
845 memset(&hi,0,sizeof(hi));
846 hi.mask = HDI_FORMAT;
847 Header_SetItem(hdr,lcol,&hi);
848 hi.fmt = parms->sortrev ? HDF_SORTDOWN : HDF_SORTUP;
849 Header_SetItem(hdr,parms->sortcol,&hi);
851 parms->viewlist_sort(NULL);
852 ListView_RedrawItems(l->hwndFrom,0, parms->viewlist.GetSize());
855 break;
856 case WM_KEYDOWN:
857 if (lParam == FVIRTKEY && wParam == VK_F5)
859 SendMessage(hwnd,WM_UPD,1,0);
860 return 1;
862 else if (lParam == FVIRTKEY && wParam == VK_BACK &&
863 GetFocus() == GetDlgItem(hwnd,IDC_LIST))
865 SendMessage(hwnd,WM_COMMAND,IDC_PARENTBUTTON,0);
866 return 1;
868 else if (lParam == FVIRTKEY && wParam == VK_RETURN &&
869 GetFocus() == GetDlgItem(hwnd,IDC_LIST))
871 SendMessage(hwnd,WM_COMMAND,IDOK,0);
872 return 1;
874 return 0;
876 return 0;
879 // return true
880 bool BrowseForSaveFile(const char *text, const char *initialdir, const char *initialfile, const char *extlist,
881 char *fn, int fnsize)
883 BrowseFile_State state( text, initialdir, initialfile, extlist, BrowseFile_State::SAVE, fn, fnsize );
884 if (!DialogBoxParam(NULL,NULL,GetForegroundWindow(),swellFileSelectProc,(LPARAM)&state)) return false;
885 if (fn && fnsize > 0 && extlist && *extlist && WDL_get_fileext(fn)[0] != '.')
887 const char *erd = extlist+strlen(extlist)+1;
888 if (*erd == '*' && erd[1] == '.') // add default extension
890 const char *a = (erd+=1);
891 while (*erd && *erd != ';') erd++;
892 if (erd > a+1) snprintf_append(fn,fnsize,"%.*s",(int)(erd-a),a);
896 return true;
899 bool BrowseForDirectory(const char *text, const char *initialdir, char *fn, int fnsize)
901 BrowseFile_State state( text, initialdir, initialdir, NULL, BrowseFile_State::OPENDIR, fn, fnsize );
902 return !!DialogBoxParam(NULL,NULL,GetForegroundWindow(),swellFileSelectProc,(LPARAM)&state);
906 char *BrowseForFiles(const char *text, const char *initialdir,
907 const char *initialfile, bool allowmul, const char *extlist)
909 BrowseFile_State state( text, initialdir, initialfile, extlist,
910 allowmul ? BrowseFile_State::OPENMULTI : BrowseFile_State::OPEN, NULL, 0 );
911 return DialogBoxParam(NULL,NULL,GetForegroundWindow(),swellFileSelectProc,(LPARAM)&state) ? state.fnout : NULL;
915 static LRESULT WINAPI swellMessageBoxProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
917 enum { IDC_LABEL=0x100 };
918 const int button_spacing = 8;
919 switch (uMsg)
921 case WM_CREATE:
922 if (lParam) // swell-specific
924 SetWindowLong(hwnd,GWL_WNDPROC,(LPARAM)SwellDialogDefaultWindowProc);
925 SetWindowLong(hwnd,DWL_DLGPROC,(LPARAM)swellMessageBoxProc);
926 void **parms = (void **)lParam;
927 if (parms[1]) SetWindowText(hwnd,(const char*)parms[1]);
930 int nbuttons=1;
931 const char *buttons[3] = { "OK", "", "" };
932 int button_ids[3] = {IDOK,0,0};
933 int button_sizes[3];
935 int mode = ((int)(INT_PTR)parms[2]);
936 if (mode == MB_RETRYCANCEL) { buttons[0]="Retry"; button_ids[0]=IDRETRY; }
937 if (mode == MB_YESNO || mode == MB_YESNOCANCEL) { buttons[0]="Yes"; button_ids[0] = IDYES; buttons[nbuttons] = "No"; button_ids[nbuttons] = IDNO; nbuttons++; }
938 if (mode == MB_OKCANCEL || mode == MB_YESNOCANCEL || mode == MB_RETRYCANCEL) { buttons[nbuttons] = "Cancel"; button_ids[nbuttons] = IDCANCEL; nbuttons++; }
940 SWELL_MakeSetCurParms(1,1,0,0,hwnd,false,false);
941 RECT labsize = {0,0,300,20};
942 HWND lab = SWELL_MakeLabel(-1,parms[0] ? (const char *)parms[0] : "", IDC_LABEL, 0,0,10,10,SS_CENTER); //we'll resize this manually
943 HDC dc=GetDC(lab);
944 if (lab && parms[0])
946 DrawText(dc,(const char *)parms[0],-1,&labsize,DT_CALCRECT|DT_NOPREFIX);// if dc isnt valid yet, try anyway
949 const int sc10 = SWELL_UI_SCALE(10);
950 const int sc8 = SWELL_UI_SCALE(8);
951 labsize.top += sc10;
952 labsize.bottom += sc10 + sc8;
954 int x;
955 int button_height=0, button_total_w=0;;
956 const int bspace = SWELL_UI_SCALE(button_spacing);
957 for (x = 0; x < nbuttons; x ++)
959 RECT r={0,0,35,12};
960 DrawText(dc,buttons[x],-1,&r,DT_CALCRECT|DT_NOPREFIX|DT_SINGLELINE);
961 button_sizes[x] = r.right-r.left + sc10;
962 button_total_w += button_sizes[x] + (x ? bspace : 0);
963 if (r.bottom-r.top+sc10 > button_height) button_height = r.bottom-r.top+sc10;
966 if (labsize.right < button_total_w+sc8*2) labsize.right = button_total_w+sc8*2;
968 int xpos = labsize.right/2 - button_total_w/2;
969 for (x = 0; x < nbuttons; x ++)
971 SWELL_MakeButton(!x,buttons[x],button_ids[x],xpos,labsize.bottom,button_sizes[x],button_height,0);
972 xpos += button_sizes[x] + bspace;
975 if (dc) ReleaseDC(lab,dc);
976 SWELL_MakeSetCurParms(1,1,0,0,NULL,false,false);
977 SetWindowPos(hwnd,NULL,0,0,
978 labsize.right + sc8*2,labsize.bottom + button_height + sc8,SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE);
979 if (lab) SetWindowPos(lab,NULL,sc8,0,labsize.right,labsize.bottom,SWP_NOACTIVATE|SWP_NOZORDER);
980 SetFocus(GetDlgItem(hwnd,button_ids[0]));
982 break;
983 case WM_SIZE:
985 RECT r;
986 GetClientRect(hwnd,&r);
987 HWND h = GetWindow(hwnd,GW_CHILD);
988 int n = 10, w[8];
989 HWND tab[8],lbl=NULL;
990 int tabsz=0, bxwid=0, button_height=0;
991 while (h && n-- && tabsz<8)
993 int idx = GetWindowLong(h,GWL_ID);
994 if (idx == IDCANCEL || idx == IDOK || idx == IDNO || idx == IDYES || idx == IDRETRY)
996 RECT tr;
997 GetClientRect(h,&tr);
998 tab[tabsz] = h;
999 w[tabsz++] = tr.right - tr.left;
1000 button_height = tr.bottom-tr.top;
1001 bxwid += tr.right-tr.left;
1002 } else if (idx==IDC_LABEL) lbl=h;
1003 h = GetWindow(h,GW_HWNDNEXT);
1005 const int bspace = SWELL_UI_SCALE(button_spacing), sc8 = SWELL_UI_SCALE(8);
1006 if (lbl) SetWindowPos(h,NULL,sc8,0,r.right,r.bottom - sc8 - button_height, SWP_NOZORDER|SWP_NOACTIVATE);
1007 int xo = r.right/2 - (bxwid + (tabsz-1)*bspace)/2;
1008 for (int x=0; x<tabsz; x++)
1010 SetWindowPos(tab[x],NULL,xo,r.bottom - button_height - sc8, 0,0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
1011 xo += w[x] + bspace;
1014 break;
1015 case WM_COMMAND:
1016 if (LOWORD(wParam) && HIWORD(wParam) == BN_CLICKED)
1018 EndDialog(hwnd,LOWORD(wParam));
1020 break;
1021 case WM_CLOSE:
1022 if (GetDlgItem(hwnd,IDCANCEL)) EndDialog(hwnd,IDCANCEL);
1023 else if (GetDlgItem(hwnd,IDNO)) EndDialog(hwnd,IDNO);
1024 else EndDialog(hwnd,IDOK);
1025 break;
1027 return 0;
1030 int MessageBox(HWND hwndParent, const char *text, const char *caption, int type)
1032 #ifndef SWELL_LICE_GDI
1033 printf("MessageBox: %s %s\n",text,caption);
1034 #endif
1035 const void *parms[4]= {text,caption,(void*)(INT_PTR)type} ;
1036 return DialogBoxParam(NULL,NULL,hwndParent,swellMessageBoxProc,(LPARAM)parms);
1038 #if 0
1039 int ret=0;
1041 if (type == MB_OK)
1043 // todo
1044 ret=IDOK;
1046 else if (type == MB_OKCANCEL)
1048 ret = 1; // todo
1049 if (ret) ret=IDOK;
1050 else ret=IDCANCEL;
1052 else if (type == MB_YESNO)
1054 ret = 1 ; // todo
1055 if (ret) ret=IDYES;
1056 else ret=IDNO;
1058 else if (type == MB_RETRYCANCEL)
1060 ret = 1; // todo
1062 if (ret) ret=IDRETRY;
1063 else ret=IDCANCEL;
1065 else if (type == MB_YESNOCANCEL)
1067 ret = 1; // todo
1069 if (ret == 1) ret=IDYES;
1070 else if (ret==-1) ret=IDNO;
1071 else ret=IDCANCEL;
1074 return ret;
1075 #endif
1078 #ifdef SWELL_LICE_GDI
1079 struct ChooseColor_State {
1080 int ncustom;
1081 int *custom;
1083 int h,s,v;
1085 LICE_IBitmap *bm;
1088 // we need to make a more accurate LICE_HSV2RGB pair, this one is lossy, doh
1089 static LRESULT WINAPI swellColorSelectProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1091 static int s_reent,s_vmode;
1092 static int wndw, custsz, buth, border, butw, edh, edlw, edew, vsize, psize, yt;
1093 if (!wndw)
1095 wndw = SWELL_UI_SCALE(400);
1096 custsz = SWELL_UI_SCALE(20);
1097 butw = SWELL_UI_SCALE(50);
1098 buth = SWELL_UI_SCALE(24);
1099 border = SWELL_UI_SCALE(4);
1100 edh = SWELL_UI_SCALE(20);
1101 edlw = SWELL_UI_SCALE(16);
1102 edew = SWELL_UI_SCALE(40);
1103 vsize = SWELL_UI_SCALE(40);
1104 psize = border+edlw + edew;
1105 yt = border + psize + border + (edh + border)*6;
1108 const int customperrow = (wndw-border)/(custsz+border);
1109 switch (uMsg)
1111 case WM_CREATE:
1112 if (lParam) // swell-specific
1114 SetWindowLong(hwnd,GWL_WNDPROC,(LPARAM)SwellDialogDefaultWindowProc);
1115 SetWindowLong(hwnd,DWL_DLGPROC,(LPARAM)swellColorSelectProc);
1116 SetWindowLongPtr(hwnd,GWLP_USERDATA,lParam);
1118 SetWindowText(hwnd,"Choose Color");
1120 SWELL_MakeSetCurParms(1,1,0,0,hwnd,false,false);
1122 SWELL_MakeButton(0, "OK", IDOK,0,0,0,0, 0);
1123 SWELL_MakeButton(0, "Cancel", IDCANCEL,0,0,0,0, 0);
1125 static const char * const lbl[] = { "R","G","B","H","S","V"};
1126 for (int x=0;x<6;x++)
1128 SWELL_MakeLabel(0,lbl[x], 0x100+x, 0,0,0,0, 0);
1129 SWELL_MakeEditField(0x200+x, 0,0,0,0, 0);
1132 ChooseColor_State *cs = (ChooseColor_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
1133 SWELL_MakeSetCurParms(1,1,0,0,NULL,false,false);
1134 int nrows = ((cs?cs->ncustom : 0 ) + customperrow-1)/wdl_max(customperrow,1);
1135 SetWindowPos(hwnd,NULL,0,0, wndw,
1136 yt + buth + border + nrows * (custsz+border),
1137 SWP_NOZORDER|SWP_NOMOVE);
1138 SendMessage(hwnd,WM_USER+100,0,3);
1140 break;
1141 case WM_LBUTTONDOWN:
1142 case WM_RBUTTONDOWN:
1144 ChooseColor_State *cs = (ChooseColor_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
1145 if (!cs) break;
1146 RECT r;
1147 GetClientRect(hwnd,&r);
1148 const int xt = r.right - edew - edlw - border*3;
1150 const int y = GET_Y_LPARAM(lParam);
1151 const int x = GET_X_LPARAM(lParam);
1152 if (x < xt && y < yt)
1154 s_vmode = x >= xt-vsize;
1155 SetCapture(hwnd);
1156 // fall through
1158 else
1160 if (cs->custom && cs->ncustom && y >= yt && y < r.bottom - buth - border)
1162 int row = (y-yt) / (custsz+border), rowoffs = (y-yt) % (custsz+border);
1163 if (rowoffs < custsz)
1165 int col = (x-border) / (custsz+border), coloffs = (x-border) % (custsz+border);
1166 if (coloffs < custsz)
1168 col += customperrow*row;
1169 if (col >= 0 && col < cs->ncustom)
1171 if (uMsg == WM_LBUTTONDOWN)
1173 LICE_RGB2HSV(GetRValue(cs->custom[col]),GetGValue(cs->custom[col]),GetBValue(cs->custom[col]),&cs->h,&cs->s,&cs->v);
1174 SendMessage(hwnd,WM_USER+100,0,3);
1176 else
1178 int rv,gv,bv;
1179 LICE_HSV2RGB(cs->h,cs->s,cs->v,&rv,&gv,&bv);
1180 cs->custom[col] = RGB(rv,gv,bv);
1181 InvalidateRect(hwnd,NULL,FALSE);
1187 break;
1189 // fall through
1191 case WM_MOUSEMOVE:
1192 if (GetCapture()==hwnd)
1194 RECT r;
1195 GetClientRect(hwnd,&r);
1196 const int xt = r.right - edew - edlw - border*3;
1197 ChooseColor_State *cs = (ChooseColor_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
1198 if (!cs) break;
1199 int var = 255 - (GET_Y_LPARAM(lParam) - border)*256 / (yt-border*2);
1200 if (var<0)var=0;
1201 else if (var>255)var=255;
1202 if (s_vmode)
1204 if (var != cs->v)
1206 cs->v=var;
1207 SendMessage(hwnd,WM_USER+100,0,3);
1210 else
1212 int hue = (GET_X_LPARAM(lParam) - border)*384 / (xt-border - vsize);
1213 if (hue<0) hue=0;
1214 else if (hue>383) hue=383;
1215 if (cs->h != hue || cs->s != var)
1217 cs->h=hue;
1218 cs->s=var;
1219 SendMessage(hwnd,WM_USER+100,0,3);
1223 break;
1224 case WM_LBUTTONUP:
1225 case WM_RBUTTONUP:
1226 ReleaseCapture();
1227 break;
1228 case WM_PAINT:
1230 PAINTSTRUCT ps;
1231 ChooseColor_State *cs = (ChooseColor_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
1232 if (cs && BeginPaint(hwnd,&ps))
1234 RECT r;
1235 GetClientRect(hwnd,&r);
1236 const int xt = r.right - edew - edlw - border*3;
1237 if (cs->custom && cs->ncustom)
1239 int ypos = yt;
1240 int xpos = border;
1241 for (int x = 0; x < cs->ncustom; x ++)
1243 HBRUSH br = CreateSolidBrush(cs->custom[x]);
1244 RECT tr={xpos,ypos,xpos+custsz, ypos+custsz };
1245 FillRect(ps.hdc,&tr,br);
1246 DeleteObject(br);
1248 xpos += border+custsz;
1249 if (xpos+custsz >= r.right)
1251 xpos=border;
1252 ypos += border + custsz;
1258 int rr,g,b;
1259 LICE_HSV2RGB(cs->h,cs->s,cs->v,&rr,&g,&b);
1260 HBRUSH br = CreateSolidBrush(RGB(rr,g,b));
1261 RECT tr={r.right - border - psize, border, r.right-border, border+psize};
1262 FillRect(ps.hdc,&tr,br);
1263 DeleteObject(br);
1266 if (!cs->bm) cs->bm = new LICE_SysBitmap(xt-border,yt-border);
1267 else cs->bm->resize(xt-border,yt-border);
1269 int x1 = xt - border - vsize;
1270 int var = cs->v;
1272 const int ysz = yt-border*2;
1273 const int vary = ysz - 1 - (ysz * cs->v)/256;
1275 for (int y = 0; y < ysz; y ++)
1277 LICE_pixel *wr = cs->bm->getBits() + cs->bm->getRowSpan() * y;
1278 const int sat = 255 - y*256/ysz;
1279 double xx=0.0, dx=384.0/x1;
1280 int x;
1281 for (x = 0; x < x1; x++)
1283 *wr++ = LICE_HSV2Pix((int)(xx+0.5),sat,var,255);
1284 xx+=dx;
1286 LICE_pixel p = LICE_HSV2Pix(cs->h,cs->s,sat ^ (y==vary ? 128 : 0),255);
1287 for (;x < xt-border;x++) *wr++ = p;
1289 LICE_pixel p = LICE_HSV2Pix(cs->h,cs->s,(128+cs->v)&255,255);
1290 const int saty = ysz - 1 - (ysz * cs->s)/256;
1291 const int huex = (x1*cs->h)/384;
1292 LICE_Line(cs->bm,huex,saty-4,huex,saty+4,p,.75f,LICE_BLIT_MODE_COPY,false);
1293 LICE_Line(cs->bm,huex-4,saty,huex+4,saty,p,.75f,LICE_BLIT_MODE_COPY,false);
1295 BitBlt(ps.hdc,border,border,xt-border,ysz,cs->bm->getDC(),0,0,SRCCOPY);
1297 EndPaint(hwnd,&ps);
1301 break;
1302 case WM_GETMINMAXINFO:
1304 LPMINMAXINFO p=(LPMINMAXINFO)lParam;
1305 p->ptMinTrackSize.x = 300;
1306 p->ptMinTrackSize.y = 300;
1308 break;
1309 case WM_USER+100:
1311 ChooseColor_State *cs = (ChooseColor_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
1312 if (cs)
1314 int t[6];
1315 t[3] = cs->h;
1316 t[4] = cs->s;
1317 t[5] = cs->v;
1318 LICE_HSV2RGB(t[3],t[4],t[5],t,t+1,t+2);
1319 s_reent++;
1320 for (int x=0;x<6;x++) if (lParam & ((x<3)?1:2)) SetDlgItemInt(hwnd,0x200+x,x==3 ? t[x]*360/384 : t[x],FALSE);
1321 s_reent--;
1322 InvalidateRect(hwnd,NULL,FALSE);
1325 break;
1326 case WM_SIZE:
1328 RECT r;
1329 GetClientRect(hwnd,&r);
1330 int tx = r.right - edew-edlw-border*2, ty = border*2 + psize;
1331 for (int x=0;x<6;x++)
1333 SetWindowPos(GetDlgItem(hwnd,0x100+x),NULL,tx, ty, edlw, edh, SWP_NOZORDER|SWP_NOACTIVATE);
1334 SetWindowPos(GetDlgItem(hwnd,0x200+x),NULL,tx+edlw+border, ty, edew, edh, SWP_NOZORDER|SWP_NOACTIVATE);
1335 ty += border+edh;
1338 r.right -= border + butw;
1339 r.bottom -= border + buth;
1340 SetWindowPos(GetDlgItem(hwnd,IDCANCEL), NULL, r.right, r.bottom, butw, buth, SWP_NOZORDER|SWP_NOACTIVATE);
1341 r.right -= border*2 + butw;
1342 SetWindowPos(GetDlgItem(hwnd,IDOK), NULL, r.right, r.bottom, butw, buth, SWP_NOZORDER|SWP_NOACTIVATE);
1345 break;
1346 case WM_COMMAND:
1347 switch (LOWORD(wParam))
1349 case IDCANCEL:
1350 EndDialog(hwnd,0);
1351 break;
1352 case IDOK:
1353 EndDialog(hwnd,1);
1354 break;
1355 case 0x200:
1356 case 0x201:
1357 case 0x202:
1358 case 0x203:
1359 case 0x204:
1360 case 0x205:
1361 if (!s_reent)
1363 const bool ishsv = LOWORD(wParam) >= 0x203;
1364 int offs = ishsv ? 0x203 : 0x200;
1365 BOOL t = FALSE;
1366 int h = GetDlgItemInt(hwnd,offs++,&t,FALSE);
1367 if (!t) break;
1368 int s = GetDlgItemInt(hwnd,offs++,&t,FALSE);
1369 if (!t) break;
1370 int v = GetDlgItemInt(hwnd,offs++,&t,FALSE);
1371 if (!t) break;
1373 ChooseColor_State *cs = (ChooseColor_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
1374 if (!ishsv)
1376 if (h<0) h=0; else if (h>255) h=255;
1377 if (s<0) s=0; else if (s>255) s=255;
1378 if (v<0) v=0; else if (v>255) v=255;
1379 LICE_RGB2HSV(h,s,v,&h,&s,&v);
1381 else
1383 h = h * 384 / 360;
1384 if (h<0) h=0; else if (h>384) h=384;
1385 if (s<0) s=0; else if (s>255) s=255;
1386 if (v<0) v=0; else if (v>255) v=255;
1389 if (cs)
1391 cs->h = h;
1392 cs->s = s;
1393 cs->v = v;
1395 SendMessage(hwnd,WM_USER+100,0,ishsv?1:2);
1397 break;
1399 break;
1402 return 0;
1404 #endif //SWELL_LICE_GDI
1406 bool SWELL_ChooseColor(HWND h, int *val, int ncustom, int *custom)
1408 #ifdef SWELL_LICE_GDI
1409 ChooseColor_State state = { ncustom, custom };
1410 int c = val ? *val : 0;
1411 LICE_RGB2HSV(GetRValue(c),GetGValue(c),GetBValue(c),&state.h,&state.s,&state.v);
1412 bool rv = DialogBoxParam(NULL,NULL,h,swellColorSelectProc,(LPARAM)&state)!=0;
1413 delete state.bm;
1414 if (rv && val)
1416 int r,g,b;
1417 LICE_HSV2RGB(state.h,state.s,state.v,&r,&g,&b);
1418 *val = RGB(r,g,b);
1420 return rv;
1421 #else
1422 return false;
1423 #endif
1426 #if defined(SWELL_FREETYPE) && defined(SWELL_LICE_GDI)
1428 struct FontChooser_State
1430 FontChooser_State()
1432 hFont = 0;
1434 ~FontChooser_State()
1436 DeleteObject(hFont);
1439 LOGFONT font;
1440 HFONT hFont;
1441 WDL_FastString lastfn;
1444 extern const char *swell_last_font_filename;
1445 const char *swell_enumFontFiles(int x);
1446 int swell_getLineLength(const char *buf, int *post_skip, int wrap_maxwid, HDC hdc);
1448 static LRESULT WINAPI swellFontChooserProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1450 enum { IDC_LIST=0x100, IDC_FACE, IDC_SIZE, IDC_WEIGHT, IDC_ITALIC };
1451 enum { preview_h = 90, _border = 4, _buth = 24 };
1453 switch (uMsg)
1455 case WM_CREATE:
1456 if (lParam) // swell-specific
1458 SetWindowLong(hwnd,GWL_WNDPROC,(LPARAM)SwellDialogDefaultWindowProc);
1459 SetWindowLong(hwnd,DWL_DLGPROC,(LPARAM)swellFontChooserProc);
1460 SetWindowLongPtr(hwnd,GWLP_USERDATA,lParam);
1462 SetWindowText(hwnd,"Choose Font");
1464 SWELL_MakeSetCurParms(1,1,0,0,hwnd,false,false);
1466 SWELL_MakeButton(0, "OK", IDOK,0,0,0,0, 0);
1467 SWELL_MakeButton(0, "Cancel", IDCANCEL,0,0,0,0, 0);
1468 SWELL_MakeListBox(IDC_LIST,0,0,0,0, LBS_OWNERDRAWFIXED);
1469 SWELL_MakeEditField(IDC_FACE, 0,0,0,0, 0);
1470 SWELL_MakeEditField(IDC_SIZE, 0,0,0,0, 0);
1471 SWELL_MakeCombo(IDC_WEIGHT, 0,0,0,0, CBS_DROPDOWNLIST);
1472 SWELL_MakeCheckBox("Italic",IDC_ITALIC,0,0,0,0, 0);
1474 SendDlgItemMessage(hwnd,IDC_WEIGHT,CB_ADDSTRING,0,(LPARAM)"Normal");
1475 SendDlgItemMessage(hwnd,IDC_WEIGHT,CB_ADDSTRING,0,(LPARAM)"Bold");
1476 SendDlgItemMessage(hwnd,IDC_WEIGHT,CB_ADDSTRING,0,(LPARAM)"Light");
1478 SWELL_MakeSetCurParms(1,1,0,0,NULL,false,false);
1480 SetWindowPos(hwnd,NULL,0,0, 550,380, SWP_NOZORDER|SWP_NOMOVE);
1482 WDL_StringKeyedArray<char> list;
1483 const char *fontfile;
1484 int x;
1485 for (x=0; (fontfile=swell_enumFontFiles(x)); x ++)
1487 char buf[512];
1488 lstrcpyn_safe(buf,WDL_get_filepart(fontfile),sizeof(buf));
1489 char *tmp = buf;
1490 while (*tmp && *tmp != '-' && *tmp != '.') tmp++;
1491 *tmp=0;
1492 if (*buf) list.AddUnsorted(buf,true);
1494 list.Resort();
1495 FontChooser_State *cs = (FontChooser_State*)lParam;
1496 bool italics = cs->font.lfItalic!=0;
1497 int wt = cs->font.lfWeight;
1498 const char *lp=NULL;
1499 int cnt=0;
1500 for (x=0;x<list.GetSize();x++)
1502 const char *p=NULL;
1503 if (list.Enumerate(x,&p) && p)
1505 if (!stricmp(p,cs->font.lfFaceName))
1506 SendDlgItemMessage(hwnd,IDC_LIST,LB_SETCURSEL,cnt,0);
1508 size_t ll;
1509 if (lp && !strncmp(p,lp,ll=strlen(lp)))
1511 // if this is an extension of the last one, skip
1512 const char *trail = p+ll;
1513 if (strlen(trail)<=2)
1515 for (int y=0;y<2;y++)
1517 char c = *trail;
1518 if (c>0) c=toupper(c);
1519 if (c == 'B' || c == 'I' || c == 'L') trail++;
1522 else while (*trail)
1524 if (!strnicmp(trail,"Bold",4)) trail+=4;
1525 else if (!strnicmp(trail,"Light",5)) trail+=5;
1526 else if (!strnicmp(trail,"Italic",6)) trail+=6;
1527 else if (!strnicmp(trail,"Oblique",7)) trail+=7;
1528 else break;
1530 if (!*trail) continue;
1532 cnt++;
1533 SendDlgItemMessage(hwnd,IDC_LIST,LB_ADDSTRING,0,(LPARAM)p);
1534 lp=p;
1537 SetDlgItemText(hwnd,IDC_FACE,cs->font.lfFaceName);
1538 SetDlgItemInt(hwnd,IDC_SIZE,cs->font.lfHeight < 0 ? -cs->font.lfHeight : cs->font.lfHeight,TRUE);
1539 SendDlgItemMessage(hwnd,IDC_WEIGHT,CB_SETCURSEL,wt<=FW_LIGHT ? 2 : wt < FW_BOLD ? 0 : 1,0);
1540 if (italics)
1541 CheckDlgButton(hwnd,IDC_ITALIC,BST_CHECKED);
1543 break;
1544 case WM_DRAWITEM:
1546 DRAWITEMSTRUCT *di=(DRAWITEMSTRUCT *)lParam;
1547 FontChooser_State *cs = (FontChooser_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
1548 if (cs && di->CtlID == IDC_LIST)
1550 char buf[512];
1551 buf[0]=0;
1552 SendDlgItemMessage(hwnd,IDC_LIST,LB_GETTEXT,di->itemID,(WPARAM)buf);
1553 if (buf[0])
1555 HFONT font = CreateFont(g_swell_ctheme.default_font_size, 0, 0, 0, cs->font.lfWeight, cs->font.lfItalic,
1556 FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, buf);
1558 HGDIOBJ oldFont = SelectObject(di->hDC,font);
1559 DrawText(di->hDC,buf,-1,&di->rcItem,DT_VCENTER|DT_LEFT|DT_NOPREFIX);
1560 wchar_t tmp[] = {'a','A','z','Z'};
1561 unsigned short ind[4];
1562 GetGlyphIndicesW(di->hDC,tmp,4,ind,0);
1563 SelectObject(di->hDC,oldFont);
1565 int x;
1566 for (x=0;x<4 && ind[x]==0xffff;x++);
1567 if (x==4)
1569 RECT r = di->rcItem;
1570 r.right-=4;
1571 DrawText(di->hDC,buf,-1,&r,DT_VCENTER|DT_RIGHT|DT_NOPREFIX);
1573 DeleteObject(font);
1578 return 0;
1579 case WM_PAINT:
1581 PAINTSTRUCT ps;
1582 FontChooser_State *cs = (FontChooser_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
1583 if (cs && BeginPaint(hwnd,&ps))
1585 RECT r;
1586 GetClientRect(hwnd,&r);
1588 const int border = SWELL_UI_SCALE(_border);
1589 const int buth = SWELL_UI_SCALE(_buth);
1590 const int ph = SWELL_UI_SCALE(preview_h);
1591 r.left += border;
1592 r.right -= border;
1593 r.bottom -= border*2 + buth;
1594 r.top = r.bottom - ph;
1596 HFONT f = CreateFontIndirect(&cs->font);
1597 HBRUSH br = CreateSolidBrush(RGB(255,255,255));
1598 FillRect(ps.hdc,&r,br);
1599 DeleteObject(br);
1600 SetTextColor(ps.hdc,RGB(0,0,0));
1601 SetBkMode(ps.hdc,TRANSPARENT);
1602 r.right-=4;
1603 r.left+=4;
1604 if (swell_last_font_filename)
1606 r.bottom -= DrawText(ps.hdc,swell_last_font_filename,-1,&r,DT_BOTTOM|DT_NOPREFIX|DT_SINGLELINE|DT_RIGHT);
1609 HGDIOBJ oldFont = SelectObject(ps.hdc,f);
1611 extern const char *g_swell_fontpangram;
1612 const char *str = g_swell_fontpangram;
1614 // thanks, http://dailypangram.tumblr.com/ :)
1615 if (!str) str = "Strangely, aerobic exercise doesn’t quite work with improvised free jazz.";
1617 while (*str)
1619 int sk=0, lb=swell_getLineLength(str, &sk, r.right-r.left, ps.hdc);
1620 if (!lb&&!sk) break;
1621 if (lb>0) r.top += DrawText(ps.hdc,str,lb,&r,DT_TOP|DT_LEFT|DT_NOPREFIX|DT_SINGLELINE);
1622 str+=lb+sk;
1626 SelectObject(ps.hdc,oldFont);
1627 DeleteObject(f);
1630 EndPaint(hwnd,&ps);
1634 break;
1635 case WM_GETMINMAXINFO:
1637 LPMINMAXINFO p=(LPMINMAXINFO)lParam;
1638 p->ptMinTrackSize.x = 400;
1639 p->ptMinTrackSize.y = 300;
1641 break;
1642 case WM_SIZE:
1644 RECT r;
1645 GetClientRect(hwnd,&r);
1646 const int border = SWELL_UI_SCALE(_border);
1647 const int buth = SWELL_UI_SCALE(_buth);
1648 const int butw = SWELL_UI_SCALE(50);
1649 const int edh = SWELL_UI_SCALE(20);
1650 const int size_w = SWELL_UI_SCALE(50);
1651 const int wt_w = SWELL_UI_SCALE(80);
1652 const int italic_w = SWELL_UI_SCALE(60);
1654 r.left += border;
1655 r.right -= border;
1657 r.bottom -= border + buth;
1658 SetWindowPos(GetDlgItem(hwnd,IDCANCEL), NULL, r.right - butw, r.bottom, butw, buth, SWP_NOZORDER|SWP_NOACTIVATE);
1659 SetWindowPos(GetDlgItem(hwnd,IDOK), NULL, r.right - border - butw*2, r.bottom, butw, buth, SWP_NOZORDER|SWP_NOACTIVATE);
1660 r.bottom -= SWELL_UI_SCALE(preview_h) + border;
1661 int psz=wdl_max(g_swell_ctheme.combo_height,edh);
1662 r.bottom -= psz + border;
1663 SetWindowPos(GetDlgItem(hwnd,IDC_FACE),NULL,r.left,r.bottom + (psz-edh)/2,
1664 r.right-r.left - size_w-wt_w-italic_w - border*3, edh, SWP_NOZORDER|SWP_NOACTIVATE);
1665 SetWindowPos(GetDlgItem(hwnd,IDC_SIZE),NULL,r.right-size_w-wt_w-italic_w-border*2,r.bottom + (psz-edh)/2,
1666 size_w, edh, SWP_NOZORDER|SWP_NOACTIVATE);
1667 SetWindowPos(GetDlgItem(hwnd,IDC_WEIGHT),NULL,r.right-wt_w-italic_w-border,r.bottom + (psz-g_swell_ctheme.combo_height)/2,
1668 wt_w, g_swell_ctheme.combo_height, SWP_NOZORDER|SWP_NOACTIVATE);
1669 SetWindowPos(GetDlgItem(hwnd,IDC_ITALIC),NULL,r.right-italic_w,r.bottom + (psz-edh)/2,
1670 italic_w, edh, SWP_NOZORDER|SWP_NOACTIVATE);
1672 SetWindowPos(GetDlgItem(hwnd,IDC_LIST), NULL, border, border, r.right, r.bottom - border*2, SWP_NOZORDER|SWP_NOACTIVATE);
1676 break;
1677 case WM_COMMAND:
1678 switch (LOWORD(wParam))
1680 case IDC_LIST:
1681 if (HIWORD(wParam) == LBN_SELCHANGE)
1683 int idx = (int) SendDlgItemMessage(hwnd,IDC_LIST,LB_GETCURSEL,0,0);
1684 if (idx>=0)
1686 char buf[512];
1687 buf[0]=0;
1688 SendDlgItemMessage(hwnd,IDC_LIST,LB_GETTEXT,idx,(WPARAM)buf);
1689 if (buf[0]) SetDlgItemText(hwnd,IDC_FACE,buf);
1692 break;
1693 case IDC_SIZE:
1694 case IDC_FACE:
1695 case IDC_ITALIC:
1696 case IDC_WEIGHT:
1698 FontChooser_State *cs = (FontChooser_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
1699 if (cs)
1701 if (LOWORD(wParam) == IDC_FACE)
1702 GetDlgItemText(hwnd,IDC_FACE,cs->font.lfFaceName,sizeof(cs->font.lfFaceName));
1703 else if (LOWORD(wParam) == IDC_SIZE)
1705 BOOL t;
1706 int a = GetDlgItemInt(hwnd,IDC_SIZE,&t,FALSE);
1707 if (t)
1709 if (cs->font.lfHeight < 0) cs->font.lfHeight = -a;
1710 else cs->font.lfHeight = a;
1713 else if (LOWORD(wParam) == IDC_ITALIC) cs->font.lfItalic = IsDlgButtonChecked(hwnd,IDC_ITALIC) ? 1:0;
1714 else if (LOWORD(wParam) == IDC_WEIGHT && HIWORD(wParam) == CBN_SELCHANGE)
1716 int idx = (int) SendDlgItemMessage(hwnd,IDC_WEIGHT,CB_GETCURSEL,0,0);
1717 if (idx==0) cs->font.lfWeight = FW_NORMAL;
1718 else if (idx==1) cs->font.lfWeight = FW_BOLD;
1719 else if (idx==2) cs->font.lfWeight = FW_LIGHT;
1721 InvalidateRect(hwnd,NULL,FALSE);
1724 break;
1725 case IDCANCEL:
1726 EndDialog(hwnd,0);
1727 break;
1728 case IDOK:
1729 EndDialog(hwnd,1);
1730 break;
1732 break;
1735 return 0;
1738 void *swell_MatchFont(const char *lfFaceName, int weight, int italic, const char **fnOut);
1740 #endif
1742 bool SWELL_ChooseFont(HWND h, LOGFONT *lf)
1744 #if defined(SWELL_FREETYPE) && defined(SWELL_LICE_GDI)
1745 FontChooser_State state;
1746 state.font = *lf;
1748 bool rv = DialogBoxParam(NULL,NULL,h,swellFontChooserProc,(LPARAM)&state)!=0;
1749 if (rv)
1751 *lf = state.font;
1753 return rv;
1754 #else
1755 return false;
1756 #endif
1759 #endif