New Playlists and new main panel

Favorite and custom playlists of Sessions. Main panel separate control of current session (with preview) and selection of session in playlists. Bugfix in history of files.
This commit is contained in:
Bruno Herbelin
2023-09-17 00:51:34 +02:00
parent 00f7e0fe62
commit 33c222555f
15 changed files with 1697 additions and 854 deletions

Binary file not shown.

View File

@@ -98,6 +98,7 @@ set(VMIX_SRCS
ScreenCaptureSource.cpp
MousePointer.cpp
Grid.cpp
Playlist.cpp
)
#####

View File

@@ -140,6 +140,54 @@ void DialogToolkit::OpenSessionDialog::open()
}
}
std::list<std::string> selectSessionsFileDialog(const std::string &label,const std::string &path);
void DialogToolkit::MultipleSessionsDialog::open()
{
if ( !busy_ && promisedlist_.empty() ) {
promisedlist_.emplace_back( std::async(std::launch::async, selectSessionsFileDialog, id_,
Settings::application.dialogRecentFolder[id_]) );
busy_ = true;
}
}
bool DialogToolkit::MultipleSessionsDialog::closed()
{
if ( !promisedlist_.empty() ) {
// check that file dialog thread finished
if (promisedlist_.back().wait_for(timeout) == std::future_status::ready ) {
// get the filename from this file dialog
std::list<std::string> list = promisedlist_.back().get();
if (!list.empty()) {
// selected a filenames
pathlist_ = list;
path_ = list.front();
// save path location
Settings::application.dialogRecentFolder[id_] = SystemToolkit::path_filename(path_);
}
else {
pathlist_.clear();
path_.clear();
}
// done with this file dialog
promisedlist_.pop_back();
busy_ = false;
return true;
}
}
return false;
}
std::string openPlaylistFileDialog(const std::string &label, const std::string &path);
void DialogToolkit::OpenPlaylistDialog::open()
{
if ( !busy_ && promises_.empty() ) {
promises_.emplace_back( std::async(std::launch::async, openPlaylistFileDialog, id_,
Settings::application.dialogRecentFolder[id_]) );
busy_ = true;
}
}
std::string openMediaFileDialog(const std::string &label, const std::string &path);
void DialogToolkit::OpenMediaDialog::open()
{
@@ -165,6 +213,21 @@ void DialogToolkit::SaveSessionDialog::setFolder(std::string path)
Settings::application.dialogRecentFolder[id_] = SystemToolkit::path_filename( path );
}
std::string savePlaylistFileDialog(const std::string &label, const std::string &path);
void DialogToolkit::SavePlaylistDialog::open()
{
if ( !busy_ && promises_.empty() ) {
promises_.emplace_back( std::async(std::launch::async, savePlaylistFileDialog, id_,
Settings::application.dialogRecentFolder[id_]) );
busy_ = true;
}
}
void DialogToolkit::SavePlaylistDialog::setFolder(std::string path)
{
Settings::application.dialogRecentFolder[id_] = SystemToolkit::path_filename( path );
}
std::string openFolderDialog(const std::string &label, const std::string &path);
void DialogToolkit::OpenFolderDialog::open()
{
@@ -227,7 +290,7 @@ std::string saveSessionFileDialog(const std::string &label, const std::string &p
#if USE_TINYFILEDIALOG
char const * save_file_name;
save_file_name = tinyfd_saveFileDialog( label.c_str(), path.c_str(), 1, save_pattern, "vimix session");
save_file_name = tinyfd_saveFileDialog( label.c_str(), path.c_str(), 1, save_pattern, "vimix (MIX)");
if (save_file_name)
filename = std::string(save_file_name);
@@ -244,7 +307,7 @@ std::string saveSessionFileDialog(const std::string &label, const std::string &p
gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER(dialog), TRUE );
// set file filters
add_filter_file_dialog(dialog, 1, save_pattern, "vimix session");
add_filter_file_dialog(dialog, 1, save_pattern, "vimix (MIX)");
add_filter_any_file_dialog(dialog);
// Set the default path
@@ -288,7 +351,7 @@ std::string openSessionFileDialog(const std::string &label, const std::string &p
#if USE_TINYFILEDIALOG
char const * open_file_name;
open_file_name = tinyfd_openFileDialog( label.c_str(), startpath.c_str(), 1, open_pattern, "vimix session", 0);
open_file_name = tinyfd_openFileDialog( label.c_str(), startpath.c_str(), 1, open_pattern, "vimix (MIX)", 0);
if (open_file_name)
filename = std::string(open_file_name);
@@ -304,7 +367,7 @@ std::string openSessionFileDialog(const std::string &label, const std::string &p
"_Open", GTK_RESPONSE_ACCEPT, NULL );
// set file filters
add_filter_file_dialog(dialog, 1, open_pattern, "vimix session");
add_filter_file_dialog(dialog, 1, open_pattern, "vimix (MIX)");
add_filter_any_file_dialog(dialog);
// Set the default path
@@ -337,6 +400,205 @@ std::string openSessionFileDialog(const std::string &label, const std::string &p
}
std::list<std::string> selectSessionsFileDialog(const std::string &label,const std::string &path)
{
std::list<std::string> files;
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
char const * open_pattern[1] = { VIMIX_FILE_PATTERN };
#if USE_TINYFILEDIALOG
char const * open_file_names;
open_file_names = tinyfd_openFileDialog(label.c_str(), startpath.c_str(), 1, open_pattern, "vimix (MIX)", 1);
if (open_file_names) {
const std::string& str (open_file_names);
const std::string& delimiters = "|";
// Skip delimiters at beginning.
std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
// Find first non-delimiter.
std::string::size_type pos = str.find_first_of(delimiters, lastPos);
while (std::string::npos != pos || std::string::npos != lastPos) {
// Found a token, add it to the vector.
files.push_back(str.substr(lastPos, pos - lastPos));
// Skip delimiters.
lastPos = str.find_first_not_of(delimiters, pos);
// Find next non-delimiter.
pos = str.find_first_of(delimiters, lastPos);
}
}
#else
if (!gtk_init()) {
DialogToolkit::ErrorDialog("Could not initialize GTK+ for dialog");
return files;
}
GtkWidget *dialog = gtk_file_chooser_dialog_new( label.c_str(), NULL,
GTK_FILE_CHOOSER_ACTION_OPEN,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Open", GTK_RESPONSE_ACCEPT, NULL );
// set file filters
add_filter_file_dialog(dialog, 1, open_pattern, "vimix (MIX)");
add_filter_any_file_dialog(dialog);
// multiple files
gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER(dialog), true );
// Set the default path
gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), startpath.c_str() );
// ensure front and centered
gtk_window_set_keep_above( GTK_WINDOW(dialog), TRUE );
if (window_x > 0 && window_y > 0)
gtk_window_move( GTK_WINDOW(dialog), window_x, window_y);
// display and get filename
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
GSList *open_file_names = gtk_file_chooser_get_filenames( GTK_FILE_CHOOSER(dialog) );
while (open_file_names) {
files.push_back( (char *) open_file_names->data );
open_file_names = open_file_names->next;
}
g_slist_free( open_file_names );
}
// remember position
gtk_window_get_position( GTK_WINDOW(dialog), &window_x, &window_y);
// done
gtk_widget_destroy(dialog);
wait_for_event();
#endif
return files;
}
std::string openPlaylistFileDialog(const std::string &label, const std::string &path)
{
std::string filename = "";
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
char const * open_pattern[1] = { VIMIX_PLAYLIST_FILE_PATTERN };
#if USE_TINYFILEDIALOG
char const * open_file_name;
open_file_name = tinyfd_openFileDialog( label.c_str(), startpath.c_str(), 1, open_pattern, "vimix playlist", 0);
if (open_file_name)
filename = std::string(open_file_name);
#else
if (!gtk_init()) {
DialogToolkit::ErrorDialog("Could not initialize GTK+ for dialog");
return filename;
}
GtkWidget *dialog = gtk_file_chooser_dialog_new( label.c_str(), NULL,
GTK_FILE_CHOOSER_ACTION_OPEN,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Open", GTK_RESPONSE_ACCEPT, NULL );
// set file filters
add_filter_file_dialog(dialog, 1, open_pattern, "vimix playlist");
add_filter_any_file_dialog(dialog);
// Set the default path
gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), startpath.c_str() );
// ensure front and centered
gtk_window_set_keep_above( GTK_WINDOW(dialog), TRUE );
if (window_x > 0 && window_y > 0)
gtk_window_move( GTK_WINDOW(dialog), window_x, window_y);
// display and get filename
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
char *open_file_name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) );
if (open_file_name) {
filename = std::string(open_file_name);
g_free( open_file_name );
}
}
// remember position
gtk_window_get_position( GTK_WINDOW(dialog), &window_x, &window_y);
// done
gtk_widget_destroy(dialog);
wait_for_event();
#endif
return filename;
}
std::string savePlaylistFileDialog(const std::string &label, const std::string &path)
{
std::string filename = "";
char const * save_pattern[1] = { VIMIX_PLAYLIST_FILE_PATTERN };
#if USE_TINYFILEDIALOG
char const * save_file_name;
save_file_name = tinyfd_saveFileDialog( label.c_str(), path.c_str(), 1, save_pattern, "vimix playlist");
if (save_file_name)
filename = std::string(save_file_name);
#else
if (!gtk_init()) {
DialogToolkit::ErrorDialog("Could not initialize GTK+ for dialog");
return filename;
}
GtkWidget *dialog = gtk_file_chooser_dialog_new( label.c_str(), NULL,
GTK_FILE_CHOOSER_ACTION_SAVE,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Save", GTK_RESPONSE_ACCEPT, NULL );
gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER(dialog), TRUE );
// set file filters
add_filter_file_dialog(dialog, 1, save_pattern, "vimix playlist");
add_filter_any_file_dialog(dialog);
// Set the default path
gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), path.c_str() );
// ensure front and centered
gtk_window_set_keep_above( GTK_WINDOW(dialog), TRUE );
if (window_x > 0 && window_y > 0)
gtk_window_move( GTK_WINDOW(dialog), window_x, window_y);
// display and get filename
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
char *save_file_name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) );
if (save_file_name) {
filename = std::string(save_file_name);
g_free( save_file_name );
}
}
// remember position
gtk_window_get_position( GTK_WINDOW(dialog), &window_x, &window_y);
// done
gtk_widget_destroy(dialog);
wait_for_event();
#endif
if (!filename.empty() && !SystemToolkit::has_extension(filename, VIMIX_PLAYLIST_FILE_EXT ) )
filename += std::string(".") + VIMIX_PLAYLIST_FILE_EXT;
return filename;
}
std::string openMediaFileDialog(const std::string &label, const std::string &path)
{
std::string filename = "";
@@ -511,7 +773,7 @@ std::list<std::string> selectImagesFileDialog(const std::string &label,const std
std::list<std::string> files;
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
char const * open_pattern[6] = { "*.jpg", "*.png", "*.tif" };
char const * open_pattern[3] = { "*.jpg", "*.png", "*.tif" };
#if USE_TINYFILEDIALOG
char const * open_file_names;

View File

@@ -30,13 +30,6 @@ public:
static bool busy() { return busy_; }
};
class OpenImageDialog : public FileDialog
{
public:
OpenImageDialog(const std::string &name) : FileDialog(name) {}
void open();
};
class OpenSessionDialog : public FileDialog
{
public:
@@ -44,13 +37,6 @@ public:
void open();
};
class OpenMediaDialog : public FileDialog
{
public:
OpenMediaDialog(const std::string &name) : FileDialog(name) {}
void open();
};
class SaveSessionDialog : public FileDialog
{
public:
@@ -59,6 +45,39 @@ public:
void open();
};
class MultipleSessionsDialog : public FileDialog
{
std::list<std::string> pathlist_;
std::vector< std::future< std::list<std::string> > > promisedlist_;
public:
MultipleSessionsDialog(const std::string &name) : FileDialog(name) {}
void open() override;
bool closed() override;
inline std::list<std::string> files() const { return pathlist_; }
};
class OpenPlaylistDialog : public FileDialog
{
public:
OpenPlaylistDialog(const std::string &name) : FileDialog(name) {}
void open();
};
class SavePlaylistDialog : public FileDialog
{
public:
SavePlaylistDialog(const std::string &name) : FileDialog(name) {}
void setFolder(std::string path);
void open();
};
class OpenMediaDialog : public FileDialog
{
public:
OpenMediaDialog(const std::string &name) : FileDialog(name) {}
void open();
};
class OpenFolderDialog : public FileDialog
{
public:
@@ -66,6 +85,13 @@ public:
void open();
};
class OpenImageDialog : public FileDialog
{
public:
OpenImageDialog(const std::string &name) : FileDialog(name) {}
void open();
};
class MultipleImagesDialog : public FileDialog
{
std::list<std::string> pathlist_;

View File

@@ -100,7 +100,7 @@ void ImGuiToolkit::ButtonDisabled(const char* label, const ImVec2 &size_arg)
ImGui::PopStyleColor(1);
}
bool ImGuiToolkit::ButtonSwitch(const char* label, bool* toggle, const char* shortcut)
bool ImGuiToolkit::ButtonSwitch(const char* label, bool* toggle, const char* tooltip, bool rightalign)
{
bool ret = false;
@@ -119,7 +119,7 @@ bool ImGuiToolkit::ButtonSwitch(const char* label, bool* toggle, const char* sho
float radius = height * 0.50f;
// toggle action : operate on the whole area
ImGui::InvisibleButton(label, ImVec2(frame_width - frame_height, frame_height));
ImGui::InvisibleButton(label, ImVec2(frame_width, frame_height));
if (ImGui::IsItemClicked()) {
*toggle = !*toggle;
ret = true;
@@ -143,19 +143,19 @@ bool ImGuiToolkit::ButtonSwitch(const char* label, bool* toggle, const char* sho
col_bg = ImGui::GetColorU32(ImLerp(colors[ImGuiCol_FrameBg], colors[ImGuiCol_TabActive], t));
// draw help text if present
if (shortcut) {
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.6, 0.6, 0.6, 0.9f));
ImGui::RenderText(draw_pos, shortcut);
ImGui::PopStyleColor(1);
}
if (tooltip != nullptr && ImGui::IsItemHovered())
ImGuiToolkit::ToolTip(tooltip);
// right alignment
float alignment = rightalign ? width + g.Style.FramePadding.x : 3.5f * ImGui::GetTextLineHeightWithSpacing();
// draw the label right aligned
const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true);
ImVec2 text_pos = draw_pos + ImVec2(frame_width -3.8f * ImGui::GetTextLineHeightWithSpacing() -label_size.x, 0.f);
ImVec2 text_pos = draw_pos + ImVec2(frame_width -alignment - g.Style.ItemSpacing.x -label_size.x, 0.f);
ImGui::RenderText(text_pos, label);
// draw switch after the text
ImVec2 p = draw_pos + ImVec2(frame_width -3.5f * ImGui::GetTextLineHeightWithSpacing(), 0.f);
ImVec2 p = draw_pos + ImVec2(frame_width -alignment, 0.f);
draw_list->AddRectFilled(p, ImVec2(p.x + width, p.y + height), col_bg, height * 0.5f);
draw_list->AddCircleFilled(ImVec2(p.x + radius + t * (width - radius * 2.0f), p.y + radius), radius - 1.5f, IM_COL32(255, 255, 255, 250));

View File

@@ -34,7 +34,7 @@ namespace ImGuiToolkit
// buttons
bool ButtonToggle (const char* label, bool* toggle, const char *tooltip = nullptr);
bool ButtonSwitch (const char* label, bool* toggle, const char *shortcut = nullptr);
bool ButtonSwitch (const char* label, bool* toggle, const char *tooltip = nullptr, bool rightalign = false);
void ButtonOpenUrl (const char* label, const char* url, const ImVec2& size_arg = ImVec2(0,0));
void ButtonDisabled(const char* label, const ImVec2& size_arg = ImVec2(0,0));
bool TextButton (const char* text, const char *tooltip = nullptr, const char *shortcut = nullptr);

View File

@@ -526,8 +526,8 @@ void ImGuiVisitor::visit (Source& s)
ImGuiToolkit::Indication(workspaces_[s.workspace()].second.c_str(), workspaces_[s.workspace()].first, 16);
// locking
ImGui::SetCursorPos( ImVec2(preview_width + 20, pos.y + 3.f * ImGui::GetFrameHeightWithSpacing()) );
const char *tooltip[2] = {"Unlocked", "Locked"};
ImGui::SetCursorPos( ImVec2(preview_width + 20, pos.y + preview_height - ImGui::GetFrameHeightWithSpacing()) );
static const char *tooltip[2] = {"Unlocked", "Locked"};
bool l = s.locked();
if (ImGuiToolkit::IconToggle(15,6,17,6, &l, tooltip ) ) {
s.setLocked(l);

162
src/Playlist.cpp Normal file
View File

@@ -0,0 +1,162 @@
/*
* This file is part of vimix - video live mixer
*
* **Copyright** (C) 2019-2023 Bruno Herbelin <bruno.herbelin@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
**/
#include <algorithm>
#include <tinyxml2.h>
#include "tinyxml2Toolkit.h"
using namespace tinyxml2;
#include "Playlist.h"
Playlist::Playlist() : current_index_(UINTMAX_MAX)
{
}
Playlist& Playlist::operator = (const Playlist& b)
{
if (this != &b) {
path_.clear();
for(auto it = b.path_.cbegin(); it != b.path_.cend(); ++it)
path_.push_back(*it);
}
return *this;
}
void Playlist::clear()
{
path_.clear();
}
void Playlist::load(const std::string &filename)
{
// try to load playlist file
XMLDocument xmlDoc;
XMLError eResult = xmlDoc.LoadFile(filename.c_str());
// do not warn if non existing file
if (eResult == XML_ERROR_FILE_NOT_FOUND)
return;
// warn and return on other error
else if (XMLResultError(eResult))
return;
// first element should be called by the application name
XMLElement *pRoot = xmlDoc.FirstChildElement("vimixplaylist");
if (pRoot == nullptr)
return;
// all good, can clear previous list
filename_ = filename;
path_.clear();
// Then it should contain a list of path
XMLElement* pathNode = pRoot->FirstChildElement("path");
for( ; pathNode ; pathNode = pathNode->NextSiblingElement()) {
const char *p = pathNode->GetText();
if (p)
add( std::string(p) );
}
}
bool Playlist::save()
{
if ( filename_.empty() )
return false;
saveAs(filename_);
return true;
}
void Playlist::saveAs(const std::string &filename)
{
XMLDocument xmlDoc;
XMLDeclaration *pDec = xmlDoc.NewDeclaration();
xmlDoc.InsertFirstChild(pDec);
XMLElement *pRoot = xmlDoc.NewElement("vimixplaylist");
xmlDoc.InsertEndChild(pRoot);
for(auto it = path_.cbegin(); it != path_.cend(); ++it) {
XMLElement *pathNode = xmlDoc.NewElement("path");
XMLText *text = xmlDoc.NewText( it->c_str() );
pathNode->InsertEndChild( text );
pRoot->InsertEndChild(pathNode);
}
XMLError eResult = xmlDoc.SaveFile(filename.c_str());
if ( !XMLResultError(eResult))
filename_ = filename;
}
bool Playlist::add(const std::string &path)
{
if (has(path))
return false;
path_.push_back(path);
return true;
}
size_t Playlist::add(const std::list<std::string> &list)
{
size_t before = path_.size();
for (auto it = list.begin(); it != list.end(); ++it) {
if (!has( *it ))
path_.push_back( *it );
}
return path_.size()-before;
}
void Playlist::remove(const std::string &path)
{
std::deque<std::string>::const_iterator it = std::find(path_.begin(), path_.end(), path);
if (it != path_.end())
path_.erase(it);
}
bool Playlist::has(const std::string &path) const
{
std::deque<std::string>::const_iterator it = std::find(path_.begin(), path_.end(), path);
return (it != path_.end());
}
void Playlist::remove(size_t index)
{
if ( index < path_.size() ) {
path_.erase (path_.begin()+index);
}
}
void Playlist::move(size_t from_index, size_t to_index)
{
if ( from_index < path_.size() && to_index < path_.size() && from_index != to_index ) {
if ( from_index < to_index ) {
path_.insert(path_.begin() + to_index + 1, path_[from_index]);
path_.erase(path_.begin() + from_index);
}
else {
path_.insert(path_.begin() + to_index, path_[from_index]);
path_.erase(path_.begin() + from_index + 1);
}
}
}

40
src/Playlist.h Normal file
View File

@@ -0,0 +1,40 @@
#ifndef PLAYLIST_H
#define PLAYLIST_H
#include <string>
#include <list>
#include <deque>
class Playlist
{
public:
Playlist();
Playlist& operator = (const Playlist& b);
// load / save XML
void clear ();
void load (const std::string &filename);
void saveAs (const std::string &filename);
bool save ();
// add / remove / test by path
bool add (const std::string &path);
size_t add (const std::list<std::string> &list);
void remove (const std::string &path);
bool has (const std::string &path) const;
// access by index, from 0 to size()
inline size_t size () const { return path_.size(); }
inline std::string at (size_t index) const { return path_.at(index); }
void remove (size_t index);
void move (size_t from_index, size_t to_index);
inline size_t current() const { return current_index_; }
private:
std::deque< std::string > path_;
std::string filename_;
size_t current_index_;
};
#endif // PLAYLIST_H

View File

@@ -41,6 +41,7 @@ XMLElement *save_history(Settings::History &h, const char *nodename, XMLDocument
pElement->SetAttribute("autoload", h.load_at_start);
pElement->SetAttribute("autosave", h.save_on_exit);
pElement->SetAttribute("valid", h.front_is_valid);
pElement->SetAttribute("ordering", h.ordering);
for(auto it = h.filenames.cbegin();
it != h.filenames.cend(); ++it) {
XMLElement *fileNode = xmlDoc.NewElement("path");
@@ -135,6 +136,8 @@ void Settings::Save(uint64_t runtime)
applicationNode->SetAttribute("action_history_follow_view", application.action_history_follow_view);
applicationNode->SetAttribute("show_tooptips", application.show_tooptips);
applicationNode->SetAttribute("accept_connections", application.accept_connections);
applicationNode->SetAttribute("pannel_main_mode", application.pannel_main_mode);
applicationNode->SetAttribute("pannel_playlist_mode", application.pannel_playlist_mode);
applicationNode->SetAttribute("pannel_history_mode", application.pannel_current_session_mode);
applicationNode->SetAttribute("pannel_always_visible", application.pannel_always_visible);
applicationNode->SetAttribute("stream_protocol", application.stream_protocol);
@@ -261,6 +264,9 @@ void Settings::Save(uint64_t runtime)
// recent session filenames
recent->InsertEndChild( save_history(application.recentSessions, "Session", xmlDoc));
// recent session folders
recent->InsertEndChild( save_history(application.recentPlaylists, "Playlist", xmlDoc));
// recent session folders
recent->InsertEndChild( save_history(application.recentFolders, "Folder", xmlDoc));
@@ -353,6 +359,9 @@ void load_history(Settings::History &h, const char *nodename, XMLElement *root)
pElement->QueryBoolAttribute("autoload", &h.load_at_start);
pElement->QueryBoolAttribute("autosave", &h.save_on_exit);
pElement->QueryBoolAttribute("valid", &h.front_is_valid);
pElement->QueryIntAttribute("ordering", &h.ordering);
h.changed = true;
}
}
@@ -403,11 +412,11 @@ void Settings::Load()
else if (XMLResultError(eResult))
return;
// first element should be called by the application name
XMLElement *pRoot = xmlDoc.FirstChildElement(application.name.c_str());
if (pRoot == nullptr)
return;
// runtime
pRoot->QueryUnsigned64Attribute("runtime", &application.total_runtime);
@@ -422,6 +431,8 @@ void Settings::Load()
applicationNode->QueryBoolAttribute("show_tooptips", &application.show_tooptips);
applicationNode->QueryBoolAttribute("accept_connections", &application.accept_connections);
applicationNode->QueryBoolAttribute("pannel_always_visible", &application.pannel_always_visible);
applicationNode->QueryIntAttribute("pannel_main_mode", &application.pannel_main_mode);
applicationNode->QueryIntAttribute("pannel_playlist_mode", &application.pannel_playlist_mode);
applicationNode->QueryIntAttribute("pannel_history_mode", &application.pannel_current_session_mode);
applicationNode->QueryIntAttribute("stream_protocol", &application.stream_protocol);
applicationNode->QueryIntAttribute("broadcast_port", &application.broadcast_port);
@@ -618,6 +629,9 @@ void Settings::Load()
// recent session filenames
load_history(application.recentSessions, "Session", pElement);
// recent session playlist
load_history(application.recentPlaylists, "Playlist", pElement);
// recent session folders
load_history(application.recentFolders, "Folder", pElement);
@@ -686,6 +700,12 @@ void Settings::Load()
}
void Settings::History::assign(const string &filename)
{
path.assign(filename);
changed = true;
}
void Settings::History::push(const string &filename)
{
if (filename.empty()) {
@@ -718,6 +738,9 @@ void Settings::History::validate()
else
fit = filenames.erase(fit);
}
if (!path.empty() && !SystemToolkit::file_exists( path )) {
path = "";
}
}
void Settings::KnownHosts::push(const string &ip, const string &port)

View File

@@ -129,16 +129,19 @@ struct History
bool load_at_start;
bool save_on_exit;
bool changed;
int ordering;
History() {
path = IMGUI_LABEL_RECENT_FILES;
path = "";
front_is_valid = false;
load_at_start = true;
save_on_exit = true;
changed = false;
ordering = 3;
}
void push(const std::string &filename);
void remove(const std::string &filename);
void push (const std::string &filename);
void remove (const std::string &filename);
void assign (const std::string &filename);
void validate();
};
@@ -278,6 +281,8 @@ struct Application
bool action_history_follow_view;
bool show_tooptips;
int pannel_main_mode;
int pannel_playlist_mode;
int pannel_current_session_mode;
bool pannel_always_visible;
@@ -321,12 +326,11 @@ struct Application
std::vector<WindowConfig> windows;
// recent files histories
int orderingSessions;
History recentSessions;
History recentPlaylists;
History recentFolders;
History recentImport;
History recentImportFolders;
int orderingImportFolder;
History recentRecordings;
std::map< std::string, std::string > dialogRecentFolder;
@@ -355,6 +359,8 @@ struct Application
loopback_camera = 0;
shm_method = 0;
shm_socket_path = "";
pannel_main_mode = 0;
pannel_playlist_mode = 0;
pannel_current_session_mode = 0;
current_view = 1;
current_workspace= 3;
@@ -363,8 +369,6 @@ struct Application
windows = std::vector<WindowConfig>(1+MAX_OUTPUT_WINDOW);
windows[0].w = 1600;
windows[0].h = 900;
orderingSessions = 3;
orderingImportFolder = 3;
}
};

