File Browser Arrow Keys Navigation
authorJulian Eisel <eiseljulian@gmail.com>
Thu, 11 Jun 2015 15:20:29 +0000 (17:20 +0200)
committerJulian Eisel <eiseljulian@gmail.com>
Thu, 11 Jun 2015 15:20:29 +0000 (17:20 +0200)
Adds support for selecting/deselecting files in File Browser using the
arrow keys. All directions (up, down, left, right) are possible.

When to Select, When to Deselect?
Standard behaviour is selecting, however if we move into a block of
already selected files (meaning 2+ files are selected) we start
deselecting

Possible Selection Methods
Simple selection (arrow-key): All other files are deselected
Expand selection (Shift+arrow key): Add to/remove from existing
selection
ill-Expand selection (Ctrl+Shift+arrow key): Add to/remove from existing
selection and fill everything in-between

From which file do we start navigating?
From each available selection method (Mouse-, Walk-, All-, Border
Select), we use the last selected file. If there's no selection at all
we use the first (down/right arrow) or last (up/left arrow) file.
(Ideally, the view would automatically be set to the new selection, but
this behaviour overlaps with an other patch I've been working on, so
prefer to do that separately)

(Also tweaks color for highlighted file for better feedback)

D1297, Review done by @campbellbarton, thx a lot :)

source/blender/blenlib/BLI_rect.h
source/blender/blenlib/intern/rct.c
source/blender/editors/space_file/CMakeLists.txt
source/blender/editors/space_file/file_draw.c
source/blender/editors/space_file/file_intern.h
source/blender/editors/space_file/file_ops.c
source/blender/editors/space_file/file_utils.c [new file with mode: 0644]
source/blender/editors/space_file/filesel.c
source/blender/editors/space_file/space_file.c
source/blender/makesdna/DNA_space_types.h

index e0a0b34ac5facc0c7df83ad3979a23aa28fbf143..242ba9f860ae941e78a920de56f5c65cf5585a2f 100644 (file)
@@ -78,6 +78,10 @@ bool BLI_rctf_isect_x(const rctf *rect, const float x);
 bool BLI_rctf_isect_y(const rctf *rect, const float y);
 bool BLI_rctf_isect_pt(const struct rctf *rect, const float x, const float y);
 bool BLI_rctf_isect_pt_v(const struct rctf *rect, const float xy[2]);
+int BLI_rcti_length_x(const rcti *rect, const int x);
+int BLI_rcti_length_y(const rcti *rect, const int y);
+float BLI_rctf_length_x(const rctf *rect, const float x);
+float BLI_rctf_length_y(const rctf *rect, const float y);
 bool BLI_rcti_isect_segment(const struct rcti *rect, const int s1[2], const int s2[2]);
 bool BLI_rctf_isect_segment(const struct rctf *rect, const float s1[2], const float s2[2]);
 bool BLI_rcti_isect_circle(const struct rcti *rect, const float xy[2], const float radius);
index 1edc900261171263d17e4fbfd46504dea6876fd2..8de5d2ef1728d5478164790c7de55f365ebe70a0 100644 (file)
@@ -122,6 +122,38 @@ bool BLI_rctf_isect_pt_v(const rctf *rect, const float xy[2])
        return true;
 }
 
+/**
+ * \returns shortest distance from \a rect to x/y (0 if inside)
+*/
+
+int BLI_rcti_length_x(const rcti *rect, const int x)
+{
+       if (x < rect->xmin) return rect->xmin - x;
+       if (x > rect->xmax) return x - rect->xmax;
+       return 0;
+}
+
+int BLI_rcti_length_y(const rcti *rect, const int y)
+{
+       if (y < rect->ymin) return rect->ymin - y;
+       if (y > rect->ymax) return y - rect->ymax;
+       return 0;
+}
+
+float BLI_rctf_length_x(const rctf *rect, const float x)
+{
+       if (x < rect->xmin) return rect->xmin - x;
+       if (x > rect->xmax) return x - rect->xmax;
+       return 0.0f;
+}
+
+float BLI_rctf_length_y(const rctf *rect, const float y)
+{
+       if (y < rect->ymin) return rect->ymin - y;
+       if (y > rect->ymax) return y - rect->ymax;
+       return 0.0f;
+}
+
 /**
  * is \a rct_b inside \a rct_a
  */
