ui.c 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927
  1. /*
  2. * sfnedit/ui.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 Common user interface routines
  27. *
  28. */
  29. #include <stdint.h>
  30. #include <stdlib.h>
  31. #include <stdarg.h>
  32. #include <string.h>
  33. #include <stdio.h>
  34. #include "stb_png.h"
  35. #include "lang.h"
  36. #include "ui.h"
  37. #include "icon.h"
  38. #include "util.h"
  39. #define SSFN_CONSOLEBITMAP_TRUECOLOR
  40. #define SSFN_COMMON
  41. #include "libsfn.h"
  42. char verstr[8];
  43. extern char filename[FILENAME_MAX+64];
  44. extern int fontsize;
  45. uint8_t *icon32, *icon64, *tools, *bga;
  46. uint32_t theme[] = { 0xFF454545, 0xFFBEBEBE, 0xFF5C5C5C, 0xFF343434, 0xFF606060, 0xFF303030, 0xFF3C3C3C,
  47. 0xFF101010, 0xFF686868, 0xFF515151, 0xFF484848, 0xFF404040,
  48. 0xFF744C4C, 0xFF5D3535, 0xFF542C2C, 0xFF4C2424,
  49. 0xFF606060, 0xFFF0F0F0, 0xFF909090, 0xFF4E4E4E,
  50. 0xFF101010, 0xFF343434, 0xFFB0B0B0,
  51. 0xFF800000, 0xFF004040, 0xFF005050, 0xFFFF0000, 0xFF007F7F, 0xFF0000B0, 0xFF00B000, 0xFF007F00, 0xFF005050 };
  52. int gw = 36+16+512, gh = 24+8+512, gotevt = 0, quiet = 0, lastpercent = 100, mainloop = 1, modified = 0, posx = 0, posy = 0;
  53. int scrolly = 0;
  54. char ws[0x110000], *status, *errstatus = NULL;
  55. int numwin = 0;
  56. ui_win_t *wins = NULL;
  57. ui_event_t event;
  58. void *winid = NULL;
  59. int winidx = 0;
  60. ssfn_t logofnt;
  61. int cursor = CURSOR_PTR, lastcursor = -1, seltool = -1;
  62. int zip = 1, ascii = 0, selfield = -1;
  63. extern int lastsave, input_maxlen, input_callback, selstart, selend, selfiles, clkfiles, selranges, clkranges, selkern;
  64. extern char *input_str, *input_cur, *input_scr, *input_end, gstat[];
  65. /**
  66. * Read in a theme from a GIMP Palette file
  67. */
  68. void ui_gettheme(char *fn)
  69. {
  70. FILE *f;
  71. char line[256], *s;
  72. int i = 0, r,g,b;
  73. f = fopen(fn, "r");
  74. if(f) {
  75. while(i < THEME_UNDEF && !feof(f)) {
  76. line[0] = 0;
  77. if(!fgets(line, 256, f) || !line[0] || line[0] == '\r' || line[0] == '\n' || line[0] == '#' ||
  78. !memcmp(line, "GIMP", 4) || !memcmp(line, "Name", 4)) continue;
  79. line[255] = 0; s = line;
  80. for(; *s && *s == ' '; s++);
  81. r = atoi(s); for(; *s && *s != ' '; s++);
  82. for(; *s && *s == ' '; s++);
  83. g = atoi(s); for(; *s && *s != ' '; s++);
  84. for(; *s && *s == ' '; s++);
  85. b = atoi(s); for(; *s && *s != ' '; s++);
  86. theme[i] = 0xFF000000 | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF);
  87. }
  88. fclose(f);
  89. }
  90. }
  91. /**
  92. * Compare two strings
  93. */
  94. int ui_casecmp(char *a, char *b, int l)
  95. {
  96. for(;*a && *b && l--;a++,b++) {
  97. if(tolowercase(*a) != tolowercase(*b)) return 1;
  98. }
  99. return 0;
  100. }
  101. /**
  102. * Report an error and exit
  103. */
  104. void ui_error(char *subsystem, int fmt, ...)
  105. {
  106. va_list args;
  107. va_start(args, fmt);
  108. fprintf(stderr, "sfnedit: %s: ", subsystem);
  109. vfprintf(stderr, lang[fmt], args);
  110. fprintf(stderr, "\n");
  111. exit(fmt + 1);
  112. }
  113. /**
  114. * Update window titles
  115. */
  116. void ui_updatetitle(int idx)
  117. {
  118. char title[278], *fn = NULL;
  119. int i;
  120. if(ctx.filename)
  121. fn = strrchr(ctx.filename, DIRSEP);
  122. if(!fn) fn = ctx.filename; else fn++;
  123. if(!idx) {
  124. memcpy(title, "sfnedit", 8);
  125. if(fn) { memcpy(title + 7, " - ", 3); strncpy(title + 10, fn, 256); }
  126. ui_titlewin(&wins[0], title);
  127. }
  128. for(i = idx ? idx : 1; i < (idx ? idx + 1 : numwin); i++) {
  129. sprintf(title, "sfnedit - U+%06X - ", wins[i].unicode);
  130. if(fn) strncpy(title + 21, fn, 256);
  131. ui_titlewin(&wins[i], title);
  132. }
  133. }
  134. /**
  135. * Get number of input fields on a window
  136. */
  137. int ui_maxfield(int idx)
  138. {
  139. if(!idx) {
  140. switch(wins[idx].tool) {
  141. case MAIN_TOOL_ABOUT: return 7;
  142. case MAIN_TOOL_LOAD: return 13;
  143. case MAIN_TOOL_SAVE: return 13;
  144. case MAIN_TOOL_PROPS: return 17;
  145. case MAIN_TOOL_RANGES: return 9;
  146. case MAIN_TOOL_GLYPHS: return 16;
  147. case MAIN_TOOL_TEST: return 9;
  148. case MAIN_TOOL_DOSAVE: return 8;
  149. case MAIN_TOOL_NEW: return 8;
  150. }
  151. } else {
  152. switch(wins[idx].tool) {
  153. case GLYPH_TOOL_COORD: return 23;
  154. case GLYPH_TOOL_LAYER: return 15;
  155. case GLYPH_TOOL_KERN: return 8;
  156. case GLYPH_TOOL_COLOR: return 7;
  157. }
  158. }
  159. return 3;
  160. }
  161. /**
  162. * Return text width in pixels
  163. */
  164. int ui_textwidth(char *str)
  165. {
  166. int ret = 0, u;
  167. if(!str || !*str) return 0;
  168. while((u = ssfn_utf8(&str)))
  169. ret += (int)ws[u];
  170. return ret;
  171. }
  172. /**
  173. * Open a window
  174. */
  175. void ui_openwin(uint32_t unicode)
  176. {
  177. int w = gw, h = gh, i, j = 0;
  178. for(i=0; i < numwin; i++) {
  179. if(wins[i].winid) {
  180. if(wins[i].unicode == unicode) { ui_focuswin(&wins[i]); return; }
  181. } else {
  182. if(!j) j = i;
  183. }
  184. }
  185. if(!j) {
  186. if(!numwin) {
  187. w = MAIN_W; h = MAIN_H; unicode = WINTYPE_MAIN;
  188. } else {
  189. w = gw; h = gh;
  190. }
  191. j = numwin++;
  192. wins = (ui_win_t*)realloc(wins, numwin*sizeof(ui_win_t));
  193. if(!wins) ui_error("openwin", ERR_MEM);
  194. }
  195. memset(&wins[j], 0, sizeof(ui_win_t));
  196. wins[j].unicode = unicode;
  197. wins[j].uniname = uninames[uniname(unicode)].name;
  198. wins[j].winid = ui_createwin(w, h);
  199. wins[j].field = -1;
  200. wins[j].rc = 1;
  201. if(unicode == WINTYPE_MAIN) { wins[j].zoom = 4; wins[j].tool = -1; }
  202. else if(!ctx.glyphs[unicode].numlayer) {
  203. ctx.glyphs[unicode].width = ctx.width;
  204. ctx.glyphs[unicode].height = ctx.height;
  205. if(!iswhitespace(unicode))
  206. ctx.glyphs[unicode].adv_x = ctx.glyphs[unicode].adv_y = ctx.glyphs[unicode].ovl_x = 0;
  207. }
  208. input_maxlen = 0;
  209. input_str = input_cur = NULL;
  210. ui_updatetitle(j);
  211. ui_resizewin(&wins[j], w, h);
  212. ui_refreshwin(j, 0, 0, w, h);
  213. ui_focuswin(&wins[j]);
  214. }
  215. /**
  216. * Close a window
  217. */
  218. void ui_closewin(int idx)
  219. {
  220. int i;
  221. ui_cursorwin(&wins[idx], CURSOR_PTR);
  222. ui_cursorwin(&wins[0], CURSOR_PTR);
  223. ui_refreshwin(0, 0, 0, wins[0].w, wins[0].h);
  224. if(idx < 0 || idx >= numwin || !wins[idx].winid) return;
  225. hist_free(&wins[idx]);
  226. if(!idx) {
  227. for(i=1; i < numwin; i++)
  228. if(wins[i].winid)
  229. ui_closewin(i);
  230. numwin = 1;
  231. if(modified && wins[0].tool != MAIN_TOOL_DOSAVE) {
  232. wins[0].tool = MAIN_TOOL_DOSAVE;
  233. ui_resizewin(&wins[0], wins[0].w, wins[0].h);
  234. ui_refreshwin(0, 0, 0, wins[0].w, wins[0].h);
  235. } else
  236. mainloop = 0;
  237. return;
  238. }
  239. ui_destroywin(&wins[idx]);
  240. wins[idx].winid = NULL;
  241. wins[idx].surface = NULL;
  242. wins[idx].data = NULL;
  243. wins[idx].unicode = -1;
  244. wins[idx].histmin = wins[idx].histmax = 0;
  245. while(numwin && !wins[numwin-1].winid) numwin--;
  246. }
  247. /**
  248. * Get common window id (idx) for driver specific window id
  249. */
  250. int ui_getwin(void *wid)
  251. {
  252. int i;
  253. if(wid == winid) return winidx;
  254. for(i=0; i < numwin; i++)
  255. if(wins[i].winid == wid) {
  256. winid = wid;
  257. winidx = i;
  258. return i;
  259. }
  260. return -1;
  261. }
  262. /**
  263. * Load resources
  264. */
  265. static void ui_loadrc()
  266. {
  267. unsigned int w, h, f;
  268. uint8_t c, *ptr;
  269. stbi__context s;
  270. stbi__result_info ri;
  271. sprintf(verstr, "v%d.%d.%d", SSFN_VERSION >> 8, (SSFN_VERSION >> 4) & 0xF, SSFN_VERSION & 0xF);
  272. /* load icons */
  273. s.read_from_callbacks = 0;
  274. s.img_buffer = s.img_buffer_original = icon32_png;
  275. s.img_buffer_end = s.img_buffer_original_end = icon32_png + sizeof(icon32_png);
  276. ri.bits_per_channel = 8;
  277. icon32 = (uint8_t*)stbi__png_load(&s, (int*)&w, (int*)&h, (int*)&f, 0, &ri);
  278. if(!icon32) ui_error("ui_loadrc", ERR_MEM);
  279. s.img_buffer = s.img_buffer_original = icon64_png;
  280. s.img_buffer_end = s.img_buffer_original_end = icon64_png + sizeof(icon64_png);
  281. ri.bits_per_channel = 8;
  282. icon64 = (uint8_t*)stbi__png_load(&s, (int*)&w, (int*)&h, (int*)&f, 0, &ri);
  283. if(!icon64) ui_error("ui_loadrc", ERR_MEM);
  284. s.img_buffer = s.img_buffer_original = icons_png;
  285. s.img_buffer_end = s.img_buffer_original_end = icons_png + sizeof(icons_png);
  286. ri.bits_per_channel = 8;
  287. tools = (uint8_t*)stbi__png_load(&s, (int*)&w, (int*)&h, (int*)&f, 0, &ri);
  288. if(!tools) ui_error("ui_loadrc", ERR_MEM);
  289. s.img_buffer = s.img_buffer_original = bga_png;
  290. s.img_buffer_end = s.img_buffer_original_end = bga_png + sizeof(bga_png);
  291. ri.bits_per_channel = 8;
  292. bga = (uint8_t*)stbi__png_load(&s, (int*)&w, (int*)&h, (int*)&f, 0, &ri);
  293. /* not an issue if we can't load this */
  294. /* uncompress ui font */
  295. ptr = unifont_gz + 3;
  296. c = *ptr++; ptr += 6;
  297. if(c & 4) { w = *ptr++; w += (*ptr++ << 8); ptr += w; }
  298. if(c & 8) { while(*ptr++ != 0); }
  299. if(c & 16) { while(*ptr++ != 0); }
  300. f = sizeof(unifont_gz) - (size_t)(ptr - unifont_gz);
  301. w = 0;
  302. ssfn_src = (ssfn_font_t*)stbi_zlib_decode_malloc_guesssize_headerflag((const char*)ptr, f, 4096, (int*)&w, 0);
  303. if(!ssfn_src) ui_error("ui_loadrc", ERR_MEM);
  304. memset(&ws, 0, sizeof(ws));
  305. ptr = (uint8_t*)ssfn_src + ssfn_src->characters_offs;
  306. for(ptr = (uint8_t*)ssfn_src + ssfn_src->characters_offs, w = 0; w < 0x110000; w++) {
  307. if(ptr[0] == 0xFF) { w += 65535; ptr++; }
  308. else if((ptr[0] & 0xC0) == 0xC0) { h = (((ptr[0] & 0x3F) << 8) | ptr[1]); w += h; ptr += 2; }
  309. else if((ptr[0] & 0xC0) == 0x80) { h = (ptr[0] & 0x3F); w += h; ptr++; }
  310. else {
  311. ws[w] = ptr[2];
  312. ptr += 6 + ptr[1] * (ptr[0] & 0x40 ? 6 : 5);
  313. }
  314. }
  315. memset(&logofnt, 0, sizeof(ssfn_t));
  316. ssfn_load(&logofnt, &logo_sfn);
  317. ssfn_select(&logofnt, SSFN_FAMILY_SERIF, NULL, SSFN_STYLE_REGULAR, 56);
  318. }
  319. /**
  320. * Quit handler (via Ctrl-C signal or normally)
  321. */
  322. void ui_quit(int sig __attribute__((unused)))
  323. {
  324. copypaste_fini();
  325. ui_fini();
  326. ssfn_free(&logofnt);
  327. sfn_free();
  328. uniname_free();
  329. free(icon64);
  330. free(icon32);
  331. free(tools);
  332. free(bga);
  333. free(ssfn_src);
  334. if(sig) exit(1);
  335. }
  336. /**
  337. * Progress bar hook
  338. */
  339. void ui_pb(int step, int numstep, int curr, int total, int msg)
  340. {
  341. int i, n;
  342. char s[64];
  343. n = (long int)(curr + 1) * 100L / (long int)(total + 1);
  344. if(n == lastpercent) return;
  345. lastpercent = n;
  346. ui_box(&wins[0], 0, wins[0].h - 18, wins[0].w, 18, theme[THEME_DARK], theme[THEME_DARK], theme[THEME_DARK]);
  347. i = 18;
  348. if(numstep > 0) {
  349. ui_box(&wins[0], 0, wins[0].h - 18, wins[0].w * step / numstep, 6, theme[THEME_LIGHTER], theme[THEME_LIGHT],
  350. theme[THEME_DARKER]);
  351. i = 12;
  352. sprintf(s, "[ %d / %d ] %3d%%", step, numstep, n);
  353. } else
  354. sprintf(s, "%3d%%", n);
  355. ui_box(&wins[0], 0, wins[0].h - i, (int)(long)wins[0].w * (long)(curr + 1) / (long)(total + 1), i,
  356. theme[THEME_LIGHTER], theme[THEME_LIGHT], theme[THEME_LIGHTER]);
  357. ssfn_dst.fg = theme[THEME_FG];
  358. ssfn_dst.bg = 0;
  359. ui_text(&wins[0], 0, wins[0].h - 18, s);
  360. ui_text(&wins[0], ssfn_dst.x + 8, wins[0].h - 18, !msg ? "" : lang[msg - 1 + STAT_MEASURE]);
  361. ui_flushwin(&wins[0], 0, wins[0].h - 18, wins[0].w, 18);
  362. }
  363. /**
  364. * Get character info to status bar
  365. */
  366. void ui_chrinfo(int unicode)
  367. {
  368. char *u, *s;
  369. u = utf8(unicode);
  370. if(!u[1]) u[2] = u[3] = 0; else if(!u[2]) u[3] = 0;
  371. if(unicode >= SSFN_LIG_FIRST && unicode <= SSFN_LIG_LAST) {
  372. sprintf(gstat, "U+%06X %02x %02x %02x %02x %d %s %s #%d", unicode,
  373. (unsigned char)u[0], (unsigned char)u[1], (unsigned char)u[2], (unsigned char)u[3], unicode,
  374. ctx.ligatures[unicode - SSFN_LIG_FIRST] ? ctx.ligatures[unicode - SSFN_LIG_FIRST] : "",
  375. lang[GLYPHS_LIGATURE], unicode - SSFN_LIG_FIRST);
  376. } else {
  377. s = uninames[uniname(unicode)].name;
  378. sprintf(gstat, "U+%06X %02x %02x %02x %02x %d %s %s", unicode,
  379. (unsigned char)u[0], (unsigned char)u[1], (unsigned char)u[2], (unsigned char)u[3], unicode,
  380. u, s && *s ? s : lang[GLYPHS_UNDEF]);
  381. }
  382. status = gstat;
  383. }
  384. /**
  385. * Redraw part of a window
  386. */
  387. void ui_refreshwin(int idx, int wx, int wy, int ww, int wh)
  388. {
  389. ui_win_t *win = &wins[idx];
  390. int i, j, k, m, p, q;
  391. uint32_t lc = theme[THEME_BG] + 0x060606;
  392. uint8_t *b = (uint8_t*)&theme[THEME_BG], *c = (uint8_t*)&lc;
  393. char st[32];
  394. if(idx < 0 || idx >= numwin || win->w < 8 || win->h < 8 || wx+ww < 32 || wy+wh < 32) return;
  395. ssfn_dst.w = win->w;
  396. ssfn_dst.h = win->h;
  397. ssfn_dst.fg = theme[THEME_FG];
  398. ssfn_dst.bg = 0;
  399. input_maxlen = 0;
  400. if(bga && (win->help || (!idx && wins[idx].tool != MAIN_TOOL_GLYPHS))) {
  401. p = win->w > 256 ? 256 : win->w;
  402. q = win->h > 256 ? 256 : win->h;
  403. for(k = (win->h-q+1)*win->p - p, m = ((256 - q) << 8) + (256 - p), j = 0; j < q; j++, k += win->p, m += 256)
  404. for(i = 0; i < p; i++)
  405. if(bga[m+i]) {
  406. ((uint8_t*)&win->data[k+i])[0] = (c[0]*bga[m+i] + (256 - bga[m+i])*b[0])>>8;
  407. ((uint8_t*)&win->data[k+i])[1] = (c[1]*bga[m+i] + (256 - bga[m+i])*b[1])>>8;
  408. ((uint8_t*)&win->data[k+i])[2] = (c[2]*bga[m+i] + (256 - bga[m+i])*b[2])>>8;
  409. }
  410. }
  411. if(win->help) {
  412. view_help(idx);
  413. } else {
  414. ui_toolbox(idx);
  415. ssfn_dst.bg = theme[THEME_BG];
  416. if(!idx) {
  417. switch(wins[idx].tool) {
  418. case -1:
  419. case MAIN_TOOL_ABOUT: view_about(); break;
  420. case MAIN_TOOL_LOAD: view_fileops(0); break;
  421. case MAIN_TOOL_SAVE: view_fileops(1); break;
  422. case MAIN_TOOL_PROPS: view_props(); break;
  423. case MAIN_TOOL_RANGES: view_ranges(); break;
  424. case MAIN_TOOL_GLYPHS: view_glyphs(); break;
  425. case MAIN_TOOL_TEST: view_test(); break;
  426. case MAIN_TOOL_DOSAVE: view_dosave(); break;
  427. case MAIN_TOOL_NEW: view_new(); break;
  428. }
  429. } else {
  430. switch(wins[idx].tool) {
  431. case GLYPH_TOOL_COORD: view_coords(idx); break;
  432. case GLYPH_TOOL_LAYER: view_layers(idx); break;
  433. case GLYPH_TOOL_KERN: view_kern(idx); break;
  434. case GLYPH_TOOL_COLOR: view_color(idx); break;
  435. }
  436. }
  437. ssfn_dst.w = win->w;
  438. ssfn_dst.h = win->h;
  439. ssfn_dst.fg = theme[THEME_FG];
  440. ssfn_dst.bg = 0;
  441. if(errstatus) {
  442. ui_box(&wins[event.win], 0, wins[event.win].h - 18, wins[event.win].w, 18,
  443. theme[THEME_BTN1D], theme[THEME_BTN1D], theme[THEME_BTN1D]);
  444. ssfn_dst.bg = theme[THEME_BTN1D];
  445. ui_text(&wins[event.win], 0, wins[event.win].h - 18, errstatus);
  446. } else {
  447. ui_box(win, 0, win->h - 18, win->w, 18, theme[THEME_DARK], theme[THEME_DARK], theme[THEME_DARK]);
  448. if(event.win && posx != -1 && posy != -1) {
  449. sprintf(st, "X: %3d Y: %3d", posx, posy);
  450. ui_text(win, 0, win->h - 18, st);
  451. } else
  452. if(!event.win && wins[0].tool == MAIN_TOOL_TEST) {
  453. sprintf(st, "%d", fontsize);
  454. ui_text(win, 0, win->h - 18, st);
  455. }
  456. }
  457. }
  458. ui_flushwin(win, wx, wy, ww, wh);
  459. }
  460. /**
  461. * Refresh all windows
  462. */
  463. void ui_refreshall()
  464. {
  465. int i;
  466. for(i=0; i < numwin; i++)
  467. if(wins[i].winid) {
  468. if(wins[i].unicode < 0x110000 && !ctx.glyphs[wins[i].unicode].numlayer &&
  469. !ctx.glyphs[wins[i].unicode].adv_x && !ctx.glyphs[wins[i].unicode].adv_y)
  470. ui_closewin(i);
  471. else {
  472. hist_free(&wins[i]);
  473. if(i) { wins[i].zoom = 0; wins[i].rc = 1; }
  474. ui_resizewin(&wins[i], wins[i].w, wins[i].h);
  475. ui_refreshwin(i, 0, 0, wins[i].w, wins[i].h);
  476. }
  477. }
  478. }
  479. /**
  480. * Finish user input
  481. */
  482. void ui_inputfinish()
  483. {
  484. char *s = input_str;
  485. if(input_maxlen && input_str && input_callback)
  486. switch(input_callback) {
  487. case 1:
  488. if((input_str[0] == 'U' || input_str[0] == 'u') && input_str[1] == '+') rs = (int)gethex(input_str + 2, 6);
  489. else rs = (int)ssfn_utf8(&s);
  490. if(rs < 0) rs = 0;
  491. if(rs > 0x10FFFF) rs = 0x10FFFF;
  492. sprintf(input_str, "U+%X", rs);
  493. break;
  494. case 2:
  495. if((input_str[0] == 'U' || input_str[0] == 'u') && input_str[1] == '+') re = (int)gethex(input_str + 2, 6);
  496. else re = (int)ssfn_utf8(&s);
  497. if(re < 0) re = 0;
  498. if(re > 0x10FFFF) re = 0x10FFFF;
  499. sprintf(input_str, "U+%X", re);
  500. break;
  501. case 3: sfn_setstr(&ctx.name, input_str, 0); break;
  502. case 4: sfn_setstr(&ctx.familyname, input_str, 0); break;
  503. case 5: sfn_setstr(&ctx.subname, input_str, 0); break;
  504. case 6: sfn_setstr(&ctx.revision, input_str, 0); break;
  505. case 7: sfn_setstr(&ctx.manufacturer, input_str, 0); break;
  506. case 8: sfn_setstr(&ctx.license, input_str, 0); break;
  507. default:
  508. if(input_callback >= 1024 && input_callback <= 1024 + SSFN_LIG_LAST - SSFN_LIG_FIRST)
  509. sfn_setstr(&ctx.ligatures[input_callback - 1024], input_str, 0);
  510. break;
  511. }
  512. input_str = input_cur = NULL; input_maxlen = 0;
  513. }
  514. /**
  515. * Main user interface event handler
  516. */
  517. void ui_main(char *fn)
  518. {
  519. int i, j;
  520. char *s, st[32];
  521. /* load resources */
  522. ui_loadrc();
  523. /* driver specific init */
  524. ui_init();
  525. /* if specified a file on command line, load it */
  526. ui_openwin(WINTYPE_MAIN);
  527. ui_cursorwin(&wins[0], CURSOR_LOADING);
  528. sfn_init(ui_pb);
  529. if(fn && sfn_load(fn, 0)) {
  530. s = strrchr(fn, DIRSEP);
  531. if(s) s++; else s = fn;
  532. strcpy(filename, s);
  533. sfn_sanitize(-1);
  534. wins[0].tool = MAIN_TOOL_GLYPHS;
  535. ui_updatetitle(0);
  536. ui_resizewin(&wins[0], wins[0].w, wins[0].h);
  537. ui_refreshwin(0, 0, 0, wins[0].w, wins[0].h);
  538. } else
  539. wins[0].tool = MAIN_TOOL_ABOUT;
  540. ui_cursorwin(&wins[0], CURSOR_PTR);
  541. ui_refreshwin(0, 0, 0, wins[0].w, wins[0].h);
  542. /* main event loop */
  543. while(mainloop) {
  544. ui_getevent();
  545. if(event.win < 0) continue;
  546. if(event.type != E_REFRESH && event.type != E_MOUSEMOVE && event.type != E_BTNRELEASE) errstatus = NULL;
  547. if(wins[event.win].help) {
  548. switch(event.type) {
  549. case E_CLOSE: ui_closewin(event.win); break;
  550. case E_RESIZE:
  551. ui_resizewin(&wins[event.win], event.w, event.h);
  552. ui_refreshwin(event.win, 0, 0, event.w, event.h);
  553. if(event.win) { gw = event.w; gh = event.h; }
  554. break;
  555. case E_REFRESH: ui_refreshwin(event.win, event.x, event.y, event.w, event.h); break;
  556. case E_BTNRELEASE: case E_KEY:
  557. wins[event.win].help = 0;
  558. ui_resizewin(&wins[event.win], wins[event.win].w, wins[event.win].h);
  559. ui_refreshwin(event.win, 0, 0, wins[event.win].w, wins[event.win].h);
  560. break;
  561. }
  562. continue;
  563. }
  564. if(event.type == E_KEY && (event.h & (3 << 1)) && (event.x == 's' || event.x == 'S')) {
  565. if(modified || 1) {
  566. wins[0].field = -1;
  567. view_fileops(1);
  568. if((event.h & 1) || !filename[0]) {
  569. wins[0].field = 12;
  570. wins[0].tool = MAIN_TOOL_SAVE;
  571. ui_resizewin(&wins[0], wins[0].w, wins[0].h);
  572. ui_refreshwin(0, 0, 0, wins[0].w, wins[0].h);
  573. ui_focuswin(&wins[0]);
  574. ctrl_dosave_onenter();
  575. wins[0].field = -1;
  576. } else {
  577. wins[0].field = 13;
  578. ctrl_fileops_onenter(1);
  579. wins[0].field = -1;
  580. if(wins[0].tool == MAIN_TOOL_SAVE) ui_refreshwin(0, 0, 0, wins[0].w, wins[0].h);
  581. if(errstatus) {
  582. ui_box(&wins[event.win], 0, wins[event.win].h - 18, wins[event.win].w, 18,
  583. theme[THEME_BTN1D], theme[THEME_BTN1D], theme[THEME_BTN1D]);
  584. ssfn_dst.bg = theme[THEME_BTN1D];
  585. ui_text(&wins[event.win], 0, wins[event.win].h - 18, errstatus);
  586. ui_flushwin(&wins[event.win], 0, wins[event.win].h - 18, wins[event.win].w, 18);
  587. }
  588. modified = 0;
  589. }
  590. }
  591. continue;
  592. }
  593. switch(event.type) {
  594. case E_CLOSE: ui_closewin(event.win); break;
  595. case E_RESIZE:
  596. ui_resizewin(&wins[event.win], event.w, event.h);
  597. ui_refreshwin(event.win, 0, 0, event.w, event.h);
  598. if(event.win) { gw = event.w; gh = event.h; }
  599. break;
  600. case E_REFRESH: ui_refreshwin(event.win, event.x, event.y, event.w, event.h); break;
  601. case E_MOUSEMOVE:
  602. status = NULL; cursor = CURSOR_PTR;
  603. if(event.win && wins[event.win].tool == GLYPH_TOOL_COORD && event.y < 24 && event.x >= 168)
  604. cursor = CURSOR_GRAB;
  605. if(event.y < 23 && event.x < 168) {
  606. i = (event.x - 1) / 24;
  607. if(i < (!event.win ? 7 : 3))
  608. status = lang[(!event.win ? MTOOL_ABOUT : GTOOL_MEASURES) + i];
  609. if(event.win && event.x >= 6 + 3 * 24)
  610. ui_chrinfo(wins[event.win].unicode);
  611. } else {
  612. if(!event.win)
  613. switch(wins[0].tool) {
  614. case MAIN_TOOL_ABOUT: ctrl_about_onmove(); break;
  615. case MAIN_TOOL_LOAD: case MAIN_TOOL_SAVE : ctrl_fileops_onmove(); break;
  616. case MAIN_TOOL_RANGES: ctrl_ranges_onmove(); break;
  617. case MAIN_TOOL_GLYPHS: ctrl_glyphs_onmove(); break;
  618. }
  619. else
  620. switch(wins[event.win].tool) {
  621. case GLYPH_TOOL_COORD: ctrl_coords_onmove(event.win); break;
  622. case GLYPH_TOOL_LAYER: ctrl_layers_onmove(event.win); break;
  623. case GLYPH_TOOL_KERN: ctrl_kern_onmove(event.win); break;
  624. case GLYPH_TOOL_COLOR: ctrl_colors_onmove(event.win); break;
  625. }
  626. }
  627. ssfn_dst.w = wins[event.win].w;
  628. ssfn_dst.h = wins[event.win].h;
  629. ssfn_dst.fg = theme[THEME_FG];
  630. if(errstatus) {
  631. ui_box(&wins[event.win], 0, wins[event.win].h - 18, wins[event.win].w, 18,
  632. theme[THEME_BTN1D], theme[THEME_BTN1D], theme[THEME_BTN1D]);
  633. ssfn_dst.bg = theme[THEME_BTN1D];
  634. ui_text(&wins[event.win], 0, wins[event.win].h - 18, errstatus);
  635. } else {
  636. ui_box(&wins[event.win], 0, wins[event.win].h - 18, wins[event.win].w, 18,
  637. theme[THEME_DARK], theme[THEME_DARK], theme[THEME_DARK]);
  638. ssfn_dst.bg = theme[THEME_DARK];
  639. if(status) {
  640. ui_text(&wins[event.win], 0, wins[event.win].h - 18, status);
  641. posx = posy = -1;
  642. } else if(event.win && posx != -1 && posy != -1) {
  643. sprintf(st, "X: %3d Y: %3d", posx, posy);
  644. ui_text(&wins[event.win], 0, wins[event.win].h - 18, st);
  645. }
  646. }
  647. if(cursor != lastcursor) {
  648. ui_cursorwin(&wins[event.win], cursor);
  649. lastcursor = cursor;
  650. }
  651. ui_flushwin(&wins[event.win], 0, wins[event.win].h - 18, wins[event.win].w, 18);
  652. break;
  653. case E_BTNPRESS:
  654. ui_inputfinish();
  655. if(event.win && wins[event.win].tool == GLYPH_TOOL_COORD && event.y < 24 && event.x >= 168 &&
  656. wins[event.win].unicode > 0) {
  657. s = utf8(wins[event.win].unicode);
  658. if(s && *s) ui_copy(s);
  659. ssfn_dst.w = wins[event.win].w;
  660. ssfn_dst.h = wins[event.win].h;
  661. ssfn_dst.fg = theme[THEME_FG];
  662. ssfn_dst.bg = theme[THEME_DARK];
  663. ui_box(&wins[event.win], 0, wins[event.win].h - 18, wins[event.win].w, 18,
  664. theme[THEME_DARK], theme[THEME_DARK], theme[THEME_DARK]);
  665. ui_text(&wins[event.win], 0, wins[event.win].h - 18, lang[COORDS_CLIPBRD]);
  666. ui_flushwin(&wins[event.win], 0, wins[event.win].h - 18, wins[event.win].w, 18);
  667. } else
  668. if(event.y < 23 && event.x < 168) {
  669. selstart = selend = selfiles = clkfiles = selranges = clkranges = -1;
  670. i = (event.x - 1) / 24;
  671. if(i < (!event.win ? 7 : 3)) {
  672. seltool = i;
  673. ui_refreshwin(event.win, 0, 0, wins[event.win].w, wins[event.win].h);
  674. }
  675. if(wins[event.win].unicode >= SSFN_LIG_FIRST && wins[event.win].unicode <= SSFN_LIG_LAST &&
  676. event.x >= 6 + 3 * 24 && event.x <= 6 + 3 * 24 + 54) {
  677. wins[event.win].field = 3;
  678. }
  679. } else {
  680. if(!event.win)
  681. switch(wins[0].tool) {
  682. case MAIN_TOOL_LOAD: ctrl_fileops_onbtnpress(0); break;
  683. case MAIN_TOOL_SAVE: ctrl_fileops_onbtnpress(1); break;
  684. case MAIN_TOOL_PROPS: ctrl_props_onbtnpress(); break;
  685. case MAIN_TOOL_RANGES: ctrl_ranges_onbtnpress(); break;
  686. case MAIN_TOOL_GLYPHS: ctrl_glyphs_onbtnpress(); break;
  687. case MAIN_TOOL_TEST: ctrl_test_onbtnpress(); break;
  688. case MAIN_TOOL_DOSAVE: ctrl_dosave_onbtnpress(); break;
  689. case MAIN_TOOL_NEW: ctrl_new_onbtnpress(); break;
  690. }
  691. else
  692. switch(wins[event.win].tool) {
  693. case GLYPH_TOOL_COORD: ctrl_coords_onbtnpress(event.win); break;
  694. case GLYPH_TOOL_LAYER: ctrl_layers_onbtnpress(event.win); break;
  695. case GLYPH_TOOL_KERN: ctrl_kern_onbtnpress(event.win); break;
  696. case GLYPH_TOOL_COLOR: ctrl_colors_onbtnpress(event.win); break;
  697. }
  698. ui_refreshwin(event.win, 0, 0, wins[event.win].w, wins[event.win].h);
  699. }
  700. break;
  701. case E_BTNRELEASE:
  702. if(event.y < 23 && event.x < 168) {
  703. i = (event.x - 1) / 24;
  704. if(i < (!event.win ? 7 : 3) && i == seltool) {
  705. wins[event.win].tool = i;
  706. wins[event.win].field = selfield = selkern = -1;
  707. fontsize = ctx.height > 32 ? 32 : ctx.height;
  708. ui_resizewin(&wins[event.win], wins[event.win].w, wins[event.win].h);
  709. }
  710. } else {
  711. if(!event.win)
  712. switch(wins[0].tool) {
  713. case MAIN_TOOL_ABOUT: ctrl_about_onclick(); break;
  714. case MAIN_TOOL_LOAD: ctrl_fileops_onclick(0); break;
  715. case MAIN_TOOL_SAVE: ctrl_fileops_onclick(1); break;
  716. case MAIN_TOOL_PROPS: ctrl_props_onclick(); break;
  717. case MAIN_TOOL_GLYPHS: ctrl_glyphs_onclick(); break;
  718. case MAIN_TOOL_TEST: ctrl_test_onclick(); break;
  719. case MAIN_TOOL_DOSAVE: ctrl_dosave_onclick(); break;
  720. case MAIN_TOOL_NEW: ctrl_new_onclick(); break;
  721. }
  722. else
  723. switch(wins[event.win].tool) {
  724. case GLYPH_TOOL_COORD: ctrl_coords_onclick(event.win); break;
  725. case GLYPH_TOOL_LAYER: ctrl_layers_onclick(event.win); break;
  726. case GLYPH_TOOL_KERN: ctrl_kern_onclick(event.win); break;
  727. case GLYPH_TOOL_COLOR: ctrl_colors_onclick(event.win); break;
  728. }
  729. }
  730. if(cursor != lastcursor) {
  731. ui_cursorwin(&wins[event.win], cursor);
  732. lastcursor = cursor;
  733. }
  734. seltool = selfield = -1;
  735. ui_resizewin(&wins[event.win], wins[event.win].w, wins[event.win].h);
  736. ui_refreshwin(event.win, 0, 0, wins[event.win].w, wins[event.win].h);
  737. break;
  738. case E_KEY:
  739. seltool = -1;
  740. switch(event.x) {
  741. case K_ESC: ui_closewin(event.win); break;
  742. case K_F1:
  743. if(event.win || wins[0].tool <= MAIN_TOOL_GLYPHS) {
  744. wins[event.win].help = 1;
  745. ui_resizewin(&wins[event.win], wins[event.win].w, wins[event.win].h);
  746. ui_refreshwin(event.win, 0, 0, wins[event.win].w, wins[event.win].h);
  747. }
  748. break;
  749. case K_TAB:
  750. ui_inputfinish();
  751. if(event.h & 1) {
  752. if(wins[event.win].field == -1)
  753. wins[event.win].field = ui_maxfield(event.win);
  754. else {
  755. wins[event.win].field--;
  756. if(event.win && wins[event.win].field == 3 && (wins[event.win].unicode < SSFN_LIG_FIRST ||
  757. wins[event.win].unicode > SSFN_LIG_LAST)) wins[event.win].field--;
  758. }
  759. } else {
  760. if(wins[event.win].field == ui_maxfield(event.win))
  761. wins[event.win].field = -1;
  762. else {
  763. wins[event.win].field++;
  764. if(event.win && wins[event.win].field == 3 && (wins[event.win].unicode < SSFN_LIG_FIRST ||
  765. wins[event.win].unicode > SSFN_LIG_LAST)) wins[event.win].field++;
  766. }
  767. }
  768. ui_resizewin(&wins[event.win], wins[event.win].w, wins[event.win].h);
  769. ui_refreshwin(event.win, 0, 0, wins[event.win].w, wins[event.win].h);
  770. break;
  771. case K_ENTER:
  772. if(input_maxlen) {
  773. ui_inputfinish();
  774. if(wins[event.win].field == ui_maxfield(event.win))
  775. wins[event.win].field = -1;
  776. else
  777. wins[event.win].field++;
  778. ui_refreshwin(event.win, 0, 0, wins[event.win].w, wins[event.win].h);
  779. } else {
  780. if(wins[event.win].field > -1 && wins[event.win].field < (!event.win ? 7 : 3)) {
  781. wins[event.win].tool = wins[event.win].field;
  782. wins[event.win].field = seltool = selfield = selkern = -1;
  783. fontsize = ctx.height > 32 ? 32 : ctx.height;
  784. ui_resizewin(&wins[event.win], wins[event.win].w, wins[event.win].h);
  785. ui_refreshwin(event.win, 0, 0, wins[event.win].w, wins[event.win].h);
  786. } else {
  787. if(!event.win)
  788. switch(wins[0].tool) {
  789. case MAIN_TOOL_ABOUT: ctrl_about_onenter(); break;
  790. case MAIN_TOOL_LOAD: ctrl_fileops_onenter(0); break;
  791. case MAIN_TOOL_SAVE: ctrl_fileops_onenter(1); break;
  792. case MAIN_TOOL_PROPS: ctrl_props_onenter(); break;
  793. case MAIN_TOOL_RANGES: ctrl_ranges_onenter(); break;
  794. case MAIN_TOOL_GLYPHS: ctrl_glyphs_onenter(); break;
  795. case MAIN_TOOL_TEST: ctrl_test_onenter(); break;
  796. case MAIN_TOOL_DOSAVE: ctrl_dosave_onenter(); break;
  797. case MAIN_TOOL_NEW: ctrl_new_onenter(); break;
  798. break;
  799. }
  800. else
  801. switch(wins[event.win].tool) {
  802. case GLYPH_TOOL_COORD: ctrl_coords_onenter(event.win); break;
  803. case GLYPH_TOOL_LAYER: ctrl_layers_onenter(event.win); break;
  804. case GLYPH_TOOL_KERN: ctrl_kern_onenter(event.win); break;
  805. case GLYPH_TOOL_COLOR: ctrl_colors_onenter(event.win); break;
  806. }
  807. ui_resizewin(&wins[event.win], wins[event.win].w, wins[event.win].h);
  808. ui_refreshwin(event.win, 0, 0, wins[event.win].w, wins[event.win].h);
  809. }
  810. }
  811. break;
  812. default:
  813. if(input_maxlen) {
  814. switch(event.x) {
  815. case K_UP: case K_HOME: input_cur = input_str; break;
  816. case K_DOWN: case K_END: input_cur = input_end; break;
  817. case K_LEFT:
  818. if(input_cur > input_str) {
  819. do { input_cur--; } while(input_cur > input_str && (*input_cur & 0xC0) == 0x80);
  820. }
  821. break;
  822. case K_RIGHT:
  823. if(input_cur < input_end) {
  824. do { input_cur++; } while(input_cur < input_end && (*input_cur & 0xC0) == 0x80);
  825. }
  826. break;
  827. case K_BACKSPC:
  828. if(input_cur > input_str) {
  829. s = input_cur;
  830. do { input_cur--; } while(input_cur > input_str && (*input_cur & 0xC0) == 0x80);
  831. for(i = 0; s + i <= input_end; i++) input_cur[i] = s[i];
  832. input_end -= s - input_cur;
  833. input_refresh = 1;
  834. }
  835. break;
  836. case K_DEL:
  837. if(input_cur < input_end) {
  838. s = input_cur;
  839. do { s++; } while(s < input_end && (*s & 0xC0) == 0x80);
  840. for(i = 0; s + i <= input_end; i++) input_cur[i] = s[i];
  841. input_end -= s - input_cur;
  842. input_refresh = 1;
  843. }
  844. break;
  845. default:
  846. j = strlen((char*)&event.x);
  847. if(event.x >= ' ' && input_end - input_str + j < input_maxlen) {
  848. for(s = input_end + j; s >= input_cur; s--) s[1] = s[0];
  849. memcpy(input_cur, &event.x, j);
  850. input_cur += j;
  851. input_end += j;
  852. input_refresh = 1;
  853. }
  854. break;
  855. }
  856. *input_end = 0;
  857. ui_resizewin(&wins[event.win], wins[event.win].w, wins[event.win].h);
  858. ui_refreshwin(event.win, 0, 0, wins[event.win].w, wins[event.win].h);
  859. input_refresh = 0;
  860. } else {
  861. if(!event.win)
  862. switch(wins[0].tool) {
  863. case MAIN_TOOL_LOAD:
  864. case MAIN_TOOL_SAVE: ctrl_fileops_onkey(); break;
  865. case MAIN_TOOL_PROPS: ctrl_props_onkey(); break;
  866. case MAIN_TOOL_RANGES: ctrl_ranges_onkey(); break;
  867. case MAIN_TOOL_GLYPHS: ctrl_glyphs_onkey(); break;
  868. case MAIN_TOOL_TEST: ctrl_test_onkey(); break;
  869. case MAIN_TOOL_NEW: ctrl_new_onkey(); break;
  870. }
  871. else
  872. switch(wins[event.win].tool) {
  873. case GLYPH_TOOL_COORD: ctrl_coords_onkey(event.win); break;
  874. case GLYPH_TOOL_LAYER: ctrl_layers_onkey(event.win); break;
  875. case GLYPH_TOOL_KERN: ctrl_kern_onkey(event.win); break;
  876. }
  877. }
  878. break;
  879. }
  880. break;
  881. }
  882. } /* while mainloop */
  883. }