mirror of
https://github.com/brunoherbelin/vimix.git
synced 2026-06-16 04:27:23 +02:00
Create new Source type MultiFile
MultiFileSource plays a sequence of numbered images.
This commit is contained in:
+127
@@ -6,6 +6,7 @@
|
||||
#include <list>
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
|
||||
#include <locale>
|
||||
#include <unicode/ustream.h>
|
||||
@@ -115,3 +116,129 @@ std::string BaseToolkit::trunc_string(const std::string& path, int N)
|
||||
}
|
||||
return trunc;
|
||||
}
|
||||
|
||||
|
||||
std::string BaseToolkit::common_prefix( const std::list<std::string> & allStrings )
|
||||
{
|
||||
if (allStrings.empty())
|
||||
return std::string();
|
||||
|
||||
const std::string &s0 = allStrings.front();
|
||||
auto _end = s0.cend();
|
||||
for (auto it=std::next(allStrings.cbegin()); it != allStrings.cend(); ++it)
|
||||
{
|
||||
auto _loc = std::mismatch(s0.cbegin(), s0.cend(), it->cbegin(), it->cend());
|
||||
if (std::distance(_loc.first, _end) > 0)
|
||||
_end = _loc.first;
|
||||
}
|
||||
|
||||
return std::string(s0.cbegin(), _end);
|
||||
}
|
||||
|
||||
|
||||
std::string BaseToolkit::common_suffix(const std::list<std::string> & allStrings)
|
||||
{
|
||||
if (allStrings.empty())
|
||||
return std::string();
|
||||
|
||||
const std::string &s0 = allStrings.front();
|
||||
auto r_end = s0.crend();
|
||||
for (auto it=std::next(allStrings.cbegin()); it != allStrings.cend(); ++it)
|
||||
{
|
||||
auto r_loc = std::mismatch(s0.crbegin(), s0.crend(), it->crbegin(), it->crend());
|
||||
if (std::distance(r_loc.first, r_end) > 0)
|
||||
r_end = r_loc.first;
|
||||
}
|
||||
|
||||
std::string suffix = std::string(s0.crbegin(), r_end);
|
||||
std::reverse(suffix.begin(), suffix.end());
|
||||
return suffix;
|
||||
}
|
||||
|
||||
|
||||
std::string BaseToolkit::common_pattern(const std::list<std::string> &allStrings)
|
||||
{
|
||||
if (allStrings.empty())
|
||||
return std::string();
|
||||
|
||||
// find common prefix and suffix
|
||||
const std::string &s0 = allStrings.front();
|
||||
auto _end = s0.cend();
|
||||
auto r_end = s0.crend();
|
||||
for (auto it=std::next(allStrings.cbegin()); it != allStrings.cend(); ++it)
|
||||
{
|
||||
auto _loc = std::mismatch(s0.cbegin(), s0.cend(), it->cbegin(), it->cend());
|
||||
if (std::distance(_loc.first, _end) > 0)
|
||||
_end = _loc.first;
|
||||
|
||||
auto r_loc = std::mismatch(s0.crbegin(), s0.crend(), it->crbegin(), it->crend());
|
||||
if (std::distance(r_loc.first, r_end) > 0)
|
||||
r_end = r_loc.first;
|
||||
}
|
||||
|
||||
std::string suffix = std::string(s0.crbegin(), r_end);
|
||||
std::reverse(suffix.begin(), suffix.end());
|
||||
|
||||
return std::string(s0.cbegin(), _end) + "*" + suffix;
|
||||
}
|
||||
|
||||
std::string BaseToolkit::common_numbered_pattern(const std::list<std::string> &allStrings, int *min, int *max)
|
||||
{
|
||||
if (allStrings.empty())
|
||||
return std::string();
|
||||
|
||||
// find common prefix and suffix
|
||||
const std::string &s0 = allStrings.front();
|
||||
auto _end = s0.cend();
|
||||
auto r_end = s0.crend();
|
||||
for (auto it=std::next(allStrings.cbegin()); it != allStrings.cend(); ++it)
|
||||
{
|
||||
auto _loc = std::mismatch(s0.cbegin(), s0.cend(), it->cbegin(), it->cend());
|
||||
if (std::distance(_loc.first, _end) > 0)
|
||||
_end = _loc.first;
|
||||
|
||||
auto r_loc = std::mismatch(s0.crbegin(), s0.crend(), it->crbegin(), it->crend());
|
||||
if (std::distance(r_loc.first, r_end) > 0)
|
||||
r_end = r_loc.first;
|
||||
}
|
||||
|
||||
// range of middle string, after prefix and before suffix
|
||||
size_t pos_prefix = std::distance(s0.cbegin(), _end);
|
||||
size_t pos_suffix = s0.size() - pos_prefix - std::distance(s0.crbegin(), r_end);
|
||||
|
||||
int n = -1;
|
||||
*max = 0;
|
||||
*min = INT_MAX;
|
||||
// loop over all strings to verify there are numbers between prefix and suffix
|
||||
for (auto it = allStrings.cbegin(); it != allStrings.cend(); ++it)
|
||||
{
|
||||
// get middle string, after prefix and before suffix
|
||||
std::string s = it->substr(pos_prefix, pos_suffix);
|
||||
// is this central string ONLY made of digits?
|
||||
if (s.end() == std::find_if(s.begin(), s.end(), [](unsigned char c)->bool { return !isdigit(c); })) {
|
||||
// yes, validate
|
||||
*max = std::max(*max, std::atoi(s.c_str()) );
|
||||
*min = std::min(*min, std::atoi(s.c_str()) );
|
||||
if (n < 0)
|
||||
n = s.size();
|
||||
else if ( n != s.size() ) {
|
||||
n = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
n = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( n < 1 )
|
||||
return std::string();
|
||||
|
||||
std::string suffix = std::string(s0.crbegin(), r_end);
|
||||
std::reverse(suffix.begin(), suffix.end());
|
||||
std::string pattern = std::string(s0.cbegin(), _end);
|
||||
pattern += "%0" + std::to_string(n) + "d";
|
||||
pattern += suffix;
|
||||
return pattern;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,16 @@ std::string bits_to_string(long b);
|
||||
// Truncate a string to display the right most N characters (e.g. ./home/me/toto.mpg -> ...ome/me/toto.mpg)
|
||||
std::string trunc_string(const std::string& path, int N);
|
||||
|
||||
// find common parts in a list of strings
|
||||
std::string common_prefix(const std::list<std::string> &allStrings);
|
||||
std::string common_suffix(const std::list<std::string> &allStrings);
|
||||
|
||||
// form a pattern "prefix*suffix" (e.g. file list)
|
||||
std::string common_pattern(const std::list<std::string> &allStrings);
|
||||
|
||||
// form a pattern "prefix%03dsuffix" (e.g. numbered file list)
|
||||
std::string common_numbered_pattern(const std::list<std::string> &allStrings, int *min, int *max);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -309,6 +309,7 @@ set(VMIX_SRCS
|
||||
PatternSource.cpp
|
||||
DeviceSource.cpp
|
||||
NetworkSource.cpp
|
||||
MultiFileSource.cpp
|
||||
FrameBuffer.cpp
|
||||
RenderingManager.cpp
|
||||
UserInterfaceManager.cpp
|
||||
@@ -326,6 +327,7 @@ set(VMIX_SRCS
|
||||
NetworkToolkit.cpp
|
||||
Connection.cpp
|
||||
ActionManager.cpp
|
||||
Overlay.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -428,6 +430,7 @@ set(VMIX_RSC_FILES
|
||||
./rsc/mesh/icon_eye_slash.ply
|
||||
./rsc/mesh/icon_vector_square_slash.ply
|
||||
./rsc/mesh/icon_cube.ply
|
||||
./rsc/mesh/icon_sequence.ply
|
||||
./rsc/mesh/h_line.ply
|
||||
./rsc/mesh/h_mark.ply
|
||||
)
|
||||
|
||||
@@ -404,6 +404,8 @@ Symbol::Symbol(Type t, glm::vec3 pos) : Node(), type_(t)
|
||||
shadows[SQUARE_POINT] = nullptr;
|
||||
icons[IMAGE] = new Mesh("mesh/icon_image.ply");
|
||||
shadows[IMAGE] = shadow;
|
||||
icons[SEQUENCE] = new Mesh("mesh/icon_sequence.ply");
|
||||
shadows[SEQUENCE]= shadow;
|
||||
icons[VIDEO] = new Mesh("mesh/icon_video.ply");
|
||||
shadows[VIDEO] = shadow;
|
||||
icons[SESSION] = new Mesh("mesh/icon_vimix.ply");
|
||||
|
||||
+2
-2
@@ -60,9 +60,9 @@ protected:
|
||||
class Symbol : public Node
|
||||
{
|
||||
public:
|
||||
typedef enum { CIRCLE_POINT = 0, SQUARE_POINT, IMAGE, VIDEO, SESSION, CLONE, RENDER, GROUP, PATTERN, CAMERA, CUBE, SHARE,
|
||||
typedef enum { CIRCLE_POINT = 0, SQUARE_POINT, IMAGE, SEQUENCE, VIDEO, SESSION, CLONE, RENDER, GROUP, PATTERN, CAMERA, CUBE, SHARE,
|
||||
DOTS, BUSY, LOCK, UNLOCK, EYE, EYESLASH, VECTORSLASH, ARROWS, ROTATION, CROP, CIRCLE, SQUARE, CLOCK, CLOCK_H, GRID, CROSS, EMPTY } Type;
|
||||
Symbol(Type t = CIRCLE_POINT, glm::vec3 pos = glm::vec3(0.f));
|
||||
Symbol(Type t, glm::vec3 pos = glm::vec3(0.f));
|
||||
~Symbol();
|
||||
|
||||
void draw (glm::mat4 modelview, glm::mat4 projection) override;
|
||||
|
||||
+71
-4
@@ -178,7 +178,7 @@ std::string DialogToolkit::openSessionFileDialog(const std::string &path)
|
||||
}
|
||||
|
||||
|
||||
std::string DialogToolkit::ImportFileDialog(const std::string &path)
|
||||
std::string DialogToolkit::openMediaFileDialog(const std::string &path)
|
||||
{
|
||||
std::string filename = "";
|
||||
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
|
||||
@@ -190,7 +190,7 @@ std::string DialogToolkit::ImportFileDialog(const std::string &path)
|
||||
"*.gif", "*.tif", "*.svg" };
|
||||
#if USE_TINYFILEDIALOG
|
||||
char const * open_file_name;
|
||||
open_file_name = tinyfd_openFileDialog( "Import a file", startpath.c_str(), 18, open_pattern, "All supported formats", 0);
|
||||
open_file_name = tinyfd_openFileDialog( "Open Media File", startpath.c_str(), 18, open_pattern, "All supported formats", 0);
|
||||
|
||||
if (open_file_name)
|
||||
filename = std::string(open_file_name);
|
||||
@@ -201,7 +201,7 @@ std::string DialogToolkit::ImportFileDialog(const std::string &path)
|
||||
return filename;
|
||||
}
|
||||
|
||||
GtkWidget *dialog = gtk_file_chooser_dialog_new( "Import Media File", NULL,
|
||||
GtkWidget *dialog = gtk_file_chooser_dialog_new( "Open Media File", NULL,
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
"_Cancel", GTK_RESPONSE_CANCEL,
|
||||
"_Open", GTK_RESPONSE_ACCEPT, NULL );
|
||||
@@ -238,7 +238,7 @@ std::string DialogToolkit::ImportFileDialog(const std::string &path)
|
||||
return filename;
|
||||
}
|
||||
|
||||
std::string DialogToolkit::FolderDialog(const std::string &path)
|
||||
std::string DialogToolkit::openFolderDialog(const std::string &path)
|
||||
{
|
||||
std::string foldername = "";
|
||||
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
|
||||
@@ -290,6 +290,73 @@ std::string DialogToolkit::FolderDialog(const std::string &path)
|
||||
}
|
||||
|
||||
|
||||
std::list<std::string> DialogToolkit::selectImagesFileDialog(const std::string &path)
|
||||
{
|
||||
std::list<std::string> files;
|
||||
|
||||
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
|
||||
char const * open_pattern[3] = { "*.tif", "*.jpg", "*.png" };
|
||||
|
||||
#if USE_TINYFILEDIALOG
|
||||
char const * open_file_names;
|
||||
open_file_names = tinyfd_openFileDialog( "Select images", startpath.c_str(), 3, open_pattern, "Images", 1);
|
||||
|
||||
if (open_file_names)
|
||||
// filename = std::string(open_file_name);
|
||||
// TODO
|
||||
#else
|
||||
|
||||
if (!gtk_init()) {
|
||||
ErrorDialog("Could not initialize GTK+ for dialog");
|
||||
return files;
|
||||
}
|
||||
|
||||
GtkWidget *dialog = gtk_file_chooser_dialog_new( "Select images", NULL,
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
"_Cancel", GTK_RESPONSE_CANCEL,
|
||||
"_Open", GTK_RESPONSE_ACCEPT, NULL );
|
||||
|
||||
// set file filters
|
||||
add_filter_file_dialog(dialog, 3, open_pattern, "All supported formats");
|
||||
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_free( open_file_names->data );
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void DialogToolkit::ErrorDialog(const char* message)
|
||||
{
|
||||
#if USE_TINYFILEDIALOG
|
||||
|
||||
+5
-2
@@ -2,6 +2,7 @@
|
||||
#define DIALOGTOOLKIT_H
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
|
||||
namespace DialogToolkit
|
||||
@@ -11,9 +12,11 @@ std::string saveSessionFileDialog(const std::string &path);
|
||||
|
||||
std::string openSessionFileDialog(const std::string &path);
|
||||
|
||||
std::string ImportFileDialog(const std::string &path);
|
||||
std::string openMediaFileDialog(const std::string &path);
|
||||
|
||||
std::string FolderDialog(const std::string &path);
|
||||
std::string openFolderDialog(const std::string &path);
|
||||
|
||||
std::list<std::string> selectImagesFileDialog(const std::string &path);
|
||||
|
||||
void ErrorDialog(const char* message);
|
||||
|
||||
|
||||
+1
-1
@@ -516,7 +516,7 @@ std::pair<Node *, glm::vec2> GeometryView::pick(glm::vec2 P)
|
||||
|
||||
bool GeometryView::canSelect(Source *s) {
|
||||
|
||||
return ( View::canSelect(s) && s->active() && s->workspace() == Settings::application.current_workspace);
|
||||
return ( View::canSelect(s) && s->ready() && s->active() && s->workspace() == Settings::application.current_workspace);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+46
-1
@@ -8,6 +8,7 @@
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/gtc/constants.hpp>
|
||||
#include <glm/gtc/matrix_access.hpp>
|
||||
|
||||
#include <tinyxml2.h>
|
||||
#include "tinyxml2Toolkit.h"
|
||||
@@ -24,6 +25,7 @@
|
||||
#include "PatternSource.h"
|
||||
#include "DeviceSource.h"
|
||||
#include "NetworkSource.h"
|
||||
#include "MultiFileSource.h"
|
||||
#include "SessionCreator.h"
|
||||
#include "SessionVisitor.h"
|
||||
#include "Settings.h"
|
||||
@@ -593,7 +595,6 @@ void ImGuiVisitor::visit (SessionFileSource& s)
|
||||
std::string label = BaseToolkit::trunc_string(path, 25);
|
||||
label = BaseToolkit::transliterate(label);
|
||||
ImGuiToolkit::ButtonOpenUrl( label.c_str(), path.c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Folder");
|
||||
|
||||
@@ -711,3 +712,47 @@ void ImGuiVisitor::visit (NetworkSource& s)
|
||||
|
||||
}
|
||||
|
||||
|
||||
void ImGuiVisitor::visit (MultiFileSource& s)
|
||||
{
|
||||
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::Text("Images sequence");
|
||||
|
||||
// information text
|
||||
std::ostringstream msg;
|
||||
msg << "Sequence of " << s.sequence().max - s.sequence().min << " ";
|
||||
msg << s.sequence().codec << " images";
|
||||
// msg << "named " << SystemToolkit::base_filename( s.sequence().location );
|
||||
// msg << " in range [" << s.sequence().min << " - " << s.sequence().max << "]";
|
||||
ImGui::Text("%s", msg.str().c_str());
|
||||
|
||||
// change range
|
||||
glm::ivec2 range = s.range();
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
if ( ImGui::DragIntRange2("Range", &range.x, &range.y, 1, s.sequence().min, s.sequence().max) ){
|
||||
s.setRange( range );
|
||||
}
|
||||
|
||||
// change framerate
|
||||
int _fps = s.framerate();
|
||||
static int _fps_changed = -1;
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
if ( ImGui::SliderInt("Framerate", &_fps, 1, 30, "%d fps") ) {
|
||||
_fps_changed = _fps;
|
||||
}
|
||||
// only call for setFramerate after mouse release to avoid repeating call to re-open the stream
|
||||
else if (_fps_changed > 0 && ImGui::IsMouseReleased(ImGuiMouseButton_Left)){
|
||||
s.setFramerate(_fps_changed);
|
||||
_fps_changed = -1;
|
||||
}
|
||||
|
||||
// offer to open file browser at location
|
||||
std::string path = SystemToolkit::path_filename(s.sequence().location);
|
||||
std::string label = BaseToolkit::trunc_string(path, 25);
|
||||
label = BaseToolkit::transliterate(label);
|
||||
ImGuiToolkit::ButtonOpenUrl( label.c_str(), path.c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Folder");
|
||||
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ public:
|
||||
void visit (PatternSource& s) override;
|
||||
void visit (DeviceSource& s) override;
|
||||
void visit (NetworkSource& s) override;
|
||||
void visit (MultiFileSource& s) override;
|
||||
};
|
||||
|
||||
#endif // IMGUIVISITOR_H
|
||||
|
||||
@@ -57,86 +57,90 @@ struct AppLog
|
||||
ImGui::SetNextWindowPos(ImVec2(430, 660), ImGuiCond_FirstUseEver);
|
||||
ImGui::SetNextWindowSize(ImVec2(1150, 220), ImGuiCond_FirstUseEver);
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(600, 180), ImVec2(FLT_MAX, FLT_MAX));
|
||||
if (ImGui::Begin(title, p_open))
|
||||
if ( !ImGui::Begin(title, p_open))
|
||||
{
|
||||
// window
|
||||
ImGui::SameLine(0, 0);
|
||||
static bool numbering = true;
|
||||
ImGuiToolkit::ButtonToggle( ICON_FA_SORT_NUMERIC_DOWN, &numbering );
|
||||
ImGui::SameLine();
|
||||
bool clear = ImGui::Button( ICON_FA_BACKSPACE " Clear");
|
||||
ImGui::SameLine();
|
||||
bool copy = ImGui::Button( ICON_FA_COPY " Copy");
|
||||
ImGui::SameLine();
|
||||
Filter.Draw("Filter", -60.0f);
|
||||
|
||||
ImGui::Separator();
|
||||
if ( ImGui::BeginChild("scrolling", ImVec2(0,0), false, ImGuiWindowFlags_AlwaysHorizontalScrollbar) )
|
||||
{
|
||||
if (clear)
|
||||
Clear();
|
||||
if (copy)
|
||||
ImGui::LogToClipboard();
|
||||
|
||||
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
||||
|
||||
mtx.lock();
|
||||
|
||||
const char* buf = Buf.begin();
|
||||
const char* buf_end = Buf.end();
|
||||
if (Filter.IsActive())
|
||||
{
|
||||
// In this example we don't use the clipper when Filter is enabled.
|
||||
// This is because we don't have a random access on the result on our filter.
|
||||
// A real application processing logs with ten of thousands of entries may want to store the result of search/filter.
|
||||
// especially if the filtering function is not trivial (e.g. reg-exp).
|
||||
for (int line_no = 0; line_no < LineOffsets.Size; line_no++)
|
||||
{
|
||||
const char* line_start = buf + LineOffsets[line_no];
|
||||
const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
|
||||
if (Filter.PassFilter(line_start, line_end))
|
||||
ImGui::TextUnformatted(line_start, line_end);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The simplest and easy way to display the entire buffer:
|
||||
// ImGui::TextUnformatted(buf_begin, buf_end);
|
||||
// And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward to skip non-visible lines.
|
||||
// Here we instead demonstrate using the clipper to only process lines that are within the visible area.
|
||||
// If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them on your side is recommended.
|
||||
// Using ImGuiListClipper requires A) random access into your data, and B) items all being the same height,
|
||||
// both of which we can handle since we an array pointing to the beginning of each line of text.
|
||||
// When using the filter (in the block of code above) we don't have random access into the data to display anymore, which is why we don't use the clipper.
|
||||
// Storing or skimming through the search result would make it possible (and would be recommended if you want to search through tens of thousands of entries)
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(LineOffsets.Size);
|
||||
while (clipper.Step())
|
||||
{
|
||||
for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
|
||||
{
|
||||
const char* line_start = buf + LineOffsets[line_no] + (numbering?0:6);
|
||||
const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
|
||||
ImGui::TextUnformatted(line_start, line_end);
|
||||
}
|
||||
}
|
||||
clipper.End();
|
||||
}
|
||||
|
||||
mtx.unlock();
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopFont();
|
||||
|
||||
// Auto scroll
|
||||
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
|
||||
ImGui::SetScrollHereY(1.0f);
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
// window
|
||||
ImGui::SameLine(0, 0);
|
||||
static bool numbering = true;
|
||||
ImGuiToolkit::ButtonToggle( ICON_FA_SORT_NUMERIC_DOWN, &numbering );
|
||||
ImGui::SameLine();
|
||||
bool clear = ImGui::Button( ICON_FA_BACKSPACE " Clear");
|
||||
ImGui::SameLine();
|
||||
bool copy = ImGui::Button( ICON_FA_COPY " Copy");
|
||||
ImGui::SameLine();
|
||||
Filter.Draw("Filter", -60.0f);
|
||||
|
||||
ImGui::Separator();
|
||||
if ( ImGui::BeginChild("scrolling", ImVec2(0,0), false, ImGuiWindowFlags_AlwaysHorizontalScrollbar) )
|
||||
{
|
||||
if (clear)
|
||||
Clear();
|
||||
if (copy)
|
||||
ImGui::LogToClipboard();
|
||||
|
||||
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
||||
|
||||
mtx.lock();
|
||||
|
||||
const char* buf = Buf.begin();
|
||||
const char* buf_end = Buf.end();
|
||||
if (Filter.IsActive())
|
||||
{
|
||||
// In this example we don't use the clipper when Filter is enabled.
|
||||
// This is because we don't have a random access on the result on our filter.
|
||||
// A real application processing logs with ten of thousands of entries may want to store the result of search/filter.
|
||||
// especially if the filtering function is not trivial (e.g. reg-exp).
|
||||
for (int line_no = 0; line_no < LineOffsets.Size; line_no++)
|
||||
{
|
||||
const char* line_start = buf + LineOffsets[line_no];
|
||||
const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
|
||||
if (Filter.PassFilter(line_start, line_end))
|
||||
ImGui::TextUnformatted(line_start, line_end);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The simplest and easy way to display the entire buffer:
|
||||
// ImGui::TextUnformatted(buf_begin, buf_end);
|
||||
// And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward to skip non-visible lines.
|
||||
// Here we instead demonstrate using the clipper to only process lines that are within the visible area.
|
||||
// If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them on your side is recommended.
|
||||
// Using ImGuiListClipper requires A) random access into your data, and B) items all being the same height,
|
||||
// both of which we can handle since we an array pointing to the beginning of each line of text.
|
||||
// When using the filter (in the block of code above) we don't have random access into the data to display anymore, which is why we don't use the clipper.
|
||||
// Storing or skimming through the search result would make it possible (and would be recommended if you want to search through tens of thousands of entries)
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(LineOffsets.Size);
|
||||
while (clipper.Step())
|
||||
{
|
||||
for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
|
||||
{
|
||||
const char* line_start = buf + LineOffsets[line_no] + (numbering?0:6);
|
||||
const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
|
||||
ImGui::TextUnformatted(line_start, line_end);
|
||||
}
|
||||
}
|
||||
clipper.End();
|
||||
}
|
||||
|
||||
mtx.unlock();
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopFont();
|
||||
|
||||
// Auto scroll
|
||||
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
|
||||
ImGui::SetScrollHereY(1.0f);
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+9
-6
@@ -84,7 +84,7 @@ guint MediaPlayer::texture() const
|
||||
|
||||
#define LIMIT_DISCOVERER
|
||||
|
||||
static MediaInfo UriDiscoverer_(std::string uri)
|
||||
MediaInfo MediaPlayer::UriDiscoverer(const std::string &uri)
|
||||
{
|
||||
#ifdef MEDIA_PLAYER_DEBUG
|
||||
Log::Info("Checking file '%s'", uri.c_str());
|
||||
@@ -220,24 +220,27 @@ static MediaInfo UriDiscoverer_(std::string uri)
|
||||
return video_stream_info;
|
||||
}
|
||||
|
||||
void MediaPlayer::open(string path)
|
||||
void MediaPlayer::open (const std::string & filename, const string &uri)
|
||||
{
|
||||
// set path
|
||||
filename_ = BaseToolkit::transliterate( path );
|
||||
filename_ = BaseToolkit::transliterate( filename );
|
||||
|
||||
// set uri to open
|
||||
uri_ = GstToolkit::filename_to_uri(path);
|
||||
if (uri.empty())
|
||||
uri_ = GstToolkit::filename_to_uri( filename );
|
||||
else
|
||||
uri_ = uri;
|
||||
|
||||
// close before re-openning
|
||||
if (isOpen())
|
||||
close();
|
||||
|
||||
// start URI discovering thread:
|
||||
discoverer_ = std::async( UriDiscoverer_, uri_);
|
||||
discoverer_ = std::async( MediaPlayer::UriDiscoverer, uri_);
|
||||
// wait for discoverer to finish in the future (test in update)
|
||||
|
||||
// // debug without thread
|
||||
// media_ = UriDiscoverer_(uri_);
|
||||
// media_ = MediaPlayer::UriDiscoverer(uri_);
|
||||
// if (media_.valid) {
|
||||
// timeline_.setEnd( media_.end );
|
||||
// timeline_.setStep( media_.dt );
|
||||
|
||||
+4
-2
@@ -91,8 +91,8 @@ public:
|
||||
/**
|
||||
* Open a media using gstreamer URI
|
||||
* */
|
||||
void open( std::string path);
|
||||
void reopen();
|
||||
void open ( const std::string &filename, const std::string &uri = "");
|
||||
void reopen ();
|
||||
/**
|
||||
* Get name of the media
|
||||
* */
|
||||
@@ -265,6 +265,8 @@ public:
|
||||
static std::list<MediaPlayer*>::const_iterator begin() { return registered_.cbegin(); }
|
||||
static std::list<MediaPlayer*>::const_iterator end() { return registered_.cend(); }
|
||||
|
||||
static MediaInfo UriDiscoverer(const std::string &uri);
|
||||
|
||||
private:
|
||||
|
||||
// video player description
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "MediaSource.h"
|
||||
#include "PatternSource.h"
|
||||
#include "DeviceSource.h"
|
||||
#include "MultiFileSource.h"
|
||||
#include "StreamSource.h"
|
||||
#include "NetworkSource.h"
|
||||
#include "ActionManager.h"
|
||||
@@ -245,6 +246,34 @@ Source * Mixer::createSourceFile(const std::string &path)
|
||||
return s;
|
||||
}
|
||||
|
||||
Source * Mixer::createSourceMultifile(const std::list<std::string> &list_files, uint fps)
|
||||
{
|
||||
// ready to create a source
|
||||
Source *s = nullptr;
|
||||
|
||||
if ( list_files.size() >0 ) {
|
||||
|
||||
// validate the creation of a sequence from the list
|
||||
MultiFileSequence sequence(list_files);
|
||||
|
||||
if ( sequence.valid() ) {
|
||||
|
||||
// try to create a sequence
|
||||
MultiFileSource *mfs = new MultiFileSource;
|
||||
mfs->setSequence(sequence, fps);
|
||||
s = mfs;
|
||||
|
||||
// remember in recent media
|
||||
Settings::application.recentImport.path = SystemToolkit::path_filename(list_files.front());
|
||||
|
||||
// propose a new name
|
||||
s->setName( SystemToolkit::base_filename( BaseToolkit::common_prefix(list_files) ) );
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Source * Mixer::createSourceRender()
|
||||
{
|
||||
// ready to create a source
|
||||
@@ -679,7 +708,7 @@ void Mixer::setCurrentSource(SourceList::iterator it)
|
||||
unsetCurrentSource();
|
||||
|
||||
// change current if 'it' is valid
|
||||
if ( it != session_->end() ) {
|
||||
if ( it != session_->end() /*&& (*it)->mode() > Source::UNINITIALIZED */) {
|
||||
current_source_ = it;
|
||||
current_source_index_ = session_->index(current_source_);
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ public:
|
||||
|
||||
// creation of sources
|
||||
Source * createSourceFile (const std::string &path);
|
||||
Source * createSourceMultifile(const std::list<std::string> &list_files, uint fps);
|
||||
Source * createSourceClone (const std::string &namesource = "");
|
||||
Source * createSourceRender ();
|
||||
Source * createSourceStream (const std::string &gstreamerpipeline);
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "defines.h"
|
||||
#include "ImageShader.h"
|
||||
#include "Resource.h"
|
||||
#include "Decorations.h"
|
||||
#include "Stream.h"
|
||||
#include "Visitor.h"
|
||||
#include "GstToolkit.h"
|
||||
#include "BaseToolkit.h"
|
||||
#include "SystemToolkit.h"
|
||||
#include "MediaPlayer.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include "MultiFileSource.h"
|
||||
|
||||
// example test gstreamer pipelines
|
||||
//
|
||||
// multifile : sequence of numbered images
|
||||
// gst-launch-1.0 multifilesrc location="/home/bhbn/Images/sequence/frames%03d.png" caps="image/png,framerate=\(fraction\)12/1" loop=1 ! decodebin ! videoconvert ! autovideosink
|
||||
//
|
||||
// imagesequencesrc : sequence of numbered images (cannot loop)
|
||||
// gst-launch-1.0 imagesequencesrc location=frames%03d.png start-index=1 framerate=24/1 ! decodebin ! videoconvert ! autovideosink
|
||||
//
|
||||
// splitfilesrc
|
||||
// gst-launch-1.0 splitfilesrc location="/home/bhbn/Videos/MOV01*.MOD" ! decodebin ! videoconvert ! autovideosink
|
||||
|
||||
MultiFileSequence::MultiFileSequence() : width(0), height(0), min(0), max(0), loop(1)
|
||||
{
|
||||
}
|
||||
|
||||
MultiFileSequence::MultiFileSequence(const std::list<std::string> &list_files) : loop(1)
|
||||
{
|
||||
location = BaseToolkit::common_numbered_pattern(list_files, &min, &max);
|
||||
|
||||
// sanity check: the location pattern looks like a filename and seems consecutive numbered
|
||||
if ( SystemToolkit::extension_filename(location).empty() ||
|
||||
SystemToolkit::path_filename(location) != SystemToolkit::path_filename(list_files.front()) ||
|
||||
list_files.size() != max - min + 1 ) {
|
||||
location.clear();
|
||||
}
|
||||
|
||||
if ( !location.empty() ) {
|
||||
MediaInfo media = MediaPlayer::UriDiscoverer( GstToolkit::filename_to_uri( list_files.front() ) );
|
||||
if (media.valid && media.isimage) {
|
||||
codec.resize(media.codec_name.size());
|
||||
std::transform(media.codec_name.begin(), media.codec_name.end(), codec.begin(), ::tolower);
|
||||
width = media.width;
|
||||
height = media.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MultiFileSequence::valid() const
|
||||
{
|
||||
return !( location.empty() || codec.empty() || width < 1 || height < 1 || max == min);
|
||||
}
|
||||
|
||||
inline MultiFileSequence& MultiFileSequence::operator = (const MultiFileSequence& b)
|
||||
{
|
||||
if (this != &b) {
|
||||
this->width = b.width;
|
||||
this->height = b.height;
|
||||
this->min = b.min;
|
||||
this->max = b.max;
|
||||
this->loop = b.loop;
|
||||
this->location = b.location;
|
||||
this->codec = b.codec;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
bool MultiFileSequence::operator != (const MultiFileSequence& b)
|
||||
{
|
||||
return ( location != b.location || codec != b.codec || width != b.width ||
|
||||
height != b.height || min != b.min || max != b.max || loop != b.loop );
|
||||
}
|
||||
|
||||
MultiFile::MultiFile() : Stream(), src_(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MultiFile::open(const MultiFileSequence &sequence, uint framerate )
|
||||
{
|
||||
if (sequence.location.empty())
|
||||
return;
|
||||
|
||||
std::ostringstream gstreamer_pipeline;
|
||||
gstreamer_pipeline << "multifilesrc name=src location=\"";
|
||||
gstreamer_pipeline << sequence.location;
|
||||
gstreamer_pipeline << "\" caps=\"image/";
|
||||
gstreamer_pipeline << sequence.codec;
|
||||
gstreamer_pipeline << ",framerate=(fraction)";
|
||||
gstreamer_pipeline << framerate;
|
||||
gstreamer_pipeline << "/1\" loop=";
|
||||
gstreamer_pipeline << sequence.loop;
|
||||
gstreamer_pipeline << " start-index=";
|
||||
gstreamer_pipeline << sequence.min;
|
||||
gstreamer_pipeline << " stop-index=";
|
||||
gstreamer_pipeline << sequence.max;
|
||||
gstreamer_pipeline << " ! decodebin ! videoconvert";
|
||||
|
||||
// (private) open stream
|
||||
Stream::open(gstreamer_pipeline.str(), sequence.width, sequence.height);
|
||||
|
||||
src_ = gst_bin_get_by_name (GST_BIN (pipeline_), "src");
|
||||
|
||||
}
|
||||
|
||||
void MultiFile::setLoop (int on)
|
||||
{
|
||||
if (src_) {
|
||||
g_object_set (src_, "loop", on, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiFile::setRange (int begin, int end)
|
||||
{
|
||||
if (src_) {
|
||||
g_object_set (src_, "start-index", MAX(begin, 0), NULL);
|
||||
g_object_set (src_, "stop-index", MAX(end, 0), NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
MultiFileSource::MultiFileSource(uint64_t id) : StreamSource(id), framerate_(0), range_(glm::ivec2(0))
|
||||
{
|
||||
// create stream
|
||||
stream_ = static_cast<Stream *>( new MultiFile );
|
||||
|
||||
// set symbol
|
||||
symbol_ = new Symbol(Symbol::SEQUENCE, glm::vec3(0.75f, 0.75f, 0.01f));
|
||||
symbol_->scale_.y = 1.5f;
|
||||
}
|
||||
|
||||
|
||||
void MultiFileSource::setFiles (const std::list<std::string> &list_files, uint framerate)
|
||||
{
|
||||
setSequence(MultiFileSequence(list_files), framerate);
|
||||
}
|
||||
|
||||
void MultiFileSource::setSequence (const MultiFileSequence &sequence, uint framerate)
|
||||
{
|
||||
framerate_ = CLAMP( framerate, 1, 30);
|
||||
sequence_ = sequence;
|
||||
|
||||
if (sequence_.valid())
|
||||
{
|
||||
// open gstreamer
|
||||
multifile()->open( sequence_, framerate_ );
|
||||
stream_->play(true);
|
||||
|
||||
// init range
|
||||
range_ = glm::ivec2(sequence_.min, sequence_.max);
|
||||
|
||||
// will be ready after init and one frame rendered
|
||||
ready_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void MultiFileSource::setFramerate (uint framerate)
|
||||
{
|
||||
if (multifile()) {
|
||||
setSequence(sequence_, framerate);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiFileSource::setLoop (bool on)
|
||||
{
|
||||
if (multifile()) {
|
||||
sequence_.loop = on ? 1 : 0;
|
||||
multifile()->setLoop( sequence_.loop );
|
||||
}
|
||||
}
|
||||
|
||||
void MultiFileSource::setRange (glm::ivec2 range)
|
||||
{
|
||||
range_.x = glm::clamp( range.x, sequence_.min, sequence_.max );
|
||||
range_.y = glm::clamp( range.y, sequence_.min, sequence_.max );
|
||||
range_.x = glm::min( range_.x, range_.y );
|
||||
range_.y = glm::max( range_.x, range_.y );
|
||||
|
||||
if (multifile())
|
||||
multifile()->setRange( range_.x, range_.y );
|
||||
}
|
||||
|
||||
void MultiFileSource::accept(Visitor& v)
|
||||
{
|
||||
Source::accept(v);
|
||||
if (!failed())
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
MultiFile *MultiFileSource::multifile() const
|
||||
{
|
||||
return dynamic_cast<MultiFile *>(stream_);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
#ifndef MULTIFILESOURCE_H
|
||||
#define MULTIFILESOURCE_H
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
#include "StreamSource.h"
|
||||
|
||||
struct MultiFileSequence {
|
||||
std::string location;
|
||||
std::string codec;
|
||||
uint width;
|
||||
uint height;
|
||||
int min;
|
||||
int max;
|
||||
int loop;
|
||||
|
||||
MultiFileSequence ();
|
||||
MultiFileSequence (const std::list<std::string> &list_files);
|
||||
bool valid () const;
|
||||
MultiFileSequence& operator = (const MultiFileSequence& b);
|
||||
bool operator != (const MultiFileSequence& b);
|
||||
};
|
||||
|
||||
class MultiFile : public Stream
|
||||
{
|
||||
public:
|
||||
MultiFile ();
|
||||
void open (const MultiFileSequence &sequence, uint framerate = 30);
|
||||
|
||||
void setRange(int begin, int end);
|
||||
void setLoop (int on);
|
||||
|
||||
protected:
|
||||
GstElement *src_ ;
|
||||
};
|
||||
|
||||
class MultiFileSource : public StreamSource
|
||||
{
|
||||
public:
|
||||
MultiFileSource (uint64_t id = 0);
|
||||
|
||||
// Source interface
|
||||
void accept (Visitor& v) override;
|
||||
|
||||
// StreamSource interface
|
||||
Stream *stream () const override { return stream_; }
|
||||
glm::ivec2 icon () const override { return glm::ivec2(3, 9); }
|
||||
|
||||
// specific interface
|
||||
void setFiles (const std::list<std::string> &list_files, uint framerate);
|
||||
|
||||
void setSequence (const MultiFileSequence &sequence, uint framerate);
|
||||
inline MultiFileSequence sequence () const { return sequence_; }
|
||||
|
||||
void setFramerate (uint fps);
|
||||
inline uint framerate () const { return framerate_; }
|
||||
|
||||
void setLoop (bool on);
|
||||
inline bool loop () const { return sequence_.loop; }
|
||||
|
||||
void setRange (glm::ivec2 range);
|
||||
glm::ivec2 range () const { return range_; }
|
||||
|
||||
MultiFile *multifile () const;
|
||||
|
||||
private:
|
||||
MultiFileSequence sequence_;
|
||||
uint framerate_;
|
||||
glm::ivec2 range_;
|
||||
};
|
||||
|
||||
|
||||
#endif // MULTIFILESOURCE_H
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
#include "Overlay.h"
|
||||
|
||||
Overlay::Overlay()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
SnapshotOverlay::SnapshotOverlay() : Overlay()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SnapshotOverlay::draw()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void SnapshotOverlay::update (float dt)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef OVERLAY_H
|
||||
#define OVERLAY_H
|
||||
|
||||
#include "View.h"
|
||||
|
||||
class Overlay
|
||||
{
|
||||
public:
|
||||
Overlay();
|
||||
|
||||
virtual void update (float dt) = 0;
|
||||
virtual void draw () = 0;
|
||||
|
||||
virtual std::pair<Node *, glm::vec2> pick(glm::vec2) {
|
||||
return { nullptr, glm::vec2(0.f) };
|
||||
}
|
||||
|
||||
virtual View::Cursor grab (Source*, glm::vec2, glm::vec2, std::pair<Node *, glm::vec2>) {
|
||||
return View::Cursor ();
|
||||
}
|
||||
|
||||
virtual View::Cursor over (glm::vec2) {
|
||||
return View::Cursor ();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class SnapshotOverlay: public Overlay
|
||||
{
|
||||
public:
|
||||
SnapshotOverlay();
|
||||
|
||||
void draw () override;
|
||||
void update (float dt) override;
|
||||
};
|
||||
|
||||
|
||||
#endif // OVERLAY_H
|
||||
@@ -424,14 +424,15 @@ glm::vec2 Rendering::project(glm::vec3 scene_coordinate, glm::mat4 modelview, bo
|
||||
|
||||
void Rendering::FileDropped(GLFWwindow *, int path_count, const char* paths[])
|
||||
{
|
||||
for (int i = 0; i < path_count; ++i) {
|
||||
int i = 0;
|
||||
for (; i < path_count; ++i) {
|
||||
std::string filename(paths[i]);
|
||||
if (filename.empty())
|
||||
break;
|
||||
// try to create a source
|
||||
Mixer::manager().addSource ( Mixer::manager().createSourceFile( filename ) );
|
||||
}
|
||||
if (path_count>0) {
|
||||
if (i>0) {
|
||||
UserInterface::manager().showPannel();
|
||||
Rendering::manager().mainWindow().show();
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "PatternSource.h"
|
||||
#include "DeviceSource.h"
|
||||
#include "NetworkSource.h"
|
||||
#include "MultiFileSource.h"
|
||||
#include "Session.h"
|
||||
#include "ImageShader.h"
|
||||
#include "ImageProcessingShader.h"
|
||||
@@ -267,6 +268,9 @@ void SessionLoader::load(XMLElement *sessionNode)
|
||||
else if ( std::string(pType) == "NetworkSource") {
|
||||
load_source = new NetworkSource(id_xml_);
|
||||
}
|
||||
else if ( std::string(pType) == "MultiFileSource") {
|
||||
load_source = new MultiFileSource(id_xml_);
|
||||
}
|
||||
|
||||
// skip failed (including clones)
|
||||
if (!load_source)
|
||||
@@ -384,6 +388,9 @@ Source *SessionLoader::createSource(tinyxml2::XMLElement *sourceNode, Mode mode)
|
||||
else if ( std::string(pType) == "NetworkSource") {
|
||||
load_source = new NetworkSource(id__);
|
||||
}
|
||||
else if ( std::string(pType) == "MultiFileSource") {
|
||||
load_source = new MultiFileSource(id__);
|
||||
}
|
||||
else if ( std::string(pType) == "CloneSource") {
|
||||
// clone from given origin
|
||||
XMLElement* originNode = xmlCurrent_->FirstChildElement("origin");
|
||||
@@ -880,3 +887,37 @@ void SessionLoader::visit (NetworkSource& s)
|
||||
}
|
||||
|
||||
|
||||
void SessionLoader::visit (MultiFileSource& s)
|
||||
{
|
||||
XMLElement* seq = xmlCurrent_->FirstChildElement("Sequence");
|
||||
|
||||
if (seq) {
|
||||
|
||||
MultiFileSequence sequence;
|
||||
sequence.location = std::string ( seq->GetText() );
|
||||
seq->QueryIntAttribute("min", &sequence.min);
|
||||
seq->QueryIntAttribute("max", &sequence.max);
|
||||
seq->QueryIntAttribute("loop", &sequence.loop);
|
||||
seq->QueryUnsignedAttribute("width", &sequence.width);
|
||||
seq->QueryUnsignedAttribute("height", &sequence.height);
|
||||
const char *codec = seq->Attribute("codec");
|
||||
if (codec)
|
||||
sequence.codec = std::string(codec);
|
||||
uint fps = 0;
|
||||
seq->QueryUnsignedAttribute("fps", &fps);
|
||||
|
||||
// different sequence
|
||||
if ( sequence != s.sequence() ) {
|
||||
s.setSequence( sequence, fps);
|
||||
}
|
||||
// same sequence, different framerate
|
||||
else if ( fps != s.framerate() ) {
|
||||
s.setFramerate( fps );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ public:
|
||||
void visit (PatternSource& s) override;
|
||||
void visit (DeviceSource& s) override;
|
||||
void visit (NetworkSource& s) override;
|
||||
void visit (MultiFileSource& s) override;
|
||||
|
||||
static void XMLToNode(const tinyxml2::XMLElement *xml, Node &n);
|
||||
static void XMLToSourcecore(tinyxml2::XMLElement *xml, SourceCore &s);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "PatternSource.h"
|
||||
#include "DeviceSource.h"
|
||||
#include "NetworkSource.h"
|
||||
#include "MultiFileSource.h"
|
||||
#include "ImageShader.h"
|
||||
#include "ImageProcessingShader.h"
|
||||
#include "MediaPlayer.h"
|
||||
@@ -593,6 +594,36 @@ void SessionVisitor::visit (MixingGroup& g)
|
||||
}
|
||||
}
|
||||
|
||||
void SessionVisitor::visit (MultiFileSource& s)
|
||||
{
|
||||
xmlCurrent_->SetAttribute("type", "MultiFileSource");
|
||||
|
||||
XMLElement *sequence = xmlDoc_->NewElement("Sequence");
|
||||
sequence->SetAttribute("fps", s.framerate());
|
||||
sequence->SetAttribute("min", s.sequence().min);
|
||||
sequence->SetAttribute("max", s.sequence().max);
|
||||
sequence->SetAttribute("loop", s.sequence().loop);
|
||||
sequence->SetAttribute("width", s.sequence().width);
|
||||
sequence->SetAttribute("height", s.sequence().height);
|
||||
sequence->SetAttribute("codec", s.sequence().codec.c_str());
|
||||
XMLText *location = xmlDoc_->NewText( s.sequence().location.c_str() );
|
||||
sequence->InsertEndChild( location );
|
||||
|
||||
xmlCurrent_->InsertEndChild(sequence);
|
||||
|
||||
// XMLElement *sequence = xmlDoc_->NewElement("Sequence");
|
||||
// sequence->SetAttribute("fps", s.fps());
|
||||
|
||||
// std::list<std::string> list = s.sequence ();
|
||||
// for (auto it = list.cbegin(); it != list.cend(); ++it) {
|
||||
// XMLElement *f = xmlDoc_->NewElement("file");
|
||||
// XMLText *filename = xmlDoc_->NewText( it->c_str() );
|
||||
// f->InsertEndChild( filename );
|
||||
// sequence->InsertEndChild(f);
|
||||
// }
|
||||
// xmlCurrent_->InsertEndChild(sequence);
|
||||
}
|
||||
|
||||
std::string SessionVisitor::getClipboard(const SourceList &list)
|
||||
{
|
||||
std::string x = "";
|
||||
|
||||
+19
-18
@@ -28,26 +28,26 @@ public:
|
||||
static std::string getClipboard(ImageProcessingShader * const s);
|
||||
|
||||
// Elements of Scene
|
||||
void visit(Scene& n) override;
|
||||
void visit(Node& n) override;
|
||||
void visit(Group& n) override;
|
||||
void visit(Switch& n) override;
|
||||
void visit(Primitive& n) override;
|
||||
void visit(Surface&) override;
|
||||
void visit(ImageSurface& n) override;
|
||||
void visit(MediaSurface& n) override;
|
||||
void visit(FrameBufferSurface&) override;
|
||||
void visit(LineStrip& n) override;
|
||||
void visit(LineSquare&) override;
|
||||
void visit(Mesh& n) override;
|
||||
void visit(Frame& n) override;
|
||||
void visit (Scene& n) override;
|
||||
void visit (Node& n) override;
|
||||
void visit (Group& n) override;
|
||||
void visit (Switch& n) override;
|
||||
void visit (Primitive& n) override;
|
||||
void visit (Surface&) override;
|
||||
void visit (ImageSurface& n) override;
|
||||
void visit (MediaSurface& n) override;
|
||||
void visit (FrameBufferSurface&) override;
|
||||
void visit (LineStrip& n) override;
|
||||
void visit (LineSquare&) override;
|
||||
void visit (Mesh& n) override;
|
||||
void visit (Frame& n) override;
|
||||
|
||||
// Elements with attributes
|
||||
void visit(MediaPlayer& n) override;
|
||||
void visit(Shader& n) override;
|
||||
void visit(ImageShader& n) override;
|
||||
void visit(MaskShader& n) override;
|
||||
void visit(ImageProcessingShader& n) override;
|
||||
void visit (MediaPlayer& n) override;
|
||||
void visit (Shader& n) override;
|
||||
void visit (ImageShader& n) override;
|
||||
void visit (MaskShader& n) override;
|
||||
void visit (ImageProcessingShader& n) override;
|
||||
|
||||
// Sources
|
||||
void visit (Source& s) override;
|
||||
@@ -60,6 +60,7 @@ public:
|
||||
void visit (DeviceSource& s) override;
|
||||
void visit (NetworkSource& s) override;
|
||||
void visit (MixingGroup& s) override;
|
||||
void visit (MultiFileSource& s) override;
|
||||
|
||||
static tinyxml2::XMLElement *NodeToXML(const Node &n, tinyxml2::XMLDocument *doc);
|
||||
static tinyxml2::XMLElement *ImageToXML(const FrameBufferImage *img, tinyxml2::XMLDocument *doc);
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ guint Stream::texture() const
|
||||
}
|
||||
|
||||
|
||||
void Stream::open(const std::string &gstreamer_description, int w, int h)
|
||||
void Stream::open(const std::string &gstreamer_description, guint w, guint h)
|
||||
{
|
||||
// set gstreamer pipeline source
|
||||
description_ = gstreamer_description;
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
/**
|
||||
* Open a media using gstreamer pipeline keyword
|
||||
* */
|
||||
void open(const std::string &gstreamer_description, int w = 1024, int h = 576);
|
||||
void open(const std::string &gstreamer_description, guint w = 1024, guint h = 576);
|
||||
/**
|
||||
* Get description string
|
||||
* */
|
||||
|
||||
+7
-3
@@ -134,7 +134,10 @@ string SystemToolkit::path_filename(const string& path)
|
||||
|
||||
string SystemToolkit::extension_filename(const string& filename)
|
||||
{
|
||||
string ext = filename.substr(filename.find_last_of(".") + 1);
|
||||
string ext;
|
||||
auto loc = filename.find_last_of(".");
|
||||
if (loc != string::npos)
|
||||
ext = filename.substr( loc + 1 );
|
||||
return ext;
|
||||
}
|
||||
|
||||
@@ -274,7 +277,7 @@ std::string SystemToolkit::path_directory(const std::string& path)
|
||||
return directorypath;
|
||||
}
|
||||
|
||||
list<string> SystemToolkit::list_directory(const string& path, const string& filter)
|
||||
list<string> SystemToolkit::list_directory(const string& path, const list<string>& extensions)
|
||||
{
|
||||
list<string> ls;
|
||||
|
||||
@@ -286,7 +289,8 @@ list<string> SystemToolkit::list_directory(const string& path, const string& fil
|
||||
if ( ent->d_type == DT_REG)
|
||||
{
|
||||
string filename = string(ent->d_name);
|
||||
if ( extension_filename(filename) == filter)
|
||||
string ext = extension_filename(filename);
|
||||
if ( extensions.empty() || find(extensions.cbegin(), extensions.cend(), ext) != extensions.cend())
|
||||
ls.push_back( full_filename(path, filename) );
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ namespace SystemToolkit
|
||||
std::string path_directory(const std::string& path);
|
||||
|
||||
// list all files of a directory mathing the given filter extension (if any)
|
||||
std::list<std::string> list_directory(const std::string& path, const std::string& filter = "");
|
||||
std::list<std::string> list_directory(const std::string& path, const std::list<std::string> &extensions);
|
||||
|
||||
// true of file exists
|
||||
bool file_exists(const std::string& path);
|
||||
|
||||
+160
-133
@@ -1100,7 +1100,7 @@ void UserInterface::RenderPreview()
|
||||
ImGui::Combo("Path", &selected_path, name_path, 4);
|
||||
if (selected_path > 2) {
|
||||
if (recordFolderFileDialogs.empty()) {
|
||||
recordFolderFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::FolderDialog, Settings::application.record.path) );
|
||||
recordFolderFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::openFolderDialog, Settings::application.record.path) );
|
||||
fileDialogPending_ = true;
|
||||
}
|
||||
}
|
||||
@@ -1413,67 +1413,70 @@ void UserInterface::RenderShaderEditor()
|
||||
{
|
||||
static bool show_statusbar = true;
|
||||
|
||||
if ( ImGui::Begin(IMGUI_TITLE_SHADEREDITOR, &Settings::application.widget.shader_editor, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_MenuBar) )
|
||||
if ( !ImGui::Begin(IMGUI_TITLE_SHADEREDITOR, &Settings::application.widget.shader_editor, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_MenuBar) )
|
||||
{
|
||||
ImGui::SetWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
|
||||
if (ImGui::BeginMenuBar())
|
||||
{
|
||||
if (ImGui::BeginMenu("Edit"))
|
||||
{
|
||||
bool ro = editor.IsReadOnly();
|
||||
if (ImGui::MenuItem("Read-only mode", nullptr, &ro))
|
||||
editor.SetReadOnly(ro);
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem( ICON_FA_UNDO " Undo", CTRL_MOD "Z", nullptr, !ro && editor.CanUndo()))
|
||||
editor.Undo();
|
||||
if (ImGui::MenuItem( ICON_FA_REDO " Redo", CTRL_MOD "Y", nullptr, !ro && editor.CanRedo()))
|
||||
editor.Redo();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem( ICON_FA_COPY " Copy", CTRL_MOD "C", nullptr, editor.HasSelection()))
|
||||
editor.Copy();
|
||||
if (ImGui::MenuItem( ICON_FA_CUT " Cut", CTRL_MOD "X", nullptr, !ro && editor.HasSelection()))
|
||||
editor.Cut();
|
||||
if (ImGui::MenuItem( ICON_FA_ERASER " Delete", "Del", nullptr, !ro && editor.HasSelection()))
|
||||
editor.Delete();
|
||||
if (ImGui::MenuItem( ICON_FA_PASTE " Paste", CTRL_MOD "V", nullptr, !ro && ImGui::GetClipboardText() != nullptr))
|
||||
editor.Paste();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem( "Select all", nullptr, nullptr))
|
||||
editor.SetSelection(TextEditor::Coordinates(), TextEditor::Coordinates(editor.GetTotalLines(), 0));
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("View"))
|
||||
{
|
||||
bool ws = editor.IsShowingWhitespaces();
|
||||
if (ImGui::MenuItem( ICON_FA_LONG_ARROW_ALT_RIGHT " Whitespace", nullptr, &ws))
|
||||
editor.SetShowWhitespaces(ws);
|
||||
ImGui::MenuItem( ICON_FA_WINDOW_MAXIMIZE " Statusbar", nullptr, &show_statusbar);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
|
||||
if (show_statusbar) {
|
||||
auto cpos = editor.GetCursorPosition();
|
||||
ImGui::Text("%6d/%-6d %6d lines | %s | %s | %s ", cpos.mLine + 1, cpos.mColumn + 1, editor.GetTotalLines(),
|
||||
editor.IsOverwrite() ? "Ovr" : "Ins",
|
||||
editor.CanUndo() ? "*" : " ",
|
||||
editor.GetLanguageDefinition().mName.c_str());
|
||||
}
|
||||
|
||||
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
|
||||
editor.Render("ShaderEditor");
|
||||
ImGui::PopFont();
|
||||
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::SetWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
|
||||
if (ImGui::BeginMenuBar())
|
||||
{
|
||||
if (ImGui::BeginMenu("Edit"))
|
||||
{
|
||||
bool ro = editor.IsReadOnly();
|
||||
if (ImGui::MenuItem("Read-only mode", nullptr, &ro))
|
||||
editor.SetReadOnly(ro);
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem( ICON_FA_UNDO " Undo", CTRL_MOD "Z", nullptr, !ro && editor.CanUndo()))
|
||||
editor.Undo();
|
||||
if (ImGui::MenuItem( ICON_FA_REDO " Redo", CTRL_MOD "Y", nullptr, !ro && editor.CanRedo()))
|
||||
editor.Redo();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem( ICON_FA_COPY " Copy", CTRL_MOD "C", nullptr, editor.HasSelection()))
|
||||
editor.Copy();
|
||||
if (ImGui::MenuItem( ICON_FA_CUT " Cut", CTRL_MOD "X", nullptr, !ro && editor.HasSelection()))
|
||||
editor.Cut();
|
||||
if (ImGui::MenuItem( ICON_FA_ERASER " Delete", "Del", nullptr, !ro && editor.HasSelection()))
|
||||
editor.Delete();
|
||||
if (ImGui::MenuItem( ICON_FA_PASTE " Paste", CTRL_MOD "V", nullptr, !ro && ImGui::GetClipboardText() != nullptr))
|
||||
editor.Paste();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem( "Select all", nullptr, nullptr))
|
||||
editor.SetSelection(TextEditor::Coordinates(), TextEditor::Coordinates(editor.GetTotalLines(), 0));
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("View"))
|
||||
{
|
||||
bool ws = editor.IsShowingWhitespaces();
|
||||
if (ImGui::MenuItem( ICON_FA_LONG_ARROW_ALT_RIGHT " Whitespace", nullptr, &ws))
|
||||
editor.SetShowWhitespaces(ws);
|
||||
ImGui::MenuItem( ICON_FA_WINDOW_MAXIMIZE " Statusbar", nullptr, &show_statusbar);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
|
||||
if (show_statusbar) {
|
||||
auto cpos = editor.GetCursorPosition();
|
||||
ImGui::Text("%6d/%-6d %6d lines | %s | %s | %s ", cpos.mLine + 1, cpos.mColumn + 1, editor.GetTotalLines(),
|
||||
editor.IsOverwrite() ? "Ovr" : "Ins",
|
||||
editor.CanUndo() ? "*" : " ",
|
||||
editor.GetLanguageDefinition().mName.c_str());
|
||||
}
|
||||
|
||||
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
|
||||
editor.Render("ShaderEditor");
|
||||
ImGui::PopFont();
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void UserInterface::RenderMetrics(bool *p_open, int* p_corner, int *p_mode)
|
||||
@@ -2330,6 +2333,7 @@ void Navigator::clearButtonSelection()
|
||||
// clear new source pannel
|
||||
new_source_preview_.setSource();
|
||||
pattern_type = -1;
|
||||
_selectedFiles.clear();
|
||||
}
|
||||
|
||||
void Navigator::showPannelSource(int index)
|
||||
@@ -2653,18 +2657,11 @@ void Navigator::RenderNewPannel()
|
||||
ImGui::SetCursorPosY(width_);
|
||||
ImGui::Text("Source");
|
||||
|
||||
// ImGui::SameLine();
|
||||
// ImGui::SetCursorPosX(pannel_width_ IMGUI_RIGHT_ALIGN);
|
||||
// if (ImGui::BeginMenu("Edit"))
|
||||
// {
|
||||
// UserInterface::manager().showMenuEdit();
|
||||
// ImGui::EndMenu();
|
||||
// }
|
||||
|
||||
//
|
||||
// News Source selection pannel
|
||||
//
|
||||
static const char* origin_names[4] = { ICON_FA_PHOTO_VIDEO " File",
|
||||
static const char* origin_names[5] = { ICON_FA_PHOTO_VIDEO " File",
|
||||
ICON_FA_SORT_NUMERIC_DOWN " Sequence",
|
||||
ICON_FA_SYNC " Internal",
|
||||
ICON_FA_COG " Generated",
|
||||
ICON_FA_PLUG " Connected"
|
||||
@@ -2673,16 +2670,16 @@ void Navigator::RenderNewPannel()
|
||||
if (ImGui::Combo("##Origin", &Settings::application.source.new_type, origin_names, IM_ARRAYSIZE(origin_names)) )
|
||||
new_source_preview_.setSource();
|
||||
|
||||
ImGui::SetCursorPosY(2.f * width_);
|
||||
|
||||
// File Source creation
|
||||
if (Settings::application.source.new_type == 0) {
|
||||
|
||||
ImGui::SetCursorPosY(2.f * width_);
|
||||
|
||||
// clic button to load file
|
||||
if ( ImGui::Button( ICON_FA_FILE_EXPORT " Open file", ImVec2(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, 0)) ) {
|
||||
// launch async call to file dialog and get its future.
|
||||
if (fileImportFileDialogs.empty()) {
|
||||
fileImportFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::ImportFileDialog, Settings::application.recentImport.path) );
|
||||
fileImportFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::openMediaFileDialog, Settings::application.recentImport.path) );
|
||||
fileDialogPending_ = true;
|
||||
}
|
||||
}
|
||||
@@ -2730,10 +2727,74 @@ void Navigator::RenderNewPannel()
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
}
|
||||
// Internal Source creator
|
||||
// Folder Source creator
|
||||
else if (Settings::application.source.new_type == 1){
|
||||
|
||||
ImGui::SetCursorPosY(2.f * width_);
|
||||
bool update_new_source = false;
|
||||
static std::vector< std::future< std::list<std::string> > > _selectedImagesFileDialogs;
|
||||
|
||||
// clic button to load file
|
||||
if ( ImGui::Button( ICON_FA_IMAGES " Open images", ImVec2(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, 0)) ) {
|
||||
_selectedFiles.clear();
|
||||
if (_selectedImagesFileDialogs.empty()) {
|
||||
_selectedImagesFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::selectImagesFileDialog, Settings::application.recentImport.path) );
|
||||
fileDialogPending_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Indication
|
||||
ImGui::SameLine();
|
||||
ImGuiToolkit::HelpMarker("Create a source from a sequence of numbered images.");
|
||||
|
||||
// return from thread for folder openning
|
||||
if ( !_selectedImagesFileDialogs.empty() ) {
|
||||
// check that file dialog thread finished
|
||||
if (_selectedImagesFileDialogs.back().wait_for(timeout) == std::future_status::ready ) {
|
||||
// get the filenames from this file dialog
|
||||
_selectedFiles = _selectedImagesFileDialogs.back().get();
|
||||
// done with this file dialog
|
||||
_selectedImagesFileDialogs.pop_back();
|
||||
fileDialogPending_ = false;
|
||||
// ask to reload the preview
|
||||
update_new_source = true;
|
||||
}
|
||||
}
|
||||
|
||||
// a selection was made
|
||||
if (!_selectedFiles.empty()) {
|
||||
|
||||
// set framerate
|
||||
static int _fps = 30;
|
||||
static bool _fps_changed = false;
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
if ( ImGui::SliderInt("Framerate", &_fps, 1, 30, "%d fps") ) {
|
||||
_fps_changed = true;
|
||||
}
|
||||
// only call for new source after mouse release to avoid repeating call to re-open the stream
|
||||
else if (_fps_changed && ImGui::IsMouseReleased(ImGuiMouseButton_Left)){
|
||||
update_new_source = true;
|
||||
_fps_changed = false;
|
||||
}
|
||||
|
||||
if (update_new_source) {
|
||||
// multiple files selected
|
||||
if (_selectedFiles.size() > 1) {
|
||||
std::string label = BaseToolkit::transliterate( _selectedFiles.front() );
|
||||
label = BaseToolkit::trunc_string(label, 35);
|
||||
new_source_preview_.setSource( Mixer::manager().createSourceMultifile(_selectedFiles, _fps), label);
|
||||
}
|
||||
// single file selected
|
||||
else {
|
||||
std::string label = BaseToolkit::transliterate( _selectedFiles.front() );
|
||||
label = BaseToolkit::trunc_string(label, 35);
|
||||
new_source_preview_.setSource( Mixer::manager().createSourceFile(_selectedFiles.front()), label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// Internal Source creator
|
||||
else if (Settings::application.source.new_type == 2){
|
||||
|
||||
// fill new_source_preview with a new source
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
@@ -2760,9 +2821,7 @@ void Navigator::RenderNewPannel()
|
||||
ImGuiToolkit::HelpMarker("Create a source replicating internal vimix objects.");
|
||||
}
|
||||
// Generated Source creator
|
||||
else if (Settings::application.source.new_type == 2){
|
||||
|
||||
ImGui::SetCursorPosY(2.f * width_);
|
||||
else if (Settings::application.source.new_type == 3){
|
||||
|
||||
bool update_new_source = false;
|
||||
|
||||
@@ -2804,9 +2863,7 @@ void Navigator::RenderNewPannel()
|
||||
}
|
||||
}
|
||||
// External source creator
|
||||
else if (Settings::application.source.new_type == 3){
|
||||
|
||||
ImGui::SetCursorPosY(2.f * width_);
|
||||
else if (Settings::application.source.new_type == 4){
|
||||
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
if (ImGui::BeginCombo("##External", "Select device"))
|
||||
@@ -2838,7 +2895,7 @@ void Navigator::RenderNewPannel()
|
||||
// if a new source was added
|
||||
if (new_source_preview_.filled()) {
|
||||
// show preview
|
||||
new_source_preview_.Render(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, Settings::application.source.new_type != 1);
|
||||
new_source_preview_.Render(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, Settings::application.source.new_type != 2);
|
||||
// ask to import the source in the mixer
|
||||
ImGui::NewLine();
|
||||
if (new_source_preview_.ready() && ImGui::Button( ICON_FA_CHECK " Create", ImVec2(pannel_width_ - padding_width_, 0)) ) {
|
||||
@@ -2876,44 +2933,6 @@ void Navigator::RenderMainPannelVimix()
|
||||
|
||||
ImGui::SetCursorPosY(width_);
|
||||
|
||||
// //
|
||||
// // Buttons to show WINDOWS
|
||||
// //
|
||||
// ImGui::Spacing();
|
||||
// ImGui::Text("Windows");
|
||||
// ImGui::Spacing();
|
||||
|
||||
// ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
|
||||
// std::string tooltip_ = "";
|
||||
|
||||
// if ( ImGuiToolkit::IconButton( Rendering::manager().mainWindow().isFullscreen() ? ICON_FA_COMPRESS_ALT : ICON_FA_EXPAND_ALT ) )
|
||||
// Rendering::manager().mainWindow().toggleFullscreen();
|
||||
// if (ImGui::IsItemHovered())
|
||||
// tooltip_ = "Fullscreen " CTRL_MOD "Shift+F";
|
||||
|
||||
// ImGui::SameLine(0, 40);
|
||||
// if ( ImGuiToolkit::IconButton( ICON_FA_STICKY_NOTE ) )
|
||||
// Mixer::manager().session()->addNote();
|
||||
// if (ImGui::IsItemHovered())
|
||||
// tooltip_ = "New note " CTRL_MOD "Shift+N";
|
||||
|
||||
// ImGui::SameLine(0, 40);
|
||||
// if ( ImGuiToolkit::IconButton( ICON_FA_FILM ) )
|
||||
// Settings::application.widget.media_player = true;
|
||||
// if (ImGui::IsItemHovered())
|
||||
// tooltip_ = "Player " CTRL_MOD "P";
|
||||
|
||||
// ImGui::SameLine(0, 40);
|
||||
// if ( ImGuiToolkit::IconButton( ICON_FA_DESKTOP ) )
|
||||
// Settings::application.widget.preview = true;
|
||||
// if (ImGui::IsItemHovered())
|
||||
// tooltip_ = "Output " CTRL_MOD "D";
|
||||
|
||||
// ImGui::PopFont();
|
||||
// if (!tooltip_.empty()) {
|
||||
// ImGuiToolkit::ToolTip(tooltip_.substr(0, tooltip_.size()-12).c_str(), tooltip_.substr(tooltip_.size()-12, 12).c_str());
|
||||
// }
|
||||
|
||||
//
|
||||
// SESSION panel
|
||||
//
|
||||
@@ -2947,7 +2966,7 @@ void Navigator::RenderMainPannelVimix()
|
||||
// Option 2 : add a folder
|
||||
if (ImGui::Selectable( ICON_FA_FOLDER_PLUS " Add Folder") ){
|
||||
if (recentFolderFileDialogs.empty()) {
|
||||
recentFolderFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::FolderDialog, Settings::application.recentFolders.path) );
|
||||
recentFolderFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::openFolderDialog, Settings::application.recentFolders.path) );
|
||||
fileDialogPending_ = true;
|
||||
}
|
||||
}
|
||||
@@ -3016,7 +3035,7 @@ void Navigator::RenderMainPannelVimix()
|
||||
// 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, "mix");
|
||||
sessions_list = SystemToolkit::list_directory( Settings::application.recentFolders.path, {"mix", "MIX"});
|
||||
}
|
||||
// indicate the list changed (do not change at every frame)
|
||||
selection_session_mode_changed = false;
|
||||
@@ -3119,8 +3138,8 @@ void Navigator::RenderMainPannelVimix()
|
||||
|
||||
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - 2.f * ImGui::GetFrameHeightWithSpacing()));
|
||||
ImGuiToolkit::HelpMarker("Select the history of recently\n"
|
||||
"opened files or a folder, and\n"
|
||||
"double-clic a filename to open it.\n\n"
|
||||
"opened files or a folder.\n"
|
||||
"Double-clic a filename to open.\n\n"
|
||||
ICON_FA_ARROW_CIRCLE_RIGHT " Enable smooth transition to\n"
|
||||
"perform smooth cross fading.");
|
||||
// toggle button for smooth transition
|
||||
@@ -3349,16 +3368,22 @@ void Navigator::RenderMainPannelVimix()
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGuiToolkit::ToolTip("Take Snapshot ", CTRL_MOD "Y");
|
||||
|
||||
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - 2.f * ImGui::GetFrameHeightWithSpacing()));
|
||||
ImGuiToolkit::HelpMarker("Snapshots capture the state of the session.\n"
|
||||
"Double-clic on a snapshot to restore it.\n\n"
|
||||
ICON_FA_ROUTE " Enable interpolation to animate\n"
|
||||
"from current state to snapshot's state.");
|
||||
// toggle button for smooth interpolation
|
||||
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - ImGui::GetFrameHeightWithSpacing()) );
|
||||
ImGuiToolkit::ButtonToggle(ICON_FA_ROUTE, &Settings::application.smooth_snapshot);
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGuiToolkit::ToolTip("Snapshot interpolation");
|
||||
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - ImGui::GetFrameHeightWithSpacing()));
|
||||
ImGuiToolkit::HelpMarker("Snapshots keeps a list of favorite\n"
|
||||
"status of the current session.\n"
|
||||
"Clic an item to preview or edit.\n"
|
||||
"Double-clic to restore immediately.\n");
|
||||
|
||||
// ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - 2.f * ImGui::GetFrameHeightWithSpacing()));
|
||||
// ImGuiToolkit::HelpMarker("Snapshots capture the state of the session.\n"
|
||||
// "Double-clic on a snapshot to restore it.\n\n"
|
||||
// ICON_FA_ROUTE " Enable interpolation to animate\n"
|
||||
// "from current state to snapshot's state.");
|
||||
// // toggle button for smooth interpolation
|
||||
// ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - ImGui::GetFrameHeightWithSpacing()) );
|
||||
// ImGuiToolkit::ButtonToggle(ICON_FA_ROUTE, &Settings::application.smooth_snapshot);
|
||||
// if (ImGui::IsItemHovered())
|
||||
// ImGuiToolkit::ToolTip("Snapshot interpolation");
|
||||
|
||||
// if (Action::manager().currentSnapshot() > 0) {
|
||||
// ImGui::SetCursorPos( pos_bot );
|
||||
@@ -3368,6 +3393,8 @@ void Navigator::RenderMainPannelVimix()
|
||||
// Action::manager().interpolate( static_cast<float> ( interpolation ) * 0.01f );
|
||||
|
||||
// }
|
||||
|
||||
ImGui::SetCursorPos( pos_bot );
|
||||
}
|
||||
|
||||
//
|
||||
@@ -3442,7 +3469,7 @@ void Navigator::RenderMainPannelSettings()
|
||||
|
||||
#ifndef NDEBUG
|
||||
ImGui::Text("Expert");
|
||||
ImGuiToolkit::ButtonSwitch( IMGUI_TITLE_HISTORY, &Settings::application.widget.history);
|
||||
// ImGuiToolkit::ButtonSwitch( IMGUI_TITLE_HISTORY, &Settings::application.widget.history);
|
||||
ImGuiToolkit::ButtonSwitch( IMGUI_TITLE_SHADEREDITOR, &Settings::application.widget.shader_editor, CTRL_MOD "E");
|
||||
ImGuiToolkit::ButtonSwitch( IMGUI_TITLE_TOOLBOX, &Settings::application.widget.toolbox, CTRL_MOD "T");
|
||||
ImGuiToolkit::ButtonSwitch( IMGUI_TITLE_LOGS, &Settings::application.widget.logs, CTRL_MOD "L");
|
||||
|
||||
@@ -60,6 +60,7 @@ class Navigator
|
||||
bool view_pannel_visible;
|
||||
bool selected_button[NAV_COUNT];
|
||||
int pattern_type;
|
||||
std::list<std::string> _selectedFiles;
|
||||
void clearButtonSelection();
|
||||
void applyButtonSelection(int index);
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
return Cursor ();
|
||||
}
|
||||
|
||||
//TODO: test mouse over provided a point in screen coordinates
|
||||
// test mouse over provided a point in screen coordinates
|
||||
virtual Cursor over (glm::vec2) {
|
||||
return Cursor ();
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ class RenderSource;
|
||||
class CloneSource;
|
||||
class NetworkSource;
|
||||
class MixingGroup;
|
||||
class MultiFileSource;
|
||||
|
||||
// Declares the interface for the visitors
|
||||
class Visitor {
|
||||
@@ -81,6 +82,7 @@ public:
|
||||
virtual void visit (SessionGroupSource&) {}
|
||||
virtual void visit (RenderSource&) {}
|
||||
virtual void visit (CloneSource&) {}
|
||||
virtual void visit (MultiFileSource&) {}
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
ply
|
||||
format ascii 1.0
|
||||
comment Created by Blender 2.92.0 - www.blender.org
|
||||
element vertex 73
|
||||
property float x
|
||||
property float y
|
||||
property float z
|
||||
element face 61
|
||||
property list uchar uint vertex_indices
|
||||
end_header
|
||||
-0.073338 -0.096730 0.001250
|
||||
0.088477 -0.052608 0.001250
|
||||
-0.088049 -0.052608 0.001250
|
||||
0.073767 -0.096730 0.001250
|
||||
-0.080068 -0.037900 0.001250
|
||||
-0.071205 0.006222 0.001250
|
||||
-0.088049 0.020929 0.001250
|
||||
0.088477 0.020929 0.001250
|
||||
0.071568 0.006222 0.001250
|
||||
0.080254 -0.037900 0.001250
|
||||
-0.065218 -0.037900 0.001250
|
||||
0.065397 -0.037900 0.001250
|
||||
-0.038191 -0.001199 0.001250
|
||||
-0.035936 -0.001199 0.001250
|
||||
-0.037069 -0.001132 0.001250
|
||||
-0.034843 -0.001395 0.001250
|
||||
-0.039256 -0.001396 0.001250
|
||||
-0.033799 -0.001711 0.001250
|
||||
-0.040255 -0.001712 0.001250
|
||||
-0.032816 -0.002139 0.001250
|
||||
-0.041178 -0.002141 0.001250
|
||||
-0.031903 -0.002669 0.001250
|
||||
-0.042017 -0.002672 0.001250
|
||||
-0.031070 -0.003293 0.001250
|
||||
-0.042761 -0.003296 0.001250
|
||||
-0.030329 -0.004002 0.001250
|
||||
-0.043403 -0.004006 0.001250
|
||||
-0.029690 -0.004787 0.001250
|
||||
-0.043932 -0.004792 0.001250
|
||||
-0.029162 -0.005639 0.001250
|
||||
-0.044340 -0.005646 0.001250
|
||||
-0.028756 -0.006551 0.001250
|
||||
-0.044617 -0.006558 0.001250
|
||||
-0.028484 -0.007512 0.001250
|
||||
-0.044753 -0.007519 0.001250
|
||||
-0.002441 -0.026583 0.001250
|
||||
0.036991 -0.037900 0.001250
|
||||
0.010975 -0.006875 0.001250
|
||||
-0.028353 -0.008515 0.001250
|
||||
-0.044741 -0.008522 0.001250
|
||||
-0.028373 -0.009518 0.001250
|
||||
-0.044580 -0.009525 0.001250
|
||||
-0.028536 -0.010479 0.001250
|
||||
-0.044282 -0.010486 0.001250
|
||||
-0.028833 -0.011391 0.001250
|
||||
-0.043857 -0.011398 0.001250
|
||||
-0.029256 -0.012245 0.001250
|
||||
-0.043315 -0.012250 0.001250
|
||||
-0.029794 -0.013031 0.001250
|
||||
-0.042667 -0.013035 0.001250
|
||||
-0.030439 -0.013740 0.001250
|
||||
-0.041923 -0.013744 0.001250
|
||||
-0.031181 -0.014365 0.001250
|
||||
-0.041094 -0.014368 0.001250
|
||||
-0.032011 -0.014896 0.001250
|
||||
-0.040189 -0.014898 0.001250
|
||||
-0.032921 -0.015324 0.001250
|
||||
-0.039221 -0.015326 0.001250
|
||||
-0.033901 -0.015641 0.001250
|
||||
-0.038198 -0.015642 0.001250
|
||||
-0.034941 -0.015838 0.001250
|
||||
-0.037132 -0.015838 0.001250
|
||||
-0.036032 -0.015905 0.001250
|
||||
-0.036562 -0.037900 0.001250
|
||||
-0.015776 -0.016935 0.001250
|
||||
-0.074375 0.035637 0.001250
|
||||
0.078371 0.050344 0.001250
|
||||
-0.077972 0.050344 0.001250
|
||||
0.074760 0.035637 0.001250
|
||||
-0.062747 0.065052 0.001250
|
||||
0.066375 0.079759 0.001250
|
||||
-0.065976 0.079759 0.001250
|
||||
0.063146 0.065052 0.001250
|
||||
3 0 1 2
|
||||
3 0 3 1
|
||||
3 4 5 6
|
||||
3 5 7 6
|
||||
3 5 8 7
|
||||
3 8 9 7
|
||||
3 4 10 5
|
||||
3 11 9 8
|
||||
3 12 13 14
|
||||
3 12 15 13
|
||||
3 16 15 12
|
||||
3 16 17 15
|
||||
3 18 17 16
|
||||
3 18 19 17
|
||||
3 20 19 18
|
||||
3 20 21 19
|
||||
3 22 21 20
|
||||
3 22 23 21
|
||||
3 24 23 22
|
||||
3 24 25 23
|
||||
3 26 25 24
|
||||
3 26 27 25
|
||||
3 28 27 26
|
||||
3 28 29 27
|
||||
3 30 29 28
|
||||
3 30 31 29
|
||||
3 32 31 30
|
||||
3 32 33 31
|
||||
3 34 33 32
|
||||
3 35 36 37
|
||||
3 34 38 33
|
||||
3 39 38 34
|
||||
3 39 40 38
|
||||
3 41 40 39
|
||||
3 41 42 40
|
||||
3 43 42 41
|
||||
3 43 44 42
|
||||
3 45 44 43
|
||||
3 45 46 44
|
||||
3 47 46 45
|
||||
3 47 48 46
|
||||
3 49 48 47
|
||||
3 49 50 48
|
||||
3 51 50 49
|
||||
3 51 52 50
|
||||
3 53 52 51
|
||||
3 53 54 52
|
||||
3 55 54 53
|
||||
3 55 56 54
|
||||
3 57 56 55
|
||||
3 57 58 56
|
||||
3 59 58 57
|
||||
3 59 60 58
|
||||
3 61 60 59
|
||||
3 61 62 60
|
||||
3 63 35 64
|
||||
3 63 36 35
|
||||
3 65 66 67
|
||||
3 65 68 66
|
||||
3 69 70 71
|
||||
3 69 72 70
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
name: vimix
|
||||
base: core18
|
||||
version: '0.5'
|
||||
version: '0.6'
|
||||
summary: Live video mixing
|
||||
title: vimix
|
||||
description: |
|
||||
|
||||
Reference in New Issue
Block a user