diff --git a/rsc/images/icons.dds b/rsc/images/icons.dds index 270174c..a69be9d 100644 Binary files a/rsc/images/icons.dds and b/rsc/images/icons.dds differ diff --git a/src/Settings.h b/src/Settings.h index 0b80f1f..87aefc9 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -321,10 +321,12 @@ struct Application std::vector windows; // recent files histories + int orderingSessions; History recentSessions; History recentFolders; History recentImport; History recentImportFolders; + int orderingImportFolder; History recentRecordings; std::map< std::string, std::string > dialogRecentFolder; @@ -361,6 +363,8 @@ struct Application windows = std::vector(1+MAX_OUTPUT_WINDOW); windows[0].w = 1600; windows[0].h = 900; + orderingSessions = 3; + orderingImportFolder = 3; } }; diff --git a/src/SystemToolkit.cpp b/src/SystemToolkit.cpp index ab35ff8..c97935e 100644 --- a/src/SystemToolkit.cpp +++ b/src/SystemToolkit.cpp @@ -280,6 +280,70 @@ string SystemToolkit::full_filename(const std::string& path, const string &filen return fullfilename; } +unsigned long SystemToolkit::file_modification_time(const std::string& path) +{ + if (file_exists(path)) { + struct stat statsfile; + // fill statistics of given file path + if( stat( path.c_str(), &statsfile) > -1 ) { + // return modification time + return (unsigned long) statsfile.st_mtime; + } + } + + return 0; +} + +std::string SystemToolkit::file_modification_time_string(const std::string& path) +{ + ostringstream oss; + + if (file_exists(path)) { + struct stat statsfile; + // fill statistics of given file path + if( stat( path.c_str(), &statsfile) > -1 ) { + // read modification time + tm *datetime = localtime(&statsfile.st_mtime); + ostringstream oss; + oss << setw(4) << setfill('0') << to_string(datetime->tm_year + 1900); + oss << setw(2) << setfill('0') << to_string(datetime->tm_mon + 1); + oss << setw(2) << setfill('0') << to_string(datetime->tm_mday ); + oss << setw(2) << setfill('0') << to_string(datetime->tm_hour ); + oss << setw(2) << setfill('0') << to_string(datetime->tm_min ); + oss << setw(2) << setfill('0') << to_string(datetime->tm_sec ); + } + + } + + return oss.str(); +} + + +void SystemToolkit::reorder_file_list(std::list &filelist, Ordering m) +{ + if ( m >= DATE ) { + auto dateComparator = [](const std::string &a, const std::string &b) { + return SystemToolkit::file_modification_time(a) < SystemToolkit::file_modification_time(b); + }; + filelist.sort( dateComparator ); + if ( m == DATE_INVERSE ) + filelist.reverse(); + } + else { + auto alphaComparator = [](const std::string &a, const std::string &b) { + std::string _a = a; + std::string _b = b; + std::transform(_a.begin(), _a.end(), _a.begin(), ::tolower); + std::transform(_b.begin(), _b.end(), _b.begin(), ::tolower); + return _a < _b; + }; + filelist.sort( alphaComparator ); + if ( m == ALPHA_INVERSE ) + filelist.reverse(); + } +} + + bool SystemToolkit::file_exists(const string& path) { if (path.empty()) @@ -307,7 +371,7 @@ string SystemToolkit::path_directory(const string& path) return directorypath; } -list SystemToolkit::list_directory(const string& path, const list& patterns) +list SystemToolkit::list_directory(const string& path, const list& patterns, Ordering m) { list ls; @@ -329,7 +393,7 @@ list SystemToolkit::list_directory(const string& path, const list list_directory(const std::string& path, const std::list &patterns); - // builds a path relative to 'relativeTo' to reach file at 'absolutePath' (e.g. /a/b/c/d rel to /a/b/e -> ../c/d) std::string path_relative_to_path(const std::string& absolutePath, const std::string& relativeTo); @@ -64,6 +61,23 @@ namespace SystemToolkit // generates a filename at given path, with basename and date prefix std::string filename_dateprefix(const std::string& path, const std::string& base, const std::string& extension); + // Get modification time of file, as string YYYYMMDDHHmmss + unsigned long file_modification_time(const std::string& path); + std::string file_modification_time_string(const std::string& path); + + + typedef enum { + ALPHA = 0, + ALPHA_INVERSE = 1, + DATE = 2, + DATE_INVERSE = 3 + } Ordering; + // + void reorder_file_list(std::list &filelist, Ordering m); + + // list all files of a directory mathing the given filter extension (if any) + std::list list_directory(const std::string& path, const std::list &patterns, Ordering m = ALPHA); + // true of file exists bool file_exists(const std::string& path); diff --git a/src/UserInterfaceManager.cpp b/src/UserInterfaceManager.cpp index a856812..9619054 100644 --- a/src/UserInterfaceManager.cpp +++ b/src/UserInterfaceManager.cpp @@ -2649,6 +2649,11 @@ void UserInterface::RenderHelp() /// /// NAVIGATOR /// +/// + +std::vector< std::pair > Navigator::icons_ordering_files = { {2,12}, {3,12}, {4,12}, {5,12} }; +std::vector< std::string > Navigator::tooltips_ordering_files = { "Alphabetical", "Invert alphabetical", "Older files first", "Recent files first" }; + Navigator::Navigator() { // default geometry @@ -3480,7 +3485,8 @@ void Navigator::RenderNewPannel() // MODE LIST FOLDER else if ( new_media_mode == MEDIA_FOLDER) { // show list of media files in folder - sourceMediaFiles = SystemToolkit::list_directory( Settings::application.recentImportFolders.path, { MEDIA_FILES_PATTERN }); + sourceMediaFiles = SystemToolkit::list_directory( Settings::application.recentImportFolders.path, { MEDIA_FILES_PATTERN }, + (SystemToolkit::Ordering) Settings::application.orderingImportFolder); } // indicate the list changed (do not change at every frame) new_media_mode_changed = false; @@ -3524,9 +3530,10 @@ void Navigator::RenderNewPannel() ImGui::ListBoxFooter(); } + // Supplementary icons to manage the list + ImVec2 pos_bot = ImGui::GetCursorPos(); + // Bottom Right side of the list: helper and options of Recent Recordings if (new_media_mode == MEDIA_RECORDING) { - // Bottom Right side of the list: helper and options - ImVec2 pos_bot = ImGui::GetCursorPos(); ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - 2.f * ImGui::GetFrameHeightWithSpacing())); ImGuiToolkit::HelpToolTip("Recently recorded videos (lastest on top). Clic on a filename to open.\n\n" ICON_FA_CHEVRON_CIRCLE_RIGHT " Auto-preload prepares this panel with the " @@ -3541,9 +3548,18 @@ void Navigator::RenderNewPannel() new_source_preview_.setSource( Mixer::manager().createSourceFile(sourceMediaFileCurrent), label); } } - // come back... - ImGui::SetCursorPos(pos_bot); } + // Top right of Media folder list + else if (new_media_mode == MEDIA_FOLDER) { + // ordering list + ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_top.y) ); + ImGui::PushID("##new_media_mode_changed"); + if ( ImGuiToolkit::IconMultistate(icons_ordering_files, &Settings::application.orderingImportFolder, tooltips_ordering_files) ) + new_media_mode_changed = true; + ImGui::PopID(); + } + // come back... + ImGui::SetCursorPos(pos_bot); } // Sequence Source creator @@ -4103,14 +4119,18 @@ void Navigator::RenderMainPannelVimix() // show list of recent sessions Settings::application.recentSessions.validate(); sessions_list = Settings::application.recentSessions.filenames; - Settings::application.recentSessions.changed = false; + SystemToolkit::reorder_file_list( sessions_list, (SystemToolkit::Ordering) Settings::application.orderingSessions); } // selection MODE 1 : LIST FOLDER else if ( selection_session_mode == 1) { // show list of vimix files in folder - sessions_list = SystemToolkit::list_directory( Settings::application.recentFolders.path, { VIMIX_FILE_PATTERN }); + sessions_list = SystemToolkit::list_directory( Settings::application.recentFolders.path, { VIMIX_FILE_PATTERN }, + (SystemToolkit::Ordering) Settings::application.orderingSessions); } + // indicate the list changed (do not change at every frame) + Settings::application.recentSessions.changed = false; + Settings::application.recentFolders.changed = false; selection_session_mode_changed = false; _file_over = sessions_list.end(); _displayed_over = sessions_list.end(); @@ -4219,8 +4239,16 @@ void Navigator::RenderMainPannelVimix() if (ImGui::IsItemHovered()) ImGuiToolkit::ToolTip("New session", SHORTCUT_NEW_FILE); + // ordering list + ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_top.y + ImGui::GetFrameHeightWithSpacing()) ); + ImGui::PushID("##selection_session_mode_changed"); + if ( ImGuiToolkit::IconMultistate(icons_ordering_files, &Settings::application.orderingSessions, tooltips_ordering_files) ) + selection_session_mode_changed = true; + ImGui::PopID(); + + // help indicator ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - 2.f * ImGui::GetFrameHeightWithSpacing())); - ImGuiToolkit::HelpToolTip("Here are listed either all recent files or all the sessions files inside a selected folder (*.mix) .\n\n" + ImGuiToolkit::HelpToolTip("Here are listed either the recent files or all the sessions files (*.mix) in a selected folder.\n\n" "Double-clic on a filename to open the session.\n\n" ICON_FA_ARROW_CIRCLE_RIGHT " Smooth transition " "performs cross fading to the opened session."); @@ -4262,35 +4290,6 @@ void Navigator::RenderMainPannelVimix() ImGui::InputText("##Info", (char *)info.c_str(), info.size(), ImGuiInputTextFlags_ReadOnly); ImGui::PopStyleColor(1); - // Kept for later? Larger info box with more details on the session file... - // ImGuiTextBuffer info; - // if (!sessionfilename.empty()) - // info.appendf("%s\n", SystemToolkit::filename(sessionfilename).c_str()); - // else - // info.append("\n"); - // info.appendf("%dx%dpx", output->width(), output->height()); - // if (p.x > -1) - // info.appendf(", %s", FrameBuffer::aspect_ratio_name[p.x]); - // // content info - // const uint N = Mixer::manager().session()->numSource(); - // if (N > 1) - // info.appendf("\n%d sources", N); - // else if (N > 0) - // info.append("\n1 source"); - // const size_t M = MediaPlayer::registered().size(); - // if (M > 0) { - // info.appendf("\n%d media files:", M); - // for (auto mit = MediaPlayer::begin(); mit != MediaPlayer::end(); ++mit) - // info.appendf("\n- %s", SystemToolkit::filename((*mit)->filename()).c_str()); - // } - // // Show info text bloc (multi line, dark background) - // ImGuiToolkit::PushFont( ImGuiToolkit::FONT_MONO ); - // ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.14f, 0.14f, 0.14f, 0.9f)); - // ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); - // ImGui::InputTextMultiline("##Info", (char *)info.c_str(), info.size(), ImVec2(IMGUI_RIGHT_ALIGN, 2*ImGui::GetTextLineHeightWithSpacing()), ImGuiInputTextFlags_ReadOnly); - // ImGui::PopStyleColor(1); - // ImGui::PopFont(); - // change resolution (height only) // get parameters to edit resolution glm::ivec2 preset = RenderView::presetFromResolution(output->resolution()); @@ -4375,7 +4374,7 @@ void Navigator::RenderMainPannelVimix() // Thumbnail static Thumbnail _file_thumbnail; static FrameBufferImage *thumbnail = nullptr; - if ( ImGui::Button( ICON_FA_TAG " New thumbnail", ImVec2(IMGUI_RIGHT_ALIGN, 0)) ) { + if ( ImGui::Button( ICON_FA_TAG " Create thumbnail", ImVec2(IMGUI_RIGHT_ALIGN, 0)) ) { Mixer::manager().session()->setThumbnail(); thumbnail = nullptr; } @@ -4647,8 +4646,8 @@ void Navigator::RenderMainPannelVimix() ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - 2.f * ImGui::GetFrameHeightWithSpacing())); ImGuiToolkit::HelpToolTip("Previous versions of the session (latest on top). " "Double-clic on a version to restore it.\n\n" - ICON_FA_CODE_BRANCH " Iterative saving automatically " - "keeps a version each time a session is saved."); + ICON_FA_CODE_BRANCH " With iterative saving on, a new version " + "is kept each time the session is saved."); // toggle button for versioning ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - ImGui::GetFrameHeightWithSpacing()) ); ImGuiToolkit::ButtonToggle(" " ICON_FA_CODE_BRANCH " ", &Settings::application.save_version_snapshot,"Iterative saving"); @@ -4803,8 +4802,8 @@ void Navigator::RenderMousePointerSelector(const ImVec2 &size) ImGui::SameLine(0, IMGUI_SAME_LINE * 3); ImGuiToolkit::ButtonToggle(Settings::application.mouse_pointer_lock ? ICON_FA_LOCK ALT_LOCK : ICON_FA_UNLOCK ALT_LOCK, &Settings::application.mouse_pointer_lock, - "Activate the selected snap mouse pointer by pressing the [" ALT_MOD "] key.\n\n" - ICON_FA_LOCK ALT_LOCK " keeps the snap mouse pointer active."); + "Activate the selected Snap mouse cursor by pressing the [" ALT_MOD "] key.\n\n" + ICON_FA_LOCK ALT_LOCK " keeps the Snap mouse cursor active."); // slider to adjust strength of the mouse pointer ImGui::SetNextItemWidth( IMGUI_RIGHT_ALIGN ); @@ -4821,12 +4820,15 @@ void Navigator::RenderMousePointerSelector(const ImVec2 &size) } // special case of GRID else { - static const char* grid_names[Grid::UNIT_ONE+1] = { "Precise (0.05)", "Small (0.1)", "Default (0.2)", "Large (0.5)", "Huge (1.0)"}; + static const char* grid_names[Grid::UNIT_ONE+1] = { "Precise", "Small", "Default", "Large", "Huge"}; int grid_current = (Grid::Units) round( *val * 4.f) ; const char* grid_current_name = (grid_current >= 0 && grid_current <= Grid::UNIT_ONE) ? grid_names[grid_current] : "Unknown"; - if (ImGui::SliderInt("##slidergrid", &grid_current, 0, Grid::UNIT_ONE, grid_current_name) ) { + if (ImGui::SliderInt("##slidergrid", &grid_current, 0, Grid::UNIT_ONE, grid_current_name) ) *val = (float) grid_current * 0.25f; + if (ImGui::IsItemHovered() && g.IO.MouseWheel != 0.f ){ + *val += 0.25f * g.IO.MouseWheel; + *val = CLAMP( *val, 0.f, 1.f); } } ImGui::SameLine(0, IMGUI_SAME_LINE); diff --git a/src/UserInterfaceManager.h b/src/UserInterfaceManager.h index 644c870..9f18fc9 100644 --- a/src/UserInterfaceManager.h +++ b/src/UserInterfaceManager.h @@ -110,6 +110,9 @@ private: MediaCreateMode new_media_mode; bool new_media_mode_changed; Source *source_to_replace; + + static std::vector< std::pair > icons_ordering_files; + static std::vector< std::string > tooltips_ordering_files; }; class ToolBox