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