View File

@@ -912,7 +912,7 @@ void SourceControlWindow::RenderSelection(size_t i)
ImGui::SameLine();
ImGui::SetCursorPosX(rendersize.x - buttons_height_ / 1.3f);
if (ImGui::Button(ICON_FA_MINUS_CIRCLE)) {
if (ImGui::Button(ICON_FA_TIMES_CIRCLE)) {
resetActiveSelection();
Mixer::manager().session()->deleteBatch(i);
}

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,7 @@
#include "TimerMetronomeWindow.h"
#include "InputMappingWindow.h"
#include "ShaderEditWindow.h"
#include "Playlist.h"
struct ImVec2;
@@ -48,8 +49,8 @@ class Navigator
float padding_width_;
// behavior pannel
bool show_config_;
bool pannel_visible_;
int pannel_main_mode_;
float pannel_alpha_;
bool view_pannel_visible;
bool selected_button[NAV_COUNT];
@@ -63,12 +64,13 @@ class Navigator
void applyButtonSelection(int index);
// side pannels
void RenderSourcePannel(Source *s);
void RenderMainPannel();
void RenderMainPannelVimix();
void RenderSourcePannel(Source *s, const ImVec2 &iconsize);
void RenderMainPannel(const ImVec2 &iconsize);
void RenderMainPannelSession();
void RenderMainPannelPlaylist();
void RenderMainPannelSettings();
void RenderTransitionPannel();
void RenderNewPannel();
void RenderTransitionPannel(const ImVec2 &iconsize);
void RenderNewPannel(const ImVec2 &iconsize);
void RenderViewOptions(uint *timeout, const ImVec2 &pos, const ImVec2 &size);
void RenderMousePointerSelector(const ImVec2 &size);
@@ -198,6 +200,10 @@ protected:
DialogToolkit::OpenSessionDialog *sessionimportdialog;
DialogToolkit::SaveSessionDialog *sessionsavedialog;
// Favorites and playlists
Playlist favorites;
std::string playlists_path;
// objects and windows
Navigator navigator;
ToolBox toolbox;
@@ -209,6 +215,7 @@ protected:
void showMenuFile();
void showMenuEdit();
void showMenuWindows();
bool saveOrSaveAs(bool force_versioning = false);
void selectSaveFilename();
void selectOpenFilename();

View File

@@ -15,6 +15,8 @@
#define VIMIX_FILE_EXT "mix"
#define VIMIX_FILE_PATTERN "*.mix"
#define VIMIX_PLAYLIST_FILE_EXT "lix"
#define VIMIX_PLAYLIST_FILE_PATTERN "*.lix"
#define MEDIA_FILES_PATTERN "*.mix", "*.mp4", "*.mpg", "*.mpeg", "*.m2v", "*.m4v", "*.avi", "*.mov",\
"*.mkv", "*.webm", "*.mod", "*.wmv", "*.mxf", "*.ogg",\
"*.flv", "*.hevc", "*.asf", "*.jpg", "*.png", "*.gif",\
@@ -140,7 +142,7 @@
#define ALT_LOCK " ALT LOCK"
#endif
#define MENU_NEW_FILE ICON_FA_FILE " New"
#define MENU_NEW_FILE ICON_FA_FILE "+ New"
#define SHORTCUT_NEW_FILE CTRL_MOD "W"
#define MENU_OPEN_FILE ICON_FA_FILE_UPLOAD " Open"
#define SHORTCUT_OPEN_FILE CTRL_MOD "O"
@@ -186,19 +188,22 @@
#define MENU_CLOSE ICON_FA_TIMES " Close"
#define DIALOG_FAILED_SOURCE ICON_FA_EXCLAMATION_TRIANGLE " Source failure"
#define MENU_NOTE ICON_FA_STICKY_NOTE " Add sticky note"
#define MENU_METRICS ICON_FA_TACHOMETER_ALT " Metrics"
#define MENU_SOURCE_TOOL ICON_FA_WRENCH " Source toolbar"
#define MENU_HELP ICON_FA_LIFE_RING " Help"
#define SHORTCUT_HELP CTRL_MOD "H"
#define MENU_LOGS ICON_FA_LIST_UL " Logs"
#define SHORTCUT_LOGS CTRL_MOD "L"
#define MENU_PLAYER ICON_FA_PLAY_CIRCLE " Player "
#define TOOLTIP_PLAYER "Player "
#define SHORTCUT_PLAYER CTRL_MOD "P"
#define MENU_OUTPUT ICON_FA_DESKTOP " Display "
#define TOOLTIP_OUTPUT "Display "
#define SHORTCUT_OUTPUT CTRL_MOD "D"
#define MENU_TIMER ICON_FA_CLOCK " Timer "
#define TOOLTIP_TIMER "Timer "
#define SHORTCUT_TIMER CTRL_MOD "T"
#define MENU_INPUTS ICON_FA_HAND_PAPER " Inputs mapping "
#define TOOLTIP_INPUTS "Inputs mapping "
#define SHORTCUT_INPUTS CTRL_MOD "I"
#define TOOLTIP_SHADEREDITOR "Shader Editor "