add creator
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,4 +2,3 @@ build
|
||||
.cache
|
||||
subprojects/*
|
||||
!subprojects/*.wrap
|
||||
src/noinc.hpp
|
||||
|
||||
@@ -37,6 +37,7 @@ deps = [
|
||||
dependency('libpqxx'),
|
||||
dependency('uuid'),
|
||||
dependency('threads'),
|
||||
dependency('gtk+-3.0'),
|
||||
dependency('OpenGL'),
|
||||
dependency('sndfile'),
|
||||
subproject('vertex_engine').get_variable('vertex_engine_dep'),
|
||||
|
||||
21
run.sh
21
run.sh
@@ -1,21 +0,0 @@
|
||||
#!/bin/zsh
|
||||
|
||||
PROJECT_NAME=$(basename $PWD)
|
||||
|
||||
run() {
|
||||
command meson compile -C build
|
||||
if [[ -z "$1" ]]; then
|
||||
cd build
|
||||
./bin/$PROJECT_NAME
|
||||
cd ..
|
||||
else
|
||||
meson test $1 -C build
|
||||
fi
|
||||
}
|
||||
|
||||
if [ -d "build" ]; then
|
||||
run
|
||||
else
|
||||
command meson setup build
|
||||
run
|
||||
fi
|
||||
@@ -10,6 +10,7 @@ headers = [
|
||||
'monitor/gui/components/audio/audio.hpp',
|
||||
'monitor/gui/components/markers/markers.hpp',
|
||||
'monitor/gui/components/helpers/helpers.hpp',
|
||||
'monitor/gui/components/file_dialog/file_dialog.hpp',
|
||||
|
||||
############ GUI/WIN
|
||||
'monitor/gui/win/win.hpp',
|
||||
@@ -17,6 +18,7 @@ headers = [
|
||||
############ UTILS
|
||||
'monitor/utils/var.hpp',
|
||||
'monitor/libs/audio/audio.hpp',
|
||||
'monitor/libs/gtkfd/gtkfd.hpp',
|
||||
|
||||
############
|
||||
# RUN
|
||||
@@ -31,9 +33,6 @@ sources = [
|
||||
'monitor/gui/components/creator/cpp/base.cpp',
|
||||
'monitor/gui/components/creator/cpp/on_event.cpp',
|
||||
'monitor/gui/components/creator/cpp/render/render.cpp',
|
||||
'monitor/gui/components/creator/cpp/render/buttons.cpp',
|
||||
'monitor/gui/components/creator/cpp/render/combo.cpp',
|
||||
'monitor/gui/components/creator/cpp/render/setup.cpp',
|
||||
'monitor/gui/components/creator/cpp/render/spinner.cpp',
|
||||
'monitor/gui/components/snapshot/cpp/base.cpp',
|
||||
'monitor/gui/components/snapshot/cpp/on_event.cpp',
|
||||
@@ -50,9 +49,11 @@ sources = [
|
||||
'monitor/gui/components/helpers/cpp/base.cpp',
|
||||
'monitor/gui/components/helpers/cpp/on_event.cpp',
|
||||
'monitor/gui/components/helpers/cpp/render.cpp',
|
||||
'monitor/gui/components/file_dialog/file_dialog.cpp',
|
||||
|
||||
############ UTILS
|
||||
'monitor/libs/audio/audio.cpp',
|
||||
'monitor/libs/gtkfd/gtkfd.cpp',
|
||||
|
||||
############ GUI/WIN
|
||||
'monitor/gui/win/cpp/base.cpp',
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
#include "monitor/gui/components/creator/creator.hpp"
|
||||
|
||||
#include "monitor/utils/event_type.hpp"
|
||||
#include "monitor/utils/var.hpp"
|
||||
|
||||
namespace monitor::components
|
||||
{
|
||||
void creator::on_attach()
|
||||
{
|
||||
CONNECT(this);
|
||||
for (const auto& entry : std::filesystem::directory_iterator(utils::var::DIR))
|
||||
if (entry.is_directory()) m_dirs.push_back(entry.path().filename().string());
|
||||
}
|
||||
|
||||
void creator::on_detach()
|
||||
@@ -22,33 +18,24 @@ namespace monitor::components
|
||||
void creator::create()
|
||||
{
|
||||
m_status = utils::var::STATUS::PROCESS;
|
||||
m_setup.m_file = utils::var::DIR / m_dir.m_name / m_file.m_name;
|
||||
|
||||
auto f = [this]() {
|
||||
m_setup.m_file = m_file_path;
|
||||
// send to: tabs
|
||||
VE::event e { utils::event_type::CREATE_SNAPSHOT, m_setup };
|
||||
EMIT(e);
|
||||
clear();
|
||||
|
||||
};
|
||||
std::thread th(f);
|
||||
th.detach();
|
||||
}
|
||||
|
||||
void creator::clear()
|
||||
{
|
||||
m_dir.clear();
|
||||
m_file.clear();
|
||||
m_files.clear();
|
||||
m_status = utils::var::STATUS::COMPLETED;
|
||||
|
||||
auto future = std::async(std::launch::async, [this]() {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
m_file_path.clear();
|
||||
m_status = utils::var::STATUS::EMPTY;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void creator::set_domain(std::string name)
|
||||
{
|
||||
if (name == "frequensy")
|
||||
m_setup.m_domain = hr::DOMAIN_PLUGIN::FREQUENSY;
|
||||
else
|
||||
m_setup.m_domain = hr::DOMAIN_PLUGIN::TIME;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,11 +11,11 @@ namespace monitor::components
|
||||
|
||||
switch (type)
|
||||
{
|
||||
// case utils::event_type::CREATE_WORKSPACE_COMPLETE:
|
||||
// {
|
||||
// clear();
|
||||
// break;
|
||||
// }
|
||||
case utils::event_type::CREATE_SNAPSHOT_COMPLETED:
|
||||
{
|
||||
clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
#include "monitor/gui/components/creator/creator.hpp"
|
||||
#include "monitor/utils/var.hpp"
|
||||
|
||||
namespace monitor::components
|
||||
{
|
||||
void creator::render_buttons()
|
||||
{
|
||||
ImGui::BeginDisabled(m_status == utils::var::STATUS::EMPTY);
|
||||
ImGui::BeginDisabled(m_status == utils::var::STATUS::PROCESS);
|
||||
ImGui::BeginDisabled(m_status == utils::var::STATUS::COMPLETED);
|
||||
|
||||
if (ImGui::Button("create", ImVec2{ 94.f, 27.f })) create();
|
||||
|
||||
ImGui::EndDisabled();
|
||||
ImGui::EndDisabled();
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
#include "monitor/gui/components/creator/creator.hpp"
|
||||
#include "monitor/utils/var.hpp"
|
||||
|
||||
namespace monitor::components
|
||||
{
|
||||
void creator::render_combo()
|
||||
{
|
||||
auto ctx = ImGui::GetCurrentContext();
|
||||
ImGui::SameLine(0.f, ctx->Style.FramePadding.x);
|
||||
|
||||
ImGui::SetNextItemWidth(400.f);
|
||||
if (ImGui::BeginCombo(VE_NO_NAME("select_dir"), m_dir.m_id < 0 ? "---" : m_dirs[m_dir.m_id].c_str()))
|
||||
{
|
||||
for (std::size_t i = 0; i < m_dirs.size(); ++i)
|
||||
{
|
||||
const bool is_selected = (m_dir.m_id == static_cast<int>(i));
|
||||
if (ImGui::Selectable(m_dirs[i].c_str(), is_selected))
|
||||
{
|
||||
m_files.clear();
|
||||
m_dir.init(i, m_dirs[i]);
|
||||
for (const auto& entry : std::filesystem::directory_iterator(utils::var::DIR/m_dir.m_name))
|
||||
if (entry.is_regular_file()) m_files.push_back(entry.path().filename().string());
|
||||
}
|
||||
if (is_selected) ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
ctx = ImGui::GetCurrentContext();
|
||||
ImGui::SameLine(0.f, ctx->Style.FramePadding.x);
|
||||
|
||||
ImGui::SetNextItemWidth(400.f);
|
||||
if (ImGui::BeginCombo(VE_NO_NAME("select_file"), m_file.m_id < 0 ? "---" : m_files[m_file.m_id].c_str()))
|
||||
{
|
||||
if (!m_files.empty())
|
||||
{
|
||||
for (std::size_t i = 0; i < m_files.size(); ++i)
|
||||
{
|
||||
const bool is_selected = (m_file.m_id == static_cast<int>(i));
|
||||
if (ImGui::Selectable(m_files[i].c_str(), is_selected))
|
||||
{
|
||||
m_file.init(i, m_files[i]);
|
||||
m_status = utils::var::STATUS::READY;
|
||||
}
|
||||
if (is_selected) ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "monitor/gui/components/creator/creator.hpp"
|
||||
#include "monitor/gui/components/file_dialog/file_dialog.hpp"
|
||||
|
||||
namespace monitor::components
|
||||
{
|
||||
@@ -6,11 +7,33 @@ namespace monitor::components
|
||||
{
|
||||
auto ctx = ImGui::GetCurrentContext();
|
||||
|
||||
render_combo();
|
||||
ImGui::BeginDisabled(m_status == utils::var::STATUS::PROCESS);
|
||||
ImGui::BeginDisabled(m_status == utils::var::STATUS::COMPLETED);
|
||||
if (ImGui::Button("open", ImVec2{ 94.f, 27.f }))
|
||||
{
|
||||
m_file_path = m_fd.open();
|
||||
if (!m_file_path.empty()) m_status = utils::var::STATUS::READY;
|
||||
else m_status = utils::var::STATUS::EMPTY;
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (!m_file_path.empty())
|
||||
{
|
||||
ImGui::Text(m_file_path.filename().c_str());
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginDisabled(m_status == utils::var::STATUS::EMPTY);
|
||||
ImGui::BeginDisabled(m_status == utils::var::STATUS::PROCESS);
|
||||
ImGui::BeginDisabled(m_status == utils::var::STATUS::COMPLETED);
|
||||
if (ImGui::Button("create", ImVec2{ 94.f, 27.f })) create();
|
||||
ImGui::EndDisabled();
|
||||
ImGui::EndDisabled();
|
||||
ImGui::EndDisabled();
|
||||
|
||||
render_spinner();
|
||||
ImGui::SameLine(0.f, 9.f * ctx->Style.FramePadding.x);
|
||||
render_setup();
|
||||
ImGui::SameLine(0.f, 2.f * ctx->Style.FramePadding.x);
|
||||
render_buttons();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
#include "monitor/gui/components/creator/creator.hpp"
|
||||
#include <harmonica.hpp>
|
||||
|
||||
namespace monitor::components
|
||||
{
|
||||
void creator::render_setup()
|
||||
{
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true);
|
||||
|
||||
float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
|
||||
|
||||
ImGui::Text("block size:");
|
||||
ImGui::SameLine(0.0f, spacing);
|
||||
render_block_size_setup(spacing);
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text(" | ");
|
||||
ImGui::SameLine(0.f, spacing);
|
||||
|
||||
render_step_size_setup(spacing);
|
||||
ImGui::SameLine();
|
||||
ImGui::Text(" | ");
|
||||
|
||||
ImGui::PopItemFlag();
|
||||
}
|
||||
|
||||
void creator::render_block_size_setup(float spacing)
|
||||
{
|
||||
ImGui::SetCursorPosY(8.f);
|
||||
VE_PUSH_FONT(ICON, 12);
|
||||
ImGui::PushID(VE_NO_NAME("block_size minus"));
|
||||
if (ImGui::Button(VE::style::icon::ICON_MINUS, ImVec2{24, 20}))
|
||||
{
|
||||
m_setup.m_block_size -= m_step;
|
||||
m_setup.m_block_size = std::max(m_step, m_setup.m_block_size);
|
||||
}
|
||||
ImGui::PopID();
|
||||
VE_POP_FONT();
|
||||
|
||||
ImGui::SameLine(0.0f, spacing);
|
||||
ImGui::Text("%zu", m_setup.m_block_size);
|
||||
// тут нужно жестко, а то при изменении циферек прагает в сторону поле
|
||||
ImGui::SameLine(1000.0f);
|
||||
|
||||
ImGui::SetCursorPosY(8.f);
|
||||
VE_PUSH_FONT(ICON, 12);
|
||||
ImGui::PushID(VE_NO_NAME("block_size plus"));
|
||||
if (ImGui::Button(VE::style::icon::ICON_PLUS, ImVec2{24, 20}))
|
||||
m_setup.m_block_size += m_step;
|
||||
ImGui::PopID();
|
||||
VE_POP_FONT();
|
||||
ImGui::PopItemFlag();
|
||||
}
|
||||
|
||||
void creator::render_step_size_setup(float spacing)
|
||||
{
|
||||
ImGui::Text("step size:");
|
||||
ImGui::SameLine(0.0f, spacing);
|
||||
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true);
|
||||
ImGui::SetCursorPosY(8.f);
|
||||
VE_PUSH_FONT(ICON, 12);
|
||||
ImGui::PushID(VE_NO_NAME("step_size_minus"));
|
||||
if (ImGui::Button(VE::style::icon::ICON_MINUS, ImVec2{24, 20}))
|
||||
{
|
||||
m_setup.m_step_size -= m_step;
|
||||
m_setup.m_step_size = std::max(m_step, m_setup.m_step_size);
|
||||
}
|
||||
ImGui::PopID();
|
||||
VE_POP_FONT();
|
||||
|
||||
ImGui::SameLine(0.0f, spacing);
|
||||
ImGui::Text("%zu", m_setup.m_step_size);
|
||||
// тут нужно жестко, а то при изменении циферек прагает в сторону поле
|
||||
ImGui::SameLine(1195.0f);
|
||||
|
||||
ImGui::SetCursorPosY(8.f);
|
||||
VE_PUSH_FONT(ICON, 12);
|
||||
ImGui::PushID(VE_NO_NAME("step_size_plus"));
|
||||
if (ImGui::Button(VE::style::icon::ICON_PLUS, ImVec2{24, 20}))
|
||||
m_setup.m_step_size += m_step;
|
||||
ImGui::PopID();
|
||||
VE_POP_FONT();
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,11 @@ namespace monitor::components
|
||||
{
|
||||
void creator::render_spinner()
|
||||
{
|
||||
ImVec2 pos{ 830.f, 12.f };
|
||||
ImVec2 buttonPos = ImGui::GetItemRectMin(); // левый верхний угол кнопки
|
||||
ImVec2 buttonSize = ImGui::GetItemRectSize(); // размер кнопки
|
||||
float spacing = 10.f;
|
||||
ImVec2 pos = ImVec2(buttonPos.x + buttonSize.x + spacing, buttonPos.y);
|
||||
|
||||
if (m_status == utils::var::STATUS::COMPLETED) m_spinner.render(pos);
|
||||
else if (m_status == utils::var::STATUS::READY) m_spinner.render(pos, "#B4CF16");
|
||||
else if (m_status == utils::var::STATUS::PROCESS) m_spinner.render(pos, "#B82C5C", true);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <VE.hpp>
|
||||
#include <harmonica.hpp>
|
||||
#include "monitor/gui/components/spinner/spinner.hpp"
|
||||
#include "monitor/gui/components/file_dialog/file_dialog.hpp"
|
||||
#include "monitor/utils/var.hpp"
|
||||
|
||||
namespace monitor::components
|
||||
@@ -13,42 +14,18 @@ namespace monitor::components
|
||||
VE_EVENT_OVERIDE();
|
||||
|
||||
private:
|
||||
struct combo
|
||||
{
|
||||
std::string m_name = "no selected";
|
||||
int m_id = -1;
|
||||
void init(int id, std::string name) { m_id = id; m_name = name; }
|
||||
void clear()
|
||||
{
|
||||
m_name.clear();
|
||||
m_id = -1;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<std::string> m_dirs;
|
||||
std::vector<std::string> m_files;
|
||||
combo m_dir;
|
||||
combo m_file;
|
||||
utils::var::STATUS m_status = utils::var::STATUS::EMPTY;
|
||||
components::spinner m_spinner;
|
||||
hr::setup m_setup;
|
||||
std::size_t m_step = 256;
|
||||
|
||||
|
||||
private:
|
||||
void set_domain(std::string name);
|
||||
hr::setup m_setup;
|
||||
file_dialog m_fd;
|
||||
std::filesystem::path m_file_path;
|
||||
|
||||
private:
|
||||
void render_spinner();
|
||||
void create();
|
||||
void clear();
|
||||
void render_combo();
|
||||
void render_spinner();
|
||||
void render_buttons();
|
||||
void render_setup();
|
||||
void render_combo_signals();
|
||||
|
||||
private:
|
||||
void render_block_size_setup(float spacing);
|
||||
void render_step_size_setup(float spacing);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
9
src/monitor/gui/components/file_dialog/file_dialog.cpp
Normal file
9
src/monitor/gui/components/file_dialog/file_dialog.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#include "file_dialog.hpp"
|
||||
|
||||
namespace monitor::components
|
||||
{
|
||||
std::filesystem::path file_dialog::open(const std::filesystem::path& default_path)
|
||||
{
|
||||
return m_nfd.open(default_path);
|
||||
}
|
||||
}
|
||||
19
src/monitor/gui/components/file_dialog/file_dialog.hpp
Normal file
19
src/monitor/gui/components/file_dialog/file_dialog.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "monitor/libs/gtkfd/gtkfd.hpp"
|
||||
|
||||
namespace monitor::components
|
||||
{
|
||||
class file_dialog
|
||||
{
|
||||
public:
|
||||
file_dialog() = default;
|
||||
~file_dialog() = default;
|
||||
|
||||
public:
|
||||
std::filesystem::path open(const std::filesystem::path& default_path = "/mnt/raid/projects/dsp/songs");
|
||||
|
||||
private:
|
||||
libs::gtkfd m_nfd;
|
||||
};
|
||||
}
|
||||
@@ -29,6 +29,9 @@ namespace monitor::components
|
||||
s->init(setup);
|
||||
s->on_attach();
|
||||
m_snapshots.push_back(s);
|
||||
|
||||
VE::event e { utils::event_type::CREATE_SNAPSHOT_COMPLETED, nullptr };
|
||||
EMIT(e);
|
||||
}
|
||||
|
||||
std::string tabs::string_cut(std::filesystem::path p, int n)
|
||||
|
||||
500
src/monitor/libs/gtkfd.OLD/gtkfd.cpp
Normal file
500
src/monitor/libs/gtkfd.OLD/gtkfd.cpp
Normal file
@@ -0,0 +1,500 @@
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "gtkfd.hpp"
|
||||
|
||||
template <typename T>
|
||||
struct Free_Guard
|
||||
{
|
||||
T* data;
|
||||
Free_Guard(T* freeable) noexcept : data(freeable) {}
|
||||
~Free_Guard() { NFDi_Free(data); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct FreeCheck_Guard
|
||||
{
|
||||
T* data;
|
||||
FreeCheck_Guard(T* freeable = nullptr) noexcept : data(freeable) {}
|
||||
~FreeCheck_Guard() { if (data) NFDi_Free(data); }
|
||||
};
|
||||
|
||||
/* current error */
|
||||
const char* g_errorstr = nullptr;
|
||||
|
||||
void NFDi_SetError(const char* msg)
|
||||
{
|
||||
g_errorstr = msg;
|
||||
}
|
||||
|
||||
template <typename T = void>
|
||||
T* NFDi_Malloc(size_t bytes)
|
||||
{
|
||||
void* ptr = malloc(bytes);
|
||||
if (!ptr) NFDi_SetError("NFDi_Malloc failed.");
|
||||
return static_cast<T*>(ptr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void NFDi_Free(T *ptr)
|
||||
{
|
||||
assert(ptr);
|
||||
free(static_cast<void*>(ptr));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* copy(const T* begin, const T* end, T* out)
|
||||
{
|
||||
for (; begin != end; ++begin) *out++ = *begin;
|
||||
return out;
|
||||
}
|
||||
|
||||
// Does not own the filter and extension.
|
||||
struct Pair_GtkFileFilter_FileExtension
|
||||
{
|
||||
GtkFileFilter* filter;
|
||||
const char* extensionBegin;
|
||||
const char* extensionEnd;
|
||||
};
|
||||
|
||||
struct ButtonClickedArgs
|
||||
{
|
||||
Pair_GtkFileFilter_FileExtension* map;
|
||||
GtkFileChooser* chooser;
|
||||
};
|
||||
|
||||
void AddFiltersToDialog(GtkFileChooser* chooser, const item* filterList, uint_t filterCount)
|
||||
{
|
||||
if (filterCount)
|
||||
{
|
||||
assert(filterList);
|
||||
// we have filters to add ... format and add them
|
||||
|
||||
for (uint_t index = 0; index != filterCount; ++index)
|
||||
{
|
||||
GtkFileFilter* filter = gtk_file_filter_new();
|
||||
|
||||
// count number of file extensions
|
||||
size_t sep = 1;
|
||||
for (const char* p_spec = filterList[index].m_spec; *p_spec; ++p_spec) if (*p_spec == L',') ++sep;
|
||||
|
||||
// calculate space needed (including the trailing '\0')
|
||||
size_t nameSize = sep + strlen(filterList[index].m_spec) + 3 + strlen(filterList[index].m_name);
|
||||
|
||||
// malloc the required memory
|
||||
char* nameBuf = NFDi_Malloc<char>(sizeof(char) * nameSize);
|
||||
|
||||
char* p_nameBuf = nameBuf;
|
||||
for (const char* p_filterName = filterList[index].m_name; *p_filterName; ++p_filterName) *p_nameBuf++ = *p_filterName;
|
||||
*p_nameBuf++ = ' ';
|
||||
*p_nameBuf++ = '(';
|
||||
const char* p_extensionStart = filterList[index].m_spec;
|
||||
for (const char *p_spec = filterList[index].m_spec; true; ++p_spec)
|
||||
{
|
||||
if (*p_spec == ',' || !*p_spec)
|
||||
{
|
||||
if (*p_spec == ',')
|
||||
{
|
||||
*p_nameBuf++ = ',';
|
||||
*p_nameBuf++ = ' ';
|
||||
}
|
||||
|
||||
// +1 for the trailing '\0'
|
||||
char* extnBuf = NFDi_Malloc<char>(sizeof(char) * (p_spec - p_extensionStart + 3));
|
||||
char* p_extnBufEnd = extnBuf;
|
||||
*p_extnBufEnd++ = '*';
|
||||
*p_extnBufEnd++ = '.';
|
||||
p_extnBufEnd = copy(p_extensionStart, p_spec, p_extnBufEnd);
|
||||
*p_extnBufEnd++ = '\0';
|
||||
assert((unsigned int)(p_extnBufEnd - extnBuf) == sizeof(char) * (p_spec - p_extensionStart + 3));
|
||||
gtk_file_filter_add_pattern(filter, extnBuf);
|
||||
NFDi_Free(extnBuf);
|
||||
if (*p_spec) p_extensionStart = p_spec + 1;
|
||||
else break;
|
||||
}
|
||||
else *p_nameBuf++ = *p_spec;
|
||||
}
|
||||
*p_nameBuf++ = ')';
|
||||
*p_nameBuf++ = '\0';
|
||||
assert((unsigned int)(p_nameBuf - nameBuf) == sizeof(char) * nameSize);
|
||||
|
||||
// add to the filter
|
||||
gtk_file_filter_set_name(filter, nameBuf);
|
||||
|
||||
// free the memory
|
||||
NFDi_Free(nameBuf);
|
||||
|
||||
// add filter to chooser
|
||||
gtk_file_chooser_add_filter(chooser, filter);
|
||||
}
|
||||
}
|
||||
|
||||
/* always append a wildcard option to the end*/
|
||||
|
||||
GtkFileFilter* filter = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name(filter, "All files");
|
||||
gtk_file_filter_add_pattern(filter, "*");
|
||||
gtk_file_chooser_add_filter(chooser, filter);
|
||||
}
|
||||
|
||||
// returns null-terminated map (trailing .filter is null)
|
||||
Pair_GtkFileFilter_FileExtension* AddFiltersToDialogWithMap(GtkFileChooser* chooser, const item* filterList, uint_t filterCount)
|
||||
{
|
||||
Pair_GtkFileFilter_FileExtension* map = NFDi_Malloc<Pair_GtkFileFilter_FileExtension>(sizeof(Pair_GtkFileFilter_FileExtension) * (filterCount + 1));
|
||||
|
||||
if (filterCount)
|
||||
{
|
||||
assert(filterList);
|
||||
for (uint_t index = 0; index != filterCount; ++index)
|
||||
{
|
||||
GtkFileFilter* filter = gtk_file_filter_new();
|
||||
|
||||
// store filter in map
|
||||
map[index].filter = filter;
|
||||
map[index].extensionBegin = filterList[index].m_spec;
|
||||
map[index].extensionEnd = nullptr;
|
||||
|
||||
// count number of file extensions
|
||||
std::size_t sep = 1;
|
||||
for (const char *p_spec = filterList[index].m_spec; *p_spec; ++p_spec)
|
||||
if (*p_spec == L',') ++sep;
|
||||
|
||||
// calculate space needed (including the trailing '\0')
|
||||
std::size_t nameSize = sep + strlen(filterList[index].m_spec) + 3 + strlen(filterList[index].m_name);
|
||||
|
||||
// malloc the required memory
|
||||
char* nameBuf = NFDi_Malloc<char>(sizeof(char) * nameSize);
|
||||
char* p_nameBuf = nameBuf;
|
||||
|
||||
for (const char *p_filterName = filterList[index].m_name; *p_filterName; ++p_filterName) *p_nameBuf++ = *p_filterName;
|
||||
*p_nameBuf++ = ' ';
|
||||
*p_nameBuf++ = '(';
|
||||
const char* p_extensionStart = filterList[index].m_spec;
|
||||
|
||||
for (const char* p_spec = filterList[index].m_spec; true; ++p_spec)
|
||||
{
|
||||
if (*p_spec == ',' || !*p_spec)
|
||||
{
|
||||
if (*p_spec == ',')
|
||||
{
|
||||
*p_nameBuf++ = ',';
|
||||
*p_nameBuf++ = ' ';
|
||||
}
|
||||
|
||||
// +1 for the trailing '\0'
|
||||
char *extnBuf = NFDi_Malloc<char>(sizeof(char) * (p_spec - p_extensionStart + 3));
|
||||
char *p_extnBufEnd = extnBuf;
|
||||
*p_extnBufEnd++ = '*';
|
||||
*p_extnBufEnd++ = '.';
|
||||
p_extnBufEnd = copy(p_extensionStart, p_spec, p_extnBufEnd);
|
||||
*p_extnBufEnd++ = '\0';
|
||||
assert((unsigned int)(p_extnBufEnd - extnBuf) == sizeof(char) * (p_spec - p_extensionStart + 3));
|
||||
gtk_file_filter_add_pattern(filter, extnBuf);
|
||||
NFDi_Free(extnBuf);
|
||||
|
||||
// store current pointer in map (if it's the first one)
|
||||
if (map[index].extensionEnd == nullptr) map[index].extensionEnd = p_spec;
|
||||
if (*p_spec) p_extensionStart = p_spec + 1;
|
||||
else break;
|
||||
}
|
||||
else *p_nameBuf++ = *p_spec;
|
||||
}
|
||||
*p_nameBuf++ = ')';
|
||||
*p_nameBuf++ = '\0';
|
||||
assert((unsigned int)(p_nameBuf - nameBuf) == sizeof(char) * nameSize);
|
||||
|
||||
// add to the filter
|
||||
gtk_file_filter_set_name(filter, nameBuf);
|
||||
|
||||
// free the memory
|
||||
NFDi_Free(nameBuf);
|
||||
|
||||
// add filter to chooser
|
||||
gtk_file_chooser_add_filter(chooser, filter);
|
||||
}
|
||||
}
|
||||
|
||||
// set trailing map index to null
|
||||
map[filterCount].filter = nullptr;
|
||||
|
||||
/* always append a wildcard option to the end*/
|
||||
GtkFileFilter* filter = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name(filter, "All files");
|
||||
gtk_file_filter_add_pattern(filter, "*");
|
||||
gtk_file_chooser_add_filter(chooser, filter);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void SetDefaultPath(GtkFileChooser* chooser, const char *defaultPath)
|
||||
{
|
||||
if (!defaultPath || !*defaultPath) return;
|
||||
|
||||
/* GTK+ manual recommends not specifically setting the default path.
|
||||
We do it anyway in order to be consistent across platforms.
|
||||
If consistency with the native OS is preferred, this is the line
|
||||
to comment out. -ml */
|
||||
gtk_file_chooser_set_current_folder(chooser, defaultPath);
|
||||
}
|
||||
|
||||
void SetDefaultName(GtkFileChooser* chooser, const char *defaultName)
|
||||
{
|
||||
if (!defaultName || !*defaultName) return;
|
||||
gtk_file_chooser_set_current_name(chooser, defaultName);
|
||||
}
|
||||
|
||||
void WaitForCleanup()
|
||||
{
|
||||
while (gtk_events_pending()) gtk_main_iteration();
|
||||
}
|
||||
|
||||
struct Widget_Guard
|
||||
{
|
||||
GtkWidget* data;
|
||||
Widget_Guard(GtkWidget* widget) : data(widget) {}
|
||||
~Widget_Guard()
|
||||
{
|
||||
WaitForCleanup();
|
||||
gtk_widget_destroy(data);
|
||||
WaitForCleanup();
|
||||
}
|
||||
};
|
||||
|
||||
static void FileActivatedSignalHandler(GtkButton* saveButton, void* userdata)
|
||||
{
|
||||
ButtonClickedArgs* args = static_cast<ButtonClickedArgs*>(userdata);
|
||||
GtkFileChooser* chooser = args->chooser;
|
||||
char* currentFileName = gtk_file_chooser_get_current_name(chooser);
|
||||
|
||||
if (*currentFileName)
|
||||
{
|
||||
// string is not empty
|
||||
// find a '.' in the file name
|
||||
const char* p_period = currentFileName;
|
||||
for (;*p_period; ++p_period) if (*p_period == '.') break;
|
||||
|
||||
if (!*p_period)
|
||||
{ // there is no '.', so append the default extension
|
||||
Pair_GtkFileFilter_FileExtension* filterMap = static_cast<Pair_GtkFileFilter_FileExtension*>(args->map);
|
||||
GtkFileFilter* currentFilter = gtk_file_chooser_get_filter(chooser);
|
||||
if (currentFilter)
|
||||
for (;filterMap->filter; ++filterMap)
|
||||
if (filterMap->filter == currentFilter) break;
|
||||
|
||||
if (filterMap->filter)
|
||||
{
|
||||
// memory for appended string (including '.' and trailing '\0')
|
||||
char* appendedFileName = NFDi_Malloc<char>(sizeof(char) * ((p_period - currentFileName) + (filterMap->extensionEnd - filterMap->extensionBegin) + 2));
|
||||
char* p_fileName = copy(currentFileName, p_period, appendedFileName);
|
||||
*p_fileName++ = '.';
|
||||
p_fileName = copy(filterMap->extensionBegin, filterMap->extensionEnd, p_fileName);
|
||||
*p_fileName++ = '\0';
|
||||
|
||||
assert(p_fileName - appendedFileName == (p_period - currentFileName) + (filterMap->extensionEnd - filterMap->extensionBegin) + 2);
|
||||
|
||||
// set the appended file name
|
||||
gtk_file_chooser_set_current_name(chooser, appendedFileName);
|
||||
|
||||
// free the memory
|
||||
NFDi_Free(appendedFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// free the memory
|
||||
g_free(currentFileName);
|
||||
}
|
||||
|
||||
const char* NFD_GetError(void)
|
||||
{
|
||||
return g_errorstr;
|
||||
}
|
||||
|
||||
void NFD_ClearError(void)
|
||||
{
|
||||
NFDi_SetError(nullptr);
|
||||
}
|
||||
|
||||
/* public */
|
||||
nfdresult_t Init()
|
||||
{
|
||||
// Init GTK
|
||||
if (!gtk_init_check(NULL, NULL))
|
||||
{
|
||||
NFDi_SetError("gtk_init_check failed to initilaize GTK+");
|
||||
return nfdresult_t::ERROR;
|
||||
}
|
||||
return nfdresult_t::OKAY;
|
||||
}
|
||||
|
||||
void NFD_Quit(void)
|
||||
{
|
||||
// do nothing, GTK cannot be de-initialized
|
||||
}
|
||||
|
||||
void NFD_FreePathN(char* filePath)
|
||||
{
|
||||
assert(filePath);
|
||||
g_free(filePath);
|
||||
}
|
||||
|
||||
nfdresult_t NFD_OpenDialogN(char** outPath, const item* filterList, uint_t filterCount, const char* defaultPath)
|
||||
{
|
||||
GtkWidget* widget = gtk_file_chooser_dialog_new("Open File", nullptr, GTK_FILE_CHOOSER_ACTION_OPEN, "_Cancel", GTK_RESPONSE_CANCEL, "_Open", GTK_RESPONSE_ACCEPT, nullptr);
|
||||
|
||||
// guard to destroy the widget when returning from this function
|
||||
Widget_Guard widgetGuard(widget);
|
||||
|
||||
/* Build the filter list */
|
||||
AddFiltersToDialog(GTK_FILE_CHOOSER(widget), filterList, filterCount);
|
||||
|
||||
/* Set the default path */
|
||||
SetDefaultPath(GTK_FILE_CHOOSER(widget), defaultPath);
|
||||
|
||||
if (gtk_dialog_run(GTK_DIALOG(widget)) == GTK_RESPONSE_ACCEPT)
|
||||
{
|
||||
// write out the file name
|
||||
*outPath = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
|
||||
return nfdresult_t::OKAY;
|
||||
}
|
||||
else return nfdresult_t::CANCEL;
|
||||
}
|
||||
|
||||
nfdresult_t NFD_OpenDialogMultipleN(const nfdpathset_t** outPaths, const item* filterList, uint_t filterCount, const char* defaultPath )
|
||||
{
|
||||
GtkWidget* widget = gtk_file_chooser_dialog_new("Open Files", nullptr, GTK_FILE_CHOOSER_ACTION_OPEN, "_Cancel", GTK_RESPONSE_CANCEL, "_Open", GTK_RESPONSE_ACCEPT, nullptr);
|
||||
|
||||
// guard to destroy the widget when returning from this function
|
||||
Widget_Guard widgetGuard(widget);
|
||||
|
||||
// set select multiple
|
||||
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(widget), TRUE);
|
||||
|
||||
/* Build the filter list */
|
||||
AddFiltersToDialog(GTK_FILE_CHOOSER(widget), filterList, filterCount);
|
||||
|
||||
/* Set the default path */
|
||||
SetDefaultPath(GTK_FILE_CHOOSER(widget), defaultPath);
|
||||
|
||||
if (gtk_dialog_run(GTK_DIALOG(widget)) == GTK_RESPONSE_ACCEPT)
|
||||
{
|
||||
// write out the file name
|
||||
GSList *fileList = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(widget));
|
||||
*outPaths = fileList;
|
||||
return nfdresult_t::OKAY;
|
||||
}
|
||||
else return nfdresult_t::CANCEL;
|
||||
}
|
||||
|
||||
nfdresult_t NFD_SaveDialogN(char** outPath, const item* filterList, uint_t filterCount, const char* defaultPath, const char* defaultName)
|
||||
{
|
||||
GtkWidget* widget = gtk_file_chooser_dialog_new("Save File", nullptr, GTK_FILE_CHOOSER_ACTION_SAVE, "_Cancel", GTK_RESPONSE_CANCEL, nullptr);
|
||||
|
||||
// guard to destroy the widget when returning from this function
|
||||
Widget_Guard widgetGuard(widget);
|
||||
GtkWidget* saveButton = gtk_dialog_add_button(GTK_DIALOG(widget), "_Save", GTK_RESPONSE_ACCEPT);
|
||||
|
||||
// Prompt on overwrite
|
||||
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(widget), TRUE);
|
||||
|
||||
/* Build the filter list */
|
||||
ButtonClickedArgs buttonClickedArgs;
|
||||
buttonClickedArgs.chooser = GTK_FILE_CHOOSER(widget);
|
||||
buttonClickedArgs.map = AddFiltersToDialogWithMap(GTK_FILE_CHOOSER(widget), filterList, filterCount);
|
||||
|
||||
/* Set the default path */
|
||||
SetDefaultPath(GTK_FILE_CHOOSER(widget), defaultPath);
|
||||
|
||||
/* Set the default file name */
|
||||
SetDefaultName(GTK_FILE_CHOOSER(widget), defaultName);
|
||||
|
||||
/* set the handler to add file extension */
|
||||
gulong handlerID = g_signal_connect(G_OBJECT(saveButton), "pressed", G_CALLBACK(FileActivatedSignalHandler), static_cast<void*>(&buttonClickedArgs));
|
||||
|
||||
/* invoke the dialog (blocks until dialog is closed) */
|
||||
gint result = gtk_dialog_run(GTK_DIALOG(widget));
|
||||
|
||||
/* unset the handler */
|
||||
g_signal_handler_disconnect(G_OBJECT(saveButton), handlerID);
|
||||
|
||||
/* free the filter map */
|
||||
NFDi_Free(buttonClickedArgs.map);
|
||||
|
||||
if (result == GTK_RESPONSE_ACCEPT)
|
||||
{
|
||||
// write out the file name
|
||||
*outPath = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
|
||||
return nfdresult_t::OKAY;
|
||||
}
|
||||
else return nfdresult_t::CANCEL;
|
||||
}
|
||||
|
||||
nfdresult_t NFD_PickFolderN(char** outPath, const char* defaultPath)
|
||||
{
|
||||
GtkWidget* widget = gtk_file_chooser_dialog_new("Select folder", nullptr, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, "_Cancel", GTK_RESPONSE_CANCEL, "_Select", GTK_RESPONSE_ACCEPT, nullptr);
|
||||
|
||||
// guard to destroy the widget when returning from this function
|
||||
Widget_Guard widgetGuard(widget);
|
||||
|
||||
/* Set the default path */
|
||||
SetDefaultPath(GTK_FILE_CHOOSER(widget), defaultPath);
|
||||
|
||||
if (gtk_dialog_run(GTK_DIALOG(widget)) == GTK_RESPONSE_ACCEPT)
|
||||
{
|
||||
// write out the file name
|
||||
*outPath = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
|
||||
return nfdresult_t::OKAY;
|
||||
}
|
||||
else return nfdresult_t::CANCEL;
|
||||
}
|
||||
|
||||
nfdresult_t NFD_PathSet_GetCount(const nfdpathset_t* pathSet, uint_t* count)
|
||||
{
|
||||
assert(pathSet);
|
||||
|
||||
// const_cast because methods on GSList aren't const, but it should act like const to the caller
|
||||
GSList*fileList = const_cast<GSList*>(static_cast<const GSList*>(pathSet));
|
||||
|
||||
*count = g_slist_length(fileList);
|
||||
return nfdresult_t::OKAY;
|
||||
}
|
||||
|
||||
nfdresult_t NFD_PathSet_GetPathN(const nfdpathset_t* pathSet, uint_t index, char** outPath)
|
||||
{
|
||||
assert(pathSet);
|
||||
|
||||
// const_cast because methods on GSList aren't const, but it should act like const to the caller
|
||||
GSList* fileList = const_cast<GSList*>(static_cast<const GSList*>(pathSet));
|
||||
|
||||
// Note: this takes linear time... but should be good enough
|
||||
*outPath = static_cast<char*>(g_slist_nth_data(fileList, index));
|
||||
return nfdresult_t::OKAY;
|
||||
}
|
||||
|
||||
void NFD_PathSet_FreePathN(const char* filePath)
|
||||
{
|
||||
assert(filePath);
|
||||
// no-op, because NFD_PathSet_Free does the freeing for us
|
||||
}
|
||||
|
||||
void NFD_PathSet_Free(const nfdpathset_t *pathSet)
|
||||
{
|
||||
assert(pathSet);
|
||||
|
||||
// const_cast because methods on GSList aren't const, but it should act like const to the caller
|
||||
GSList* fileList = const_cast<GSList*>(static_cast<const GSList*>(pathSet));
|
||||
|
||||
// free all the nodes
|
||||
for (GSList* node = fileList; node; node = node->next)
|
||||
{
|
||||
assert(node->data);
|
||||
g_free(node->data);
|
||||
}
|
||||
|
||||
// free the path set memory
|
||||
g_slist_free(fileList);
|
||||
}
|
||||
|
||||
|
||||
|
||||
244
src/monitor/libs/gtkfd.OLD/gtkfd.hpp
Normal file
244
src/monitor/libs/gtkfd.OLD/gtkfd.hpp
Normal file
@@ -0,0 +1,244 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory> // for std::unique_ptr
|
||||
#include <stddef.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include "monitor/utils/using.hpp"
|
||||
|
||||
namespace monitor::libs
|
||||
{
|
||||
class gtkfd
|
||||
{
|
||||
struct item
|
||||
{
|
||||
const char* m_name;
|
||||
const char* m_spec;
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
using nfdpathset_t = GSList;
|
||||
|
||||
struct item
|
||||
{
|
||||
const char* m_name;
|
||||
const char* m_spec;
|
||||
};
|
||||
|
||||
enum class nfdresult_t
|
||||
{
|
||||
OKAY, /* user pressed okay, or successful return */
|
||||
ERROR, /* programmatic error */
|
||||
CANCEL /* user pressed cancel */
|
||||
};
|
||||
|
||||
/* call this to de-initialize NFD, if NFD_Init returned NFD_OKAY */
|
||||
void NFD_Quit(void);
|
||||
|
||||
/* free a file path that was returned by the dialogs */
|
||||
/* Note: use NFD_PathSet_FreePath to free path from pathset instead of this function */
|
||||
void NFD_FreePathN(char* filePath);
|
||||
|
||||
/* single file open dialog */
|
||||
/* It is the caller's responsibility to free `outPath` via NFD_FreePathN() if this function returns NFD_OKAY */
|
||||
/* If filterCount is zero, filterList is ignored (you can use NULL) */
|
||||
/* If defaultPath is NULL, the operating system will decide */
|
||||
nfdresult_t NFD_OpenDialogN(char** outPath, const item* filterList, uint_t filterCount, const char* defaultPath);
|
||||
|
||||
/* multiple file open dialog */
|
||||
/* It is the caller's responsibility to free `outPaths` via NFD_PathSet_Free() if this function returns NFD_OKAY */
|
||||
/* If filterCount is zero, filterList is ignored (you can use NULL) */
|
||||
/* If defaultPath is NULL, the operating system will decide */
|
||||
nfdresult_t NFD_OpenDialogMultipleN(const nfdpathset_t** outPaths, const item* filterList, uint_t filterCount, const char* defaultPath);
|
||||
|
||||
/* save dialog */
|
||||
/* It is the caller's responsibility to free `outPath` via NFD_FreePathN() if this function returns NFD_OKAY */
|
||||
/* If filterCount is zero, filterList is ignored (you can use NULL) */
|
||||
/* If defaultPath is NULL, the operating system will decide */
|
||||
nfdresult_t NFD_SaveDialogN(char** outPath, const item* filterList, uint_t filterCount, const char* defaultPath, const char* defaultName);
|
||||
|
||||
/* select folder dialog */
|
||||
/* It is the caller's responsibility to free `outPath` via NFD_FreePathN() if this function returns NFD_OKAY */
|
||||
/* If defaultPath is NULL, the operating system will decide */
|
||||
nfdresult_t NFD_PickFolderN(char** outPath, const char* defaultPath);
|
||||
|
||||
/* Get last error -- set when nfdresult_t returns NFD_ERROR */
|
||||
/* Returns the last error that was set, or NULL if there is no error. */
|
||||
/* The memory is owned by NFD and should not be freed by user code. */
|
||||
/* This is *always* ASCII printable characters, so it can be interpreted as UTF-8 without any conversion. */
|
||||
const char* NFD_GetError(void);
|
||||
|
||||
nfdresult_t Init();
|
||||
|
||||
/* clear the error */
|
||||
void NFD_ClearError(void);
|
||||
|
||||
/* get the number of entries stored in pathSet */
|
||||
/* note that some paths might be invalid (NFD_ERROR will be returned by NFD_PathSet_GetPath), so we might not actually have this number of usable paths */
|
||||
nfdresult_t NFD_PathSet_GetCount(const nfdpathset_t* pathSet, uint_t* count);
|
||||
|
||||
/* Get the UTF-8 path at offset index */
|
||||
/* It is the caller's responsibility to free `outPath` via NFD_PathSet_FreePathN() if this function returns NFD_OKAY */
|
||||
nfdresult_t NFD_PathSet_GetPathN(const nfdpathset_t* pathSet, uint_t index, char** outPath);
|
||||
|
||||
/* Free the path gotten by NFD_PathSet_GetPathN */
|
||||
void NFD_PathSet_FreePathN(const char* filePath);
|
||||
|
||||
/* Free the pathSet */
|
||||
void NFD_PathSet_Free(const nfdpathset_t* pathSet);
|
||||
|
||||
namespace gtkfd
|
||||
{
|
||||
inline nfdresult_t init() noexcept
|
||||
{
|
||||
return ::Init();
|
||||
}
|
||||
|
||||
inline void Quit() noexcept
|
||||
{
|
||||
::NFD_Quit();
|
||||
}
|
||||
|
||||
inline void FreePath(char *outPath) noexcept
|
||||
{
|
||||
::NFD_FreePathN(outPath);
|
||||
}
|
||||
|
||||
inline nfdresult_t OpenDialog(char*& outPath, const item* filterList = nullptr, uint_t filterCount = 0, const char* defaultPath = nullptr) noexcept
|
||||
{
|
||||
return ::NFD_OpenDialogN(&outPath, filterList, filterCount, defaultPath);
|
||||
}
|
||||
|
||||
inline nfdresult_t OpenDialogMultiple(const nfdpathset_t*& outPaths, const item* filterList = nullptr, uint_t filterCount = 0, const char* defaultPath = nullptr) noexcept
|
||||
{
|
||||
return ::NFD_OpenDialogMultipleN(&outPaths, filterList, filterCount, defaultPath);
|
||||
}
|
||||
|
||||
inline nfdresult_t SaveDialog(char*& outPath, const item* filterList = nullptr, uint_t filterCount = 0, const char* defaultPath = nullptr, const char* defaultName = nullptr) noexcept
|
||||
{
|
||||
return ::NFD_SaveDialogN(&outPath, filterList, filterCount, defaultPath, defaultName);
|
||||
}
|
||||
|
||||
inline nfdresult_t PickFolder(char*& outPath, const char* defaultPath = nullptr) noexcept
|
||||
{
|
||||
return ::NFD_PickFolderN(&outPath, defaultPath);
|
||||
}
|
||||
|
||||
inline const char* GetError() noexcept
|
||||
{
|
||||
return ::NFD_GetError();
|
||||
}
|
||||
|
||||
inline void ClearError() noexcept
|
||||
{
|
||||
::NFD_ClearError();
|
||||
}
|
||||
|
||||
namespace PathSet
|
||||
{
|
||||
inline nfdresult_t Count(const nfdpathset_t* pathSet, uint_t& count) noexcept
|
||||
{
|
||||
return ::NFD_PathSet_GetCount(pathSet, &count);
|
||||
}
|
||||
|
||||
inline nfdresult_t GetPath(const nfdpathset_t* pathSet, uint_t index, char*& outPath) noexcept
|
||||
{
|
||||
return ::NFD_PathSet_GetPathN(pathSet, index, &outPath);
|
||||
}
|
||||
|
||||
inline void FreePath(char* filePath) noexcept
|
||||
{
|
||||
::NFD_PathSet_FreePathN(filePath);
|
||||
}
|
||||
|
||||
inline void Free(const nfdpathset_t* pathSet) noexcept
|
||||
{
|
||||
::NFD_PathSet_Free(pathSet);
|
||||
}
|
||||
}
|
||||
|
||||
class guard
|
||||
{
|
||||
public:
|
||||
inline guard() noexcept { init(); }
|
||||
inline ~guard() noexcept { Quit(); }
|
||||
|
||||
// Not allowed to copy or move this class
|
||||
guard(const guard&) = delete;
|
||||
guard& operator=(const guard&) = delete;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct PathDeleter
|
||||
{
|
||||
inline void operator()(T* ptr) const noexcept { FreePath(ptr); }
|
||||
};
|
||||
|
||||
typedef std::unique_ptr<char, PathDeleter<char>> UniquePath;
|
||||
typedef std::unique_ptr<char, PathDeleter<char>> UniquePathN;
|
||||
typedef std::unique_ptr<char, PathDeleter<char>> UniquePathU8;
|
||||
|
||||
struct PathSetDeleter
|
||||
{
|
||||
inline void operator()(const nfdpathset_t* ptr) const noexcept { PathSet::Free(ptr); }
|
||||
};
|
||||
|
||||
typedef std::unique_ptr<const nfdpathset_t, PathSetDeleter> UniquePathSet;
|
||||
|
||||
template <typename T>
|
||||
struct PathSetPathDeleter
|
||||
{
|
||||
inline void operator()(T* ptr) const noexcept { PathSet::FreePath(ptr); }
|
||||
};
|
||||
|
||||
typedef std::unique_ptr<char, PathSetPathDeleter<char>> UniquePathSetPath;
|
||||
typedef std::unique_ptr<char, PathSetPathDeleter<char>> UniquePathSetPathN;
|
||||
typedef std::unique_ptr<char, PathSetPathDeleter<char>> UniquePathSetPathU8;
|
||||
|
||||
inline nfdresult_t OpenDialog(UniquePathN& outPath, const item* filterList = nullptr, uint_t filterCount = 0, const char* defaultPath = nullptr) noexcept
|
||||
{
|
||||
char* out;
|
||||
nfdresult_t res = OpenDialog(out, filterList, filterCount, defaultPath);
|
||||
if (res == nfdresult_t::OKAY) outPath.reset(out);
|
||||
return res;
|
||||
}
|
||||
|
||||
inline nfdresult_t OpenDialogMultiple(UniquePathSet& outPaths, const item* filterList = nullptr, uint_t filterCount = 0, const char* defaultPath = nullptr) noexcept
|
||||
{
|
||||
const nfdpathset_t* out;
|
||||
nfdresult_t res = OpenDialogMultiple(out, filterList, filterCount, defaultPath);
|
||||
if (res == nfdresult_t::OKAY) outPaths.reset(out);
|
||||
return res;
|
||||
}
|
||||
|
||||
inline nfdresult_t SaveDialog(UniquePathN& outPath, const item* filterList = nullptr, uint_t filterCount = 0, const char* defaultPath = nullptr, const char* defaultName = nullptr) noexcept
|
||||
{
|
||||
char* out;
|
||||
nfdresult_t res = SaveDialog(out, filterList, filterCount, defaultPath, defaultName);
|
||||
if (res == nfdresult_t::OKAY) outPath.reset(out);
|
||||
return res;
|
||||
}
|
||||
|
||||
inline nfdresult_t PickFolder(UniquePathN& outPath, const char* defaultPath = nullptr) noexcept
|
||||
{
|
||||
char* out;
|
||||
nfdresult_t res = PickFolder(out, defaultPath);
|
||||
if (res == nfdresult_t::OKAY) outPath.reset(out);
|
||||
return res;
|
||||
}
|
||||
|
||||
namespace PathSet
|
||||
{
|
||||
inline nfdresult_t Count(const UniquePathSet& uniquePathSet, uint_t& count) noexcept { return Count(uniquePathSet.get(), count); }
|
||||
inline nfdresult_t GetPath(const UniquePathSet& uniquePathSet, uint_t index, UniquePathSetPathN& outPath) noexcept
|
||||
{
|
||||
char* out;
|
||||
nfdresult_t res = GetPath(uniquePathSet.get(), index, out);
|
||||
if (res == nfdresult_t::OKAY) outPath.reset(out);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
42
src/monitor/libs/gtkfd/gtkfd.cpp
Normal file
42
src/monitor/libs/gtkfd/gtkfd.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "gtkfd.hpp"
|
||||
#include <hack/logger/logger.hpp>
|
||||
|
||||
namespace monitor::libs
|
||||
{
|
||||
void WaitForCleanup()
|
||||
{
|
||||
while (gtk_events_pending()) gtk_main_iteration();
|
||||
}
|
||||
|
||||
struct widget_guard
|
||||
{
|
||||
GtkWidget* m_widget;
|
||||
widget_guard(GtkWidget* w) : m_widget(w) {}
|
||||
~widget_guard()
|
||||
{
|
||||
WaitForCleanup();
|
||||
gtk_widget_destroy(m_widget);
|
||||
WaitForCleanup();
|
||||
}
|
||||
};
|
||||
|
||||
std::filesystem::path gtkfd::open(const std::filesystem::path& default_path) noexcept
|
||||
{
|
||||
std::filesystem::path out_path;
|
||||
if (!gtk_init_check(NULL, NULL))
|
||||
{
|
||||
hack::error()("Failed init gtk");
|
||||
return out_path;
|
||||
}
|
||||
|
||||
GtkWidget* widget = gtk_file_chooser_dialog_new("Open File", nullptr, GTK_FILE_CHOOSER_ACTION_OPEN, "_Cancel", GTK_RESPONSE_CANCEL, "_Open", GTK_RESPONSE_ACCEPT, nullptr);
|
||||
widget_guard wg(widget);
|
||||
|
||||
// set the default path
|
||||
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(widget), default_path.c_str());
|
||||
|
||||
if (gtk_dialog_run(GTK_DIALOG(widget)) == GTK_RESPONSE_ACCEPT)
|
||||
out_path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
|
||||
return out_path;
|
||||
}
|
||||
}
|
||||
19
src/monitor/libs/gtkfd/gtkfd.hpp
Normal file
19
src/monitor/libs/gtkfd/gtkfd.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
namespace monitor::libs
|
||||
{
|
||||
class gtkfd
|
||||
{
|
||||
public:
|
||||
gtkfd() = default;
|
||||
~gtkfd() = default;
|
||||
|
||||
public:
|
||||
std::filesystem::path open(const std::filesystem::path& default_path) noexcept;
|
||||
std::filesystem::path save(const std::filesystem::path& default_path, const std::string& default_name) noexcept;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ namespace monitor::utils
|
||||
enum class event_type
|
||||
{
|
||||
CREATE_SNAPSHOT,
|
||||
CREATE_SNAPSHOT_COMPLETED,
|
||||
|
||||
SET_AUDIO_POSITION,
|
||||
AUDIO_PAUSE,
|
||||
AUDIO_STOP,
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <hack/logger/logger.hpp>
|
||||
|
||||
#include "noinc.hpp"
|
||||
|
||||
namespace monitor::utils::var
|
||||
{
|
||||
const std::size_t MAX_RENDER_SIZE = 1'200'000;
|
||||
|
||||
Reference in New Issue
Block a user