DISTRHO Plugin Framework
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2435 lines
66KB

  1. /* libSOFD - Simple Open File Dialog [for X11 without toolkit]
  2. *
  3. * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a copy
  6. * of this software and associated documentation files (the "Software"), to deal
  7. * in the Software without restriction, including without limitation the rights
  8. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. * copies of the Software, and to permit persons to whom the Software is
  10. * furnished to do so, subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be included in
  13. * all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. * THE SOFTWARE.
  22. */
  23. /* Test and example:
  24. * gcc -Wall -D SOFD_TEST -g -o sofd libsofd.c -lX11
  25. *
  26. * public API documentation and example code at the bottom of this file
  27. *
  28. * This small lib may one day include openGL rendering and
  29. * wayland window support, but not today. Today we celebrate
  30. * 30 years of X11.
  31. */
  32. #ifdef SOFD_TEST
  33. #define SOFD_HAVE_X11
  34. #include "libsofd.h"
  35. #endif
  36. #include <stdio.h>
  37. #include <stdint.h>
  38. #include <string.h>
  39. #include <stdlib.h>
  40. #include <unistd.h>
  41. #include <libgen.h>
  42. #include <time.h>
  43. #include <sys/types.h>
  44. #include <sys/stat.h>
  45. #include <assert.h>
  46. #if defined(__clang__)
  47. # pragma clang diagnostic push
  48. # pragma clang diagnostic ignored "-Wnarrowing"
  49. #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
  50. # pragma GCC diagnostic push
  51. # pragma GCC diagnostic ignored "-Wnarrowing"
  52. #endif
  53. // shared 'recently used' implementation
  54. // sadly, xbel does not qualify as simple.
  55. // hence we use a simple format alike the
  56. // gtk-bookmark list (one file per line)
  57. #define MAX_RECENT_ENTRIES 24
  58. #define MAX_RECENT_AGE (15552000) // 180 days (in sec)
  59. typedef struct {
  60. char path[1024];
  61. time_t atime;
  62. } FibRecentFile;
  63. static FibRecentFile *_recentlist = NULL;
  64. static unsigned int _recentcnt = 0;
  65. static uint8_t _recentlock = 0;
  66. static int fib_isxdigit (const char x) {
  67. if (
  68. (x >= '0' && x <= '9')
  69. ||
  70. (x >= 'a' && x <= 'f')
  71. ||
  72. (x >= 'A' && x <= 'F')
  73. ) return 1;
  74. return 0;
  75. }
  76. static void decode_3986 (char *str) {
  77. int len = strlen (str);
  78. int idx = 0;
  79. while (idx + 2 < len) {
  80. char *in = &str[idx];
  81. if (('%' == *in) && fib_isxdigit (in[1]) && fib_isxdigit (in[2])) {
  82. char hexstr[3];
  83. hexstr[0] = in[1];
  84. hexstr[1] = in[2];
  85. hexstr[2] = 0;
  86. long hex = strtol (hexstr, NULL, 16);
  87. *in = hex;
  88. memmove (&str[idx+1], &str[idx + 3], len - idx - 2);
  89. len -= 2;
  90. }
  91. ++idx;
  92. }
  93. }
  94. static char *encode_3986 (const char *str) {
  95. size_t alloc, newlen;
  96. char *ns = NULL;
  97. unsigned char in;
  98. size_t i = 0;
  99. size_t length;
  100. if (!str) return strdup ("");
  101. alloc = strlen (str) + 1;
  102. newlen = alloc;
  103. ns = (char*) malloc (alloc);
  104. length = alloc;
  105. while (--length) {
  106. in = *str;
  107. switch (in) {
  108. case '0': case '1': case '2': case '3': case '4':
  109. case '5': case '6': case '7': case '8': case '9':
  110. case 'a': case 'b': case 'c': case 'd': case 'e':
  111. case 'f': case 'g': case 'h': case 'i': case 'j':
  112. case 'k': case 'l': case 'm': case 'n': case 'o':
  113. case 'p': case 'q': case 'r': case 's': case 't':
  114. case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
  115. case 'A': case 'B': case 'C': case 'D': case 'E':
  116. case 'F': case 'G': case 'H': case 'I': case 'J':
  117. case 'K': case 'L': case 'M': case 'N': case 'O':
  118. case 'P': case 'Q': case 'R': case 'S': case 'T':
  119. case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
  120. case '_': case '~': case '.': case '-':
  121. case '/': case ',': // XXX not in RFC3986
  122. ns[i++] = in;
  123. break;
  124. default:
  125. newlen += 2; /* this'll become a %XX */
  126. if (newlen > alloc) {
  127. alloc *= 2;
  128. ns = (char*) realloc (ns, alloc);
  129. }
  130. snprintf (&ns[i], 4, "%%%02X", in);
  131. i += 3;
  132. break;
  133. }
  134. ++str;
  135. }
  136. ns[i] = 0;
  137. return ns;
  138. }
  139. void x_fib_free_recent () {
  140. free (_recentlist);
  141. _recentlist = NULL;
  142. _recentcnt = 0;
  143. }
  144. static int cmp_recent (const void *p1, const void *p2) {
  145. FibRecentFile *a = (FibRecentFile*) p1;
  146. FibRecentFile *b = (FibRecentFile*) p2;
  147. if (a->atime == b->atime) return 0;
  148. return a->atime < b->atime;
  149. }
  150. int x_fib_add_recent (const char *path, time_t atime) {
  151. unsigned int i;
  152. struct stat fs;
  153. if (_recentlock) { return -1; }
  154. if (access (path, R_OK)) {
  155. return -1;
  156. }
  157. if (stat (path, &fs)) {
  158. return -1;
  159. }
  160. if (!S_ISREG (fs.st_mode)) {
  161. return -1;
  162. }
  163. if (atime == 0) atime = time (NULL);
  164. if (MAX_RECENT_AGE > 0 && atime + MAX_RECENT_AGE < time (NULL)) {
  165. return -1;
  166. }
  167. for (i = 0; i < _recentcnt; ++i) {
  168. if (!strcmp (_recentlist[i].path, path)) {
  169. if (_recentlist[i].atime < atime) {
  170. _recentlist[i].atime = atime;
  171. }
  172. qsort (_recentlist, _recentcnt, sizeof(FibRecentFile), cmp_recent);
  173. return _recentcnt;
  174. }
  175. }
  176. _recentlist = (FibRecentFile*)realloc (_recentlist, (_recentcnt + 1) * sizeof(FibRecentFile));
  177. _recentlist[_recentcnt].atime = atime;
  178. strcpy (_recentlist[_recentcnt].path, path);
  179. qsort (_recentlist, _recentcnt + 1, sizeof(FibRecentFile), cmp_recent);
  180. if (_recentcnt >= MAX_RECENT_ENTRIES) {
  181. return (_recentcnt);
  182. }
  183. return (++_recentcnt);
  184. }
  185. #ifdef PATHSEP
  186. #undef PATHSEP
  187. #endif
  188. #ifdef PLATFORM_WINDOWS
  189. #define DIRSEP '\\'
  190. #else
  191. #define DIRSEP '/'
  192. #endif
  193. static void mkpath(const char *dir) {
  194. char tmp[1024];
  195. char *p;
  196. size_t len;
  197. snprintf (tmp, sizeof(tmp), "%s", dir);
  198. len = strlen(tmp);
  199. if (tmp[len - 1] == '/')
  200. tmp[len - 1] = 0;
  201. for (p = tmp + 1; *p; ++p)
  202. if(*p == DIRSEP) {
  203. *p = 0;
  204. #ifdef PLATFORM_WINDOWS
  205. mkdir(tmp);
  206. #else
  207. mkdir(tmp, 0755);
  208. #endif
  209. *p = DIRSEP;
  210. }
  211. #ifdef PLATFORM_WINDOWS
  212. mkdir(tmp);
  213. #else
  214. mkdir(tmp, 0755);
  215. #endif
  216. }
  217. int x_fib_save_recent (const char *fn) {
  218. if (_recentlock) { return -1; }
  219. if (!fn) { return -1; }
  220. if (_recentcnt < 1 || !_recentlist) { return -1; }
  221. unsigned int i;
  222. char *dn = strdup (fn);
  223. mkpath (dirname (dn));
  224. free (dn);
  225. FILE *rf = fopen (fn, "w");
  226. if (!rf) return -1;
  227. qsort (_recentlist, _recentcnt, sizeof(FibRecentFile), cmp_recent);
  228. for (i = 0; i < _recentcnt; ++i) {
  229. char *n = encode_3986 (_recentlist[i].path);
  230. fprintf (rf, "%s %lu\n", n, _recentlist[i].atime);
  231. free (n);
  232. }
  233. fclose (rf);
  234. return 0;
  235. }
  236. int x_fib_load_recent (const char *fn) {
  237. char tmp[1024];
  238. if (_recentlock) { return -1; }
  239. if (!fn) { return -1; }
  240. x_fib_free_recent ();
  241. if (access (fn, R_OK)) {
  242. return -1;
  243. }
  244. FILE *rf = fopen (fn, "r");
  245. if (!rf) return -1;
  246. while (fgets (tmp, sizeof(tmp), rf)
  247. && strlen (tmp) > 1
  248. && strlen (tmp) < sizeof(tmp))
  249. {
  250. char *s;
  251. tmp[strlen (tmp) - 1] = '\0'; // strip newline
  252. if (!(s = strchr (tmp, ' '))) { // find name <> atime sep
  253. continue;
  254. }
  255. *s = '\0';
  256. time_t t = atol (++s);
  257. decode_3986 (tmp);
  258. x_fib_add_recent (tmp, t);
  259. }
  260. fclose (rf);
  261. return 0;
  262. }
  263. unsigned int x_fib_recent_count () {
  264. return _recentcnt;
  265. }
  266. const char *x_fib_recent_at (unsigned int i) {
  267. if (i >= _recentcnt)
  268. return NULL;
  269. return _recentlist[i].path;
  270. }
  271. #ifdef PLATFORM_WINDOWS
  272. #define PATHSEP "\\"
  273. #else
  274. #define PATHSEP "/"
  275. #endif
  276. const char *x_fib_recent_file(const char *appname) {
  277. static char recent_file[1024];
  278. assert(!strchr(appname, '/'));
  279. const char *xdg = getenv("XDG_DATA_HOME");
  280. if (xdg && (strlen(xdg) + strlen(appname) + 10) < sizeof(recent_file)) {
  281. sprintf(recent_file, "%s" PATHSEP "%s" PATHSEP "recent", xdg, appname);
  282. return recent_file;
  283. }
  284. #ifdef PLATFORM_WINDOWS
  285. const char * homedrive = getenv("HOMEDRIVE");
  286. const char * homepath = getenv("HOMEPATH");
  287. if (homedrive && homepath && (strlen(homedrive) + strlen(homepath) + strlen(appname) + 29) < PATH_MAX) {
  288. sprintf(recent_file, "%s%s" PATHSEP "Application Data" PATHSEP "%s" PATHSEP "recent.txt", homedrive, homepath, appname);
  289. return recent_file;
  290. }
  291. #elif defined PLATFORM_OSX
  292. const char *home = getenv("HOME");
  293. if (home && (strlen(home) + strlen(appname) + 29) < sizeof(recent_file)) {
  294. sprintf(recent_file, "%s/Library/Preferences/%s/recent", home, appname);
  295. return recent_file;
  296. }
  297. #else
  298. const char *home = getenv("HOME");
  299. if (home && (strlen(home) + strlen(appname) + 22) < sizeof(recent_file)) {
  300. sprintf(recent_file, "%s/.local/share/%s/recent", home, appname);
  301. return recent_file;
  302. }
  303. #endif
  304. return NULL;
  305. }
  306. #ifdef SOFD_HAVE_X11
  307. #include <mntent.h>
  308. #include <dirent.h>
  309. #include <X11/Xlib.h>
  310. #include <X11/Xatom.h>
  311. #include <X11/Xutil.h>
  312. #include <X11/keysym.h>
  313. #include <X11/Xos.h>
  314. #ifndef MIN
  315. #define MIN(A,B) ( (A) < (B) ? (A) : (B) )
  316. #endif
  317. #ifndef MAX
  318. #define MAX(A,B) ( (A) < (B) ? (B) : (A) )
  319. #endif
  320. static Window _fib_win = 0;
  321. static GC _fib_gc = 0;
  322. static XColor _c_gray0, _c_gray1, _c_gray2, _c_gray3, _c_gray4, _c_gray5, _c_gray6;
  323. static Font _fibfont = 0;
  324. static Pixmap _pixbuffer = None;
  325. static int _fib_width = 100;
  326. static int _fib_height = 100;
  327. static int _btn_w = 0;
  328. static int _btn_span = 0;
  329. static int _fib_font_height = 0;
  330. static int _fib_dir_indent = 0;
  331. static int _fib_spc_norm = 0;
  332. static int _fib_font_ascent = 0;
  333. static int _fib_font_vsep = 0;
  334. static int _fib_font_size_width = 0;
  335. static int _fib_font_time_width = 0;
  336. static int _fib_place_width = 0;
  337. static int _scrl_f = 0;
  338. static int _scrl_y0 = -1;
  339. static int _scrl_y1 = -1;
  340. static int _scrl_my = -1;
  341. static int _scrl_mf = -1;
  342. static int _view_p = -1;
  343. static int _fsel = -1;
  344. static int _hov_b = -1;
  345. static int _hov_f = -1;
  346. static int _hov_p = -1;
  347. static int _hov_h = -1;
  348. static int _hov_l = -1;
  349. static int _hov_s = -1;
  350. static int _sort = 0;
  351. static int _columns = 0;
  352. static int _fib_filter_fn = 1;
  353. static int _fib_hidden_fn = 0;
  354. static int _fib_show_places = 0;
  355. static uint8_t _fib_mapped = 0;
  356. static uint8_t _fib_resized = 0;
  357. static unsigned long _dblclk = 0;
  358. static int _status = -2;
  359. static char _rv_open[1024] = "";
  360. static char _fib_cfg_custom_places[1024] = "";
  361. static char _fib_cfg_custom_font[256] = "";
  362. static char _fib_cfg_title[128] = "xjadeo - Open Video File";
  363. typedef struct {
  364. char name[256];
  365. int x0;
  366. int xw;
  367. } FibPathButton;
  368. typedef struct {
  369. char name[256];
  370. char strtime[32];
  371. char strsize[32];
  372. int ssizew;
  373. off_t size;
  374. time_t mtime;
  375. uint8_t flags; // 2: selected, 4: isdir 8: recent-entry
  376. FibRecentFile *rfp;
  377. } FibFileEntry;
  378. typedef struct {
  379. char text[24];
  380. uint8_t flags; // 2: selected, 4: toggle, 8 disable
  381. int x0;
  382. int tw;
  383. int xw;
  384. void (*callback)(Display*);
  385. } FibButton;
  386. typedef struct {
  387. char name[256];
  388. char path[1024];
  389. uint8_t flags; // 1: hover, 2: selected, 4:add sep
  390. } FibPlace;
  391. static char _cur_path[1024] = "";
  392. static FibFileEntry *_dirlist = NULL;
  393. static FibPathButton *_pathbtn = NULL;
  394. static FibPlace *_placelist = NULL;
  395. static int _dircount = 0;
  396. static int _pathparts = 0;
  397. static int _placecnt = 0;
  398. static FibButton _btn_ok;
  399. static FibButton _btn_cancel;
  400. static FibButton _btn_filter;
  401. static FibButton _btn_places;
  402. static FibButton _btn_hidden;
  403. static FibButton *_btns[] = {&_btn_places, &_btn_filter, &_btn_hidden, &_btn_cancel, &_btn_ok};
  404. static int (*_fib_filter_function)(const char *filename);
  405. /* hardcoded layout */
  406. #define DSEP 6 // px; horiz space beween elements, also l+r margin for file-list
  407. #define PSEP 4 // px; horiz space beween paths
  408. #define FILECOLUMN (17 * _fib_dir_indent) //px; min width of file-column
  409. #define LISTTOP 2.7 //em; top of the file-browser list
  410. #define LISTBOT 4.75 //em; bottom of the file-browers list
  411. #define BTNBTMMARGIN 0.75 //em; height/margin of the button row
  412. #define BTNPADDING 2 // px - only used for open/cancel buttons
  413. #define SCROLLBARW (3 + (_fib_spc_norm&~1)) //px; - should be SCROLLBARW = (N * 2 + 3)
  414. #define SCROLLBOXH 10 //px; arrow box top+bottom
  415. #define PLACESW _fib_place_width //px;
  416. #define PLACESWMAX (15 *_fib_spc_norm) //px;
  417. #define PATHBTNTOP _fib_font_vsep //px; offset by (_fib_font_ascent);
  418. #define FAREAMRGB 3 //px; base L+R margin
  419. #define FAREAMRGR (FAREAMRGB + 1) //px; right margin of file-area + 1 (line width)
  420. #define FAREAMRGL (_fib_show_places ? PLACESW + FAREAMRGB : FAREAMRGB) //px; left margin of file-area
  421. #define TEXTSEP 4 //px;
  422. #define FAREATEXTL (FAREAMRGL + TEXTSEP) //px; filename text-left FAREAMRGL + TEXTSEP
  423. #define SORTBTNOFF -10 //px;
  424. #define DBLCLKTME 400 //msec; double click time
  425. #define DRAW_OUTLINE
  426. #define DOUBLE_BUFFER
  427. static int query_font_geometry (Display *dpy, GC gc, const char *txt, int *w, int *h, int *a, int *d) {
  428. XCharStruct text_structure;
  429. int font_direction, font_ascent, font_descent;
  430. XFontStruct *fontinfo = XQueryFont (dpy, XGContextFromGC (gc));
  431. if (!fontinfo) { return -1; }
  432. XTextExtents (fontinfo, txt, strlen (txt), &font_direction, &font_ascent, &font_descent, &text_structure);
  433. if (w) *w = XTextWidth (fontinfo, txt, strlen (txt));
  434. if (h) *h = text_structure.ascent + text_structure.descent;
  435. if (a) *a = text_structure.ascent;
  436. if (d) *d = text_structure.descent;
  437. XFreeFontInfo (NULL, fontinfo, 1);
  438. return 0;
  439. }
  440. static void VDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y, unsigned int w, unsigned int h) {
  441. const unsigned long blackColor = BlackPixel (dpy, DefaultScreen (dpy));
  442. #ifdef DRAW_OUTLINE
  443. XSetForeground (dpy, gc, _c_gray5.pixel);
  444. XDrawLine (dpy, d, gc, x + 1, y + h, x + w, y + h);
  445. XDrawLine (dpy, d, gc, x + w, y + 1, x + w, y + h);
  446. XSetForeground (dpy, gc, blackColor);
  447. XDrawLine (dpy, d, gc, x + 1, y, x + w, y);
  448. XDrawLine (dpy, d, gc, x, y + 1, x, y + h);
  449. #else
  450. XSetForeground (dpy, _fib_gc, blackColor);
  451. XDrawRectangle (dpy, d, gc, x, y, w, h);
  452. #endif
  453. }
  454. static void fib_expose (Display *dpy, Window realwin) {
  455. int i;
  456. XID win;
  457. const unsigned long whiteColor = WhitePixel (dpy, DefaultScreen (dpy));
  458. const unsigned long blackColor = BlackPixel (dpy, DefaultScreen (dpy));
  459. if (!_fib_mapped) return;
  460. if (_fib_resized
  461. #ifdef DOUBLE_BUFFER
  462. || !_pixbuffer
  463. #endif
  464. )
  465. {
  466. #ifdef DOUBLE_BUFFER
  467. unsigned int w = 0, h = 0;
  468. if (_pixbuffer != None) {
  469. Window ignored_w;
  470. int ignored_i;
  471. unsigned int ignored_u;
  472. XGetGeometry(dpy, _pixbuffer, &ignored_w, &ignored_i, &ignored_i, &w, &h, &ignored_u, &ignored_u);
  473. if (_fib_width != (int)w || _fib_height != (int)h) {
  474. XFreePixmap (dpy, _pixbuffer);
  475. _pixbuffer = None;
  476. }
  477. }
  478. if (_pixbuffer == None) {
  479. XWindowAttributes wa;
  480. XGetWindowAttributes (dpy, realwin, &wa);
  481. _pixbuffer = XCreatePixmap (dpy, realwin, _fib_width, _fib_height, wa.depth);
  482. }
  483. #endif
  484. if (_pixbuffer != None) {
  485. XSetForeground (dpy, _fib_gc, _c_gray1.pixel);
  486. XFillRectangle (dpy, _pixbuffer, _fib_gc, 0, 0, _fib_width, _fib_height);
  487. } else {
  488. XSetForeground (dpy, _fib_gc, _c_gray1.pixel);
  489. XFillRectangle (dpy, realwin, _fib_gc, 0, 0, _fib_width, _fib_height);
  490. }
  491. _fib_resized = 0;
  492. }
  493. if (_pixbuffer == None) {
  494. win = realwin;
  495. } else {
  496. win = _pixbuffer;
  497. }
  498. // Top Row: dirs and up navigation
  499. int ppw = 0;
  500. int ppx = FAREAMRGB;
  501. for (i = _pathparts - 1; i >= 0; --i) {
  502. ppw += _pathbtn[i].xw + PSEP;
  503. if (ppw >= _fib_width - PSEP - _pathbtn[0].xw - FAREAMRGB) break; // XXX, first change is from "/" to "<", NOOP
  504. }
  505. ++i;
  506. // border-less "<" parent/up, IFF space is limited
  507. if (i > 0) {
  508. if (0 == _hov_p || (_hov_p > 0 && _hov_p < _pathparts - 1)) {
  509. XSetForeground (dpy, _fib_gc, _c_gray4.pixel);
  510. } else {
  511. XSetForeground (dpy, _fib_gc, blackColor);
  512. }
  513. XDrawString (dpy, win, _fib_gc, ppx, PATHBTNTOP, "<", 1);
  514. ppx += _pathbtn[0].xw + PSEP;
  515. if (i == _pathparts) --i;
  516. }
  517. _view_p = i;
  518. while (i < _pathparts) {
  519. if (i == _hov_p) {
  520. XSetForeground (dpy, _fib_gc, _c_gray0.pixel);
  521. } else {
  522. XSetForeground (dpy, _fib_gc, _c_gray2.pixel);
  523. }
  524. XFillRectangle (dpy, win, _fib_gc,
  525. ppx + 1, PATHBTNTOP - _fib_font_ascent,
  526. _pathbtn[i].xw - 1, _fib_font_height);
  527. VDrawRectangle (dpy, win, _fib_gc,
  528. ppx, PATHBTNTOP - _fib_font_ascent,
  529. _pathbtn[i].xw, _fib_font_height);
  530. XDrawString (dpy, win, _fib_gc, ppx + 1 + BTNPADDING, PATHBTNTOP,
  531. _pathbtn[i].name, strlen (_pathbtn[i].name));
  532. _pathbtn[i].x0 = ppx; // current position
  533. ppx += _pathbtn[i].xw + PSEP;
  534. ++i;
  535. }
  536. // middle, scroll list of file names
  537. const int ltop = LISTTOP * _fib_font_vsep;
  538. const int llen = (_fib_height - LISTBOT * _fib_font_vsep) / _fib_font_vsep;
  539. const int fsel_height = 4 + llen * _fib_font_vsep;
  540. const int fsel_width = _fib_width - FAREAMRGL - FAREAMRGR - (llen < _dircount ? SCROLLBARW : 0);
  541. const int t_x = FAREATEXTL;
  542. int t_s = FAREATEXTL + fsel_width;
  543. int t_t = FAREATEXTL + fsel_width;
  544. // check which colums can be visible
  545. // depending on available width of window.
  546. _columns = 0;
  547. if (fsel_width > FILECOLUMN + _fib_font_size_width + _fib_font_time_width) {
  548. _columns |= 2;
  549. t_s = FAREAMRGL + fsel_width - _fib_font_time_width - TEXTSEP;
  550. }
  551. if (fsel_width > FILECOLUMN + _fib_font_size_width) {
  552. _columns |= 1;
  553. t_t = t_s - _fib_font_size_width - TEXTSEP;
  554. }
  555. int fstop = _scrl_f; // first entry in scroll position
  556. const int ttop = ltop - _fib_font_height + _fib_font_ascent;
  557. if (fstop > 0 && fstop + llen > _dircount) {
  558. fstop = MAX (0, _dircount - llen);
  559. _scrl_f = fstop;
  560. }
  561. // list header
  562. XSetForeground (dpy, _fib_gc, _c_gray3.pixel);
  563. XFillRectangle (dpy, win, _fib_gc, FAREAMRGL, ltop - _fib_font_vsep, fsel_width, _fib_font_vsep);
  564. // draw background of file list
  565. XSetForeground (dpy, _fib_gc, _c_gray2.pixel);
  566. XFillRectangle (dpy, win, _fib_gc, FAREAMRGL, ltop, fsel_width, fsel_height);
  567. #ifdef DRAW_OUTLINE
  568. VDrawRectangle (dpy, win, _fib_gc, FAREAMRGL, ltop - _fib_font_vsep -1, _fib_width - FAREAMRGL - FAREAMRGR, fsel_height + _fib_font_vsep + 1);
  569. #endif
  570. switch (_hov_h) {
  571. case 1:
  572. XSetForeground (dpy, _fib_gc, _c_gray0.pixel);
  573. XFillRectangle (dpy, win, _fib_gc, t_x + _fib_dir_indent - TEXTSEP + 1, ltop - _fib_font_vsep, t_t - t_x - _fib_dir_indent - 1, _fib_font_vsep);
  574. break;
  575. case 2:
  576. XSetForeground (dpy, _fib_gc, _c_gray0.pixel);
  577. XFillRectangle (dpy, win, _fib_gc, t_t - TEXTSEP + 1, ltop - _fib_font_vsep, _fib_font_size_width + TEXTSEP - 1, _fib_font_vsep);
  578. break;
  579. case 3:
  580. XSetForeground (dpy, _fib_gc, _c_gray0.pixel);
  581. XFillRectangle (dpy, win, _fib_gc, t_s - TEXTSEP + 1, ltop - _fib_font_vsep, TEXTSEP + TEXTSEP + _fib_font_time_width - 1, _fib_font_vsep);
  582. break;
  583. default:
  584. break;
  585. }
  586. // column headings and sort order
  587. int arp = MAX (2, _fib_font_height / 5); // arrow scale
  588. const int trioff = _fib_font_height - _fib_font_ascent - arp + 1;
  589. XPoint ptri[4] = { {0, ttop - trioff }, {arp, -arp - arp - 1}, {-arp - arp, 0}, {arp, arp + arp + 1}};
  590. if (_sort & 1) {
  591. ptri[0].y = ttop -arp - arp - 1;
  592. ptri[1].y *= -1;
  593. ptri[3].y *= -1;
  594. }
  595. switch (_sort) {
  596. case 0:
  597. case 1:
  598. ptri[0].x = t_t + SORTBTNOFF + 2 - arp;
  599. XSetForeground (dpy, _fib_gc, _c_gray6.pixel);
  600. XFillPolygon (dpy, win, _fib_gc, ptri, 3, Convex, CoordModePrevious);
  601. XDrawLines (dpy, win, _fib_gc, ptri, 4, CoordModePrevious);
  602. break;
  603. case 2:
  604. case 3:
  605. if (_columns & 1) {
  606. ptri[0].x = t_s + SORTBTNOFF + 2 - arp;
  607. XSetForeground (dpy, _fib_gc, _c_gray6.pixel);
  608. XFillPolygon (dpy, win, _fib_gc, ptri, 3, Convex, CoordModePrevious);
  609. XDrawLines (dpy, win, _fib_gc, ptri, 4, CoordModePrevious);
  610. }
  611. break;
  612. case 4:
  613. case 5:
  614. if (_columns & 2) {
  615. ptri[0].x = FAREATEXTL + fsel_width + SORTBTNOFF + 2 - arp;
  616. XSetForeground (dpy, _fib_gc, _c_gray6.pixel);
  617. XFillPolygon (dpy, win, _fib_gc, ptri, 3, Convex, CoordModePrevious);
  618. XDrawLines (dpy, win, _fib_gc, ptri, 4, CoordModePrevious);
  619. }
  620. break;
  621. }
  622. #if 0 // bottom header bottom border
  623. XSetForeground (dpy, _fib_gc, _c_gray5.pixel);
  624. XSetLineAttributes (dpy, _fib_gc, 1, LineOnOffDash, CapButt, JoinMiter);
  625. XDrawLine (dpy, win, _fib_gc,
  626. FAREAMRGL + 1, ltop,
  627. FAREAMRGL + fsel_width, ltop);
  628. XSetLineAttributes (dpy, _fib_gc, 1, LineSolid, CapButt, JoinMiter);
  629. #endif
  630. XSetForeground (dpy, _fib_gc, _c_gray4.pixel);
  631. XDrawLine (dpy, win, _fib_gc,
  632. t_x + _fib_dir_indent - TEXTSEP, ltop - _fib_font_vsep + 3,
  633. t_x + _fib_dir_indent - TEXTSEP, ltop - 3);
  634. XSetForeground (dpy, _fib_gc, blackColor);
  635. XDrawString (dpy, win, _fib_gc, t_x + _fib_dir_indent, ttop, "Name", 4);
  636. if (_columns & 1) {
  637. XSetForeground (dpy, _fib_gc, _c_gray4.pixel);
  638. XDrawLine (dpy, win, _fib_gc,
  639. t_t - TEXTSEP, ltop - _fib_font_vsep + 3,
  640. t_t - TEXTSEP, ltop - 3);
  641. XSetForeground (dpy, _fib_gc, blackColor);
  642. XDrawString (dpy, win, _fib_gc, t_t, ttop, "Size", 4);
  643. }
  644. if (_columns & 2) {
  645. XSetForeground (dpy, _fib_gc, _c_gray4.pixel);
  646. XDrawLine (dpy, win, _fib_gc,
  647. t_s - TEXTSEP, ltop - _fib_font_vsep + 3,
  648. t_s - TEXTSEP, ltop - 3);
  649. XSetForeground (dpy, _fib_gc, blackColor);
  650. if (_pathparts > 0)
  651. XDrawString (dpy, win, _fib_gc, t_s, ttop, "Last Modified", 13);
  652. else
  653. XDrawString (dpy, win, _fib_gc, t_s, ttop, "Last Used", 9);
  654. }
  655. // scrollbar sep
  656. if (llen < _dircount) {
  657. const int sx0 = _fib_width - SCROLLBARW - FAREAMRGR;
  658. XSetForeground (dpy, _fib_gc, _c_gray4.pixel);
  659. XDrawLine (dpy, win, _fib_gc,
  660. sx0 - 1, ltop - _fib_font_vsep,
  661. #ifdef DRAW_OUTLINE
  662. sx0 - 1, ltop + fsel_height
  663. #else
  664. sx0 - 1, ltop - 1
  665. #endif
  666. );
  667. }
  668. // clip area for file-name
  669. XRectangle clp = {FAREAMRGL + 1, ltop, t_t - FAREAMRGL - TEXTSEP - TEXTSEP - 1, fsel_height};
  670. // list files in view
  671. for (i = 0; i < llen; ++i) {
  672. const int j = i + fstop;
  673. if (j >= _dircount) break;
  674. const int t_y = ltop + (i+1) * _fib_font_vsep - 4;
  675. XSetForeground (dpy, _fib_gc, blackColor);
  676. if (_dirlist[j].flags & 2) {
  677. XSetForeground (dpy, _fib_gc, blackColor);
  678. XFillRectangle (dpy, win, _fib_gc,
  679. FAREAMRGL, t_y - _fib_font_ascent, fsel_width, _fib_font_height);
  680. XSetForeground (dpy, _fib_gc, whiteColor);
  681. }
  682. if (_hov_f == j && !(_dirlist[j].flags & 2)) {
  683. XSetForeground (dpy, _fib_gc, _c_gray4.pixel);
  684. }
  685. if (_dirlist[j].flags & 4) {
  686. XDrawString (dpy, win, _fib_gc, t_x, t_y, "D", 1);
  687. }
  688. XSetClipRectangles (dpy, _fib_gc, 0, 0, &clp, 1, Unsorted);
  689. XDrawString (dpy, win, _fib_gc,
  690. t_x + _fib_dir_indent, t_y,
  691. _dirlist[j].name, strlen (_dirlist[j].name));
  692. XSetClipMask (dpy, _fib_gc, None);
  693. if (_columns & 1) // right-aligned 'size'
  694. XDrawString (dpy, win, _fib_gc,
  695. t_s - TEXTSEP - 2 - _dirlist[j].ssizew, t_y,
  696. _dirlist[j].strsize, strlen (_dirlist[j].strsize));
  697. if (_columns & 2)
  698. XDrawString (dpy, win, _fib_gc,
  699. t_s, t_y,
  700. _dirlist[j].strtime, strlen (_dirlist[j].strtime));
  701. }
  702. // scrollbar
  703. if (llen < _dircount) {
  704. float sl = (fsel_height + _fib_font_vsep - (SCROLLBOXH + SCROLLBOXH)) / (float) _dircount;
  705. sl = MAX ((8. / llen), sl); // 8px min height of scroller
  706. const int sy1 = llen * sl;
  707. const float mx = (fsel_height + _fib_font_vsep - (SCROLLBOXH + SCROLLBOXH) - sy1) / (float)(_dircount - llen);
  708. const int sy0 = fstop * mx;
  709. const int sx0 = _fib_width - SCROLLBARW - FAREAMRGR;
  710. const int stop = ltop - _fib_font_vsep;
  711. _scrl_y0 = stop + SCROLLBOXH + sy0;
  712. _scrl_y1 = _scrl_y0 + sy1;
  713. assert (fstop + llen <= _dircount);
  714. // scroll-bar background
  715. XSetForeground (dpy, _fib_gc, _c_gray3.pixel);
  716. XFillRectangle (dpy, win, _fib_gc, sx0, stop, SCROLLBARW, fsel_height + _fib_font_vsep);
  717. // scroller
  718. if (_hov_s == 0) {
  719. XSetForeground (dpy, _fib_gc, _c_gray0.pixel);
  720. } else {
  721. XSetForeground (dpy, _fib_gc, _c_gray1.pixel);
  722. }
  723. XFillRectangle (dpy, win, _fib_gc, sx0 + 1, stop + SCROLLBOXH + sy0, SCROLLBARW - 2, sy1);
  724. int scrw = (SCROLLBARW -3) / 2;
  725. // arrows top and bottom
  726. if (_hov_s == 1) {
  727. XSetForeground (dpy, _fib_gc, _c_gray0.pixel);
  728. } else {
  729. XSetForeground (dpy, _fib_gc, _c_gray1.pixel);
  730. }
  731. XPoint ptst[4] = { {sx0 + 1, stop + 8}, {scrw, -7}, {scrw, 7}, {-2 * scrw, 0}};
  732. XFillPolygon (dpy, win, _fib_gc, ptst, 3, Convex, CoordModePrevious);
  733. XDrawLines (dpy, win, _fib_gc, ptst, 4, CoordModePrevious);
  734. if (_hov_s == 2) {
  735. XSetForeground (dpy, _fib_gc, _c_gray0.pixel);
  736. } else {
  737. XSetForeground (dpy, _fib_gc, _c_gray1.pixel);
  738. }
  739. XPoint ptsb[4] = { {sx0 + 1, ltop + fsel_height - 9}, {2*scrw, 0}, {-scrw, 7}, {-scrw, -7}};
  740. XFillPolygon (dpy, win, _fib_gc, ptsb, 3, Convex, CoordModePrevious);
  741. XDrawLines (dpy, win, _fib_gc, ptsb, 4, CoordModePrevious);
  742. } else {
  743. _scrl_y0 = _scrl_y1 = -1;
  744. }
  745. if (_fib_show_places) {
  746. assert (_placecnt > 0);
  747. // heading
  748. XSetForeground (dpy, _fib_gc, _c_gray3.pixel);
  749. XFillRectangle (dpy, win, _fib_gc, FAREAMRGB, ltop - _fib_font_vsep, PLACESW - TEXTSEP, _fib_font_vsep);
  750. // body
  751. XSetForeground (dpy, _fib_gc, _c_gray2.pixel);
  752. XFillRectangle (dpy, win, _fib_gc, FAREAMRGB, ltop, PLACESW - TEXTSEP, fsel_height);
  753. #ifdef DRAW_OUTLINE
  754. VDrawRectangle (dpy, win, _fib_gc, FAREAMRGB, ltop - _fib_font_vsep -1, PLACESW - TEXTSEP, fsel_height + _fib_font_vsep + 1);
  755. #endif
  756. XSetForeground (dpy, _fib_gc, blackColor);
  757. XDrawString (dpy, win, _fib_gc, FAREAMRGB + TEXTSEP, ttop, "Places", 6);
  758. XRectangle pclip = {FAREAMRGB + 1, ltop, PLACESW - TEXTSEP -1, fsel_height};
  759. XSetClipRectangles (dpy, _fib_gc, 0, 0, &pclip, 1, Unsorted);
  760. const int plx = FAREAMRGB + TEXTSEP;
  761. for (i = 0; i < llen && i < _placecnt; ++i) {
  762. const int ply = ltop + (i+1) * _fib_font_vsep - 4;
  763. if (i == _hov_l) {
  764. XSetForeground (dpy, _fib_gc, _c_gray4.pixel);
  765. } else {
  766. XSetForeground (dpy, _fib_gc, blackColor);
  767. }
  768. XDrawString (dpy, win, _fib_gc,
  769. plx, ply,
  770. _placelist[i].name, strlen (_placelist[i].name));
  771. if (_placelist[i].flags & 4) {
  772. XSetForeground (dpy, _fib_gc, _c_gray3.pixel);
  773. const int plly = ply - _fib_font_ascent + _fib_font_height;
  774. const int pllx0 = FAREAMRGB;
  775. const int pllx1 = FAREAMRGB + (PLACESW - TEXTSEP);
  776. XDrawLine (dpy, win, _fib_gc, pllx0, plly, pllx1, plly);
  777. }
  778. }
  779. XSetClipMask (dpy, _fib_gc, None);
  780. if (_placecnt > llen) {
  781. const int plly = ltop + fsel_height - _fib_font_height + _fib_font_ascent;
  782. const int pllx0 = FAREAMRGB + (PLACESW - TEXTSEP) * .75;
  783. const int pllx1 = FAREAMRGB + (PLACESW - TEXTSEP - TEXTSEP);
  784. XSetForeground (dpy, _fib_gc, blackColor);
  785. XSetLineAttributes (dpy, _fib_gc, 1, LineOnOffDash, CapButt, JoinMiter);
  786. XDrawLine (dpy, win, _fib_gc, pllx0, plly, pllx1, plly);
  787. XSetLineAttributes (dpy, _fib_gc, 1, LineSolid, CapButt, JoinMiter);
  788. }
  789. }
  790. // Bottom Buttons
  791. const int numb = sizeof(_btns) / sizeof(FibButton*);
  792. int xtra = _fib_width - _btn_span;
  793. const int cbox = _fib_font_ascent - 2;
  794. const int bbase = _fib_height - BTNBTMMARGIN * _fib_font_vsep - BTNPADDING;
  795. const int cblw = cbox > 20 ? 5 : ( cbox > 9 ? 3 : 1);
  796. int bx = FAREAMRGB;
  797. for (i = 0; i < numb; ++i) {
  798. if (_btns[i]->flags & 8) { continue; }
  799. if (_btns[i]->flags & 4) {
  800. // checkbutton
  801. const int cby0 = bbase - cbox + 1 + BTNPADDING;
  802. if (i == _hov_b) {
  803. XSetForeground (dpy, _fib_gc, _c_gray4.pixel);
  804. } else {
  805. XSetForeground (dpy, _fib_gc, blackColor);
  806. }
  807. XDrawRectangle (dpy, win, _fib_gc,
  808. bx, cby0 - 1, cbox + 1, cbox + 1);
  809. if (i == _hov_b) {
  810. XSetForeground (dpy, _fib_gc, _c_gray5.pixel);
  811. } else {
  812. XSetForeground (dpy, _fib_gc, blackColor);
  813. }
  814. XDrawString (dpy, win, _fib_gc, BTNPADDING + bx + _fib_font_ascent, 1 + bbase + BTNPADDING,
  815. _btns[i]->text, strlen (_btns[i]->text));
  816. if (i == _hov_b) {
  817. XSetForeground (dpy, _fib_gc, _c_gray0.pixel);
  818. } else {
  819. if (_btns[i]->flags & 2) {
  820. XSetForeground (dpy, _fib_gc, _c_gray1.pixel);
  821. } else {
  822. XSetForeground (dpy, _fib_gc, _c_gray2.pixel);
  823. }
  824. }
  825. XFillRectangle (dpy, win, _fib_gc,
  826. bx+1, cby0, cbox, cbox);
  827. if (_btns[i]->flags & 2) {
  828. XSetLineAttributes (dpy, _fib_gc, cblw, LineSolid, CapRound, JoinMiter);
  829. XSetForeground (dpy, _fib_gc, _c_gray6.pixel);
  830. XDrawLine (dpy, win, _fib_gc,
  831. bx + 2, cby0 + 1,
  832. bx + cbox - 1, cby0 + cbox - 2);
  833. XDrawLine (dpy, win, _fib_gc,
  834. bx + cbox - 1, cby0 + 1,
  835. bx + 2, cby0 + cbox - 2);
  836. XSetLineAttributes (dpy, _fib_gc, 1, LineSolid, CapButt, JoinMiter);
  837. }
  838. } else {
  839. if (xtra > 0) {
  840. bx += xtra;
  841. xtra = 0;
  842. }
  843. // pushbutton
  844. uint8_t can_hover = 1; // special case
  845. if (_btns[i] == &_btn_ok) {
  846. if (_fsel < 0 || _fsel >= _dircount) {
  847. can_hover = 0;
  848. }
  849. }
  850. if (can_hover && i == _hov_b) {
  851. XSetForeground (dpy, _fib_gc, _c_gray0.pixel);
  852. } else {
  853. XSetForeground (dpy, _fib_gc, _c_gray2.pixel);
  854. }
  855. XFillRectangle (dpy, win, _fib_gc,
  856. bx + 1, bbase - _fib_font_ascent,
  857. _btn_w - 1, _fib_font_height + BTNPADDING + BTNPADDING);
  858. VDrawRectangle (dpy, win, _fib_gc,
  859. bx, bbase - _fib_font_ascent,
  860. _btn_w, _fib_font_height + BTNPADDING + BTNPADDING);
  861. XDrawString (dpy, win, _fib_gc, bx + (_btn_w - _btns[i]->tw) * .5, 1 + bbase + BTNPADDING,
  862. _btns[i]->text, strlen (_btns[i]->text));
  863. }
  864. _btns[i]->x0 = bx;
  865. bx += _btns[i]->xw + DSEP;
  866. }
  867. if (_pixbuffer != None) {
  868. XCopyArea(dpy, _pixbuffer, realwin, _fib_gc, 0, 0, _fib_width, _fib_height, 0, 0);
  869. }
  870. XFlush (dpy);
  871. }
  872. static void fib_reset () {
  873. _hov_p = _hov_f = _hov_h = _hov_l = -1;
  874. _scrl_f = 0;
  875. _fib_resized = 1;
  876. }
  877. static int cmp_n_up (const void *p1, const void *p2) {
  878. FibFileEntry *a = (FibFileEntry*) p1;
  879. FibFileEntry *b = (FibFileEntry*) p2;
  880. if ((a->flags & 4) && !(b->flags & 4)) return -1;
  881. if (!(a->flags & 4) && (b->flags & 4)) return 1;
  882. return strcmp (a->name, b->name);
  883. }
  884. static int cmp_n_down (const void *p1, const void *p2) {
  885. FibFileEntry *a = (FibFileEntry*) p1;
  886. FibFileEntry *b = (FibFileEntry*) p2;
  887. if ((a->flags & 4) && !(b->flags & 4)) return -1;
  888. if (!(a->flags & 4) && (b->flags & 4)) return 1;
  889. return strcmp (b->name, a->name);
  890. }
  891. static int cmp_t_up (const void *p1, const void *p2) {
  892. FibFileEntry *a = (FibFileEntry*) p1;
  893. FibFileEntry *b = (FibFileEntry*) p2;
  894. if ((a->flags & 4) && !(b->flags & 4)) return -1;
  895. if (!(a->flags & 4) && (b->flags & 4)) return 1;
  896. if (a->mtime == b->mtime) return 0;
  897. return a->mtime > b->mtime ? -1 : 1;
  898. }
  899. static int cmp_t_down (const void *p1, const void *p2) {
  900. FibFileEntry *a = (FibFileEntry*) p1;
  901. FibFileEntry *b = (FibFileEntry*) p2;
  902. if ((a->flags & 4) && !(b->flags & 4)) return -1;
  903. if (!(a->flags & 4) && (b->flags & 4)) return 1;
  904. if (a->mtime == b->mtime) return 0;
  905. return a->mtime > b->mtime ? 1 : -1;
  906. }
  907. static int cmp_s_up (const void *p1, const void *p2) {
  908. FibFileEntry *a = (FibFileEntry*) p1;
  909. FibFileEntry *b = (FibFileEntry*) p2;
  910. if ((a->flags & 4) && (b->flags & 4)) return 0; // dir, no size, retain order
  911. if ((a->flags & 4) && !(b->flags & 4)) return -1;
  912. if (!(a->flags & 4) && (b->flags & 4)) return 1;
  913. if (a->size == b->size) return 0;
  914. return a->size > b->size ? -1 : 1;
  915. }
  916. static int cmp_s_down (const void *p1, const void *p2) {
  917. FibFileEntry *a = (FibFileEntry*) p1;
  918. FibFileEntry *b = (FibFileEntry*) p2;
  919. if ((a->flags & 4) && (b->flags & 4)) return 0; // dir, no size, retain order
  920. if ((a->flags & 4) && !(b->flags & 4)) return -1;
  921. if (!(a->flags & 4) && (b->flags & 4)) return 1;
  922. if (a->size == b->size) return 0;
  923. return a->size > b->size ? 1 : -1;
  924. }
  925. static void fmt_size (Display *dpy, FibFileEntry *f) {
  926. if (f->size > 10995116277760) {
  927. sprintf (f->strsize, "%.0f TB", f->size / 1099511627776.f);
  928. }
  929. if (f->size > 1099511627776) {
  930. sprintf (f->strsize, "%.1f TB", f->size / 1099511627776.f);
  931. }
  932. else if (f->size > 10737418240) {
  933. sprintf (f->strsize, "%.0f GB", f->size / 1073741824.f);
  934. }
  935. else if (f->size > 1073741824) {
  936. sprintf (f->strsize, "%.1f GB", f->size / 1073741824.f);
  937. }
  938. else if (f->size > 10485760) {
  939. sprintf (f->strsize, "%.0f MB", f->size / 1048576.f);
  940. }
  941. else if (f->size > 1048576) {
  942. sprintf (f->strsize, "%.1f MB", f->size / 1048576.f);
  943. }
  944. else if (f->size > 10240) {
  945. sprintf (f->strsize, "%.0f KB", f->size / 1024.f);
  946. }
  947. else if (f->size >= 1000) {
  948. sprintf (f->strsize, "%.1f KB", f->size / 1024.f);
  949. }
  950. else {
  951. sprintf (f->strsize, "%.0f B", f->size / 1.f);
  952. }
  953. int sw = 0;
  954. query_font_geometry (dpy, _fib_gc, f->strsize, &sw, NULL, NULL, NULL);
  955. if (sw > _fib_font_size_width) {
  956. _fib_font_size_width = sw;
  957. }
  958. f->ssizew = sw;
  959. }
  960. static void fmt_time (Display *dpy, FibFileEntry *f) {
  961. struct tm *tmp;
  962. tmp = localtime (&f->mtime);
  963. if (!tmp) {
  964. return;
  965. }
  966. strftime (f->strtime, sizeof(f->strtime), "%F %H:%M", tmp);
  967. int tw = 0;
  968. query_font_geometry (dpy, _fib_gc, f->strtime, &tw, NULL, NULL, NULL);
  969. if (tw > _fib_font_time_width) {
  970. _fib_font_time_width = tw;
  971. }
  972. }
  973. static void fib_resort (const char * sel) {
  974. if (_dircount < 1) { return; }
  975. int (*sortfn)(const void *p1, const void *p2);
  976. switch (_sort) {
  977. case 1: sortfn = &cmp_n_down; break;
  978. case 2: sortfn = &cmp_s_down; break;
  979. case 3: sortfn = &cmp_s_up; break;
  980. case 4: sortfn = &cmp_t_down; break;
  981. case 5: sortfn = &cmp_t_up; break;
  982. default:
  983. sortfn = &cmp_n_up;
  984. break;
  985. }
  986. qsort (_dirlist, _dircount, sizeof(_dirlist[0]), sortfn);
  987. int i;
  988. for (i = 0; i < _dircount && sel; ++i) {
  989. if (!strcmp (_dirlist[i].name, sel)) {
  990. _fsel = i;
  991. break;
  992. }
  993. }
  994. }
  995. static void fib_select (Display *dpy, int item) {
  996. if (_fsel >= 0) {
  997. _dirlist[_fsel].flags &= ~2;
  998. }
  999. _fsel = item;
  1000. if (_fsel >= 0 && _fsel < _dircount) {
  1001. _dirlist[_fsel].flags |= 2;
  1002. const int llen = (_fib_height - LISTBOT * _fib_font_vsep) / _fib_font_vsep;
  1003. if (_fsel < _scrl_f) {
  1004. _scrl_f = _fsel;
  1005. }
  1006. else if (_fsel >= _scrl_f + llen) {
  1007. _scrl_f = 1 + _fsel - llen;
  1008. }
  1009. } else {
  1010. _fsel = -1;
  1011. }
  1012. fib_expose (dpy, _fib_win);
  1013. }
  1014. static inline int fib_filter (const char *name) {
  1015. if (_fib_filter_function) {
  1016. return _fib_filter_function (name);
  1017. } else {
  1018. return 1;
  1019. }
  1020. }
  1021. static void fib_pre_opendir (Display *dpy) {
  1022. if (_dirlist) free (_dirlist);
  1023. if (_pathbtn) free (_pathbtn);
  1024. _dirlist = NULL;
  1025. _pathbtn = NULL;
  1026. _dircount = 0;
  1027. _pathparts = 0;
  1028. query_font_geometry (dpy, _fib_gc, "Size ", &_fib_font_size_width, NULL, NULL, NULL);
  1029. fib_reset ();
  1030. _fsel = -1;
  1031. }
  1032. static void fib_post_opendir (Display *dpy, const char *sel) {
  1033. if (_dircount > 0)
  1034. _fsel = 0; // select first
  1035. else
  1036. _fsel = -1;
  1037. fib_resort (sel);
  1038. if (_dircount > 0 && _fsel >= 0) {
  1039. fib_select (dpy, _fsel);
  1040. } else {
  1041. fib_expose (dpy, _fib_win);
  1042. }
  1043. }
  1044. static int fib_dirlistadd (Display *dpy, const int i, const char* path, const char *name, time_t mtime) {
  1045. char tp[1024];
  1046. struct stat fs;
  1047. if (!_fib_hidden_fn && name[0] == '.') return -1;
  1048. if (!strcmp (name, ".")) return -1;
  1049. if (!strcmp (name, "..")) return -1;
  1050. strcpy (tp, path);
  1051. strcat (tp, name);
  1052. if (access (tp, R_OK)) {
  1053. return -1;
  1054. }
  1055. if (stat (tp, &fs)) {
  1056. return -1;
  1057. }
  1058. assert (i < _dircount); // could happen if dir changes while we're reading.
  1059. if (i >= _dircount) return -1;
  1060. if (S_ISDIR (fs.st_mode)) {
  1061. _dirlist[i].flags |= 4;
  1062. }
  1063. else if (S_ISREG (fs.st_mode)) {
  1064. if (!fib_filter (name)) return -1;
  1065. }
  1066. #if 0 // only needed with lstat()
  1067. else if (S_ISLNK (fs.st_mode)) {
  1068. if (!fib_filter (name)) return -1;
  1069. }
  1070. #endif
  1071. else {
  1072. return -1;
  1073. }
  1074. strcpy (_dirlist[i].name, name);
  1075. _dirlist[i].mtime = mtime > 0 ? mtime : fs.st_mtime;
  1076. _dirlist[i].size = fs.st_size;
  1077. if (!(_dirlist[i].flags & 4))
  1078. fmt_size (dpy, &_dirlist[i]);
  1079. fmt_time (dpy, &_dirlist[i]);
  1080. return 0;
  1081. }
  1082. static int fib_openrecent (Display *dpy, const char *sel) {
  1083. int i;
  1084. unsigned int j;
  1085. assert (_recentcnt > 0);
  1086. fib_pre_opendir (dpy);
  1087. query_font_geometry (dpy, _fib_gc, "Last Used", &_fib_font_time_width, NULL, NULL, NULL);
  1088. _dirlist = (FibFileEntry*) calloc (_recentcnt, sizeof(FibFileEntry));
  1089. _dircount = _recentcnt;
  1090. for (j = 0, i = 0; j < _recentcnt; ++j) {
  1091. char base[1024];
  1092. char *s = strrchr (_recentlist[j].path, '/');
  1093. if (!s || !*++s) continue;
  1094. size_t len = (s - _recentlist[j].path);
  1095. strncpy (base, _recentlist[j].path, len);
  1096. base[len] = '\0';
  1097. if (!fib_dirlistadd (dpy, i, base, s, _recentlist[j].atime)) {
  1098. _dirlist[i].rfp = &_recentlist[j];
  1099. _dirlist[i].flags |= 8;
  1100. ++i;
  1101. }
  1102. }
  1103. _dircount = i;
  1104. fib_post_opendir (dpy, sel);
  1105. return _dircount;
  1106. }
  1107. static int fib_opendir (Display *dpy, const char* path, const char *sel) {
  1108. char *t0, *t1;
  1109. int i;
  1110. assert (path);
  1111. if (strlen (path) == 0 && _recentcnt > 0) { // XXX we should use a better indication for this
  1112. strcpy (_cur_path, "");
  1113. return fib_openrecent (dpy, sel);
  1114. }
  1115. assert (strlen (path) < sizeof(_cur_path) -1);
  1116. assert (strlen (path) > 0);
  1117. assert (strstr (path, "//") == NULL);
  1118. assert (path[0] == '/');
  1119. fib_pre_opendir (dpy);
  1120. query_font_geometry (dpy, _fib_gc, "Last Modified", &_fib_font_time_width, NULL, NULL, NULL);
  1121. DIR *dir = opendir (path);
  1122. if (!dir) {
  1123. strcpy (_cur_path, "/");
  1124. } else {
  1125. int i;
  1126. struct dirent *de;
  1127. if (path != _cur_path)
  1128. strcpy (_cur_path, path);
  1129. if (_cur_path[strlen (_cur_path) -1] != '/')
  1130. strcat (_cur_path, "/");
  1131. while ((de = readdir (dir))) {
  1132. if (!_fib_hidden_fn && de->d_name[0] == '.') continue;
  1133. ++_dircount;
  1134. }
  1135. if (_dircount > 0)
  1136. _dirlist = (FibFileEntry*) calloc (_dircount, sizeof(FibFileEntry));
  1137. rewinddir (dir);
  1138. i = 0;
  1139. while ((de = readdir (dir))) {
  1140. if (!fib_dirlistadd (dpy, i, _cur_path, de->d_name, 0))
  1141. ++i;
  1142. }
  1143. _dircount = i;
  1144. closedir (dir);
  1145. }
  1146. t0 = _cur_path;
  1147. while (*t0 && (t0 = strchr (t0, '/'))) {
  1148. ++_pathparts;
  1149. ++t0;
  1150. }
  1151. assert (_pathparts > 0);
  1152. _pathbtn = (FibPathButton*) calloc (_pathparts + 1, sizeof(FibPathButton));
  1153. t1 = _cur_path;
  1154. i = 0;
  1155. while (*t1 && (t0 = strchr (t1, '/'))) {
  1156. if (i == 0) {
  1157. strcpy (_pathbtn[i].name, "/");
  1158. } else {
  1159. *t0 = 0;
  1160. strcpy (_pathbtn[i].name, t1);
  1161. }
  1162. query_font_geometry (dpy, _fib_gc, _pathbtn[i].name, &_pathbtn[i].xw, NULL, NULL, NULL);
  1163. _pathbtn[i].xw += BTNPADDING + BTNPADDING;
  1164. *t0 = '/';
  1165. t1 = t0 + 1;
  1166. ++i;
  1167. }
  1168. fib_post_opendir (dpy, sel);
  1169. return _dircount;
  1170. }
  1171. static int fib_open (Display *dpy, int item) {
  1172. char tp[1024];
  1173. if (_dirlist[item].flags & 8) {
  1174. assert (_dirlist[item].rfp);
  1175. strcpy (_rv_open, _dirlist[item].rfp->path);
  1176. _status = 1;
  1177. return 0;
  1178. }
  1179. strcpy (tp, _cur_path);
  1180. strcat (tp, _dirlist[item].name);
  1181. if (_dirlist[item].flags & 4) {
  1182. fib_opendir (dpy, tp, NULL);
  1183. return 0;
  1184. } else {
  1185. _status = 1;
  1186. strcpy (_rv_open, tp);
  1187. }
  1188. return 0;
  1189. }
  1190. static void cb_cancel (Display *dpy) {
  1191. _status = -1;
  1192. // unused
  1193. return; (void)dpy;
  1194. }
  1195. static void cb_open (Display *dpy) {
  1196. if (_fsel >= 0 && _fsel < _dircount) {
  1197. fib_open (dpy, _fsel);
  1198. }
  1199. }
  1200. static void sync_button_states () {
  1201. if (_fib_show_places)
  1202. _btn_places.flags |= 2;
  1203. else
  1204. _btn_places.flags &= ~2;
  1205. if (_fib_filter_fn) // inverse -> show all
  1206. _btn_filter.flags &= ~2;
  1207. else
  1208. _btn_filter.flags |= 2;
  1209. if (_fib_hidden_fn)
  1210. _btn_hidden.flags |= 2;
  1211. else
  1212. _btn_hidden.flags &= ~2;
  1213. }
  1214. static void cb_places (Display *dpy) {
  1215. _fib_show_places = ! _fib_show_places;
  1216. if (_placecnt < 1)
  1217. _fib_show_places = 0;
  1218. sync_button_states ();
  1219. _fib_resized = 1;
  1220. fib_expose (dpy, _fib_win);
  1221. }
  1222. static void cb_filter (Display *dpy) {
  1223. _fib_filter_fn = ! _fib_filter_fn;
  1224. sync_button_states ();
  1225. char *sel = _fsel >= 0 ? strdup (_dirlist[_fsel].name) : NULL;
  1226. fib_opendir (dpy, _cur_path, sel);
  1227. free (sel);
  1228. }
  1229. static void cb_hidden (Display *dpy) {
  1230. _fib_hidden_fn = ! _fib_hidden_fn;
  1231. sync_button_states ();
  1232. char *sel = _fsel >= 0 ? strdup (_dirlist[_fsel].name) : NULL;
  1233. fib_opendir (dpy, _cur_path, sel);
  1234. free (sel);
  1235. }
  1236. static int fib_widget_at_pos (Display *dpy, int x, int y, int *it) {
  1237. const int btop = _fib_height - BTNBTMMARGIN * _fib_font_vsep - _fib_font_ascent - BTNPADDING;
  1238. const int bbot = btop + _fib_font_height + BTNPADDING + BTNPADDING;
  1239. const int llen = (_fib_height - LISTBOT * _fib_font_vsep) / _fib_font_vsep;
  1240. const int ltop = LISTTOP * _fib_font_vsep;
  1241. const int fbot = ltop + 4 + llen * _fib_font_vsep;
  1242. const int ptop = PATHBTNTOP - _fib_font_ascent;
  1243. assert (it);
  1244. // paths at top
  1245. if (y > ptop && y < ptop + _fib_font_height && _view_p >= 0 && _pathparts > 0) {
  1246. int i = _view_p;
  1247. *it = -1;
  1248. if (i > 0) { // special case '<'
  1249. if (x > FAREAMRGB && x <= FAREAMRGB + _pathbtn[0].xw) {
  1250. *it = _view_p - 1;
  1251. i = _pathparts;
  1252. }
  1253. }
  1254. while (i < _pathparts) {
  1255. if (x >= _pathbtn[i].x0 && x <= _pathbtn[i].x0 + _pathbtn[i].xw) {
  1256. *it = i;
  1257. break;
  1258. }
  1259. ++i;
  1260. }
  1261. assert (*it < _pathparts);
  1262. if (*it >= 0) return 1;
  1263. else return 0;
  1264. }
  1265. // buttons at bottom
  1266. if (y > btop && y < bbot) {
  1267. size_t i;
  1268. *it = -1;
  1269. for (i = 0; i < sizeof(_btns) / sizeof(FibButton*); ++i) {
  1270. const int bx = _btns[i]->x0;
  1271. if (_btns[i]->flags & 8) { continue; }
  1272. if (x > bx && x < bx + _btns[i]->xw) {
  1273. *it = i;
  1274. }
  1275. }
  1276. if (*it >= 0) return 3;
  1277. else return 0;
  1278. }
  1279. // main file area
  1280. if (y >= ltop - _fib_font_vsep && y < fbot && x > FAREAMRGL && x < _fib_width - FAREAMRGR) {
  1281. // scrollbar
  1282. if (_scrl_y0 > 0 && x >= _fib_width - (FAREAMRGR + SCROLLBARW) && x <= _fib_width - FAREAMRGR) {
  1283. if (y >= _scrl_y0 && y < _scrl_y1) {
  1284. *it = 0;
  1285. } else if (y >= _scrl_y1) {
  1286. *it = 2;
  1287. } else {
  1288. *it = 1;
  1289. }
  1290. return 4;
  1291. }
  1292. // file-list
  1293. else if (y >= ltop) {
  1294. const int item = (y - ltop) / _fib_font_vsep + _scrl_f;
  1295. *it = -1;
  1296. if (item >= 0 && item < _dircount) {
  1297. *it = item;
  1298. }
  1299. if (*it >= 0) return 2;
  1300. else return 0;
  1301. }
  1302. else {
  1303. *it = -1;
  1304. const int fsel_width = _fib_width - FAREAMRGL - FAREAMRGR - (llen < _dircount ? SCROLLBARW : 0);
  1305. const int t_s = FAREAMRGL + fsel_width - _fib_font_time_width - TEXTSEP - TEXTSEP;
  1306. const int t_t = FAREAMRGL + fsel_width - TEXTSEP - _fib_font_size_width - ((_columns & 2) ? ( _fib_font_time_width + TEXTSEP + TEXTSEP) : 0);
  1307. if (x >= fsel_width + FAREAMRGL) ;
  1308. else if ((_columns & 2) && x >= t_s) *it = 3;
  1309. else if ((_columns & 1) && x >= t_t) *it = 2;
  1310. else if (x >= FAREATEXTL + _fib_dir_indent - TEXTSEP) *it = 1;
  1311. if (*it >= 0) return 5;
  1312. else return 0;
  1313. }
  1314. }
  1315. // places list
  1316. if (_fib_show_places && y >= ltop && y < fbot && x > FAREAMRGB && x < FAREAMRGL - FAREAMRGB) {
  1317. const int item = (y - ltop) / _fib_font_vsep;
  1318. *it = -1;
  1319. if (item >= 0 && item < _placecnt) {
  1320. *it = item;
  1321. }
  1322. if (*it >= 0) return 6;
  1323. else return 0;
  1324. }
  1325. return 0;
  1326. // unused
  1327. (void)dpy;
  1328. }
  1329. static void fib_update_hover (Display *dpy, int need_expose, const int type, const int item) {
  1330. int hov_p = -1;
  1331. int hov_b = -1;
  1332. int hov_h = -1;
  1333. int hov_s = -1;
  1334. #ifdef LIST_ENTRY_HOVER
  1335. int hov_f = -1;
  1336. int hov_l = -1;
  1337. #endif
  1338. switch (type) {
  1339. case 1: hov_p = item; break;
  1340. case 3: hov_b = item; break;
  1341. case 4: hov_s = item; break;
  1342. case 5: hov_h = item; break;
  1343. #ifdef LIST_ENTRY_HOVER
  1344. case 6: hov_l = item; break;
  1345. case 2: hov_f = item; break;
  1346. #endif
  1347. default: break;
  1348. }
  1349. #ifdef LIST_ENTRY_HOVER
  1350. if (hov_f != _hov_f) { _hov_f = hov_f; need_expose = 1; }
  1351. if (hov_l != _hov_l) { _hov_l = hov_l; need_expose = 1; }
  1352. #endif
  1353. if (hov_b != _hov_b) { _hov_b = hov_b; need_expose = 1; }
  1354. if (hov_p != _hov_p) { _hov_p = hov_p; need_expose = 1; }
  1355. if (hov_h != _hov_h) { _hov_h = hov_h; need_expose = 1; }
  1356. if (hov_s != _hov_s) { _hov_s = hov_s; need_expose = 1; }
  1357. if (need_expose) {
  1358. fib_expose (dpy, _fib_win);
  1359. }
  1360. }
  1361. static void fib_motion (Display *dpy, int x, int y) {
  1362. int it = -1;
  1363. if (_scrl_my >= 0) {
  1364. const int sdiff = y - _scrl_my;
  1365. const int llen = (_fib_height - LISTBOT * _fib_font_vsep) / _fib_font_vsep;
  1366. const int fsel_height = 4 + llen * _fib_font_vsep;
  1367. const float sl = (fsel_height + _fib_font_vsep - (SCROLLBOXH + SCROLLBOXH)) / (float) _dircount;
  1368. int news = _scrl_mf + sdiff / sl;
  1369. if (news < 0) news = 0;
  1370. if (news >= (_dircount - llen)) news = _dircount - llen;
  1371. if (news != _scrl_f) {
  1372. _scrl_f = news;
  1373. fib_expose (dpy, _fib_win);
  1374. }
  1375. return;
  1376. }
  1377. const int type = fib_widget_at_pos (dpy, x, y, &it);
  1378. fib_update_hover (dpy, 0, type, it);
  1379. }
  1380. static void fib_mousedown (Display *dpy, int x, int y, int btn, unsigned long time) {
  1381. int it;
  1382. switch (fib_widget_at_pos (dpy, x, y, &it)) {
  1383. case 4: // scrollbar
  1384. if (btn == 1) {
  1385. _dblclk = 0;
  1386. if (it == 0) {
  1387. _scrl_my = y;
  1388. _scrl_mf = _scrl_f;
  1389. } else {
  1390. int llen = (_fib_height - LISTBOT * _fib_font_vsep) / _fib_font_vsep;
  1391. if (llen < 2) llen = 2;
  1392. int news = _scrl_f;
  1393. if (it == 1) {
  1394. news -= llen - 1;
  1395. } else {
  1396. news += llen - 1;
  1397. }
  1398. if (news < 0) news = 0;
  1399. if (news >= (_dircount - llen)) news = _dircount - llen;
  1400. if (news != _scrl_f && _scrl_y0 >= 0) {
  1401. assert (news >=0);
  1402. _scrl_f = news;
  1403. fib_update_hover (dpy, 1, 4, it);
  1404. }
  1405. }
  1406. }
  1407. break;
  1408. case 2: // file-list
  1409. if (btn == 4 || btn == 5) {
  1410. const int llen = (_fib_height - LISTBOT * _fib_font_vsep) / _fib_font_vsep;
  1411. int news = _scrl_f + ((btn == 4) ? - 1 : 1);
  1412. if (news < 0) news = 0;
  1413. if (news >= (_dircount - llen)) news = _dircount - llen;
  1414. if (news != _scrl_f && _scrl_y0 >= 0) {
  1415. assert (news >=0);
  1416. _scrl_f = news;
  1417. fib_update_hover (dpy, 1, 0, 0);
  1418. }
  1419. _dblclk = 0;
  1420. }
  1421. else if (btn == 1 && it >= 0 && it < _dircount) {
  1422. if (_fsel == it) {
  1423. if (time - _dblclk < DBLCLKTME) {
  1424. fib_open (dpy, it);
  1425. _dblclk = 0;
  1426. }
  1427. _dblclk = time;
  1428. } else {
  1429. fib_select (dpy, it);
  1430. _dblclk = time;
  1431. }
  1432. /*if (_fsel >= 0) {
  1433. if (!(_dirlist[_fsel].flags & 4));
  1434. }*/
  1435. }
  1436. break;
  1437. case 1: // paths
  1438. assert (_fsel < _dircount);
  1439. assert (it >= 0 && it < _pathparts);
  1440. {
  1441. int i = 0;
  1442. char path[1024] = "/";
  1443. while (++i <= it) {
  1444. strcat (path, _pathbtn[i].name);
  1445. strcat (path, "/");
  1446. }
  1447. char *sel = NULL;
  1448. if (i < _pathparts)
  1449. sel = strdup (_pathbtn[i].name);
  1450. else if (i == _pathparts && _fsel >= 0)
  1451. sel = strdup (_dirlist[_fsel].name);
  1452. fib_opendir (dpy, path, sel);
  1453. free (sel);
  1454. }
  1455. break;
  1456. case 3: // btn
  1457. if (btn == 1 && _btns[it]->callback) {
  1458. _btns[it]->callback (dpy);
  1459. }
  1460. break;
  1461. case 5: // sort
  1462. if (btn == 1) {
  1463. switch (it) {
  1464. case 1: if (_sort == 0) _sort = 1; else _sort = 0; break;
  1465. case 2: if (_sort == 2) _sort = 3; else _sort = 2; break;
  1466. case 3: if (_sort == 4) _sort = 5; else _sort = 4; break;
  1467. }
  1468. if (_fsel >= 0) {
  1469. assert (_dirlist && _dircount >= _fsel);
  1470. _dirlist[_fsel].flags &= ~2;
  1471. char *sel = strdup (_dirlist[_fsel].name);
  1472. fib_resort (sel);
  1473. free (sel);
  1474. } else {
  1475. fib_resort (NULL);
  1476. _fsel = -1;
  1477. }
  1478. fib_reset ();
  1479. _hov_h = it;
  1480. fib_select (dpy, _fsel);
  1481. }
  1482. break;
  1483. case 6:
  1484. if (btn == 1 && it >= 0 && it < _placecnt) {
  1485. fib_opendir (dpy, _placelist[it].path, NULL);
  1486. }
  1487. break;
  1488. default:
  1489. break;
  1490. }
  1491. }
  1492. static void fib_mouseup (Display *dpy, int x, int y, int btn, unsigned long time) {
  1493. _scrl_my = -1;
  1494. // unused
  1495. return; (void)dpy; (void)x; (void)y; (void)btn; (void)time;
  1496. }
  1497. static void add_place_raw (Display *dpy, const char *name, const char *path) {
  1498. _placelist = (FibPlace*) realloc (_placelist, (_placecnt + 1) * sizeof(FibPlace));
  1499. strcpy (_placelist[_placecnt].path, path);
  1500. strcpy (_placelist[_placecnt].name, name);
  1501. _placelist[_placecnt].flags = 0;
  1502. int sw = -1;
  1503. query_font_geometry (dpy, _fib_gc, name, &sw, NULL, NULL, NULL);
  1504. if (sw > _fib_place_width) {
  1505. _fib_place_width = sw;
  1506. }
  1507. ++_placecnt;
  1508. }
  1509. static int add_place_places (Display *dpy, const char *name, const char *url) {
  1510. char const * path;
  1511. struct stat fs;
  1512. int i;
  1513. if (!url || strlen (url) < 1) return -1;
  1514. if (!name || strlen (name) < 1) return -1;
  1515. if (url[0] == '/') {
  1516. path = url;
  1517. }
  1518. else if (!strncmp (url, "file:///", 8)) {
  1519. path = &url[7];
  1520. }
  1521. else {
  1522. return -1;
  1523. }
  1524. if (access (path, R_OK)) {
  1525. return -1;
  1526. }
  1527. if (stat (path, &fs)) {
  1528. return -1;
  1529. }
  1530. if (!S_ISDIR (fs.st_mode)) {
  1531. return -1;
  1532. }
  1533. for (i = 0; i < _placecnt; ++i) {
  1534. if (!strcmp (path, _placelist[i].path)) {
  1535. return -1;
  1536. }
  1537. }
  1538. add_place_raw (dpy, name, path);
  1539. return 0;
  1540. }
  1541. static int parse_gtk_bookmarks (Display *dpy, const char *fn) {
  1542. char tmp[1024];
  1543. if (access (fn, R_OK)) {
  1544. return -1;
  1545. }
  1546. FILE *bm = fopen (fn, "r");
  1547. if (!bm) return -1;
  1548. int found = 0;
  1549. while (fgets (tmp, sizeof(tmp), bm)
  1550. && strlen (tmp) > 1
  1551. && strlen (tmp) < sizeof(tmp))
  1552. {
  1553. char *s, *n;
  1554. tmp[strlen (tmp) - 1] = '\0'; // strip newline
  1555. if ((s = strchr (tmp, ' '))) {
  1556. *s = '\0';
  1557. n = strdup (++s);
  1558. decode_3986 (tmp);
  1559. if (!add_place_places (dpy, n, tmp)) {
  1560. ++found;
  1561. }
  1562. free (n);
  1563. } else if ((s = strrchr (tmp, '/'))) {
  1564. n = strdup (++s);
  1565. decode_3986 (tmp);
  1566. if (!add_place_places (dpy, n, tmp)) {
  1567. ++found;
  1568. }
  1569. free (n);
  1570. }
  1571. }
  1572. fclose (bm);
  1573. return found;
  1574. }
  1575. static const char *ignore_mountpoints[] = {
  1576. "/bin", "/boot", "/dev", "/etc",
  1577. "/lib", "/live", "/mnt", "/opt",
  1578. "/root", "/sbin", "/srv", "/tmp",
  1579. "/usr", "/var", "/proc", "/sbin",
  1580. "/net", "/sys"
  1581. };
  1582. static const char *ignore_fs[] = {
  1583. "auto", "autofs",
  1584. "debugfs", "devfs",
  1585. "devpts", "ecryptfs",
  1586. "fusectl", "kernfs",
  1587. "linprocfs", "proc",
  1588. "ptyfs", "rootfs",
  1589. "selinuxfs", "sysfs",
  1590. "tmpfs", "usbfs",
  1591. "nfsd", "rpc_pipefs",
  1592. };
  1593. static const char *ignore_devices[] = {
  1594. "binfmt_", "devpts",
  1595. "gvfs", "none",
  1596. "nfsd", "sunrpc",
  1597. "/dev/loop", "/dev/vn"
  1598. };
  1599. static int check_mount (const char *mountpoint, const char *fs, const char *device) {
  1600. size_t i;
  1601. if (!mountpoint || !fs || !device) return -1;
  1602. //printf("%s %s %s\n", mountpoint, fs, device);
  1603. for (i = 0 ; i < sizeof(ignore_mountpoints) / sizeof(char*); ++i) {
  1604. if (!strncmp (mountpoint, ignore_mountpoints[i], strlen (ignore_mountpoints[i]))) {
  1605. return 1;
  1606. }
  1607. }
  1608. if (!strncmp (mountpoint, "/home", 5)) {
  1609. return 1;
  1610. }
  1611. for (i = 0 ; i < sizeof(ignore_fs) / sizeof(char*); ++i) {
  1612. if (!strncmp (fs, ignore_fs[i], strlen (ignore_fs[i]))) {
  1613. return 1;
  1614. }
  1615. }
  1616. for (i = 0 ; i < sizeof(ignore_devices) / sizeof(char*); ++i) {
  1617. if (!strncmp (device, ignore_devices[i], strlen (ignore_devices[i]))) {
  1618. return 1;
  1619. }
  1620. }
  1621. return 0;
  1622. }
  1623. static int read_mtab (Display *dpy, const char *mtab) {
  1624. FILE *mt = fopen (mtab, "r");
  1625. if (!mt) return -1;
  1626. int found = 0;
  1627. struct mntent *mntent;
  1628. while ((mntent = getmntent (mt)) != NULL) {
  1629. char *s;
  1630. if (check_mount (mntent->mnt_dir, mntent->mnt_type, mntent->mnt_fsname))
  1631. continue;
  1632. if ((s = strrchr (mntent->mnt_dir, '/'))) {
  1633. ++s;
  1634. } else {
  1635. s = mntent->mnt_dir;
  1636. }
  1637. if (!add_place_places (dpy, s, mntent->mnt_dir)) {
  1638. ++found;
  1639. }
  1640. }
  1641. fclose (mt);
  1642. return found;
  1643. }
  1644. static void populate_places (Display *dpy) {
  1645. char tmp[1024];
  1646. int spacer = -1;
  1647. if (_placecnt > 0) return;
  1648. _fib_place_width = 0;
  1649. if (_recentcnt > 0) {
  1650. add_place_raw (dpy, "Recently Used", "");
  1651. _placelist[0].flags |= 4;
  1652. }
  1653. add_place_places (dpy, "Home", getenv ("HOME"));
  1654. if (getenv ("HOME")) {
  1655. strcpy (tmp, getenv ("HOME"));
  1656. strcat (tmp, "/Desktop");
  1657. add_place_places (dpy, "Desktop", tmp);
  1658. }
  1659. add_place_places (dpy, "Filesystem", "/");
  1660. if (_placecnt > 0) spacer = _placecnt -1;
  1661. if (strlen (_fib_cfg_custom_places) > 0) {
  1662. parse_gtk_bookmarks (dpy, _fib_cfg_custom_places);
  1663. }
  1664. if (read_mtab (dpy, "/proc/mounts") < 1) {
  1665. read_mtab (dpy, "/etc/mtab");
  1666. }
  1667. int parsed_bookmarks = 0;
  1668. if (!parsed_bookmarks && getenv ("HOME")) {
  1669. strcpy (tmp, getenv ("HOME"));
  1670. strcat (tmp, "/.gtk-bookmarks");
  1671. if (parse_gtk_bookmarks (dpy, tmp) > 0) {
  1672. parsed_bookmarks = 1;
  1673. }
  1674. }
  1675. if (!parsed_bookmarks && getenv ("XDG_CONFIG_HOME")) {
  1676. strcpy (tmp, getenv ("XDG_CONFIG_HOME"));
  1677. strcat (tmp, "/gtk-3.0/bookmarks");
  1678. if (parse_gtk_bookmarks (dpy, tmp) > 0) {
  1679. parsed_bookmarks = 1;
  1680. }
  1681. }
  1682. if (!parsed_bookmarks && getenv ("HOME")) {
  1683. strcpy (tmp, getenv ("HOME"));
  1684. strcat (tmp, "/.config/gtk-3.0/bookmarks");
  1685. if (parse_gtk_bookmarks (dpy, tmp) > 0) {
  1686. parsed_bookmarks = 1;
  1687. }
  1688. }
  1689. if (_fib_place_width > 0) {
  1690. _fib_place_width = MIN (_fib_place_width + TEXTSEP + _fib_dir_indent /*extra*/ , PLACESWMAX);
  1691. }
  1692. if (spacer > 0 && spacer < _placecnt -1) {
  1693. _placelist[ spacer ].flags |= 4;
  1694. }
  1695. }
  1696. static uint8_t font_err = 0;
  1697. static int x_error_handler (Display *d, XErrorEvent *e) {
  1698. font_err = 1;
  1699. return 0;
  1700. // unused
  1701. (void)d; (void)e;
  1702. }
  1703. int x_fib_show (Display *dpy, Window parent, int x, int y) {
  1704. if (_fib_win) {
  1705. XSetInputFocus (dpy, _fib_win, RevertToParent, CurrentTime);
  1706. return -1;
  1707. }
  1708. _status = 0;
  1709. _rv_open[0] = '\0';
  1710. Colormap colormap = DefaultColormap (dpy, DefaultScreen (dpy));
  1711. _c_gray1.flags= DoRed | DoGreen | DoBlue;
  1712. _c_gray0.red = _c_gray0.green = _c_gray0.blue = 61710; // 95% hover prelight
  1713. _c_gray1.red = _c_gray1.green = _c_gray1.blue = 60416; // 93% window bg, scrollbar-fg
  1714. _c_gray2.red = _c_gray2.green = _c_gray2.blue = 54016; // 83% button & list bg
  1715. _c_gray3.red = _c_gray3.green = _c_gray3.blue = 48640; // 75% heading + scrollbar-bg
  1716. _c_gray4.red = _c_gray4.green = _c_gray4.blue = 26112; // 40% prelight text, sep lines
  1717. _c_gray5.red = _c_gray5.green = _c_gray5.blue = 12800; // 20% 3D border
  1718. _c_gray6.red = _c_gray6.green = _c_gray6.blue = 6400; // 10% checkbox cross, sort triangles
  1719. if (!XAllocColor (dpy, colormap, &_c_gray0)) return -1;
  1720. if (!XAllocColor (dpy, colormap, &_c_gray1)) return -1;
  1721. if (!XAllocColor (dpy, colormap, &_c_gray2)) return -1;
  1722. if (!XAllocColor (dpy, colormap, &_c_gray3)) return -1;
  1723. if (!XAllocColor (dpy, colormap, &_c_gray4)) return -1;
  1724. if (!XAllocColor (dpy, colormap, &_c_gray5)) return -1;
  1725. if (!XAllocColor (dpy, colormap, &_c_gray6)) return -1;
  1726. XSetWindowAttributes attr;
  1727. memset (&attr, 0, sizeof(XSetWindowAttributes));
  1728. attr.border_pixel = _c_gray2.pixel;
  1729. attr.event_mask = ExposureMask | KeyPressMask
  1730. | ButtonPressMask | ButtonReleaseMask
  1731. | ConfigureNotify | StructureNotifyMask
  1732. | PointerMotionMask | LeaveWindowMask;
  1733. _fib_win = XCreateWindow (
  1734. dpy, DefaultRootWindow (dpy),
  1735. x, y, _fib_width, _fib_height,
  1736. 1, CopyFromParent, InputOutput, CopyFromParent,
  1737. CWEventMask | CWBorderPixel, &attr);
  1738. if (!_fib_win) { return 1; }
  1739. if (parent)
  1740. XSetTransientForHint (dpy, _fib_win, parent);
  1741. XStoreName (dpy, _fib_win, "Select File");
  1742. Atom wmDelete = XInternAtom (dpy, "WM_DELETE_WINDOW", True);
  1743. XSetWMProtocols (dpy, _fib_win, &wmDelete, 1);
  1744. _fib_gc = XCreateGC (dpy, _fib_win, 0, NULL);
  1745. XSetLineAttributes (dpy, _fib_gc, 1, LineSolid, CapButt, JoinMiter);
  1746. const char dl[1] = {1};
  1747. XSetDashes (dpy, _fib_gc, 0, dl, 1);
  1748. int (*handler)(Display *, XErrorEvent *) = XSetErrorHandler (&x_error_handler);
  1749. #define _XTESTFONT(FN) \
  1750. { \
  1751. font_err = 0; \
  1752. _fibfont = XLoadFont (dpy, FN); \
  1753. XSetFont (dpy, _fib_gc, _fibfont); \
  1754. XSync (dpy, False); \
  1755. }
  1756. font_err = 1;
  1757. if (getenv ("XJFONT")) _XTESTFONT (getenv ("XJFONT"));
  1758. if (font_err && strlen (_fib_cfg_custom_font) > 0) _XTESTFONT (_fib_cfg_custom_font);
  1759. if (font_err) _XTESTFONT ("-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*");
  1760. if (font_err) _XTESTFONT ("-*-verdana-medium-r-normal-*-12-*-*-*-*-*-*-*");
  1761. if (font_err) _XTESTFONT ("-misc-fixed-medium-r-normal-*-13-*-*-*-*-*-*-*");
  1762. if (font_err) _XTESTFONT ("-misc-fixed-medium-r-normal-*-12-*-*-*-*-*-*-*");
  1763. if (font_err) _fibfont = None;
  1764. XSync (dpy, False);
  1765. XSetErrorHandler (handler);
  1766. if (_fib_font_height == 0) { // 1st time only
  1767. query_font_geometry (dpy, _fib_gc, "D ", &_fib_dir_indent, NULL, NULL, NULL);
  1768. query_font_geometry (dpy, _fib_gc, "_", &_fib_spc_norm, NULL, NULL, NULL);
  1769. if (query_font_geometry (dpy, _fib_gc, "|0Yy", NULL, &_fib_font_height, &_fib_font_ascent, NULL)) {
  1770. XFreeGC (dpy, _fib_gc);
  1771. XDestroyWindow (dpy, _fib_win);
  1772. _fib_win = 0;
  1773. return -1;
  1774. }
  1775. _fib_font_height += 3;
  1776. _fib_font_ascent += 2;
  1777. _fib_font_vsep = _fib_font_height + 2;
  1778. }
  1779. populate_places (dpy);
  1780. strcpy (_btn_ok.text, "Open");
  1781. strcpy (_btn_cancel.text, "Cancel");
  1782. strcpy (_btn_filter.text, "List All Files");
  1783. strcpy (_btn_places.text, "Show Places");
  1784. strcpy (_btn_hidden.text, "Show Hidden");
  1785. _btn_ok.callback = &cb_open;
  1786. _btn_cancel.callback = &cb_cancel;
  1787. _btn_filter.callback = &cb_filter;
  1788. _btn_places.callback = &cb_places;
  1789. _btn_hidden.callback = &cb_hidden;
  1790. _btn_filter.flags |= 4;
  1791. _btn_places.flags |= 4;
  1792. _btn_hidden.flags |= 4;
  1793. if (!_fib_filter_function) {
  1794. _btn_filter.flags |= 8;
  1795. }
  1796. size_t i;
  1797. int btncnt = 0;
  1798. _btn_w = 0;
  1799. _btn_span = 0;
  1800. for (i = 0; i < sizeof(_btns) / sizeof(FibButton*); ++i) {
  1801. if (_btns[i]->flags & 8) { continue; }
  1802. query_font_geometry (dpy, _fib_gc, _btns[i]->text, &_btns[i]->tw, NULL, NULL, NULL);
  1803. if (_btns[i]->flags & 4) {
  1804. _btn_span += _btns[i]->tw + _fib_font_ascent + TEXTSEP;
  1805. } else {
  1806. ++btncnt;
  1807. if (_btns[i]->tw > _btn_w)
  1808. _btn_w = _btns[i]->tw;
  1809. }
  1810. }
  1811. _btn_w += BTNPADDING + BTNPADDING + TEXTSEP + TEXTSEP + TEXTSEP;
  1812. _btn_span += _btn_w * btncnt + DSEP * (i - 1) + FAREAMRGR + FAREAMRGB;
  1813. for (i = 0; i < sizeof(_btns) / sizeof(FibButton*); ++i) {
  1814. if (_btns[i]->flags & 8) { continue; }
  1815. if (_btns[i]->flags & 4) {
  1816. _btns[i]->xw = _btns[i]->tw + _fib_font_ascent + TEXTSEP;
  1817. } else {
  1818. _btns[i]->xw = _btn_w;
  1819. }
  1820. }
  1821. sync_button_states () ;
  1822. _fib_height = _fib_font_vsep * (15.8);
  1823. _fib_width = MAX (_btn_span, 440);
  1824. XResizeWindow (dpy, _fib_win, _fib_width, _fib_height);
  1825. XTextProperty x_wname, x_iname;
  1826. XSizeHints hints;
  1827. XWMHints wmhints;
  1828. hints.flags = PSize | PMinSize;
  1829. hints.min_width = _btn_span;
  1830. hints.min_height = 8 * _fib_font_vsep;
  1831. char *w_name = & _fib_cfg_title[0];
  1832. wmhints.input = True;
  1833. wmhints.flags = InputHint;
  1834. if (XStringListToTextProperty (&w_name, 1, &x_wname) &&
  1835. XStringListToTextProperty (&w_name, 1, &x_iname))
  1836. {
  1837. XSetWMProperties (dpy, _fib_win, &x_wname, &x_iname, NULL, 0, &hints, &wmhints, NULL);
  1838. XFree (x_wname.value);
  1839. XFree (x_iname.value);
  1840. }
  1841. XSetWindowBackground (dpy, _fib_win, _c_gray1.pixel);
  1842. _fib_mapped = 0;
  1843. XMapRaised (dpy, _fib_win);
  1844. if (!strlen (_cur_path) || !fib_opendir (dpy, _cur_path, NULL)) {
  1845. fib_opendir (dpy, getenv ("HOME") ? getenv ("HOME") : "/", NULL);
  1846. }
  1847. #if 0
  1848. XGrabPointer (dpy, _fib_win, True,
  1849. ButtonReleaseMask | ButtonPressMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask | StructureNotifyMask,
  1850. GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
  1851. XGrabKeyboard (dpy, _fib_win, True, GrabModeAsync, GrabModeAsync, CurrentTime);
  1852. //XSetInputFocus (dpy, parent, RevertToNone, CurrentTime);
  1853. #endif
  1854. _recentlock = 1;
  1855. return 0;
  1856. }
  1857. void x_fib_close (Display *dpy) {
  1858. if (!_fib_win) return;
  1859. XFreeGC (dpy, _fib_gc);
  1860. XDestroyWindow (dpy, _fib_win);
  1861. _fib_win = 0;
  1862. free (_dirlist);
  1863. _dirlist = NULL;
  1864. free (_pathbtn);
  1865. _pathbtn = NULL;
  1866. if (_fibfont != None) XUnloadFont (dpy, _fibfont);
  1867. _fibfont = None;
  1868. free (_placelist);
  1869. _placelist = NULL;
  1870. _dircount = 0;
  1871. _pathparts = 0;
  1872. _placecnt = 0;
  1873. if (_pixbuffer != None) XFreePixmap (dpy, _pixbuffer);
  1874. _pixbuffer = None;
  1875. Colormap colormap = DefaultColormap (dpy, DefaultScreen (dpy));
  1876. XFreeColors (dpy, colormap, &_c_gray0.pixel, 1, 0);
  1877. XFreeColors (dpy, colormap, &_c_gray1.pixel, 1, 0);
  1878. XFreeColors (dpy, colormap, &_c_gray2.pixel, 1, 0);
  1879. XFreeColors (dpy, colormap, &_c_gray3.pixel, 1, 0);
  1880. XFreeColors (dpy, colormap, &_c_gray4.pixel, 1, 0);
  1881. XFreeColors (dpy, colormap, &_c_gray5.pixel, 1, 0);
  1882. XFreeColors (dpy, colormap, &_c_gray6.pixel, 1, 0);
  1883. _recentlock = 0;
  1884. }
  1885. int x_fib_handle_events (Display *dpy, XEvent *event) {
  1886. if (!_fib_win) return 0;
  1887. if (_status) return 0;
  1888. if (event->xany.window != _fib_win) {
  1889. return 0;
  1890. }
  1891. switch (event->type) {
  1892. case MapNotify:
  1893. _fib_mapped = 1;
  1894. break;
  1895. case UnmapNotify:
  1896. _fib_mapped = 0;
  1897. break;
  1898. case LeaveNotify:
  1899. fib_update_hover (dpy, 1, 0, 0);
  1900. break;
  1901. case ClientMessage:
  1902. if (!strcmp (XGetAtomName (dpy, event->xclient.message_type), "WM_PROTOCOLS")) {
  1903. _status = -1;
  1904. }
  1905. break;
  1906. case ConfigureNotify:
  1907. if (
  1908. (event->xconfigure.width > 1 && event->xconfigure.height > 1)
  1909. &&
  1910. (event->xconfigure.width != _fib_width || event->xconfigure.height != _fib_height)
  1911. )
  1912. {
  1913. _fib_width = event->xconfigure.width;
  1914. _fib_height = event->xconfigure.height;
  1915. _fib_resized = 1;
  1916. }
  1917. break;
  1918. case Expose:
  1919. if (event->xexpose.count == 0) {
  1920. fib_expose (dpy, event->xany.window);
  1921. }
  1922. break;
  1923. case MotionNotify:
  1924. fib_motion (dpy, event->xmotion.x, event->xmotion.y);
  1925. if (event->xmotion.is_hint == NotifyHint) {
  1926. XGetMotionEvents (dpy, event->xany.window, CurrentTime, CurrentTime, NULL);
  1927. }
  1928. break;
  1929. case ButtonPress:
  1930. fib_mousedown (dpy, event->xbutton.x, event->xbutton.y, event->xbutton.button, event->xbutton.time);
  1931. break;
  1932. case ButtonRelease:
  1933. fib_mouseup (dpy, event->xbutton.x, event->xbutton.y, event->xbutton.button, event->xbutton.time);
  1934. break;
  1935. case KeyRelease:
  1936. break;
  1937. case KeyPress:
  1938. {
  1939. KeySym key;
  1940. char buf[100];
  1941. static XComposeStatus stat;
  1942. XLookupString (&event->xkey, buf, sizeof(buf), &key, &stat);
  1943. switch (key) {
  1944. case XK_Escape:
  1945. _status = -1;
  1946. break;
  1947. case XK_Up:
  1948. if (_fsel > 0) {
  1949. fib_select (dpy, _fsel - 1);
  1950. }
  1951. break;
  1952. case XK_Down:
  1953. if (_fsel < _dircount -1) {
  1954. fib_select ( dpy, _fsel + 1);
  1955. }
  1956. break;
  1957. case XK_Page_Up:
  1958. if (_fsel > 0) {
  1959. int llen = (_fib_height - LISTBOT * _fib_font_vsep) / _fib_font_vsep;
  1960. if (llen < 1) llen = 1; else --llen;
  1961. int fs = MAX (0, _fsel - llen);
  1962. fib_select ( dpy, fs);
  1963. }
  1964. break;
  1965. case XK_Page_Down:
  1966. if (_fsel < _dircount) {
  1967. int llen = (_fib_height - LISTBOT * _fib_font_vsep) / _fib_font_vsep;
  1968. if (llen < 1) llen = 1; else --llen;
  1969. int fs = MIN (_dircount - 1, _fsel + llen);
  1970. fib_select ( dpy, fs);
  1971. }
  1972. break;
  1973. case XK_Left:
  1974. if (_pathparts > 1) {
  1975. int i = 0;
  1976. char path[1024] = "/";
  1977. while (++i < _pathparts - 1) {
  1978. strcat (path, _pathbtn[i].name);
  1979. strcat (path, "/");
  1980. }
  1981. char *sel = strdup (_pathbtn[_pathparts-1].name);
  1982. fib_opendir (dpy, path, sel);
  1983. free (sel);
  1984. }
  1985. break;
  1986. case XK_Right:
  1987. if (_fsel >= 0 && _fsel < _dircount) {
  1988. if (_dirlist[_fsel].flags & 4) {
  1989. cb_open (dpy);
  1990. }
  1991. }
  1992. break;
  1993. case XK_Return:
  1994. cb_open (dpy);
  1995. break;
  1996. default:
  1997. if ((key >= XK_a && key <= XK_z) || (key >= XK_0 && key <= XK_9)) {
  1998. int i;
  1999. for (i = 0; i < _dircount; ++i) {
  2000. int j = (_fsel + i + 1) % _dircount;
  2001. char kcmp = _dirlist[j].name[0];
  2002. if (kcmp > 0x40 && kcmp <= 0x5A) kcmp |= 0x20;
  2003. if (kcmp == (char)key) {
  2004. fib_select ( dpy, j);
  2005. break;
  2006. }
  2007. }
  2008. }
  2009. break;
  2010. }
  2011. }
  2012. break;
  2013. }
  2014. if (_status) {
  2015. x_fib_close (dpy);
  2016. }
  2017. return _status;
  2018. }
  2019. int x_fib_status () {
  2020. return _status;
  2021. }
  2022. int x_fib_configure (int k, const char *v) {
  2023. if (_fib_win) { return -1; }
  2024. switch (k) {
  2025. case 0:
  2026. if (strlen (v) >= sizeof(_cur_path) -1) return -2;
  2027. if (strlen (v) < 1) return -2;
  2028. if (v[0] != '/') return -2;
  2029. if (strstr (v, "//")) return -2;
  2030. strncpy (_cur_path, v, sizeof(_cur_path));
  2031. break;
  2032. case 1:
  2033. if (strlen (v) >= sizeof(_fib_cfg_title) -1) return -2;
  2034. strncpy (_fib_cfg_title, v, sizeof(_fib_cfg_title));
  2035. break;
  2036. case 2:
  2037. if (strlen (v) >= sizeof(_fib_cfg_custom_font) -1) return -2;
  2038. strncpy (_fib_cfg_custom_font, v, sizeof(_fib_cfg_custom_font));
  2039. break;
  2040. case 3:
  2041. if (strlen (v) >= sizeof(_fib_cfg_custom_places) -1) return -2;
  2042. strncpy (_fib_cfg_custom_places, v, sizeof(_fib_cfg_custom_places));
  2043. break;
  2044. default:
  2045. return -2;
  2046. }
  2047. return 0;
  2048. }
  2049. int x_fib_cfg_buttons (int k, int v) {
  2050. if (_fib_win) { return -1; }
  2051. switch (k) {
  2052. case 1:
  2053. if (v < 0) {
  2054. _btn_hidden.flags |= 8;
  2055. } else {
  2056. _btn_hidden.flags &= ~8;
  2057. }
  2058. if (v == 1) {
  2059. _btn_hidden.flags |= 2;
  2060. _fib_hidden_fn = 1;
  2061. } else if (v == 0) {
  2062. _btn_hidden.flags &= 2;
  2063. _fib_hidden_fn = 0;
  2064. }
  2065. break;
  2066. case 2:
  2067. if (v < 0) {
  2068. _btn_places.flags |= 8;
  2069. } else {
  2070. _btn_places.flags &= ~8;
  2071. }
  2072. if (v == 1) {
  2073. _btn_places.flags |= 2;
  2074. _fib_show_places = 1;
  2075. } else if (v == 0) {
  2076. _btn_places.flags &= ~2;
  2077. _fib_show_places = 0;
  2078. }
  2079. break;
  2080. case 3:
  2081. // NB. filter button is automatically hidden
  2082. // IFF the filter-function is NULL.
  2083. if (v < 0) {
  2084. _btn_filter.flags |= 8;
  2085. } else {
  2086. _btn_filter.flags &= ~8;
  2087. }
  2088. if (v == 1) {
  2089. _btn_filter.flags &= ~2; // inverse - 'show all' = !filter
  2090. _fib_filter_fn = 1;
  2091. } else if (v == 0) {
  2092. _btn_filter.flags |= 2;
  2093. _fib_filter_fn = 0;
  2094. }
  2095. break;
  2096. default:
  2097. return -2;
  2098. }
  2099. return 0;
  2100. }
  2101. int x_fib_cfg_filter_callback (int (*cb)(const char*)) {
  2102. if (_fib_win) { return -1; }
  2103. _fib_filter_function = cb;
  2104. return 0;
  2105. }
  2106. char *x_fib_filename () {
  2107. if (_status > 0 && !_fib_win)
  2108. return strdup (_rv_open);
  2109. else
  2110. return NULL;
  2111. }
  2112. #endif // SOFD_HAVE_X11
  2113. #if defined(__clang__)
  2114. # pragma clang diagnostic pop
  2115. #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
  2116. # pragma GCC diagnostic pop
  2117. #endif
  2118. /* example usage */
  2119. #ifdef SOFD_TEST
  2120. static int fib_filter_movie_filename (const char *name) {
  2121. if (!_fib_filter_fn) return 1;
  2122. const int l3 = strlen (name) - 3;
  2123. const int l4 = l3 - 1;
  2124. const int l5 = l4 - 1;
  2125. const int l6 = l5 - 1;
  2126. const int l9 = l6 - 3;
  2127. if (
  2128. (l4 > 0 && (
  2129. !strcasecmp (&name[l4], ".avi")
  2130. || !strcasecmp (&name[l4], ".mov")
  2131. || !strcasecmp (&name[l4], ".ogg")
  2132. || !strcasecmp (&name[l4], ".ogv")
  2133. || !strcasecmp (&name[l4], ".mpg")
  2134. || !strcasecmp (&name[l4], ".mov")
  2135. || !strcasecmp (&name[l4], ".mp4")
  2136. || !strcasecmp (&name[l4], ".mkv")
  2137. || !strcasecmp (&name[l4], ".vob")
  2138. || !strcasecmp (&name[l4], ".asf")
  2139. || !strcasecmp (&name[l4], ".avs")
  2140. || !strcasecmp (&name[l4], ".dts")
  2141. || !strcasecmp (&name[l4], ".flv")
  2142. || !strcasecmp (&name[l4], ".m4v")
  2143. )) ||
  2144. (l5 > 0 && (
  2145. !strcasecmp (&name[l5], ".h264")
  2146. || !strcasecmp (&name[l5], ".webm")
  2147. )) ||
  2148. (l6 > 0 && (
  2149. !strcasecmp (&name[l6], ".dirac")
  2150. )) ||
  2151. (l9 > 0 && (
  2152. !strcasecmp (&name[l9], ".matroska")
  2153. )) ||
  2154. (l3 > 0 && (
  2155. !strcasecmp (&name[l3], ".dv")
  2156. || !strcasecmp (&name[l3], ".ts")
  2157. ))
  2158. )
  2159. {
  2160. return 1;
  2161. }
  2162. return 0;
  2163. }
  2164. int main (int argc, char **argv) {
  2165. Display* dpy = XOpenDisplay (0);
  2166. if (!dpy) return -1;
  2167. x_fib_cfg_filter_callback (fib_filter_movie_filename);
  2168. x_fib_configure (1, "Open Movie File");
  2169. x_fib_load_recent ("/tmp/sofd.recent");
  2170. x_fib_show (dpy, 0, 300, 300);
  2171. while (1) {
  2172. XEvent event;
  2173. while (XPending (dpy) > 0) {
  2174. XNextEvent (dpy, &event);
  2175. if (x_fib_handle_events (dpy, &event)) {
  2176. if (x_fib_status () > 0) {
  2177. char *fn = x_fib_filename ();
  2178. printf ("OPEN '%s'\n", fn);
  2179. x_fib_add_recent (fn, time (NULL));
  2180. free (fn);
  2181. }
  2182. }
  2183. }
  2184. if (x_fib_status ()) {
  2185. break;
  2186. }
  2187. usleep (80000);
  2188. }
  2189. x_fib_close (dpy);
  2190. x_fib_save_recent ("/tmp/sofd.recent");
  2191. x_fib_free_recent ();
  2192. XCloseDisplay (dpy);
  2193. return 0;
  2194. }
  2195. #endif