diff --git a/ChangeLog b/ChangeLog index d98653ae1de670c5741873ac5300de783d40f51e..d2428e91152caf4462ca24509159493b49a4b001 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 3d43159c43b014c7b648b74de920d6001059b5b3..325d993ceb6e35728671c4c700db1eaca5c4d1eb 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 78afde0c217244060515985bdb84ae946e5724a1..7a738787c0c1ef7c450464a3acaea6121edd4ad8 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 b26ef54ec04091238f690715d8bff537b49be808..fdc4467978fd4349135292f1c9d416cf7db7f84e 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 9e43eec3efb1b97c9a28fa554464f543085a91f6..2239914a9734d2adc4c847893fa2db526c61a867 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 f3e7c585d1a8bc6df1a8e00c332766925b05d3a5..e8174d5f46763b029bb3f3e1efdd11f70fc3c277 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 *);