diff options
author | Antonio Vazquez <blendergit@gmail.com> | 2019-10-02 14:15:37 +0300 |
---|---|---|
committer | Antonio Vazquez <blendergit@gmail.com> | 2019-10-02 14:15:37 +0300 |
commit | 386ba094988fc793f8e060d15438566e5e2d2cae (patch) | |
tree | 4cbde50b5d1d7a45c89ee99c29dd1b86d1d97b59 /source/blender/editors/space_file | |
parent | 6129e20cec4639aebf335ff13b2ba0c59670662d (diff) | |
parent | f97a64aa9b7b384f8221a1ef4f2eef9cde1238db (diff) |
Merge branch 'master' into temp-gpencil-drw-engine
Conflicts:
source/blender/draw/engines/gpencil/gpencil_engine.c
Diffstat (limited to 'source/blender/editors/space_file')
-rw-r--r-- | source/blender/editors/space_file/file_draw.c | 680 | ||||
-rw-r--r-- | source/blender/editors/space_file/file_intern.h | 23 | ||||
-rw-r--r-- | source/blender/editors/space_file/file_ops.c | 218 | ||||
-rw-r--r-- | source/blender/editors/space_file/file_panels.c | 116 | ||||
-rw-r--r-- | source/blender/editors/space_file/filelist.c | 177 | ||||
-rw-r--r-- | source/blender/editors/space_file/filelist.h | 6 | ||||
-rw-r--r-- | source/blender/editors/space_file/filesel.c | 369 | ||||
-rw-r--r-- | source/blender/editors/space_file/fsmenu.c | 19 | ||||
-rw-r--r-- | source/blender/editors/space_file/space_file.c | 169 |
9 files changed, 1112 insertions, 665 deletions
diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 3a6d59c1dbf..0aec6d5e6a0 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -26,6 +26,7 @@ #include <errno.h> #include "BLI_blenlib.h" +#include "BLI_fileops_types.h" #include "BLI_utildefines.h" #include "BLI_math.h" @@ -70,239 +71,57 @@ #include "file_intern.h" // own include -/* Dummy helper - we need dynamic tooltips here. */ -static char *file_draw_tooltip_func(bContext *UNUSED(C), void *argN, const char *UNUSED(tip)) +void ED_file_path_button(bScreen *screen, + const SpaceFile *sfile, + FileSelectParams *params, + uiBlock *block) { - char *dyn_tooltip = argN; - return BLI_strdup(dyn_tooltip); -} - -/* Note: This function uses pixelspace (0, 0, winx, winy), not view2d. - * The controls are laid out as follows: - * - * ------------------------------------------- - * | Directory input | execute | - * ------------------------------------------- - * | Filename input | + | - | cancel | - * ------------------------------------------- - * - * The input widgets will stretch to fill any excess space. - * When there isn't enough space for all controls to be shown, they are - * hidden in this order: x/-, execute/cancel, input widgets. - */ -void file_draw_buttons(const bContext *C, ARegion *ar) -{ - /* Button layout. */ - const int max_x = ar->winx - 10; - const int line1_y = ar->winy - (IMASEL_BUTTONS_HEIGHT / 2 + IMASEL_BUTTONS_MARGIN); - const int line2_y = line1_y - (IMASEL_BUTTONS_HEIGHT / 2 + IMASEL_BUTTONS_MARGIN); - const int input_minw = 20; - const int btn_h = UI_UNIT_Y; - const int btn_fn_w = UI_UNIT_X; - const int btn_minw = 80; - const int btn_margin = 20; - const int separator = 4; - - /* Additional locals. */ - char uiblockstr[32]; - int loadbutton; - int fnumbuttons; - int min_x = 10; - int chan_offs = 0; - int available_w = max_x - min_x; - int line1_w = available_w; - int line2_w = available_w; - + PointerRNA params_rna_ptr; uiBut *but; - uiBlock *block; - SpaceFile *sfile = CTX_wm_space_file(C); - FileSelectParams *params = ED_fileselect_get_params(sfile); - ARegion *artmp; - const bool is_browse_only = (sfile->op == NULL); - - /* Initialize UI block. */ - BLI_snprintf(uiblockstr, sizeof(uiblockstr), "win %p", (void *)ar); - block = UI_block_begin(C, ar, uiblockstr, UI_EMBOSS); - - /* exception to make space for collapsed region icon */ - for (artmp = CTX_wm_area(C)->regionbase.first; artmp; artmp = artmp->next) { - if (artmp->regiontype == RGN_TYPE_TOOLS && artmp->flag & RGN_FLAG_HIDDEN) { - chan_offs = 16; - min_x += chan_offs; - available_w -= chan_offs; - } - } - - /* Is there enough space for the execute / cancel buttons? */ - if (is_browse_only) { - loadbutton = 0; - } - else { - const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - loadbutton = UI_fontstyle_string_width(fstyle, params->title) + btn_margin; - CLAMP_MIN(loadbutton, btn_minw); - if (available_w <= loadbutton + separator + input_minw) { - loadbutton = 0; - } + RNA_pointer_create(&screen->id, &RNA_FileSelectParams, params, ¶ms_rna_ptr); + + /* callbacks for operator check functions */ + UI_block_func_set(block, file_draw_check_cb, NULL, NULL); + + but = uiDefButR(block, + UI_BTYPE_TEXT, + -1, + "", + 0, + 0, + UI_UNIT_X * 10, + UI_UNIT_Y, + ¶ms_rna_ptr, + "directory", + 0, + 0.0f, + (float)FILE_MAX, + 0.0f, + 0.0f, + TIP_("File path")); + + BLI_assert(!UI_but_flag_is_set(but, UI_BUT_UNDO)); + BLI_assert(!UI_but_is_utf8(but)); + + UI_but_func_complete_set(but, autocomplete_directory, NULL); + UI_but_funcN_set(but, file_directory_enter_handle, NULL, but); + + /* TODO, directory editing is non-functional while a library is loaded + * until this is properly supported just disable it. */ + if (sfile && sfile->files && filelist_lib(sfile->files)) { + UI_but_flag_enable(but, UI_BUT_DISABLED); } - if (loadbutton) { - line1_w -= (loadbutton + separator); - line2_w = line1_w; - } - - /* Is there enough space for file number increment/decrement buttons? */ - fnumbuttons = 2 * btn_fn_w; - if (!loadbutton || line2_w <= fnumbuttons + separator + input_minw) { - fnumbuttons = 0; - } - else { - line2_w -= (fnumbuttons + separator); - } - - /* Text input fields for directory and file. */ - if (available_w > 0) { - const struct FileDirEntry *file = sfile->files ? - filelist_file(sfile->files, params->active_file) : - NULL; - int overwrite_alert = file_draw_check_exists(sfile); - const bool is_active_dir = file && (file->typeflag & FILE_TYPE_FOLDER); - - /* callbacks for operator check functions */ - UI_block_func_set(block, file_draw_check_cb, NULL, NULL); - - but = uiDefBut(block, - UI_BTYPE_TEXT, - -1, - "", - min_x, - line1_y, - line1_w - chan_offs, - btn_h, - params->dir, - 0.0, - (float)FILE_MAX, - 0, - 0, - TIP_("File path")); - UI_but_func_complete_set(but, autocomplete_directory, NULL); - UI_but_flag_enable(but, UI_BUT_NO_UTF8); - UI_but_flag_disable(but, UI_BUT_UNDO); - UI_but_funcN_set(but, file_directory_enter_handle, NULL, but); - - /* TODO, directory editing is non-functional while a library is loaded - * until this is properly supported just disable it. */ - if (sfile->files && filelist_lib(sfile->files)) { - UI_but_flag_enable(but, UI_BUT_DISABLED); - } - - if ((params->flag & FILE_DIRSEL_ONLY) == 0) { - but = uiDefBut( - block, - UI_BTYPE_TEXT, - -1, - "", - min_x, - line2_y, - line2_w - chan_offs, - btn_h, - is_active_dir ? (char *)"" : params->file, - 0.0, - (float)FILE_MAXFILE, - 0, - 0, - TIP_(overwrite_alert ? N_("File name, overwrite existing") : N_("File name"))); - UI_but_func_complete_set(but, autocomplete_file, NULL); - UI_but_flag_enable(but, UI_BUT_NO_UTF8); - UI_but_flag_disable(but, UI_BUT_UNDO); - /* silly workaround calling NFunc to ensure this does not get called - * immediate ui_apply_but_func but only after button deactivates */ - UI_but_funcN_set(but, file_filename_enter_handle, NULL, but); - - /* check if this overrides a file and if the operator option is used */ - if (overwrite_alert) { - UI_but_flag_enable(but, UI_BUT_REDALERT); - } - } - - /* clear func */ - UI_block_func_set(block, NULL, NULL, NULL); - } - - /* Filename number increment / decrement buttons. */ - if (fnumbuttons && (params->flag & FILE_DIRSEL_ONLY) == 0) { - UI_block_align_begin(block); - but = uiDefIconButO(block, - UI_BTYPE_BUT, - "FILE_OT_filenum", - 0, - ICON_REMOVE, - min_x + line2_w + separator - chan_offs, - line2_y, - btn_fn_w, - btn_h, - TIP_("Decrement the filename number")); - RNA_int_set(UI_but_operator_ptr_get(but), "increment", -1); - - but = uiDefIconButO(block, - UI_BTYPE_BUT, - "FILE_OT_filenum", - 0, - ICON_ADD, - min_x + line2_w + separator + btn_fn_w - chan_offs, - line2_y, - btn_fn_w, - btn_h, - TIP_("Increment the filename number")); - RNA_int_set(UI_but_operator_ptr_get(but), "increment", 1); - UI_block_align_end(block); - } - - /* Execute / cancel buttons. */ - if (loadbutton) { - const struct FileDirEntry *file = sfile->files ? - filelist_file(sfile->files, params->active_file) : - NULL; - char const *str_exec; - - if (file && FILENAME_IS_PARENT(file->relpath)) { - str_exec = IFACE_("Parent Directory"); - } - else if (file && file->typeflag & FILE_TYPE_DIR) { - str_exec = IFACE_("Open Directory"); - } - else { - str_exec = params->title; /* params->title is already translated! */ - } - - but = uiDefButO(block, - UI_BTYPE_BUT, - "FILE_OT_execute", - WM_OP_EXEC_REGION_WIN, - str_exec, - max_x - loadbutton, - line1_y, - loadbutton, - btn_h, - ""); - /* Just a display hint. */ - UI_but_flag_enable(but, UI_BUT_ACTIVE_DEFAULT); - - uiDefButO(block, - UI_BTYPE_BUT, - "FILE_OT_cancel", - WM_OP_EXEC_REGION_WIN, - IFACE_("Cancel"), - max_x - loadbutton, - line2_y, - loadbutton, - btn_h, - ""); - } + /* clear func */ + UI_block_func_set(block, NULL, NULL, NULL); +} - UI_block_end(C, block); - UI_block_draw(C, block); +/* Dummy helper - we need dynamic tooltips here. */ +static char *file_draw_tooltip_func(bContext *UNUSED(C), void *argN, const char *UNUSED(tip)) +{ + char *dyn_tooltip = argN; + return BLI_strdup(dyn_tooltip); } static void draw_tile(int sx, int sy, int width, int height, int colorid, int shade) @@ -319,13 +138,10 @@ static void file_draw_icon( { uiBut *but; int x, y; - // float alpha = 1.0f; x = sx; y = sy - height; - /*if (icon == ICON_FILE_BLANK) alpha = 0.375f;*/ - but = uiDefIconBut( block, UI_BTYPE_LABEL, 0, icon, x, y, width, height, NULL, 0.0f, 0.0f, 0.0f, 0.0f, NULL); UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path)); @@ -349,7 +165,7 @@ static void file_draw_string(int sx, rcti rect; char fname[FILE_MAXFILE]; - if (string[0] == '\0') { + if (string[0] == '\0' || width < 1) { return; } @@ -362,7 +178,7 @@ static void file_draw_string(int sx, /* no text clipping needed, UI_fontstyle_draw does it but is a bit too strict * (for buttons it works) */ rect.xmin = sx; - rect.xmax = (int)(sx + ceil(width + 5.0f / UI_DPI_FAC)); + rect.xmax = sx + round_fl_to_int(width); rect.ymin = sy - height; rect.ymax = sy; @@ -404,8 +220,8 @@ static void file_draw_preview(uiBlock *block, float scaledx, scaledy; float scale; int ex, ey; - bool use_dropshadow = !is_icon && (typeflags & FILE_TYPE_IMAGE); - float col[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + bool show_outline = !is_icon && + (typeflags & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_BLENDER)); BLI_assert(imb != NULL); @@ -440,15 +256,20 @@ static void file_draw_preview(uiBlock *block, xco = sx + (int)dx; yco = sy - layout->prv_h + (int)dy; - /* shadow */ - if (use_dropshadow) { - UI_draw_box_shadow(220, (float)xco, (float)yco, (float)(xco + ex), (float)(yco + ey)); - } - GPU_blend(true); - /* the image */ - if (!is_icon && typeflags & FILE_TYPE_FTFONT) { + /* the large image */ + + float col[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + if (is_icon) { + if (typeflags & FILE_TYPE_DIR) { + UI_GetThemeColor4fv(TH_ICON_FOLDER, col); + } + else { + UI_GetThemeColor4fv(TH_TEXT, col); + } + } + else if (typeflags & FILE_TYPE_FTFONT) { UI_GetThemeColor4fv(TH_TEXT, col); } @@ -477,30 +298,58 @@ static void file_draw_preview(uiBlock *block, GPU_blend_set_func_separate( GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - if (icon) { - UI_icon_draw_ex((float)xco + (7 * UI_DPI_FAC), - (float)yco + (7 * UI_DPI_FAC), - icon, - icon_aspect, - 1.0f, - 0.0f, - NULL, - false); + if (icon && (icon != ICON_FILE_FONT)) { + /* size of center icon is scaled to fit container and UI scale */ + float icon_x, icon_y; + + if (is_icon) { + const float icon_size = 16.0f / icon_aspect * U.dpi_fac; + float icon_opacity = 0.3f; + uchar icon_color[4] = {0, 0, 0, 255}; + float bgcolor[4]; + UI_GetThemeColor4fv(TH_ICON_FOLDER, bgcolor); + if (rgb_to_grayscale(bgcolor) < 0.5f) { + icon_color[0] = 255; + icon_color[1] = 255; + icon_color[2] = 255; + } + icon_x = xco + (ex / 2.0f) - (icon_size / 2.0f); + icon_y = yco + (ey / 2.0f) - (icon_size * ((typeflags & FILE_TYPE_DIR) ? 0.78f : 0.75f)); + UI_icon_draw_ex( + icon_x, icon_y, icon, icon_aspect / U.dpi_fac, icon_opacity, 0.0f, icon_color, false); + } + else { + const uchar dark[4] = {0, 0, 0, 255}; + const uchar light[4] = {255, 255, 255, 255}; + + /* Smaller, fainter icon for preview image thumbnail. */ + icon_x = xco + (2.0f * UI_DPI_FAC); + icon_y = yco + (2.0f * UI_DPI_FAC); + + UI_icon_draw_ex(icon_x + 1, icon_y - 1, icon, 1.0f / U.dpi_fac, 0.2f, 0.0f, dark, false); + UI_icon_draw_ex(icon_x, icon_y, icon, 1.0f / U.dpi_fac, 0.6f, 0.0f, light, false); + } } - /* border */ - if (use_dropshadow) { + /* Contrasting outline around some preview types. */ + if (show_outline) { GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformColor4f(0.0f, 0.0f, 0.0f, 0.4f); + float border_color[4] = {1.0f, 1.0f, 1.0f, 0.4f}; + float bgcolor[4]; + UI_GetThemeColor4fv(TH_BACK, bgcolor); + if (rgb_to_grayscale(bgcolor) > 0.5f) { + border_color[0] = 0.0f; + border_color[1] = 0.0f; + border_color[2] = 0.0f; + } + immUniformColor4fv(border_color); imm_draw_box_wire_2d(pos, (float)xco, (float)yco, (float)(xco + ex), (float)(yco + ey)); immUnbindProgram(); } but = uiDefBut(block, UI_BTYPE_LABEL, 0, "", xco, yco, ex, ey, NULL, 0.0, 0.0, 0, 0, NULL); - UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path)); /* dragregion */ if (drag) { @@ -557,6 +406,7 @@ static void renamebutton_cb(bContext *C, void *UNUSED(arg1), char *oldname) static void draw_background(FileLayout *layout, View2D *v2d) { + const int item_height = layout->tile_h + (2 * layout->tile_border_y); int i; int sy; @@ -565,9 +415,11 @@ static void draw_background(FileLayout *layout, View2D *v2d) immUniformThemeColorShade(TH_BACK, -7); /* alternating flat shade background */ - for (i = 0; (i <= layout->rows); i += 2) { - sy = (int)v2d->cur.ymax - i * (layout->tile_h + 2 * layout->tile_border_y) - - layout->tile_border_y; + for (i = 2; (i <= layout->rows + 1); i += 2) { + sy = (int)v2d->cur.ymax - layout->offset_top - i * item_height - layout->tile_border_y; + + /* Offsett pattern slightly to add scroll effect. */ + sy += round_fl_to_int(item_height * (v2d->tot.ymax - v2d->cur.ymax) / item_height); immRectf(pos, v2d->cur.xmin, @@ -632,6 +484,176 @@ static void draw_dividers(FileLayout *layout, View2D *v2d) } } +static void draw_columnheader_background(const FileLayout *layout, const View2D *v2d) +{ + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformThemeColorShade(TH_BACK, 11); + + immRectf(pos, + v2d->cur.xmin, + v2d->cur.ymax - layout->attribute_column_header_h, + v2d->cur.xmax, + v2d->cur.ymax); + + immUnbindProgram(); +} + +static void draw_columnheader_columns(const FileSelectParams *params, + FileLayout *layout, + const View2D *v2d, + const uchar text_col[4]) +{ + const float divider_pad = 0.2 * layout->attribute_column_header_h; + int sx = v2d->cur.xmin, sy = v2d->cur.ymax; + + for (FileAttributeColumnType column_type = 0; column_type < ATTRIBUTE_COLUMN_MAX; + column_type++) { + if (!file_attribute_column_type_enabled(params, column_type)) { + continue; + } + const FileAttributeColumn *column = &layout->attribute_columns[column_type]; + + /* Active sort type triangle */ + if (params->sort == column->sort_type) { + float tri_color[4]; + + rgba_uchar_to_float(tri_color, text_col); + UI_draw_icon_tri(sx + column->width - (0.3f * U.widget_unit) - + ATTRIBUTE_COLUMN_PADDING / 2.0f, + sy + (0.1f * U.widget_unit) - (layout->attribute_column_header_h / 2), + (params->flag & FILE_SORT_INVERT) ? 't' : 'v', + tri_color); + } + + file_draw_string(sx + ATTRIBUTE_COLUMN_PADDING, + sy - layout->tile_border_y, + IFACE_(column->name), + column->width - 2 * ATTRIBUTE_COLUMN_PADDING, + layout->attribute_column_header_h - layout->tile_border_y, + UI_STYLE_TEXT_LEFT, + text_col); + + /* Separator line */ + if (column_type != COLUMN_NAME) { + uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformThemeColorShade(TH_BACK, -10); + immBegin(GPU_PRIM_LINES, 2); + immVertex2f(pos, sx - 1, sy - divider_pad); + immVertex2f(pos, sx - 1, sy - layout->attribute_column_header_h + divider_pad); + immEnd(); + immUnbindProgram(); + } + + sx += column->width; + } + + /* Vertical separator lines line */ + { + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformThemeColorShade(TH_BACK, -10); + immBegin(GPU_PRIM_LINES, 4); + immVertex2f(pos, v2d->cur.xmin, sy); + immVertex2f(pos, v2d->cur.xmax, sy); + immVertex2f(pos, v2d->cur.xmin, sy - layout->attribute_column_header_h); + immVertex2f(pos, v2d->cur.xmax, sy - layout->attribute_column_header_h); + immEnd(); + immUnbindProgram(); + } +} + +/** + * Updates the stat string stored in file->entry if necessary. + */ +static const char *filelist_get_details_column_string(FileAttributeColumnType column, + const FileDirEntry *file, + const bool small_size, + const bool update_stat_strings) +{ + switch (column) { + case COLUMN_DATETIME: + if (!(file->typeflag & FILE_TYPE_BLENDERLIB) && !FILENAME_IS_CURRPAR(file->relpath)) { + if ((file->entry->datetime_str[0] == '\0') || update_stat_strings) { + char date[FILELIST_DIRENTRY_DATE_LEN], time[FILELIST_DIRENTRY_TIME_LEN]; + bool is_today, is_yesterday; + + BLI_filelist_entry_datetime_to_string( + NULL, file->entry->time, small_size, time, date, &is_today, &is_yesterday); + + if (is_today || is_yesterday) { + BLI_strncpy(date, is_today ? N_("Today") : N_("Yesterday"), sizeof(date)); + } + BLI_snprintf( + file->entry->datetime_str, sizeof(file->entry->datetime_str), "%s %s", date, time); + } + + return file->entry->datetime_str; + } + break; + case COLUMN_SIZE: + if ((file->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) || + !(file->typeflag & (FILE_TYPE_DIR | FILE_TYPE_BLENDERLIB))) { + if ((file->entry->size_str[0] == '\0') || update_stat_strings) { + BLI_filelist_entry_size_to_string( + NULL, file->entry->size, small_size, file->entry->size_str); + } + + return file->entry->size_str; + } + break; + default: + break; + } + + return NULL; +} + +static void draw_details_columns(const FileSelectParams *params, + const FileLayout *layout, + const FileDirEntry *file, + const int pos_x, + const int pos_y, + const uchar text_col[4]) +{ + const bool small_size = SMALL_SIZE_CHECK(params->thumbnail_size); + const bool update_stat_strings = small_size != SMALL_SIZE_CHECK(layout->curr_size); + int sx = pos_x - layout->tile_border_x - (UI_UNIT_X * 0.1f), sy = pos_y; + + for (FileAttributeColumnType column_type = 0; column_type < ATTRIBUTE_COLUMN_MAX; + column_type++) { + const FileAttributeColumn *column = &layout->attribute_columns[column_type]; + + /* Name column is not a detail column (should already be drawn), always skip here. */ + if (column_type == COLUMN_NAME) { + sx += column->width; + continue; + } + if (!file_attribute_column_type_enabled(params, column_type)) { + continue; + } + + const char *str = filelist_get_details_column_string( + column_type, file, small_size, update_stat_strings); + + if (str) { + file_draw_string(sx + ATTRIBUTE_COLUMN_PADDING, + sy - layout->tile_border_y, + IFACE_(str), + column->width - 2 * ATTRIBUTE_COLUMN_PADDING, + layout->tile_h, + column->text_align, + text_col); + } + + sx += column->width; + } +} + void file_draw_list(const bContext *C, ARegion *ar) { SpaceFile *sfile = CTX_wm_space_file(C); @@ -652,18 +674,14 @@ void file_draw_list(const bContext *C, ARegion *ar) bool is_icon; eFontStyle_Align align; bool do_drag; - int column_space = 0.6f * UI_UNIT_X; unsigned char text_col[4]; - const bool small_size = SMALL_SIZE_CHECK(params->thumbnail_size); - const bool update_stat_strings = small_size != SMALL_SIZE_CHECK(layout->curr_size); - const float thumb_icon_aspect = sqrtf(64.0f / (float)(params->thumbnail_size)); + const bool draw_columnheader = (params->display == FILE_VERTICALDISPLAY); + const float thumb_icon_aspect = MIN2(64.0f / (float)(params->thumbnail_size), 1.0f); numfiles = filelist_files_ensure(files); if (params->display != FILE_IMGDISPLAY) { - draw_background(layout, v2d); - draw_dividers(layout, v2d); } @@ -679,13 +697,14 @@ void file_draw_list(const bContext *C, ARegion *ar) numfiles_layout += layout->rows; } else { - numfiles_layout += layout->columns; + numfiles_layout += layout->flow_columns; } filelist_file_cache_slidingwindow_set(files, numfiles_layout); - textwidth = (FILE_IMGDISPLAY == params->display) ? layout->tile_w : - (int)layout->column_widths[COLUMN_NAME]; + textwidth = (FILE_IMGDISPLAY == params->display) ? + layout->tile_w : + round_fl_to_int(layout->attribute_columns[COLUMN_NAME].width); textheight = (int)(layout->textheight * 3.0 / 2.0 + 0.5); align = (FILE_IMGDISPLAY == params->display) ? UI_STYLE_TEXT_CENTER : UI_STYLE_TEXT_LEFT; @@ -719,11 +738,16 @@ void file_draw_list(const bContext *C, ARegion *ar) BLF_batch_draw_begin(); + UI_GetThemeColor4ubv(TH_TEXT, text_col); + for (i = offset; (i < numfiles) && (i < offset + numfiles_layout); i++) { unsigned int file_selflag; char path[FILE_MAX_LIBEXTRA]; + int padx = 0.1f * UI_UNIT_X; + int icon_ofs = 0; + ED_fileselect_layout_tilepos(layout, i, &sx, &sy); - sx += (int)(v2d->tot.xmin + 0.1f * UI_UNIT_X); + sx += (int)(v2d->tot.xmin + padx); sy = (int)(v2d->tot.ymax - sy); file = filelist_file(files, i); @@ -737,15 +761,14 @@ void file_draw_list(const bContext *C, ARegion *ar) int colorid = (file_selflag & FILE_SEL_SELECTED) ? TH_HILITE : TH_BACK; int shade = (params->highlight_file == i) || (file_selflag & FILE_SEL_HIGHLIGHTED) ? 35 : 0; + const short width = ELEM(params->display, FILE_VERTICALDISPLAY, FILE_HORIZONTALDISPLAY) ? + layout->tile_w - (2 * padx) : + layout->tile_w; BLI_assert(i == 0 || !FILENAME_IS_CURRPAR(file->relpath)); - draw_tile(sx, - sy - 1, - layout->tile_w + 4, - sfile->layout->tile_h + layout->tile_border_y, - colorid, - shade); + draw_tile( + sx, sy - 1, width, sfile->layout->tile_h + layout->tile_border_y, colorid, shade); } } UI_draw_roundbox_corner_set(UI_CNR_NONE); @@ -778,38 +801,28 @@ void file_draw_list(const bContext *C, ARegion *ar) file_draw_icon(block, path, sx, - sy - (UI_UNIT_Y / 6), + sy - layout->tile_border_y, filelist_geticon(files, i, true), ICON_DEFAULT_WIDTH_SCALE, ICON_DEFAULT_HEIGHT_SCALE, do_drag); - sx += ICON_DEFAULT_WIDTH_SCALE + 0.2f * UI_UNIT_X; + icon_ofs += ICON_DEFAULT_WIDTH_SCALE + 0.2f * UI_UNIT_X; } - UI_GetThemeColor4ubv(TH_TEXT, text_col); - if (file_selflag & FILE_SEL_EDITING) { uiBut *but; - short width; - - if (params->display == FILE_SHORTDISPLAY) { - width = layout->tile_w - (ICON_DEFAULT_WIDTH_SCALE + 0.2f * UI_UNIT_X); - } - else if (params->display == FILE_LONGDISPLAY) { - width = layout->column_widths[COLUMN_NAME] + (column_space * 3.5f); - } - else { - BLI_assert(params->display == FILE_IMGDISPLAY); - width = textwidth; - } + const short width = (params->display == FILE_IMGDISPLAY) ? + textwidth : + layout->attribute_columns[COLUMN_NAME].width - + ATTRIBUTE_COLUMN_PADDING; but = uiDefBut(block, UI_BTYPE_TEXT, 1, "", - sx, + sx + icon_ofs, sy - layout->tile_h - 0.15f * UI_UNIT_X, - width, + width - icon_ofs, textheight, sfile->params->renamefile, 1.0f, @@ -825,74 +838,19 @@ void file_draw_list(const bContext *C, ARegion *ar) sfile->files, file, FILE_SEL_REMOVE, FILE_SEL_EDITING, CHECK_ALL); } } - - if (!(file_selflag & FILE_SEL_EDITING)) { - int tpos = (FILE_IMGDISPLAY == params->display) ? sy - layout->tile_h + layout->textheight : - sy; - file_draw_string(sx + 1, tpos, file->name, (float)textwidth, textheight, align, text_col); - } - - sx += (int)layout->column_widths[COLUMN_NAME] + column_space; - if (params->display == FILE_SHORTDISPLAY) { - if ((file->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) || - !(file->typeflag & (FILE_TYPE_DIR | FILE_TYPE_BLENDERLIB))) { - if ((file->entry->size_str[0] == '\0') || update_stat_strings) { - BLI_filelist_entry_size_to_string( - NULL, file->entry->size, small_size, file->entry->size_str); - } - file_draw_string(sx, - sy, - file->entry->size_str, - layout->column_widths[COLUMN_SIZE], - layout->tile_h, - align, - text_col); - } - sx += (int)layout->column_widths[COLUMN_SIZE] + column_space; + else { + const int txpos = (params->display == FILE_IMGDISPLAY) ? sx : sx + 1 + icon_ofs; + const int typos = (params->display == FILE_IMGDISPLAY) ? + sy - layout->tile_h + layout->textheight : + sy - layout->tile_border_y; + const int twidth = (params->display == FILE_IMGDISPLAY) ? + textwidth : + textwidth - 1 - icon_ofs - padx - layout->tile_border_x; + file_draw_string(txpos, typos, file->name, (float)twidth, textheight, align, text_col); } - else if (params->display == FILE_LONGDISPLAY) { - if (!(file->typeflag & FILE_TYPE_BLENDERLIB) && !FILENAME_IS_CURRPAR(file->relpath)) { - if ((file->entry->date_str[0] == '\0') || update_stat_strings) { - BLI_filelist_entry_datetime_to_string( - NULL, file->entry->time, small_size, file->entry->time_str, file->entry->date_str); - } - file_draw_string(sx, - sy, - file->entry->date_str, - layout->column_widths[COLUMN_DATE], - layout->tile_h, - align, - text_col); - sx += (int)layout->column_widths[COLUMN_DATE] + column_space; - file_draw_string(sx, - sy, - file->entry->time_str, - layout->column_widths[COLUMN_TIME], - layout->tile_h, - align, - text_col); - sx += (int)layout->column_widths[COLUMN_TIME] + column_space; - } - else { - sx += (int)layout->column_widths[COLUMN_DATE] + column_space; - sx += (int)layout->column_widths[COLUMN_TIME] + column_space; - } - if ((file->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) || - !(file->typeflag & (FILE_TYPE_DIR | FILE_TYPE_BLENDERLIB))) { - if ((file->entry->size_str[0] == '\0') || update_stat_strings) { - BLI_filelist_entry_size_to_string( - NULL, file->entry->size, small_size, file->entry->size_str); - } - file_draw_string(sx, - sy, - file->entry->size_str, - layout->column_widths[COLUMN_SIZE], - layout->tile_h, - align, - text_col); - } - sx += (int)layout->column_widths[COLUMN_SIZE] + column_space; + if (params->display != FILE_IMGDISPLAY) { + draw_details_columns(params, layout, file, sx, sy, text_col); } } @@ -901,5 +859,11 @@ void file_draw_list(const bContext *C, ARegion *ar) UI_block_end(C, block); UI_block_draw(C, block); + /* Draw last, on top of file list. */ + if (draw_columnheader) { + draw_columnheader_background(layout, v2d); + draw_columnheader_columns(params, layout, v2d, text_col); + } + layout->curr_size = params->thumbnail_size; } diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index bad25511dd5..b0ff67844d8 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -30,9 +30,7 @@ struct ARegion; struct ARegionType; struct FileSelectParams; struct SpaceFile; - -/* file_ops.c */ -struct ARegion *file_tools_region(struct ScrArea *sa); +struct View2D; /* file_draw.c */ #define TILE_BORDER_X (UI_UNIT_X / 4) @@ -42,9 +40,10 @@ struct ARegion *file_tools_region(struct ScrArea *sa); #define IMASEL_BUTTONS_HEIGHT (UI_UNIT_Y * 2) #define IMASEL_BUTTONS_MARGIN (UI_UNIT_Y / 6) +#define ATTRIBUTE_COLUMN_PADDING (0.5f * UI_UNIT_X) + #define SMALL_SIZE_CHECK(_size) ((_size) < 64) /* Related to FileSelectParams.thumbnail_size. */ -void file_draw_buttons(const bContext *C, ARegion *ar); void file_calc_previews(const bContext *C, ARegion *ar); void file_draw_list(const bContext *C, ARegion *ar); @@ -64,6 +63,7 @@ typedef enum WalkSelectDirection { } WalkSelectDirections; void FILE_OT_highlight(struct wmOperatorType *ot); +void FILE_OT_sort_column_ui_context(struct wmOperatorType *ot); void FILE_OT_select(struct wmOperatorType *ot); void FILE_OT_select_walk(struct wmOperatorType *ot); void FILE_OT_select_all(struct wmOperatorType *ot); @@ -82,7 +82,6 @@ void FILE_OT_directory_new(struct wmOperatorType *ot); void FILE_OT_previous(struct wmOperatorType *ot); void FILE_OT_next(struct wmOperatorType *ot); void FILE_OT_refresh(struct wmOperatorType *ot); -void FILE_OT_bookmark_toggle(struct wmOperatorType *ot); void FILE_OT_filenum(struct wmOperatorType *ot); void FILE_OT_delete(struct wmOperatorType *ot); void FILE_OT_rename(struct wmOperatorType *ot); @@ -108,10 +107,21 @@ void file_sfile_to_operator_ex(bContext *C, struct SpaceFile *sfile, char *filepath); void file_sfile_to_operator(bContext *C, struct wmOperator *op, struct SpaceFile *sfile); + void file_operator_to_sfile(bContext *C, struct SpaceFile *sfile, struct wmOperator *op); /* filesel.c */ void fileselect_file_set(SpaceFile *sfile, const int index); +bool file_attribute_column_type_enabled(const FileSelectParams *params, + FileAttributeColumnType column); +bool file_attribute_column_header_is_inside(const struct View2D *v2d, + const FileLayout *layout, + int x, + int y); +FileAttributeColumnType file_attribute_column_type_find_isect(const View2D *v2d, + const FileSelectParams *params, + FileLayout *layout, + int x); float file_string_width(const char *str); float file_font_pointsize(void); @@ -122,7 +132,8 @@ int autocomplete_file(struct bContext *C, char *str, void *arg_v); void file_params_renamefile_activate(struct SpaceFile *sfile, struct FileSelectParams *params); /* file_panels.c */ -void file_panels_register(struct ARegionType *art); +void file_tool_props_region_panels_register(struct ARegionType *art); +void file_execute_region_panels_register(struct ARegionType *art); /* file_utils.c */ void file_tile_boundbox(const ARegion *ar, FileLayout *layout, const int file, rcti *r_bounds); diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index eb5f02b6e13..b4b51de302d 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -78,7 +78,12 @@ static FileSelection find_file_mouse_rect(SpaceFile *sfile, ARegion *ar, const r BLI_rctf_rcti_copy(&rect_region_fl, rect_region); + /* Okay, manipulating v2d rects here is hacky... */ + v2d->mask.ymax -= sfile->layout->offset_top; + v2d->cur.ymax -= sfile->layout->offset_top; UI_view2d_region_to_view_rctf(v2d, &rect_region_fl, &rect_view_fl); + v2d->mask.ymax += sfile->layout->offset_top; + v2d->cur.ymax += sfile->layout->offset_top; BLI_rcti_init(&rect_view, (int)(v2d->tot.xmin + rect_view_fl.xmin), @@ -190,7 +195,6 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen) const bool is_parent_dir = FILENAME_IS_PARENT(file->relpath); if (do_diropen == false) { - params->file[0] = '\0'; retval = FILE_SELECT_DIR; } /* the path is too long and we are not going up! */ @@ -235,7 +239,7 @@ static bool file_is_any_selected(struct FileList *files) int i; /* Is any file selected ? */ - for (i = 0; i < numfiles; ++i) { + for (i = 0; i < numfiles; i++) { if (filelist_entry_select_index_get(files, i, CHECK_ALL)) { return true; } @@ -262,8 +266,8 @@ static void file_ensure_inside_viewbounds(ARegion *ar, SpaceFile *sfile, const i cur->ymax = cur->ymin + ar->winy; } /* up */ - else if (cur->ymax < rect.ymax) { - cur->ymax = rect.ymax + layout->tile_border_y; + else if ((cur->ymax - layout->offset_top) < rect.ymax) { + cur->ymax = rect.ymax + layout->tile_border_y + layout->offset_top; cur->ymin = cur->ymax - ar->winy; } /* left - also use if tile is wider than viewbounds so view is aligned to file name */ @@ -278,7 +282,7 @@ static void file_ensure_inside_viewbounds(ARegion *ar, SpaceFile *sfile, const i } else { BLI_assert(cur->xmin <= rect.xmin && cur->xmax >= rect.xmax && cur->ymin <= rect.ymin && - cur->ymax >= rect.ymax); + (cur->ymax - layout->offset_top) >= rect.ymax); changed = false; } @@ -384,7 +388,7 @@ static int file_box_select_modal(bContext *C, wmOperator *op, const wmEvent *eve if (result == OPERATOR_RUNNING_MODAL) { WM_operator_properties_border_to_rcti(op, &rect); - BLI_rcti_isect(&(ar->v2d.mask), &rect, &rect); + ED_fileselect_layout_isect_rect(sfile->layout, &ar->v2d, &rect, &rect); sel = file_selection_get(C, &rect, 0); if ((sel.first != params->sel_first) || (sel.last != params->sel_last)) { @@ -440,13 +444,13 @@ static int file_box_select_exec(bContext *C, wmOperator *op) file_deselect_all(sfile, FILE_SEL_SELECTED); } - BLI_rcti_isect(&(ar->v2d.mask), &rect, &rect); + ED_fileselect_layout_isect_rect(sfile->layout, &ar->v2d, &rect, &rect); ret = file_select(C, &rect, select ? FILE_SEL_ADD : FILE_SEL_REMOVE, false, false); /* unselect '..' parent entry - it's not supposed to be selected if more than * one file is selected */ - filelist_entry_select_index_set(sfile->files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); + filelist_entry_parent_select_set(sfile->files, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); if (FILE_SELECT_DIR == ret) { WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); @@ -485,6 +489,7 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) const bool extend = RNA_boolean_get(op->ptr, "extend"); const bool fill = RNA_boolean_get(op->ptr, "fill"); const bool do_diropen = RNA_boolean_get(op->ptr, "open"); + const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); if (ar->regiontype != RGN_TYPE_WINDOW) { return OPERATOR_CANCELLED; @@ -493,7 +498,7 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) rect.xmin = rect.xmax = event->mval[0]; rect.ymin = rect.ymax = event->mval[1]; - if (!BLI_rcti_isect_pt(&ar->v2d.mask, rect.xmin, rect.ymin)) { + if (!ED_fileselect_layout_is_inside_pt(sfile->layout, &ar->v2d, rect.xmin, rect.ymin)) { return OPERATOR_CANCELLED; } @@ -514,14 +519,18 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (extend) { /* unselect '..' parent entry - it's not supposed to be selected if more * than one file is selected */ - filelist_entry_select_index_set( - sfile->files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); + filelist_entry_parent_select_set(sfile->files, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); } - if (FILE_SELECT_DIR == ret) { + if (ret == FILE_SELECT_NOTHING) { + if (deselect_all) { + file_deselect_all(sfile, FILE_SEL_SELECTED); + } + } + else if (ret == FILE_SELECT_DIR) { WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); } - else if (FILE_SELECT_FILE == ret) { + else if (ret == FILE_SELECT_FILE) { WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); } @@ -537,8 +546,8 @@ void FILE_OT_select(wmOperatorType *ot) /* identifiers */ ot->name = "Select"; - ot->description = "Activate/select file"; ot->idname = "FILE_OT_select"; + ot->description = "Handle mouse clicks to select and activate items"; /* api callbacks */ ot->invoke = file_select_invoke; @@ -556,6 +565,12 @@ void FILE_OT_select(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, "open", true, "Open", "Open a directory when selecting it"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, + "deselect_all", + false, + "Deselect On Nothing", + "Deselect all when nothing under the cursor"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** @@ -580,6 +595,11 @@ static bool file_walk_select_selection_set(bContext *C, BLI_assert(params); + if (numfiles == 0) { + /* No files visible, nothing to do. */ + return false; + } + if (has_selection) { if (extend && filelist_entry_select_index_get(files, active_old, CHECK_ALL) && filelist_entry_select_index_get(files, active_new, CHECK_ALL)) { @@ -609,7 +629,7 @@ static bool file_walk_select_selection_set(bContext *C, } /* select first file */ else if (ELEM(direction, FILE_SELECT_WALK_DOWN, FILE_SELECT_WALK_RIGHT)) { - params->active_file = active = extend ? 1 : 0; + params->active_file = active = 0; } else { BLI_assert(0); @@ -626,7 +646,7 @@ static bool file_walk_select_selection_set(bContext *C, /* unselect '..' parent entry - it's not supposed to be selected if more * than one file is selected */ - filelist_entry_select_index_set(files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); + filelist_entry_parent_select_set(files, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); } else { /* deselect all first */ @@ -641,11 +661,6 @@ static bool file_walk_select_selection_set(bContext *C, if (fill) { FileSelection sel = {MIN2(active, last_sel), MAX2(active, last_sel)}; - /* clamping selection to not include '..' parent entry */ - if (sel.first == 0) { - sel.first = 1; - } - /* fill selection between last and first selected file */ filelist_entries_select_index_range_set( files, &sel, deselect ? FILE_SEL_REMOVE : FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL); @@ -653,6 +668,12 @@ static bool file_walk_select_selection_set(bContext *C, if (deselect) { filelist_entry_select_index_set(files, active, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL); } + + /* unselect '..' parent entry - it's not supposed to be selected if more + * than one file is selected */ + if ((sel.last - sel.first) > 1) { + filelist_entry_parent_select_set(files, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); + } } else { filelist_entry_select_index_set( @@ -688,10 +709,15 @@ static bool file_walk_select_do(bContext *C, /* *** get all needed files for handling selection *** */ + if (numfiles == 0) { + /* No files visible, nothing to do. */ + return false; + } + 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; + const int idx_shift = (layout->flag & FILE_LAYOUT_HOR) ? layout->rows : layout->flow_columns; if ((layout->flag & FILE_LAYOUT_HOR && direction == FILE_SELECT_WALK_UP) || (layout->flag & FILE_LAYOUT_VER && direction == FILE_SELECT_WALK_LEFT)) { @@ -718,7 +744,7 @@ static bool file_walk_select_do(bContext *C, BLI_assert(0); } - if (!IN_RANGE(active_new, 0, numfiles)) { + if (!IN_RANGE(active_new, -1, numfiles)) { if (extend) { /* extend to invalid file -> abort */ return false; @@ -1185,7 +1211,7 @@ int file_highlight_set(SpaceFile *sfile, ARegion *ar, int mx, int my) mx -= ar->winrct.xmin; my -= ar->winrct.ymin; - if (BLI_rcti_isect_pt(&ar->v2d.mask, mx, my)) { + if (ED_fileselect_layout_is_inside_pt(sfile->layout, v2d, mx, my)) { float fx, fy; int highlight_file; @@ -1234,6 +1260,53 @@ void FILE_OT_highlight(struct wmOperatorType *ot) ot->poll = ED_operator_file_active; } +static int file_column_sort_ui_context_invoke(bContext *C, + wmOperator *UNUSED(op), + const wmEvent *event) +{ + const ARegion *ar = CTX_wm_region(C); + SpaceFile *sfile = CTX_wm_space_file(C); + + if (file_attribute_column_header_is_inside( + &ar->v2d, sfile->layout, event->mval[0], event->mval[1])) { + const FileAttributeColumnType column_type = file_attribute_column_type_find_isect( + &ar->v2d, sfile->params, sfile->layout, event->mval[0]); + + if (column_type != COLUMN_NONE) { + const FileAttributeColumn *column = &sfile->layout->attribute_columns[column_type]; + + if (column->sort_type != FILE_SORT_NONE) { + if (sfile->params->sort == column->sort_type) { + /* Already sorting by selected column -> toggle sort invert (three state logic). */ + sfile->params->flag ^= FILE_SORT_INVERT; + } + else { + sfile->params->sort = column->sort_type; + sfile->params->flag &= ~FILE_SORT_INVERT; + } + + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); + } + } + } + + return OPERATOR_PASS_THROUGH; +} + +void FILE_OT_sort_column_ui_context(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Sort from Column"; + ot->description = "Change sorting to use column under cursor"; + ot->idname = "FILE_OT_sort_column_ui_context"; + + /* api callbacks */ + ot->invoke = file_column_sort_ui_context_invoke; + ot->poll = ED_operator_file_active; + + ot->flag = OPTYPE_INTERNAL; +} + int file_cancel_exec(bContext *C, wmOperator *UNUSED(unused)) { wmWindowManager *wm = CTX_wm_manager(C); @@ -1433,14 +1506,11 @@ void file_draw_check_cb(bContext *C, void *UNUSED(arg1), void *UNUSED(arg2)) bool file_draw_check_exists(SpaceFile *sfile) { if (sfile->op) { /* fails on reload */ - PropertyRNA *prop; - if ((prop = RNA_struct_find_property(sfile->op->ptr, "check_existing"))) { - if (RNA_property_boolean_get(sfile->op->ptr, prop)) { - char filepath[FILE_MAX]; - BLI_join_dirfile(filepath, sizeof(filepath), sfile->params->dir, sfile->params->file); - if (BLI_is_file(filepath)) { - return true; - } + if (sfile->params && (sfile->params->flag & FILE_CHECK_EXISTING)) { + char filepath[FILE_MAX]; + BLI_join_dirfile(filepath, sizeof(filepath), sfile->params->dir, sfile->params->file); + if (BLI_is_file(filepath)) { + return true; } } } @@ -1683,7 +1753,7 @@ static int file_smoothscroll_invoke(bContext *C, wmOperator *UNUSED(op), const w /* check if we are editing a name */ int edit_idx = -1; - for (i = 0; i < numfiles; ++i) { + for (i = 0; i < numfiles; i++) { if (filelist_entry_select_index_get(sfile->files, i, CHECK_ALL) & (FILE_SEL_EDITING | FILE_SEL_HIGHLIGHTED)) { edit_idx = i; @@ -1713,7 +1783,7 @@ static int file_smoothscroll_invoke(bContext *C, wmOperator *UNUSED(op), const w /* Number of items in a block (i.e. lines in a column in horizontal layout, or columns in a line * in vertical layout). */ - const int items_block_size = is_horizontal ? sfile->layout->rows : sfile->layout->columns; + const int items_block_size = is_horizontal ? sfile->layout->rows : sfile->layout->flow_columns; /* Scroll offset is the first file in the row/column we are editing in. */ if (sfile->scroll_offset == 0) { @@ -1915,6 +1985,7 @@ int file_directory_new_exec(bContext *C, wmOperator *op) wmWindowManager *wm = CTX_wm_manager(C); SpaceFile *sfile = CTX_wm_space_file(C); ScrArea *sa = CTX_wm_area(C); + const bool do_diropen = RNA_boolean_get(op->ptr, "open"); if (!sfile->params) { BKE_report(op->reports, RPT_WARNING, "No parent directory given"); @@ -1963,9 +2034,11 @@ int file_directory_new_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - /* now remember file to jump into editing */ - BLI_strncpy(sfile->params->renamefile, name, FILE_MAXFILE); - sfile->params->rename_flag = FILE_PARAMS_RENAME_PENDING; + /* If we don't enter the directory directly, remember file to jump into editing. */ + if (do_diropen == false) { + BLI_strncpy(sfile->params->renamefile, name, FILE_MAXFILE); + sfile->params->rename_flag = FILE_PARAMS_RENAME_PENDING; + } /* set timer to smoothly view newly generated file */ /* max 30 frs/sec */ @@ -1978,7 +2051,7 @@ int file_directory_new_exec(bContext *C, wmOperator *op) /* reload dir to make sure we're seeing what's in the directory */ ED_fileselect_clear(wm, sa, sfile); - if (RNA_boolean_get(op->ptr, "open")) { + if (do_diropen) { BLI_strncpy(sfile->params->dir, path, sizeof(sfile->params->dir)); ED_file_change_dir(C); } @@ -1998,7 +2071,7 @@ void FILE_OT_directory_new(struct wmOperatorType *ot) ot->idname = "FILE_OT_directory_new"; /* api callbacks */ - ot->invoke = WM_operator_confirm; + ot->invoke = WM_operator_confirm_or_exec; ot->exec = file_directory_new_exec; ot->poll = ED_operator_file_active; /* <- important, handler is on window level */ @@ -2007,6 +2080,7 @@ void FILE_OT_directory_new(struct wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, "open", false, "Open", "Open new directory"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + WM_operator_properties_confirm_or_exec(ot); } /* TODO This should go to BLI_path_utils. */ @@ -2075,6 +2149,10 @@ void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UN SpaceFile *sfile = CTX_wm_space_file(C); if (sfile->params) { + char old_dir[sizeof(sfile->params->dir)]; + + BLI_strncpy(old_dir, sfile->params->dir, sizeof(old_dir)); + file_expand_directory(C); /* special case, user may have pasted a filepath into the directory */ @@ -2108,8 +2186,10 @@ void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UN BLI_cleanup_dir(BKE_main_blendfile_path(bmain), sfile->params->dir); if (filelist_is_dir(sfile->files, sfile->params->dir)) { - /* if directory exists, enter it immediately */ - ED_file_change_dir(C); + if (!STREQ(sfile->params->dir, old_dir)) { /* Avoids flickering when nothing's changed. */ + /* if directory exists, enter it immediately */ + ED_file_change_dir(C); + } /* don't do for now because it selects entire text instead of * placing cursor at the end */ @@ -2138,6 +2218,8 @@ void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UN WM_operator_properties_create_ptr(&ptr, ot); RNA_string_set(&ptr, "directory", sfile->params->dir); RNA_boolean_set(&ptr, "open", true); + /* Enable confirmation prompt, else it's too easy to accidentaly create new directories. */ + RNA_boolean_set(&ptr, "confirm", true); if (lastdir) { BLI_strncpy(sfile->params->dir, lastdir, sizeof(sfile->params->dir)); @@ -2239,57 +2321,15 @@ void FILE_OT_hidedot(struct wmOperatorType *ot) ot->poll = ED_operator_file_active; /* <- important, handler is on window level */ } -ARegion *file_tools_region(ScrArea *sa) +static bool file_filenum_poll(bContext *C) { - ARegion *ar, *arnew; - - if ((ar = BKE_area_find_region_type(sa, RGN_TYPE_TOOLS)) != NULL) { - return ar; - } - - /* add subdiv level; after header */ - ar = BKE_area_find_region_type(sa, RGN_TYPE_HEADER); - - /* is error! */ - if (ar == NULL) { - return NULL; - } - - arnew = MEM_callocN(sizeof(ARegion), "tools for file"); - BLI_insertlinkafter(&sa->regionbase, ar, arnew); - arnew->regiontype = RGN_TYPE_TOOLS; - arnew->alignment = RGN_ALIGN_LEFT; - - ar = MEM_callocN(sizeof(ARegion), "tool props for file"); - BLI_insertlinkafter(&sa->regionbase, arnew, ar); - ar->regiontype = RGN_TYPE_TOOL_PROPS; - ar->alignment = RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV; - - return arnew; -} - -static int file_bookmark_toggle_exec(bContext *C, wmOperator *UNUSED(unused)) -{ - ScrArea *sa = CTX_wm_area(C); - ARegion *ar = file_tools_region(sa); + SpaceFile *sfile = CTX_wm_space_file(C); - if (ar) { - ED_region_toggle_hidden(C, ar); + if (!ED_operator_file_active(C)) { + return false; } - return OPERATOR_FINISHED; -} - -void FILE_OT_bookmark_toggle(struct wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Toggle Bookmarks"; - ot->description = "Toggle bookmarks display"; - ot->idname = "FILE_OT_bookmark_toggle"; - - /* api callbacks */ - ot->exec = file_bookmark_toggle_exec; - ot->poll = ED_operator_file_active; /* <- important, handler is on window level */ + return sfile->params && (sfile->params->flag & FILE_CHECK_EXISTING); } /** @@ -2349,7 +2389,7 @@ void FILE_OT_filenum(struct wmOperatorType *ot) /* api callbacks */ ot->exec = file_filenum_exec; - ot->poll = ED_operator_file_active; /* <- important, handler is on window level */ + ot->poll = file_filenum_poll; /* props */ RNA_def_int(ot->srna, "increment", 1, -100, 100, "Increment", "", -100, 100); diff --git a/source/blender/editors/space_file/file_panels.c b/source/blender/editors/space_file/file_panels.c index d9a6e70121f..9ba098fcf45 100644 --- a/source/blender/editors/space_file/file_panels.c +++ b/source/blender/editors/space_file/file_panels.c @@ -95,7 +95,7 @@ static void file_panel_operator(const bContext *C, Panel *pa) UI_block_func_set(uiLayoutGetBlock(pa->layout), NULL, NULL, NULL); } -void file_panels_register(ARegionType *art) +void file_tool_props_region_panels_register(ARegionType *art) { PanelType *pt; @@ -103,8 +103,122 @@ void file_panels_register(ARegionType *art) strcpy(pt->idname, "FILE_PT_operator"); strcpy(pt->label, N_("Operator")); strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); + pt->flag = PNL_NO_HEADER; pt->poll = file_panel_operator_poll; pt->draw_header = file_panel_operator_header; pt->draw = file_panel_operator; BLI_addtail(&art->paneltypes, pt); } + +static void file_panel_execution_cancel_button(uiLayout *layout) +{ + uiLayout *row = uiLayoutRow(layout, false); + uiLayoutSetScaleX(row, 0.8f); + uiLayoutSetFixedSize(row, true); + uiItemO(row, IFACE_("Cancel"), ICON_NONE, "FILE_OT_cancel"); +} + +static void file_panel_execution_execute_button(uiLayout *layout, const char *title) +{ + uiLayout *row = uiLayoutRow(layout, false); + uiLayoutSetScaleX(row, 0.8f); + uiLayoutSetFixedSize(row, true); + /* Just a display hint. */ + uiLayoutSetActiveDefault(row, true); + uiItemO(row, title, ICON_NONE, "FILE_OT_execute"); +} + +static void file_panel_execution_buttons_draw(const bContext *C, Panel *pa) +{ + bScreen *screen = CTX_wm_screen(C); + SpaceFile *sfile = CTX_wm_space_file(C); + FileSelectParams *params = ED_fileselect_get_params(sfile); + uiBlock *block = uiLayoutGetBlock(pa->layout); + uiBut *but; + uiLayout *row; + PointerRNA params_rna_ptr, *but_extra_rna_ptr; + + const bool overwrite_alert = file_draw_check_exists(sfile); + const bool windows_layout = +#ifdef _WIN32 + true; +#else + false; +#endif + + RNA_pointer_create(&screen->id, &RNA_FileSelectParams, params, ¶ms_rna_ptr); + + row = uiLayoutRow(pa->layout, false); + uiLayoutSetScaleY(row, 1.3f); + + /* callbacks for operator check functions */ + UI_block_func_set(block, file_draw_check_cb, NULL, NULL); + + but = uiDefButR(block, + UI_BTYPE_TEXT, + -1, + "", + 0, + 0, + UI_UNIT_X * 5, + UI_UNIT_Y, + ¶ms_rna_ptr, + "filename", + 0, + 0.0f, + (float)FILE_MAXFILE, + 0, + 0, + TIP_(overwrite_alert ? N_("File name, overwrite existing") : N_("File name"))); + + BLI_assert(!UI_but_flag_is_set(but, UI_BUT_UNDO)); + BLI_assert(!UI_but_is_utf8(but)); + + UI_but_func_complete_set(but, autocomplete_file, NULL); + /* silly workaround calling NFunc to ensure this does not get called + * immediate ui_apply_but_func but only after button deactivates */ + UI_but_funcN_set(but, file_filename_enter_handle, NULL, but); + + if (params->flag & FILE_CHECK_EXISTING) { + but_extra_rna_ptr = UI_but_extra_operator_icon_add( + but, "FILE_OT_filenum", WM_OP_EXEC_REGION_WIN, ICON_ADD); + RNA_int_set(but_extra_rna_ptr, "increment", 1); + but_extra_rna_ptr = UI_but_extra_operator_icon_add( + but, "FILE_OT_filenum", WM_OP_EXEC_REGION_WIN, ICON_REMOVE); + RNA_int_set(but_extra_rna_ptr, "increment", -1); + } + + /* check if this overrides a file and if the operator option is used */ + if (overwrite_alert) { + UI_but_flag_enable(but, UI_BUT_REDALERT); + } + UI_block_func_set(block, NULL, NULL, NULL); + + { + uiLayout *sub = uiLayoutRow(row, false); + uiLayoutSetOperatorContext(sub, WM_OP_EXEC_REGION_WIN); + + if (windows_layout) { + file_panel_execution_execute_button(sub, params->title); + file_panel_execution_cancel_button(sub); + } + else { + file_panel_execution_cancel_button(sub); + file_panel_execution_execute_button(sub, params->title); + } + } +} + +void file_execute_region_panels_register(ARegionType *art) +{ + PanelType *pt; + + pt = MEM_callocN(sizeof(PanelType), "spacetype file execution buttons"); + strcpy(pt->idname, "FILE_PT_execution_buttons"); + strcpy(pt->label, N_("Execute Buttons")); + strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); + pt->flag = PNL_NO_HEADER; + pt->poll = file_panel_operator_poll; + pt->draw = file_panel_execution_buttons_draw; + BLI_addtail(&art->paneltypes, pt); +} diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index f7dda1defe8..27cccf6bab1 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -329,25 +329,21 @@ enum { FL_IS_PENDING = 1 << 2, FL_NEED_SORTING = 1 << 3, FL_NEED_FILTERING = 1 << 4, + FL_SORT_INVERT = 1 << 5, }; -#define SPECIAL_IMG_SIZE 48 -#define SPECIAL_IMG_ROWS 4 -#define SPECIAL_IMG_COLS 4 +#define SPECIAL_IMG_SIZE 256 +#define SPECIAL_IMG_ROWS 1 +#define SPECIAL_IMG_COLS 7 enum { - SPECIAL_IMG_FOLDER = 0, - SPECIAL_IMG_PARENT = 1, - SPECIAL_IMG_REFRESH = 2, - SPECIAL_IMG_BLENDFILE = 3, - SPECIAL_IMG_SOUNDFILE = 4, - SPECIAL_IMG_MOVIEFILE = 5, - SPECIAL_IMG_PYTHONFILE = 6, - SPECIAL_IMG_TEXTFILE = 7, - SPECIAL_IMG_FONTFILE = 8, - SPECIAL_IMG_UNKNOWNFILE = 9, - SPECIAL_IMG_LOADING = 10, - SPECIAL_IMG_BACKUP = 11, + SPECIAL_IMG_DOCUMENT = 0, + SPECIAL_IMG_UNSUPORTED = 1, + SPECIAL_IMG_FOLDER = 2, + SPECIAL_IMG_PARENT = 3, + SPECIAL_IMG_DRIVE_FIXED = 4, + SPECIAL_IMG_DRIVE_ATTACHED = 5, + SPECIAL_IMG_DRIVE_REMOTE = 6, SPECIAL_IMG_MAX, }; @@ -369,6 +365,19 @@ static void filelist_cache_clear(FileListEntryCache *cache, size_t new_size); /* ********** Sort helpers ********** */ +struct FileSortData { + bool inverted; +}; + +static int compare_apply_inverted(int val, const struct FileSortData *sort_data) +{ + return sort_data->inverted ? -val : val; +} + +/** + * Handles inverted sorting itself (currently there's nothing to invert), so if this returns non-0, + * it should be used as-is and not inverted. + */ static int compare_direntry_generic(const FileListInternEntry *entry1, const FileListInternEntry *entry2) { @@ -420,10 +429,11 @@ static int compare_direntry_generic(const FileListInternEntry *entry1, return 0; } -static int compare_name(void *UNUSED(user_data), const void *a1, const void *a2) +static int compare_name(void *user_data, const void *a1, const void *a2) { const FileListInternEntry *entry1 = a1; const FileListInternEntry *entry2 = a2; + const struct FileSortData *sort_data = user_data; char *name1, *name2; int ret; @@ -434,13 +444,14 @@ static int compare_name(void *UNUSED(user_data), const void *a1, const void *a2) name1 = entry1->name; name2 = entry2->name; - return BLI_natstrcmp(name1, name2); + return compare_apply_inverted(BLI_strcasecmp_natural(name1, name2), sort_data); } -static int compare_date(void *UNUSED(user_data), const void *a1, const void *a2) +static int compare_date(void *user_data, const void *a1, const void *a2) { const FileListInternEntry *entry1 = a1; const FileListInternEntry *entry2 = a2; + const struct FileSortData *sort_data = user_data; char *name1, *name2; int64_t time1, time2; int ret; @@ -452,22 +463,23 @@ static int compare_date(void *UNUSED(user_data), const void *a1, const void *a2) time1 = (int64_t)entry1->st.st_mtime; time2 = (int64_t)entry2->st.st_mtime; if (time1 < time2) { - return 1; + return compare_apply_inverted(1, sort_data); } if (time1 > time2) { - return -1; + return compare_apply_inverted(-1, sort_data); } name1 = entry1->name; name2 = entry2->name; - return BLI_natstrcmp(name1, name2); + return compare_apply_inverted(BLI_strcasecmp_natural(name1, name2), sort_data); } -static int compare_size(void *UNUSED(user_data), const void *a1, const void *a2) +static int compare_size(void *user_data, const void *a1, const void *a2) { const FileListInternEntry *entry1 = a1; const FileListInternEntry *entry2 = a2; + const struct FileSortData *sort_data = user_data; char *name1, *name2; uint64_t size1, size2; int ret; @@ -479,22 +491,23 @@ static int compare_size(void *UNUSED(user_data), const void *a1, const void *a2) size1 = entry1->st.st_size; size2 = entry2->st.st_size; if (size1 < size2) { - return 1; + return compare_apply_inverted(1, sort_data); } if (size1 > size2) { - return -1; + return compare_apply_inverted(-1, sort_data); } name1 = entry1->name; name2 = entry2->name; - return BLI_natstrcmp(name1, name2); + return compare_apply_inverted(BLI_strcasecmp_natural(name1, name2), sort_data); } -static int compare_extension(void *UNUSED(user_data), const void *a1, const void *a2) +static int compare_extension(void *user_data, const void *a1, const void *a2) { const FileListInternEntry *entry1 = a1; const FileListInternEntry *entry2 = a2; + const struct FileSortData *sort_data = user_data; char *name1, *name2; int ret; @@ -516,10 +529,10 @@ static int compare_extension(void *UNUSED(user_data), const void *a1, const void return -1; } if (entry1->blentype < entry2->blentype) { - return -1; + return compare_apply_inverted(-1, sort_data); } if (entry1->blentype > entry2->blentype) { - return 1; + return compare_apply_inverted(1, sort_data); } } else { @@ -539,48 +552,58 @@ static int compare_extension(void *UNUSED(user_data), const void *a1, const void } if ((ret = BLI_strcasecmp(sufix1, sufix2))) { - return ret; + return compare_apply_inverted(ret, sort_data); } } name1 = entry1->name; name2 = entry2->name; - return BLI_natstrcmp(name1, name2); + return compare_apply_inverted(BLI_strcasecmp_natural(name1, name2), sort_data); } void filelist_sort(struct FileList *filelist) { if ((filelist->flags & FL_NEED_SORTING) && (filelist->sort != FILE_SORT_NONE)) { + void *sort_cb = NULL; + switch (filelist->sort) { case FILE_SORT_ALPHA: - BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_name, NULL); + sort_cb = compare_name; break; case FILE_SORT_TIME: - BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_date, NULL); + sort_cb = compare_date; break; case FILE_SORT_SIZE: - BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_size, NULL); + sort_cb = compare_size; break; case FILE_SORT_EXTENSION: - BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_extension, NULL); + sort_cb = compare_extension; break; case FILE_SORT_NONE: /* Should never reach this point! */ default: BLI_assert(0); break; } + BLI_listbase_sort_r( + &filelist->filelist_intern.entries, + sort_cb, + &(struct FileSortData){.inverted = (filelist->flags & FL_SORT_INVERT) != 0}); filelist_filter_clear(filelist); filelist->flags &= ~FL_NEED_SORTING; } } -void filelist_setsorting(struct FileList *filelist, const short sort) +void filelist_setsorting(struct FileList *filelist, const short sort, bool invert_sort) { - if (filelist->sort != sort) { + const bool was_invert_sort = filelist->flags & FL_SORT_INVERT; + + if ((filelist->sort != sort) || (was_invert_sort != invert_sort)) { filelist->sort = sort; filelist->flags |= FL_NEED_SORTING; + filelist->flags = invert_sort ? (filelist->flags | FL_SORT_INVERT) : + (filelist->flags & ~FL_SORT_INVERT); } } @@ -635,9 +658,9 @@ static bool is_filtered_file(FileListInternEntry *file, { bool is_filtered = !is_hidden_file(file->relpath, filter); - if (is_filtered && (filter->flags & FLF_DO_FILTER) && !FILENAME_IS_CURRPAR(file->relpath)) { + if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) { /* We only check for types if some type are enabled in filtering. */ - if (filter->filter) { + if (filter->filter && (filter->flags & FLF_DO_FILTER)) { if (file->typeflag & FILE_TYPE_DIR) { if (file->typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { @@ -657,6 +680,7 @@ static bool is_filtered_file(FileListInternEntry *file, } } } + /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */ if (is_filtered && (filter->filter_search[0] != '\0')) { if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) { is_filtered = false; @@ -676,9 +700,9 @@ static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileLis if (BLO_library_path_explode(path, dir, &group, &name)) { is_filtered = !is_hidden_file(file->relpath, filter); - if (is_filtered && (filter->flags & FLF_DO_FILTER) && !FILENAME_IS_CURRPAR(file->relpath)) { + if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) { /* We only check for types if some type are enabled in filtering. */ - if (filter->filter || filter->filter_id) { + if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) { if (file->typeflag & FILE_TYPE_DIR) { if (file->typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { @@ -704,6 +728,7 @@ static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileLis } } } + /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */ if (is_filtered && (filter->filter_search[0] != '\0')) { if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) { is_filtered = false; @@ -870,7 +895,7 @@ void filelist_free_icons(void) BLI_assert(G.background == false); - for (i = 0; i < SPECIAL_IMG_MAX; ++i) { + for (i = 0; i < SPECIAL_IMG_MAX; i++) { IMB_freeImBuf(gSpecialFileImages[i]); gSpecialFileImages[i] = NULL; } @@ -904,42 +929,12 @@ static ImBuf *filelist_geticon_image_ex(const unsigned int typeflag, const char if (FILENAME_IS_PARENT(relpath)) { ibuf = gSpecialFileImages[SPECIAL_IMG_PARENT]; } - else if (FILENAME_IS_CURRENT(relpath)) { - ibuf = gSpecialFileImages[SPECIAL_IMG_REFRESH]; - } else { ibuf = gSpecialFileImages[SPECIAL_IMG_FOLDER]; } } - else if (typeflag & FILE_TYPE_BLENDER) { - ibuf = gSpecialFileImages[SPECIAL_IMG_BLENDFILE]; - } - else if (typeflag & FILE_TYPE_BLENDERLIB) { - ibuf = gSpecialFileImages[SPECIAL_IMG_UNKNOWNFILE]; - } - else if (typeflag & (FILE_TYPE_MOVIE)) { - ibuf = gSpecialFileImages[SPECIAL_IMG_MOVIEFILE]; - } - else if (typeflag & FILE_TYPE_SOUND) { - ibuf = gSpecialFileImages[SPECIAL_IMG_SOUNDFILE]; - } - else if (typeflag & FILE_TYPE_PYSCRIPT) { - ibuf = gSpecialFileImages[SPECIAL_IMG_PYTHONFILE]; - } - else if (typeflag & FILE_TYPE_FTFONT) { - ibuf = gSpecialFileImages[SPECIAL_IMG_FONTFILE]; - } - else if (typeflag & FILE_TYPE_TEXT) { - ibuf = gSpecialFileImages[SPECIAL_IMG_TEXTFILE]; - } - else if (typeflag & FILE_TYPE_IMAGE) { - ibuf = gSpecialFileImages[SPECIAL_IMG_LOADING]; - } - else if (typeflag & FILE_TYPE_BLENDER_BACKUP) { - ibuf = gSpecialFileImages[SPECIAL_IMG_BACKUP]; - } else { - ibuf = gSpecialFileImages[SPECIAL_IMG_UNKNOWNFILE]; + ibuf = gSpecialFileImages[SPECIAL_IMG_DOCUMENT]; } return ibuf; @@ -1001,14 +996,20 @@ static int filelist_geticon_ex(const int typeflag, return ICON_FILE_BLANK; } else if (typeflag & FILE_TYPE_COLLADA) { - return ICON_FILE_BLANK; + return ICON_FILE_3D; } else if (typeflag & FILE_TYPE_ALEMBIC) { - return ICON_FILE_BLANK; + return ICON_FILE_3D; + } + else if (typeflag & FILE_TYPE_OBJECT_IO) { + return ICON_FILE_3D; } else if (typeflag & FILE_TYPE_TEXT) { return ICON_FILE_TEXT; } + else if (typeflag & FILE_TYPE_ARCHIVE) { + return ICON_FILE_ARCHIVE; + } else if (typeflag & FILE_TYPE_BLENDERLIB) { const int ret = UI_idcode_icon_get(blentype); if (ret != ICON_NONE) { @@ -1243,7 +1244,8 @@ static void filelist_cache_previews_clear(FileListEntryCache *cache) BLI_task_pool_cancel(cache->previews_pool); while ((preview = BLI_thread_queue_pop_timeout(cache->previews_done, 0))) { - // printf("%s: DONE %d - %s - %p\n", __func__, preview->index, preview->path, preview->img); + // printf("%s: DONE %d - %s - %p\n", __func__, preview->index, preview->path, + // preview->img); if (preview->img) { IMB_freeImBuf(preview->img); } @@ -2128,6 +2130,12 @@ int ED_path_extension_type(const char *path) else if (BLI_path_extension_check(path, ".abc")) { return FILE_TYPE_ALEMBIC; } + else if (BLI_path_extension_check(path, ".zip")) { + return FILE_TYPE_ARCHIVE; + } + else if (BLI_path_extension_check_n(path, ".obj", ".3ds", ".fbx", ".glb", ".gltf", NULL)) { + return FILE_TYPE_OBJECT_IO; + } else if (BLI_path_extension_check_array(path, imb_ext_image)) { return FILE_TYPE_IMAGE; } @@ -2177,11 +2185,13 @@ int ED_file_extension_icon(const char *path) case FILE_TYPE_BTX: return ICON_FILE_BLANK; case FILE_TYPE_COLLADA: - return ICON_FILE_BLANK; case FILE_TYPE_ALEMBIC: - return ICON_FILE_BLANK; + case FILE_TYPE_OBJECT_IO: + return ICON_FILE_3D; case FILE_TYPE_TEXT: return ICON_FILE_TEXT; + case FILE_TYPE_ARCHIVE: + return ICON_FILE_ARCHIVE; default: return ICON_FILE_BLANK; } @@ -2298,6 +2308,19 @@ unsigned int filelist_entry_select_index_get(FileList *filelist, return 0; } +/** + * Set selection of the '..' parent entry, but only if it's actually visible. + */ +void filelist_entry_parent_select_set(FileList *filelist, + FileSelType select, + unsigned int flag, + FileCheckType check) +{ + if ((filelist->filter_data.flags & FLF_HIDE_PARENT) == 0) { + filelist_entry_select_index_set(filelist, 0, select, flag, check); + } +} + /* WARNING! dir must be FILE_MAX_LIBEXTRA long! */ bool filelist_islibrary(struct FileList *filelist, char *dir, char **group) { diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h index caf77246797..9b1107294ff 100644 --- a/source/blender/editors/space_file/filelist.h +++ b/source/blender/editors/space_file/filelist.h @@ -55,7 +55,7 @@ void folderlist_pushdir(struct ListBase *folderlist, const char *dir); const char *folderlist_peeklastdir(struct ListBase *folderdist); int folderlist_clear_next(struct SpaceFile *sfile); -void filelist_setsorting(struct FileList *filelist, const short sort); +void filelist_setsorting(struct FileList *filelist, const short sort, bool invert_sort); void filelist_sort(struct FileList *filelist); void filelist_setfilter_options(struct FileList *filelist, @@ -117,6 +117,10 @@ unsigned int filelist_entry_select_get(struct FileList *filelist, unsigned int filelist_entry_select_index_get(struct FileList *filelist, const int index, FileCheckType check); +void filelist_entry_parent_select_set(struct FileList *filelist, + FileSelType select, + unsigned int flag, + FileCheckType check); void filelist_setrecursion(struct FileList *filelist, const int recursion_level); diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index db42d007b8e..e2c9bb8d6e5 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -49,6 +49,10 @@ #include "BLI_utildefines.h" #include "BLI_fnmatch.h" +#include "BLO_readfile.h" + +#include "BLT_translation.h" + #include "BKE_appdir.h" #include "BKE_context.h" #include "BKE_main.h" @@ -69,6 +73,8 @@ #include "file_intern.h" #include "filelist.h" +#define VERTLIST_MAJORCOLUMN_WIDTH (25 * UI_UNIT_X) + FileSelectParams *ED_fileselect_get_params(struct SpaceFile *sfile) { if (!sfile->params) { @@ -98,7 +104,9 @@ short ED_fileselect_set_params(SpaceFile *sfile) sizeof(sfile->params->file)); sfile->params->filter_glob[0] = '\0'; /* set the default thumbnails size */ - sfile->params->thumbnail_size = 128; + sfile->params->thumbnail_size = U_default.file_space_data.thumbnail_size; + /* Show size column by default. */ + sfile->params->details_flags = U_default.file_space_data.details_flags; } params = sfile->params; @@ -161,6 +169,13 @@ short ED_fileselect_set_params(SpaceFile *sfile) params->flag &= ~FILE_DIRSEL_ONLY; } + if ((prop = RNA_struct_find_property(op->ptr, "check_existing"))) { + params->flag |= RNA_property_boolean_get(op->ptr, prop) ? FILE_CHECK_EXISTING : 0; + } + if ((prop = RNA_struct_find_property(op->ptr, "hide_props_region"))) { + params->flag |= RNA_property_boolean_get(op->ptr, prop) ? FILE_HIDE_TOOL_PROPS : 0; + } + params->filter = 0; if ((prop = RNA_struct_find_property(op->ptr, "filter_blender"))) { params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_BLENDER : 0; @@ -189,6 +204,9 @@ short ED_fileselect_set_params(SpaceFile *sfile) if ((prop = RNA_struct_find_property(op->ptr, "filter_text"))) { params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_TEXT : 0; } + if ((prop = RNA_struct_find_property(op->ptr, "filter_archive"))) { + params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_ARCHIVE : 0; + } if ((prop = RNA_struct_find_property(op->ptr, "filter_folder"))) { params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_FOLDER : 0; } @@ -258,26 +276,11 @@ short ED_fileselect_set_params(SpaceFile *sfile) params->sort = RNA_property_enum_get(op->ptr, prop); } else { - params->sort = FILE_SORT_ALPHA; + params->sort = U_default.file_space_data.sort_type; } if (params->display == FILE_DEFAULTDISPLAY) { - if (params->display_previous == FILE_DEFAULTDISPLAY) { - if (U.uiflag & USER_SHOW_THUMBNAILS) { - if (params->filter & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT)) { - params->display = FILE_IMGDISPLAY; - } - else { - params->display = FILE_SHORTDISPLAY; - } - } - else { - params->display = FILE_SHORTDISPLAY; - } - } - else { - params->display = params->display_previous; - } + params->display = U_default.file_space_data.display_type; } if (is_relative_path) { @@ -291,10 +294,9 @@ short ED_fileselect_set_params(SpaceFile *sfile) else { /* default values, if no operator */ params->type = FILE_UNIX; - params->flag |= FILE_HIDE_DOT; + params->flag |= U_default.file_space_data.flag; params->flag &= ~FILE_DIRSEL_ONLY; - params->display = FILE_SHORTDISPLAY; - params->display_previous = FILE_DEFAULTDISPLAY; + params->display = FILE_VERTICALDISPLAY; params->sort = FILE_SORT_ALPHA; params->filter = 0; params->filter_glob[0] = '\0'; @@ -330,6 +332,63 @@ short ED_fileselect_set_params(SpaceFile *sfile) return 1; } +/* The subset of FileSelectParams.flag items we store into preferences. */ +#define PARAMS_FLAGS_REMEMBERED (FILE_HIDE_DOT | FILE_SORT_INVERT) + +void ED_fileselect_set_params_from_userdef(SpaceFile *sfile) +{ + wmOperator *op = sfile->op; + UserDef_FileSpaceData *sfile_udata = &U.file_space_data; + + ED_fileselect_set_params(sfile); + + if (!op) { + return; + } + + if (!RNA_struct_property_is_set(op->ptr, "display_type")) { + sfile->params->display = sfile_udata->display_type; + } + if (!RNA_struct_property_is_set(op->ptr, "sort_method")) { + sfile->params->sort = sfile_udata->sort_type; + } + sfile->params->thumbnail_size = sfile_udata->thumbnail_size; + sfile->params->details_flags = sfile_udata->details_flags; + + /* Combine flags we take from params with the flags we take from userdef. */ + sfile->params->flag = (sfile->params->flag & ~PARAMS_FLAGS_REMEMBERED) | + (sfile_udata->flag & PARAMS_FLAGS_REMEMBERED); +} + +/** + * Update the user-preference data for the file space. In fact, this also contains some + * non-FileSelectParams data, but it's neglectable. + * + * \param temp_win_size: If the browser was opened in a temporary window, pass its size here so we + * can store that in the preferences. Otherwise NULL. + */ +void ED_fileselect_params_to_userdef(SpaceFile *sfile, int temp_win_size[2]) +{ + UserDef_FileSpaceData *sfile_udata_new = &U.file_space_data; + UserDef_FileSpaceData sfile_udata_old = U.file_space_data; + + sfile_udata_new->display_type = sfile->params->display; + sfile_udata_new->thumbnail_size = sfile->params->thumbnail_size; + sfile_udata_new->sort_type = sfile->params->sort; + sfile_udata_new->details_flags = sfile->params->details_flags; + sfile_udata_new->flag = sfile->params->flag & PARAMS_FLAGS_REMEMBERED; + + if (temp_win_size) { + sfile_udata_new->temp_win_sizex = temp_win_size[0]; + sfile_udata_new->temp_win_sizey = temp_win_size[1]; + } + + /* Tag prefs as dirty if something has changed. */ + if (memcmp(sfile_udata_new, &sfile_udata_old, sizeof(sfile_udata_old)) != 0) { + U.runtime.is_dirty = true; + } +} + void ED_fileselect_reset_params(SpaceFile *sfile) { sfile->params->type = FILE_UNIX; @@ -344,7 +403,7 @@ void ED_fileselect_reset_params(SpaceFile *sfile) void fileselect_file_set(SpaceFile *sfile, const int index) { const struct FileDirEntry *file = filelist_file(sfile->files, index); - if (file && file->relpath && file->relpath[0] && !(file->typeflag & FILE_TYPE_FOLDER)) { + if (file && file->relpath && file->relpath[0] && !(file->typeflag & FILE_TYPE_DIR)) { BLI_strncpy(sfile->params->file, file->relpath, FILE_MAXFILE); } } @@ -372,10 +431,10 @@ int ED_fileselect_layout_numfiles(FileLayout *layout, ARegion *ar) } else { const int y_item = layout->tile_h + (2 * layout->tile_border_y); - const int y_view = (int)(BLI_rctf_size_y(&ar->v2d.cur)); + const int y_view = (int)(BLI_rctf_size_y(&ar->v2d.cur)) - layout->offset_top; const int y_over = y_item - (y_view % y_item); numfiles = (int)((float)(y_view + y_over) / (float)(y_item)); - return numfiles * layout->columns; + return numfiles * layout->flow_columns; } } @@ -395,19 +454,19 @@ FileSelection ED_fileselect_layout_offset_rect(FileLayout *layout, const rcti *r } colmin = (rect->xmin) / (layout->tile_w + 2 * layout->tile_border_x); - rowmin = (rect->ymin) / (layout->tile_h + 2 * layout->tile_border_y); + rowmin = (rect->ymin - layout->offset_top) / (layout->tile_h + 2 * layout->tile_border_y); colmax = (rect->xmax) / (layout->tile_w + 2 * layout->tile_border_x); - rowmax = (rect->ymax) / (layout->tile_h + 2 * layout->tile_border_y); + rowmax = (rect->ymax - layout->offset_top) / (layout->tile_h + 2 * layout->tile_border_y); - if (is_inside(colmin, rowmin, layout->columns, layout->rows) || - is_inside(colmax, rowmax, layout->columns, layout->rows)) { - CLAMP(colmin, 0, layout->columns - 1); + if (is_inside(colmin, rowmin, layout->flow_columns, layout->rows) || + is_inside(colmax, rowmax, layout->flow_columns, layout->rows)) { + CLAMP(colmin, 0, layout->flow_columns - 1); CLAMP(rowmin, 0, layout->rows - 1); - CLAMP(colmax, 0, layout->columns - 1); + CLAMP(colmax, 0, layout->flow_columns - 1); CLAMP(rowmax, 0, layout->rows - 1); } - if ((colmin > layout->columns - 1) || (rowmin > layout->rows - 1)) { + if ((colmin > layout->flow_columns - 1) || (rowmin > layout->rows - 1)) { sel.first = -1; } else { @@ -415,10 +474,10 @@ FileSelection ED_fileselect_layout_offset_rect(FileLayout *layout, const rcti *r sel.first = layout->rows * colmin + rowmin; } else { - sel.first = colmin + layout->columns * rowmin; + sel.first = colmin + layout->flow_columns * rowmin; } } - if ((colmax > layout->columns - 1) || (rowmax > layout->rows - 1)) { + if ((colmax > layout->flow_columns - 1) || (rowmax > layout->rows - 1)) { sel.last = -1; } else { @@ -426,7 +485,7 @@ FileSelection ED_fileselect_layout_offset_rect(FileLayout *layout, const rcti *r sel.last = layout->rows * colmax + rowmax; } else { - sel.last = colmax + layout->columns * rowmax; + sel.last = colmax + layout->flow_columns * rowmax; } } @@ -443,9 +502,9 @@ int ED_fileselect_layout_offset(FileLayout *layout, int x, int y) } offsetx = (x) / (layout->tile_w + 2 * layout->tile_border_x); - offsety = (y) / (layout->tile_h + 2 * layout->tile_border_y); + offsety = (y - layout->offset_top) / (layout->tile_h + 2 * layout->tile_border_y); - if (offsetx > layout->columns - 1) { + if (offsetx > layout->flow_columns - 1) { return -1; } if (offsety > layout->rows - 1) { @@ -456,27 +515,123 @@ int ED_fileselect_layout_offset(FileLayout *layout, int x, int y) active_file = layout->rows * offsetx + offsety; } else { - active_file = offsetx + layout->columns * offsety; + active_file = offsetx + layout->flow_columns * offsety; } return active_file; } +/** + * Get the currently visible bounds of the layout in screen space. Matches View2D.mask minus the + * top column-header row. + */ +void ED_fileselect_layout_maskrect(const FileLayout *layout, const View2D *v2d, rcti *r_rect) +{ + *r_rect = v2d->mask; + r_rect->ymax -= layout->offset_top; +} + +bool ED_fileselect_layout_is_inside_pt(const FileLayout *layout, const View2D *v2d, int x, int y) +{ + rcti maskrect; + ED_fileselect_layout_maskrect(layout, v2d, &maskrect); + return BLI_rcti_isect_pt(&maskrect, x, y); +} + +bool ED_fileselect_layout_isect_rect(const FileLayout *layout, + const View2D *v2d, + const rcti *rect, + rcti *r_dst) +{ + rcti maskrect; + ED_fileselect_layout_maskrect(layout, v2d, &maskrect); + return BLI_rcti_isect(&maskrect, rect, r_dst); +} + void ED_fileselect_layout_tilepos(FileLayout *layout, int tile, int *x, int *y) { if (layout->flag == FILE_LAYOUT_HOR) { *x = layout->tile_border_x + (tile / layout->rows) * (layout->tile_w + 2 * layout->tile_border_x); - *y = layout->tile_border_y + + *y = layout->offset_top + layout->tile_border_y + (tile % layout->rows) * (layout->tile_h + 2 * layout->tile_border_y); } else { *x = layout->tile_border_x + - ((tile) % layout->columns) * (layout->tile_w + 2 * layout->tile_border_x); - *y = layout->tile_border_y + - ((tile) / layout->columns) * (layout->tile_h + 2 * layout->tile_border_y); + ((tile) % layout->flow_columns) * (layout->tile_w + 2 * layout->tile_border_x); + *y = layout->offset_top + layout->tile_border_y + + ((tile) / layout->flow_columns) * (layout->tile_h + 2 * layout->tile_border_y); } } +/** + * Check if the region coordinate defined by \a x and \a y are inside the column header. + */ +bool file_attribute_column_header_is_inside(const View2D *v2d, + const FileLayout *layout, + int x, + int y) +{ + rcti header_rect = v2d->mask; + header_rect.ymin = header_rect.ymax - layout->attribute_column_header_h; + return BLI_rcti_isect_pt(&header_rect, x, y); +} + +bool file_attribute_column_type_enabled(const FileSelectParams *params, + FileAttributeColumnType column) +{ + switch (column) { + case COLUMN_NAME: + /* Always enabled */ + return true; + case COLUMN_DATETIME: + return (params->details_flags & FILE_DETAILS_DATETIME) != 0; + case COLUMN_SIZE: + return (params->details_flags & FILE_DETAILS_SIZE) != 0; + default: + return false; + } +} + +/** + * Find the column type at region coordinate given by \a x (y doesn't matter for this). + */ +FileAttributeColumnType file_attribute_column_type_find_isect(const View2D *v2d, + const FileSelectParams *params, + FileLayout *layout, + int x) +{ + float mx, my; + int offset_tile; + + UI_view2d_region_to_view(v2d, x, v2d->mask.ymax - layout->offset_top - 1, &mx, &my); + offset_tile = ED_fileselect_layout_offset( + layout, (int)(v2d->tot.xmin + mx), (int)(v2d->tot.ymax - my)); + if (offset_tile > -1) { + int tile_x, tile_y; + int pos_x = 0; + int rel_x; /* x relative to the hovered tile */ + + ED_fileselect_layout_tilepos(layout, offset_tile, &tile_x, &tile_y); + /* Column header drawing doesn't use left tile border, so subtract it. */ + rel_x = mx - (tile_x - layout->tile_border_x); + + for (FileAttributeColumnType column = 0; column < ATTRIBUTE_COLUMN_MAX; column++) { + if (!file_attribute_column_type_enabled(params, column)) { + continue; + } + const int width = layout->attribute_columns[column].width; + + if (IN_RANGE(rel_x, pos_x, pos_x + width)) { + return column; + } + + pos_x += width; + } + } + + return COLUMN_NONE; +} + float file_string_width(const char *str) { uiStyle *style = UI_style_get(); @@ -512,20 +667,52 @@ float file_font_pointsize(void) #endif } -static void column_widths(FileSelectParams *params, struct FileLayout *layout) +static void file_attribute_columns_widths(const FileSelectParams *params, FileLayout *layout) { - int i; + FileAttributeColumn *columns = layout->attribute_columns; const bool small_size = SMALL_SIZE_CHECK(params->thumbnail_size); + const int pad = small_size ? 0 : ATTRIBUTE_COLUMN_PADDING * 2; - for (i = 0; i < MAX_FILE_COLUMN; ++i) { - layout->column_widths[i] = 0; + for (int i = 0; i < ATTRIBUTE_COLUMN_MAX; i++) { + layout->attribute_columns[i].width = 0; } - layout->column_widths[COLUMN_NAME] = ((float)params->thumbnail_size / 8.0f) * UI_UNIT_X; /* Biggest possible reasonable values... */ - layout->column_widths[COLUMN_DATE] = file_string_width(small_size ? "23/08/89" : "23-Dec-89"); - layout->column_widths[COLUMN_TIME] = file_string_width("23:59"); - layout->column_widths[COLUMN_SIZE] = file_string_width(small_size ? "98.7 M" : "98.7 MiB"); + columns[COLUMN_DATETIME].width = file_string_width(small_size ? "23/08/89" : + "23 Dec 6789, 23:59") + + pad; + columns[COLUMN_SIZE].width = file_string_width(small_size ? "98.7 M" : "098.7 MiB") + pad; + if (params->display == FILE_IMGDISPLAY) { + columns[COLUMN_NAME].width = ((float)params->thumbnail_size / 8.0f) * UI_UNIT_X; + } + /* Name column uses remaining width */ + else { + int remwidth = layout->tile_w; + for (FileAttributeColumnType column_type = ATTRIBUTE_COLUMN_MAX - 1; column_type >= 0; + column_type--) { + if ((column_type == COLUMN_NAME) || + !file_attribute_column_type_enabled(params, column_type)) { + continue; + } + remwidth -= columns[column_type].width; + } + columns[COLUMN_NAME].width = remwidth; + } +} + +static void file_attribute_columns_init(const FileSelectParams *params, FileLayout *layout) +{ + file_attribute_columns_widths(params, layout); + + layout->attribute_columns[COLUMN_NAME].name = N_("Name"); + layout->attribute_columns[COLUMN_NAME].sort_type = FILE_SORT_ALPHA; + layout->attribute_columns[COLUMN_NAME].text_align = UI_STYLE_TEXT_LEFT; + layout->attribute_columns[COLUMN_DATETIME].name = N_("Date Modified"); + layout->attribute_columns[COLUMN_DATETIME].sort_type = FILE_SORT_TIME; + layout->attribute_columns[COLUMN_DATETIME].text_align = UI_STYLE_TEXT_LEFT; + layout->attribute_columns[COLUMN_SIZE].name = N_("Size"); + layout->attribute_columns[COLUMN_SIZE].sort_type = FILE_SORT_SIZE; + layout->attribute_columns[COLUMN_SIZE].text_align = UI_STYLE_TEXT_RIGHT; } void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar) @@ -533,7 +720,6 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar) FileSelectParams *params = ED_fileselect_get_params(sfile); FileLayout *layout = NULL; View2D *v2d = &ar->v2d; - int maxlen = 0; int numfiles; int textheight; @@ -560,61 +746,69 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar) layout->tile_w = layout->prv_w + 2 * layout->prv_border_x; layout->tile_h = layout->prv_h + 2 * layout->prv_border_y + textheight; layout->width = (int)(BLI_rctf_size_x(&v2d->cur) - 2 * layout->tile_border_x); - layout->columns = layout->width / (layout->tile_w + 2 * layout->tile_border_x); - if (layout->columns > 0) { - layout->rows = numfiles / layout->columns + 1; // XXX dirty, modulo is zero + layout->flow_columns = layout->width / (layout->tile_w + 2 * layout->tile_border_x); + layout->attribute_column_header_h = 0; + layout->offset_top = 0; + if (layout->flow_columns > 0) { + layout->rows = numfiles / layout->flow_columns + 1; // XXX dirty, modulo is zero } else { - layout->columns = 1; + layout->flow_columns = 1; layout->rows = numfiles + 1; // XXX dirty, modulo is zero } layout->height = sfile->layout->rows * (layout->tile_h + 2 * layout->tile_border_y) + - layout->tile_border_y * 2; + layout->tile_border_y * 2 - layout->offset_top; layout->flag = FILE_LAYOUT_VER; } - else { - int column_space = 0.6f * UI_UNIT_X; - int column_icon_space = 0.2f * UI_UNIT_X; + else if (params->display == FILE_VERTICALDISPLAY) { + int rowcount; - layout->prv_w = 0; - layout->prv_h = 0; + layout->prv_w = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_X; + layout->prv_h = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_Y; + layout->tile_border_x = 0.4f * UI_UNIT_X; + layout->tile_border_y = 0.1f * UI_UNIT_Y; + layout->tile_h = textheight * 3 / 2; + layout->width = (int)(BLI_rctf_size_x(&v2d->cur) - 2 * layout->tile_border_x); + layout->tile_w = layout->width; + layout->flow_columns = 1; + layout->attribute_column_header_h = layout->tile_h * 1.2f + 2 * layout->tile_border_y; + layout->offset_top = layout->attribute_column_header_h; + rowcount = (int)(BLI_rctf_size_y(&v2d->cur) - layout->offset_top - 2 * layout->tile_border_y) / + (layout->tile_h + 2 * layout->tile_border_y); + file_attribute_columns_init(params, layout); + + layout->rows = MAX2(rowcount, numfiles); + BLI_assert(layout->rows != 0); + layout->height = sfile->layout->rows * (layout->tile_h + 2 * layout->tile_border_y) + + layout->tile_border_y * 2 + layout->offset_top; + layout->flag = FILE_LAYOUT_VER; + } + else if (params->display == FILE_HORIZONTALDISPLAY) { + layout->prv_w = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_X; + layout->prv_h = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_Y; layout->tile_border_x = 0.4f * UI_UNIT_X; layout->tile_border_y = 0.1f * UI_UNIT_Y; - layout->prv_border_x = 0; - layout->prv_border_y = 0; layout->tile_h = textheight * 3 / 2; + layout->attribute_column_header_h = 0; + layout->offset_top = layout->attribute_column_header_h; layout->height = (int)(BLI_rctf_size_y(&v2d->cur) - 2 * layout->tile_border_y); /* Padding by full scrollbar H is too much, can overlap tile border Y. */ layout->rows = (layout->height - V2D_SCROLL_HEIGHT + layout->tile_border_y) / (layout->tile_h + 2 * layout->tile_border_y); + layout->tile_w = VERTLIST_MAJORCOLUMN_WIDTH; + file_attribute_columns_init(params, layout); - column_widths(params, layout); - - if (params->display == FILE_SHORTDISPLAY) { - maxlen = ICON_DEFAULT_WIDTH_SCALE + column_icon_space + - (int)layout->column_widths[COLUMN_NAME] + column_space + - (int)layout->column_widths[COLUMN_SIZE] + column_space; - } - else { - maxlen = ICON_DEFAULT_WIDTH_SCALE + column_icon_space + - (int)layout->column_widths[COLUMN_NAME] + column_space + - (int)layout->column_widths[COLUMN_DATE] + column_space + - (int)layout->column_widths[COLUMN_TIME] + column_space + - (int)layout->column_widths[COLUMN_SIZE] + column_space; - } - layout->tile_w = maxlen; if (layout->rows > 0) { - layout->columns = numfiles / layout->rows + 1; // XXX dirty, modulo is zero + layout->flow_columns = numfiles / layout->rows + 1; // XXX dirty, modulo is zero } else { layout->rows = 1; - layout->columns = numfiles + 1; // XXX dirty, modulo is zero + layout->flow_columns = numfiles + 1; // XXX dirty, modulo is zero } - layout->width = sfile->layout->columns * (layout->tile_w + 2 * layout->tile_border_x) + + layout->width = sfile->layout->flow_columns * (layout->tile_w + 2 * layout->tile_border_x) + layout->tile_border_x * 2; layout->flag = FILE_LAYOUT_HOR; } - params->display_previous = params->display; layout->dirty = false; } @@ -742,7 +936,7 @@ int autocomplete_file(struct bContext *C, char *str, void *UNUSED(arg_v)) int nentries = filelist_files_ensure(sfile->files); int i; - for (i = 0; i < nentries; ++i) { + for (i = 0; i < nentries; i++) { FileDirEntry *file = filelist_file(sfile->files, i); UI_autocomplete_update_name(autocpl, file->relpath); } @@ -771,6 +965,17 @@ void ED_fileselect_exit(wmWindowManager *wm, ScrArea *sa, SpaceFile *sfile) return; } if (sfile->op) { + wmWindow *temp_win = WM_window_is_temp_screen(wm->winactive) ? wm->winactive : NULL; + int win_size[2]; + + if (temp_win) { + /* Get DPI/pixelsize independent size to be stored in preferences. */ + WM_window_set_dpi(temp_win); /* Ensure the DPI is taken from the right window. */ + win_size[0] = WM_window_pixels_x(temp_win) / UI_DPI_FAC; + win_size[1] = WM_window_pixels_y(temp_win) / UI_DPI_FAC; + } + ED_fileselect_params_to_userdef(sfile, temp_win ? win_size : NULL); + WM_event_fileselect_event(wm, sfile->op, EVT_FILESELECT_EXTERNAL_CANCEL); sfile->op = NULL; } diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c index b50c37baae6..7faa2b883f2 100644 --- a/source/blender/editors/space_file/fsmenu.c +++ b/source/blender/editors/space_file/fsmenu.c @@ -404,7 +404,7 @@ void fsmenu_write_file(struct FSMenu *fsmenu, const char *filename) fprintf(fp, "[Recent]\n"); for (fsm_iter = ED_fsmenu_get_category(fsmenu, FS_CATEGORY_RECENT); fsm_iter && (nwritten < FSMENU_RECENT_MAX); - fsm_iter = fsm_iter->next, ++nwritten) { + fsm_iter = fsm_iter->next, nwritten++) { if (fsm_iter->path && fsm_iter->save) { fsmenu_entry_generate_name(fsm_iter, fsm_name, sizeof(fsm_name)); if (fsm_iter->name[0] && !STREQ(fsm_iter->name, fsm_name)) { @@ -492,20 +492,17 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) /* Flee from horrible win querying hover floppy drives! */ if (i > 1) { - /* Try to get volume label as well... */ + /* Try to get a friendly drive description. */ + SHFILEINFOW shFile = {0}; BLI_strncpy_wchar_from_utf8(wline, tmps, 4); - if (GetVolumeInformationW( - wline, wline + 4, FILE_MAXDIR - 4, NULL, NULL, NULL, NULL, 0)) { - size_t label_len; - - BLI_strncpy_wchar_as_utf8(line, wline + 4, FILE_MAXDIR - 4); - - label_len = MIN2(strlen(line), FILE_MAXDIR - 6); - BLI_snprintf(line + label_len, 6, " (%.2s)", tmps); - + if (SHGetFileInfoW(wline, 0, &shFile, sizeof(SHFILEINFOW), SHGFI_DISPLAYNAME)) { + BLI_strncpy_wchar_as_utf8(line, shFile.szDisplayName, FILE_MAXDIR); name = line; } } + if (name == NULL) { + name = tmps; + } fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, tmps, name, FS_INSERT_SORTED); } diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 1fd878e4662..d63fcf402de 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -55,6 +55,40 @@ #include "filelist.h" #include "GPU_framebuffer.h" +static ARegion *file_execute_region_ensure(ScrArea *sa, ARegion *ar_prev) +{ + ARegion *ar; + + if ((ar = BKE_area_find_region_type(sa, RGN_TYPE_EXECUTE)) != NULL) { + return ar; + } + + ar = MEM_callocN(sizeof(ARegion), "execute region for file"); + BLI_insertlinkafter(&sa->regionbase, ar_prev, ar); + ar->regiontype = RGN_TYPE_EXECUTE; + ar->alignment = RGN_ALIGN_BOTTOM; + ar->flag = RGN_FLAG_DYNAMIC_SIZE; + + return ar; +} + +static ARegion *file_tool_props_region_ensure(ScrArea *sa, ARegion *ar_prev) +{ + ARegion *ar; + + if ((ar = BKE_area_find_region_type(sa, RGN_TYPE_TOOL_PROPS)) != NULL) { + return ar; + } + + /* add subdiv level; after execute region */ + ar = MEM_callocN(sizeof(ARegion), "tool props for file"); + BLI_insertlinkafter(&sa->regionbase, ar_prev, ar); + ar->regiontype = RGN_TYPE_TOOL_PROPS; + ar->alignment = RGN_ALIGN_RIGHT; + + return ar; +} + /* ******************** default callbacks for file space ***************** */ static SpaceLink *file_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) @@ -78,17 +112,14 @@ static SpaceLink *file_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scen ar->regiontype = RGN_TYPE_TOOLS; ar->alignment = RGN_ALIGN_LEFT; - /* Tool props (aka operator) region */ - ar = MEM_callocN(sizeof(ARegion), "tool props region for file"); - BLI_addtail(&sfile->regionbase, ar); - ar->regiontype = RGN_TYPE_TOOL_PROPS; - ar->alignment = RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV; - /* ui list region */ ar = MEM_callocN(sizeof(ARegion), "ui region for file"); BLI_addtail(&sfile->regionbase, ar); ar->regiontype = RGN_TYPE_UI; ar->alignment = RGN_ALIGN_TOP; + ar->flag |= RGN_FLAG_DYNAMIC_SIZE; + + /* Tool props and execute region are added as needed, see file_refresh(). */ /* main region */ ar = MEM_callocN(sizeof(ARegion), "main region for file"); @@ -201,9 +232,50 @@ static SpaceLink *file_duplicate(SpaceLink *sl) return (SpaceLink *)sfilen; } +static void file_ensure_valid_region_state(bContext *C, + wmWindowManager *wm, + wmWindow *win, + ScrArea *sa, + SpaceFile *sfile, + FileSelectParams *params) +{ + ARegion *ar_ui = BKE_area_find_region_type(sa, RGN_TYPE_UI); + ARegion *ar_props = BKE_area_find_region_type(sa, RGN_TYPE_TOOL_PROPS); + ARegion *ar_execute = BKE_area_find_region_type(sa, RGN_TYPE_EXECUTE); + bool needs_init = false; /* To avoid multiple ED_area_initialize() calls. */ + + /* If there's an file-operation, ensure we have the option and execute region */ + if (sfile->op && (ar_props == NULL)) { + ar_execute = file_execute_region_ensure(sa, ar_ui); + ar_props = file_tool_props_region_ensure(sa, ar_execute); + + if (params->flag & FILE_HIDE_TOOL_PROPS) { + ar_props->flag |= RGN_FLAG_HIDDEN; + } + else { + ar_props->flag &= ~RGN_FLAG_HIDDEN; + } + + needs_init = true; + } + /* If there's _no_ file-operation, ensure we _don't_ have the option and execute region */ + else if ((sfile->op == NULL) && (ar_props != NULL)) { + BLI_assert(ar_execute != NULL); + + ED_region_remove(C, sa, ar_props); + ED_region_remove(C, sa, ar_execute); + needs_init = true; + } + + if (needs_init) { + ED_area_initialize(wm, win, sa); + } +} + static void file_refresh(const bContext *C, ScrArea *sa) { wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); SpaceFile *sfile = CTX_wm_space_file(C); FileSelectParams *params = ED_fileselect_get_params(sfile); struct FSMenu *fsmenu = ED_fsmenu_get(); @@ -217,15 +289,16 @@ static void file_refresh(const bContext *C, ScrArea *sa) } filelist_setdir(sfile->files, params->dir); filelist_setrecursion(sfile->files, params->recursion_level); - filelist_setsorting(sfile->files, params->sort); - filelist_setfilter_options(sfile->files, - (params->flag & FILE_FILTER) != 0, - (params->flag & FILE_HIDE_DOT) != 0, - false, /* TODO hide_parent, should be controllable? */ - params->filter, - params->filter_id, - params->filter_glob, - params->filter_search); + filelist_setsorting(sfile->files, params->sort, params->flag & FILE_SORT_INVERT); + filelist_setfilter_options( + sfile->files, + (params->flag & FILE_FILTER) != 0, + (params->flag & FILE_HIDE_DOT) != 0, + true, /* Just always hide parent, prefer to not add an extra user option for this. */ + params->filter, + params->filter_id, + params->filter_glob, + params->filter_search); /* Update the active indices of bookmarks & co. */ sfile->systemnr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_SYSTEM, params->dir); @@ -254,7 +327,7 @@ static void file_refresh(const bContext *C, ScrArea *sa) else { filelist_cache_previews_set(sfile->files, false); if (sfile->previews_timer) { - WM_event_remove_timer_notifier(wm, CTX_wm_window(C), sfile->previews_timer); + WM_event_remove_timer_notifier(wm, win, sfile->previews_timer); sfile->previews_timer = NULL; } } @@ -268,11 +341,8 @@ static void file_refresh(const bContext *C, ScrArea *sa) } /* Might be called with NULL sa, see file_main_region_draw() below. */ - if (sa && BKE_area_find_region_type(sa, RGN_TYPE_TOOLS) == NULL) { - /* Create TOOLS/TOOL_PROPS regions. */ - file_tools_region(sa); - - ED_area_initialize(wm, CTX_wm_window(C), sa); + if (sa) { + file_ensure_valid_region_state((bContext *)C, wm, win, sa, sfile, params); } ED_area_tag_redraw(sa); @@ -406,6 +476,11 @@ static void file_main_region_draw(const bContext *C, ARegion *ar) v2d->keepofs &= ~V2D_LOCKOFS_Y; v2d->keepofs |= V2D_LOCKOFS_X; } + else if (params->display == FILE_VERTICALDISPLAY) { + v2d->scroll = V2D_SCROLL_RIGHT; + v2d->keepofs &= ~V2D_LOCKOFS_Y; + v2d->keepofs |= V2D_LOCKOFS_X; + } else { v2d->scroll = V2D_SCROLL_BOTTOM; v2d->keepofs &= ~V2D_LOCKOFS_X; @@ -439,7 +514,9 @@ static void file_main_region_draw(const bContext *C, ARegion *ar) UI_view2d_view_restore(C); /* scrollers */ - scrollers = UI_view2d_scrollers_calc(v2d, NULL); + rcti view_rect; + ED_fileselect_layout_maskrect(sfile->layout, v2d, &view_rect); + scrollers = UI_view2d_scrollers_calc(v2d, &view_rect); UI_view2d_scrollers_draw(v2d, scrollers); UI_view2d_scrollers_free(scrollers); } @@ -452,13 +529,13 @@ static void file_operatortypes(void) WM_operatortype_append(FILE_OT_select_box); WM_operatortype_append(FILE_OT_select_bookmark); WM_operatortype_append(FILE_OT_highlight); + WM_operatortype_append(FILE_OT_sort_column_ui_context); WM_operatortype_append(FILE_OT_execute); WM_operatortype_append(FILE_OT_cancel); WM_operatortype_append(FILE_OT_parent); WM_operatortype_append(FILE_OT_previous); WM_operatortype_append(FILE_OT_next); WM_operatortype_append(FILE_OT_refresh); - WM_operatortype_append(FILE_OT_bookmark_toggle); WM_operatortype_append(FILE_OT_bookmark_add); WM_operatortype_append(FILE_OT_bookmark_delete); WM_operatortype_append(FILE_OT_bookmark_cleanup); @@ -538,7 +615,8 @@ static void file_ui_region_init(wmWindowManager *wm, ARegion *ar) { wmKeyMap *keymap; - UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_HEADER, ar->winx, ar->winy); + ED_region_panels_init(wm, ar); + ar->v2d.keepzoom |= V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y; /* own keymap */ keymap = WM_keymap_ensure(wm->defaultconf, "File Browser", SPACE_FILE, 0); @@ -550,22 +628,24 @@ static void file_ui_region_init(wmWindowManager *wm, ARegion *ar) static void file_ui_region_draw(const bContext *C, ARegion *ar) { - float col[3]; - /* clear */ - UI_GetThemeColor3fv(TH_BACK, col); - GPU_clear_color(col[0], col[1], col[2], 0.0); - GPU_clear(GPU_COLOR_BIT); + ED_region_panels(C, ar); +} - /* scrolling here is just annoying, disable it */ - ar->v2d.cur.ymax = BLI_rctf_size_y(&ar->v2d.cur); - ar->v2d.cur.ymin = 0; +static void file_execution_region_init(wmWindowManager *wm, ARegion *ar) +{ + wmKeyMap *keymap; - /* set view2d view matrix for scrolling (without scrollers) */ - UI_view2d_view_ortho(&ar->v2d); + ED_region_panels_init(wm, ar); + ar->v2d.keepzoom |= V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y; - file_draw_buttons(C, ar); + /* own keymap */ + keymap = WM_keymap_ensure(wm->defaultconf, "File Browser", SPACE_FILE, 0); + WM_event_add_keymap_handler_v2d_mask(&ar->handlers, keymap); +} - UI_view2d_view_restore(C); +static void file_execution_region_draw(const bContext *C, ARegion *ar) +{ + ED_region_panels(C, ar); } static void file_ui_region_listener(wmWindow *UNUSED(win), @@ -656,13 +736,22 @@ void ED_spacetype_file(void) /* regions: ui */ art = MEM_callocN(sizeof(ARegionType), "spacetype file region"); art->regionid = RGN_TYPE_UI; - art->prefsizey = 60; art->keymapflag = ED_KEYMAP_UI; art->listener = file_ui_region_listener; art->init = file_ui_region_init; art->draw = file_ui_region_draw; BLI_addhead(&st->regiontypes, art); + /* regions: execution */ + art = MEM_callocN(sizeof(ARegionType), "spacetype file region"); + art->regionid = RGN_TYPE_EXECUTE; + art->keymapflag = ED_KEYMAP_UI; + art->listener = file_ui_region_listener; + art->init = file_execution_region_init; + art->draw = file_execution_region_draw; + BLI_addhead(&st->regiontypes, art); + file_execute_region_panels_register(art); + /* regions: channels (directories) */ art = MEM_callocN(sizeof(ARegionType), "spacetype file region"); art->regionid = RGN_TYPE_TOOLS; @@ -677,14 +766,14 @@ void ED_spacetype_file(void) /* regions: tool properties */ art = MEM_callocN(sizeof(ARegionType), "spacetype file operator region"); art->regionid = RGN_TYPE_TOOL_PROPS; - art->prefsizex = 0; - art->prefsizey = 360; + art->prefsizex = 240; + art->prefsizey = 60; art->keymapflag = ED_KEYMAP_UI; art->listener = file_tools_region_listener; art->init = file_tools_region_init; art->draw = file_tools_region_draw; BLI_addhead(&st->regiontypes, art); - file_panels_register(art); + file_tool_props_region_panels_register(art); BKE_spacetype_register(st); } |