summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2010-04-28 13:32:30 +0200
committerYorhel <git@yorhel.nl>2010-04-28 13:32:30 +0200
commitb7ccf78b90f7fded5f6627babcd7b0e92ac807fd (patch)
treee1c1a87a2a132c180d4a6e62ade8a8c9925f5802
parentc68a229e0913247588a5a0836a8c99b18eec2502 (diff)
More intuitive multi-page browsing
Here is the new multi-page listing functionality I promised in 5db9c2aea11052451c7e11bf8eef73393e4a072e. It may look very easy, but getting this to work right wasn't, unfortunately.
-rw-r--r--ChangeLog1
-rw-r--r--src/browser.c20
-rw-r--r--src/calc.c1
-rw-r--r--src/delete.c1
-rw-r--r--src/dirlist.c65
-rw-r--r--src/dirlist.h3
6 files changed, 84 insertions, 7 deletions
diff --git a/ChangeLog b/ChangeLog
index d98653a..d2428e9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -4,6 +4,7 @@ git - ?
- Fixed crash on browsing dirs with a small window size (#2991787)
- Fixed buffer overflow when some directories can't be scanned (#2981704)
- Improved browsing performance
+ - More intuitive multi-page browsing
- Various minor fixes
1.6 - 2009-10-23
diff --git a/src/browser.c b/src/browser.c
index 3d43159..325d993 100644
--- a/src/browser.c
+++ b/src/browser.c
@@ -205,12 +205,10 @@ void browse_draw() {
return;
/* get start position */
- t = dirlist_get(-1*((winrows)/2));
- if(t == dirlist_next(NULL))
- t = NULL;
+ t = dirlist_top(0);
/* print the list to the screen */
- for(i=0; (t=dirlist_next(t)) && i<winrows-3; i++) {
+ for(i=0; t && i<winrows-3; t=dirlist_next(t),i++) {
browse_draw_item(t, 2+i);
/* save the selected row number for later */
if(t->flags & FF_BSEL)
@@ -284,28 +282,34 @@ int browse_key(int ch) {
case KEY_UP:
case 'k':
dirlist_select(dirlist_get(-1));
+ dirlist_top(-1);
info_start = 0;
break;
case KEY_DOWN:
case 'j':
dirlist_select(dirlist_get(1));
+ dirlist_top(1);
info_start = 0;
break;
case KEY_HOME:
dirlist_select(dirlist_next(NULL));
+ dirlist_top(2);
info_start = 0;
break;
case KEY_LL:
case KEY_END:
dirlist_select(dirlist_get(1<<30));
+ dirlist_top(1);
info_start = 0;
break;
case KEY_PPAGE:
dirlist_select(dirlist_get(-1*(winrows-3)));
+ dirlist_top(-1);
info_start = 0;
break;
case KEY_NPAGE:
dirlist_select(dirlist_get(winrows-3));
+ dirlist_top(1);
info_start = 0;
break;
@@ -338,15 +342,19 @@ int browse_key(int ch) {
case 10:
case KEY_RIGHT:
case 'l':
- if(sel != NULL && sel->sub != NULL)
+ if(sel != NULL && sel->sub != NULL) {
dirlist_open(sel->sub);
+ dirlist_top(-3);
+ }
info_show = 0;
break;
case KEY_LEFT:
case 'h':
case '<':
- if(sel != NULL && sel->parent->parent != NULL)
+ if(sel != NULL && sel->parent->parent != NULL) {
dirlist_open(sel->parent);
+ dirlist_top(-3);
+ }
info_show = 0;
break;
diff --git a/src/calc.c b/src/calc.c
index 78afde0..7a73878 100644
--- a/src/calc.c
+++ b/src/calc.c
@@ -509,6 +509,7 @@ int calc_process() {
freedir(orig);
}
browse_init(root->sub);
+ dirlist_top(-3);
return 0;
}
diff --git a/src/delete.c b/src/delete.c
index b26ef54..fdc4467 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -236,6 +236,7 @@ void delete_process() {
else {
nextsel->flags |= FF_BSEL;
browse_init(nextsel);
+ dirlist_top(-4);
}
}
diff --git a/src/dirlist.c b/src/dirlist.c
index 9e43eec..2239914 100644
--- a/src/dirlist.c
+++ b/src/dirlist.c
@@ -40,7 +40,7 @@ int dirlist_sort_desc = 1,
/* private state vars */
struct dir dirlist_parent_alloc;
-struct dir *head, *head_real, *selected;
+struct dir *head, *head_real, *selected, *top = NULL;
@@ -282,6 +282,67 @@ void dirlist_select(struct dir *d) {
}
+
+/* We need a hint in order to figure out which item should be on top:
+ * 0 = only get the current top, don't set anything
+ * 1 = selected has moved down
+ * -1 = selected has moved up
+ * -2 = selected = first item in the list (faster version of '1')
+ * -3 = top should be considered as invalid (after sorting or opening an other dir)
+ * -4 = an item has been deleted
+ * -5 = hidden flag has been changed
+ *
+ * Actions:
+ * hint = -1 or -4 -> top = selected_is_visible ? top : selected
+ * hint = -2 or -3 -> top = selected-(winrows-3)/2
+ * hint = 1 -> top = selected_is_visible ? top : selected-(winrows-4)
+ * hint = 0 or -5 -> top = selected_is_visible ? top : selected-(winrows-3)/2
+ *
+ * Regardless of the hint, the returned top will always be chosen such that the
+ * selected item is visible.
+ */
+struct dir *dirlist_top(int hint) {
+ struct dir *t;
+ int i = winrows-3, visible = 0;
+
+ if(hint == -2 || hint == -3)
+ top = NULL;
+
+ /* check whether the current selected item is within the visible window */
+ if(top) {
+ i = winrows-3;
+ t = dirlist_get(0);
+ while(t && i--) {
+ if(t == top) {
+ visible++;
+ break;
+ }
+ t = dirlist_prev(t);
+ }
+ }
+
+ /* otherwise, get a new top */
+ if(!visible)
+ top = hint == -1 || hint == -4 ? dirlist_get(0) :
+ hint == 1 ? dirlist_get(-1*(winrows-4)) :
+ dirlist_get(-1*(winrows-3)/2);
+
+ /* also make sure that if the list is longer than the window and the last
+ * item is visible, that this last item is also the last on the window */
+ t = top;
+ i = winrows-3;
+ while(t && i--)
+ t = dirlist_next(t);
+ t = top;
+ do {
+ top = t;
+ t = dirlist_prev(t);
+ } while(t && i-- > 0);
+
+ return top;
+}
+
+
void dirlist_set_sort(int col, int desc, int df) {
/* update config */
if(col != DL_NOCHANGE)
@@ -297,11 +358,13 @@ void dirlist_set_sort(int col, int desc, int df) {
dirlist_parent->next = head_real;
else
head = head_real;
+ dirlist_top(-3);
}
void dirlist_set_hidden(int hidden) {
dirlist_hidden = hidden;
dirlist_fixup();
+ dirlist_top(-5);
}
diff --git a/src/dirlist.h b/src/dirlist.h
index f3e7c58..e8174d5 100644
--- a/src/dirlist.h
+++ b/src/dirlist.h
@@ -49,6 +49,9 @@ struct dir *dirlist_next(struct dir *);
* hidden items aren't considered */
struct dir *dirlist_get(int i);
+/* Get/set the first visible item in the list on the screen */
+struct dir *dirlist_top(int hint);
+
/* Set selected dir (must be in the currently opened directory, obviously) */
void dirlist_select(struct dir *);