123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848 |
- /*
- * ui_file.h
- * https://gitlab.com/bztsrc/smgui
- *
- * Copyright (C) 2024 bzt (bztsrc@gitlab), MIT license
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY
- * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
- * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- * @brief Text input with file picker for SMGUI
- */
- #ifndef UI_FILE_H
- #define UI_FILE_H 1
- #ifndef UI_H
- #include <ui.h>
- #undef UI_H
- #endif
- #ifdef __cplusplus
- extern "C" {
- #endif
- #ifdef __WIN32__
- #define DIRSEP "\\"
- #include <windows.h>
- #include <wchar.h>
- #else
- #define DIRSEP "/"
- #endif
- #define __USE_MISC
- #include <limits.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <dirent.h>
- #include <time.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #ifndef FILENAME_MAX
- #define FILENAME_MAX 255
- #endif
- #ifndef PATH_MAX
- #ifdef MAX_PATH
- #define PATH_MAX MAX_PATH
- #else
- #define PATH_MAX 4096
- #endif
- #endif
- extern char *realpath (const char *__name, char *__resolved);
- #define UI_FILE UI_CUSTOM,.bbox=ui_file_bbox,.view=ui_file_view,.ctrl=ui_file_ctrl,.fini=ui_path_stop
- #define UI_PATH UI_CUSTOM,.bbox=ui_path_bbox,.view=ui_path_view,.ctrl=ui_path_ctrl,.fini=ui_path_stop
- static ui_form_t _ui_file_popup[] = { { .type = UI_END } };
- typedef struct {
- uint64_t size;
- uint64_t date;
- int idx;
- char type;
- char name[FILENAME_MAX + 1];
- } ui_files_t;
- #define UI_PATH_SEARCH 1
- #define UI_PATH_NEWDIR 2
- #define UI_PATH_ONLYDIR 4
- #define UI_PATH_HIDDEN 8
- typedef int (*ui_form_select)(char *path, int isdir);
- typedef struct {
- char exts[256], filter[FILENAME_MAX + 1], fn[FILENAME_MAX + 1];
- int flags, numfiles, shfiles, numpath, pathscr, pathsel, pathh, sw, tw, srt, scr, sel;
- uint16_t pathw[256];
- ui_files_t *files;
- ui_form_select select;
- } ui_path_t;
- static ui_path_t _ui_path_ctx;
- static ui_form_t _ui_path_vscr;
- /* we must not rely on all fonts always have these glyphs */
- uint16_t _ui_path_glyphs[6*16] = {
- 0,0,0,480,528,1032,1288,1288,1160,528,3552,6144,12288,0,0,0,0,2048,18808,8824,40958,4098,24058,14074,30714,32762,32762,32762,
- 32762,16380,0,0,0,0,120,120,8190,4098,32762,32762,32762,32762,32762,32762,32762,16380,0,0,0,0,248,392,648,1160,2184,8072,4104,
- 4104,4104,4104,4104,8184,0,0
- };
- /**
- * Recalculate form element bounding box
- */
- int ui_path_bbox(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form, int *dw, int *dh)
- {
- if(!ctx || !form || !dw || !dh) return UI_ERR_BADINP;
- (void)x; (void)y; (void)w; (void)h;
- if(ctx->fnt && ctx->bbox && form->ptr) {
- /* if string is empty, use a default one to get height and baseline correctly */
- (*ctx->bbox)(ctx->fnt, form->ptr && *((char*)form->ptr) ? (char*)form->ptr : "Ag", NULL, dw, dh, &form->l, &form->t);
- if(!form->ptr || !*((char*)form->ptr)) *dw = 0;
- (*dh) += 4;
- }
- if(form->ew > 0) *dw = form->ew;
- return UI_OK;
- }
- /**
- * Display a file browser
- */
- int ui_path_view(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form)
- {
- ui_files_t *file;
- ui_path_t *path;
- uint64_t size, now;
- int i, j, n, X, Y, ph, ps, pt, c, dw, dh, t;
- char *s, *e, u, tmp[32];
- struct tm *lt;
- if(!ctx || !form || !form->data || !ctx->fnt || !ctx->bbox || !ctx->draw) return UI_ERR_BADINP;
- if(form->flags & (UI_HIDDEN || UI_DISABLED)) return UI_OK;
- path = (ui_path_t*)form->data;
- /* path buttons */
- ph = path->pathh;
- if((path->flags & UI_PATH_NEWDIR) && path->pathh < 16) path->pathh = 16;
- ph += 6;
- for(X = x, s = form->ptr, i = 0; *s && i < path->numpath; s = e, i++) {
- #ifdef __WIN32__
- if(!i) { e = s + 2; } else
- #endif
- { for(e = s + 1; *e && e[-1] != DIRSEP[0]; e++){} if(e[-1] != DIRSEP[0]) break; }
- if(i >= path->pathscr) {
- _ui_text(ctx, X, y, path->pathw[i] - 2, ph, 0, form->t, 0, 64 | (i == path->pathsel ? 32 : 16), s, e);
- X += path->pathw[i];
- }
- }
- X = x + w;
- if(path->flags & UI_PATH_NEWDIR) {
- _ui_text(ctx, X - 20, y, 20, ph, 0, 0, 0, path->pathsel == -2 ? 0 : 16, "", NULL);
- _ui_bmp16(ctx, X - 18, y + (ph - 16) / 2 + !!(path->pathsel == -2), &_ui_path_glyphs[16], ctx->theme[UI_IFG]);
- X -= 24;
- }
- if(path->flags & UI_PATH_SEARCH) {
- j = w / 3;
- if(j > 128) j = 128;
- X -= j;
- _ui_text(ctx, X, y, j, ph, 0, form->t, 0, ctx->str == path->filter, path->filter, NULL);
- if(!(path->flags & UI_PATH_NEWDIR) && !path->filter[0] && ctx->str != path->filter)
- _ui_bmp16(ctx, X + 2, y + (ph - 16) / 2, &_ui_path_glyphs[0], ctx->theme[UI_IL]);
- }
- y += ph + 2; h -= ph + 2;
- /* file list border */
- _ui_rect(ctx, x, y, w, h, ctx->theme[UI_ID], ctx->theme[UI_IBG], ctx->theme[UI_IL]);
- x++; y++; w -= 2; h -= 2;
- /* file list header */
- pt = w - path->tw; ps = pt - path->sw;
- if(form->str > 0) {
- _ui_header(ctx, x, y, ps, path->pathh + 4, path->pathsel == -4, path->srt, ctx->txtv[form->str]);
- _ui_header(ctx, x + ps, y, path->sw, path->pathh + 4, path->pathsel == -5, path->srt - 2, ctx->txtv[form->str + 1]);
- _ui_header(ctx, x + pt, y, path->tw, path->pathh + 4, path->pathsel == -6, path->srt - 4, ctx->txtv[form->str + 2]);
- y += path->pathh + 4; h -= path->pathh + 4;
- }
- /* file list */
- w -= ctx->sw;
- _ui_vscrbar(ctx, x + w, y, h, path->scr, path->shfiles * path->pathh, path->pathsel == -7 && ctx->vscr && ctx->vscr->ptr == &path->scr);
- ctx->cx0 = x; ctx->cx1 = x + w; ctx->cy0 = y; ctx->cy1 = y + h;
- _ui_boxbg(ctx, x, y, w, h, path->pathh, 0, path->scr);
- if(path->files) {
- now = time(NULL);
- n = x + ps - 2 < ctx->cx1 ? x + ps - 2 : ctx->cx1;
- if(path->scr + h > path->shfiles * path->pathh) path->scr = path->shfiles * path->pathh - h;
- if(path->scr < 0) path->scr = 0;
- for(i = 0, Y = y - path->scr; i < path->shfiles && Y < ctx->cy1; i++, Y += path->pathh) {
- if(Y + path->pathh < y) continue;
- /* background */
- file = (ui_files_t*)&path->files[path->files[i].idx];
- j = (file->type + 2) * 16;
- if(i == path->sel) {
- if(ctx->skin[UI_INP].buf)
- _ui_blit(ctx, x, Y, w, path->pathh, &ctx->skin[UI_HL], 0, 0, 0);
- else
- _ui_frect(ctx, x, Y, w, path->pathh, ctx->theme[UI_HLBG]);
- c = UI_HLFG;
- } else
- c = UI_FG;
- /* file name */
- _ui_bmp16(ctx, x + 2, Y + (path->pathh - 16) / 2, &_ui_path_glyphs[j], ctx->theme[c]);
- (*ctx->draw)(ctx->fnt, file->name, NULL, ctx->screen.buf, ctx->theme[c], x + 20, Y, 0, form->t, ctx->screen.p,
- x + 20, ctx->cy0, n, ctx->cy1);
- if(file->size != -1U) {
- /* file size */
- u = 0; size = file->size;
- if(size > 1023) {
- size = (size + 1023) >> 10; u = 'k';
- if(size > 1023) {
- size = (size + 1023) >> 10; u = 'M';
- if(size > 1023) {
- size = (size + 1023) >> 10; u = 'G';
- if(size > 1023) { size = (size + 1023) >> 10; u = 'T'; }
- }
- }
- }
- sprintf(tmp, "%u%c", (uint32_t)size, u);
- (*ctx->bbox)(ctx->fnt, tmp, NULL, &dw, &dh, NULL, &t);
- (*ctx->draw)(ctx->fnt, tmp, NULL, ctx->screen.buf, ctx->theme[c], x + pt - 2 - dw, Y, 0, t, ctx->screen.p,
- x + pt - 2 - dw, ctx->cy0, x + pt, ctx->cy1);
- /* file date */
- tmp[0] = 0;
- if(form->str) {
- /* localized human friendly */
- size = now - file->date;
- if(size < 120) strcpy(tmp, ctx->txtv[form->str + 3]); else
- if(size < 3600) sprintf(tmp, "%u %s", (uint32_t)(size/60), ctx->txtv[form->str + 4]); else
- if(size < 7200) strcpy(tmp, ctx->txtv[form->str + 5]); else
- if(size < 24*3600) sprintf(tmp, "%u %s", (uint32_t)(size/3600), ctx->txtv[form->str + 6]); else
- if(size < 48*3600) strcpy(tmp, ctx->txtv[form->str + 7]);
- }
- if(!tmp[0]) {
- /* non-localized, standard ISO date otherwise */
- lt = localtime((const time_t*)&file->date);
- sprintf(tmp, "%04d-%02d-%02d", lt->tm_year+1900, lt->tm_mon+1, lt->tm_mday);
- }
- (*ctx->draw)(ctx->fnt, tmp, NULL, ctx->screen.buf, ctx->theme[c], x + pt + 2, Y, 0, t, ctx->screen.p,
- x + pt + 2, ctx->cy0, x + w - 2, ctx->cy1);
- }
- y += path->pathh; h -= path->pathh;
- }
- }
- return UI_OK;
- }
- /**
- * Sorting helpers
- */
- int _ui_strnatcmp(const char *a, const char *b, int n)
- {
- #define UI_ISDIGIT(x) (((unsigned int)(x) - '0' ) < 10U)
- int result = 0, i;
- for(i = 1; *a && *b; a++, b++, i++) {
- if(UI_ISDIGIT(*a) && UI_ISDIGIT(*b)) {
- for(result = 0; UI_ISDIGIT(*a) && UI_ISDIGIT(*b); a++, b++, i++)
- if(!result) result = *a - *b;
- if(!UI_ISDIGIT(*a) && UI_ISDIGIT(*b)) return -1;
- if(!UI_ISDIGIT(*b) && UI_ISDIGIT(*a)) return +1;
- if(result || (!*a && !*b)) return result;
- }
- result = (*a >= 'a' && *a <= 'z' ? *a + 'A' - 'a' : *a) - (*b >= 'a' && *b <= 'z' ? *b + 'A' - 'a' : *b);
- if(result || (n > 0 && i >= n)) return result;
- }
- return *a - *b;
- #undef UI_ISDIGIT
- }
- static int _ui_path_nameasc(const void *a, const void *b)
- {
- ui_files_t *A = (ui_files_t*)a, *B = (ui_files_t*)b;
- return A->type != B->type ? A->type - B->type : _ui_strnatcmp(A->name, B->name, 0);
- }
- static int _ui_path_namedec(const void *a, const void *b)
- {
- ui_files_t *A = (ui_files_t*)a, *B = (ui_files_t*)b;
- return A->type != B->type ? A->type - B->type : _ui_strnatcmp(B->name, A->name, 0);
- }
- static int _ui_path_sizeasc(const void *a, const void *b)
- {
- ui_files_t *A = (ui_files_t*)a, *B = (ui_files_t*)b;
- return A->type != B->type ? A->type - B->type : A->size > B->size;
- }
- static int _ui_path_sizedec(const void *a, const void *b)
- {
- ui_files_t *A = (ui_files_t*)a, *B = (ui_files_t*)b;
- return A->type != B->type ? A->type - B->type : B->size > A->size;
- }
- static int _ui_path_dateasc(const void *a, const void *b)
- {
- ui_files_t *A = (ui_files_t*)a, *B = (ui_files_t*)b;
- return A->type != B->type ? A->type - B->type : A->date > B->date;
- }
- static int _ui_path_datedec(const void *a, const void *b)
- {
- ui_files_t *A = (ui_files_t*)a, *B = (ui_files_t*)b;
- return A->type != B->type ? A->type - B->type : B->date > A->date;
- }
- /**
- * Sort and filter files list
- */
- void _ui_path_filter(ui_path_t *path)
- {
- int i, l;
- if(!path || !path->files) return;
- switch(path->srt) {
- case 1: qsort(path->files, path->numfiles, sizeof(ui_files_t), _ui_path_namedec); break;
- case 2: qsort(path->files, path->numfiles, sizeof(ui_files_t), _ui_path_sizeasc); break;
- case 3: qsort(path->files, path->numfiles, sizeof(ui_files_t), _ui_path_sizedec); break;
- case 4: qsort(path->files, path->numfiles, sizeof(ui_files_t), _ui_path_dateasc); break;
- case 5: qsort(path->files, path->numfiles, sizeof(ui_files_t), _ui_path_datedec); break;
- default: qsort(path->files, path->numfiles, sizeof(ui_files_t), _ui_path_nameasc); break;
- }
- if(!path->filter[0]) {
- path->shfiles = path->numfiles;
- for(i = 0; i < path->numfiles; i++)
- path->files[i].idx = i;
- } else {
- l = strlen(path->filter);
- for(path->shfiles = i = 0; i < path->numfiles; i++)
- if(!_ui_strnatcmp(path->files[i].name, path->filter, l))
- path->files[path->shfiles++].idx = i;
- }
- }
- /**
- * Get file list
- */
- void _ui_path_getfiles(ui_t *ctx, ui_form_t *form)
- {
- #ifdef __WIN32__
- static wchar_t szFn[PATH_MAX + FILENAME_MAX + 1];
- WIN32_FIND_DATAW ffd;
- HANDLE h;
- struct _stat64 st;
- #else
- static char fn[PATH_MAX + FILENAME_MAX + 1];
- DIR *dir;
- struct dirent *ent;
- struct stat st;
- #endif
- ui_path_t *path;
- int i, j, k, l, dw, dh, tw, pw;
- char *s, *e, tmp[32];
- if(!ctx || !form || !form->ptr || !form->data) return;
- path = (ui_path_t*)form->data;
- /* get path buttons */
- path->pathsel = -1;
- path->numpath = path->pathscr = path->pathh = path->sw = path->tw = path->scr = path->sel = path->shfiles = 0;
- if(ctx->fnt && ctx->bbox) {
- dw = 0; dh = ctx->ds; form->t = ctx->dt;
- if(path->pathh < dh) path->pathh = dh;
- for(s = form->ptr, i = 0; *s && i < 255; s = e) {
- #ifdef __WIN32__
- if(!i) { e = s + 2; } else
- #endif
- { for(e = s + 1; *e && e[-1] != DIRSEP[0]; e++){} if(e[-1] != DIRSEP[0]) break; }
- (*ctx->bbox)(ctx->fnt, s, e, &dw, &dh, NULL, NULL);
- path->pathw[i++] = dw + 8;
- if(path->pathh < dh) path->pathh = dh;
- }
- pw = form->ew;
- if(path->flags & UI_PATH_NEWDIR) {
- path->flags |= UI_PATH_SEARCH;
- pw -= 24;
- }
- if(path->flags & UI_PATH_SEARCH) {
- tw = form->ew / 3;
- if(tw > 128) tw = 128;
- pw -= tw + 8;
- }
- path->numpath = i;
- for(tw = 0; i > 0 && tw + path->pathw[i - 1] < pw; tw += path->pathw[--i]);
- path->pathscr = i;
- if(form->str > 0 && form->str + 7 < ctx->txtc && ctx->txtv) {
- (*ctx->bbox)(ctx->fnt, ctx->txtv[form->str + 1], NULL, &path->sw, &dh, NULL, NULL);
- (*ctx->bbox)(ctx->fnt, "9999M", NULL, &dw, &dh, NULL, NULL);
- if(path->sw < dw) path->sw = dw;
- path->sw += 8 + 4;
- (*ctx->bbox)(ctx->fnt, "9999-99-99", NULL, &path->tw, &dh, NULL, NULL);
- path->tw += 8;
- for(i = 2; i < 8; i++) {
- if(i == 4 || i == 6) {
- sprintf(tmp, "99 %s", ctx->txtv[form->str + i]);
- (*ctx->bbox)(ctx->fnt, tmp, NULL, &dw, &dh, NULL, NULL);
- } else
- (*ctx->bbox)(ctx->fnt, ctx->txtv[form->str + i], NULL, &dw, &dh, NULL, NULL);
- if(path->tw < dw) path->tw = dw;
- }
- path->tw += 4 + ctx->sw;
- } else form->str = 0;
- }
- /* file list */
- #ifdef __WIN32__
- if(!((char*)form->ptr)[0]) {
- if((path->files = (ui_files_t*)realloc(path->files, 27 * sizeof(ui_files_t)))) {
- memset(path->files, 0, 27 * sizeof(ui_files_t));
- dw = (int)GetLogicalDrives();
- for(i = l = 0; l < 27; l++)
- if(dw & (1 << l)) {
- path->files[i].size = -1U;
- path->files[i].name[0] = 'A' + l;
- path->files[i++].name[1] = ':';
- }
- path->numfiles = i;
- }
- path->srt = 0;
- memset(path->filter, 0, FILENAME_MAX);
- } else {
- MultiByteToWideChar(CP_UTF8, 0, (char*)form->ptr, -1, szFn, PATH_MAX + FILENAME_MAX);
- l = wcslen(szFn);
- if(l > 0 && szFn[l - 1] != DIRSEP[0]) szFn[l++] = DIRSEP[0];
- wcscpy_s(szFn + l, FILENAME_MAX, L"*.*");
- if((h = FindFirstFileW(szFn, &ffd)) != INVALID_HANDLE_VALUE) {
- i = 0;
- do {
- if(!wcscmp(ffd.cFileName, L".") || !wcscmp(ffd.cFileName, L"..") ||
- ((path->flags & UI_PATH_ONLYDIR) && !(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) ||
- (!(path->flags & UI_PATH_HIDDEN) && (ffd.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM))))
- continue;
- i++;
- } while(FindNextFileW(h, &ffd) != 0);
- if((path->files = (ui_files_t*)realloc(path->files, (i + 1) * sizeof(ui_files_t)))) {
- h = FindFirstFileW(szFn, &ffd);
- i = 0;
- do {
- if(!wcscmp(ffd.cFileName, L".") || !wcscmp(ffd.cFileName, L"..") ||
- ((path->flags & UI_PATH_ONLYDIR) && !(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) ||
- (!(path->flags & UI_PATH_HIDDEN) && (ffd.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM))))
- continue;
- wcscpy_s(szFn + l, FILENAME_MAX, ffd.cFileName);
- if(!_wstat64(szFn, &st)) {
- path->files[i].type = !(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
- path->files[i].size = st.st_size;
- path->files[i].date = st.st_mtime;
- WideCharToMultiByte(CP_UTF8, 0, ffd.cFileName, -1, path->files[i].name, FILENAME_MAX, NULL, NULL);
- #else
- l = strlen(form->ptr);
- memcpy(fn, form->ptr, l);
- if(l && fn[l - 1] == DIRSEP[0]) fn[--l] = 0;
- if((dir = opendir(form->ptr))) {
- fn[l++] = DIRSEP[0];
- i = 0;
- while((ent = readdir(dir))) {
- if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") ||
- ((path->flags & UI_PATH_ONLYDIR) && ent->d_type != DT_DIR) ||
- (!(path->flags & UI_PATH_HIDDEN) && ent->d_name[0] == '.'))
- continue;
- i++;
- }
- rewinddir(dir);
- if((path->files = (ui_files_t*)realloc(path->files, (i + 1) * sizeof(ui_files_t)))) {
- i = 0;
- while((ent = readdir(dir))) {
- if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") ||
- ((path->flags & UI_PATH_ONLYDIR) && ent->d_type != DT_DIR) ||
- (!(path->flags & UI_PATH_HIDDEN) && ent->d_name[0] == '.'))
- continue;
- strncpy(fn + l, ent->d_name, FILENAME_MAX);
- if(!stat(fn, &st)) {
- path->files[i].type = ent->d_type != DT_DIR;
- path->files[i].size = st.st_size;
- path->files[i].date = st.st_mtime;
- strncpy(path->files[i].name, ent->d_name, FILENAME_MAX);
- #endif
- if(path->exts[0]) {
- if((s = strrchr(path->files[i].name, '.')))
- for(s++, j = strlen(s), e = path->exts, k = 1; k > 0 && e < path->exts + 256 && *e; e += k + 1) {
- k = strlen(e);
- if(j == k && !_ui_strnatcmp(s, e, j)) { i++; break; }
- }
- } else
- i++;
- #ifdef __WIN32__
- }
- } while(FindNextFileW(h, &ffd) != 0);
- }
- FindClose(h);
- path->numfiles = i;
- }
- }
- #else
- }
- }
- }
- closedir(dir);
- path->numfiles = i;
- }
- #endif
- _ui_path_filter(path);
- if(path->fn[0]) {
- for(i = 0; i < path->shfiles; i++)
- if(!strcmp(path->files[path->files[i].idx].name, path->fn)) {
- path->sel = i;
- path->scr = i * path->pathh;
- }
- memset(path->fn, 0, FILENAME_MAX);
- }
- }
- /**
- * Create a directory
- */
- void _ui_path_mkdir(char *fn)
- {
- #ifdef __WIN32__
- wchar_t szFn[PATH_MAX + FILENAME_MAX + 1];
- if(!fn || !*fn) return;
- MultiByteToWideChar(CP_UTF8, 0, fn, -1, szFn, PATH_MAX + FILENAME_MAX);
- _wmkdir(szFn);
- #else
- if(!fn || !*fn) return;
- mkdir(fn, 0775);
- #endif
- }
- /**
- * Start file picker
- */
- void ui_path_start(ui_t *ctx, ui_form_t *form)
- {
- #ifdef __WIN32__
- wchar_t szFn[PATH_MAX + FILENAME_MAX + 1], szPath[PATH_MAX + FILENAME_MAX + 1];
- struct _stat64 st;
- #else
- struct stat st;
- #endif
- char path[PATH_MAX + FILENAME_MAX + 1], *s;
- if(!ctx || !form || !form->ptr || !form->data) return;
- if(!((char*)form->ptr)[0]) strcpy(form->ptr, ".");
- memset(path, 0, sizeof(path));
- #ifdef __WIN32__
- MultiByteToWideChar(CP_UTF8, 0, form->ptr, -1, szFn, PATH_MAX + FILENAME_MAX);
- memset(szPath, 0, sizeof(szPath));
- GetFullPathNameW(szFn, PATH_MAX + FILENAME_MAX, szPath, NULL);
- if(!szPath[0]) _wgetcwd(szPath, PATH_MAX + FILENAME_MAX);
- WideCharToMultiByte(CP_UTF8, 0, szPath, -1, path, PATH_MAX + FILENAME_MAX, NULL, NULL);
- if(_wstat64(szPath, &st)) st.st_mode = 0;
- #else
- realpath(form->ptr, path);
- if(!path[0]) getcwd(path, PATH_MAX + FILENAME_MAX);
- if(stat(path, &st)) st.st_mode = 0;
- #endif
- if(S_ISDIR(st.st_mode)) {
- if(path[strlen(path) - 1] != DIRSEP[0])
- strcat(path, DIRSEP);
- memset(((ui_path_t*)form->data)->fn, 0, FILENAME_MAX);
- } else {
- if((s = strrchr(path, DIRSEP[0]))) {
- strncpy(((ui_path_t*)form->data)->fn, s + 1, FILENAME_MAX);
- s[1] = 0;
- }
- }
- strncpy((char*)form->ptr, path, form->max - 1);
- if((char*)form->ptr == ctx->buf)
- ctx->end = ctx->cur = ctx->buf + strlen(ctx->buf);
- _ui_path_getfiles(ctx, form);
- }
- /**
- * Stop file picker
- */
- int ui_path_stop(ui_t *ctx, ui_form_t *form)
- {
- ui_path_t *path;
- if(!ctx || !form || !form->data) return UI_ERR_BADINP;
- path = (ui_path_t*)form->data;
- if(path->files) free(path->files);
- path->files = NULL;
- path->numfiles = path->shfiles = 0;
- return UI_OK;
- }
- /**
- * Process events
- */
- int ui_path_ctrl(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form, ui_event_t *evt)
- {
- ui_files_t *file;
- ui_path_t *path;
- int sel = -1, clk = -1, i, j = w / 3, ph, pt, ps, X = x, Y = y, t, b;
- char *s, *e;
- if(!ctx || !form || !form->ptr || !form->data || !evt) return UI_ERR_BADINP;
- path = (ui_path_t*)form->data;
- ph = path->pathh;
- if((path->flags & UI_PATH_NEWDIR) && path->pathh < 16) path->pathh = 16;
- ph += 6;
- if(ctx->mousex >= x && ctx->mousex < x + w) {
- if(ctx->mousey >= y && ctx->mousey <= y + ph) {
- j = w / 3;
- if(j > 128) j = 128;
- /* one of the path's components */
- for(X = x, i = path->pathscr; i < path->numpath; X += path->pathw[i], i++)
- if(ctx->mousex >= X && ctx->mousex < X + path->pathw[i] - 2) { sel = i; break; }
- X = x + w;
- /* search or newdir button */
- if(path->flags & UI_PATH_NEWDIR) { X -= 24; if(ctx->mousex >= X + 4) sel = -2; }
- if(path->flags & UI_PATH_SEARCH) { X -= j; if(ctx->mousex >= X && ctx->mousex < X + j) sel = -3; }
- }
- y += ph + 2; h -= ph + 2;
- x++; y++; w -= 2; h -= 2;
- pt = w - path->tw; ps = pt - path->sw;
- if(form->str > 0) {
- /* one of the headers */
- if(ctx->mousey >= y && ctx->mousey < y + path->pathh + 4) {
- if(ctx->mousex >= x && ctx->mousex < x + ps) sel = -4; else
- if(ctx->mousex >= x + ps && ctx->mousex < x + ps + path->sw) sel = -5; else
- if(ctx->mousex >= x + pt && ctx->mousex < x + pt + path->tw) sel = -6;
- }
- y += path->pathh + 8; h -= path->pathh + 8;
- }
- if(ctx->mousey >= y && ctx->mousey < y + h) {
- if(ctx->mousex >= x + w - ctx->sw) sel = -7; else
- if(path->pathh > 0) clk = (ctx->mousey - y + path->scr) / path->pathh;
- }
- }
- if(evt->type == UI_EVT_KEY) {
- if(!memcmp(evt->key, "Up", 3)) {
- if(path->sel > 0) path->sel--;
- if(path->sel * path->pathh < path->scr) path->scr = path->sel * path->pathh;
- if((path->sel + 1) * path->pathh - path->scr > h) path->scr = (path->sel + 1) * path->pathh - h;
- evt->type = UI_EVT_NONE;
- } else
- if(!memcmp(evt->key, "Down", 5)) {
- if(path->sel + 1 < path->shfiles) path->sel++;
- if(path->sel * path->pathh < path->scr) path->scr = path->sel * path->pathh;
- if((path->sel + 1) * path->pathh - path->scr > h) path->scr = (path->sel + 1) * path->pathh - h;
- evt->type = UI_EVT_NONE;
- } else
- if(!ctx->str && (!memcmp(evt->key, "Left", 5) || evt->key[0] == '\b') && path->numpath > 1) {
- sel = path->numpath - 2; goto back;
- } else
- if(!ctx->str && (!memcmp(evt->key, "Right", 6) || evt->key[0] == '\n') && path->sel >= 0) {
- clk = path->sel; goto enter;
- } else
- if(ctx->str == path->filter && ctx->buf) {
- /* user typed in the filter input */
- strcpy(path->filter, ctx->buf);
- _ui_path_filter(path);
- }
- } else
- if(evt->type == UI_EVT_MOUSE) {
- if(evt->btn & UI_BTN_RELEASE) {
- if(path->pathsel == sel) {
- switch(sel) {
- case -2:
- if(path->filter[0]) {
- i = strlen((char*)form->ptr);
- t = strlen(path->filter);
- if(i + t + 2 < form->max) {
- s = (char*)form->ptr + i; if(i && s[-1] != DIRSEP[0]) *s++ = DIRSEP[0];
- strcpy(s, path->filter);
- s += t; if(s[-1] == DIRSEP[0]) *--s = 0;
- memset(path->fn, 0, FILENAME_MAX);
- _ui_path_mkdir(form->ptr); *s++ = DIRSEP[0]; *s = 0;
- _ui_path_getfiles(ctx, form);
- }
- }
- break;
- case -3: _ui_text_start(ctx, X, Y, j, ph, form, path->filter, sizeof(path->filter) - 1); return UI_OK;
- case -4: if(path->srt != 0) path->srt = 0; else path->srt = 1; _ui_path_filter(path); path->sel = path->scr = 0; break;
- case -5: if(path->srt != 2) path->srt = 2; else path->srt = 3; _ui_path_filter(path); path->sel = path->scr = 0; break;
- case -6: if(path->srt != 4) path->srt = 4; else path->srt = 5; _ui_path_filter(path); path->sel = path->scr = 0; break;
- default:
- back: if(sel >= 0 && sel < path->numpath) {
- s = form->ptr;
- #ifdef __WIN32__
- if(sel)
- #endif
- {
- for(i = 0; *s && i <= sel; s = e, i++) {
- #ifdef __WIN32__
- if(!i) { e = s + 2; } else
- #endif
- { for(e = s + 1; *e && e[-1] != DIRSEP[0]; e++){} if(e[-1] != DIRSEP[0]) break; }
- }
- }
- for(e = path->fn, i = 0; s[i] && s[i] != DIRSEP[0]; i++)
- e[i] = s[i];
- e[i] = 0;
- *s = 0;
- memset(path->filter, 0, sizeof(path->filter));
- _ui_path_getfiles(ctx, form);
- }
- break;
- }
- }
- path->pathsel = -1;
- ctx->vscr = NULL;
- } else
- if(evt->btn & (UI_BTN_U | UI_BTN_D)) {
- i = h / 10; if(i < 4) i = 4;
- if(evt->btn & UI_BTN_U) path->scr -= i; else path->scr += i;
- i = path->shfiles * path->pathh - h;
- if(path->scr > i) path->scr = i;
- if(path->scr < 0) path->scr = 0;
- } else
- if(evt->btn & UI_BTN_L) {
- path->pathsel = sel;
- if(sel == -7) {
- /* scrollbar */
- memset(&_ui_path_vscr, 0, sizeof(_ui_path_vscr));
- _ui_path_vscr.type = UI_VSCRBAR;
- _ui_path_vscr.max = path->shfiles * path->pathh;
- _ui_path_vscr.ptr = &path->scr;
- ctx->vscr = &_ui_path_vscr;
- ctx->sm = _ui_path_vscr.max - h;
- t = _ui_scr(h, path->scr, _ui_path_vscr.max, ctx->sw, &b);
- ctx->sb = ctx->mousey >= y + t && ctx->mousey < y + t + b ? ctx->mousey - y - t : b / 2;
- ctx->s1 = y; ctx->s2 = y + h - b + ctx->sb;
- } else
- if(clk >= 0) {
- /* file list */
- if(path->sel == clk) {
- enter: file = &path->files[path->files[clk].idx];
- i = strlen((char*)form->ptr);
- t = strlen(file->name);
- if(i + t + 2 < form->max) {
- path->sel = -1;
- s = (char*)form->ptr + i;
- if(i && s[-1] != DIRSEP[0]) { *s++ = DIRSEP[0]; *s = 0; }
- strcpy(s, file->name);
- memset(path->filter, 0, sizeof(path->filter));
- if(!file->type) {
- /* directory clicked */
- s += t; *s++ = DIRSEP[0]; *s = 0;
- if(path->select && (*path->select)(form->ptr, 1)) goto sel;
- _ui_path_getfiles(ctx, form);
- } else {
- /* file clicked */
- if(!path->select || (*path->select)(form->ptr, 0)) {
- sel: if((char*)form->ptr == ctx->buf)
- ctx->end = ctx->cur = ctx->buf + strlen(ctx->buf);
- ctx->flags |= UI_CLOSE | UI_DONE;
- _ui_text_ctrl(ctx, evt);
- } else *s = 0;
- }
- }
- } else path->sel = clk;
- }
- }
- }
- return UI_OK;
- }
- /**
- * Recalculate form element bounding box
- */
- int ui_file_bbox(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form, int *dw, int *dh)
- {
- if(!ctx || !form || !dw || !dh) return UI_ERR_BADINP;
- (void)x; (void)y; (void)w; (void)h;
- if(ctx->fnt && ctx->bbox && form->ptr) {
- /* if string is empty, use a default one to get height and baseline correctly */
- (*ctx->bbox)(ctx->fnt, form->ptr && *((char*)form->ptr) ? (char*)form->ptr : "Ag", NULL, dw, dh, &form->l, &form->t);
- if(!form->ptr || !*((char*)form->ptr)) *dw = 0;
- (*dh) += 4;
- }
- if(form->ew > 0) *dw = form->ew;
- return UI_OK;
- }
- /**
- * Display a text input field and a file picker
- */
- int ui_file_view(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form)
- {
- int i;
- if(!ctx || !form) return UI_ERR_BADINP;
- if(form == _ui_file_popup) {
- /* draw the file input box and a path box below on a popup */
- i = form->min;
- if(!(form->flags & UI_NOBORDER)) {
- _ui_rect(ctx, x, y, w, h, ctx->theme[UI_IB], ctx->theme[UI_IBG], ctx->theme[UI_IB]);
- x++; y++; w -= 2; h -= 2; i -= 2;
- }
- if(ctx->skin[UI_INP].buf)
- _ui_blit(ctx, x, y, w, h, &ctx->skin[UI_INP], 0, 0, 0);
- else
- _ui_frect(ctx, x, y, w, h, ctx->theme[UI_IBG]);
- _ui_text(ctx, x, y, w, i, form->l, form->t, form->flags, 9, form->ptr, NULL);
- ui_path_view(ctx, form->ex, form->ey, form->ew, form->eh, form);
- } else
- /* draw the input field */
- _ui_text(ctx, x, y, w, h, form->l, form->t, form->flags, ctx->text == form, form->ptr, NULL);
- return UI_OK;
- }
- /**
- * Process events
- */
- int ui_file_ctrl(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form, ui_event_t *evt)
- {
- if(!ctx || !form || !evt) return UI_ERR_BADINP;
- if(!ctx->popup && evt->type == UI_EVT_MOUSE && (evt->btn & UI_BTN_L)) {
- /* user clicked on the text input field */
- if(form->ptr && form->max > 0) {
- evt->type = UI_EVT_NONE;
- ((char*)form->ptr)[form->max - 1] = 0;
- _ui_text_start(ctx, x, y, w, h, form, form->ptr, form->max);
- memcpy(_ui_file_popup, form, sizeof(ui_form_t));
- _ui_file_popup[0].ex = x + 4;
- _ui_file_popup[0].ey = y + h + 4;
- _ui_file_popup[0].ew = w - 8;
- _ui_file_popup[0].eh = form->min > h ? form->min : 256;
- _ui_file_popup[0].min = h;
- _ui_file_popup[0].ptr = ctx->buf;
- _ui_file_popup[0].data = &_ui_path_ctx;
- memset(&_ui_path_ctx, 0, sizeof(_ui_path_ctx));
- ui_path_start(ctx, _ui_file_popup);
- ctx->popup = _ui_file_popup;
- ctx->px = x;
- ctx->py = y;
- ctx->pw = w;
- ctx->ph = h + _ui_file_popup[0].eh + 8;
- ctx->pe = &ui_file_ctrl;
- ctx->dr = &ui_file_view;
- }
- } else
- if(ctx->popup) {
- /* popup event handler */
- ui_path_ctrl(ctx, ctx->popup->ex, ctx->popup->ey, ctx->popup->ew, ctx->popup->eh, ctx->popup, evt);
- }
- return UI_OK;
- }
- #ifdef __cplusplus
- }
- #endif
- #endif /* UI_FILE_H */
|