/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) Copyright (C) 2006 and later, Cockos, Inc. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. This file provides basic APIs for browsing for files, directories, and messageboxes. These APIs don't all match the Windows equivelents, but are close enough to make it not too much trouble. */ #ifndef SWELL_PROVIDED_BY_APP #include "swell.h" #include "swell-internal.h" #include "swell-dlggen.h" #include "../wdlcstring.h" #include "../assocarray.h" #include "../ptrlist.h" #include #include #include "../lineparse.h" #define WDL_HASSTRINGS_EXPORT static #include "../has_strings.h" #ifndef SWELL_BROWSE_RECENT_SIZE #define SWELL_BROWSE_RECENT_SIZE 12 #endif static WDL_PtrList s_browse_rcu, s_browse_rcu_tmp; static int recent_size() { return s_browse_rcu.GetSize() + s_browse_rcu_tmp.GetSize(); } static void recent_addtocb(HWND hwnd) { int x; for (x=0;x=SWELL_BROWSE_RECENT_SIZE) s_browse_rcu.Delete(SWELL_BROWSE_RECENT_SIZE,true,free); s_browse_rcu.Insert(0,strdup(path)); } for (x=0;x<=s_browse_rcu.GetSize();x++) { char tmp[64]; snprintf(tmp,sizeof(tmp),"path%d",x); WritePrivateProfileString(".swell_recent_path",tmp, s_browse_rcu.Get(x),""); } } static void recent_read() { s_browse_rcu_tmp.Empty(true,free); if (s_browse_rcu.GetSize()) return; int x; for (x=0;x 0 && date64 < WDL_INT64_CONST(0x793406fff)) { struct tm *a=localtime(&date); if (a) strftime(buf,bufsz,"%c",a); } } void format_size(char *buf, int bufsz) { if (type == 1) { lstrcpyn_safe(buf,"",bufsz); } else { static const char *tab[]={ "bytes","KB","MB","GB" }; int lf=0; WDL_INT64 s=size; if (s<1024) { snprintf(buf,bufsz,"%d %s",(int)s,tab[0]); } else { int w = 1; do { w++; lf = (int)(s&1023); s/=1024; } while (s >= 1024 && w<4); snprintf(buf,bufsz,"%d.%d %s",(int)s,(int)((lf*10.0)/1024.0+0.5),tab[w-1]); } } } char *format_all(char *buf, int bufsz) { char dstr[128],sstr[128]; format_date(dstr,sizeof(dstr)); format_size(sstr,sizeof(sstr)); snprintf(buf,bufsz,"%s\t%s\t%s",WDL_get_filepart(name),dstr,sstr); return buf; } }; void viewlist_clear() { rec *l = viewlist_store.Get(); const int n = viewlist_store.GetSize(); for (int x = 0; x < n; x ++) free(l[x].name); viewlist_store.Resize(0); viewlist.Empty(); } WDL_TypedBuf viewlist_store; WDL_PtrList viewlist; bool show_hidden; void viewlist_sort(const char *filter) { if (filter) { viewlist.Empty(); LineParser lp; const bool no_filter = !*filter || !WDL_makeSearchFilter(filter,&lp); for (int x=0;xformat_all(tmp,sizeof(tmp)),&lp)) viewlist.Add(r); } } s_sortrev = sortrev; if (viewlist.GetSize()>1) qsort(viewlist.GetList(), viewlist.GetSize(),sizeof(rec*), sortcol == 1 ? sortFunc_sz : sortcol == 2 ? sortFunc_date : sortFunc_fn); } static int sortFunc_fn(const void *_a, const void *_b) { const rec *a = *(const rec * const*)_a, *b = *(const rec * const*)_b; int d = a->type - b->type; if (d) return d; d = stricmp(a->name,b->name); return s_sortrev ? -d : d; } static int sortFunc_date(const void *_a, const void *_b) { const rec *a = *(const rec * const*)_a, *b = *(const rec * const*)_b; if (a->date != b->date) return s_sortrev ? (a->date>b->date?-1:1) : (a->date>b->date?1:-1); return stricmp(a->name,b->name); } static int sortFunc_sz(const void *_a, const void *_b) { const rec *a = *(const rec * const *)_a, *b = *(const rec * const *)_b; int d = a->type - b->type; if (d) return s_sortrev ? -d : d; if (a->size != b->size) return s_sortrev ? (a->size>b->size?-1:1) : (a->size>b->size?1:-1); return stricmp(a->name,b->name); } void scan_path(const char *path, const char *filterlist, bool dir_only) { viewlist_clear(); DIR *dir = opendir(path); if (!dir) return; char tmp[2048]; struct dirent *ent; while (NULL != (ent = readdir(dir))) { if (ent->d_name[0] == '.') { if (ent->d_name[1] == 0 || ent->d_name[1] == '.' || !show_hidden) continue; } bool is_dir = (ent->d_type == DT_DIR); if (ent->d_type == DT_UNKNOWN) { snprintf(tmp,sizeof(tmp),"%s/%s",path,ent->d_name); DIR *d = opendir(tmp); if (d) { is_dir = true; closedir(d); } } else if (ent->d_type == DT_LNK) { snprintf(tmp,sizeof(tmp),"%s/%s",path,ent->d_name); char *rp = realpath(tmp,NULL); if (rp) { DIR *d = opendir(rp); if (d) { is_dir = true; closedir(d); } free(rp); } } if (!dir_only || is_dir) { if (filterlist && *filterlist && !is_dir) { const char *f = filterlist; while (*f) { const char *nf = f; while (*nf && *nf != ';') nf++; if (*f != '*') { const char *nw = f; while (nw < nf && *nw != '*') nw++; if ((nw!=nf || f+strlen(ent->d_name) == nw) && !strncasecmp(ent->d_name,f,nw-f)) { // matched leading text if (nw == nf) break; f = nw; } } if (*f == '*') { f++; if (!*f || *f == ';' || (*f == '.' && f[1] == '*')) break; size_t l = strlen(ent->d_name); if (f+l > nf && !strncasecmp(ent->d_name + l - (nf-f), f,nf-f)) break; } f = nf; while (*f == ';') f++; } if (!*f) continue; // did not match } snprintf(tmp,sizeof(tmp),"%s/%s",path,ent->d_name); struct stat st={0,}; stat(tmp,&st); rec r = { st.st_size, st.st_mtime, strdup(ent->d_name), is_dir?1:2 } ; viewlist_store.Add(&r,1); } } // sort viewlist closedir(dir); } }; char BrowseFile_State::s_sortrev; static void preprocess_user_path(char *buf, int bufsz) { if (buf[0] == '~') { char *tmp = strdup(buf+1); if (buf[1] == '/' || !buf[1]) { char *p = getenv("HOME"); if (p && *p) snprintf(buf,bufsz,"%s%s",p,tmp); } else { snprintf(buf,bufsz,"/home/%s",tmp); // if someone wants to write code to lookup homedirs, please, go right ahead! } free(tmp); } } static LRESULT WINAPI swellFileSelectProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { enum { IDC_EDIT=0x100, IDC_LABEL, IDC_CHILD, IDC_DIR, IDC_LIST, IDC_EXT, IDC_PARENTBUTTON, IDC_FILTER, ID_SHOW_HIDDEN }; enum { WM_UPD=WM_USER+100 }; const int maxPathLen = 2048; const char *multiple_files = "(multiple files)"; switch (uMsg) { case WM_CREATE: if (lParam) // swell-specific { SetWindowLong(hwnd,GWL_WNDPROC,(LPARAM)SwellDialogDefaultWindowProc); SetWindowLong(hwnd,DWL_DLGPROC,(LPARAM)swellFileSelectProc); SetWindowLong(hwnd,GWL_STYLE, GetWindowLong(hwnd,GWL_STYLE)|WS_THICKFRAME); SetWindowLongPtr(hwnd,GWLP_USERDATA,lParam); BrowseFile_State *parms = (BrowseFile_State *)lParam; char tmp[1024]; recent_read(); recent_add_tmp(parms->initialdir); if (parms->initialfile && *parms->initialfile != '.') { lstrcpyn_safe(tmp,parms->initialfile,sizeof(tmp)); WDL_remove_filepart(tmp); recent_add_tmp(tmp); } if (parms->caption) SetWindowText(hwnd,parms->caption); SWELL_MakeSetCurParms(1,1,0,0,hwnd,false,false); HWND edit = SWELL_MakeEditField(IDC_EDIT, 0,0,0,0, 0); SWELL_MakeButton(0, parms->mode == BrowseFile_State::OPENDIR ? "Choose directory" : parms->mode == BrowseFile_State::SAVE ? "Save" : "Open", IDOK,0,0,0,0, 0); SWELL_MakeButton(0, "Cancel", IDCANCEL,0,0,0,0, 0); HWND dir = SWELL_MakeCombo(IDC_DIR, 0,0,0,0, 0); SWELL_MakeButton(0, "..", IDC_PARENTBUTTON, 0,0,0,0, 0); SWELL_MakeEditField(IDC_FILTER, 0,0,0,0, 0); const char *ent = parms->mode == BrowseFile_State::OPENDIR ? "dir_browser" : "file_browser"; GetPrivateProfileString(".swell",ent,"", tmp,sizeof(tmp),""); int x=0,y=0,w=0,h=0, c1=0,c2=0,c3=0,extraflag=0; int flag = SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER; if (tmp[0] && sscanf(tmp,"%d %d %d %d %d %d %d %d",&x,&y,&w,&h,&c1,&c2,&c3,&extraflag) >= 4) flag &= ~SWP_NOMOVE; if (w < 100) w=SWELL_UI_SCALE(600); if (h < 100) h=SWELL_UI_SCALE(400); if (extraflag&1) parms->show_hidden=true; if (c1 + c2 + c3 < w/2) { c1=SWELL_UI_SCALE(280); c2=SWELL_UI_SCALE(120); c3=SWELL_UI_SCALE(140); } HWND list = SWELL_MakeControl("",IDC_LIST,"SysListView32",LVS_REPORT|LVS_SHOWSELALWAYS| (parms->mode == BrowseFile_State::OPENMULTI ? 0 : LVS_SINGLESEL)| LVS_OWNERDATA|WS_BORDER|WS_TABSTOP,0,0,0,0,0); if (list) { LVCOLUMN c={LVCF_TEXT|LVCF_WIDTH, 0, c1, (char*)"Filename" }; ListView_InsertColumn(list,0,&c); c.cx = c2; c.pszText = (char*) "Size"; ListView_InsertColumn(list,1,&c); c.cx = c3; c.pszText = (char*) "Date"; ListView_InsertColumn(list,2,&c); HWND hdr = ListView_GetHeader(list); HDITEM hi; memset(&hi,0,sizeof(hi)); hi.mask = HDI_FORMAT; hi.fmt = parms->sortrev ? HDF_SORTDOWN : HDF_SORTUP; Header_SetItem(hdr,parms->sortcol,&hi); } HWND extlist = (parms->extlist && *parms->extlist) ? SWELL_MakeCombo(IDC_EXT, 0,0,0,0, CBS_DROPDOWNLIST) : NULL; if (extlist) { const char *p = parms->extlist; while (*p) { const char *rd=p; p += strlen(p)+1; if (!*p) break; int a = SendMessage(extlist,CB_ADDSTRING,0,(LPARAM)rd); SendMessage(extlist,CB_SETITEMDATA,a,(LPARAM)p); p += strlen(p)+1; } int sel = 0; if (parms->initialfile && *parms->initialfile) { sel = ext_valid_for_extlist(WDL_get_fileext(parms->initialfile), parms->extlist); if (sel<0) sel=0; } SendMessage(extlist,CB_SETCURSEL,sel,0); } SWELL_MakeLabel(-1,parms->mode == BrowseFile_State::OPENDIR ? "Directory: " : "File:",IDC_LABEL, 0,0,0,0, 0); if (BFSF_Templ_dlgid && BFSF_Templ_dlgproc) { HWND dlg = SWELL_CreateDialog(BFSF_Templ_reshead, BFSF_Templ_dlgid, hwnd, BFSF_Templ_dlgproc, 0); if (dlg) SetWindowLong(dlg,GWL_ID,IDC_CHILD); BFSF_Templ_dlgproc=0; BFSF_Templ_dlgid=0; } SWELL_MakeSetCurParms(1,1,0,0,NULL,false,false); if (edit && dir) { char buf[maxPathLen]; const char *filepart = ""; if (parms->initialfile && *parms->initialfile && *parms->initialfile != '.') { lstrcpyn_safe(buf,parms->initialfile,sizeof(buf)); char *p = (char *)WDL_get_filepart(buf); if (p > buf) { p[-1]=0; filepart = p; } else { filepart = parms->initialfile; goto get_dir; } } else { get_dir: if (parms->initialdir && *parms->initialdir && strcmp(parms->initialdir,".")) { lstrcpyn_safe(buf,parms->initialdir,sizeof(buf)); } else if (!getcwd(buf,sizeof(buf))) buf[0]=0; } SetWindowText(edit,filepart); SendMessage(hwnd, WM_UPD, IDC_DIR, (LPARAM)buf); } if (list) SetWindowPos(list,HWND_BOTTOM,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE); SetWindowPos(hwnd,NULL,x,y, w,h, flag); SendMessage(hwnd,WM_UPD,1,0); SendMessage(edit,EM_SETSEL,0,(LPARAM)-1); SetFocus(edit); } break; case WM_DESTROY: { BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA); if (parms) { RECT r; GetWindowRect(hwnd,&r); HWND list = GetDlgItem(hwnd,IDC_LIST); const int c1 = ListView_GetColumnWidth(list,0); const int c2 = ListView_GetColumnWidth(list,1); const int c3 = ListView_GetColumnWidth(list,2); char tmp[128]; int extraflag=0; if (parms->show_hidden) extraflag|=1; snprintf(tmp,sizeof(tmp),"%d %d %d %d %d %d %d %d",r.left,r.top,r.right-r.left,r.bottom-r.top,c1,c2,c3,extraflag); const char *ent = parms->mode == BrowseFile_State::OPENDIR ? "dir_browser" : "file_browser"; WritePrivateProfileString(".swell",ent, tmp, ""); } } break; case WM_UPD: switch (wParam) { case IDC_DIR: // update directory combo box -- destroys buffer pointed to by lParam if (lParam) { char *path = (char*)lParam; HWND combo=GetDlgItem(hwnd,IDC_DIR); SendMessage(combo,CB_RESETCONTENT,0,0); WDL_remove_trailing_dirchars(path); while (path[0]) { SendMessage(combo,CB_ADDSTRING,0,(LPARAM)path); WDL_remove_filepart(path); WDL_remove_trailing_dirchars(path); } SendMessage(combo,CB_ADDSTRING,0,(LPARAM)"/"); recent_addtocb(combo); SendMessage(combo,CB_SETCURSEL,0,0); } break; case IDC_EXT: case 1: { BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA); if (parms) { SetDlgItemText(hwnd,IDC_FILTER,""); KillTimer(hwnd,1); char buf[maxPathLen]; const char *filt = NULL; HWND ext = GetDlgItem(hwnd,IDC_EXT); if (ext) { LRESULT aidx = SendMessage(ext,CB_GETCURSEL,0,0); if (aidx != CB_ERR) { filt = (const char *)SendMessage(ext,CB_GETITEMDATA,aidx,0); if (wParam == IDC_EXT && parms->extlist && *parms->extlist) { GetDlgItemText(hwnd,IDC_EDIT,buf,sizeof(buf)); if (buf[0]) { const char *erd = filt; if (*erd == '*' && erd[1] == '.' && erd[2] && erd[2] != '*') { const char *a = (erd+=1); while (*erd && *erd != ';') erd++; if (erd > a+1) { if (ext_valid_for_extlist(WDL_get_fileext(buf),parms->extlist)>=0) { WDL_remove_fileext(buf); } else { char *p = buf; while (*p) p++; while (p > buf && (p[-1] == ' ' || p[-1] == '.' || p[-1] == '/' || p[-1] == '\\')) p--; *p=0; } if (buf[0]) { snprintf_append(buf,sizeof(buf),"%.*s",(int)(erd-a),a); SetDlgItemText(hwnd,IDC_EDIT,buf); } } } } } } } GetDlgItemText(hwnd,IDC_DIR,buf,sizeof(buf)); preprocess_user_path(buf,sizeof(buf)); if (buf[0]) parms->scan_path(buf, filt, parms->mode == BrowseFile_State::OPENDIR); else parms->viewlist_clear(); HWND list = GetDlgItem(hwnd,IDC_LIST); ListView_SetItemCount(list, 0); // clear selection parms->viewlist_sort(""); ListView_SetItemCount(list, parms->viewlist.GetSize()); ListView_RedrawItems(list,0, parms->viewlist.GetSize()); } } break; } break; case WM_GETMINMAXINFO: { LPMINMAXINFO p=(LPMINMAXINFO)lParam; p->ptMinTrackSize.x = 300; p->ptMinTrackSize.y = 300; } break; case WM_SIZE: { BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA); // reposition controls RECT r; GetClientRect(hwnd,&r); const int buth = SWELL_UI_SCALE(24), cancelbutw = SWELL_UI_SCALE(50), okbutw = SWELL_UI_SCALE(parms->mode == BrowseFile_State::OPENDIR ? 120 : 50); const int xborder = SWELL_UI_SCALE(4), yborder=SWELL_UI_SCALE(8); const int fnh = SWELL_UI_SCALE(20), fnlblw = SWELL_UI_SCALE(parms->mode == BrowseFile_State::OPENDIR ? 70 : 50); const int ypad = SWELL_UI_SCALE(4); int ypos = r.bottom - ypad - buth; int xpos = r.right; SetWindowPos(GetDlgItem(hwnd,IDCANCEL), NULL, xpos -= cancelbutw + xborder, ypos, cancelbutw,buth, SWP_NOZORDER|SWP_NOACTIVATE); SetWindowPos(GetDlgItem(hwnd,IDOK), NULL, xpos -= okbutw + xborder, ypos, okbutw,buth, SWP_NOZORDER|SWP_NOACTIVATE); HWND emb = GetDlgItem(hwnd,IDC_CHILD); if (emb) { RECT sr; GetClientRect(emb,&sr); if (ypos > r.bottom-ypad-sr.bottom) ypos = r.bottom-ypad-sr.bottom; SetWindowPos(emb,NULL, xborder,ypos, xpos - xborder*2, sr.bottom, SWP_NOZORDER|SWP_NOACTIVATE); ShowWindow(emb,SW_SHOWNA); } HWND filt = GetDlgItem(hwnd,IDC_EXT); if (filt) { SetWindowPos(filt, NULL, xborder*2 + fnlblw, ypos -= fnh + yborder, r.right-fnlblw-xborder*3, fnh, SWP_NOZORDER|SWP_NOACTIVATE); } SetWindowPos(GetDlgItem(hwnd,IDC_EDIT), NULL, xborder*2 + fnlblw, ypos -= fnh + yborder, r.right-fnlblw-xborder*3, fnh, SWP_NOZORDER|SWP_NOACTIVATE); SetWindowPos(GetDlgItem(hwnd,IDC_LABEL), NULL, xborder, ypos, fnlblw, fnh, SWP_NOZORDER|SWP_NOACTIVATE); const int comboh = g_swell_ctheme.combo_height; const int filterw = wdl_max(r.right/8, SWELL_UI_SCALE(50)); SetWindowPos(GetDlgItem(hwnd,IDC_DIR), NULL, xborder, yborder/2, r.right-xborder*4 - comboh - filterw, comboh, SWP_NOZORDER|SWP_NOACTIVATE); SetWindowPos(GetDlgItem(hwnd,IDC_PARENTBUTTON),NULL, r.right-xborder*2-comboh - filterw,yborder/2, comboh,comboh,SWP_NOZORDER|SWP_NOACTIVATE); SetWindowPos(GetDlgItem(hwnd,IDC_FILTER),NULL, r.right-xborder-filterw,yborder/2 + (comboh-fnh)/2, filterw,fnh,SWP_NOZORDER|SWP_NOACTIVATE); 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); } break; case WM_TIMER: if (wParam == 1) { KillTimer(hwnd,1); BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA); if (parms) { char buf[128]; GetDlgItemText(hwnd,IDC_FILTER,buf,sizeof(buf)); parms->viewlist_sort(buf); HWND list = GetDlgItem(hwnd,IDC_LIST); ListView_SetItemCount(list, parms->viewlist.GetSize()); ListView_RedrawItems(list,0, parms->viewlist.GetSize()); } } break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_FILTER: if (HIWORD(wParam) == EN_CHANGE) { KillTimer(hwnd,1); SetTimer(hwnd,1,250,NULL); } return 0; case IDC_EXT: if (HIWORD(wParam) == CBN_SELCHANGE) { SendMessage(hwnd,WM_UPD,IDC_EXT,0); } return 0; case IDC_PARENTBUTTON: { int a = (int) SendDlgItemMessage(hwnd,IDC_DIR,CB_GETCURSEL,0,0); int cbcnt = (int) SendDlgItemMessage(hwnd,IDC_DIR,CB_GETCOUNT,0,0); if (a>=0 && a < cbcnt - recent_size()) { SendDlgItemMessage(hwnd,IDC_DIR,CB_SETCURSEL,a+1,0); } else { char buf[maxPathLen]; GetDlgItemText(hwnd,IDC_DIR,buf,sizeof(buf)); preprocess_user_path(buf,sizeof(buf)); WDL_remove_filepart(buf); if (a>=0) SendMessage(hwnd,WM_UPD,IDC_DIR,(LPARAM)buf); else SetDlgItemText(hwnd,IDC_DIR,buf); } SendMessage(hwnd,WM_UPD,1,0); } return 0; case IDC_DIR: if (HIWORD(wParam) == CBN_SELCHANGE) { int a = (int) SendDlgItemMessage(hwnd,IDC_DIR,CB_GETCURSEL,0,0); int cbcnt = (int) SendDlgItemMessage(hwnd,IDC_DIR,CB_GETCOUNT,0,0); if (a>=cbcnt - recent_size()) { char buf[maxPathLen]; GetDlgItemText(hwnd,IDC_DIR,buf,sizeof(buf)); preprocess_user_path(buf,sizeof(buf)); SendMessage(hwnd,WM_UPD,IDC_DIR,(LPARAM)buf); } SendMessage(hwnd,WM_UPD,1,0); } return 0; case IDCANCEL: EndDialog(hwnd,0); return 0; case IDOK: { char buf[maxPathLen], msg[2048]; GetDlgItemText(hwnd,IDC_DIR,buf,sizeof(buf)); preprocess_user_path(buf,sizeof(buf)); if (GetFocus() == GetDlgItem(hwnd,IDC_DIR)) { DIR *dir = opendir(buf); if (!dir) { //snprintf(msg,sizeof(msg),"Path does not exist:\r\n\r\n%s",buf); //MessageBox(hwnd,msg,"Path not found",MB_OK); return 0; } closedir(dir); SendMessage(hwnd,WM_UPD,1,0); SendMessage(hwnd, WM_UPD, IDC_DIR, (LPARAM)buf); HWND e = GetDlgItem(hwnd,IDC_EDIT); SendMessage(e,EM_SETSEL,0,(LPARAM)-1); SetFocus(e); return 0; } size_t buflen = strlen(buf); if (!buflen) strcpy(buf,"/"); else { if (buflen > sizeof(buf)-2) buflen = sizeof(buf)-2; if (buf[buflen-1]!='/') { buf[buflen++] = '/'; buf[buflen]=0; } } GetDlgItemText(hwnd,IDC_EDIT,msg,sizeof(msg)); preprocess_user_path(msg,sizeof(msg)); BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA); int cnt; if (parms->mode == BrowseFile_State::OPENMULTI && (cnt=ListView_GetSelectedCount(GetDlgItem(hwnd,IDC_LIST)))>1 && (!*msg || !strcmp(msg,multiple_files))) { recent_write(buf); HWND list = GetDlgItem(hwnd,IDC_LIST); WDL_TypedBuf fs; fs.Set(buf,strlen(buf)+1); int a = ListView_GetNextItem(list,-1,LVNI_SELECTED); while (a != -1 && fs.GetSize() < 4096*4096 && cnt--) { if (a < 0 || a >= parms->viewlist.GetSize()) break; const struct BrowseFile_State::rec *rec = parms->viewlist.Get(a); if (!rec) break; fs.Add(rec->name,strlen(rec->name)+1); a = ListView_GetNextItem(list,a,LVNI_SELECTED); } fs.Add("",1); parms->fnout = (char*)malloc(fs.GetSize()); if (parms->fnout) memcpy(parms->fnout,fs.Get(),fs.GetSize()); EndDialog(hwnd,1); return 0; } else { if (msg[0] == '.' && (msg[1] == '.' || msg[1] == 0)) { if (msg[1] == '.') { int a = (int) SendDlgItemMessage(hwnd,IDC_DIR,CB_GETCURSEL,0,0); if (a>=0) SendDlgItemMessage(hwnd,IDC_DIR,CB_SETCURSEL,a+1,0); } SetDlgItemText(hwnd,IDC_EDIT,""); SendMessage(hwnd,WM_UPD,1,0); return 0; } else if (msg[0] == '/') lstrcpyn_safe(buf,msg,sizeof(buf)); else lstrcatn(buf,msg,sizeof(buf)); } switch (parms->mode) { case BrowseFile_State::OPENDIR: if (!buf[0]) return 0; else if (msg[0]) { // navigate to directory if filepart set treatAsDir: DIR *dir = opendir(buf); if (!dir) { snprintf(msg,sizeof(msg),"Error opening directory:\r\n\r\n%.1000s\r\n\r\nCreate?",buf); if (MessageBox(hwnd,msg,"Create directory?",MB_OKCANCEL)==IDCANCEL) return 0; CreateDirectory(buf,NULL); dir=opendir(buf); } if (!dir) { MessageBox(hwnd,"Error creating directory","Error",MB_OK); return 0; } closedir(dir); SendMessage(hwnd, WM_UPD, IDC_DIR, (LPARAM)buf); SetDlgItemText(hwnd,IDC_EDIT,""); SendMessage(hwnd,WM_UPD,1,0); return 0; } else { DIR *dir = opendir(buf); if (!dir) return 0; closedir(dir); } break; case BrowseFile_State::SAVE: if (!buf[0]) return 0; else { struct stat st={0,}; DIR *dir = opendir(buf); if (dir) { closedir(dir); SendMessage(hwnd, WM_UPD, IDC_DIR, (LPARAM)buf); SetDlgItemText(hwnd,IDC_EDIT,""); SendMessage(hwnd,WM_UPD,1,0); return 0; } if (buf[strlen(buf)-1] == '/') goto treatAsDir; const char *extlist = parms->extlist; if (extlist && *extlist && ext_valid_for_extlist(WDL_get_fileext(buf),extlist) < 0) { const char *erd = extlist+strlen(extlist)+1; LRESULT combo_sel; HWND combo = GetDlgItem(hwnd, IDC_EXT); if (combo && (combo_sel=SendMessage(combo,CB_GETCURSEL,0,0)) != CB_ERR) { const char *filt = (const char *)SendMessage(combo,CB_GETITEMDATA,combo_sel,0); if (filt && *filt == '*' && filt[1] == '.' && filt[2] && filt[2] != '*') erd = filt; } if (*erd == '*' && erd[1] == '.' && erd[2] && erd[2] != '*') // add default extension { const char *a = (erd+=1); while (*erd && *erd != ';') erd++; if (erd > a+1) snprintf_append(buf,sizeof(buf),"%.*s",(int)(erd-a),a); } } if (!stat(buf,&st)) { snprintf(msg,sizeof(msg),"File exists:\r\n\r\n%.1000s\r\n\r\nOverwrite?",buf); if (MessageBox(hwnd,msg,"Overwrite file?",MB_OKCANCEL)==IDCANCEL) return 0; } } break; default: if (!buf[0]) return 0; else { struct stat st={0,}; DIR *dir = opendir(buf); if (dir) { closedir(dir); SendMessage(hwnd, WM_UPD, IDC_DIR, (LPARAM)buf); SetDlgItemText(hwnd,IDC_EDIT,""); SendMessage(hwnd,WM_UPD,1,0); return 0; } if (stat(buf,&st)) { //snprintf(msg,sizeof(msg),"File does not exist:\r\n\r\n%s",buf); //MessageBox(hwnd,msg,"File not found",MB_OK); return 0; } } break; } if (parms->fnout) { lstrcpyn_safe(parms->fnout,buf,parms->fnout_sz); } else { size_t l = strlen(buf); parms->fnout = (char*)calloc(l+2,1); memcpy(parms->fnout,buf,l); } if (parms->mode != BrowseFile_State::OPENDIR) WDL_remove_filepart(buf); recent_write(buf); } EndDialog(hwnd,1); return 0; case ID_SHOW_HIDDEN: { BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA); parms->show_hidden = !parms->show_hidden; SendMessage(hwnd,WM_UPD,1,0); } return 0; } break; case WM_NOTIFY: { LPNMHDR l=(LPNMHDR)lParam; if (l->code == LVN_GETDISPINFO) { BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA); NMLVDISPINFO *lpdi = (NMLVDISPINFO*) lParam; const int idx=lpdi->item.iItem; if (l->idFrom == IDC_LIST && parms) { struct BrowseFile_State::rec *rec = parms->viewlist.Get(idx); if (rec && rec->name) { if (lpdi->item.mask&LVIF_TEXT) { switch (lpdi->item.iSubItem) { case 0: lpdi->item.pszText = rec->name; break; case 1: rec->format_size(lpdi->item.pszText,lpdi->item.cchTextMax); break; case 2: rec->format_date(lpdi->item.pszText,lpdi->item.cchTextMax); break; } } } } } else if (l->code == LVN_ODFINDITEM) { } else if (l->code == LVN_ITEMCHANGED) { const int selidx = ListView_GetNextItem(l->hwndFrom, -1, LVNI_SELECTED); BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA); if (selidx>=0 && parms) { if (parms->mode == BrowseFile_State::OPENMULTI && ListView_GetSelectedCount(l->hwndFrom)>1) { SetDlgItemText(hwnd,IDC_EDIT,multiple_files); } else { struct BrowseFile_State::rec *rec = parms->viewlist.Get(selidx); if (rec) { SetDlgItemText(hwnd,IDC_EDIT,rec->name); } } } } else if (l->code == NM_DBLCLK) { SendMessage(hwnd,WM_COMMAND,IDOK,0); } else if (l->code == LVN_COLUMNCLICK) { NMLISTVIEW* lv = (NMLISTVIEW*) l; BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA); parms->sortrev=!parms->sortrev; char lcol = parms->sortcol; if ((int)parms->sortcol != lv->iSubItem) { parms->sortcol = (char)lv->iSubItem; parms->sortrev=0; } HWND hdr = ListView_GetHeader(l->hwndFrom); HDITEM hi; memset(&hi,0,sizeof(hi)); hi.mask = HDI_FORMAT; Header_SetItem(hdr,lcol,&hi); hi.fmt = parms->sortrev ? HDF_SORTDOWN : HDF_SORTUP; Header_SetItem(hdr,parms->sortcol,&hi); parms->viewlist_sort(NULL); ListView_RedrawItems(l->hwndFrom,0, parms->viewlist.GetSize()); } } break; case WM_KEYDOWN: if (lParam == FVIRTKEY && wParam == VK_F5) { SendMessage(hwnd,WM_UPD,1,0); return 1; } else if (lParam == (FVIRTKEY|FCONTROL) && wParam == 'H') { SendMessage(hwnd,WM_COMMAND,ID_SHOW_HIDDEN,0); return 1; } else if (lParam == FVIRTKEY && wParam == VK_BACK && GetFocus() == GetDlgItem(hwnd,IDC_LIST)) { SendMessage(hwnd,WM_COMMAND,IDC_PARENTBUTTON,0); return 1; } else if ((lParam&0xff) == FVIRTKEY && wParam == VK_RETURN && GetFocus() == GetDlgItem(hwnd,IDC_LIST)) { SendMessage(hwnd,WM_COMMAND,IDOK,0); return 1; } return 0; case WM_CONTEXTMENU: { BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA); HMENU menu = CreatePopupMenu(); SWELL_InsertMenu(menu,0,MF_BYPOSITION|(parms->show_hidden ? MF_CHECKED:MF_UNCHECKED), ID_SHOW_HIDDEN, "Show files/directories beginning with ."); POINT p; GetCursorPos(&p); TrackPopupMenu(menu,0,p.x,p.y,0,hwnd,NULL); DestroyMenu(menu); } return 1; } return 0; } // return true bool BrowseForSaveFile(const char *text, const char *initialdir, const char *initialfile, const char *extlist, char *fn, int fnsize) { BrowseFile_State state( text, initialdir, initialfile, extlist, BrowseFile_State::SAVE, fn, fnsize ); if (!DialogBoxParam(NULL,NULL,GetForegroundWindow(),swellFileSelectProc,(LPARAM)&state)) return false; return true; } bool BrowseForDirectory(const char *text, const char *initialdir, char *fn, int fnsize) { BrowseFile_State state( text, initialdir, initialdir, NULL, BrowseFile_State::OPENDIR, fn, fnsize ); return !!DialogBoxParam(NULL,NULL,GetForegroundWindow(),swellFileSelectProc,(LPARAM)&state); } char *BrowseForFiles(const char *text, const char *initialdir, const char *initialfile, bool allowmul, const char *extlist) { BrowseFile_State state( text, initialdir, initialfile, extlist, allowmul ? BrowseFile_State::OPENMULTI : BrowseFile_State::OPEN, NULL, 0 ); return DialogBoxParam(NULL,NULL,GetForegroundWindow(),swellFileSelectProc,(LPARAM)&state) ? state.fnout : NULL; } static const char *mbidtostr(int idx) { switch (idx) { case IDOK: return "OK"; case IDCANCEL: return "Cancel"; case IDYES: return "Yes"; case IDNO: return "No"; case IDRETRY: return "Retry"; case IDABORT: return "Abort"; case IDIGNORE: return "Ignore"; default: WDL_ASSERT(idx == 0); return ""; } } static LRESULT WINAPI swellMessageBoxProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { enum { IDC_LABEL=0x100 }; const int button_spacing = 8; switch (uMsg) { case WM_CREATE: if (lParam) // swell-specific { SetWindowLong(hwnd,GWL_WNDPROC,(LPARAM)SwellDialogDefaultWindowProc); SetWindowLong(hwnd,DWL_DLGPROC,(LPARAM)swellMessageBoxProc); void **parms = (void **)lParam; if (parms[1]) SetWindowText(hwnd,(const char*)parms[1]); int b1=IDOK, b2=0,b3=0; const int fmode = (int)(INT_PTR)parms[2]; switch (fmode & 0xf) { case MB_ABORTRETRYIGNORE: b1 = IDABORT; b2 = IDRETRY; b3 = IDIGNORE; break; case MB_RETRYCANCEL: b1 = IDRETRY; // fallthrough case MB_OKCANCEL: b2 = IDCANCEL; break; case MB_YESNOCANCEL: b3 = IDCANCEL; // fallthrough case MB_YESNO: b1 = IDYES; b2 = IDNO; break; } SWELL_MakeSetCurParms(1,1,0,0,hwnd,false,false); RECT labsize = {0,0,300,20}; HWND lab = SWELL_MakeLabel(-1,parms[0] ? (const char *)parms[0] : "", IDC_LABEL, 0,0,10,10,SS_CENTER|SS_NOPREFIX); //we'll resize this manually HDC dc=GetDC(lab); if (lab && parms[0]) { DrawText(dc,(const char *)parms[0],-1,&labsize,DT_CALCRECT|DT_NOPREFIX);// if dc isnt valid yet, try anyway } const int sc10 = SWELL_UI_SCALE(10); const int sc8 = SWELL_UI_SCALE(8); labsize.top += sc10; labsize.bottom += sc10 + sc8; RECT vp; SWELL_GetViewPort(&vp,NULL,true); vp.bottom -= vp.top; if (labsize.bottom > vp.bottom*7/8) labsize.bottom = vp.bottom*7/8; int x; int button_height=0, button_total_w=0;; const int bspace = SWELL_UI_SCALE(button_spacing); int button_sizes[3]; const int nbuttons = b3 ? 3 : b2 ? 2 : 1; for (x = 0; x < nbuttons; x ++) { const int idx = x==0?b1:x==1?b2:b3; RECT r={0,0,35,12}; DrawText(dc,mbidtostr(idx),-1,&r,DT_CALCRECT|DT_NOPREFIX|DT_SINGLELINE); button_sizes[x] = r.right-r.left + sc10; button_total_w += button_sizes[x] + (x ? bspace : 0); if (r.bottom-r.top+sc10 > button_height) button_height = r.bottom-r.top+sc10; } if (labsize.right < button_total_w+sc8*2) labsize.right = button_total_w+sc8*2; int xpos = labsize.right/2 - button_total_w/2; const int def_id = (fmode & MB_DEFBUTTON3)&&b3 ? b3 : (fmode & MB_DEFBUTTON2)&&b2 ? b2 : b1; for (x = 0; x < nbuttons; x ++) { const int idx = x==0?b1:x==1?b2:b3; SWELL_MakeButton(idx==def_id,mbidtostr(idx),idx,xpos,labsize.bottom,button_sizes[x],button_height,0); xpos += button_sizes[x] + bspace; } if (dc) ReleaseDC(lab,dc); SWELL_MakeSetCurParms(1,1,0,0,NULL,false,false); SetWindowPos(hwnd,NULL,0,0, labsize.right + sc8*2,labsize.bottom + button_height + sc8,SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE); if (lab) SetWindowPos(lab,NULL,sc8,0,labsize.right,labsize.bottom,SWP_NOACTIVATE|SWP_NOZORDER); SetFocus(GetDlgItem(hwnd,def_id)); } break; case WM_SIZE: { RECT r; GetClientRect(hwnd,&r); HWND h = GetWindow(hwnd,GW_CHILD); int n = 10, w[8]; HWND tab[8],lbl=NULL; int tabsz=0, bxwid=0, button_height=0; while (h && n-- && tabsz<8) { int idx = GetWindowLong(h,GWL_ID); if (idx == IDCANCEL || idx == IDOK || idx == IDNO || idx == IDYES || idx == IDRETRY) { RECT tr; GetClientRect(h,&tr); tab[tabsz] = h; w[tabsz++] = tr.right - tr.left; button_height = tr.bottom-tr.top; bxwid += tr.right-tr.left; } else if (idx==IDC_LABEL) lbl=h; h = GetWindow(h,GW_HWNDNEXT); } const int bspace = SWELL_UI_SCALE(button_spacing), sc8 = SWELL_UI_SCALE(8); if (lbl) SetWindowPos(lbl,NULL,sc8,0,r.right,r.bottom - sc8 - button_height, SWP_NOZORDER|SWP_NOACTIVATE); int xo = r.right/2 - (bxwid + (tabsz-1)*bspace)/2; for (int x=0; x255) ir=255; if (ig<0) ig=0; else if (ig>255) ig=255; if (ib<0) ib=0; else if (ib>255) ib=255; return RGB(ir,ig,ib); } static void _RGB2HSV(double r, double g, double b, double *h, double *s, double *v) { const double maxrgb=wdl_max(wdl_max(r,g),b); const double df = maxrgb - wdl_min(wdl_min(r,g),b); double d=r-g, degoffs = 240.0; if (g > r) { if (g > b) { degoffs=120; d=b-r; } } else if (r > b) { degoffs=0.0; d=g-b; } *v = maxrgb; if (df) { degoffs += (d*60)/df; if (degoffs<0.0) degoffs+=360.0; else if (degoffs >= 360.0) degoffs-=360.0; *h = degoffs; *s = (df*256)/(maxrgb+1); } else *h = *s = 0; } static LRESULT WINAPI swellColorSelectProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { static int s_reent,s_vmode; static int wndw, custsz, buth, border, butw, edh, edlw, edew, vsize, psize, yt; if (!wndw) { wndw = SWELL_UI_SCALE(400); custsz = SWELL_UI_SCALE(20); butw = SWELL_UI_SCALE(50); buth = SWELL_UI_SCALE(24); border = SWELL_UI_SCALE(4); edh = SWELL_UI_SCALE(20); edlw = SWELL_UI_SCALE(16); edew = SWELL_UI_SCALE(40); vsize = SWELL_UI_SCALE(40); psize = border+edlw + edew; yt = border + psize + border + (edh + border)*6; } const int customperrow = (wndw-border)/(custsz+border); switch (uMsg) { case WM_CREATE: if (lParam) // swell-specific { SetWindowLong(hwnd,GWL_WNDPROC,(LPARAM)SwellDialogDefaultWindowProc); SetWindowLong(hwnd,DWL_DLGPROC,(LPARAM)swellColorSelectProc); SetWindowLongPtr(hwnd,GWLP_USERDATA,lParam); SetWindowText(hwnd,"Choose Color"); SWELL_MakeSetCurParms(1,1,0,0,hwnd,false,false); SWELL_MakeButton(0, "OK", IDOK,0,0,0,0, 0); SWELL_MakeButton(0, "Cancel", IDCANCEL,0,0,0,0, 0); SWELL_MakeLabel(0, "(right click a custom color to save)", 0x500, 0,0,0,0, 0); static const char * const lbl[] = { "R","G","B","H","S","V"}; for (int x=0;x<6;x++) { SWELL_MakeLabel(0,lbl[x], 0x100+x, 0,0,0,0, 0); SWELL_MakeEditField(0x200+x, 0,0,0,0, 0); } ChooseColor_State *cs = (ChooseColor_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA); SWELL_MakeSetCurParms(1,1,0,0,NULL,false,false); int nrows = ((cs?cs->ncustom : 0 ) + customperrow-1)/wdl_max(customperrow,1); SetWindowPos(hwnd,NULL,0,0, wndw, yt + buth + border + nrows * (custsz+border), SWP_NOZORDER|SWP_NOMOVE); SendMessage(hwnd,WM_USER+100,0,3); } break; case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: { ChooseColor_State *cs = (ChooseColor_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA); if (!cs) break; RECT r; GetClientRect(hwnd,&r); const int xt = r.right - edew - edlw - border*3; const int y = GET_Y_LPARAM(lParam); const int x = GET_X_LPARAM(lParam); if (x < xt && y < yt) { s_vmode = x >= xt-vsize; SetCapture(hwnd); // fall through } else { if (cs->custom && cs->ncustom && y >= yt && y < r.bottom - buth - border) { int row = (y-yt) / (custsz+border), rowoffs = (y-yt) % (custsz+border); if (rowoffs < custsz) { int col = (x-border) / (custsz+border), coloffs = (x-border) % (custsz+border); if (coloffs < custsz) { col += customperrow*row; if (col >= 0 && col < cs->ncustom) { if (uMsg == WM_LBUTTONDOWN) { _RGB2HSV(GetRValue(cs->custom[col]),GetGValue(cs->custom[col]),GetBValue(cs->custom[col]),&cs->h,&cs->s,&cs->v); SendMessage(hwnd,WM_USER+100,0,3); } else { cs->custom[col] = _HSV2RGBV(cs->h,cs->s,cs->v); InvalidateRect(hwnd,NULL,FALSE); } } } } } break; } // fall through } case WM_MOUSEMOVE: if (GetCapture()==hwnd) { RECT r; GetClientRect(hwnd,&r); const int xt = r.right - edew - edlw - border*3; ChooseColor_State *cs = (ChooseColor_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA); if (!cs) break; int var = 255 - (GET_Y_LPARAM(lParam) - border)*256 / (yt-border*2); if (var<0)var=0; else if (var>255)var=255; if (s_vmode) { if (var != cs->v) { cs->v=var; SendMessage(hwnd,WM_USER+100,0,3); } } else { int hue = (GET_X_LPARAM(lParam) - border)*360 / (xt-border - vsize); if (hue<0) hue=0; else if (hue>359) hue=359; if (cs->h != hue || cs->s != var) { cs->h=hue; cs->s=var; SendMessage(hwnd,WM_USER+100,0,3); } } } break; case WM_LBUTTONUP: case WM_RBUTTONUP: ReleaseCapture(); break; case WM_PAINT: { PAINTSTRUCT ps; ChooseColor_State *cs = (ChooseColor_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA); if (cs && BeginPaint(hwnd,&ps)) { RECT r; GetClientRect(hwnd,&r); const int xt = r.right - edew - edlw - border*3; if (cs->custom && cs->ncustom) { int ypos = yt; int xpos = border; for (int x = 0; x < cs->ncustom; x ++) { HBRUSH br = CreateSolidBrush(cs->custom[x]); RECT tr={xpos,ypos,xpos+custsz, ypos+custsz }; FillRect(ps.hdc,&tr,br); DeleteObject(br); xpos += border+custsz; if (xpos+custsz >= r.right) { xpos=border; ypos += border + custsz; } } } { HBRUSH br = CreateSolidBrush(_HSV2RGBV(cs->h,cs->s,cs->v)); RECT tr={r.right - border - psize, border, r.right-border, border+psize}; FillRect(ps.hdc,&tr,br); DeleteObject(br); } if (!cs->bm) cs->bm = new LICE_SysBitmap(xt-border,yt-border); else cs->bm->resize(xt-border,yt-border); int x1 = xt - border - vsize; int var = cs->v; const int ysz = yt-border*2; const int vary = ysz - 1 - (ysz * cs->v)/256; for (int y = 0; y < ysz; y ++) { LICE_pixel *wr = cs->bm->getBits() + cs->bm->getRowSpan() * y; const int sat = 255 - y*256/ysz; double xx=0.0, dx=384.0/x1; int x; for (x = 0; x < x1; x++) { *wr++ = LICE_HSV2Pix((int)(xx+0.5),sat,var,255); xx+=dx; } LICE_pixel p = LICE_HSV2Pix(cs->h * 384.0/360.0,cs->s,sat ^ (y==vary ? 128 : 0),255); for (;x < xt-border;x++) *wr++ = p; } LICE_pixel p = LICE_HSV2Pix((int)(cs->h+0.5),(int)(cs->s+0.5),((int)(128.5+cs->v))&255,255); const int saty = ysz - 1 - (int) (ysz * cs->s + 0.5)/256; const int huex = (x1*cs->h)/360; LICE_Line(cs->bm,huex,saty-4,huex,saty+4,p,.75f,LICE_BLIT_MODE_COPY,false); LICE_Line(cs->bm,huex-4,saty,huex+4,saty,p,.75f,LICE_BLIT_MODE_COPY,false); BitBlt(ps.hdc,border,border,xt-border,ysz,cs->bm->getDC(),0,0,SRCCOPY); EndPaint(hwnd,&ps); } } break; case WM_GETMINMAXINFO: { LPMINMAXINFO p=(LPMINMAXINFO)lParam; p->ptMinTrackSize.x = 300; p->ptMinTrackSize.y = 300; } break; case WM_USER+100: { ChooseColor_State *cs = (ChooseColor_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA); if (cs) { double t[6]; t[3] = cs->h; t[4] = cs->s; t[5] = cs->v; _HSV2RGB(t[3],t[4],t[5],t,t+1,t+2); s_reent++; for (int x=0;x<6;x++) if (lParam & ((x<3)?1:2)) SetDlgItemInt(hwnd,0x200+x,(int) (t[x]+0.5),FALSE); s_reent--; InvalidateRect(hwnd,NULL,FALSE); } } break; case WM_SIZE: { RECT r; GetClientRect(hwnd,&r); int tx = r.right - edew-edlw-border*2, ty = border*2 + psize; for (int x=0;x<6;x++) { SetWindowPos(GetDlgItem(hwnd,0x100+x),NULL,tx, ty, edlw, edh, SWP_NOZORDER|SWP_NOACTIVATE); SetWindowPos(GetDlgItem(hwnd,0x200+x),NULL,tx+edlw+border, ty, edew, edh, SWP_NOZORDER|SWP_NOACTIVATE); ty += border+edh; } r.right -= border + butw; r.bottom -= border + buth; SetWindowPos(GetDlgItem(hwnd,IDCANCEL), NULL, r.right, r.bottom, butw, buth, SWP_NOZORDER|SWP_NOACTIVATE); r.right -= border*2 + butw; SetWindowPos(GetDlgItem(hwnd,IDOK), NULL, r.right, r.bottom, butw, buth, SWP_NOZORDER|SWP_NOACTIVATE); SetWindowPos(GetDlgItem(hwnd,0x500), NULL, border, r.bottom, r.right-border*2, buth, SWP_NOZORDER|SWP_NOACTIVATE); } break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDCANCEL: EndDialog(hwnd,0); break; case IDOK: EndDialog(hwnd,1); break; case 0x200: case 0x201: case 0x202: case 0x203: case 0x204: case 0x205: if (!s_reent) { const bool ishsv = LOWORD(wParam) >= 0x203; int offs = ishsv ? 0x203 : 0x200; BOOL t = FALSE; double h = GetDlgItemInt(hwnd,offs++,&t,FALSE); if (!t) break; double s = GetDlgItemInt(hwnd,offs++,&t,FALSE); if (!t) break; double v = GetDlgItemInt(hwnd,offs++,&t,FALSE); if (!t) break; if (s<0) s=0; else if (s>255) s=255; if (v<0) v=0; else if (v>255) v=255; if (h<0) h=0; if (!ishsv) { if (h>255) h=255; _RGB2HSV(h,s,v,&h,&s,&v); } else { if (h>360) h=360; } ChooseColor_State *cs = (ChooseColor_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA); if (cs) { cs->h = h; cs->s = s; cs->v = v; } SendMessage(hwnd,WM_USER+100,0,ishsv?1:2); } break; } break; } return 0; } #endif //SWELL_LICE_GDI bool SWELL_ChooseColor(HWND h, COLORREF *val, int ncustom, COLORREF *custom) { #ifdef SWELL_LICE_GDI ChooseColor_State state = { ncustom, custom }; COLORREF c = val ? *val : 0; _RGB2HSV(GetRValue(c),GetGValue(c),GetBValue(c),&state.h,&state.s,&state.v); bool rv = DialogBoxParam(NULL,NULL,h,swellColorSelectProc,(LPARAM)&state)!=0; delete state.bm; if (rv && val) { *val = _HSV2RGBV(state.h,state.s,state.v); } return rv; #else return false; #endif } #if defined(SWELL_FREETYPE) && defined(SWELL_LICE_GDI) struct FontChooser_State { FontChooser_State() { hFont = 0; } ~FontChooser_State() { DeleteObject(hFont); } LOGFONT font; HFONT hFont; WDL_FastString lastfn; }; extern const char *swell_last_font_filename; const char *swell_enumFontFiles(int x); int swell_getLineLength(const char *buf, int *post_skip, int wrap_maxwid, HDC hdc); static LRESULT WINAPI swellFontChooserProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { enum { IDC_LIST=0x100, IDC_FACE, IDC_SIZE, IDC_WEIGHT, IDC_ITALIC }; enum { preview_h = 90, _border = 4, _buth = 24 }; switch (uMsg) { case WM_CREATE: if (lParam) // swell-specific { SetWindowLong(hwnd,GWL_WNDPROC,(LPARAM)SwellDialogDefaultWindowProc); SetWindowLong(hwnd,DWL_DLGPROC,(LPARAM)swellFontChooserProc); SetWindowLongPtr(hwnd,GWLP_USERDATA,lParam); SetWindowText(hwnd,"Choose Font"); SWELL_MakeSetCurParms(1,1,0,0,hwnd,false,false); SWELL_MakeButton(0, "OK", IDOK,0,0,0,0, 0); SWELL_MakeButton(0, "Cancel", IDCANCEL,0,0,0,0, 0); SWELL_MakeListBox(IDC_LIST,0,0,0,0, LBS_OWNERDRAWFIXED); SWELL_MakeEditField(IDC_FACE, 0,0,0,0, 0); SWELL_MakeEditField(IDC_SIZE, 0,0,0,0, 0); SWELL_MakeCombo(IDC_WEIGHT, 0,0,0,0, CBS_DROPDOWNLIST); SWELL_MakeCheckBox("Italic",IDC_ITALIC,0,0,0,0, 0); SendDlgItemMessage(hwnd,IDC_WEIGHT,CB_ADDSTRING,0,(LPARAM)"Normal"); SendDlgItemMessage(hwnd,IDC_WEIGHT,CB_ADDSTRING,0,(LPARAM)"Bold"); SendDlgItemMessage(hwnd,IDC_WEIGHT,CB_ADDSTRING,0,(LPARAM)"Light"); SWELL_MakeSetCurParms(1,1,0,0,NULL,false,false); SetWindowPos(hwnd,NULL,0,0, 550,380, SWP_NOZORDER|SWP_NOMOVE); WDL_StringKeyedArray list; const char *fontfile; int x; for (x=0; (fontfile=swell_enumFontFiles(x)); x ++) { char buf[512]; lstrcpyn_safe(buf,WDL_get_filepart(fontfile),sizeof(buf)); char *tmp = buf; while (*tmp && *tmp != '-' && *tmp != '.') tmp++; *tmp=0; if (*buf) list.AddUnsorted(buf,true); } swell_enumFontFiles(-1); // clear cache list.Resort(); FontChooser_State *cs = (FontChooser_State*)lParam; bool italics = cs->font.lfItalic!=0; int wt = cs->font.lfWeight; const char *lp=NULL; int cnt=0; for (x=0;xfont.lfFaceName)) SendDlgItemMessage(hwnd,IDC_LIST,LB_SETCURSEL,cnt,0); size_t ll; if (lp && !strncmp(p,lp,ll=strlen(lp))) { // if this is an extension of the last one, skip const char *trail = p+ll; if (strlen(trail)<=2) { for (int y=0;y<2;y++) { char c = *trail; if (c>0) c=toupper(c); if (c == 'B' || c == 'I' || c == 'L') trail++; } } else while (*trail) { if (!strnicmp(trail,"Bold",4)) trail+=4; else if (!strnicmp(trail,"Light",5)) trail+=5; else if (!strnicmp(trail,"Italic",6)) trail+=6; else if (!strnicmp(trail,"Oblique",7)) trail+=7; else break; } if (!*trail) continue; } cnt++; SendDlgItemMessage(hwnd,IDC_LIST,LB_ADDSTRING,0,(LPARAM)p); lp=p; } } SetDlgItemText(hwnd,IDC_FACE,cs->font.lfFaceName); SetDlgItemInt(hwnd,IDC_SIZE,cs->font.lfHeight < 0 ? -cs->font.lfHeight : cs->font.lfHeight,TRUE); SendDlgItemMessage(hwnd,IDC_WEIGHT,CB_SETCURSEL,wt<=FW_LIGHT ? 2 : wt < FW_BOLD ? 0 : 1,0); if (italics) CheckDlgButton(hwnd,IDC_ITALIC,BST_CHECKED); } break; case WM_DRAWITEM: { DRAWITEMSTRUCT *di=(DRAWITEMSTRUCT *)lParam; FontChooser_State *cs = (FontChooser_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA); if (cs && di->CtlID == IDC_LIST) { char buf[512]; buf[0]=0; SendDlgItemMessage(hwnd,IDC_LIST,LB_GETTEXT,di->itemID,(WPARAM)buf); if (buf[0]) { HFONT font = CreateFont(g_swell_ctheme.default_font_size, 0, 0, 0, cs->font.lfWeight, cs->font.lfItalic, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, buf); HGDIOBJ oldFont = SelectObject(di->hDC,font); DrawText(di->hDC,buf,-1,&di->rcItem,DT_VCENTER|DT_LEFT|DT_NOPREFIX); wchar_t tmp[] = {'a','A','z','Z'}; unsigned short ind[4]; GetGlyphIndicesW(di->hDC,tmp,4,ind,0); SelectObject(di->hDC,oldFont); int x; for (x=0;x<4 && ind[x]==0xffff;x++); if (x==4) { RECT r = di->rcItem; r.right-=4; DrawText(di->hDC,buf,-1,&r,DT_VCENTER|DT_RIGHT|DT_NOPREFIX); } DeleteObject(font); } } } return 0; case WM_PAINT: { PAINTSTRUCT ps; FontChooser_State *cs = (FontChooser_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA); if (cs && BeginPaint(hwnd,&ps)) { RECT r; GetClientRect(hwnd,&r); const int border = SWELL_UI_SCALE(_border); const int buth = SWELL_UI_SCALE(_buth); const int ph = SWELL_UI_SCALE(preview_h); r.left += border; r.right -= border; r.bottom -= border*2 + buth; r.top = r.bottom - ph; HFONT f = CreateFontIndirect(&cs->font); HBRUSH br = CreateSolidBrush(RGB(255,255,255)); FillRect(ps.hdc,&r,br); DeleteObject(br); SetTextColor(ps.hdc,RGB(0,0,0)); SetBkMode(ps.hdc,TRANSPARENT); r.right-=4; r.left+=4; if (swell_last_font_filename) { r.bottom -= DrawText(ps.hdc,swell_last_font_filename,-1,&r,DT_BOTTOM|DT_NOPREFIX|DT_SINGLELINE|DT_RIGHT); } HGDIOBJ oldFont = SelectObject(ps.hdc,f); extern const char *g_swell_fontpangram; const char *str = g_swell_fontpangram; // // thanks, http://dailypangram.tumblr.com/ :) if (!str) str = "Strangely, aerobic exercise doesn’t quite work with improvised free jazz."; while (*str) { int sk=0, lb=swell_getLineLength(str, &sk, r.right-r.left, ps.hdc); if (!lb&&!sk) break; if (lb>0) r.top += DrawText(ps.hdc,str,lb,&r,DT_TOP|DT_LEFT|DT_NOPREFIX|DT_SINGLELINE); str+=lb+sk; } SelectObject(ps.hdc,oldFont); DeleteObject(f); EndPaint(hwnd,&ps); } } break; case WM_GETMINMAXINFO: { LPMINMAXINFO p=(LPMINMAXINFO)lParam; p->ptMinTrackSize.x = 400; p->ptMinTrackSize.y = 300; } break; case WM_SIZE: { RECT r; GetClientRect(hwnd,&r); const int border = SWELL_UI_SCALE(_border); const int buth = SWELL_UI_SCALE(_buth); const int butw = SWELL_UI_SCALE(50); const int edh = SWELL_UI_SCALE(20); const int size_w = SWELL_UI_SCALE(50); const int wt_w = SWELL_UI_SCALE(80); const int italic_w = SWELL_UI_SCALE(60); r.left += border; r.right -= border; r.bottom -= border + buth; SetWindowPos(GetDlgItem(hwnd,IDCANCEL), NULL, r.right - butw, r.bottom, butw, buth, SWP_NOZORDER|SWP_NOACTIVATE); SetWindowPos(GetDlgItem(hwnd,IDOK), NULL, r.right - border - butw*2, r.bottom, butw, buth, SWP_NOZORDER|SWP_NOACTIVATE); r.bottom -= SWELL_UI_SCALE(preview_h) + border; int psz=wdl_max(g_swell_ctheme.combo_height,edh); r.bottom -= psz + border; SetWindowPos(GetDlgItem(hwnd,IDC_FACE),NULL,r.left,r.bottom + (psz-edh)/2, r.right-r.left - size_w-wt_w-italic_w - border*3, edh, SWP_NOZORDER|SWP_NOACTIVATE); SetWindowPos(GetDlgItem(hwnd,IDC_SIZE),NULL,r.right-size_w-wt_w-italic_w-border*2,r.bottom + (psz-edh)/2, size_w, edh, SWP_NOZORDER|SWP_NOACTIVATE); SetWindowPos(GetDlgItem(hwnd,IDC_WEIGHT),NULL,r.right-wt_w-italic_w-border,r.bottom + (psz-g_swell_ctheme.combo_height)/2, wt_w, g_swell_ctheme.combo_height, SWP_NOZORDER|SWP_NOACTIVATE); SetWindowPos(GetDlgItem(hwnd,IDC_ITALIC),NULL,r.right-italic_w,r.bottom + (psz-edh)/2, italic_w, edh, SWP_NOZORDER|SWP_NOACTIVATE); SetWindowPos(GetDlgItem(hwnd,IDC_LIST), NULL, border, border, r.right, r.bottom - border*2, SWP_NOZORDER|SWP_NOACTIVATE); } break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_LIST: if (HIWORD(wParam) == LBN_SELCHANGE) { int idx = (int) SendDlgItemMessage(hwnd,IDC_LIST,LB_GETCURSEL,0,0); if (idx>=0) { char buf[512]; buf[0]=0; SendDlgItemMessage(hwnd,IDC_LIST,LB_GETTEXT,idx,(WPARAM)buf); if (buf[0]) SetDlgItemText(hwnd,IDC_FACE,buf); } } break; case IDC_SIZE: case IDC_FACE: case IDC_ITALIC: case IDC_WEIGHT: { FontChooser_State *cs = (FontChooser_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA); if (cs) { if (LOWORD(wParam) == IDC_FACE) GetDlgItemText(hwnd,IDC_FACE,cs->font.lfFaceName,sizeof(cs->font.lfFaceName)); else if (LOWORD(wParam) == IDC_SIZE) { BOOL t; int a = GetDlgItemInt(hwnd,IDC_SIZE,&t,FALSE); if (t) { if (cs->font.lfHeight < 0) cs->font.lfHeight = -a; else cs->font.lfHeight = a; } } else if (LOWORD(wParam) == IDC_ITALIC) cs->font.lfItalic = IsDlgButtonChecked(hwnd,IDC_ITALIC) ? 1:0; else if (LOWORD(wParam) == IDC_WEIGHT && HIWORD(wParam) == CBN_SELCHANGE) { int idx = (int) SendDlgItemMessage(hwnd,IDC_WEIGHT,CB_GETCURSEL,0,0); if (idx==0) cs->font.lfWeight = FW_NORMAL; else if (idx==1) cs->font.lfWeight = FW_BOLD; else if (idx==2) cs->font.lfWeight = FW_LIGHT; } InvalidateRect(hwnd,NULL,FALSE); } } break; case IDCANCEL: EndDialog(hwnd,0); break; case IDOK: EndDialog(hwnd,1); break; } break; } return 0; } void *swell_MatchFont(const char *lfFaceName, int weight, int italic, const char **fnOut); #endif bool SWELL_ChooseFont(HWND h, LOGFONT *lf) { #if defined(SWELL_FREETYPE) && defined(SWELL_LICE_GDI) FontChooser_State state; state.font = *lf; bool rv = DialogBoxParam(NULL,NULL,h,swellFontChooserProc,(LPARAM)&state)!=0; if (rv) { *lf = state.font; } return rv; #else return false; #endif } #endif