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