logmeow/fragments/filters.cpp

269 lines
11 KiB
C++

#include <imgui/imgui.h>
#include <imgui/misc/cpp/imgui_stdlib.h>
#include "../group_panel.h"
#include "../filters.h"
#include "../config.h"
#include "ok_buttons_fragment.h"
#include "filters.h"
static inline void render_integer_filter(IntegerFilter* filter);
static inline void render_string_filter(StringFilter* filter);
static inline void render_buffer_filter(BufferFilter* filter);
static inline void render_priority_filter(PriorityFilter* filter);
static inline void render_group_filter(GroupFilter* filter);
static std::unique_ptr<Filter> render_add_filter_popup();
static void update_logcat_entries(const Config& active_config,
const std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets);
static void try_write_config(const Config& config);
static void render_filter(Filter* filter, std::string* title, bool* request_removal) {
ImGui::PushID(filter);
ImGui::BeginGroupPanel();
if (title) {
ImGui::AlignTextToFramePadding();
ImGui::Text("Title:%s%s", title->empty() ? "" : " ", title->c_str());
ImGui::SameLine();
bool popup_just_opened = false;
if (ImGui::Button("Edit")) {
ImGui::OpenPopup("change_filter_title");
popup_just_opened = true;
}
if (ImGui::BeginPopup("change_filter_title")) {
ImGui::SetKeyboardFocusHere();
bool enter_pressed = ImGui::InputText("##title", title, ImGuiInputTextFlags_EnterReturnsTrue);
if (enter_pressed || (!popup_just_opened && !ImGui::IsItemActive())) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
ImGui::SameLine();
}
if (ImGui::Button("Remove")) {
*request_removal = true;
}
{
ImGui::SameLine();
bool disabled = filter->disabled();
if (ImGui::Checkbox("Disabled", &disabled)) {
filter->disabled(disabled);
}
}
{
StringFilter* sfilter = dynamic_cast<StringFilter*>(filter);
IntegerFilter* ifilter = !sfilter ? dynamic_cast<IntegerFilter*>(filter) : nullptr;
if (sfilter || ifilter) {
ImGui::SameLine();
ImGui::Checkbox("Inverted", sfilter ? &sfilter->inverted : &ifilter->inverted);
}
if (sfilter) {
ImGui::SameLine();
ImGui::Checkbox("Exact match", &sfilter->exact_match);
}
}
if (filter->error()) {
ImGui::SameLine();
ImGui::Text("(%s)", filter->error()->c_str());
}
if (IntegerFilter* ifilter = dynamic_cast<IntegerFilter*>(filter)) {
render_integer_filter(ifilter);
} else if (StringFilter* sfilter = dynamic_cast<StringFilter*>(filter)) {
render_string_filter(sfilter);
} else if (BufferFilter* bfilter = dynamic_cast<BufferFilter*>(filter)) {
render_buffer_filter(bfilter);
} else if (PriorityFilter* pfilter = dynamic_cast<PriorityFilter*>(filter)) {
render_priority_filter(pfilter);
} else if (GroupFilter* gfilter = dynamic_cast<GroupFilter*>(filter)) {
render_group_filter(gfilter);
} else {
ImGui::TextUnformatted("An unknown filter, this probably shouldn't be mutated");
}
ImGui::EndGroupPanel();
ImGui::PopID();
}
static inline void render_integer_filter(IntegerFilter* filter) {
const char* head;
switch (filter->key) {
case FilterKey::PID: head = "PID"; break;
case FilterKey::TID: head = "TID"; break;
default: head = "Something";
};
ImGui::AlignTextToFramePadding();
ImGui::Text("%s is%s", head, filter->inverted ? " not" : "");
ImGui::SameLine();
if (ImGui::InputScalar("##int", ImGuiDataType_U64, &filter->other)) {
filter->updated();
}
}
static inline void render_string_filter(StringFilter* filter) {
const char* head;
switch (filter->key) {
case FilterKey::User: head = "User"; break;
case FilterKey::Tag: head = "Tag"; break;
case FilterKey::Message: head = "Message"; break;
default: head = "Something";
};
ImGui::AlignTextToFramePadding();
ImGui::Text("%s %s", head, filter->exact_match
? (filter->inverted ? "is not" : "is")
: (filter->inverted ? "doesn't have" : "has"));
ImGui::SameLine();
if (ImGui::InputText("##str", &filter->other)) {
filter->updated();
}
}
static inline void render_buffer_filter(BufferFilter* filter) {
auto update_if_needed = [&](bool updated) {
if (updated) {
filter->updated();
}
};
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted("Buffer is:"); ImGui::SameLine();
update_if_needed(ImGui::CheckboxFlags("Unknown", &filter->wanted, static_cast<unsigned int>(Buffer::Unknown))); ImGui::SameLine();
update_if_needed(ImGui::CheckboxFlags("Main", &filter->wanted, static_cast<unsigned int>(Buffer::Main))); ImGui::SameLine();
update_if_needed(ImGui::CheckboxFlags("System", &filter->wanted, static_cast<unsigned int>(Buffer::System))); ImGui::SameLine();
update_if_needed(ImGui::CheckboxFlags("Radio", &filter->wanted, static_cast<unsigned int>(Buffer::Radio))); ImGui::SameLine();
update_if_needed(ImGui::CheckboxFlags("Events", &filter->wanted, static_cast<unsigned int>(Buffer::Events))); ImGui::SameLine();
update_if_needed(ImGui::CheckboxFlags("Crash", &filter->wanted, static_cast<unsigned int>(Buffer::Crash)));
}
static inline void render_priority_filter(PriorityFilter* filter) {
auto update_if_needed = [&](bool updated) {
if (updated) {
filter->updated();
}
};
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted("Priority is:"); ImGui::SameLine();
update_if_needed(ImGui::CheckboxFlags("Unknown", &filter->wanted, static_cast<unsigned int>(Priority::Unknown))); ImGui::SameLine();
update_if_needed(ImGui::CheckboxFlags("Verbose", &filter->wanted, static_cast<unsigned int>(Priority::Verbose))); ImGui::SameLine();
update_if_needed(ImGui::CheckboxFlags("Debug", &filter->wanted, static_cast<unsigned int>(Priority::Debug))); ImGui::SameLine();
update_if_needed(ImGui::CheckboxFlags("Info", &filter->wanted, static_cast<unsigned int>(Priority::Info))); ImGui::SameLine();
update_if_needed(ImGui::CheckboxFlags("Warning", &filter->wanted, static_cast<unsigned int>(Priority::Warn))); ImGui::SameLine();
update_if_needed(ImGui::CheckboxFlags("Error", &filter->wanted, static_cast<unsigned int>(Priority::Error))); ImGui::SameLine();
update_if_needed(ImGui::CheckboxFlags("Fatal", &filter->wanted, static_cast<unsigned int>(Priority::Fatal)));
}
static inline void render_group_filter(GroupFilter* filter) {
int selected_type = static_cast<int>(filter->type);
if (ImGui::Combo("of these are true", &selected_type, "All\0Any\0One\0None\0")) {
filter->type = static_cast<GroupFilter::Type>(selected_type);
}
for (std::vector<std::unique_ptr<Filter>>::iterator it = filter->filters.begin(); it != filter->filters.end();) {
bool removal_requested = false;
render_filter(it->get(), nullptr, &removal_requested);
if (removal_requested) {
filter->filters.erase(it);
filter->updated();
} else {
it++;
}
}
if (ImGui::Button("+ add filter")) {
ImGui::OpenPopup("add_filter");
}
if (ImGui::BeginPopup("add_filter")) {
std::unique_ptr<Filter> added_filter = render_add_filter_popup();
if (added_filter) {
filter->filters.push_back(std::move(added_filter));
filter->updated();
}
ImGui::EndPopup();
}
}
static std::unique_ptr<Filter> render_add_filter_popup() {
if (ImGui::Selectable("Buffer")) {
return std::make_unique<BufferFilter>(0, false);
} else if (ImGui::Selectable("User")) {
return std::make_unique<StringFilter>(FilterKey::User, "", false, true, false);
} else if (ImGui::Selectable("PID")) {
return std::make_unique<IntegerFilter>(FilterKey::PID, 0, false, false);
} else if (ImGui::Selectable("TID")) {
return std::make_unique<IntegerFilter>(FilterKey::TID, 0, false, false);
} else if (ImGui::Selectable("Priority")) {
return std::make_unique<PriorityFilter>(0, false);
} else if (ImGui::Selectable("Tag")) {
return std::make_unique<StringFilter>(FilterKey::Tag, "", false, true, false);
} else if (ImGui::Selectable("Message")) {
return std::make_unique<StringFilter>(FilterKey::Message, "", false, true, false);
} else if (ImGui::Selectable("Group of filters")) {
return std::make_unique<GroupFilter>(std::vector<std::unique_ptr<Filter>>(), GroupFilter::Type::All, false);
}
return std::unique_ptr<Filter>();
}
static void update_logcat_entries(const Config& active_config,
const std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets) {
filtered_logcat_entry_offsets.clear();
for (size_t i=0; i < logcat_entries.size(); i++) {
if (matches(logcat_entries[i], active_config.filters, active_config.exclusions)) {
filtered_logcat_entry_offsets.push_back(i);
}
}
}
static void try_write_config(const Config& config) {
try {
write_config(config);
} catch (const std::exception& e) {
log(std::string("Failed to write config: ") + e.what());
}
}
void filters_fragment(Config& active_config, Filters& __restrict active_filters, Filters& __restrict inactive_filters,
const std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets,
bool* p_open) {
ImGui::TextWrapped("You can use regex for strings by prepending \"regex:\". "
"While editing titles, press Enter to use the new title or press Escape to keep the old one");
for (Filters::iterator it = inactive_filters.begin(); it != inactive_filters.end();) {
bool removal_requested = false;
render_filter(it->second.get(), &it->first, &removal_requested);
if (removal_requested) {
inactive_filters.erase(it);
} else {
it++;
}
}
if (ImGui::Button("+ add filter")) {
ImGui::OpenPopup("add_filter");
}
if (ImGui::BeginPopup("add_filter")) {
std::unique_ptr<Filter> added_filter = render_add_filter_popup();
if (added_filter) {
inactive_filters.push_back(std::make_pair("", std::move(added_filter)));
}
ImGui::EndPopup();
}
ImGui::Separator();
ok_buttons_fragment(p_open, [&]() {
active_filters = std::move(inactive_filters);
try_write_config(active_config);
update_logcat_entries(active_config, logcat_entries, filtered_logcat_entry_offsets);
}, [&]() {
copy_filters(active_filters, inactive_filters);
try_write_config(active_config);
update_logcat_entries(active_config, logcat_entries, filtered_logcat_entry_offsets);
});
}