1 /* vifm
2 * Copyright (C) 2001 Ken Steen.
3 * Copyright (C) 2011 xaizek.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18 */
19
20 #include "flist_pos.h"
21
22 #include <assert.h> /* assert() */
23 #include <stddef.h> /* NULL size_t */
24 #include <stdlib.h> /* abs() */
25 #include <string.h> /* strcmp() */
26 #include <wctype.h> /* towupper() */
27
28 #include "cfg/config.h"
29 #include "ui/fileview.h"
30 #include "ui/ui.h"
31 #include "utils/fs.h"
32 #include "utils/regexp.h"
33 #include "utils/path.h"
34 #include "utils/str.h"
35 #include "utils/utf8.h"
36 #include "utils/utils.h"
37 #include "filelist.h"
38 #include "filtering.h"
39 #include "types.h"
40
41 static int get_curr_col(const view_t *view);
42 static int get_curr_line(const view_t *view);
43 static int get_max_col(const view_t *view);
44 static int get_max_line(const view_t *view);
45 static int get_column_top_pos(const view_t *view);
46 static int get_column_bottom_pos(const view_t *view);
47 static const char * get_last_ext(const char name[]);
48 static int is_mismatched_entry(const dir_entry_t *entry);
49 static int find_next(const view_t *view, entry_predicate pred);
50 static int find_prev(const view_t *view, entry_predicate pred);
51 static int file_can_be_displayed(const char directory[], const char filename[]);
52
53 int
fpos_find_by_name(const view_t * view,const char name[])54 fpos_find_by_name(const view_t *view, const char name[])
55 {
56 return fpos_find_entry(view, name, NULL);
57 }
58
59 int
fpos_find_entry(const view_t * view,const char name[],const char dir[])60 fpos_find_entry(const view_t *view, const char name[], const char dir[])
61 {
62 int i;
63 for(i = 0; i < view->list_rows; ++i)
64 {
65 if(dir != NULL && stroscmp(view->dir_entry[i].origin, dir) != 0)
66 {
67 continue;
68 }
69
70 if(stroscmp(view->dir_entry[i].name, name) == 0)
71 {
72 return i;
73 }
74 }
75 return -1;
76 }
77
78 int
fpos_scroll_down(view_t * view,int lines_count)79 fpos_scroll_down(view_t *view, int lines_count)
80 {
81 if(!fpos_are_all_files_visible(view))
82 {
83 view->list_pos =
84 get_corrected_list_pos_down(view, lines_count*view->run_size);
85 return 1;
86 }
87 return 0;
88 }
89
90 int
fpos_scroll_up(view_t * view,int lines_count)91 fpos_scroll_up(view_t *view, int lines_count)
92 {
93 if(!fpos_are_all_files_visible(view))
94 {
95 view->list_pos =
96 get_corrected_list_pos_up(view, lines_count*view->run_size);
97 return 1;
98 }
99 return 0;
100 }
101
102 void
fpos_set_pos(view_t * view,int pos)103 fpos_set_pos(view_t *view, int pos)
104 {
105 if(pos < 1)
106 {
107 pos = 0;
108 }
109
110 if(pos > view->list_rows - 1)
111 {
112 pos = view->list_rows - 1;
113 }
114
115 if(pos != -1)
116 {
117 view_t *const other = (view == curr_view) ? other_view : curr_view;
118
119 view->list_pos = pos;
120 fview_position_updated(view);
121
122 /* Synchronize cursor with the other pane. */
123 if(view->custom.type == CV_DIFF && other->list_pos != pos)
124 {
125 fpos_set_pos(other, pos);
126 }
127 }
128 }
129
130 void
fpos_ensure_valid_pos(view_t * view)131 fpos_ensure_valid_pos(view_t *view)
132 {
133 if(view->list_pos < 0)
134 {
135 view->list_pos = 0;
136 }
137
138 if(view->list_pos >= view->list_rows)
139 {
140 view->list_pos = view->list_rows - 1;
141 }
142 }
143
144 int
fpos_get_col(const view_t * view,int pos)145 fpos_get_col(const view_t *view, int pos)
146 {
147 return (fview_is_transposed(view) ? pos/view->run_size : pos%view->run_size);
148 }
149
150 int
fpos_get_line(const view_t * view,int pos)151 fpos_get_line(const view_t *view, int pos)
152 {
153 return (fview_is_transposed(view) ? pos%view->run_size : pos/view->run_size);
154 }
155
156 int
fpos_can_move_left(const view_t * view)157 fpos_can_move_left(const view_t *view)
158 {
159 return (view->list_pos > 0);
160 }
161
162 int
fpos_can_move_right(const view_t * view)163 fpos_can_move_right(const view_t *view)
164 {
165 return (view->list_pos < view->list_rows - 1);
166 }
167
168 int
fpos_can_move_up(const view_t * view)169 fpos_can_move_up(const view_t *view)
170 {
171 return fview_is_transposed(view) ? view->list_pos > 0
172 : view->list_pos >= view->run_size;
173 }
174
175 int
fpos_can_move_down(const view_t * view)176 fpos_can_move_down(const view_t *view)
177 {
178 return fview_is_transposed(view) ? view->list_pos < view->list_rows - 1
179 : get_curr_line(view) < get_max_line(view);
180 }
181
182 int
fpos_at_first_col(const view_t * view)183 fpos_at_first_col(const view_t *view)
184 {
185 return (get_curr_col(view) == 0);
186 }
187
188 int
fpos_at_last_col(const view_t * view)189 fpos_at_last_col(const view_t *view)
190 {
191 return (get_curr_col(view) == get_max_col(view));
192 }
193
194 /* Retrieves column number of cursor. Returns the number. */
195 static int
get_curr_col(const view_t * view)196 get_curr_col(const view_t *view)
197 {
198 return fpos_get_col(view, view->list_pos);
199 }
200
201 /* Retrieves line number of cursor. Returns the number. */
202 static int
get_curr_line(const view_t * view)203 get_curr_line(const view_t *view)
204 {
205 return fpos_get_line(view, view->list_pos);
206 }
207
208 /* Retrieves maximum column number. Returns the number. */
209 static int
get_max_col(const view_t * view)210 get_max_col(const view_t *view)
211 {
212 return fview_is_transposed(view) ? (view->list_rows - 1)/view->run_size
213 : (view->run_size - 1);
214 }
215
216 /* Retrieves maximum line number. Returns the number. */
217 static int
get_max_line(const view_t * view)218 get_max_line(const view_t *view)
219 {
220 return fview_is_transposed(view) ? (view->run_size - 1)
221 : (view->list_rows - 1)/view->run_size;
222 }
223
224 int
fpos_line_start(const view_t * view)225 fpos_line_start(const view_t *view)
226 {
227 return fview_is_transposed(view) ? view->list_pos%view->run_size
228 : ROUND_DOWN(view->list_pos, view->run_size);
229 }
230
231 int
fpos_line_end(const view_t * view)232 fpos_line_end(const view_t *view)
233 {
234 if(fview_is_transposed(view))
235 {
236 const int last_top_pos = ROUND_DOWN(view->list_rows - 1, view->run_size);
237 const int pos = last_top_pos + view->list_pos%view->run_size;
238 return (pos < view->list_rows ? pos : pos - view->run_size);
239 }
240
241 return MIN(view->list_rows - 1, fpos_line_start(view) + view->run_size - 1);
242 }
243
244 int
fpos_get_hor_step(const struct view_t * view)245 fpos_get_hor_step(const struct view_t *view)
246 {
247 return (fview_is_transposed(view) ? view->run_size : 1);
248 }
249
250 int
fpos_get_ver_step(const struct view_t * view)251 fpos_get_ver_step(const struct view_t *view)
252 {
253 return (fview_is_transposed(view) ? 1 : view->run_size);
254 }
255
256 int
fpos_has_hidden_top(const view_t * view)257 fpos_has_hidden_top(const view_t *view)
258 {
259 return (fview_is_transposed(view) ? 0 : can_scroll_up(view));
260 }
261
262 int
fpos_has_hidden_bottom(const view_t * view)263 fpos_has_hidden_bottom(const view_t *view)
264 {
265 return (fview_is_transposed(view) ? 0 : can_scroll_down(view));
266 }
267
268 int
fpos_get_top_pos(const view_t * view)269 fpos_get_top_pos(const view_t *view)
270 {
271 return get_column_top_pos(view)
272 + (can_scroll_up(view) ? fpos_get_offset(view) : 0);
273 }
274
275 int
fpos_get_middle_pos(const view_t * view)276 fpos_get_middle_pos(const view_t *view)
277 {
278 const int top_pos = get_column_top_pos(view);
279 const int bottom_pos = get_column_bottom_pos(view);
280 const int v = (fview_is_transposed(view) ? 1 : view->run_size);
281 return top_pos + (DIV_ROUND_UP(bottom_pos - top_pos, v)/2)*v;
282 }
283
284 int
fpos_get_bottom_pos(const view_t * view)285 fpos_get_bottom_pos(const view_t *view)
286 {
287 return get_column_bottom_pos(view)
288 - (can_scroll_down(view) ? fpos_get_offset(view) : 0);
289 }
290
291 int
fpos_get_offset(const view_t * view)292 fpos_get_offset(const view_t *view)
293 {
294 int val;
295
296 if(fview_is_transposed(view))
297 {
298 /* Scroll offset doesn't make much sense for transposed table. */
299 return 0;
300 }
301
302 val = MIN(DIV_ROUND_UP(view->window_rows - 1, 2), MAX(cfg.scroll_off, 0));
303 return val*view->column_count;
304 }
305
306 int
fpos_are_all_files_visible(const view_t * view)307 fpos_are_all_files_visible(const view_t *view)
308 {
309 return view->list_rows <= view->window_cells;
310 }
311
312 int
fpos_get_last_visible_cell(const view_t * view)313 fpos_get_last_visible_cell(const view_t *view)
314 {
315 return view->top_line + view->window_cells - 1;
316 }
317
318 int
fpos_half_scroll(view_t * view,int down)319 fpos_half_scroll(view_t *view, int down)
320 {
321 int new_pos;
322
323 int offset = MAX(view->window_cells/2, view->run_size);
324 offset = ROUND_DOWN(offset, curr_view->run_size);
325
326 if(down)
327 {
328 new_pos = get_corrected_list_pos_down(view, offset);
329 new_pos = MAX(new_pos, view->list_pos + offset);
330
331 if(new_pos >= view->list_rows)
332 {
333 new_pos -= view->column_count*
334 DIV_ROUND_UP(new_pos - (view->list_rows - 1), view->column_count);
335 }
336 }
337 else
338 {
339 new_pos = get_corrected_list_pos_up(view, offset);
340 new_pos = MIN(new_pos, view->list_pos - offset);
341
342 if(new_pos < 0)
343 {
344 new_pos += view->column_count*DIV_ROUND_UP(-new_pos, view->column_count);
345 }
346 }
347
348 scroll_by_files(view, new_pos - view->list_pos);
349 return new_pos;
350 }
351
352 /* Retrieves position of a file at the top of visible part of current column.
353 * Returns the position. */
354 static int
get_column_top_pos(const view_t * view)355 get_column_top_pos(const view_t *view)
356 {
357 const int column_correction = fview_is_transposed(view)
358 ? ROUND_DOWN(view->list_pos - view->top_line, view->run_size)
359 : view->list_pos%view->run_size;
360 return view->top_line + column_correction;
361 }
362
363 /* Retrieves position of a file at the bottom of visible part of current column.
364 * Returns the position. */
365 static int
get_column_bottom_pos(const view_t * view)366 get_column_bottom_pos(const view_t *view)
367 {
368 if(fview_is_transposed(view))
369 {
370 const int top_pos = get_column_top_pos(view);
371 const int last = view->list_rows - 1;
372 return MIN(top_pos + view->window_rows - 1, last);
373 }
374 else
375 {
376 const int last_top_pos =
377 ROUND_DOWN(MIN(fpos_get_last_visible_cell(view), view->list_rows - 1),
378 view->run_size);
379 const int pos = last_top_pos + view->list_pos%view->run_size;
380 return (pos < view->list_rows ? pos : pos - view->run_size);
381 }
382 }
383
384 int
fpos_find_group(const view_t * view,int next)385 fpos_find_group(const view_t *view, int next)
386 {
387 /* TODO: refactor/simplify this function (fpos_find_group()). */
388
389 const int correction = next ? -1 : 0;
390 const int lb = correction;
391 const int ub = view->list_rows + correction;
392 const int inc = next ? +1 : -1;
393
394 int pos = view->list_pos;
395 dir_entry_t *pentry = &view->dir_entry[pos];
396 const char *ext = get_last_ext(pentry->name);
397 size_t char_width = utf8_chrw(pentry->name);
398 wchar_t ch = towupper(get_first_wchar(pentry->name));
399 const SortingKey sorting_key =
400 flist_custom_active(view) && cv_compare(view->custom.type)
401 ? SK_BY_ID
402 : abs(view->sort[0]);
403 const int is_dir = fentry_is_dir(pentry);
404 const char *const type_str = get_type_str(pentry->type);
405 regmatch_t pmatch = { .rm_so = 0, .rm_eo = 0 };
406 #ifndef _WIN32
407 char perms[16];
408 get_perm_string(perms, sizeof(perms), pentry->mode);
409 #endif
410 if(sorting_key == SK_BY_GROUPS)
411 {
412 pmatch = get_group_match(&view->primary_group, pentry->name);
413 }
414 while(pos > lb && pos < ub)
415 {
416 dir_entry_t *nentry;
417 pos += inc;
418 nentry = &view->dir_entry[pos];
419 switch(sorting_key)
420 {
421 case SK_BY_FILEEXT:
422 if(fentry_is_dir(nentry))
423 {
424 if(strncmp(pentry->name, nentry->name, char_width) != 0)
425 {
426 return pos;
427 }
428 }
429 if(strcmp(get_last_ext(nentry->name), ext) != 0)
430 {
431 return pos;
432 }
433 break;
434 case SK_BY_EXTENSION:
435 if(strcmp(get_last_ext(nentry->name), ext) != 0)
436 return pos;
437 break;
438 case SK_BY_GROUPS:
439 {
440 regmatch_t nmatch = get_group_match(&view->primary_group,
441 nentry->name);
442
443 if(pmatch.rm_eo - pmatch.rm_so != nmatch.rm_eo - nmatch.rm_so ||
444 (pmatch.rm_eo != pmatch.rm_so &&
445 strncmp(pentry->name + pmatch.rm_so, nentry->name + nmatch.rm_so,
446 pmatch.rm_eo - pmatch.rm_so + 1U) != 0))
447 return pos;
448 }
449 break;
450 case SK_BY_TARGET:
451 if((nentry->type == FT_LINK) != (pentry->type == FT_LINK))
452 {
453 /* One of the entries is not a link. */
454 return pos;
455 }
456 if(nentry->type == FT_LINK)
457 {
458 /* Both entries are symbolic links. */
459 char full_path[PATH_MAX + 1];
460 char nlink[PATH_MAX + 1], plink[PATH_MAX + 1];
461
462 get_full_path_of(nentry, sizeof(full_path), full_path);
463 if(get_link_target(full_path, nlink, sizeof(nlink)) != 0)
464 {
465 return pos;
466 }
467 get_full_path_of(pentry, sizeof(full_path), full_path);
468 if(get_link_target(full_path, plink, sizeof(plink)) != 0)
469 {
470 return pos;
471 }
472
473 if(stroscmp(nlink, plink) != 0)
474 {
475 return pos;
476 }
477 }
478 break;
479 case SK_BY_NAME:
480 if(strncmp(pentry->name, nentry->name, char_width) != 0)
481 return pos;
482 break;
483 case SK_BY_INAME:
484 if((wchar_t)towupper(get_first_wchar(nentry->name)) != ch)
485 return pos;
486 break;
487 case SK_BY_SIZE:
488 if(nentry->size != pentry->size)
489 return pos;
490 break;
491 case SK_BY_NITEMS:
492 if(fentry_get_nitems(view, nentry) != fentry_get_nitems(view, pentry))
493 return pos;
494 break;
495 case SK_BY_TIME_ACCESSED:
496 if(nentry->atime != pentry->atime)
497 return pos;
498 break;
499 case SK_BY_TIME_CHANGED:
500 if(nentry->ctime != pentry->ctime)
501 return pos;
502 break;
503 case SK_BY_TIME_MODIFIED:
504 if(nentry->mtime != pentry->mtime)
505 return pos;
506 break;
507 case SK_BY_DIR:
508 if(is_dir != fentry_is_dir(nentry))
509 {
510 return pos;
511 }
512 break;
513 case SK_BY_TYPE:
514 if(get_type_str(nentry->type) != type_str)
515 {
516 return pos;
517 }
518 break;
519 #ifndef _WIN32
520 case SK_BY_GROUP_NAME:
521 case SK_BY_GROUP_ID:
522 if(nentry->gid != pentry->gid)
523 return pos;
524 break;
525 case SK_BY_OWNER_NAME:
526 case SK_BY_OWNER_ID:
527 if(nentry->uid != pentry->uid)
528 return pos;
529 break;
530 case SK_BY_MODE:
531 if(nentry->mode != pentry->mode)
532 return pos;
533 break;
534 case SK_BY_INODE:
535 if(nentry->inode != pentry->inode)
536 return pos;
537 break;
538 case SK_BY_PERMISSIONS:
539 {
540 char nperms[16];
541 get_perm_string(nperms, sizeof(nperms), nentry->mode);
542 if(strcmp(nperms, perms) != 0)
543 {
544 return pos;
545 }
546 break;
547 }
548 case SK_BY_NLINKS:
549 if(nentry->nlinks != pentry->nlinks)
550 {
551 return pos;
552 }
553 break;
554 #endif
555 }
556 /* Id sorting is builtin only and is defined outside SortingKey
557 * enumeration. */
558 if((int)sorting_key == SK_BY_ID)
559 {
560 if(nentry->id != pentry->id)
561 {
562 return pos;
563 }
564 }
565 }
566 return pos;
567 }
568
569 /* Finds pointer to the beginning of the last extension of the file name.
570 * Returns the pointer, which might point to the NUL byte if there are no
571 * extensions. */
572 static const char *
get_last_ext(const char name[])573 get_last_ext(const char name[])
574 {
575 const char *const ext = strrchr(name, '.');
576 return (ext == NULL) ? (name + strlen(name)) : (ext + 1);
577 }
578
579 int
fpos_find_dir_group(const view_t * view,int next)580 fpos_find_dir_group(const view_t *view, int next)
581 {
582 const int correction = next ? -1 : 0;
583 const int lb = correction;
584 const int ub = view->list_rows + correction;
585 const int inc = next ? +1 : -1;
586
587 int pos = curr_view->list_pos;
588 dir_entry_t *pentry = &curr_view->dir_entry[pos];
589 const int is_dir = fentry_is_dir(pentry);
590 while(pos > lb && pos < ub)
591 {
592 dir_entry_t *nentry;
593 pos += inc;
594 nentry = &curr_view->dir_entry[pos];
595 if(is_dir != fentry_is_dir(nentry))
596 {
597 break;
598 }
599 }
600 return pos;
601 }
602
603 int
fpos_first_sibling(const view_t * view)604 fpos_first_sibling(const view_t *view)
605 {
606 const int parent = view->list_pos - view->dir_entry[view->list_pos].child_pos;
607 return (parent == view->list_pos ? 0 : parent + 1);
608 }
609
610 int
fpos_last_sibling(const view_t * view)611 fpos_last_sibling(const view_t *view)
612 {
613 int pos = view->list_pos - view->dir_entry[view->list_pos].child_pos;
614 if(pos == view->list_pos)
615 {
616 /* For top-level entry, find the last top-level entry. */
617 pos = view->list_rows - 1;
618 while(view->dir_entry[pos].child_pos != 0)
619 {
620 pos -= view->dir_entry[pos].child_pos;
621 }
622 }
623 else
624 {
625 /* For non-top-level entry, go to last tree item and go up until our
626 * child. */
627 const int parent = pos;
628 pos = parent + view->dir_entry[parent].child_count;
629 while(pos - view->dir_entry[pos].child_pos != parent)
630 {
631 pos -= view->dir_entry[pos].child_pos;
632 }
633 }
634 return pos;
635 }
636
637 int
fpos_next_dir_sibling(const view_t * view)638 fpos_next_dir_sibling(const view_t *view)
639 {
640 int pos = view->list_pos;
641 const int parent = view->dir_entry[pos].child_pos == 0
642 ? -1
643 : pos - view->dir_entry[pos].child_pos;
644 const int past_end = parent == -1
645 ? view->list_rows
646 : parent + view->dir_entry[parent].child_count;
647 pos += view->dir_entry[pos].child_count + 1;
648 while(pos < past_end)
649 {
650 dir_entry_t *const e = &view->dir_entry[pos];
651 if(fentry_is_dir(e))
652 {
653 break;
654 }
655 /* Skip over whole sub-tree. */
656 pos += e->child_count + 1;
657 }
658 return (pos < past_end ? pos : view->list_pos);
659 }
660
661 int
fpos_prev_dir_sibling(const view_t * view)662 fpos_prev_dir_sibling(const view_t *view)
663 {
664 int pos = view->list_pos;
665 /* Determine original parent (-1 for top-most entry). */
666 const int parent = view->dir_entry[pos].child_pos == 0
667 ? -1
668 : pos - view->dir_entry[pos].child_pos;
669 --pos;
670 while(pos > parent)
671 {
672 dir_entry_t *const e = &view->dir_entry[pos];
673 const int p = (e->child_pos == 0) ? -1 : (pos - e->child_pos);
674 /* If we find ourselves deeper than originally, just go up one level. */
675 if(p != parent)
676 {
677 pos = p;
678 continue;
679 }
680
681 /* We're looking for directories. */
682 if(fentry_is_dir(e))
683 {
684 break;
685 }
686 /* We're on a file on the same level. */
687 --pos;
688 }
689 return (pos > parent ? pos : view->list_pos);
690 }
691
692 int
fpos_next_dir(const view_t * view)693 fpos_next_dir(const view_t *view)
694 {
695 return find_next(view, &fentry_is_dir);
696 }
697
698 int
fpos_prev_dir(const view_t * view)699 fpos_prev_dir(const view_t *view)
700 {
701 return find_prev(view, &fentry_is_dir);
702 }
703
704 int
fpos_next_selected(const view_t * view)705 fpos_next_selected(const view_t *view)
706 {
707 return find_next(view, &is_entry_selected);
708 }
709
710 int
fpos_prev_selected(const view_t * view)711 fpos_prev_selected(const view_t *view)
712 {
713 return find_prev(view, &is_entry_selected);
714 }
715
716 int
fpos_next_mismatch(const view_t * view)717 fpos_next_mismatch(const view_t *view)
718 {
719 return (view->custom.type == CV_DIFF)
720 ? find_next(view, &is_mismatched_entry)
721 : view->list_pos;
722 }
723
724 int
fpos_prev_mismatch(const view_t * view)725 fpos_prev_mismatch(const view_t *view)
726 {
727 return (view->custom.type == CV_DIFF)
728 ? find_prev(view, &is_mismatched_entry)
729 : view->list_pos;
730 }
731
732 /* Checks whether entry corresponds to comparison mismatch. Returns non-zero if
733 * so, otherwise zero is returned. */
734 static int
is_mismatched_entry(const dir_entry_t * entry)735 is_mismatched_entry(const dir_entry_t *entry)
736 {
737 /* To avoid passing view pointer here, we exploit the fact that entry_to_pos()
738 * checks whether it's argument belongs to the given view. */
739 int pos = entry_to_pos(&lwin, entry);
740 view_t *other = &rwin;
741 if(pos == -1)
742 {
743 pos = entry_to_pos(&rwin, entry);
744 other = &lwin;
745 }
746
747 return (other->dir_entry[pos].id != entry->id);
748 }
749
750 /* Finds position of the next entry matching the predicate. Returns new
751 * position which isn't changed if no next directory is found. */
752 static int
find_next(const view_t * view,entry_predicate pred)753 find_next(const view_t *view, entry_predicate pred)
754 {
755 int pos = view->list_pos;
756 while(++pos < view->list_rows)
757 {
758 if(pred(&view->dir_entry[pos]))
759 {
760 break;
761 }
762 }
763 return (pos == view->list_rows ? view->list_pos : pos);
764 }
765
766 /* Finds position of the previous entry matching the predicate. Returns new
767 * position which isn't changed if no previous directory is found. */
768 static int
find_prev(const view_t * view,entry_predicate pred)769 find_prev(const view_t *view, entry_predicate pred)
770 {
771 int pos = view->list_pos;
772 while(--pos >= 0)
773 {
774 if(pred(&view->dir_entry[pos]))
775 {
776 break;
777 }
778 }
779 return (pos < 0 ? view->list_pos : pos);
780 }
781
782 int
fpos_ensure_selected(view_t * view,const char name[])783 fpos_ensure_selected(view_t *view, const char name[])
784 {
785 int file_pos;
786 char nm[NAME_MAX + 1];
787
788 /* Don't reset filters to find "file with empty name". */
789 if(name[0] == '\0')
790 {
791 return 0;
792 }
793
794 /* This is for compatibility with paths loaded from vifminfo that have
795 * trailing slash. */
796 copy_str(nm, sizeof(nm), name);
797 chosp(nm);
798
799 file_pos = fpos_find_by_name(view, nm);
800 if(file_pos < 0 && file_can_be_displayed(view->curr_dir, nm))
801 {
802 if(nm[0] == '.')
803 {
804 dot_filter_set(view, 1);
805 file_pos = fpos_find_by_name(view, nm);
806 }
807
808 if(file_pos < 0)
809 {
810 name_filters_remove(view);
811
812 /* name_filters_remove() postpones reloading of list files. */
813 (void)populate_dir_list(view, 1);
814
815 file_pos = fpos_find_by_name(view, nm);
816 }
817 }
818
819 fpos_set_pos(view, (file_pos < 0) ? 0 : file_pos);
820 return file_pos >= 0;
821 }
822
823 /* Checks if file specified can be displayed. Used to filter some files, that
824 * are hidden intentionally. Returns non-zero if file can be made visible. */
825 static int
file_can_be_displayed(const char directory[],const char filename[])826 file_can_be_displayed(const char directory[], const char filename[])
827 {
828 if(is_parent_dir(filename))
829 {
830 return cfg_parent_dir_is_visible(is_root_dir(directory));
831 }
832 return path_exists_at(directory, filename, DEREF);
833 }
834
835 int
fpos_find_by_ch(const view_t * view,int ch,int backward,int wrap)836 fpos_find_by_ch(const view_t *view, int ch, int backward, int wrap)
837 {
838 int x;
839 const int upcase = (cfg.case_override & CO_GOTO_FILE)
840 ? (cfg.case_ignore & CO_GOTO_FILE)
841 : (cfg.ignore_case && !(cfg.smart_case && iswupper(ch)));
842
843 if(upcase)
844 {
845 ch = towupper(ch);
846 }
847
848 x = view->list_pos;
849 do
850 {
851 if(backward)
852 {
853 x--;
854 if(x < 0)
855 {
856 if(wrap)
857 x = view->list_rows - 1;
858 else
859 return -1;
860 }
861 }
862 else
863 {
864 x++;
865 if(x > view->list_rows - 1)
866 {
867 if(wrap)
868 x = 0;
869 else
870 return -1;
871 }
872 }
873
874 if(ch > 255)
875 {
876 wchar_t wc = get_first_wchar(view->dir_entry[x].name);
877 if(upcase)
878 wc = towupper(wc);
879 if(wc == (wchar_t)ch)
880 break;
881 }
882 else
883 {
884 int c = view->dir_entry[x].name[0];
885 if(upcase)
886 c = towupper(c);
887 if(c == ch)
888 break;
889 }
890 }
891 while(x != view->list_pos);
892
893 return x;
894 }
895
896 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
897 /* vim: set cinoptions+=t0 : */
898