Mercurial > hg > pub > prymula > com
comparison DPF-Prymula-audioplugins/dpf/distrho/extra/sofd/libsofd.c @ 3:84e66ea83026
DPF-Prymula-audioplugins-0.231015-2
author | prymula <prymula76@outlook.com> |
---|---|
date | Mon, 16 Oct 2023 21:53:34 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
2:cf2cb71d31dd | 3:84e66ea83026 |
---|---|
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 |