diff --git a/logcat_entry.cpp b/logcat_entry.cpp index 12da149..2224a5d 100644 --- a/logcat_entry.cpp +++ b/logcat_entry.cpp @@ -32,6 +32,44 @@ const char* to_string(Buffer buffer) { } } +static std::string leftpad(const std::string& str, size_t characters) { + return str.size() >= characters + ? str + : std::string(characters - str.size(), ' ') + str; +} + +static inline char to_char(Priority priority) { + switch (priority) { + case Priority::Verbose: return 'V'; + case Priority::Debug: return 'D'; + case Priority::Info: return 'I'; + case Priority::Warn: return 'W'; + case Priority::Error: return 'E'; + case Priority::Fatal: return 'F'; + case Priority::Unknown: return 'U'; + } +} + +static inline std::string rightpad(const std::string& str, size_t characters) { + return str.size() >= characters + ? str + : str + std::string(characters - str.size(), ' '); +} + +std::string to_string(const LogcatEntry& logcat_entry) { + char logcat_style_time_as_str[32] = {0}; + struct tm tm; + strftime(logcat_style_time_as_str, 31 * sizeof(char), "%Y-%m-%d %H:%M:%S", localtime_r(&logcat_entry.time, &tm)); + + return std::string(logcat_style_time_as_str) + + ' ' + leftpad(logcat_entry.user.value_or(" "), 5) + + ' ' + leftpad(std::to_string(logcat_entry.pid), 5) + + ' ' + leftpad(std::to_string(logcat_entry.tid), 5) + + ' ' + to_char(logcat_entry.priority) + + ' ' + rightpad(logcat_entry.tag, 5) + + ": " + logcat_entry.message; +} + static inline Priority priority_from(char c) { switch (c) { case 'V': return Priority::Verbose; diff --git a/logcat_entry.h b/logcat_entry.h index 0c61ef7..f599b92 100644 --- a/logcat_entry.h +++ b/logcat_entry.h @@ -36,6 +36,7 @@ struct LogcatEntry { const char* to_string(Priority priority); const char* to_string(Buffer buffer); +std::string to_string(const LogcatEntry& logcat_entry); std::optional try_parse_logcat_entry(char* buf, size_t length, Buffer buffer); std::optional try_parse_buffer(char* buf, size_t length); diff --git a/windows/logs.cpp b/windows/logs.cpp index 2fba225..4bae7d7 100644 --- a/windows/logs.cpp +++ b/windows/logs.cpp @@ -5,6 +5,32 @@ #include "../log.h" #include "logs.h" +static inline void render_table_item(const LogEntry& log_entry, size_t log_entry_index) { + struct tm tm; + char time_as_str[128] = {0}; + strftime(time_as_str, 127 * sizeof(char), "%c", localtime_r(&log_entry.time, &tm)); + auto table_item_popup = [&](unsigned char index) { + // is it safe to use the index as a id? i mean, you can't access the clear button when the popup is active + std::string id = std::to_string(log_entry_index * 10 + index); + if (ImGui::BeginPopupContextItem(id.c_str())) { + ImGui::TextDisabled("[%s] %s", time_as_str, log_entry.message.c_str()); + ImGui::Separator(); + ImGui::PushFont(nullptr); + + if (ImGui::Selectable("Copy")) ImGui::SetClipboardText(to_string(log_entry).c_str()); + if (ImGui::Selectable("Copy Time")) ImGui::SetClipboardText(time_as_str); + if (ImGui::Selectable("Copy Message")) ImGui::SetClipboardText(log_entry.message.c_str()); + + ImGui::PopFont(); + ImGui::EndPopup(); + } + }; + + ImGui::TableNextRow(); + if (ImGui::TableSetColumnIndex(0)) { ImGui::TextUnformatted(time_as_str); table_item_popup(0); } + if (ImGui::TableSetColumnIndex(1)) { ImGui::TextUnformatted(log_entry.message); table_item_popup(1); } +} + static inline void render_table(ImFont* monospace_font, bool* autoscrolling) { ImGui::TableSetupScrollFreeze(0, 1); ImGui::TableSetupColumn("Time", ImGuiTableColumnFlags_None); @@ -21,14 +47,7 @@ static inline void render_table(ImFont* monospace_font, bool* autoscrolling) { assert(i_u >= 0); size_t i = static_cast(i_u); - const LogEntry* log_entry = &log_entries[i]; - struct tm tm; - char time_as_str[128] = {0}; - strftime(time_as_str, 127 * sizeof(char), "%c", localtime_r(&log_entry->time, &tm)); - - ImGui::TableNextRow(); - if (ImGui::TableSetColumnIndex(0)) ImGui::TextUnformatted(time_as_str); - if (ImGui::TableSetColumnIndex(1)) ImGui::TextUnformatted(log_entry->message); + render_table_item(log_entries[i], i); } } clipper.End(); diff --git a/windows/main.cpp b/windows/main.cpp index 9aebc3a..6911bc5 100644 --- a/windows/main.cpp +++ b/windows/main.cpp @@ -8,6 +8,49 @@ #include "../logcat_thread.h" #include "main.h" +static inline void render_table_item(const LogcatEntry& logcat_entry, size_t logcat_entry_index) { + struct tm tm; + char time_as_str[128] = {0}; + strftime(time_as_str, 127 * sizeof(char), "%c", localtime_r(&logcat_entry.time, &tm)); + auto table_item_popup = [&](unsigned char index) { + // is it safe to use the index as a id? i mean, you can't access the clear button when the popup is active + std::string id = std::to_string(logcat_entry_index * 10 + index); + if (ImGui::BeginPopupContextItem(id.c_str())) { + std::string text = to_string(logcat_entry); + ImGui::TextDisabled("%s", text.c_str()); + ImGui::Separator(); + ImGui::PushFont(nullptr); + + if (ImGui::Selectable("Copy")) ImGui::SetClipboardText(text.c_str()); + if (ImGui::Selectable("Copy User", false, logcat_entry.user ? 0 : ImGuiSelectableFlags_Disabled)) { + ImGui::SetClipboardText(logcat_entry.user->c_str()); + } + if (ImGui::Selectable("Copy PID")) ImGui::SetClipboardText(std::to_string(logcat_entry.pid).c_str()); + if (ImGui::Selectable("Copy TID")) ImGui::SetClipboardText(std::to_string(logcat_entry.tid).c_str()); + if (ImGui::Selectable("Copy Buffer")) ImGui::SetClipboardText(to_string(logcat_entry.buffer)); + if (ImGui::Selectable("Copy Priority")) ImGui::SetClipboardText(to_string(logcat_entry.priority)); + if (ImGui::Selectable("Copy Tag")) ImGui::SetClipboardText(logcat_entry.tag.c_str()); + if (ImGui::Selectable("Copy Message")) ImGui::SetClipboardText(logcat_entry.message.c_str()); + + ImGui::PopFont(); + ImGui::EndPopup(); + } + }; + + ImGui::TableNextRow(); + if (ImGui::TableSetColumnIndex(0)) { ImGui::TextUnformatted(time_as_str); table_item_popup(0); } + if (logcat_entry.user && ImGui::TableSetColumnIndex(1)) { + ImGui::TextUnformatted(*logcat_entry.user); + table_item_popup(1); + } + if (ImGui::TableSetColumnIndex(2)) { ImGui::Text("%zu", logcat_entry.pid); table_item_popup(2); } + if (ImGui::TableSetColumnIndex(3)) { ImGui::Text("%zu", logcat_entry.tid); table_item_popup(3); } + if (ImGui::TableSetColumnIndex(4)) { ImGui::TextUnformatted(to_string(logcat_entry.buffer)); table_item_popup(4); } + if (ImGui::TableSetColumnIndex(5)) { ImGui::TextUnformatted(to_string(logcat_entry.priority)); table_item_popup(5); } + if (ImGui::TableSetColumnIndex(6)) { ImGui::TextUnformatted(logcat_entry.tag); table_item_popup(6); } + if (ImGui::TableSetColumnIndex(7)) { ImGui::TextUnformatted(logcat_entry.message); table_item_popup(7); } +} + static inline void render_table(ImFont* monospace_font, std::vector& logcat_entries, std::vector& filtered_logcat_entry_offsets) { ImGui::TableSetupScrollFreeze(0, 1); ImGui::TableSetupColumn("Time", ImGuiTableColumnFlags_None); @@ -25,23 +68,12 @@ static inline void render_table(ImFont* monospace_font, std::vector ImGuiListClipper clipper; clipper.Begin(static_cast(filtered_logcat_entry_offsets.size())); while (clipper.Step()) { - for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { - const LogcatEntry* logcat_entry = &logcat_entries[filtered_logcat_entry_offsets[static_cast(i)]]; - struct tm tm; - char time_as_str[128] = {0}; - strftime(time_as_str, 127 * sizeof(char), "%c", localtime_r(&logcat_entry->time, &tm)); + for (int i_u = clipper.DisplayStart; i_u < clipper.DisplayEnd; i_u++) { + assert(i_u >= 0); + size_t i = static_cast(i_u); - ImGui::TableNextRow(); - if (ImGui::TableSetColumnIndex(0)) ImGui::TextUnformatted(time_as_str); - if (logcat_entry->user && ImGui::TableSetColumnIndex(1)) { - ImGui::TextUnformatted(*logcat_entry->user); - } - if (ImGui::TableSetColumnIndex(2)) ImGui::Text("%zu", logcat_entry->pid); - if (ImGui::TableSetColumnIndex(3)) ImGui::Text("%zu", logcat_entry->tid); - if (ImGui::TableSetColumnIndex(4)) ImGui::TextUnformatted(to_string(logcat_entry->buffer)); - if (ImGui::TableSetColumnIndex(5)) ImGui::TextUnformatted(to_string(logcat_entry->priority)); - if (ImGui::TableSetColumnIndex(6)) ImGui::TextUnformatted(logcat_entry->tag); - if (ImGui::TableSetColumnIndex(7)) ImGui::TextUnformatted(logcat_entry->message); + size_t logcat_entry_index = filtered_logcat_entry_offsets[i]; + render_table_item(logcat_entries[logcat_entry_index], logcat_entry_index); } } clipper.End();