summaryrefslogtreecommitdiffstats
path: root/src/breadcrumb_curses.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/breadcrumb_curses.cc278
1 files changed, 180 insertions, 98 deletions
diff --git a/src/breadcrumb_curses.cc b/src/breadcrumb_curses.cc
index 6481e6b..6232221 100644
--- a/src/breadcrumb_curses.cc
+++ b/src/breadcrumb_curses.cc
@@ -31,15 +31,19 @@
#include "base/itertools.hh"
#include "itertools.similar.hh"
-#include "log_format_fwd.hh"
-#include "logfile.hh"
using namespace lnav::roles::literals;
+void
+breadcrumb_curses::no_op_action(breadcrumb_curses&)
+{
+}
+
breadcrumb_curses::breadcrumb_curses()
{
this->bc_match_search_overlay.sos_parent = this;
this->bc_match_source.set_reverse_selection(true);
+ this->bc_match_view.set_title("breadcrumb popup");
this->bc_match_view.set_selectable(true);
this->bc_match_view.set_overlay_source(&this->bc_match_search_overlay);
this->bc_match_view.set_sub_source(&this->bc_match_source);
@@ -49,11 +53,11 @@ breadcrumb_curses::breadcrumb_curses()
this->add_child_view(&this->bc_match_view);
}
-void
+bool
breadcrumb_curses::do_update()
{
if (!this->bc_line_source) {
- return;
+ return false;
}
size_t crumb_index = 0;
@@ -66,44 +70,49 @@ breadcrumb_curses::do_update()
{
this->bc_last_selected_crumb = crumbs.size() - 1;
}
+ this->bc_displayed_crumbs.clear();
attr_line_t crumbs_line;
for (const auto& crumb : crumbs) {
- auto accum_width
- = utf8_string_length(crumbs_line.get_string()).template unwrap();
- auto elem_width = utf8_string_length(crumb.c_display_value.get_string())
- .template unwrap();
+ auto accum_width = crumbs_line.column_width();
+ auto elem_width = crumb.c_display_value.column_width();
auto is_selected = this->bc_selected_crumb
&& (crumb_index == this->bc_selected_crumb.value());
if (is_selected && ((accum_width + elem_width) > width)) {
crumbs_line.clear();
- crumbs_line.append("\u22ef\u276d"_breadcrumb);
+ crumbs_line.append("\u22ef\uff1a"_breadcrumb);
accum_width = 2;
}
+ line_range crumb_range;
+ crumb_range.lr_start = (int) crumbs_line.length();
crumbs_line.append(crumb.c_display_value);
+ crumb_range.lr_end = (int) crumbs_line.length();
if (is_selected) {
sel_crumb_offset = accum_width;
crumbs_line.get_attrs().emplace_back(
- line_range{
- (int) (crumbs_line.length()
- - crumb.c_display_value.length()),
- (int) crumbs_line.length(),
- },
- VC_STYLE.template value(text_attrs{A_REVERSE}));
+ crumb_range, VC_STYLE.template value(text_attrs{A_REVERSE}));
}
+
+ this->bc_displayed_crumbs.emplace_back(
+ line_range{(int) accum_width,
+ (int) (accum_width + elem_width),
+ line_range::unit::codepoint},
+ crumb_index);
crumb_index += 1;
- crumbs_line.append("\u276d"_breadcrumb);
+ crumbs_line.append(" \uff1a"_breadcrumb);
}
line_range lr{0, static_cast<int>(width)};
view_curses::mvwattrline(
- this->bc_window, this->bc_y, 0, crumbs_line, lr, role_t::VCR_STATUS);
+ this->bc_window, this->vc_y, 0, crumbs_line, lr, role_t::VCR_STATUS);
if (this->bc_selected_crumb) {
this->bc_match_view.set_x(sel_crumb_offset);
}
view_curses::do_update();
+
+ return true;
}
void
@@ -123,13 +132,7 @@ breadcrumb_curses::reload_data()
[](const auto& elem) { return elem.p_key; },
this->bc_current_search,
128)
- | lnav::itertools::sort_with([](const auto& lhs, const auto& rhs) {
- return strnatcasecmp(lhs.p_key.size(),
- lhs.p_key.data(),
- rhs.p_key.size(),
- rhs.p_key.data())
- < 0;
- });
+ | lnav::itertools::sort_with(breadcrumb::possibility::sort_cmp);
if (selected_crumb_ref.c_key.is<std::string>()
&& selected_crumb_ref.c_expected_input
!= breadcrumb::crumb::expected_input_t::anything)
@@ -175,6 +178,9 @@ breadcrumb_curses::reload_data()
this->bc_match_view.set_needs_update();
this->bc_match_view.set_selection(
vis_line_t(selected_value.value_or(-1_vl)));
+ if (selected_value) {
+ this->bc_match_view.set_top(vis_line_t(selected_value.value()));
+ }
this->bc_match_view.reload_data();
this->set_needs_update();
}
@@ -182,6 +188,7 @@ breadcrumb_curses::reload_data()
void
breadcrumb_curses::focus()
{
+ this->bc_match_view.set_y(this->vc_y + 1);
this->bc_focused_crumbs = this->bc_line_source();
if (this->bc_focused_crumbs.empty()) {
return;
@@ -217,7 +224,7 @@ breadcrumb_curses::handle_key(int ch)
bool retval = false;
switch (ch) {
- case KEY_CTRL_A:
+ case KEY_CTRL('a'):
if (this->bc_selected_crumb) {
this->bc_selected_crumb = 0;
this->bc_current_search.clear();
@@ -225,7 +232,7 @@ breadcrumb_curses::handle_key(int ch)
}
retval = true;
break;
- case KEY_CTRL_E:
+ case KEY_CTRL('e'):
if (this->bc_selected_crumb) {
this->bc_selected_crumb = this->bc_focused_crumbs.size() - 1;
this->bc_current_search.clear();
@@ -278,22 +285,26 @@ breadcrumb_curses::handle_key(int ch)
retval = true;
break;
case KEY_NPAGE:
- this->bc_match_view.shift_selection(3);
+ this->bc_match_view.shift_selection(
+ listview_curses::shift_amount_t::down_page);
retval = true;
break;
case KEY_PPAGE:
- this->bc_match_view.shift_selection(-3);
+ this->bc_match_view.shift_selection(
+ listview_curses::shift_amount_t::up_page);
retval = true;
break;
case KEY_UP:
- this->bc_match_view.shift_selection(-1);
+ this->bc_match_view.shift_selection(
+ listview_curses::shift_amount_t::up_line);
retval = true;
break;
case KEY_DOWN:
- this->bc_match_view.shift_selection(1);
+ this->bc_match_view.shift_selection(
+ listview_curses::shift_amount_t::down_line);
retval = true;
break;
- case 0x7f:
+ case KEY_DELETE:
case KEY_BACKSPACE:
if (!this->bc_current_search.empty()) {
this->bc_current_search.pop_back();
@@ -305,6 +316,8 @@ breadcrumb_curses::handle_key(int ch)
case '\r':
this->perform_selection(perform_behavior_t::always);
break;
+ case KEY_ESCAPE:
+ break;
default:
if (isprint(ch)) {
this->bc_current_search.push_back(ch);
@@ -371,40 +384,55 @@ breadcrumb_curses::perform_selection(
}
bool
-breadcrumb_curses::search_overlay_source::list_value_for_overlay(
- const listview_curses& lv,
- int y,
- int bottom,
- vis_line_t line,
- attr_line_t& value_out)
+breadcrumb_curses::search_overlay_source::list_static_overlay(
+ const listview_curses& lv, int y, int bottom, attr_line_t& value_out)
{
- if (y == 0) {
- auto* parent = this->sos_parent;
- auto sel_opt = parent->bc_focused_crumbs
- | lnav::itertools::nth(parent->bc_selected_crumb);
- auto exp_input = sel_opt
- | lnav::itertools::map(&breadcrumb::crumb::c_expected_input)
- | lnav::itertools::unwrap_or(
- breadcrumb::crumb::expected_input_t::exact);
-
- value_out.with_attr_for_all(VC_STYLE.value(text_attrs{A_UNDERLINE}));
-
- if (!parent->bc_current_search.empty()) {
- value_out = parent->bc_current_search;
-
- role_t combobox_role = role_t::VCR_STATUS;
- switch (exp_input) {
- case breadcrumb::crumb::expected_input_t::exact:
- if (parent->bc_similar_values.empty()) {
- combobox_role = role_t::VCR_ALERT_STATUS;
- }
- break;
- case breadcrumb::crumb::expected_input_t::index: {
- size_t index;
+ if (y != 0) {
+ return false;
+ }
+ auto* parent = this->sos_parent;
+ auto sel_opt = parent->bc_focused_crumbs
+ | lnav::itertools::nth(parent->bc_selected_crumb);
+ auto exp_input = sel_opt
+ | lnav::itertools::map(&breadcrumb::crumb::c_expected_input)
+ | lnav::itertools::unwrap_or(
+ breadcrumb::crumb::expected_input_t::exact);
+
+ value_out.with_attr_for_all(VC_STYLE.value(text_attrs{A_UNDERLINE}));
- if (sscanf(parent->bc_current_search.c_str(), "%zu", &index)
- != 1
- || index < 0
+ if (!parent->bc_current_search.empty()) {
+ value_out = parent->bc_current_search;
+
+ role_t combobox_role = role_t::VCR_STATUS;
+ switch (exp_input) {
+ case breadcrumb::crumb::expected_input_t::exact:
+ if (parent->bc_similar_values.empty()) {
+ combobox_role = role_t::VCR_ALERT_STATUS;
+ }
+ break;
+ case breadcrumb::crumb::expected_input_t::index: {
+ size_t index;
+
+ if (sscanf(parent->bc_current_search.c_str(), "%zu", &index)
+ != 1
+ || index < 0
+ || (index
+ >= (sel_opt | lnav::itertools::map([](const auto& cr) {
+ return cr->c_possible_range.value_or(0);
+ })
+ | lnav::itertools::unwrap_or(size_t{0}))))
+ {
+ combobox_role = role_t::VCR_ALERT_STATUS;
+ }
+ break;
+ }
+ case breadcrumb::crumb::expected_input_t::index_or_exact: {
+ size_t index;
+
+ if (sscanf(parent->bc_current_search.c_str(), "%zu", &index)
+ == 1)
+ {
+ if (index < 0
|| (index
>= (sel_opt
| lnav::itertools::map([](const auto& cr) {
@@ -414,48 +442,102 @@ breadcrumb_curses::search_overlay_source::list_value_for_overlay(
{
combobox_role = role_t::VCR_ALERT_STATUS;
}
- break;
+ } else if (parent->bc_similar_values.empty()) {
+ combobox_role = role_t::VCR_ALERT_STATUS;
}
- case breadcrumb::crumb::expected_input_t::index_or_exact: {
- size_t index;
-
- if (sscanf(parent->bc_current_search.c_str(), "%zu", &index)
- == 1)
- {
- if (index < 0
- || (index
- >= (sel_opt
- | lnav::itertools::map([](const auto& cr) {
- return cr->c_possible_range.value_or(
- 0);
- })
- | lnav::itertools::unwrap_or(size_t{0}))))
- {
- combobox_role = role_t::VCR_ALERT_STATUS;
- }
- } else if (parent->bc_similar_values.empty()) {
- combobox_role = role_t::VCR_ALERT_STATUS;
- }
- break;
- }
- case breadcrumb::crumb::expected_input_t::anything:
- break;
+ break;
}
- value_out.with_attr_for_all(VC_ROLE.value(combobox_role));
+ case breadcrumb::crumb::expected_input_t::anything:
+ break;
+ }
+ value_out.with_attr_for_all(VC_ROLE.value(combobox_role));
+ return true;
+ }
+ if (parent->bc_selected_crumb) {
+ auto& selected_crumb_ref
+ = parent->bc_focused_crumbs[parent->bc_selected_crumb.value()];
+
+ if (!selected_crumb_ref.c_search_placeholder.empty()) {
+ value_out = selected_crumb_ref.c_search_placeholder;
+ value_out.with_attr_for_all(
+ VC_ROLE.value(role_t::VCR_INACTIVE_STATUS));
return true;
}
- if (parent->bc_selected_crumb) {
- auto& selected_crumb_ref
- = parent->bc_focused_crumbs[parent->bc_selected_crumb.value()];
-
- if (!selected_crumb_ref.c_search_placeholder.empty()) {
- value_out = selected_crumb_ref.c_search_placeholder;
- value_out.with_attr_for_all(
- VC_ROLE.value(role_t::VCR_INACTIVE_STATUS));
+ }
+
+ return false;
+}
+
+bool
+breadcrumb_curses::handle_mouse(mouse_event& me)
+{
+ if (me.me_state == mouse_button_state_t::BUTTON_STATE_PRESSED
+ && this->bc_focused_crumbs.empty())
+ {
+ this->focus();
+ this->on_focus(*this);
+ this->do_update();
+ }
+
+ auto find_res = this->bc_displayed_crumbs
+ | lnav::itertools::find_if([&me](const auto& elem) {
+ return me.me_button == mouse_button_t::BUTTON_LEFT
+ && elem.dc_range.contains(me.me_x);
+ });
+
+ if (!this->bc_focused_crumbs.empty()) {
+ if (me.me_y > 0 || !find_res
+ || find_res.value()->dc_index == this->bc_selected_crumb)
+ {
+ if (view_curses::handle_mouse(me)) {
+ if (me.me_y > 0
+ && (me.me_state
+ == mouse_button_state_t::BUTTON_STATE_DOUBLE_CLICK
+ || me.me_state
+ == mouse_button_state_t::BUTTON_STATE_RELEASED))
+ {
+ this->perform_selection(perform_behavior_t::if_different);
+ this->blur();
+ this->reload_data();
+ this->on_blur(*this);
+ this->bc_initial_mouse_event = true;
+ }
return true;
}
}
+ if (!this->bc_initial_mouse_event
+ && me.me_state == mouse_button_state_t::BUTTON_STATE_RELEASED
+ && me.me_y == 0 && find_res
+ && find_res.value()->dc_index == this->bc_selected_crumb.value())
+ {
+ this->blur();
+ this->reload_data();
+ this->on_blur(*this);
+ this->bc_initial_mouse_event = true;
+ return true;
+ }
}
- return false;
+ if (me.me_state == mouse_button_state_t::BUTTON_STATE_RELEASED) {
+ this->bc_initial_mouse_event = false;
+ }
+
+ if (me.me_y != 0) {
+ return true;
+ }
+
+ if (find_res) {
+ auto crumb_index = find_res.value()->dc_index;
+
+ if (this->bc_selected_crumb) {
+ this->blur();
+ this->focus();
+ this->reload_data();
+ this->bc_selected_crumb = crumb_index;
+ this->bc_current_search.clear();
+ this->reload_data();
+ }
+ }
+
+ return true;
}