aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--config.h6
-rw-r--r--main.c453
-rw-r--r--options.c12
-rw-r--r--options.h1
-rw-r--r--thumbs.c224
-rw-r--r--thumbs.h63
-rw-r--r--util.h3
-rw-r--r--window.c87
-rw-r--r--window.h9
10 files changed, 661 insertions, 199 deletions
diff --git a/Makefile b/Makefile
index d4bcf95..01f4bf7 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ VERSION=git-20110219
CC?=gcc
PREFIX?=/usr/local
-CFLAGS+= -Wall -pedantic -DVERSION=\"$(VERSION)\"
+CFLAGS+= -Wall -pedantic -DVERSION=\"$(VERSION)\" -g
LDFLAGS+=
LIBS+= -lX11 -lImlib2
diff --git a/config.h b/config.h
index e140446..93bc424 100644
--- a/config.h
+++ b/config.h
@@ -3,9 +3,11 @@
#define WIN_WIDTH 800
#define WIN_HEIGHT 600
-/* default color to use for window background: *
+/* default color for window background: *
* (see X(7) "COLOR NAMES" section for valid values) */
#define BG_COLOR "#999999"
+/* default color for thumbnail selection: */
+#define SEL_COLOR "#0040FF"
/* 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 6828795..c1f2b72 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 ? fileidx + 1 : 0, tns.cnt,
+ tns.cnt ? filenames[fileidx] : "");
} 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,6 +258,15 @@ 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;
@@ -246,6 +280,153 @@ 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 = MAX(0, win.x + img.x);
+ y = MAX(0, 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;
+
+ /* switch to thumnail mode */
+ case XK_Return:
+ if (options->thumbnails) {
+ mode = MODE_THUMBS;
+ changed = tns.dirty = 1;
+ }
+ break;
+
+ /* miscellaneous */
+ case XK_a:
+ img_toggle_antialias(&img);
+ changed = 1;
+ break;
+ case XK_r:
+ changed = load_image();
+ break;
+ }
+ } else {
+ /* thumbnail mode */
+ switch (ksym) {
+ /* open selected image */
+ case XK_Return:
+ load_image();
+ mode = MODE_NORMAL;
+ win_set_cursor(&win, CURSOR_ARROW);
+ changed = 1;
+ break;
+
+ /* move selection */
+ case XK_h:
+ case XK_Left:
+ changed = tns_move_selection(&tns, &win, MOVE_LEFT);
+ break;
+ case XK_j:
+ case XK_Down:
+ changed = tns_move_selection(&tns, &win, MOVE_DOWN);
+ break;
+ case XK_k:
+ case XK_Up:
+ changed = tns_move_selection(&tns, &win, MOVE_UP);
+ break;
+ case XK_l:
+ case XK_Right:
+ changed = tns_move_selection(&tns, &win, MOVE_RIGHT);
+ break;
+ }
+ }
+
+ /* common key mappings */
switch (ksym) {
case XK_Escape:
cleanup();
@@ -253,126 +434,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 = MAX(0, win.x + img.x);
- y = MAX(0, 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 +454,66 @@ 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;
+ win_set_cursor(&win, CURSOR_ARROW);
+ changed = 1;
+ break;
+ }
+ break;
+ }
}
- if (changed) {
- img_render(&img, &win);
- update_title();
- timeout = 0;
- }
+ if (changed)
+ redraw();
}
void on_motionnotify(XMotionEvent *mev) {
@@ -446,23 +532,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 +584,11 @@ void run() {
break;
case ConfigureNotify:
if (win_configure(&win, &ev.xconfigure)) {
- img.checkpan = 1;
timeout = 75000;
+ if (mode == MODE_NORMAL)
+ img.checkpan = 1;
+ else
+ tns.dirty = 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..3de9518
--- /dev/null
+++ b/thumbs.c
@@ -0,0 +1,224 @@
+/* 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 int fileidx;
+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 = 0;
+ tns->thumbs = (thumb_t*) s_malloc(cnt * sizeof(thumb_t));
+ memset(tns->thumbs, 0, cnt * sizeof(thumb_t));
+ tns->dirty = 0;
+}
+
+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();
+ tns->dirty = 1;
+}
+
+void tns_check_view(tns_t *tns, Bool scrolled) {
+ if (!tns)
+ return;
+
+ tns->first -= tns->first % tns->cols;
+
+ if (scrolled) {
+ /* move selection into visible area */
+ } else {
+ /* scroll to selection */
+ if (tns->first + tns->cols * tns->rows <= fileidx) {
+ tns->first = fileidx - fileidx % tns->cols -
+ tns->cols * (tns->rows - 1);
+ tns->dirty = 1;
+ } else if (tns->first > fileidx) {
+ tns->first = fileidx - fileidx % tns->cols;
+ tns->dirty = 1;
+ }
+ }
+}
+
+void tns_render(tns_t *tns, win_t *win) {
+ int i, cnt, r, x, y;
+ thumb_t *t;
+
+ if (!tns || !tns->dirty || !win)
+ return;
+
+ win_clear(win);
+
+ tns->cols = MAX(1, win->w / thumb_dim);
+ tns->rows = MAX(1, win->h / thumb_dim);
+
+ if (tns->cnt < tns->cols * tns->rows) {
+ tns->first = 0;
+ cnt = tns->cnt;
+ } else {
+ tns_check_view(tns, False);
+ cnt = tns->cols * tns->rows;
+ if ((r = tns->first + cnt - tns->cnt) >= tns->cols)
+ tns->first -= r - r % tns->cols;
+ if (r > 0)
+ cnt -= r % tns->cols;
+ }
+
+ r = cnt % tns->cols ? 1 : 0;
+ tns->x = x = (win->w - MIN(cnt, tns->cols) * thumb_dim) / 2 + 5;
+ tns->y = y = (win->h - (cnt / tns->cols + r) * thumb_dim) / 2 + 5;
+
+ for (i = 0; i < cnt; ++i) {
+ t = &tns->thumbs[tns->first + i];
+ t->x = x + (THUMB_SIZE - t->w) / 2;
+ t->y = y + (THUMB_SIZE - t->h) / 2;
+ win_draw_pixmap(win, t->pm, t->x, t->y, t->w, t->h);
+ if ((i + 1) % tns->cols == 0) {
+ x = tns->x;
+ y += thumb_dim;
+ } else {
+ x += thumb_dim;
+ }
+ }
+
+ tns->dirty = 0;
+ tns_highlight(tns, win, fileidx, True);
+}
+
+void tns_highlight(tns_t *tns, win_t *win, int n, Bool hl) {
+ thumb_t *t;
+
+ if (!tns || !win)
+ return;
+
+ if (n >= 0 && n < tns->cnt) {
+ t = &tns->thumbs[n];
+ win_draw_rect(win, t->x - 2, t->y - 2, t->w + 4, t->h + 4, hl);
+ }
+
+ win_draw(win);
+}
+
+int tns_move_selection(tns_t *tns, win_t *win, movedir_t dir) {
+ int old;
+
+ if (!tns || !win)
+ return 0;
+
+ old = fileidx;
+
+ switch (dir) {
+ case MOVE_LEFT:
+ if (fileidx > 0)
+ --fileidx;
+ break;
+ case MOVE_RIGHT:
+ if (fileidx < tns->cnt - 1)
+ ++fileidx;
+ break;
+ case MOVE_UP:
+ if (fileidx >= tns->cols)
+ fileidx -= tns->cols;
+ break;
+ case MOVE_DOWN:
+ if (fileidx + tns->cols < tns->cnt)
+ fileidx += tns->cols;
+ break;
+ }
+
+ if (fileidx != old) {
+ tns_highlight(tns, win, old, False);
+ tns_check_view(tns, False);
+ if (!tns->dirty)
+ tns_highlight(tns, win, fileidx, True);
+ }
+
+ return fileidx != old;
+}
+
+int tns_translate(tns_t *tns, int x, int y) {
+ int n;
+ thumb_t *t;
+
+ if (!tns || x < tns->x || y < tns->y)
+ return -1;
+
+ n = tns->first + (y - tns->y) / thumb_dim * tns->cols +
+ (x - tns->x) / thumb_dim;
+
+ if (n < 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..c545b90
--- /dev/null
+++ b/thumbs.h
@@ -0,0 +1,63 @@
+/* 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 x;
+ int y;
+ int cols;
+ int rows;
+ int first;
+ int sel;
+ unsigned char dirty;
+} tns_t;
+
+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, Bool);
+
+int 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*);