aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config.h4
-rw-r--r--main.c446
-rw-r--r--options.c12
-rw-r--r--options.h1
-rw-r--r--thumbs.c188
-rw-r--r--thumbs.h62
-rw-r--r--util.h3
-rw-r--r--window.c87
-rw-r--r--window.h9
9 files changed, 614 insertions, 198 deletions
diff --git a/config.h b/config.h
index e140446..775e34c 100644
--- a/config.h
+++ b/config.h
@@ -6,6 +6,8 @@
/* default color to use for window background: *
* (see X(7) "COLOR NAMES" section for valid values) */
#define BG_COLOR "#999999"
+/* default color to use for selections: */
+#define SEL_COLOR "#0000BB"
/* how should images be scaled when they are loaded?: *
* (also controllable via -d/-s/-Z/-z options) *
@@ -19,3 +21,5 @@ static const float zoom_levels[] = {
12.5, 25.0, 50.0, 75.0,
100.0, 150.0, 200.0, 400.0, 800.0
};
+
+#define THUMB_SIZE 50
diff --git a/main.c b/main.c
index 5bd7b75..35326fd 100644
--- a/main.c
+++ b/main.c
@@ -22,6 +22,7 @@
#include <dirent.h>
#include <sys/select.h>
#include <sys/stat.h>
+#include <sys/time.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
@@ -29,15 +30,23 @@
#include "image.h"
#include "options.h"
+#include "thumbs.h"
#include "util.h"
#include "window.h"
+typedef enum appmode_e {
+ MODE_NORMAL = 0,
+ MODE_THUMBS
+} appmode_t;
+
void update_title();
int check_append(const char*);
void read_dir_rec(const char*);
void run();
+appmode_t mode;
img_t img;
+tns_t tns;
win_t win;
#define DNAME_CNT 512
@@ -54,6 +63,7 @@ void cleanup() {
if (!in++) {
img_free(&img);
+ tns_free(&tns, &win);
win_close(&win);
}
}
@@ -119,12 +129,21 @@ int main(int argc, char **argv) {
win_open(&win);
img_init(&img, &win);
- load_image();
- img_render(&img, &win);
- update_title();
+ if (options->thumbnails)
+ tns_init(&tns, filecnt);
- run();
+ if (options->thumbnails == 2) {
+ mode = MODE_THUMBS;
+ win_clear(&win);
+ win_draw(&win);
+ } else {
+ mode = MODE_NORMAL;
+ load_image();
+ img_render(&img, &win);
+ }
+ update_title();
+ run();
cleanup();
return 0;
@@ -135,15 +154,21 @@ void update_title() {
float size;
const char *unit;
- if (img.valid) {
- size = filesize;
- size_readable(&size, &unit);
- n = snprintf(win_title, TITLE_LEN, "sxiv: [%d/%d] <%d%%> (%.2f%s) %s",
- fileidx + 1, filecnt, (int) (img.zoom * 100.0), size, unit,
- filenames[fileidx]);
+ if (mode == MODE_THUMBS) {
+ n = snprintf(win_title, TITLE_LEN, "sxiv: [%d/%d] %s",
+ tns.cnt ? tns.sel + 1 : 0, tns.cnt,
+ tns.cnt ? filenames[tns.sel] : "");
} else {
- n = snprintf(win_title, TITLE_LEN, "sxiv: [%d/%d] broken: %s",
- fileidx + 1, filecnt, filenames[fileidx]);
+ if (img.valid) {
+ size = filesize;
+ size_readable(&size, &unit);
+ n = snprintf(win_title, TITLE_LEN, "sxiv: [%d/%d] <%d%%> (%.2f%s) %s",
+ fileidx + 1, filecnt, (int) (img.zoom * 100.0), size, unit,
+ filenames[fileidx]);
+ } else {
+ n = snprintf(win_title, TITLE_LEN, "sxiv: [%d/%d] broken: %s",
+ fileidx + 1, filecnt, filenames[fileidx]);
+ }
}
if (n >= TITLE_LEN) {
@@ -233,12 +258,21 @@ void read_dir_rec(const char *dirname) {
int timeout;
int mox, moy;
+void redraw() {
+ if (mode == MODE_NORMAL)
+ img_render(&img, &win);
+ else
+ tns_render(&tns, &win);
+ update_title();
+ timeout = 0;
+}
+
void on_keypress(XKeyEvent *kev) {
int x, y;
unsigned int w, h;
char key;
KeySym ksym;
- int changed;
+ int changed, sel;
if (!kev)
return;
@@ -246,6 +280,147 @@ void on_keypress(XKeyEvent *kev) {
XLookupString(kev, &key, 1, &ksym, NULL);
changed = 0;
+ if (mode == MODE_NORMAL) {
+ switch (ksym) {
+ /* navigate image list */
+ case XK_n:
+ case XK_space:
+ if (fileidx + 1 < filecnt) {
+ ++fileidx;
+ changed = load_image();
+ }
+ break;
+ case XK_p:
+ case XK_BackSpace:
+ if (fileidx > 0) {
+ --fileidx;
+ changed = load_image();
+ }
+ break;
+ case XK_bracketleft:
+ if (fileidx != 0) {
+ fileidx = MAX(0, fileidx - 10);
+ changed = load_image();
+ }
+ break;
+ case XK_bracketright:
+ if (fileidx != filecnt - 1) {
+ fileidx = MIN(fileidx + 10, filecnt - 1);
+ changed = load_image();
+ }
+ break;
+ case XK_g:
+ if (fileidx != 0) {
+ fileidx = 0;
+ changed = load_image();
+ }
+ break;
+ case XK_G:
+ if (fileidx != filecnt - 1) {
+ fileidx = filecnt - 1;
+ changed = load_image();
+ }
+ break;
+
+ /* zooming */
+ case XK_plus:
+ case XK_equal:
+ changed = img_zoom_in(&img);
+ break;
+ case XK_minus:
+ changed = img_zoom_out(&img);
+ break;
+ case XK_0:
+ changed = img_zoom(&img, 1.0);
+ break;
+ case XK_w:
+ if ((changed = img_fit_win(&img, &win)))
+ img_center(&img, &win);
+ break;
+
+ /* panning */
+ case XK_h:
+ case XK_Left:
+ changed = img_pan(&img, &win, PAN_LEFT);
+ break;
+ case XK_j:
+ case XK_Down:
+ changed = img_pan(&img, &win, PAN_DOWN);
+ break;
+ case XK_k:
+ case XK_Up:
+ changed = img_pan(&img, &win, PAN_UP);
+ break;
+ case XK_l:
+ case XK_Right:
+ changed = img_pan(&img, &win, PAN_RIGHT);
+ break;
+
+ /* rotation */
+ case XK_less:
+ img_rotate_left(&img, &win);
+ changed = 1;
+ break;
+ case XK_greater:
+ img_rotate_right(&img, &win);
+ changed = 1;
+ break;
+
+ /* control window */
+ case XK_W:
+ x = win.x + img.x;
+ y = win.y + img.y;
+ w = img.w * img.zoom;
+ h = img.h * img.zoom;
+ if ((changed = win_moveresize(&win, x, y, w, h))) {
+ img.x = x - win.x;
+ img.y = y - win.y;
+ }
+ break;
+
+ /* miscellaneous */
+ case XK_a:
+ img_toggle_antialias(&img);
+ changed = 1;
+ break;
+ case XK_r:
+ changed = load_image();
+ break;
+ }
+ } else {
+ /* thumbnail mode */
+ sel = tns.sel;
+
+ switch (ksym) {
+ /* open selected image */
+ case XK_Return:
+ fileidx = sel;
+ load_image();
+ mode = MODE_NORMAL;
+ changed = 1;
+ break;
+
+ /* move selection */
+ case XK_h:
+ case XK_Left:
+ tns_move_selection(&tns, &win, MOVE_LEFT);
+ break;
+ case XK_j:
+ case XK_Down:
+ tns_move_selection(&tns, &win, MOVE_DOWN);
+ break;
+ case XK_k:
+ case XK_Up:
+ tns_move_selection(&tns, &win, MOVE_UP);
+ break;
+ case XK_l:
+ case XK_Right:
+ tns_move_selection(&tns, &win, MOVE_RIGHT);
+ break;
+ }
+ }
+
+ /* common key mappings */
switch (ksym) {
case XK_Escape:
cleanup();
@@ -253,126 +428,18 @@ void on_keypress(XKeyEvent *kev) {
case XK_q:
cleanup();
exit(0);
-
- /* navigate image list */
- case XK_n:
- case XK_space:
- if (fileidx + 1 < filecnt) {
- ++fileidx;
- changed = load_image();
- }
- break;
- case XK_p:
- case XK_BackSpace:
- if (fileidx > 0) {
- --fileidx;
- changed = load_image();
- }
- break;
- case XK_bracketleft:
- if (fileidx != 0) {
- fileidx = MAX(0, fileidx - 10);
- changed = load_image();
- }
- break;
- case XK_bracketright:
- if (fileidx != filecnt - 1) {
- fileidx = MIN(fileidx + 10, filecnt - 1);
- changed = load_image();
- }
- break;
- case XK_g:
- if (fileidx != 0) {
- fileidx = 0;
- changed = load_image();
- }
- break;
- case XK_G:
- if (fileidx != filecnt - 1) {
- fileidx = filecnt - 1;
- changed = load_image();
- }
- break;
-
- /* zooming */
- case XK_plus:
- case XK_equal:
- changed = img_zoom_in(&img);
- break;
- case XK_minus:
- changed = img_zoom_out(&img);
- break;
- case XK_0:
- changed = img_zoom(&img, 1.0);
- break;
- case XK_w:
- if ((changed = img_fit_win(&img, &win)))
- img_center(&img, &win);
- break;
-
- /* panning */
- case XK_h:
- case XK_Left:
- changed = img_pan(&img, &win, PAN_LEFT);
- break;
- case XK_j:
- case XK_Down:
- changed = img_pan(&img, &win, PAN_DOWN);
- break;
- case XK_k:
- case XK_Up:
- changed = img_pan(&img, &win, PAN_UP);
- break;
- case XK_l:
- case XK_Right:
- changed = img_pan(&img, &win, PAN_RIGHT);
- break;
-
- /* rotation */
- case XK_less:
- img_rotate_left(&img, &win);
- changed = 1;
- break;
- case XK_greater:
- img_rotate_right(&img, &win);
- changed = 1;
- break;
-
- /* control window */
case XK_f:
win_toggle_fullscreen(&win);
/* render on next configurenotify */
break;
- case XK_W:
- x = win.x + img.x;
- y = win.y + img.y;
- w = img.w * img.zoom;
- h = img.h * img.zoom;
- if ((changed = win_moveresize(&win, x, y, w, h))) {
- img.x = x - win.x;
- img.y = y - win.y;
- }
- break;
-
- /* miscellaneous */
- case XK_a:
- img_toggle_antialias(&img);
- changed = 1;
- break;
- case XK_r:
- changed = load_image();
- break;
}
- if (changed) {
- img_render(&img, &win);
- update_title();
- timeout = 0;
- }
+ if (changed)
+ redraw();
}
void on_buttonpress(XButtonEvent *bev) {
- int changed;
+ int changed, sel;
unsigned int mask;
if (!bev)
@@ -381,53 +448,65 @@ void on_buttonpress(XButtonEvent *bev) {
mask = CLEANMASK(bev->state);
changed = 0;
- switch (bev->button) {
- case Button1:
- if (fileidx + 1 < filecnt) {
- ++fileidx;
- changed = load_image();
- }
- break;
- case Button2:
- mox = bev->x;
- moy = bev->y;
- win_set_cursor(&win, CURSOR_HAND);
- break;
- case Button3:
- if (fileidx > 0) {
- --fileidx;
- changed = load_image();
- }
- break;
- case Button4:
- if (mask == ControlMask)
- changed = img_zoom_in(&img);
- else if (mask == ShiftMask)
+ if (mode == MODE_NORMAL) {
+ switch (bev->button) {
+ case Button1:
+ if (fileidx + 1 < filecnt) {
+ ++fileidx;
+ changed = load_image();
+ }
+ break;
+ case Button2:
+ mox = bev->x;
+ moy = bev->y;
+ win_set_cursor(&win, CURSOR_HAND);
+ break;
+ case Button3:
+ if (fileidx > 0) {
+ --fileidx;
+ changed = load_image();
+ }
+ break;
+ case Button4:
+ if (mask == ControlMask)
+ changed = img_zoom_in(&img);
+ else if (mask == ShiftMask)
+ changed = img_pan(&img, &win, PAN_LEFT);
+ else
+ changed = img_pan(&img, &win, PAN_UP);
+ break;
+ case Button5:
+ if (mask == ControlMask)
+ changed = img_zoom_out(&img);
+ else if (mask == ShiftMask)
+ changed = img_pan(&img, &win, PAN_RIGHT);
+ else
+ changed = img_pan(&img, &win, PAN_DOWN);
+ break;
+ case 6:
changed = img_pan(&img, &win, PAN_LEFT);
- else
- changed = img_pan(&img, &win, PAN_UP);
- break;
- case Button5:
- if (mask == ControlMask)
- changed = img_zoom_out(&img);
- else if (mask == ShiftMask)
+ break;
+ case 7:
changed = img_pan(&img, &win, PAN_RIGHT);
- else
- changed = img_pan(&img, &win, PAN_DOWN);
- break;
- case 6:
- changed = img_pan(&img, &win, PAN_LEFT);
- break;
- case 7:
- changed = img_pan(&img, &win, PAN_RIGHT);
- break;
+ break;
+ }
+ } else {
+ /* thumbnail mode */
+ switch (bev->button) {
+ case Button1:
+ if ((sel = tns_translate(&tns, bev->x, bev->y)) >= 0) {
+ fileidx = sel;
+ load_image();
+ mode = MODE_NORMAL;
+ changed = 1;
+ break;
+ }
+ break;
+ }
}
- if (changed) {
- img_render(&img, &win);
- update_title();
- timeout = 0;
- }
+ if (changed)
+ redraw();
}
void on_motionnotify(XMotionEvent *mev) {
@@ -446,23 +525,39 @@ void on_motionnotify(XMotionEvent *mev) {
void run() {
int xfd;
fd_set fds;
- struct timeval t;
+ struct timeval t, t0;
XEvent ev;
timeout = 0;
while (1) {
- if (timeout) {
+ if (mode == MODE_THUMBS && tns.cnt < filecnt) {
+ win_set_cursor(&win, CURSOR_WATCH);
+ gettimeofday(&t0, 0);
+
+ while (!XPending(win.env.dpy) && tns.cnt < filecnt) {
+ tns_load(&tns, &win, filenames[tns.cnt]);
+ gettimeofday(&t, 0);
+ if (TV_TO_DOUBLE(t) - TV_TO_DOUBLE(t0) >= 0.25)
+ break;
+ }
+ if (tns.cnt == filecnt)
+ win_set_cursor(&win, CURSOR_ARROW);
+ if (!XPending(win.env.dpy)) {
+ redraw();
+ continue;
+ } else {
+ timeout = 75000;
+ }
+ } else if (timeout) {
t.tv_sec = 0;
t.tv_usec = timeout;
xfd = ConnectionNumber(win.env.dpy);
FD_ZERO(&fds);
FD_SET(xfd, &fds);
-
- if (!XPending(win.env.dpy) && !select(xfd + 1, &fds, 0, 0, &t)) {
- img_render(&img, &win);
- timeout = 0;
- }
+
+ if (!XPending(win.env.dpy) && !select(xfd + 1, &fds, 0, 0, &t))
+ redraw();
}
if (!XNextEvent(win.env.dpy, &ev)) {
@@ -482,8 +577,9 @@ void run() {
break;
case ConfigureNotify:
if (win_configure(&win, &ev.xconfigure)) {
- img.checkpan = 1;
timeout = 75000;
+ if (mode == MODE_NORMAL)
+ img.checkpan = 1;
}
break;
case ClientMessage:
diff --git a/options.c b/options.c
index 286d0b6..ca3be0c 100644
--- a/options.c
+++ b/options.c
@@ -25,12 +25,13 @@
#include "config.h"
#include "options.h"
+#include "util.h"
options_t _options;
const options_t *options = (const options_t*) &_options;
void print_usage() {
- printf("usage: sxiv [-dFfhpqrsvZ] [-g GEOMETRY] [-z ZOOM] FILES...\n");
+ printf("usage: sxiv [-dFfhpqrsTtvZ] [-g GEOMETRY] [-z ZOOM] FILES...\n");
}
void print_version() {
@@ -44,6 +45,7 @@ void parse_options(int argc, char **argv) {
_options.scalemode = SCALE_MODE;
_options.zoom = 1.0;
_options.aa = 1;
+ _options.thumbnails = 0;
_options.fixed = 0;
_options.fullscreen = 0;
@@ -52,7 +54,7 @@ void parse_options(int argc, char **argv) {
_options.quiet = 0;
_options.recursive = 0;
- while ((opt = getopt(argc, argv, "dFfg:hpqrsvZz:")) != -1) {
+ while ((opt = getopt(argc, argv, "dFfg:hpqrsTtvZz:")) != -1) {
switch (opt) {
case '?':
print_usage();
@@ -84,6 +86,12 @@ void parse_options(int argc, char **argv) {
case 's':
_options.scalemode = SCALE_FIT;
break;
+ case 'T':
+ _options.thumbnails = 2;
+ break;
+ case 't':
+ _options.thumbnails = MAX(_options.thumbnails, 1);
+ break;
case 'v':
print_version();
exit(0);
diff --git a/options.h b/options.h
index fd2e0bd..246ddb7 100644
--- a/options.h
+++ b/options.h
@@ -29,6 +29,7 @@ typedef struct options_s {
scalemode_t scalemode;
float zoom;
unsigned char aa;
+ unsigned char thumbnails;
unsigned char fixed;
unsigned char fullscreen;
diff --git a/thumbs.c b/thumbs.c
new file mode 100644
index 0000000..25a7fc1
--- /dev/null
+++ b/thumbs.c
@@ -0,0 +1,188 @@
+/* sxiv: thumbs.c
+ * Copyright (c) 2011 Bert Muennich <muennich at informatik.hu-berlin.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <Imlib2.h>
+
+#include "config.h"
+#include "thumbs.h"
+#include "util.h"
+
+extern Imlib_Image *im_broken;
+const int thumb_dim = THUMB_SIZE + 10;
+
+void tns_init(tns_t *tns, int cnt) {
+ if (!tns)
+ return;
+
+ tns->cnt = tns->first = tns->sel = 0;
+ tns->thumbs = (thumb_t*) s_malloc(cnt * sizeof(thumb_t));
+}
+
+void tns_free(tns_t *tns, win_t *win) {
+ int i;
+
+ if (!tns)
+ return;
+
+ for (i = 0; i < tns->cnt; ++i)
+ win_free_pixmap(win, tns->thumbs[i].pm);
+
+ free(tns->thumbs);
+ tns->thumbs = NULL;
+}
+
+void tns_load(tns_t *tns, win_t *win, const char *filename) {
+ int w, h;
+ float z, zw, zh;
+ thumb_t *t;
+ Imlib_Image *im;
+
+ if (!tns || !win || !filename)
+ return;
+
+ if ((im = imlib_load_image(filename)))
+ imlib_context_set_image(im);
+ else
+ imlib_context_set_image(im_broken);
+
+ w = imlib_image_get_width();
+ h = imlib_image_get_height();
+ zw = (float) THUMB_SIZE / (float) w;
+ zh = (float) THUMB_SIZE / (float) h;
+ z = MIN(zw, zh);
+ if (!im && z > 1.0)
+ z = 1.0;
+
+ t = &tns->thumbs[tns->cnt++];
+ t->w = z * w;
+ t->h = z * h;
+
+ t->pm = win_create_pixmap(win, t->w, t->h);
+ imlib_context_set_drawable(t->pm);
+ imlib_render_image_part_on_drawable_at_size(0, 0, w, h,
+ 0, 0, t->w, t->h);
+ imlib_free_image();
+}
+
+void tns_render(tns_t *tns, win_t *win) {
+ int i, cnt, x, y;
+
+ if (!tns || !win)
+ return;
+
+ tns->cols = MAX(1, win->w / thumb_dim);
+ tns->rows = MAX(1, win->h / thumb_dim);
+
+ cnt = tns->cols * tns->rows;
+ if (tns->first && tns->first + cnt > tns->cnt)
+ tns->first = MAX(0, tns->cnt - cnt);
+ cnt = MIN(tns->first + cnt, tns->cnt);
+
+ win_clear(win);
+
+ x = y = 5;
+ i = tns->first;
+
+ while (i < cnt) {
+ tns->thumbs[i].x = x + (THUMB_SIZE - tns->thumbs[i].w) / 2;
+ tns->thumbs[i].y = y + (THUMB_SIZE - tns->thumbs[i].h) / 2;
+ win_draw_pixmap(win, tns->thumbs[i].pm, tns->thumbs[i].x,
+ tns->thumbs[i].y, tns->thumbs[i].w, tns->thumbs[i].h);
+ if (++i % tns->cols == 0) {
+ x = 5;
+ y += thumb_dim;
+ } else {
+ x += thumb_dim;
+ }
+ }
+
+ tns_highlight(tns, win, -1);
+}
+
+void tns_highlight(tns_t *tns, win_t *win, int old) {
+ thumb_t *t;
+
+ if (!tns || !win)
+ return;
+
+ if (old >= 0 && old < tns->cnt) {
+ t = &tns->thumbs[old];
+ win_draw_rect(win, t->x - 2, t->y - 2, t->w + 4, t->h + 4, False);
+ }
+ if (tns->sel < tns->cnt) {
+ t = &tns->thumbs[tns->sel];
+ win_draw_rect(win, t->x - 2, t->y - 2, t->w + 4, t->h + 4, True);
+ }
+
+ win_draw(win);
+}
+
+void tns_move_selection(tns_t *tns, win_t *win, movedir_t dir) {
+ int sel;
+
+ if (!tns || !win)
+ return;
+
+ sel = tns->sel;
+
+ switch (dir) {
+ case MOVE_LEFT:
+ if (sel % tns->cols > 0) {
+ --tns->sel;
+ tns_highlight(tns, win, sel);
+ }
+ break;
+ case MOVE_RIGHT:
+ if (sel % tns->cols < tns->cols - 1 && sel < tns->cnt - 1) {
+ ++tns->sel;
+ tns_highlight(tns, win, sel);
+ }
+ break;
+ case MOVE_UP:
+ if (sel / tns->cols > 0) {
+ tns->sel -= tns->cols;
+ tns_highlight(tns, win, sel);
+ }
+ break;
+ case MOVE_DOWN:
+ if (sel / tns->cols < tns->rows - 1 && sel + tns->cols < tns->cnt) {
+ tns->sel += tns->cols;
+ tns_highlight(tns, win, sel);
+ }
+ break;
+ }
+}
+
+int tns_translate(tns_t *tns, int x, int y) {
+ int n;
+ thumb_t *t;
+
+ if (!tns || x < 5 || y < 5)
+ return -1;
+
+ if ((n = y / thumb_dim * tns-> cols + x / thumb_dim) < tns->cnt) {
+ t = &tns->thumbs[n];
+ if (x > t->x && x < t->x + t->w && y > t->y && y < t->y + t->h)
+ return n;
+ }
+
+ return -1;
+}
diff --git a/thumbs.h b/thumbs.h
new file mode 100644
index 0000000..70d8b3c
--- /dev/null
+++ b/thumbs.h
@@ -0,0 +1,62 @@
+/* sxiv: thumbs.h
+ * Copyright (c) 2011 Bert Muennich <muennich at informatik.hu-berlin.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef THUMBS_H
+#define THUMBS_H
+
+#include "window.h"
+
+typedef enum movedir_e {
+ MOVE_LEFT = 0,
+ MOVE_RIGHT,
+ MOVE_UP,
+ MOVE_DOWN
+} movedir_t;
+
+typedef struct thumb_s {
+ Pixmap pm;
+ int x;
+ int y;
+ int w;
+ int h;
+} thumb_t;
+
+typedef struct tns_s {
+ thumb_t *thumbs;
+ int cnt;
+ int cols;
+ int rows;
+ int first;
+ int sel;
+} tns_t;
+
+extern const int thumb_dim;
+
+void tns_init(tns_t*, int);
+void tns_free(tns_t*, win_t*);
+
+void tns_load(tns_t*, win_t*, const char*);
+
+void tns_render(tns_t*, win_t*);
+void tns_highlight(tns_t*, win_t*, int);
+
+void tns_move_selection(tns_t*, win_t*, movedir_t);
+
+int tns_translate(tns_t*, int, int);
+
+#endif /* THUMBS_H */
diff --git a/util.h b/util.h
index 2caf347..f1db6b8 100644
--- a/util.h
+++ b/util.h
@@ -27,6 +27,9 @@
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define LEN(a) (sizeof(a) / sizeof(a[0]))
+#define TV_TO_DOUBLE(x) ((double) ((x).tv_sec) + 0.000001 * \
+ (double) ((x).tv_usec))
+
void* s_malloc(size_t);
void* s_realloc(void*, size_t);
diff --git a/window.c b/window.c
index 76a8e87..857b3b5 100644
--- a/window.c
+++ b/window.c
@@ -26,9 +26,10 @@
#include "util.h"
#include "window.h"
-static Cursor arrow;
-static Cursor hand;
-static GC bgc;
+static Cursor carrow;
+static Cursor chand;
+static Cursor cwatch;
+static GC gc;
Atom wm_delete_win;
@@ -49,7 +50,8 @@ void win_set_sizehints(win_t *win) {
void win_open(win_t *win) {
win_env_t *e;
XClassHint classhint;
- XColor bgcol;
+ XColor col;
+ XGCValues gcval;
int gmask;
if (!win)
@@ -66,13 +68,18 @@ void win_open(win_t *win) {
e->cmap = DefaultColormap(e->dpy, e->scr);
e->depth = DefaultDepth(e->dpy, e->scr);
- if (!XAllocNamedColor(e->dpy, DefaultColormap(e->dpy, e->scr), BG_COLOR,
- &bgcol, &bgcol))
+ if (XAllocNamedColor(e->dpy, DefaultColormap(e->dpy, e->scr), BG_COLOR,
+ &col, &col))
+ win->bgcol = col.pixel;
+ else
+ die("could not allocate color: %s", BG_COLOR);
+ if (XAllocNamedColor(e->dpy, DefaultColormap(e->dpy, e->scr), SEL_COLOR,
+ &col, &col))
+ win->selcol = col.pixel;
+ else
die("could not allocate color: %s", BG_COLOR);
- win->bgcol = bgcol.pixel;
win->pm = 0;
-
win->fullscreen = 0;
/* determine window offsets, width & height */
@@ -107,10 +114,12 @@ void win_open(win_t *win) {
XSelectInput(e->dpy, win->xwin, StructureNotifyMask | KeyPressMask |
ButtonPressMask | ButtonReleaseMask | Button2MotionMask);
- arrow = XCreateFontCursor(e->dpy, XC_left_ptr);
- hand = XCreateFontCursor(e->dpy, XC_fleur);
+ carrow = XCreateFontCursor(e->dpy, XC_left_ptr);
+ chand = XCreateFontCursor(e->dpy, XC_fleur);
+ cwatch = XCreateFontCursor(e->dpy, XC_watch);
- bgc = XCreateGC(e->dpy, win->xwin, 0, None);
+ gcval.line_width = 2;
+ gc = XCreateGC(e->dpy, win->xwin, GCLineWidth, &gcval);
win_set_title(win, "sxiv");
@@ -135,10 +144,11 @@ void win_close(win_t *win) {
if (!win)
return;
- XFreeCursor(win->env.dpy, arrow);
- XFreeCursor(win->env.dpy, hand);
+ XFreeCursor(win->env.dpy, carrow);
+ XFreeCursor(win->env.dpy, chand);
+ XFreeCursor(win->env.dpy, cwatch);
- XFreeGC(win->env.dpy, bgc);
+ XFreeGC(win->env.dpy, gc);
XDestroyWindow(win->env.dpy, win->xwin);
XCloseDisplay(win->env.dpy);
@@ -211,6 +221,18 @@ void win_toggle_fullscreen(win_t *win) {
SubstructureNotifyMask, &ev);
}
+Pixmap win_create_pixmap(win_t *win, int w, int h) {
+ if (!win)
+ return 0;
+
+ return XCreatePixmap(win->env.dpy, win->xwin, w, h, win->env.depth);
+}
+
+void win_free_pixmap(win_t *win, Pixmap pm) {
+ if (win && pm)
+ XFreePixmap(win->env.dpy, pm);
+}
+
void win_clear(win_t *win) {
win_env_t *e;
XGCValues gcval;
@@ -219,15 +241,37 @@ void win_clear(win_t *win) {
return;
e = &win->env;
-
+ gcval.foreground = win->fullscreen ? BlackPixel(e->dpy, e->scr) :
+ win->bgcol;
if (win->pm)
XFreePixmap(e->dpy, win->pm);
win->pm = XCreatePixmap(e->dpy, win->xwin, e->scrw, e->scrh, e->depth);
- gcval.foreground = win->fullscreen ? BlackPixel(e->dpy, e->scr) : win->bgcol;
- XChangeGC(e->dpy, bgc, GCForeground, &gcval);
+ XChangeGC(e->dpy, gc, GCForeground, &gcval);
+ XFillRectangle(e->dpy, win->pm, gc, 0, 0, e->scrw, e->scrh);
+}
- XFillRectangle(e->dpy, win->pm, bgc, 0, 0, e->scrw, e->scrh);
+void win_draw_pixmap(win_t *win, Pixmap pm, int x, int y, int w, int h) {
+ if (win)
+ XCopyArea(win->env.dpy, pm, win->pm, gc, 0, 0, w, h, x, y);
+}
+
+void win_draw_rect(win_t *win, int x, int y, int w, int h, Bool sel) {
+ win_env_t *e;
+ XGCValues gcval;
+
+ if (!win)
+ return;
+
+ e = &win->env;
+
+ if (sel)
+ gcval.foreground = win->selcol;
+ else
+ gcval.foreground = win->fullscreen ? BlackPixel(e->dpy, e->scr) :
+ win->bgcol;
+ XChangeGC(e->dpy, gc, GCForeground, &gcval);
+ XDrawRectangle(e->dpy, win->pm, gc, x, y, w, h);
}
void win_draw(win_t *win) {
@@ -264,11 +308,14 @@ void win_set_cursor(win_t *win, win_cur_t cursor) {
switch (cursor) {
case CURSOR_HAND:
- XDefineCursor(win->env.dpy, win->xwin, hand);
+ XDefineCursor(win->env.dpy, win->xwin, chand);
+ break;
+ case CURSOR_WATCH:
+ XDefineCursor(win->env.dpy, win->xwin, cwatch);
break;
case CURSOR_ARROW:
default:
- XDefineCursor(win->env.dpy, win->xwin, arrow);
+ XDefineCursor(win->env.dpy, win->xwin, carrow);
break;
}
}
diff --git a/window.h b/window.h
index 1ae431b..e06c19f 100644
--- a/window.h
+++ b/window.h
@@ -25,7 +25,8 @@
typedef enum win_cur_e {
CURSOR_ARROW = 0,
- CURSOR_HAND
+ CURSOR_HAND,
+ CURSOR_WATCH
} win_cur_t;
typedef struct win_env_s {
@@ -42,6 +43,7 @@ typedef struct win_s {
win_env_t env;
unsigned long bgcol;
+ unsigned long selcol;
Pixmap pm;
int x;
@@ -63,7 +65,12 @@ int win_moveresize(win_t*, int, int, unsigned int, unsigned int);
void win_toggle_fullscreen(win_t*);
+Pixmap win_create_pixmap(win_t*, int, int);
+void win_free_pixmap(win_t*, Pixmap);
+
void win_clear(win_t*);
+void win_draw_pixmap(win_t*, Pixmap, int, int, int, int);
+void win_draw_rect(win_t*, int, int, int, int, Bool);
void win_draw(win_t*);
void win_set_title(win_t*, const char*);