m_fileops.c 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825
  1. /*
  2. * sfnedit/m_fileops.c
  3. *
  4. * Copyright (C) 2020 bzt (bztsrc@gitlab)
  5. *
  6. * Permission is hereby granted, free of charge, to any person
  7. * obtaining a copy of this software and associated documentation
  8. * files (the "Software"), to deal in the Software without
  9. * restriction, including without limitation the rights to use, copy,
  10. * modify, merge, publish, distribute, sublicense, and/or sell copies
  11. * of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be
  15. * included in all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  19. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  20. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  21. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  22. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  24. * DEALINGS IN THE SOFTWARE.
  25. *
  26. * @brief Main window file operation tools
  27. *
  28. */
  29. #include <stdint.h>
  30. #include <stdio.h>
  31. #include <string.h>
  32. #include <unistd.h>
  33. #include <dirent.h>
  34. #include <time.h>
  35. #include <sys/stat.h>
  36. #include <inttypes.h>
  37. #include <stdlib.h>
  38. #include <limits.h>
  39. #include "libsfn.h"
  40. #include "ui.h"
  41. #include "lang.h"
  42. #ifdef __WIN32__
  43. #include <windows.h>
  44. #include <winnls.h>
  45. #include <shlobj.h>
  46. #else
  47. #ifndef __wur
  48. #define __wur
  49. #endif
  50. extern char *realpath (const char *__restrict __name, char *__restrict __resolved) __THROW __wur;
  51. #endif
  52. #ifndef PRIu64
  53. #if __WORDSIZE == 64
  54. #define PRIu64 "lu"
  55. #define PRId64 "ld"
  56. #else
  57. #define PRIu64 "llu"
  58. #define PRId64 "lld"
  59. #endif
  60. #endif
  61. #ifndef PATH_MAX
  62. # ifdef MAX_PATH
  63. # define PATH_MAX MAX_PATH
  64. # else
  65. # define PATH_MAX 65536
  66. # endif
  67. #endif
  68. #ifndef FILENAME_MAX
  69. # define FILENAME_MAX 256
  70. #endif
  71. typedef struct {
  72. char *name;
  73. char type;
  74. uint64_t size;
  75. time_t time;
  76. } filelist_t;
  77. filelist_t *files = NULL;
  78. int numfiles = 0, scrollfiles = 0, pagefiles = 0, selfiles = -1, ordering = 0, pathX[PATH_MAX/FILENAME_MAX+64] = { 0 }, pathlen;
  79. int lastsave = -1, clkfiles = 0, question_y = 0, numdrives = 0, seldrive = 0;
  80. char fn[PATH_MAX+64] = {0}, filename[FILENAME_MAX+64] = {0}, path[PATH_MAX/FILENAME_MAX+64][FILENAME_MAX], fsearch[256] = { 0 };
  81. char strrs[16] = { 0 }, strre[16] = { 0 }, fstatus[256], drives[128];
  82. unsigned char peekbuf[256];
  83. /**
  84. * Sort file names
  85. */
  86. static int fncmp(const void *a, const void *b)
  87. {
  88. filelist_t *A = (filelist_t*)a, *B = (filelist_t*)b;
  89. if(ordering < 4) {
  90. if(A->type && !B->type) return 1;
  91. if(!A->type && B->type) return -1;
  92. }
  93. switch(ordering) {
  94. case 0: return strcmp(A->name, B->name);
  95. case 1: return strcmp(B->name, A->name);
  96. case 2: return A->size > B->size;
  97. case 3: return B->size > A->size;
  98. case 4: return (int)(A->time - B->time);
  99. case 5: return (int)(B->time - A->time);
  100. }
  101. return 0;
  102. }
  103. /**
  104. * Read directory contents
  105. */
  106. void fileops_readdir(int save)
  107. {
  108. char tmp[PATH_MAX+64];
  109. DIR *dir;
  110. struct dirent *de;
  111. struct stat st;
  112. int i, j, k, l = strlen(fsearch);
  113. if(files) {
  114. for(i = 0; i < numfiles; i++)
  115. if(files[i].name)
  116. free(files[i].name);
  117. free(files);
  118. files = NULL;
  119. }
  120. numfiles = scrollfiles = 0; selfiles = clkfiles = -1;
  121. for(tmp[0] = 0, i = 0; i < pathlen; i++)
  122. strcat(tmp, path[i]);
  123. j = strlen(tmp);
  124. dir = opendir(tmp);
  125. if(dir) {
  126. while((de = readdir(dir))) {
  127. if(de->d_name[0] == '.') continue;
  128. strcpy(tmp + j, de->d_name);
  129. if(stat(tmp, &st)) continue;
  130. if(!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) continue;
  131. k = strlen(de->d_name);
  132. if(!S_ISDIR(st.st_mode)) {
  133. if(k < 3) continue;
  134. if(!memcmp(de->d_name + k - 3, ".gz", 3)) k -= 3;
  135. if(k < 5) continue;
  136. if( memcmp(de->d_name + k - 4, ".sfn", 4) && memcmp(de->d_name + k - 4, ".asc", 4)
  137. #ifndef USE_NOFOREIGN
  138. && (save || (memcmp(de->d_name + k - 4, ".pf2", 4) &&
  139. memcmp(de->d_name + k - 4, ".pfa", 4) && memcmp(de->d_name + k - 4, ".pfb", 4) &&
  140. memcmp(de->d_name + k - 5, ".woff",5) && memcmp(de->d_name + k - 5, "woff2",5) &&
  141. memcmp(de->d_name + k - 4, ".ttf", 4) && memcmp(de->d_name + k - 4, ".ttc", 4) &&
  142. memcmp(de->d_name + k - 4, ".otf", 4) && memcmp(de->d_name + k - 4, ".pcf", 4) &&
  143. memcmp(de->d_name + k - 4, ".sfd", 4) && memcmp(de->d_name + k - 4, ".svg", 4) &&
  144. memcmp(de->d_name + k - 4, ".fnt", 4) && memcmp(de->d_name + k - 4, ".fon", 4) &&
  145. memcmp(de->d_name + k - 4, ".psf", 4) && memcmp(de->d_name + k - 5, ".psfu",5) &&
  146. memcmp(de->d_name + k - 4, ".bdf", 4) && memcmp(de->d_name + k - 4, ".hex", 4) &&
  147. memcmp(de->d_name + k - 4, ".bmf", 4) && memcmp(de->d_name + k - 5, ".yaff", 5) &&
  148. memcmp(de->d_name + k - 6, ".kbits", 6) && memcmp(de->d_name + k - 6, ".kbitx", 6) &&
  149. memcmp(de->d_name + k - 4, ".tga", 4) && memcmp(de->d_name + k - 4, ".png", 4)))
  150. #endif
  151. ) continue;
  152. }
  153. if(l) {
  154. if(k < l) continue;
  155. k -= l;
  156. for(i = 0; i <= k && ui_casecmp(de->d_name + i, fsearch, l); i++);
  157. if(i > k) continue;
  158. }
  159. i = numfiles++;
  160. files = (filelist_t*)realloc(files, numfiles * sizeof(filelist_t));
  161. if(!files) { numfiles = 0; break; }
  162. files[i].name = (char*)malloc(strlen(de->d_name)+1);
  163. if(!files[i].name) { numfiles--; continue; }
  164. strcpy(files[i].name, de->d_name);
  165. files[i].type = S_ISDIR(st.st_mode) ? 0 : 1;
  166. files[i].size = st.st_size;
  167. files[i].time = st.st_mtime;
  168. }
  169. closedir(dir);
  170. }
  171. qsort(files, numfiles, sizeof(filelist_t), fncmp);
  172. memset(peekbuf, 0, sizeof(peekbuf));
  173. px = py = 0;
  174. }
  175. /**
  176. * Peek into selected file to see if it's an image
  177. */
  178. void fileops_peek(int sel)
  179. {
  180. FILE *f;
  181. char tmp[PATH_MAX+64];
  182. int i, w, h;
  183. memset(peekbuf, 0, sizeof(peekbuf));
  184. px = py = 0;
  185. if(sel >= 0 && sel < numfiles) {
  186. for(tmp[0] = 0, i = 0; i < pathlen; i++)
  187. strcat(tmp, path[i]);
  188. strcat(tmp, files[sel].name);
  189. if((f = fopen(tmp, "rb"))) {
  190. i = fread(peekbuf, 1, sizeof(peekbuf), f);
  191. fclose(f);
  192. w = h = 0;
  193. if(peekbuf[0]==0 && (peekbuf[1]==0 || peekbuf[1]==1) && (peekbuf[2]==1 || peekbuf[2]==2 || peekbuf[2]==9 ||
  194. peekbuf[2]==10) && (peekbuf[16]==8 || peekbuf[16]==24 || peekbuf[16]==32)) {
  195. w = (peekbuf[13] << 8) + peekbuf[12];
  196. h = (peekbuf[15] << 8) + peekbuf[14];
  197. } else
  198. if(peekbuf[0]==0x89 && peekbuf[1]=='P' && peekbuf[2]=='N' && peekbuf[3]=='G' && peekbuf[12] == 'I' && peekbuf[13] == 'H') {
  199. w = (peekbuf[18] << 8) + peekbuf[19];
  200. h = (peekbuf[22] << 8) + peekbuf[23];
  201. }
  202. if(w >= 128 && h >= 128 && !(w & 0xf) && !(h & 0xf) && (w < h ? h / w : w / h) < 3) { px = w / 16; py = h / 16; }
  203. }
  204. }
  205. }
  206. /**
  207. * File operations windows, load / save
  208. */
  209. void view_fileops(int save)
  210. {
  211. struct tm *lt;
  212. char *s, tmp[32];
  213. int i, j, k;
  214. ui_win_t *win = &wins[0];
  215. time_t now = time(NULL), diff;
  216. #ifdef __WIN32__
  217. wchar_t home[MAX_PATH];
  218. #endif
  219. if(!fn[0]) {
  220. if(ctx.filename)
  221. #ifdef __WIN32__
  222. strcpy(fn, ctx.filename);
  223. #else
  224. s = realpath(ctx.filename, fn);
  225. #endif
  226. else {
  227. #ifdef __WIN32__
  228. if(SHGetFolderPathW(HWND_DESKTOP, CSIDL_DESKTOPDIRECTORY, NULL, 0, home))
  229. wsprintfW(home, L".\\");
  230. for(i = 0, s = fn; home[i]; i++) {
  231. if(home[i] < 0x80) {
  232. *s++ = home[i];
  233. } else if(home[i] < 0x800) {
  234. *s++ = ((home[i]>>6)&0x1F)|0xC0;
  235. *s++ = (home[i]&0x3F)|0x80;
  236. } else {
  237. *s++ = ((home[i]>>12)&0x0F)|0xE0;
  238. *s++ = ((home[i]>>6)&0x3F)|0x80;
  239. *s++ = (home[i]&0x3F)|0x80;
  240. }
  241. }
  242. *s++ = DIRSEP;
  243. *s = 0;
  244. #else
  245. if((!getcwd(fn, sizeof(fn) - 3) || !fn[0] || !memcmp(fn, "/usr", 4)) && (s = getenv("HOME")) != NULL) strcpy(fn, s);
  246. strcat(fn, "/");
  247. #endif
  248. strcpy(filename, "noname.sfn");
  249. }
  250. }
  251. ssfn_dst.fg = theme[THEME_FG];
  252. ssfn_dst.bg = theme[THEME_BG];
  253. ssfn_dst.w = win->w;
  254. ssfn_dst.h = win->h;
  255. ui_box(win, 10, 29, win->w - 168, 20, theme[THEME_BG], theme[THEME_BG], theme[THEME_BG]);
  256. memset(path, 0, sizeof(path));
  257. for(i = pathlen = 0, s = fn; *s; s++) {
  258. if(i < FILENAME_MAX-1) path[pathlen][i++] = *s;
  259. if(*s == DIRSEP) { i = 0; pathlen++; }
  260. }
  261. memset(pathX, 0, sizeof(pathX));
  262. if(wins[0].field == 7) j = k = THEME_CURSOR; else { j = THEME_LIGHT; k = THEME_DARK; }
  263. ssfn_dst.w = win->w - 158; if(ssfn_dst.w < 1) ssfn_dst.w = 1;
  264. for(i = 0, ssfn_dst.x = 10; i < pathlen && ssfn_dst.x < win->w - 158; i++) {
  265. pathX[i] = ssfn_dst.x;
  266. ui_text(win, ssfn_dst.x, 30, path[i]);
  267. ui_rect(win, pathX[i] - 3, 29, ssfn_dst.x - pathX[i] + 6, 20, theme[selfield == i + 7 ? k : j],
  268. theme[selfield == i + 7 ? j : k]);
  269. ssfn_dst.x += 8;
  270. }
  271. pathX[i] = ssfn_dst.x - 8;
  272. ssfn_dst.w = win->w;
  273. if(!files || lastsave != save || input_refresh) fileops_readdir(save);
  274. lastsave = save;
  275. if(wins[0].field == 9 && selfiles == -1 && numfiles > 0)
  276. selfiles = 0;
  277. pagefiles = (win->h - 51 - 72) / 16; if(pagefiles < 2) pagefiles = 2;
  278. if(selfiles + 1 > scrollfiles + pagefiles - 1) scrollfiles = selfiles - pagefiles + 1;
  279. if(selfiles >= 0 && selfiles < scrollfiles) scrollfiles = selfiles;
  280. if(wins[0].tool == MAIN_TOOL_LOAD || wins[0].tool == MAIN_TOOL_SAVE) {
  281. ssfn_dst.w = win->w - 8; if(ssfn_dst.w < 1) ssfn_dst.w = 1;
  282. ssfn_dst.h = win->h;
  283. ui_icon(win, win->w - 134 - 16, 30, ICON_SEARCH, 0);
  284. ui_input(win, win->w - 132, 29, 120, fsearch, wins[0].field == 8, 255, 0);
  285. ui_rect(win, 7, 51, win->w - 14, win->h - 102, theme[THEME_DARKER], theme[THEME_LIGHT]);
  286. ui_box(win, 8, 52, 18, 20, theme[THEME_LIGHT], theme[THEME_BG], theme[THEME_DARKER]);
  287. ssfn_dst.w = win->w - 284; if(ssfn_dst.w < 1) ssfn_dst.w = 1;
  288. ui_box(win, 26, 52, win->w - 314, 20, theme[selfield == 1 ? THEME_DARKER : THEME_LIGHT], theme[THEME_BG],
  289. theme[selfield == 1 ? THEME_LIGHT : THEME_DARKER]);
  290. ssfn_dst.fg = theme[THEME_LIGHTER];
  291. ui_text(win, 34, 53, lang[FILEOP_NAME]);
  292. ssfn_dst.fg = theme[THEME_DARKER];
  293. ui_text(win, 33, 52, lang[FILEOP_NAME]);
  294. ssfn_dst.w = win->w - 168; if(ssfn_dst.w < 1) ssfn_dst.w = 1;
  295. ui_box(win, win->w - 288, 52, 120, 20, theme[selfield == 2 ? THEME_DARKER : THEME_LIGHT], theme[THEME_BG],
  296. theme[selfield == 2 ? THEME_LIGHT : THEME_DARKER]);
  297. ssfn_dst.fg = theme[THEME_LIGHTER];
  298. ui_text(win, win->w - 280, 53, lang[FILEOP_SIZE]);
  299. ssfn_dst.fg = theme[THEME_DARKER];
  300. ui_text(win, win->w - 281, 52, lang[FILEOP_SIZE]);
  301. ssfn_dst.w = win->w - 9; if(ssfn_dst.w < 1) ssfn_dst.w = 1;
  302. ui_box(win, win->w - 168, 52, 159, 20, theme[selfield == 3 ? THEME_DARKER : THEME_LIGHT], theme[THEME_BG],
  303. theme[selfield == 3 ? THEME_LIGHT : THEME_DARKER]);
  304. ssfn_dst.fg = theme[THEME_LIGHTER];
  305. ui_text(win, win->w - 160, 53, lang[FILEOP_TIME]);
  306. ssfn_dst.fg = theme[THEME_DARKER];
  307. ui_text(win, win->w - 161, 52, lang[FILEOP_TIME]);
  308. ssfn_dst.w = win->w;
  309. ssfn_dst.fg = theme[THEME_FG];
  310. j = win->w * 60;
  311. switch(ordering >> 1) {
  312. case 0: ui_tri(win, win->w - 300, 60, ordering & 1); break;
  313. case 1: ui_tri(win, win->w - 180, 60, ordering & 1); break;
  314. case 2: ui_tri(win, win->w - 20, 60, ordering & 1); break;
  315. }
  316. ssfn_dst.y = 72;
  317. ssfn_dst.h = win->h - 63;
  318. for(i = scrollfiles; i < numfiles && ssfn_dst.y < ssfn_dst.h; i++, ssfn_dst.y += 16) {
  319. if(i == selfiles) {
  320. ui_box(win, 9, ssfn_dst.y, win->w - 30, 16, theme[THEME_SELBG], theme[THEME_SELBG], theme[THEME_SELBG]);
  321. ssfn_dst.fg = theme[THEME_SELFG];
  322. } else
  323. ssfn_dst.fg = theme[THEME_FG];
  324. ui_icon(win, 9, ssfn_dst.y, files[i].type ? ICON_FILE : ICON_FOLDER, 0);
  325. ssfn_dst.w = win->w - 284; if(ssfn_dst.w < 1) ssfn_dst.w = 1;
  326. ui_text(win, 30, ssfn_dst.y, files[i].name);
  327. sprintf(tmp,"%13" PRIu64,files[i].size);
  328. ssfn_dst.w = win->w - 168; if(ssfn_dst.w < 1) ssfn_dst.w = 1;
  329. ui_text(win, win->w - 288, ssfn_dst.y, tmp);
  330. ssfn_dst.w = win->w - 9; if(ssfn_dst.w < 1) ssfn_dst.w = 1;
  331. diff = now - files[i].time;
  332. if(diff < 120) strcpy(tmp, lang[FILEOP_NOW]); else
  333. if(diff < 3600) sprintf(tmp, lang[FILEOP_MSAGO], (int)(diff/60)); else
  334. if(diff < 7200) sprintf(tmp, lang[FILEOP_HAGO], (int)(diff/60)); else
  335. if(diff < 24*3600) sprintf(tmp, lang[FILEOP_HSAGO], (int)(diff/3600)); else
  336. if(diff < 48*3600) strcpy(tmp, lang[FILEOP_YESTERDAY]); else {
  337. lt = localtime(&files[i].time);
  338. if(files[i].time < 7*24*3600) strcpy(tmp, lang[FILEOP_WDAY0 + lt->tm_wday]); else
  339. sprintf(tmp, "%04d-%02d-%02d", lt->tm_year+1900, lt->tm_mon+1, lt->tm_mday);
  340. }
  341. ui_text(win, win->w - 160, ssfn_dst.y, tmp);
  342. }
  343. ssfn_dst.fg = theme[THEME_FG];
  344. ssfn_dst.w = win->w;
  345. ssfn_dst.h = win->h;
  346. ui_hscrbar(win, win->w - 20, 72, 12, win->h - 124, scrollfiles, pagefiles, numfiles, selfield == 5);
  347. j = win->w / 4;
  348. if(!save) {
  349. if(!strrs[0]) sprintf(strrs, "U+%X", rs);
  350. if(!strre[0]) sprintf(strre, "U+%X", re);
  351. ui_text(win, 8, win->h - 44, lang[FILEOP_RANGE]);
  352. ui_input(win, 80, win->h - 44, 72, strrs, wins[0].field == 10, 15, 1);
  353. ui_input(win, 164, win->h - 44, 72, strre, wins[0].field == 11, 15, 2);
  354. ui_bool(win, 250, win->h - 44, lang[FILEOP_REPLACE], replace, wins[0].field == 12);
  355. if(px || py) {
  356. ui_num(win, 440, win->h - 44, px, wins[0].field == 14, selfield - 14);
  357. ui_num(win, 496, win->h - 44, py, wins[0].field == 15, selfield - 16);
  358. }
  359. ui_button(win, win->w - 12 - j, win->h - 44, j, lang[FILEOP_IMPORT], selfield == 4 ? 1 : 0, wins[0].field == 13);
  360. } else {
  361. ui_bool(win, 8, win->h - 44, "ASC", ascii, wins[0].field == 10);
  362. ui_bool(win, 64, win->h - 44, "gzip", zip, wins[0].field == 11);
  363. s = strrchr(filename, '.');
  364. if(s && !strcmp(s, ".gz")) { *s = 0; s = strrchr(filename, '.'); }
  365. if(!s) s = filename + strlen(filename);
  366. if(s != filename) memcpy(s, ascii ? ".asc" : ".sfn", 5);
  367. ui_input(win, 128, win->h - 44, win->w - j - 12 - 128 - 12, filename, wins[0].field == 12, 255, 0);
  368. ui_button(win, win->w - 12 - j, win->h - 44, j, lang[FILEOP_SAVE], selfield == 4 ? 3 : 2, wins[0].field == 13);
  369. }
  370. }
  371. #ifdef __WIN32__
  372. if(selfield == 7) {
  373. memset(drives, 0, sizeof(drives)); numdrives = 0;
  374. k = (int)GetLogicalDrives();
  375. for(i = 0, j = 1; i < 32; i++, j <<= 1)
  376. if(k & j) {
  377. drives[numdrives * 4] = 'A' + i;
  378. drives[numdrives * 4 + 1] = ':';
  379. drives[numdrives * 4 + 2] = DIRSEP;
  380. numdrives++;
  381. }
  382. ui_box(win, 10, 52, 32, 4 + numdrives * 16, theme[THEME_DARK], theme[THEME_BG], theme[THEME_LIGHT]);
  383. for(i = 0; i < numdrives; i++) {
  384. if(i == seldrive) {
  385. ui_box(win, 11, 52 + i * 16, 30, 16, theme[THEME_SELBG], theme[THEME_SELBG], theme[THEME_SELBG]);
  386. ssfn_dst.fg = theme[THEME_SELFG];
  387. } else
  388. ssfn_dst.fg = theme[THEME_FG];
  389. ui_text(win, 12, 52 + i * 16, drives + i * 4);
  390. }
  391. }
  392. #endif
  393. }
  394. /**
  395. * On enter handler
  396. */
  397. void ctrl_fileops_onenter(int save)
  398. {
  399. int i, j = wins[0].w / 4;
  400. char *s;
  401. clkfiles = -1;
  402. if(wins[0].field == 7) {
  403. if(pathlen > 0) pathlen--;
  404. for(fn[0] = 0, i = 0; i < pathlen; i++)
  405. strcat(fn, path[i]);
  406. fileops_readdir(save);
  407. path[pathlen][strlen(path[pathlen]) - 1] = 0;
  408. for(selfiles = 0; selfiles < numfiles && strcmp(files[selfiles].name, path[pathlen]); selfiles++);
  409. }
  410. if(!save) {
  411. if(wins[0].field == 12) replace ^= 1;
  412. if(wins[0].field == 9 || wins[0].field == 13) {
  413. if(selfiles >= 0 && selfiles < numfiles) {
  414. for(fn[0] = 0, i = 0; i < pathlen; i++)
  415. strcat(fn, path[i]);
  416. strcat(fn, files[selfiles].name);
  417. if(files[selfiles].type) {
  418. ui_cursorwin(&wins[0], CURSOR_LOADING);
  419. ui_button(&wins[0], wins[0].w - 12 - j, wins[0].h - 44, j, lang[FILEOP_IMPORT], 0, -1);
  420. ui_flushwin(&wins[0], wins[0].w - 12 - j, wins[0].h - 44, j, 32);
  421. if(sfn_load(fn, 0)) {
  422. sfn_sanitize(-1);
  423. strcpy(filename, files[selfiles].name);
  424. wins[0].tool = MAIN_TOOL_GLYPHS;
  425. wins[0].field = selfield = -1;
  426. ui_updatetitle(0);
  427. } else {
  428. sprintf(fstatus, "libsfn: %s", lang[ERR_LOAD]);
  429. errstatus = fstatus;
  430. }
  431. ui_cursorwin(&wins[0], CURSOR_PTR);
  432. } else {
  433. strcat(path[pathlen++], files[selfiles].name);
  434. strcat(fn, DIRSEPS);
  435. fileops_readdir(save);
  436. selfiles = 0;
  437. wins[0].field = 9;
  438. }
  439. }
  440. }
  441. } else {
  442. if(wins[0].field == 10) { ascii ^= 1; if(ascii) zip = 0; }
  443. if(wins[0].field == 11) { zip ^= 1; if(zip) ascii = 0; }
  444. if(wins[0].field == 9 || wins[0].field == 13) {
  445. for(fn[0] = 0, i = 0; i < pathlen; i++)
  446. strcat(fn, path[i]);
  447. if(wins[0].field == 13 || (selfiles >= 0 && selfiles < numfiles && files[selfiles].type)) {
  448. if(wins[0].field == 9) strcpy(filename, files[selfiles].name);
  449. s = strrchr(filename, '.');
  450. if(s && !strcmp(s, ".gz")) { *s = 0; s = strrchr(filename, '.'); }
  451. if(!s) s = filename + strlen(filename);
  452. if(s != filename) memcpy(s, ascii ? ".asc" : ".sfn", 5);
  453. strcat(fn, filename);
  454. ui_cursorwin(&wins[0], CURSOR_LOADING);
  455. if(wins[0].tool == MAIN_TOOL_SAVE) {
  456. ui_input(&wins[0], 128, wins[0].h - 44, wins[0].w - j - 12 - 128 - 12, filename, 0, 255, 0);
  457. ui_button(&wins[0], wins[0].w - 12 - j, wins[0].h - 44, j, lang[FILEOP_SAVE], 2, -1);
  458. ui_flushwin(&wins[0], 0, wins[0].h - 44, wins[0].w, 32);
  459. }
  460. sfn_sanitize(-1);
  461. if(sfn_save(fn, ascii, zip)) ui_updatetitle(0); else
  462. {
  463. sprintf(fstatus, "libsfn: %s", lang[ERR_SAVE]);
  464. errstatus = fstatus;
  465. }
  466. fileops_readdir(save);
  467. ui_cursorwin(&wins[0], CURSOR_PTR);
  468. } else {
  469. strcat(path[pathlen++], files[selfiles].name);
  470. strcat(fn, files[selfiles].name);
  471. strcat(fn, DIRSEPS);
  472. fileops_readdir(save);
  473. selfiles = 0;
  474. wins[0].field = 9;
  475. }
  476. }
  477. }
  478. ui_resizewin(&wins[0], wins[0].w, wins[0].h);
  479. ui_refreshwin(0, 0, 0, wins[0].w, wins[0].h);
  480. selfield = -1;
  481. }
  482. /**
  483. * On key handler
  484. */
  485. void ctrl_fileops_onkey()
  486. {
  487. int i, j;
  488. if(wins[0].field == 9) {
  489. switch(event.x) {
  490. case K_LEFT:
  491. case K_BACKSPC:
  492. wins[0].field = 7;
  493. ctrl_fileops_onenter(wins[0].tool == MTOOL_SAVE);
  494. wins[0].field = 9;
  495. break;
  496. case K_RIGHT: ctrl_fileops_onenter(wins[0].tool == MTOOL_SAVE); break;
  497. case K_UP:
  498. if(event.h & 1) { if(ordering > 0) ordering--; else ordering = 5; }
  499. else { if(selfiles > 0) selfiles--; }
  500. break;
  501. case K_DOWN:
  502. if(event.h & 1) { if(ordering < 5) ordering++; else ordering = 0; }
  503. else { if(selfiles + 1 < numfiles) selfiles++; }
  504. break;
  505. case K_PGUP:
  506. selfiles -= pagefiles - 1;
  507. if(selfiles < 0) selfiles = 0;
  508. break;
  509. case K_PGDN:
  510. selfiles += pagefiles - 1;
  511. if(selfiles >= numfiles) selfiles = numfiles - 1;
  512. break;
  513. case K_HOME: selfiles = 0; break;
  514. case K_END: selfiles = numfiles - 1; break;
  515. default:
  516. j = strlen((char*)&event.x);
  517. for(i = selfiles + 1; i < numfiles && memcmp(files[i].name, (char*)&event.x, j); i++);
  518. if(i < numfiles) selfiles = i;
  519. else {
  520. for(i = 0; i < selfiles && memcmp(files[i].name, (char*)&event.x, j); i++);
  521. if(i < selfiles) selfiles = i;
  522. }
  523. break;
  524. }
  525. ui_resizewin(&wins[0], wins[0].w, wins[0].h);
  526. ui_refreshwin(0, 0, 0, wins[0].w, wins[0].h);
  527. } else if(event.x == K_DOWN) {
  528. wins[0].field = 9;
  529. selfiles = 0;
  530. ui_resizewin(&wins[0], wins[0].w, wins[0].h);
  531. ui_refreshwin(0, 0, 0, wins[0].w, wins[0].h);
  532. }
  533. fileops_peek(selfiles);
  534. selfield = 0;
  535. }
  536. /**
  537. * On button press handler
  538. */
  539. void ctrl_fileops_onbtnpress(int save)
  540. {
  541. int i, j = wins[0].w / 4;
  542. selfield = 0; wins[0].field = -1;
  543. if(event.y >= 29 && event.y <= 49) {
  544. if(event.x >= wins[0].w - 132 && event.x <= wins[0].w - 8) wins[0].field = 8;
  545. else {
  546. for(i = 0; i < pathlen; i++)
  547. if(event.x >= pathX[i] - 3 && event.x <= pathX[i + 1] - 5) {
  548. selfield = 7 + i; break;
  549. }
  550. }
  551. } else
  552. if(event.y > 51 && event.y < 73) {
  553. if(event.x >= 26 && event.x <= wins[0].w - 287) selfield = 1; else
  554. if(event.x >= wins[0].w - 288 && event.x <= wins[0].w - 167) selfield = 2; else
  555. if(event.x >= wins[0].w - 168 && event.x <= wins[0].w - 10) selfield = 3;
  556. } else
  557. if(event.y > 73 && event.y <= wins[0].h - 51) {
  558. wins[0].field = 9;
  559. if(event.x >= wins[0].w - 20 && numfiles) {
  560. i = 72 + (wins[0].h - 144) * scrollfiles / numfiles; j = 20 + (wins[0].h - 144) * pagefiles / numfiles;
  561. if(event.y >= i && event.y < i + j) { selfield = 5; scrolly = event.y - i; }
  562. } else {
  563. if(event.w & 1) {
  564. selfiles = (event.y - 73) / 16 + scrollfiles;
  565. if(selfiles >= numfiles) selfiles = numfiles - 1;
  566. if(selfiles != clkfiles) clkfiles = selfiles;
  567. else ctrl_fileops_onenter(save);
  568. } else
  569. if(event.w & (1 << 3)) {
  570. if(scrollfiles > 0) scrollfiles--;
  571. if(selfiles > scrollfiles + pagefiles - 1) selfiles = scrollfiles + pagefiles - 1;
  572. } else
  573. if(event.w & (1 << 4)) {
  574. if(scrollfiles + pagefiles < numfiles) scrollfiles++;
  575. if(selfiles < scrollfiles) selfiles = scrollfiles;
  576. }
  577. fileops_peek(selfiles);
  578. }
  579. ui_resizewin(&wins[0], wins[0].w, wins[0].h);
  580. } else
  581. if(event.y >= wins[0].h - 44 && event.y <= wins[0].h - 18) {
  582. if(!save) {
  583. if(event.x >= 80 && event.x <= 160) wins[0].field = 10; else
  584. if(event.x >= 164 && event.x <= 240) wins[0].field = 11; else
  585. if(event.x >= 250 && event.x <= 440 && event.x <= wins[0].w - 16 - j) replace ^= 1;
  586. if(wins[0].w - 16 - j > 548) {
  587. if(event.x >= 440 && event.x < 492) {
  588. if(event.w & (1 << 3)) { if(px < 128) px++; } else
  589. if(event.w & (1 << 4)) { if(px > 8) px--; } else
  590. if(event.x >= 480) selfield = 14 + (event.y - (wins[0].h - 44) > 12 ? 1 : 0); else wins[0].field = 14;
  591. } else
  592. if(event.x >= 496 && event.x < 548) {
  593. if(event.w & (1 << 3)) { if(py < 128) py++; } else
  594. if(event.w & (1 << 4)) { if(py > 8) py--; } else
  595. if(event.x >= 536) selfield = 16 + (event.y - (wins[0].h - 44) > 12 ? 1 : 0); else wins[0].field = 15;
  596. }
  597. }
  598. } else {
  599. if(event.x >= 8 && event.x <= 60) { ascii ^= 1; if(ascii) zip = 0; } else
  600. if(event.x >= 64 && event.x <= 124) { zip ^= 1; if(zip) ascii = 0; } else
  601. if(event.x >= 128 && event.x <= wins[0].w - 16 - j) wins[0].field = 12;
  602. }
  603. if(event.x >= wins[0].w - 12 - j && event.x <= wins[0].w - 12) selfield = 4;
  604. }
  605. }
  606. /**
  607. * On click (button release) handler
  608. */
  609. void ctrl_fileops_onclick(int save)
  610. {
  611. int i, j = wins[0].w / 4;
  612. #ifdef __WIN32__
  613. if(selfield == 7 && event.y > 54 && event.y < 54 + numdrives * 16 && event.x >= 10 && event.x < 42) {
  614. strcpy(path[0], &drives[((event.y - 54) / 16) * 4]);
  615. strcpy(fn, path[0]);
  616. pathlen = 1;
  617. fileops_readdir(save);
  618. path[pathlen][strlen(path[pathlen]) - 1] = 0;
  619. } else
  620. #endif
  621. if(event.y >= 29 && event.y <= 49) {
  622. if(event.x < wins[0].w - 132 && selfield >= 7 && event.x >= pathX[selfield-7] - 3 && event.x <= pathX[selfield-6] - 5) {
  623. pathlen = selfield - 6;
  624. for(fn[0] = 0, i = 0; i < pathlen; i++)
  625. strcat(fn, path[i]);
  626. fileops_readdir(save);
  627. path[pathlen][strlen(path[pathlen]) - 1] = 0;
  628. }
  629. } else
  630. if(event.y > 51 && event.y < 73) {
  631. if(event.x >= 26 && event.x <= wins[0].w - 287 && selfield == 1) {
  632. if(ordering == 0) ordering = 1; else ordering = 0;
  633. } else
  634. if(event.x >= wins[0].w - 288 && event.x <= wins[0].w - 167 && selfield == 2) {
  635. if(ordering == 2) ordering = 3; else ordering = 2;
  636. } else
  637. if(event.x >= wins[0].w - 168 && event.x <= wins[0].w - 10 && selfield == 3) {
  638. if(ordering == 4) ordering = 5; else ordering = 4;
  639. }
  640. fileops_readdir(save);
  641. } else
  642. if(event.y >= wins[0].h - 44 && event.y <= wins[0].h - 18) {
  643. if(wins[0].w - 16 - j > 548) {
  644. if(event.x >= 480 && event.x < 492) {
  645. if(selfield == 14) { if(px < 128) px++; } else
  646. if(selfield == 15) { if(px > 8) px--; }
  647. } else
  648. if(event.x >= 536 && event.x < 548) {
  649. if(selfield == 16) { if(py < 128) py++; } else
  650. if(selfield == 17) { if(py > 8) py--; }
  651. }
  652. }
  653. if(event.x >= wins[0].w - 12 - j && event.x <= wins[0].w - 12 && selfield == 4) {
  654. wins[0].field = 13;
  655. ctrl_fileops_onenter(save);
  656. wins[0].field = 9;
  657. }
  658. }
  659. selfield = scrolly = 0;
  660. }
  661. /**
  662. * On mouse move handler
  663. */
  664. void ctrl_fileops_onmove()
  665. {
  666. int i;
  667. if(selfield == 5 && event.y > 73 && event.y < wins[0].h - 72) {
  668. i = scrollfiles;
  669. scrollfiles = (event.y - scrolly - 73) * numfiles / (wins[0].h - 144);
  670. if(scrollfiles > numfiles - pagefiles) scrollfiles = numfiles - pagefiles;
  671. if(scrollfiles < 0) scrollfiles = 0;
  672. if(selfiles < scrollfiles) selfiles = scrollfiles;
  673. if(selfiles > scrollfiles + pagefiles - 1) selfiles = scrollfiles + pagefiles - 1;
  674. if(i != scrollfiles) {
  675. ui_resizewin(&wins[0], wins[0].w, wins[0].h);
  676. ui_refreshwin(0, 0, 0, wins[0].w, wins[0].h);
  677. }
  678. }
  679. if(px && event.y >= wins[0].h - 44 && event.y <= wins[0].h - 18 && event.x >= 440 && event.x < wins[0].w - 16 - wins[0].w / 4)
  680. status = lang[FILEOP_GDIM];
  681. #ifdef __WIN32__
  682. if(selfield == 7 && event.y > 54 && event.y < 54 + numdrives * 16 && event.x >= 10 && event.x < 42) {
  683. i = (event.y - 54) / 16;
  684. if(i != seldrive) {
  685. seldrive = i;
  686. ui_resizewin(&wins[0], wins[0].w, wins[0].h);
  687. ui_refreshwin(0, 0, 0, wins[0].w, wins[0].h);
  688. }
  689. } else
  690. seldrive = -1;
  691. #endif
  692. }
  693. /**
  694. * Save modified font question window
  695. */
  696. void view_dosave()
  697. {
  698. ui_win_t *win = &wins[0];
  699. question_y = (win->h / 2) + 16;
  700. ssfn_dst.bg = 0;
  701. ui_text(win, (win->w - ui_textwidth(lang[FILEOP_DOSAVE])) / 2, question_y - 48, lang[FILEOP_DOSAVE]);
  702. ui_button(win, 20, question_y, (win->w - 80) / 2, lang[FILEOP_YES], selfield == 1, win->field == 7);
  703. ui_button(win, win->w / 2 + 20, question_y, (win->w - 80) / 2, lang[FILEOP_NO], selfield == 2 ? 3 : 2, win->field == 8);
  704. }
  705. /**
  706. * On enter handler
  707. */
  708. void ctrl_dosave_onenter()
  709. {
  710. if(wins[0].field != 8) {
  711. wins[0].tool = MAIN_TOOL_SAVE;
  712. wins[0].field = 12; selfield = -1;
  713. ui_resizewin(&wins[0], wins[0].w, wins[0].h);
  714. ui_refreshwin(0, 0, 0, wins[0].w, wins[0].h);
  715. ctrl_fileops_onenter(1);
  716. }
  717. modified = 0;
  718. }
  719. /**
  720. * On button press handler
  721. */
  722. void ctrl_dosave_onbtnpress()
  723. {
  724. selfield = 0;
  725. if(question_y && event.y >= question_y && event.y < question_y + 20)
  726. selfield = (event.x < wins[0].w / 2) ? 1 : 2;
  727. }
  728. /**
  729. * On click (button release) handler
  730. */
  731. void ctrl_dosave_onclick()
  732. {
  733. int x = wins[0].w / 2;
  734. if(question_y && event.y >= question_y && event.y < question_y + 20) {
  735. if(event.x < x && selfield == 1) { wins[0].field = 7; ctrl_dosave_onenter(); }
  736. if(event.x > x && selfield == 2) { wins[0].field = 8; ctrl_dosave_onenter(); }
  737. }
  738. }
  739. /**
  740. * Start new font question window
  741. */
  742. void view_new()
  743. {
  744. ui_win_t *win = &wins[0];
  745. question_y = (win->h / 2) + 16;
  746. ssfn_dst.bg = 0;
  747. ui_text(win, (win->w - ui_textwidth(lang[FILEOP_NEW])) / 2, question_y - 48, lang[FILEOP_NEW]);
  748. ui_button(win, 20, question_y, (win->w - 80) / 2, lang[FILEOP_YES], selfield == 1 ? 3 : 2, win->field == 7);
  749. ui_button(win, win->w / 2 + 20, question_y, (win->w - 80) / 2, lang[FILEOP_NO], selfield == 2, win->field == 8);
  750. }
  751. /**
  752. * On enter handler
  753. */
  754. void ctrl_new_onenter()
  755. {
  756. int i;
  757. if(wins[0].field != 8) {
  758. for(i=1; i < numwin; i++)
  759. if(wins[i].winid)
  760. ui_closewin(i);
  761. numwin = 1;
  762. sfn_free(); sfn_init(ui_pb);
  763. }
  764. wins[0].tool = MAIN_TOOL_GLYPHS;
  765. wins[0].field = selfield = -1;
  766. }
  767. /**
  768. * On key handler
  769. */
  770. void ctrl_new_onkey()
  771. {
  772. if(event.x == K_BACKSPC) {
  773. wins[0].tool = MAIN_TOOL_GLYPHS;
  774. wins[0].field = selfield = -1;
  775. }
  776. }
  777. /**
  778. * On button press handler
  779. */
  780. void ctrl_new_onbtnpress()
  781. {
  782. selfield = 0;
  783. if(question_y && event.y >= question_y && event.y < question_y + 20)
  784. selfield = (event.x < wins[0].w / 2) ? 1 : 2;
  785. }
  786. /**
  787. * On click (button release) handler
  788. */
  789. void ctrl_new_onclick()
  790. {
  791. int x = wins[0].w / 2;
  792. if(question_y && event.y >= question_y && event.y < question_y + 20) {
  793. if(event.x < x && selfield == 1) { wins[0].field = 7; ctrl_new_onenter(); }
  794. if(event.x > x && selfield == 2) { wins[0].field = 8; ctrl_new_onenter(); }
  795. }
  796. }