396 lines
15 KiB
C++
396 lines
15 KiB
C++
/*
|
|
@todo: popup when error
|
|
@todo: read recursive
|
|
@todo: add option for user to control what files are read
|
|
@todo: pdf files
|
|
@todo: plumbing for custom commands - pdf: {tools_path}/vlc {filename} --start-at {hour}:{second}:{minute}
|
|
@todo: config with preload_files, initial_query, style
|
|
@todo: help menu and config menu
|
|
|
|
@todo: check for number of cores before creating threads
|
|
@todo:
|
|
*/
|
|
|
|
#define BASIC_IMPL
|
|
#include "../basic/basic.h"
|
|
#include "../basic/filesystem.h"
|
|
#include "../basic/thread_queue.h"
|
|
|
|
#include <mutex>
|
|
#include <stdio.h>
|
|
|
|
#include "imgui.h"
|
|
#include "imgui_impl_sdl2.h"
|
|
#include "imgui_impl_opengl3.h"
|
|
#include <SDL.h>
|
|
#include "glad.h"
|
|
|
|
struct FileLoadResult {
|
|
String filename;
|
|
String error;
|
|
};
|
|
|
|
Arena Perm;
|
|
Array<FileLoadResult> XFileLoadResults;
|
|
|
|
#include "config.cpp"
|
|
#include <pdfio.h>
|
|
#include "read_pdf.cpp"
|
|
#include "read_srt.cpp"
|
|
|
|
WorkQueue MainWorkQueue;
|
|
|
|
#include "loading_thread.cpp"
|
|
#include "searching_thread.cpp"
|
|
|
|
void UISearchResults(Array<String> filenames) {
|
|
Scratch scratch;
|
|
Array<String> matches = LockSearchResults();
|
|
defer { UnlockSearchResults(); };
|
|
|
|
float font_size = ImGui::GetFontSize();
|
|
ImFont *font = ImGui::GetFont();
|
|
const ImGuiViewport *main_viewport = ImGui::GetMainViewport();
|
|
|
|
int64_t chars_per_line = (int64_t)(main_viewport->WorkSize.x / 2) / (int64_t)font->GetCharAdvance('_') - strlen(Prompt) * 2;
|
|
|
|
ImGuiListClipper clipper;
|
|
clipper.Begin((int)matches.len);
|
|
while (clipper.Step()) {
|
|
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
|
ImGui::PushID(i);
|
|
defer { ImGui::PopID(); };
|
|
auto &it = matches[i];
|
|
uintptr_t begin_region = (uintptr_t)XArena.data;
|
|
uintptr_t end_region = (uintptr_t)XArena.data + XArena.len;
|
|
|
|
uint64_t begin = (uintptr_t)(it.data - chars_per_line / 2);
|
|
uint64_t end = (uintptr_t)(it.data + it.len + chars_per_line / 2);
|
|
|
|
uint64_t a = Clamp(begin, begin_region, end_region);
|
|
uint64_t b = Clamp((uintptr_t)it.data, begin_region, end_region);
|
|
String left = {(char *)a, (int64_t)(b - a)};
|
|
|
|
uint64_t c = Clamp((uintptr_t)(it.data + it.len), begin_region, end_region);
|
|
uint64_t d = Clamp(end, begin_region, end_region);
|
|
String right = {(char *)c, (int64_t)(d - c)};
|
|
|
|
String middle = it;
|
|
String string = Format(scratch, "%.*s**%.*s**%.*s",
|
|
FmtString(left), FmtString(middle), FmtString(right));
|
|
|
|
XToSource *item = XFindSource(middle);
|
|
if (ImGui::Button(string.data)) {
|
|
String exe_folder = GetExeDir(scratch);
|
|
exe_folder = Format(scratch, "\"%.*s\"", FmtString(exe_folder));
|
|
Array<ReplaceVar> replace_vars = {scratch};
|
|
replace_vars.add({"exe_folder", GetExeDir(scratch)});
|
|
|
|
if (item->kind == SourceKind_SRT) {
|
|
String it = FindVideoForSRT(filenames, item->filepath);
|
|
if (it.len) {
|
|
int seconds = item->srt.hour * 60 * 60 + item->srt.minute * 60 + item->srt.second;
|
|
String copy = Copy(scratch, it);
|
|
for (int i = 0; i < copy.len; i += 1)
|
|
if (copy.data[i] == '/') copy.data[i] = '\\';
|
|
|
|
replace_vars.add({"video", Format(scratch, "\"%.*s\"", FmtString(copy))});
|
|
replace_vars.add({"time_in_seconds", Format(scratch, "%d", seconds)});
|
|
String args = ReplaceVars(scratch, replace_vars, SRTCommand);
|
|
Process process = RunEx(args);
|
|
if (!process.is_valid) XFileLoadResults.bounded_add({"", process.error_message});
|
|
}
|
|
} else if (item->kind == SourceKind_TXT) {
|
|
replace_vars.add({"file", Format(scratch, "\"%.*s\"", FmtString(item->filepath))});
|
|
replace_vars.add({"line", Format(scratch, "%d", item->txt.row + 1)});
|
|
String args = ReplaceVars(scratch, replace_vars, TXTCommand);
|
|
Process process = RunEx(args);
|
|
if (!process.is_valid) XFileLoadResults.bounded_add({"", process.error_message});
|
|
} else if (item->kind == SourceKind_PDF) {
|
|
replace_vars.add({"file", Format(scratch, "\"%.*s\"", FmtString(item->filepath))});
|
|
replace_vars.add({"page", Format(scratch, "%d", item->pdf.page)});
|
|
String args = ReplaceVars(scratch, replace_vars, PDFCommand);
|
|
Process process = RunEx(args);
|
|
if (!process.is_valid) XFileLoadResults.bounded_add({"", process.error_message});
|
|
} else {
|
|
Assert(!"Invalid codepath");
|
|
}
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::Text(SkipToLastSlash(item->filepath).data);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UILoadedFiles() {
|
|
Scratch scratch;
|
|
Array<FileLoadResult> file_load_results = XLockFileLoadResults();
|
|
defer { XUnlockFileResults(); };
|
|
|
|
ImGuiListClipper clipper;
|
|
clipper.Begin((int)file_load_results.len);
|
|
while (clipper.Step()) {
|
|
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
|
FileLoadResult file = file_load_results[file_load_results.len - 1 - i];
|
|
if (file.error.len) {
|
|
ImGui::PushStyleColor(ImGuiCol_Text, (ImVec4)ImColor::HSV(7.0f, 0.6f, 0.6f));
|
|
String string = Format(scratch, "%.*s error: %.*s", FmtString(file.filename), FmtString(file.error));
|
|
ImGui::Text(string.data);
|
|
ImGui::PopStyleColor();
|
|
} else {
|
|
String string = Format(scratch, "%.*s loaded", FmtString(file.filename));
|
|
ImGui::Text(string.data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(int, char **) {
|
|
InitOS();
|
|
InitScratch();
|
|
InitArena(&Perm);
|
|
|
|
// {
|
|
// Scratch scratch;
|
|
// for (FileIter it = IterateFiles(scratch, "D:/pdfs"); IsValid(it); Advance(&it)) {
|
|
// if (it.is_directory || !EndsWith(it.absolute_path, ".pdf", true)) continue;
|
|
// PDF pdf = pdfioReadPDF(scratch, it.absolute_path);
|
|
// Array<String> strings = {scratch};
|
|
// For(pdf.pages) {
|
|
// strings.add(it.string);
|
|
// }
|
|
// String s = Merge(scratch, strings, "\n");
|
|
// WriteFile(Format(scratch, "%.*s.txt", FmtString(it.absolute_path)), s);
|
|
|
|
// String pdf_content = ReadFile(scratch, it.absolute_path);
|
|
// }
|
|
// }
|
|
|
|
XInitLoading();
|
|
InitSearch();
|
|
LoadConfig();
|
|
ThreadStartupInfo infos[16] = {};
|
|
InitWorkQueue(&MainWorkQueue, Lengthof(infos), infos);
|
|
|
|
Array<String> filenames = {};
|
|
|
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) {
|
|
printf("Error: %s\n", SDL_GetError());
|
|
return -1;
|
|
}
|
|
|
|
const char *glsl_version = "#version 150";
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
|
|
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
|
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
|
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
|
SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
|
|
SDL_Window *window = SDL_CreateWindow("Transcript browser", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 960, 600, window_flags);
|
|
if (window == nullptr) {
|
|
printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError());
|
|
return -1;
|
|
}
|
|
|
|
SDL_GLContext gl_context = SDL_GL_CreateContext(window);
|
|
SDL_GL_MakeCurrent(window, gl_context);
|
|
|
|
if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) {
|
|
return -1;
|
|
}
|
|
|
|
SDL_GL_SetSwapInterval(1); // Enable vsync
|
|
|
|
// Setup Dear ImGui context
|
|
IMGUI_CHECKVERSION();
|
|
ImGui::CreateContext();
|
|
ImGuiIO &io = ImGui::GetIO();
|
|
(void)io;
|
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
|
|
|
|
// Setup Dear ImGui style
|
|
// ImGui::StyleColorsDark();
|
|
ImGui::StyleColorsLight();
|
|
|
|
// Setup Platform/Renderer backends
|
|
ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
|
|
ImGui_ImplOpenGL3_Init(glsl_version);
|
|
io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f);
|
|
|
|
{
|
|
String read_on_start = ReadOnStart;
|
|
if (read_on_start != "-") {
|
|
Scratch scratch;
|
|
Array<String> strings = Split(scratch, read_on_start, ";");
|
|
For(strings) XAddFolder(it, &filenames);
|
|
}
|
|
}
|
|
|
|
bool command_menu_open = false;
|
|
bool show_loaded_files = false;
|
|
bool set_focus_to_input = true;
|
|
int64_t frame = -1;
|
|
|
|
// Main loop
|
|
bool done = false;
|
|
while (!done) {
|
|
Scratch frame_arena;
|
|
frame += 1;
|
|
if (frame > 4) set_focus_to_input = false;
|
|
|
|
bool set_focus_to_list = false;
|
|
bool tab_press = false;
|
|
bool enter_press = false;
|
|
bool f1_press = false;
|
|
bool f2_press = false;
|
|
SDL_Event event;
|
|
#if 1
|
|
while (SDL_PollEvent(&event)) {
|
|
#else
|
|
if (SDL_WaitEvent(&event)) {
|
|
#endif
|
|
ImGui_ImplSDL2_ProcessEvent(&event);
|
|
if (event.type == SDL_KEYDOWN) {
|
|
switch (event.key.keysym.sym) {
|
|
case SDLK_UP:
|
|
case SDLK_DOWN:
|
|
case SDLK_PAGEUP:
|
|
case SDLK_PAGEDOWN:
|
|
set_focus_to_list = true;
|
|
break;
|
|
case SDLK_BACKSPACE:
|
|
case SDLK_DELETE:
|
|
set_focus_to_input = true;
|
|
break;
|
|
case SDLK_RETURN:
|
|
enter_press = true;
|
|
break;
|
|
case SDLK_TAB:
|
|
tab_press = true;
|
|
break;
|
|
case SDLK_F1:
|
|
f1_press = true;
|
|
break;
|
|
case SDLK_F2:
|
|
f2_press = true;
|
|
break;
|
|
}
|
|
} else if (event.type == SDL_TEXTINPUT) {
|
|
set_focus_to_input = true;
|
|
} else if (event.type == SDL_QUIT) {
|
|
done = true;
|
|
} else if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) {
|
|
done = true;
|
|
}
|
|
}
|
|
|
|
ImGui_ImplOpenGL3_NewFrame();
|
|
ImGui_ImplSDL2_NewFrame();
|
|
ImGui::NewFrame();
|
|
|
|
#if 0
|
|
ImGui::ShowDemoWindow();
|
|
#else
|
|
{
|
|
|
|
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse;
|
|
const ImGuiViewport *main_viewport = ImGui::GetMainViewport();
|
|
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
|
|
ImGui::SetNextWindowSize(ImVec2(main_viewport->Size.x, 40), ImGuiCond_Always);
|
|
|
|
ImGui::Begin("input", NULL, window_flags);
|
|
|
|
if (show_loaded_files) {
|
|
if (ImGui::Button("Show files (F1)") || f1_press) {
|
|
show_loaded_files = !show_loaded_files;
|
|
}
|
|
} else {
|
|
if (ImGui::Button("Hide files (F1)") || f1_press) {
|
|
show_loaded_files = !show_loaded_files;
|
|
}
|
|
}
|
|
ImGui::SameLine();
|
|
|
|
if (command_menu_open) {
|
|
if (ImGui::Button("Hide config (F2)") || f2_press) {
|
|
command_menu_open = !command_menu_open;
|
|
}
|
|
} else {
|
|
if (ImGui::Button("Show config (F2)") || f2_press) {
|
|
command_menu_open = !command_menu_open;
|
|
}
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
if (!command_menu_open && set_focus_to_input) ImGui::SetKeyboardFocusHere(0);
|
|
if (tab_press && ImGui::IsWindowFocused()) set_focus_to_list = true;
|
|
if (ImGui::InputText("Input your query", Prompt, sizeof(Prompt))) {
|
|
StartSearchingForMatches();
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
if (enter_press) {
|
|
String prompt = Prompt;
|
|
if (StartsWith(prompt, "read=")) {
|
|
XAddFolder(prompt.skip(5), &filenames);
|
|
memset(Prompt, 0, sizeof(Prompt));
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse;
|
|
const ImGuiViewport *main_viewport = ImGui::GetMainViewport();
|
|
ImGui::SetNextWindowPos(ImVec2(0, 35), ImGuiCond_Always);
|
|
ImGui::SetNextWindowSize(ImVec2(main_viewport->Size.x, main_viewport->Size.y - 35), ImGuiCond_Always);
|
|
|
|
ImGui::Begin("result", NULL, window_flags);
|
|
if (!ImGui::IsWindowFocused() && set_focus_to_list) ImGui::SetKeyboardFocusHere(0);
|
|
|
|
if (command_menu_open) {
|
|
ImGui::InputText(".srt", SRTCommand, sizeof(SRTCommand));
|
|
ImGui::InputText(".pdf", PDFCommand, sizeof(PDFCommand));
|
|
ImGui::InputText(".txt", TXTCommand, sizeof(TXTCommand));
|
|
ImGui::InputText("Folders to read during startup", ReadOnStart, sizeof(ReadOnStart));
|
|
if (ImGui::Button("Save config")) {
|
|
SaveConfig();
|
|
LoadConfig();
|
|
}
|
|
} else {
|
|
if (show_loaded_files) {
|
|
UILoadedFiles();
|
|
} else {
|
|
UISearchResults(filenames);
|
|
}
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
#endif
|
|
|
|
ImGui::Render();
|
|
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
|
|
glClearColor(1, 1, 1, 1);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
|
SDL_GL_SwapWindow(window);
|
|
SDL_Delay(16);
|
|
}
|
|
|
|
// Cleanup
|
|
ImGui_ImplOpenGL3_Shutdown();
|
|
ImGui_ImplSDL2_Shutdown();
|
|
ImGui::DestroyContext();
|
|
|
|
SDL_GL_DeleteContext(gl_context);
|
|
SDL_DestroyWindow(window);
|
|
SDL_Quit();
|
|
|
|
return 0;
|
|
} |