diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index 93912a0eb18..3b3f6fe7caf 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -534,6 +534,14 @@ void BLO_sanitize_experimental_features_userpref_blend(struct UserDef *userdef); */ struct BlendThumbnail *BLO_thumbnail_from_file(const char *filepath); +/** + * Does a very light reading of given .blend file to extract its version. + * + * \param filepath: The path of the blend file to extract version from. + * \return The file version + */ +short BLO_version_from_file(const char *filepath); + /** Default theme, see: `release/datafiles/userdef/userdef_default_theme.c`. */ extern const struct bTheme U_theme_default; /** Default preferences, defined by: `release/datafiles/userdef/userdef_default.c`. */ diff --git a/source/blender/blenloader/intern/readfile.cc b/source/blender/blenloader/intern/readfile.cc index e6152cf5ee6..003312bebaf 100644 --- a/source/blender/blenloader/intern/readfile.cc +++ b/source/blender/blenloader/intern/readfile.cc @@ -1412,6 +1412,17 @@ BlendThumbnail *BLO_thumbnail_from_file(const char *filepath) return data; } +short BLO_version_from_file(const char *filepath) +{ + short version = 0; + FileData *fd = blo_filedata_from_file_minimal(filepath); + if (fd) { + version = fd->fileversion; + blo_filedata_free(fd); + } + return version; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/space_file/file_draw.cc b/source/blender/editors/space_file/file_draw.cc index f3549bcc1bc..3e83e4fa830 100644 --- a/source/blender/editors/space_file/file_draw.cc +++ b/source/blender/editors/space_file/file_draw.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include "MEM_guardedalloc.h" @@ -29,11 +30,16 @@ #include "BKE_context.hh" #include "BKE_report.h" +#include "BLO_readfile.h" + #include "BLT_translation.h" #include "BLF_api.h" +#include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "IMB_metadata.h" +#include "IMB_thumbs.h" #include "DNA_userdef_types.h" #include "DNA_windowmanager_types.h" @@ -108,11 +114,227 @@ void ED_file_path_button(bScreen *screen, UI_block_func_set(block, nullptr, nullptr, nullptr); } -/* Dummy helper - we need dynamic tooltips here. */ -static char *file_draw_tooltip_func(bContext * /*C*/, void *argN, const char * /*tip*/) +struct file_tooltip_data { + const SpaceFile *sfile; + const FileDirEntry *file; +}; + +static file_tooltip_data *file_tooltip_data_create(const SpaceFile *sfile, + const FileDirEntry *file) { - char *dyn_tooltip = static_cast(argN); - return BLI_strdup(dyn_tooltip); + file_tooltip_data *data = (file_tooltip_data *)MEM_mallocN(sizeof(file_tooltip_data), + "tooltip_data"); + data->sfile = sfile; + data->file = file; + return data; +} + +static void file_draw_tooltip_custom_func(bContext * /*C*/, struct uiTooltipData *tip, void *argN) +{ + file_tooltip_data *file_data = static_cast(argN); + const SpaceFile *sfile = file_data->sfile; + const FileList *files = sfile->files; + const FileSelectParams *params = ED_fileselect_get_active_params(sfile); + const FileDirEntry *file = file_data->file; + + BLI_assert_msg(!file->asset, "Asset tooltip should never be overridden here."); + + /* Check the FileDirEntry first to see if the preview is already loaded. */ + ImBuf *thumb = filelist_file_getimage(file); + + /* Only free if it is loaded later. */ + bool free_imbuf = (thumb == nullptr); + + UI_tooltip_text_field_add( + tip, BLI_strdup(file->name), nullptr, UI_TIP_STYLE_HEADER, UI_TIP_LC_MAIN); + UI_tooltip_text_field_add(tip, nullptr, nullptr, UI_TIP_STYLE_SPACER, UI_TIP_LC_NORMAL); + + if (!(file->typeflag & FILE_TYPE_BLENDERLIB)) { + + char full_path[FILE_MAX_LIBEXTRA]; + filelist_file_get_full_path(files, file, full_path); + + if (params->recursion_level > 0) { + char root[FILE_MAX]; + BLI_path_split_dir_part(full_path, root, FILE_MAX); + UI_tooltip_text_field_add( + tip, BLI_strdup(root), nullptr, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL); + } + + if (file->redirection_path) { + UI_tooltip_text_field_add(tip, + BLI_sprintfN("%s: %s", N_("Link target"), file->redirection_path), + nullptr, + UI_TIP_STYLE_NORMAL, + UI_TIP_LC_NORMAL); + } + if (file->attributes & FILE_ATTR_OFFLINE) { + UI_tooltip_text_field_add(tip, + BLI_strdup(N_("This file is offline")), + nullptr, + UI_TIP_STYLE_NORMAL, + UI_TIP_LC_ALERT); + } + if (file->attributes & FILE_ATTR_READONLY) { + UI_tooltip_text_field_add(tip, + BLI_strdup(N_("This file is read-only")), + nullptr, + UI_TIP_STYLE_NORMAL, + UI_TIP_LC_ALERT); + } + if (file->attributes & (FILE_ATTR_SYSTEM | FILE_ATTR_RESTRICTED)) { + UI_tooltip_text_field_add(tip, + BLI_strdup(N_("This is a restricted system file")), + nullptr, + UI_TIP_STYLE_NORMAL, + UI_TIP_LC_ALERT); + } + + if (file->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { + char version_st[128] = {0}; + if (!thumb) { + /* Load the thumbnail from cache if existing, but don't create if not. */ + thumb = IMB_thumb_read(full_path, THB_LARGE); + } + if (thumb) { + /* Look for version in existing thumbnail if available. */ + IMB_metadata_get_field( + thumb->metadata, "Thumb::Blender::Version", version_st, sizeof(version_st)); + } + + if (!version_st[0] && !(file->attributes & FILE_ATTR_OFFLINE)) { + /* Load Blender version directly from the file. */ + short version = BLO_version_from_file(full_path); + if (version != 0) { + SNPRINTF(version_st, "%d.%01d", version / 100, version % 100); + } + } + + if (version_st[0]) { + UI_tooltip_text_field_add(tip, + BLI_sprintfN("Blender %s", version_st), + nullptr, + UI_TIP_STYLE_NORMAL, + UI_TIP_LC_NORMAL); + UI_tooltip_text_field_add(tip, nullptr, nullptr, UI_TIP_STYLE_SPACER, UI_TIP_LC_NORMAL); + } + } + else if (file->typeflag & FILE_TYPE_IMAGE) { + if (!thumb) { + /* Load the thumbnail from cache if existing, create if not. */ + thumb = IMB_thumb_manage(full_path, THB_LARGE, THB_SOURCE_IMAGE); + } + if (thumb) { + char value1[128]; + char value2[128]; + if (IMB_metadata_get_field( + thumb->metadata, "Thumb::Image::Width", value1, sizeof(value1)) && + IMB_metadata_get_field( + thumb->metadata, "Thumb::Image::Height", value2, sizeof(value2))) + { + UI_tooltip_text_field_add(tip, + BLI_sprintfN("%s \u00D7 %s", value1, value2), + nullptr, + UI_TIP_STYLE_NORMAL, + UI_TIP_LC_NORMAL); + UI_tooltip_text_field_add(tip, nullptr, nullptr, UI_TIP_STYLE_SPACER, UI_TIP_LC_NORMAL); + } + } + } + else if (file->typeflag & FILE_TYPE_MOVIE) { + if (!thumb) { + /* This could possibly take a while. */ + thumb = IMB_thumb_manage(full_path, THB_LARGE, THB_SOURCE_MOVIE); + } + if (thumb) { + char value1[128]; + char value2[128]; + char value3[128]; + if (IMB_metadata_get_field( + thumb->metadata, "Thumb::Video::Width", value1, sizeof(value1)) && + IMB_metadata_get_field( + thumb->metadata, "Thumb::Video::Height", value2, sizeof(value2))) + { + UI_tooltip_text_field_add(tip, + BLI_sprintfN("%s \u00D7 %s", value1, value2), + nullptr, + UI_TIP_STYLE_NORMAL, + UI_TIP_LC_NORMAL); + } + if (IMB_metadata_get_field( + thumb->metadata, "Thumb::Video::Frames", value1, sizeof(value1)) && + IMB_metadata_get_field(thumb->metadata, "Thumb::Video::FPS", value2, sizeof(value2)) && + IMB_metadata_get_field( + thumb->metadata, "Thumb::Video::Duration", value3, sizeof(value3))) + { + UI_tooltip_text_field_add( + tip, + BLI_sprintfN("%s %s @ %s %s", value1, N_("Frames"), value2, N_("FPS")), + nullptr, + UI_TIP_STYLE_NORMAL, + UI_TIP_LC_NORMAL); + UI_tooltip_text_field_add(tip, + BLI_sprintfN("%s %s", value3, N_("seconds")), + nullptr, + UI_TIP_STYLE_NORMAL, + UI_TIP_LC_NORMAL); + UI_tooltip_text_field_add(tip, nullptr, nullptr, UI_TIP_STYLE_SPACER, UI_TIP_LC_NORMAL); + } + } + } + + char date_st[FILELIST_DIRENTRY_DATE_LEN], time_st[FILELIST_DIRENTRY_TIME_LEN]; + bool is_today, is_yesterday; + std::string day_string = (""); + BLI_filelist_entry_datetime_to_string( + NULL, file->time, false, time_st, date_st, &is_today, &is_yesterday); + if (is_today || is_yesterday) { + day_string = (is_today ? N_("Today") : N_("Yesterday")) + std::string(" "); + } + UI_tooltip_text_field_add(tip, + BLI_sprintfN("%s: %s%s%s", + N_("Modified"), + day_string.c_str(), + (is_today || is_yesterday) ? "" : date_st, + (is_today || is_yesterday) ? time_st : ""), + nullptr, + UI_TIP_STYLE_NORMAL, + UI_TIP_LC_NORMAL); + + if (!(file->typeflag & FILE_TYPE_DIR) && file->size > 0) { + char size[16]; + BLI_filelist_entry_size_to_string(NULL, file->size, false, size); + if (file->size < 10000) { + char size_full[16]; + BLI_str_format_uint64_grouped(size_full, file->size); + UI_tooltip_text_field_add( + tip, + BLI_sprintfN("%s: %s (%s %s)", N_("Size"), size, size_full, N_("bytes")), + nullptr, + UI_TIP_STYLE_NORMAL, + UI_TIP_LC_NORMAL); + } + else { + UI_tooltip_text_field_add(tip, + BLI_sprintfN("%s: %s", N_("Size"), size), + nullptr, + UI_TIP_STYLE_NORMAL, + UI_TIP_LC_NORMAL); + } + } + } + + if (thumb && params->display != FILE_IMGDISPLAY) { + float scale = (96.0f * UI_SCALE_FAC) / float(MAX2(thumb->x, thumb->y)); + short size[2] = {short(float(thumb->x) * scale), short(float(thumb->y) * scale)}; + UI_tooltip_text_field_add(tip, nullptr, nullptr, UI_TIP_STYLE_SPACER, UI_TIP_LC_NORMAL); + UI_tooltip_text_field_add(tip, nullptr, nullptr, UI_TIP_STYLE_SPACER, UI_TIP_LC_NORMAL); + UI_tooltip_image_field_add(tip, thumb, size); + } + + if (thumb && free_imbuf) { + IMB_freeImBuf(thumb); + } } static char *file_draw_asset_tooltip_func(bContext * /*C*/, void *argN, const char * /*tip*/) @@ -172,7 +394,7 @@ static void file_but_enable_drag(uiBut *but, static uiBut *file_add_icon_but(const SpaceFile *sfile, uiBlock *block, - const char *path, + const char * /*path*/, const FileDirEntry *file, const rcti *tile_draw_rect, int icon, @@ -195,7 +417,8 @@ static uiBut *file_add_icon_but(const SpaceFile *sfile, UI_but_func_tooltip_set(but, file_draw_asset_tooltip_func, file->asset, nullptr); } else { - UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path), MEM_freeN); + UI_but_func_tooltip_custom_set( + but, file_draw_tooltip_custom_func, file_tooltip_data_create(sfile, file), MEM_freeN); } return but; @@ -335,7 +558,8 @@ static void file_add_preview_drag_but(const SpaceFile *sfile, UI_but_func_tooltip_set(but, file_draw_asset_tooltip_func, file->asset, nullptr); } else { - UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path), MEM_freeN); + UI_but_func_tooltip_custom_set( + but, file_draw_tooltip_custom_func, file_tooltip_data_create(sfile, file), MEM_freeN); } } diff --git a/source/blender/imbuf/intern/anim_movie.cc b/source/blender/imbuf/intern/anim_movie.cc index 66f173f0816..9945a509aa0 100644 --- a/source/blender/imbuf/intern/anim_movie.cc +++ b/source/blender/imbuf/intern/anim_movie.cc @@ -1549,6 +1549,30 @@ ImBuf *IMB_anim_previewframe(anim *anim) IMB_freeImBuf(ibuf); position = anim->duration_in_frames / 2; ibuf = IMB_anim_absolute(anim, position, IMB_TC_NONE, IMB_PROXY_NONE); + + char value[128]; + IMB_metadata_ensure(&ibuf->metadata); + SNPRINTF(value, "%i", anim->x); + IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Width", value); + SNPRINTF(value, "%i", anim->y); + IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Height", value); + SNPRINTF(value, "%i", anim->duration_in_frames); + IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Frames", value); + +#ifdef WITH_FFMPEG + if (anim->pFormatCtx && anim->curtype == ANIM_FFMPEG) { + AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; + AVRational frame_rate = av_guess_frame_rate(anim->pFormatCtx, v_st, NULL); + if (frame_rate.num != 0) { + double duration = anim->duration_in_frames / av_q2d(frame_rate); + SNPRINTF(value, "%g", av_q2d(frame_rate)); + IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::FPS", value); + SNPRINTF(value, "%g", duration); + IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Duration", value); + IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Codec", anim->pCodec->long_name); + } + } +#endif } return ibuf; } diff --git a/source/blender/windowmanager/intern/wm_files.cc b/source/blender/windowmanager/intern/wm_files.cc index caf2f753fc9..124bea5fc6e 100644 --- a/source/blender/windowmanager/intern/wm_files.cc +++ b/source/blender/windowmanager/intern/wm_files.cc @@ -92,6 +92,7 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "IMB_metadata.h" #include "IMB_thumbs.h" #include "ED_asset.hh" @@ -1722,6 +1723,12 @@ static ImBuf *blend_file_thumb_from_screenshot(bContext *C, BlendThumbnail **r_t /* File-system thumbnail image can be 256x256. */ IMB_scaleImBuf(ibuf, ex * 2, ey * 2); + /* Save metadata for quick access. */ + char version_st[10] = {0}; + SNPRINTF(version_st, "%d.%01d", BLENDER_VERSION / 100, BLENDER_VERSION % 100); + IMB_metadata_ensure(&ibuf->metadata); + IMB_metadata_set_field(ibuf->metadata, "Thumb::Blender::Version", version_st); + /* Thumbnail inside blend should be 128x128. */ ImBuf *thumb_ibuf = IMB_dupImBuf(ibuf); IMB_scaleImBuf(thumb_ibuf, ex, ey); @@ -1831,6 +1838,13 @@ static ImBuf *blend_file_thumb_from_camera(const bContext *C, /* dirty oversampling */ ImBuf *thumb_ibuf; thumb_ibuf = IMB_dupImBuf(ibuf); + + /* Save metadata for quick access. */ + char version_st[10] = {0}; + SNPRINTF(version_st, "%d.%01d", BLENDER_VERSION / 100, BLENDER_VERSION % 100); + IMB_metadata_ensure(&ibuf->metadata); + IMB_metadata_set_field(ibuf->metadata, "Thumb::Blender::Version", version_st); + /* BLEN_THUMB_SIZE is size of thumbnail inside blend file: 128x128. */ IMB_scaleImBuf(thumb_ibuf, BLEN_THUMB_SIZE, BLEN_THUMB_SIZE); thumb = BKE_main_thumbnail_from_imbuf(nullptr, thumb_ibuf);