index fc007a659b4dde3b780faa30ce4d73bd016427d6..4c33499da419a9e05d0dcb1a51e3e84326f5efba 100644 (file)
@@ -42,6 +42,7 @@ set(SRC
        file_draw.c
        file_ops.c
        file_panels.c
+       file_utils.c
        filelist.c
        filesel.c
        fsmenu.c
index 0e4d88576630b231fc7296330418dcf5a80d34dd..6347ce7c4e8460ed638bed9ae39a8f01b3c35142 100644 (file)
@@ -543,9 +543,9 @@ void file_draw_list(const bContext *C, ARegion *ar)
 
 
                if (!(file->selflag & FILE_SEL_EDITING)) {
-                       if ((params->active_file == i) || (file->selflag & FILE_SEL_HIGHLIGHTED) || (file->selflag & FILE_SEL_SELECTED)) {
+                       if ((params->highlight_file == i) || (file->selflag & FILE_SEL_HIGHLIGHTED) || (file->selflag & FILE_SEL_SELECTED)) {
                                int colorid = (file->selflag & FILE_SEL_SELECTED) ? TH_HILITE : TH_BACK;
-                               int shade = (params->active_file == i) || (file->selflag & FILE_SEL_HIGHLIGHTED) ? 20 : 0;
+                               int shade = (params->highlight_file == i) || (file->selflag & FILE_SEL_HIGHLIGHTED) ? 35 : 0;
 
                                /* readonly files (".." and ".") must not be drawn as selected - set color back to normal */
                                if (FILENAME_IS_CURRPAR(file->relname)) {
index dbb5e8a773dd9e2da6999649b1f2389b3d34ace6..d7af6c9b81299b31c45e520009aad16c75a8af39 100644 (file)
@@ -59,8 +59,17 @@ bool file_draw_check_exists(SpaceFile *sfile);
 /* file_ops.h */
 struct wmOperatorType;
 struct wmOperator;
+
+typedef enum WalkSelectDirection {
+       FILE_SELECT_WALK_UP,
+       FILE_SELECT_WALK_DOWN,
+       FILE_SELECT_WALK_LEFT,
+       FILE_SELECT_WALK_RIGHT,
+} WalkSelectDirections;
+
 void FILE_OT_highlight(struct wmOperatorType *ot);
 void FILE_OT_select(struct wmOperatorType *ot);
+void FILE_OT_select_walk(struct wmOperatorType *ot);
 void FILE_OT_select_all_toggle(struct wmOperatorType *ot);
 void FILE_OT_select_border(struct wmOperatorType *ot);
 void FILE_OT_select_bookmark(struct wmOperatorType *ot);
@@ -111,5 +120,8 @@ int autocomplete_file(struct bContext *C, char *str, void *arg_v);
 /* file_panels.c */
 void file_panels_register(struct ARegionType *art);
 
+/* file_utils.c */
+void file_tile_boundbox(const ARegion *ar, FileLayout *layout, const int file, rcti *r_bounds);
+
 #endif /* __FILE_INTERN_H__ */
 
index f8d13bb6adbdcb07c290f188dfe2b1f5a31ab032..680a28f5e479d6cdb4dd0b0b9653ec80f17ed4c6 100644 (file)
@@ -176,7 +176,8 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen)
            (selected_idx < numfiles) &&
            (file = filelist_file(sfile->files, selected_idx)))
        {
-               params->active_file = selected_idx;
+               params->highlight_file = selected_idx;
+               sfile->params->active_file = selected_idx;
 
                if (S_ISDIR(file->type)) {
                        const bool is_parent_dir = FILENAME_IS_PARENT(file->relname);
@@ -242,6 +243,53 @@ static FileSelect file_select(bContext *C, const rcti *rect, FileSelType select,
        return retval;
 }
 
+/**
+ * \warning: loops over all files so better use cautiously
+ */
+static bool file_is_any_selected(struct FileList *files)
+{
+       const int numfiles = filelist_numfiles(files);
+       int i;
+
+       for (i = 0; i < numfiles; ++i) {
+               if (filelist_is_selected(files, i, CHECK_ALL)) {
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static int file_border_select_find_last_selected(
+        SpaceFile *sfile, ARegion *ar, const FileSelection *sel,
+        const int mouse_xy[2])
+{
+       FileLayout *layout = ED_fileselect_get_layout(sfile, ar);
+       rcti bounds_first, bounds_last;
+       int dist_first, dist_last;
+
+       file_tile_boundbox(ar, layout, sel->first, &bounds_first);
+       file_tile_boundbox(ar, layout, sel->last, &bounds_last);
+
+       /* are first and last in the same column (horizontal layout)/row (vertical layout)? */
+       if ((layout->flag & FILE_LAYOUT_HOR && bounds_first.xmin == bounds_last.xmin) ||
+           (layout->flag & FILE_LAYOUT_VER && bounds_first.ymin != bounds_last.ymin))
+       {
+               /* use vertical distance */
+               const int my_loc = mouse_xy[1] - ar->winrct.ymin;
+               dist_first = BLI_rcti_length_y(&bounds_first, my_loc);
+               dist_last = BLI_rcti_length_y(&bounds_last, my_loc);
+       }
+       else {
+               /* use horizontal distance */
+               const int mx_loc = mouse_xy[0] - ar->winrct.xmin;
+               dist_first = BLI_rcti_length_x(&bounds_first, mx_loc);
+               dist_last = BLI_rcti_length_x(&bounds_last, mx_loc);
+       }
+
+       return dist_first < dist_last ? sel->first : sel->last;
+}
+
 static int file_border_select_modal(bContext *C, wmOperator *op, const wmEvent *event)
 {
        ARegion *ar = CTX_wm_region(C);
@@ -276,17 +324,17 @@ static int file_border_select_modal(bContext *C, wmOperator *op, const wmEvent *
                                        file->selflag &= ~FILE_SEL_HIGHLIGHTED;
                                }
 
-                               /* active_file gets highlighted as well - make sure it is no readonly file */
+                               /* make sure highlight_file is no readonly file */
                                if (sel.last == idx) {
-                                       params->active_file = idx;
+                                       params->highlight_file = idx;
                                }
                        }
                }
                params->sel_first = sel.first; params->sel_last = sel.last;
-
+               params->active_file = file_border_select_find_last_selected(sfile, ar, &sel, &event->x);
        }
        else {
-               params->active_file = -1;
+               params->highlight_file = -1;
                params->sel_first = params->sel_last = -1;
                file_deselect_all(sfile, FILE_SEL_HIGHLIGHTED);
                WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
@@ -361,7 +409,7 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
                return OPERATOR_CANCELLED;
 
        if (sfile && sfile->params) {
-               int idx = sfile->params->active_file;
+               int idx = sfile->params->highlight_file;
 
                if (idx >= 0) {
                        struct direntry *file = filelist_file(sfile->files, idx);
@@ -409,35 +457,240 @@ void FILE_OT_select(wmOperatorType *ot)
        RNA_def_property_flag(prop, PROP_SKIP_SAVE);
 }
 
+/**
+ * \returns true if selection has changed
+ */
+static bool file_walk_select_selection_set(
+        bContext *C, SpaceFile *sfile,
+        const int direction, const int numfiles,
+        const int active_old, const int active_new, const int other_site,
+        const bool has_selection, const bool extend, const bool fill)
+{
+       FileSelectParams *params = sfile->params;
+       struct FileList *files = sfile->files;
+       const int last_sel = params->active_file; /* store old value */
+       int active = active_old; /* could use active_old instead, just for readability */
+       bool deselect = false;
+
+       if (has_selection) {
+               if (extend &&
+                   filelist_is_selected(files, active_old, FILE_SEL_SELECTED) &&
+                   filelist_is_selected(files, active_new, FILE_SEL_SELECTED))
+               {
+                               /* conditions for deselecting: initial file is selected, new file is
+                                * selected and either other_side isn't selected/found or we use fill */
+                               deselect = (fill || other_site == -1 || !filelist_is_selected(files, other_site, FILE_SEL_SELECTED));
+
+                               /* don't change active here since we either want to deselect active or we want to
+                                * walk through a block of selected files without selecting/deselecting anything */
+                               params->active_file = active_new;
+                               /* but we want to change active if we use fill (needed to get correct selection bounds) */
+                               if (deselect && fill) active = active_new;
+               }
+               else {
+                       /* regular selection change */
+                       params->active_file = active = active_new;
+               }
+       }
+       else {
+               /* select last file */
+               if (ELEM(direction, FILE_SELECT_WALK_UP, FILE_SELECT_WALK_LEFT)) {
+                       params->active_file = active = numfiles - 1;
+               }
+               /* select first file */
+               else if (ELEM(direction, FILE_SELECT_WALK_DOWN, FILE_SELECT_WALK_RIGHT)) {
+                       params->active_file = active = 1;
+               }
+               else {
+                       BLI_assert(0);
+               }
+       }
+
+       if (!params || active < 0) return false;
+
+       /* highlight the active walker file for extended selection for better visual feedback */
+       if (extend) {
+               params->highlight_file = params->active_file;
+       }
+       else {
+               /* deselect all first */
+               file_deselect_all(sfile, FILE_SEL_SELECTED);
+
+               /* highlight file under mouse pos */
+               params->highlight_file = -1;
+               WM_event_add_mousemove(C);
+       }
+
+       /* do the actual selection */
+       if (fill) {
+               FileSelection sel = { MIN2(active, last_sel), MAX2(active, last_sel) };
+
+               /* fill selection between last and first selected file */
+               filelist_select(sfile->files, &sel, deselect ? FILE_SEL_REMOVE : FILE_SEL_ADD,
+                               FILE_SEL_SELECTED, CHECK_ALL);
+               /* entire sel is cleared here, so select active again */
+               if (deselect) {
+                       filelist_select_file(sfile->files, active, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL);
+               }
+       }
+       else {
+               filelist_select_file(sfile->files, active, deselect ? FILE_SEL_REMOVE : FILE_SEL_ADD,
+                                    FILE_SEL_SELECTED, CHECK_ALL);
+       }
+
+       BLI_assert(IN_RANGE(active, 0, numfiles));
+
+       /* selection changed */
+       return true;
+}
+
+/**
+ * \returns true if selection has changed
+ */
+static bool file_walk_select_do(
+        bContext *C, SpaceFile *sfile,
+        FileSelectParams *params, const int direction,
+        const bool extend, const bool fill)
+{
+       const int numfiles = filelist_numfiles(sfile->files);
+       const bool has_selection = file_is_any_selected(sfile->files);
+       const int active_old = params->active_file;
+       int active_new = -1;
+       int other_site = -1; /* file on the other site of active_old */
+
+
+       /* *** get all needed files for handling selection *** */
+
+       if (has_selection) {
+               ARegion *ar = CTX_wm_region(C);
+               FileLayout *layout = ED_fileselect_get_layout(sfile, ar);
+               const int idx_shift = (layout->flag & FILE_LAYOUT_HOR) ? layout->rows : layout->columns;
+
+               if ((layout->flag & FILE_LAYOUT_HOR && direction == FILE_SELECT_WALK_UP) ||
+                   (layout->flag & FILE_LAYOUT_VER && direction == FILE_SELECT_WALK_LEFT))
+               {
+                       active_new = active_old - 1;
+                       other_site = active_old + 1;
+               }
+               else if ((layout->flag & FILE_LAYOUT_HOR && direction == FILE_SELECT_WALK_DOWN) ||
+                        (layout->flag & FILE_LAYOUT_VER && direction == FILE_SELECT_WALK_RIGHT))
+               {
+                       active_new = active_old + 1;
+                       other_site = active_old - 1;
+               }
+               else if ((layout->flag & FILE_LAYOUT_HOR && direction == FILE_SELECT_WALK_LEFT) ||
+                        (layout->flag & FILE_LAYOUT_VER && direction == FILE_SELECT_WALK_UP))
+               {
+                       active_new = active_old - idx_shift;
+                       other_site = active_old + idx_shift;
+               }
+               else if ((layout->flag & FILE_LAYOUT_HOR && direction == FILE_SELECT_WALK_RIGHT) ||
+                        (layout->flag & FILE_LAYOUT_VER && direction == FILE_SELECT_WALK_DOWN))
+               {
+
+                       active_new = active_old + idx_shift;
+                       other_site = active_old - idx_shift;
+               }
+               else {
+                       BLI_assert(0);
+               }
+
+               if (!IN_RANGE(active_new, 0, numfiles)) {
+                       /* extend to invalid file -> abort */
+                       if (extend) return false;
+                       /* select initial file */
+                       else active_new = active_old;
+               }
+               if (!IN_RANGE(other_site, 0, numfiles)) {
+                       other_site = -1;
+               }
+       }
+
+       return file_walk_select_selection_set(C, sfile, direction, numfiles, active_old, active_new, other_site,
+                                             has_selection, extend, fill);
+}
+
+static int file_walk_select_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+       SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C);
+       FileSelectParams *params = sfile->params;
+       const int direction = RNA_enum_get(op->ptr, "direction");
+       const bool extend = RNA_boolean_get(op->ptr, "extend");
+       const bool fill = RNA_boolean_get(op->ptr, "fill");
+
+       if (file_walk_select_do(C, sfile, params, direction, extend, fill)) {
+               WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+               return OPERATOR_FINISHED;
+       }
+
+       return OPERATOR_CANCELLED;
+}
+
+void FILE_OT_select_walk(wmOperatorType *ot)
+{
+       static EnumPropertyItem direction_items[] = {
+               {FILE_SELECT_WALK_UP,    "UP",    0, "Prev",  ""},
+               {FILE_SELECT_WALK_DOWN,  "DOWN",  0, "Next",  ""},
+               {FILE_SELECT_WALK_LEFT,  "LEFT",  0, "Left",  ""},
+               {FILE_SELECT_WALK_RIGHT, "RIGHT", 0, "Right", ""},
+               {0, NULL, 0, NULL, NULL}
+       };
+       PropertyRNA *prop;
+
+       /* identifiers */
+       ot->name = "Walk Select/Deselect File";
+       ot->description = "Select/Deselect files by walking through them";
+       ot->idname = "FILE_OT_select_walk";
+
+       /* api callbacks */
+       ot->invoke = file_walk_select_invoke;
+       ot->poll = ED_operator_file_active;
+
+       /* properties */
+       prop = RNA_def_enum(ot->srna, "direction", direction_items, 0, "Walk Direction",
+                           "Select/Deselect file in this direction");
+       RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+       prop = RNA_def_boolean(ot->srna, "extend", false, "Extend",
+                              "Extend selection instead of deselecting everything first");
+       RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+       prop = RNA_def_boolean(ot->srna, "fill", false, "Fill", "Select everything beginning with the last selection");
+       RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+}
+
 static int file_select_all_exec(bContext *C, wmOperator *UNUSED(op))
 {
        ScrArea *sa = CTX_wm_area(C);
        SpaceFile *sfile = CTX_wm_space_file(C);
        FileSelection sel;
-       int numfiles = filelist_numfiles(sfile->files);
-       int i;
-       bool is_selected = false;
+       const int numfiles = filelist_numfiles(sfile->files);
+       const bool has_selection = file_is_any_selected(sfile->files);
 
        sel.first = 0; 
        sel.last = numfiles - 1;
 
-       /* Is any file selected ? */
-       for (i = 0; i < numfiles; ++i) {
-               if (filelist_is_selected(sfile->files, i, CHECK_ALL)) {
-                       is_selected = true;
-                       break;
-               }
-       }
        /* select all only if previously no file was selected */
-       if (is_selected) {
+       if (has_selection) {
                filelist_select(sfile->files, &sel, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
        }
        else {
                const FileCheckType check_type = (sfile->params->flag & FILE_DIRSEL_ONLY) ? CHECK_DIRS : CHECK_FILES;
+               int i;
+
                filelist_select(sfile->files, &sel, FILE_SEL_ADD, FILE_SEL_SELECTED, check_type);
+
+               /* set active_file to first selected */
+               for (i = 0; i < numfiles; i++) {
+                       if (filelist_is_selected(sfile->files, i, check_type)) {
+                               sfile->params->active_file = i;
+                               break;
+                       }
+               }
        }
+
        file_draw_check(C);
+       WM_event_add_mousemove(C);
        ED_area_tag_redraw(sa);
+
        return OPERATOR_FINISHED;
 }
 
@@ -748,28 +1001,28 @@ int file_highlight_set(SpaceFile *sfile, ARegion *ar, int mx, int my)
        numfiles = filelist_numfiles(sfile->files);
        params = ED_fileselect_get_params(sfile);
 
-       origfile = params->active_file;
+       origfile = params->highlight_file;
 
        mx -= ar->winrct.xmin;
        my -= ar->winrct.ymin;
 
        if (BLI_rcti_isect_pt(&ar->v2d.mask, mx, my)) {
                float fx, fy;
-               int active_file;
+               int highlight_file;
 
                UI_view2d_region_to_view(v2d, mx, my, &fx, &fy);
 
-               active_file = ED_fileselect_layout_offset(sfile->layout, (int)(v2d->tot.xmin + fx), (int)(v2d->tot.ymax - fy));
+               highlight_file = ED_fileselect_layout_offset(sfile->layout, (int)(v2d->tot.xmin + fx), (int)(v2d->tot.ymax - fy));
 
-               if ((active_file >= 0) && (active_file < numfiles))
-                       params->active_file = active_file;
+               if ((highlight_file >= 0) && (highlight_file < numfiles))
+                       params->highlight_file = highlight_file;
                else
-                       params->active_file = -1;
+                       params->highlight_file = -1;
        }
        else
-               params->active_file = -1;
+               params->highlight_file = -1;
 
-       return (params->active_file != origfile);
+       return (params->highlight_file != origfile);
 }
 
 static int file_highlight_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
@@ -1669,7 +1922,7 @@ static int file_rename_exec(bContext *C, wmOperator *UNUSED(op))
        SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C);
        
        if (sfile->params) {
-               int idx = sfile->params->active_file;
+               int idx = sfile->params->highlight_file;
                int numfiles = filelist_numfiles(sfile->files);
                if ( (0 <= idx) && (idx < numfiles) ) {
                        struct direntry *file = filelist_file(sfile->files, idx);
@@ -1690,7 +1943,7 @@ static int file_rename_poll(bContext *C)
        SpaceFile *sfile = CTX_wm_space_file(C);
 
        if (sfile && sfile->params) {
-               int idx = sfile->params->active_file;
+               int idx = sfile->params->highlight_file;
 
                if (idx >= 0) {
                        struct direntry *file = filelist_file(sfile->files, idx);
@@ -1699,7 +1952,7 @@ static int file_rename_poll(bContext *C)
                        }
                }
 
-               if (sfile->params->active_file < 0) {
+               if (sfile->params->highlight_file < 0) {
                        poll = 0;
                }
                else {
diff --git a/source/blender/editors/space_file/file_utils.c b/source/blender/editors/space_file/file_utils.c
new file mode 100644 (file)
index 0000000..8a80e4a
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/space_file/file_utils.c
+ *  \ingroup spfile
+ */
+
+#include "BLI_rect.h"
+
+#include "BKE_context.h"
+
+#include "ED_screen.h"
+#include "ED_fileselect.h"
+
+#include "WM_types.h"
+
+#include "file_intern.h"
+
+
+void file_tile_boundbox(const ARegion *ar, FileLayout *layout, const int file, rcti *r_bounds)
+{
+       int xmin, ymax;
+
+       ED_fileselect_layout_tilepos(layout, file, &xmin, &ymax);
+       BLI_rcti_init(r_bounds, xmin, xmin + layout->tile_w + layout->tile_border_x,
+                     ar->winy - ymax - layout->tile_h - layout->tile_border_y, ar->winy - ymax);
+}
index 62b4bfaf1793b4accb1bf316d2941a1250206f79..05786139845037538bef256f583da5d65b55d426 100644 (file)
@@ -697,7 +697,7 @@ void ED_fileselect_clear(struct wmWindowManager *wm, struct SpaceFile *sfile)
                filelist_free(sfile->files);
        }
 
-       sfile->params->active_file = -1;
+       sfile->params->highlight_file = -1;
        WM_main_add_notifier(NC_SPACE | ND_SPACE_FILE_LIST, NULL);
 }
 
index 782b318b8a26635ea51206475bd9eb25ac9a115e..c7e0e4ad4e9a70b7992da374573b9375d3adb0bf 100644 (file)
@@ -212,7 +212,7 @@ static void file_refresh(const bContext *C, ScrArea *sa)
        if (!sfile->files) {
                sfile->files = filelist_new(params->type);
                filelist_setdir(sfile->files, params->dir);
-               params->active_file = -1; /* added this so it opens nicer (ton) */
+               params->highlight_file = -1; /* added this so it opens nicer (ton) */
        }
        filelist_setsorting(sfile->files, params->sort);
        filelist_setfilter_options(sfile->files, params->flag & FILE_HIDE_DOT,
@@ -377,7 +377,7 @@ static void file_main_area_draw(const bContext *C, ARegion *ar)
        UI_view2d_view_ortho(v2d);
        
        /* on first read, find active file */
-       if (params->active_file == -1) {
+       if (params->highlight_file == -1) {
                wmEvent *event = CTX_wm_window(C)->eventstate;
                file_highlight_set(sfile, ar, event->x, event->y);
        }
@@ -397,6 +397,7 @@ static void file_main_area_draw(const bContext *C, ARegion *ar)
 static void file_operatortypes(void)
 {
        WM_operatortype_append(FILE_OT_select);
+       WM_operatortype_append(FILE_OT_select_walk);
        WM_operatortype_append(FILE_OT_select_all_toggle);
        WM_operatortype_append(FILE_OT_select_border);
        WM_operatortype_append(FILE_OT_select_bookmark);
@@ -462,6 +463,49 @@ static void file_keymap(struct wmKeyConfig *keyconf)
        RNA_boolean_set(kmi->ptr, "fill", true);
        RNA_boolean_set(kmi->ptr, "open", false);
 
+
+       /* arrow keys navigation (walk selecting) */
+       kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", UPARROWKEY, KM_PRESS, 0, 0);
+       RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_UP);
+       kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", UPARROWKEY, KM_PRESS, KM_SHIFT, 0);
+       RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_UP);
+       RNA_boolean_set(kmi->ptr, "extend", true);
+       kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", UPARROWKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0);
+       RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_UP);
+       RNA_boolean_set(kmi->ptr, "extend", true);
+       RNA_boolean_set(kmi->ptr, "fill", true);
+
+       kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", DOWNARROWKEY, KM_PRESS, 0, 0);
+       RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_DOWN);
+       kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", DOWNARROWKEY, KM_PRESS, KM_SHIFT, 0);
+       RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_DOWN);
+       RNA_boolean_set(kmi->ptr, "extend", true);
+       kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", DOWNARROWKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0);
+       RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_DOWN);
+       RNA_boolean_set(kmi->ptr, "extend", true);
+       RNA_boolean_set(kmi->ptr, "fill", true);
+
+       kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", LEFTARROWKEY, KM_PRESS, 0, 0);
+       RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_LEFT);
+       kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", LEFTARROWKEY, KM_PRESS, KM_SHIFT, 0);
+       RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_LEFT);
+       RNA_boolean_set(kmi->ptr, "extend", true);
+       kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", LEFTARROWKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0);
+       RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_LEFT);
+       RNA_boolean_set(kmi->ptr, "extend", true);
+       RNA_boolean_set(kmi->ptr, "fill", true);
+
+       kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", RIGHTARROWKEY, KM_PRESS, 0, 0);
+       RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_RIGHT);
+       kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", RIGHTARROWKEY, KM_PRESS, KM_SHIFT, 0);
+       RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_RIGHT);
+       RNA_boolean_set(kmi->ptr, "extend", true);
+       kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", RIGHTARROWKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0);
+       RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_RIGHT);
+       RNA_boolean_set(kmi->ptr, "extend", true);
+       RNA_boolean_set(kmi->ptr, "fill", true);
+
+
        /* front and back mouse folder navigation */
        WM_keymap_add_item(keymap, "FILE_OT_previous", BUTTON4MOUSE, KM_CLICK, 0, 0);
        WM_keymap_add_item(keymap, "FILE_OT_next", BUTTON5MOUSE, KM_CLICK, 0, 0);
index 9c023628164d73465fa5a01c3122e91efb6ac5d3..2098591e4a8fa6217bb7c0133bdd4c510607ad9a 100644 (file)
@@ -591,7 +591,8 @@ typedef struct FileSelectParams {
 
        char filter_search[64];  /* text items' name must match to be shown. */
 
-       int active_file;
+       int active_file;    /* active file used for keyboard navigation */
+       int highlight_file; /* file under cursor */
        int sel_first;
        int sel_last;
        unsigned short thumbnail_size;