[listview] scroll within overlay

pull/1179/head
Tim Stack 10 months ago
parent f2ec975b1c
commit 7c8d32308a

@ -73,6 +73,11 @@ Interface changes:
alternation is also now done in groups of two rows instead
of only a single row. Numbers are also rendered using the
"number" theme style as well.
* The log message overlay in the LOG view is now limited
2/3rds of the height and supports scrolling. The "alt-text"
theme style is also used to draw the overlay contents now
as well. (The overlay is used to display the parser
details, comments, and annotations.)
Breaking changes:
* Removed the `-w` command-line option. This option was

@ -278,19 +278,23 @@ 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:
@ -371,40 +375,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 (!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
|| index < 0
if (sscanf(parent->bc_current_search.c_str(), "%zu", &index)
== 1)
{
if (index < 0
|| (index
>= (sel_opt
| lnav::itertools::map([](const auto& cr) {
@ -414,46 +433,26 @@ breadcrumb_curses::search_overlay_source::list_value_for_overlay(
{
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) {
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;
} else if (parent->bc_similar_values.empty()) {
combobox_role = role_t::VCR_ALERT_STATUS;
}
case breadcrumb::crumb::expected_input_t::anything:
break;
break;
}
value_out.with_attr_for_all(VC_ROLE.value(combobox_role));
return true;
case breadcrumb::crumb::expected_input_t::anything:
break;
}
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;
}
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;
}
}

@ -73,11 +73,10 @@ public:
private:
class search_overlay_source : public list_overlay_source {
public:
bool list_value_for_overlay(const listview_curses& lv,
int y,
int bottom,
vis_line_t line,
attr_line_t& value_out) override;
bool list_static_overlay(const listview_curses& lv,
int y,
int bottom,
attr_line_t& value_out) override;
breadcrumb_curses* sos_parent{nullptr};
};

@ -288,15 +288,15 @@ db_label_source::row_for_time(struct timeval time_bucket)
return nonstd::nullopt;
}
size_t
db_overlay_source::list_overlay_count(const listview_curses& lv)
void
db_overlay_source::list_value_for_overlay(const listview_curses& lv,
vis_line_t row,
std::vector<attr_line_t>& value_out)
{
size_t retval = 1;
if (!this->dos_active || lv.get_inner_height() == 0) {
this->dos_lines.clear();
return retval;
return;
}
auto& vc = view_colors::singleton();
@ -307,7 +307,6 @@ db_overlay_source::list_overlay_count(const listview_curses& lv)
lv.get_dimensions(height, width);
this->dos_lines.clear();
for (size_t col = 0; col < cols.size(); col++) {
const char* col_value = cols[col];
size_t col_len = strlen(col_value);
@ -327,21 +326,21 @@ db_overlay_source::list_overlay_count(const listview_curses& lv)
{
const std::string& header
= this->dos_labels->dls_headers[col].hm_name;
this->dos_lines.emplace_back(" JSON Column: " + header);
value_out.emplace_back(" JSON Column: " + header);
retval += 1;
}
stacked_bar_chart<std::string> chart;
int start_line = this->dos_lines.size();
int start_line = value_out.size();
chart.with_stacking_enabled(false).with_margins(3, 0);
for (auto& jpw_value : jpw.jpw_values) {
this->dos_lines.emplace_back(" " + jpw_value.wt_ptr + " = "
+ jpw_value.wt_value);
value_out.emplace_back(" " + jpw_value.wt_ptr + " = "
+ jpw_value.wt_value);
string_attrs_t& sa = this->dos_lines.back().get_attrs();
string_attrs_t& sa = value_out.back().get_attrs();
struct line_range lr(1, 2);
sa.emplace_back(lr, VC_GRAPHIC.value(ACS_LTEE));
@ -376,7 +375,7 @@ db_overlay_source::list_overlay_count(const listview_curses& lv)
auto num_scan_res = scn::scan_value<double>(iter->wt_value);
if (num_scan_res) {
auto& sa = this->dos_lines[curr_line].get_attrs();
auto& sa = value_out[curr_line].get_attrs();
int left = 3;
chart.chart_attrs_for_value(
@ -387,9 +386,9 @@ db_overlay_source::list_overlay_count(const listview_curses& lv)
}
if (retval > 1) {
this->dos_lines.emplace_back("");
value_out.emplace_back("");
string_attrs_t& sa = this->dos_lines.back().get_attrs();
string_attrs_t& sa = value_out.back().get_attrs();
struct line_range lr(1, 2);
sa.emplace_back(lr, VC_GRAPHIC.value(ACS_LLCORNER));
@ -399,66 +398,55 @@ db_overlay_source::list_overlay_count(const listview_curses& lv)
retval += 1;
}
return retval;
}
bool
db_overlay_source::list_value_for_overlay(const listview_curses& lv,
int y,
int bottom,
vis_line_t row,
attr_line_t& value_out)
db_overlay_source::list_static_overlay(const listview_curses& lv,
int y,
int bottom,
attr_line_t& value_out)
{
if (y == 0) {
this->list_overlay_count(lv);
std::string& line = value_out.get_string();
db_label_source* dls = this->dos_labels;
string_attrs_t& sa = value_out.get_attrs();
if (y != 0) {
return false;
}
for (size_t lpc = 0; lpc < this->dos_labels->dls_headers.size(); lpc++)
{
auto actual_col_size
= std::min(dls->dls_max_column_width,
dls->dls_headers[lpc].hm_column_size);
std::string cell_title = dls->dls_headers[lpc].hm_name;
truncate_to(cell_title, dls->dls_max_column_width);
auto cell_length
= utf8_string_length(cell_title).unwrapOr(actual_col_size);
int before, total_fill = actual_col_size - cell_length;
auto line_len_before = line.length();
before = total_fill / 2;
total_fill -= before;
line.append(before, ' ');
line.append(cell_title);
line.append(total_fill, ' ');
line.append(1, ' ');
struct line_range header_range(line_len_before, line.length());
text_attrs attrs;
if (this->dos_labels->dls_headers[lpc].hm_graphable) {
attrs = dls->dls_headers[lpc].hm_title_attrs
| text_attrs{A_REVERSE};
} else {
attrs.ta_attrs = A_UNDERLINE;
}
sa.emplace_back(header_range, VC_STYLE.value(text_attrs{attrs}));
}
auto& line = value_out.get_string();
auto* dls = this->dos_labels;
auto& sa = value_out.get_attrs();
struct line_range lr(0);
for (size_t lpc = 0; lpc < this->dos_labels->dls_headers.size(); lpc++) {
auto actual_col_size = std::min(dls->dls_max_column_width,
dls->dls_headers[lpc].hm_column_size);
std::string cell_title = dls->dls_headers[lpc].hm_name;
sa.emplace_back(lr, VC_STYLE.value(text_attrs{A_BOLD | A_UNDERLINE}));
return true;
} else if (this->dos_active && y >= 2
&& ((size_t) y) < (this->dos_lines.size() + 2))
{
value_out = this->dos_lines[y - 2];
return true;
truncate_to(cell_title, dls->dls_max_column_width);
auto cell_length
= utf8_string_length(cell_title).unwrapOr(actual_col_size);
int before, total_fill = actual_col_size - cell_length;
auto line_len_before = line.length();
before = total_fill / 2;
total_fill -= before;
line.append(before, ' ');
line.append(cell_title);
line.append(total_fill, ' ');
line.append(1, ' ');
struct line_range header_range(line_len_before, line.length());
text_attrs attrs;
if (this->dos_labels->dls_headers[lpc].hm_graphable) {
attrs
= dls->dls_headers[lpc].hm_title_attrs | text_attrs{A_REVERSE};
} else {
attrs.ta_attrs = A_UNDERLINE;
}
sa.emplace_back(header_range, VC_STYLE.value(text_attrs{attrs}));
}
return false;
struct line_range lr(0);
sa.emplace_back(lr, VC_STYLE.value(text_attrs{A_BOLD | A_UNDERLINE}));
return true;
}

@ -130,16 +130,16 @@ public:
class db_overlay_source : public list_overlay_source {
public:
size_t list_overlay_count(const listview_curses& lv);
bool list_static_overlay(const listview_curses& lv,
int y,
int bottom,
attr_line_t& value_out) override;
bool list_value_for_overlay(const listview_curses& lv,
int y,
int bottom,
vis_line_t row,
attr_line_t& value_out) override;
void list_value_for_overlay(const listview_curses& lv,
vis_line_t line,
std::vector<attr_line_t>& value_out) override;
bool dos_active{false};
db_label_source* dos_labels{nullptr};
std::vector<attr_line_t> dos_lines;
};
#endif

@ -77,8 +77,6 @@ field_overlay_source::build_field_lines(const listview_curses& lv,
display = display || this->fos_contexts.top().c_show;
}
this->build_meta_line(lv, this->fos_lines, row);
if (!display) {
return;
}
@ -218,7 +216,7 @@ field_overlay_source::build_field_lines(const listview_curses& lv,
}
if ((!this->fos_contexts.empty() && this->fos_contexts.top().c_show)
|| diff_tv.tv_sec > 0)
|| diff_tv.tv_sec > 0 || ll->is_time_skewed())
{
this->fos_lines.emplace_back(time_line);
}
@ -462,11 +460,13 @@ field_overlay_source::build_meta_line(const listview_curses& lv,
&& (!line_meta_opt
|| line_meta_opt.value()->bm_annotations.la_pairs.empty()))
{
auto anno_msg = attr_line_t(" ")
.append(":memo:"_emoji)
.append(" Annotations available, use ")
.append(":annotate"_quoted_code)
.append(" to apply them to this line");
auto anno_msg
= attr_line_t(" ")
.append(":memo:"_emoji)
.append(" Annotations available, use ")
.append(":annotate"_quoted_code)
.append(" to apply them to this line")
.with_attr_for_all(VC_ROLE.value(role_t::VCR_COMMENT));
dst.emplace_back(anno_msg);
}
@ -600,10 +600,6 @@ field_overlay_source::build_meta_line(const listview_curses& lv,
}
}
}
if (dst.size() > 30) {
dst.resize(30);
}
}
void
@ -619,38 +615,36 @@ field_overlay_source::add_key_line_attrs(int key_size, bool last_line)
sa.emplace_back(lr, VC_STYLE.value(text_attrs{A_BOLD}));
}
bool
field_overlay_source::list_value_for_overlay(const listview_curses& lv,
int y,
int bottom,
vis_line_t row,
attr_line_t& value_out)
void
field_overlay_source::list_value_for_overlay(
const listview_curses& lv,
vis_line_t row,
std::vector<attr_line_t>& value_out)
{
if (y == 0) {
this->fos_meta_lines.clear();
this->fos_meta_lines_row = -1_vl;
if (row == lv.get_selection()) {
this->build_field_lines(lv, row);
return false;
}
if (1 <= y && y <= (int) this->fos_lines.size()) {
value_out = this->fos_lines[y - 1];
return true;
value_out = this->fos_lines;
}
this->build_meta_line(lv, value_out, row);
}
if (!this->fos_meta_lines.empty() && this->fos_meta_lines_row == row - 1_vl)
{
value_out = this->fos_meta_lines.front();
this->fos_meta_lines.erase(this->fos_meta_lines.begin());
return true;
}
nonstd::optional<attr_line_t>
field_overlay_source::list_header_for_overlay(const listview_curses& lv,
vis_line_t vl)
{
attr_line_t retval;
if (row < lv.get_inner_height()) {
this->fos_meta_lines.clear();
this->build_meta_line(lv, this->fos_meta_lines, row);
this->fos_meta_lines_row = row;
retval.append(this->fos_lss.get_filename_offset(), ' ');
if (this->fos_contexts.top().c_show) {
return retval
.appendf(FMT_STRING("\u258C Line {:L} parser details. "
"(Press '"),
(int) vl)
.append("p"_hotkey)
.append("' to toggle this view)");
}
return false;
return retval.append("\u258C Line ")
.append(lnav::roles::number(fmt::format(FMT_STRING("{:L}"), (int) vl)))
.append(" metadata");
}

@ -55,11 +55,12 @@ public:
this->fos_meta_lines_row = -1_vl;
}
bool list_value_for_overlay(const listview_curses& lv,
int y,
int bottom,
nonstd::optional<attr_line_t> list_header_for_overlay(
const listview_curses& lv, vis_line_t vl) override;
void list_value_for_overlay(const listview_curses& lv,
vis_line_t row,
attr_line_t& value_out) override;
std::vector<attr_line_t>& value_out) override;
void build_field_lines(const listview_curses& lv, vis_line_t row);
void build_meta_line(const listview_curses& lv,

@ -388,45 +388,44 @@ spinner_index()
}
bool
files_overlay_source::list_value_for_overlay(const listview_curses& lv,
int y,
int bottom,
vis_line_t line,
attr_line_t& value_out)
files_overlay_source::list_static_overlay(const listview_curses& lv,
int y,
int bottom,
attr_line_t& value_out)
{
if (y == 0) {
static const char PROG[] = "-\\|/";
constexpr size_t PROG_SIZE = sizeof(PROG) - 1;
auto& fc = lnav_data.ld_active_files;
auto fc_prog = fc.fc_progress;
safe::WriteAccess<safe_scan_progress> sp(*fc_prog);
if (!sp->sp_extractions.empty()) {
const auto& prog = sp->sp_extractions.front();
value_out.with_ansi_string(fmt::format(
"{} Extracting " ANSI_COLOR(COLOR_CYAN) "{}" ANSI_NORM
"... {:>8}/{}",
PROG[spinner_index() % PROG_SIZE],
prog.ep_path.filename().string(),
humanize::file_size(prog.ep_out_size,
humanize::alignment::none),
humanize::file_size(prog.ep_total_size,
humanize::alignment::none)));
return true;
}
if (!sp->sp_tailers.empty()) {
auto first_iter = sp->sp_tailers.begin();
value_out.with_ansi_string(fmt::format(
"{} Connecting to " ANSI_COLOR(COLOR_CYAN) "{}" ANSI_NORM
": {}",
PROG[spinner_index() % PROG_SIZE],
first_iter->first,
first_iter->second.tp_message));
return true;
}
if (y != 0) {
return false;
}
static const char PROG[] = "-\\|/";
constexpr size_t PROG_SIZE = sizeof(PROG) - 1;
auto& fc = lnav_data.ld_active_files;
auto fc_prog = fc.fc_progress;
safe::WriteAccess<safe_scan_progress> sp(*fc_prog);
if (!sp->sp_extractions.empty()) {
const auto& prog = sp->sp_extractions.front();
value_out.with_ansi_string(fmt::format(
"{} Extracting " ANSI_COLOR(COLOR_CYAN) "{}" ANSI_NORM
"... {:>8}/{}",
PROG[spinner_index() % PROG_SIZE],
prog.ep_path.filename().string(),
humanize::file_size(prog.ep_out_size, humanize::alignment::none),
humanize::file_size(prog.ep_total_size,
humanize::alignment::none)));
return true;
}
if (!sp->sp_tailers.empty()) {
auto first_iter = sp->sp_tailers.begin();
value_out.with_ansi_string(fmt::format(
"{} Connecting to " ANSI_COLOR(COLOR_CYAN) "{}" ANSI_NORM ": {}",
PROG[spinner_index() % PROG_SIZE],
first_iter->first,
first_iter->second.tp_message));
return true;
}
return false;
}

@ -64,17 +64,15 @@ public:
};
struct files_overlay_source : public list_overlay_source {
bool list_value_for_overlay(const listview_curses& lv,
int y,
int bottom,
vis_line_t line,
attr_line_t& value_out) override;
bool list_static_overlay(const listview_curses& lv,
int y,
int bottom,
attr_line_t& value_out) override;
};
namespace files_model {
struct no_selection {
};
struct no_selection {};
template<typename C, typename T>
struct selection_base {
@ -99,13 +97,11 @@ struct error_selection
struct other_selection
: public selection_base<
other_selection,
std::map<std::string, other_file_descriptor>::iterator> {
};
std::map<std::string, other_file_descriptor>::iterator> {};
struct file_selection
: public selection_base<file_selection,
std::vector<std::shared_ptr<logfile>>::iterator> {
};
std::vector<std::shared_ptr<logfile>>::iterator> {};
using files_list_selection = mapbox::util::
variant<no_selection, error_selection, other_selection, file_selection>;

@ -94,11 +94,13 @@ listview_curses::reload_data()
{
if (this->lv_source == nullptr) {
this->lv_top = 0_vl;
this->lv_focused_overlay_top = 0_vl;
this->lv_left = 0;
} else {
if (this->lv_top >= this->get_inner_height()) {
this->lv_top
= std::max(0_vl, vis_line_t(this->get_inner_height() - 1));
this->lv_focused_overlay_top = 0_vl;
}
if (this->lv_selectable) {
if (this->get_inner_height() == 0) {
@ -156,25 +158,22 @@ listview_curses::handle_key(int ch)
case '\r':
case 'j':
case KEY_DOWN:
if (this->is_selectable()) {
this->shift_selection(1);
} else {
this->shift_top(1_vl);
}
this->shift_selection(shift_amount_t::down_line);
break;
case 'k':
case KEY_UP:
if (this->is_selectable()) {
this->shift_selection(-1);
} else {
this->shift_top(-1_vl);
}
this->shift_selection(shift_amount_t::up_line);
break;
case 'b':
case KEY_BACKSPACE:
case KEY_PPAGE:
if (this->lv_focused_overlay_top != 0_vl) {
this->shift_selection(shift_amount_t::up_page);
break;
}
if (this->lv_top == 0_vl && this->lv_selectable
&& this->lv_selection != 0_vl)
{
@ -187,6 +186,16 @@ listview_curses::handle_key(int ch)
case ' ':
case KEY_NPAGE: {
if (this->lv_overlay_source != nullptr) {
std::vector<attr_line_t> overlay_content;
this->lv_overlay_source->list_value_for_overlay(
*this, this->get_selection(), overlay_content);
if (!overlay_content.empty()) {
this->shift_selection(shift_amount_t::down_page);
break;
}
}
auto rows_avail
= this->rows_available(this->lv_top, RD_DOWN) - 1_vl;
auto top_for_last = this->get_top_for_last_row();
@ -257,6 +266,20 @@ listview_curses::handle_key(int ch)
return retval;
}
vis_line_t
listview_curses::get_overlay_top(vis_line_t row, size_t count, size_t total)
{
if (row == this->get_selection()) {
return this->lv_focused_overlay_top;
}
if (this->get_selection() > row && count < total) {
return vis_line_t{static_cast<int>(total - count)};
}
return 0_vl;
}
void
listview_curses::do_update()
{
@ -265,6 +288,7 @@ listview_curses::do_update()
return;
}
std::vector<attr_line_t> row_overlay_content;
vis_line_t height;
unsigned long width;
@ -303,12 +327,8 @@ listview_curses::do_update()
lr.lr_start = this->lv_left;
lr.lr_end = this->lv_left + wrap_width;
if (this->lv_overlay_source != nullptr
&& this->lv_overlay_source->list_value_for_overlay(
*this,
y - this->lv_y,
bottom - this->lv_y,
row,
overlay_line))
&& this->lv_overlay_source->list_static_overlay(
*this, y - this->lv_y, bottom - this->lv_y, overlay_line))
{
mvwattrline(this->lv_window, y, this->lv_x, overlay_line, lr);
overlay_line.clear();
@ -335,6 +355,103 @@ listview_curses::do_update()
lr.lr_end += wrap_width;
++y;
} while (this->lv_word_wrap && y < bottom && remaining > 0);
if (this->lv_overlay_source != nullptr) {
row_overlay_content.clear();
lr.lr_start = this->lv_left;
lr.lr_end = this->lv_left + wrap_width;
this->lv_overlay_source->list_value_for_overlay(
*this, row, row_overlay_content);
auto overlay_height = this->get_overlay_height(
row_overlay_content.size(), height);
auto ov_height_remaining = overlay_height;
auto overlay_top = this->get_overlay_top(
row, overlay_height, row_overlay_content.size());
auto overlay_row = overlay_top;
if (row_overlay_content.size() > 1) {
auto hdr
= this->lv_overlay_source->list_header_for_overlay(
*this, row);
if (hdr) {
auto ov_hdr_attrs = text_attrs{};
ov_hdr_attrs.ta_attrs |= A_UNDERLINE;
auto ov_hdr = hdr.value().with_attr_for_all(
VC_STYLE.value(ov_hdr_attrs));
mvwattrline(this->lv_window,
y,
this->lv_x,
ov_hdr,
lr,
role_t::VCR_STATUS_INFO);
++y;
}
}
auto overlay_y = y;
while (ov_height_remaining > 0 && y < bottom) {
mvwattrline(this->lv_window,
y,
this->lv_x,
row_overlay_content[overlay_row],
lr,
role_t::VCR_ALT_ROW);
ov_height_remaining -= 1;
++overlay_row;
++y;
}
if (overlay_height != row_overlay_content.size()) {
double progress = 1.0;
double coverage = 1.0;
vis_line_t lines;
if (!row_overlay_content.empty()) {
progress = (double) overlay_top
/ (double) row_overlay_content.size();
coverage = (double) overlay_height
/ (double) row_overlay_content.size();
}
auto scroll_y = overlay_y
+ (int) (progress * (double) overlay_height);
lines = vis_line_t(
scroll_y
+ std::min(
(int) overlay_height,
(int) (coverage * (double) overlay_height)));
for (unsigned int gutter_y = overlay_y;
gutter_y < (overlay_y + overlay_height);
gutter_y++)
{
auto role = this->vc_default_role;
auto bar_role = role_t::VCR_SCROLLBAR;
text_attrs attrs;
chtype ch = gutter_y == overlay_y
? ACS_URCORNER
: (gutter_y == (overlay_y + overlay_height - 1)
? ACS_LRCORNER
: ACS_VLINE);
if (gutter_y >= (unsigned int) scroll_y
&& gutter_y <= (unsigned int) lines)
{
role = bar_role;
}
attrs = vc.attrs_for_role(role);
wattr_set(this->lv_window,
attrs.ta_attrs,
vc.ensure_color_pair(attrs.ta_fg_color,
attrs.ta_bg_color),
nullptr);
mvwaddch(this->lv_window,
gutter_y,
this->lv_x + width - 2,
ch);
}
}
}
++row;
} else {
wattr_set(this->lv_window,
@ -421,43 +538,127 @@ listview_curses::do_update()
}
view_curses::do_update();
}
#if 0
else if (this->lv_overlay_needs_update && this->lv_overlay_source != NULL) {
vis_line_t y(this->lv_y), height, bottom;
attr_line_t overlay_line;
unsigned long width, wrap_width;
struct line_range lr;
void
listview_curses::shift_selection(shift_amount_t sa)
{
vis_line_t height;
unsigned long width;
this->lv_overlay_source->list_overlay_count(*this);
this->get_dimensions(height, width);
wrap_width = width - (this->lv_word_wrap ? 1 : this->lv_show_scrollbar ? 1 : 0);
this->get_dimensions(height, width);
if (this->lv_focused_overlay_top > 0
&& (sa == shift_amount_t::up_line || sa == shift_amount_t::up_page))
{
switch (sa) {
case shift_amount_t::up_line:
this->lv_focused_overlay_top -= 1_vl;
break;
case shift_amount_t::up_page: {
std::vector<attr_line_t> overlay_content;
this->lv_overlay_source->list_value_for_overlay(
*this, this->get_selection(), overlay_content);
auto overlay_height
= this->get_overlay_height(overlay_content.size(), height);
if (this->lv_focused_overlay_top > overlay_height) {
this->lv_focused_overlay_top
-= vis_line_t{static_cast<int>(overlay_height - 1)};
} else {
this->lv_focused_overlay_top = 0_vl;
}
break;
}
default:
break;
}
this->set_needs_update();
return;
}
lr.lr_start = this->lv_left;
lr.lr_end = this->lv_left + wrap_width;
if (this->get_selection() > 0 && this->lv_overlay_source != nullptr) {
vis_line_t focused = this->get_selection();
std::vector<attr_line_t> overlay_content;
bool is_up = false;
bottom = y + height;
while (y < bottom) {
if (this->lv_overlay_source->list_value_for_overlay(
*this,
y - vis_line_t(this->lv_y),
overlay_line)) {
this->mvwattrline(this->lv_window, y, this->lv_x, overlay_line, lr);
overlay_line.clear();
switch (sa) {
case shift_amount_t::up_line:
case shift_amount_t::up_page:
focused -= 1_vl;
is_up = true;
break;
default:
break;
}
this->lv_overlay_source->list_value_for_overlay(
*this, focused, overlay_content);
if (!overlay_content.empty()) {
auto overlay_height
= this->get_overlay_height(overlay_content.size(), height);
auto ov_top_for_last = vis_line_t{
static_cast<int>(overlay_content.size() - overlay_height)};
if (is_up) {
this->set_selection(focused);
this->lv_focused_overlay_top = ov_top_for_last;
this->set_needs_update();
return;
}
if (this->lv_focused_overlay_top + 1 + overlay_height
<= overlay_content.size())
{
switch (sa) {
case shift_amount_t::down_line:
this->lv_focused_overlay_top += 1_vl;
break;
case shift_amount_t::down_page: {
if (this->lv_focused_overlay_top + overlay_height - 1
>= ov_top_for_last)
{
this->lv_focused_overlay_top = ov_top_for_last;
} else {
this->lv_focused_overlay_top += vis_line_t{
static_cast<int>(overlay_height - 1)};
}
break;
}
default:
break;
}
this->set_needs_update();
return;
}
if (overlay_height < overlay_content.size()) {
sa = shift_amount_t::down_line;
}
++y;
}
this->lv_focused_overlay_top = 0_vl;
}
#endif
}
void
listview_curses::shift_selection(int offset)
{
vis_line_t new_selection = this->lv_selection + vis_line_t(offset);
auto offset = 0_vl;
switch (sa) {
case shift_amount_t::up_line:
offset = -1_vl;
break;
case shift_amount_t::up_page:
offset = -(height - 1_vl);
break;
case shift_amount_t::down_line:
offset = 1_vl;
break;
case shift_amount_t::down_page:
offset = height - 1_vl;
break;
}
if (this->is_selectable()) {
auto new_selection = this->lv_selection + vis_line_t(offset);
if (new_selection >= 0_vl && new_selection < this->get_inner_height()) {
this->set_selection(new_selection);
if (new_selection >= 0_vl && new_selection < this->get_inner_height()) {
this->set_selection(new_selection);
}
} else {
this->shift_top(vis_line_t{offset});
}
}
@ -577,6 +778,7 @@ listview_curses::set_top(vis_line_t top, bool suppress_flash)
} else if (this->lv_top != top) {
auto old_top = this->lv_top;
this->lv_top = top;
this->lv_focused_overlay_top = 0_vl;
if (this->lv_selectable) {
if (this->lv_selection < 0_vl) {
} else if (this->lv_selection < top) {
@ -665,6 +867,7 @@ listview_curses::set_selection(vis_line_t sel)
}
if (sel == -1_vl) {
this->lv_selection = sel;
this->lv_focused_overlay_top = 0_vl;
this->lv_source->listview_selection_changed(*this);
this->set_needs_update();
this->invoke_scroll();
@ -700,6 +903,7 @@ listview_curses::set_selection(vis_line_t sel)
if (this->lv_sync_selection_and_top) {
this->lv_top = sel;
}
this->lv_focused_overlay_top = 0_vl;
this->lv_source->listview_selection_changed(*this);
this->set_needs_update();
this->invoke_scroll();
@ -732,3 +936,23 @@ listview_curses::get_top_for_last_row()
return retval;
}
vis_line_t
listview_curses::shift_top(vis_line_t offset, bool suppress_flash)
{
if (offset < 0 && this->lv_top == 0) {
if (suppress_flash == false) {
alerter::singleton().chime("the top of the view has been reached");
}
} else {
this->set_top(std::max(0_vl, this->lv_top + offset), suppress_flash);
}
return this->lv_top;
}
size_t
listview_curses::get_overlay_height(size_t total, vis_line_t view_height)
{
return std::min(total, static_cast<size_t>(2 * (view_height / 3)));
}

@ -107,12 +107,25 @@ public:
virtual void reset() {}
virtual bool list_value_for_overlay(const listview_curses& lv,
int y,
int bottom,
virtual bool list_static_overlay(const listview_curses& lv,
int y,
int bottom,
attr_line_t& value_out)
{
return false;
}
virtual nonstd::optional<attr_line_t> list_header_for_overlay(
const listview_curses& lv, vis_line_t line)
{
return nonstd::nullopt;
}
virtual void list_value_for_overlay(const listview_curses& lv,
vis_line_t line,
attr_line_t& value_out)
= 0;
std::vector<attr_line_t>& value_out)
{
}
};
class list_input_delegate {
@ -209,7 +222,14 @@ public:
void set_selection(vis_line_t sel);
void shift_selection(int offset);
enum class shift_amount_t {
up_line,
up_page,
down_line,
down_page,
};
void shift_selection(shift_amount_t sa);
vis_line_t get_selection() const
{
@ -332,20 +352,7 @@ public:
* @param suppress_flash Don't call flash() if the offset is out-of-bounds.
* @return The final value of top.
*/
vis_line_t shift_top(vis_line_t offset, bool suppress_flash = false)
{
if (offset < 0 && this->lv_top == 0) {
if (suppress_flash == false) {
alerter::singleton().chime(
"the top of the view has been reached");
}
} else {
this->set_top(std::max(0_vl, this->lv_top + offset),
suppress_flash);
}
return this->lv_top;
}
vis_line_t shift_top(vis_line_t offset, bool suppress_flash = false);
/**
* Set the column number to be displayed at the left of the view. If the
@ -529,6 +536,9 @@ protected:
void update_top_from_selection();
vis_line_t get_overlay_top(vis_line_t row, size_t count, size_t total);
size_t get_overlay_height(size_t total, vis_line_t view_height);
enum class lv_mode_t {
NONE,
DOWN,
@ -549,6 +559,7 @@ protected:
vis_line_t lv_top{0}; /*< The line at the top of the view. */
unsigned int lv_left{0}; /*< The column at the left of the view. */
vis_line_t lv_height{0}; /*< The abs/rel height of the view. */
vis_line_t lv_focused_overlay_top{0};
int lv_history_position{0};
bool lv_overlay_needs_update{true};
bool lv_show_scrollbar{true}; /*< Draw the scrollbar in the view. */

@ -1993,36 +1993,40 @@ looper()
if (lnav_data.ld_view_stack.empty()) {
lnav_data.ld_looping = false;
} else {
textview_curses* tc = *lnav_data.ld_view_stack.top();
std::vector<attr_line_t> rows(1);
tc->get_data_source()->listview_value_for_rows(
*tc, tc->get_selection(), rows);
string_attrs_t& sa = rows[0].get_attrs();
auto line_attr_opt = get_string_attr(sa, logline::L_FILE);
bool found = false;
if (line_attr_opt) {
auto lf = line_attr_opt.value().get();
log_debug("file name when SIGINT: %s",
lf->get_filename().c_str());
for (auto& cp : lnav_data.ld_child_pollers) {
auto cp_name = cp.get_filename();
if (!cp_name) {
log_debug("no child_poller");
continue;
}
auto* tc = *lnav_data.ld_view_stack.top();
if (tc->get_inner_height() > 0_vl) {
std::vector<attr_line_t> rows(1);
if (lf->get_filename() == cp_name.value()) {
log_debug("found it, sending signal!");
cp.send_sigint();
found = true;
tc->get_data_source()->listview_value_for_rows(
*tc, tc->get_selection(), rows);
auto& sa = rows[0].get_attrs();
auto line_attr_opt
= get_string_attr(sa, logline::L_FILE);
bool found = false;
if (line_attr_opt) {
auto lf = line_attr_opt.value().get();
log_debug("file name when SIGINT: %s",
lf->get_filename().c_str());
for (auto& cp : lnav_data.ld_child_pollers) {
auto cp_name = cp.get_filename();
if (!cp_name) {
log_debug("no child_poller");
continue;
}
if (lf->get_filename() == cp_name.value()) {
log_debug("found it, sending signal!");
cp.send_sigint();
found = true;
}
}
}
}
if (!found) {
lnav_data.ld_looping = false;
if (!found) {
lnav_data.ld_looping = false;
}
}
}
}
@ -3325,21 +3329,19 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
}
auto* los = tc->get_overlay_source();
attr_line_t ov_al;
while (los != nullptr && tc->get_inner_height() > 0_vl
&& los->list_static_overlay(
*tc, y, tc->get_inner_height(), ov_al))
{
write_line_to(stdout, ov_al);
++y;
}
vis_line_t vl;
for (vl = tc->get_top(); vl < tc->get_inner_height();
++vl, ++y)
{
attr_line_t al;
while (los != nullptr
&& los->list_value_for_overlay(
*tc, y, tc->get_inner_height(), vl, al))
{
write_line_to(stdout, al);
++y;
}
std::vector<attr_line_t> rows(1);
tc->listview_value_for_rows(*tc, vl, rows);
if (suppress_empty_lines && rows[0].empty()) {
@ -3347,17 +3349,15 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
}
write_line_to(stdout, rows[0]);
}
{
attr_line_t al;
while (los != nullptr
&& los->list_value_for_overlay(
*tc, y, tc->get_inner_height(), vl, al)
&& !al.empty())
{
write_line_to(stdout, al);
++y;
std::vector<attr_line_t> row_overlay_content;
if (los != nullptr) {
los->list_value_for_overlay(
*tc, vl, row_overlay_content);
for (const auto& ov_row : row_overlay_content) {
write_line_to(stdout, ov_row);
++y;
}
}
}
}

@ -1318,15 +1318,15 @@ com_save_to(exec_context& ec,
attr_line_t ov_al;
auto* los = tc->get_overlay_source();
while (
los != nullptr
&& los->list_static_overlay(*tc, y, tc->get_inner_height(), ov_al))
{
write_line_to(outfile, ov_al);
++y;
}
tc->listview_value_for_rows(*tc, top, rows);
for (auto& al : rows) {
while (los != nullptr
&& los->list_value_for_overlay(
*tc, y, tc->get_inner_height(), top, ov_al))
{
write_line_to(outfile, ov_al);
++y;
}
wrapped_count += vis_line_t((al.length() - 1) / (dim.second - 2));
if (anonymize) {
al.al_attrs.clear();
@ -1334,17 +1334,18 @@ com_save_to(exec_context& ec,
}
write_line_to(outfile, al);
++y;
std::vector<attr_line_t> row_overlay_content;
if (los != nullptr) {
los->list_value_for_overlay(*tc, top, row_overlay_content);
for (const auto& ov_row : row_overlay_content) {
write_line_to(outfile, ov_row);
line_count += 1;
++y;
}
}
line_count += 1;
++top;
++y;
}
while (los != nullptr
&& los->list_value_for_overlay(
*tc, y, tc->get_inner_height(), top, ov_al)
&& !ov_al.empty())
{
write_line_to(outfile, ov_al);
++y;
}
tc->set_word_wrap(wrapped);
@ -1467,37 +1468,37 @@ com_save_to(exec_context& ec,
});
}
los->reset();
auto y = 0_vl;
while (
los != nullptr
&& los->list_static_overlay(*tc, y, tc->get_inner_height(), ov_al))
{
write_line_to(outfile, ov_al);
++y;
}
for (auto iter = all_user_marks.begin(); iter != all_user_marks.end();
iter++, count++)
{
if (ec.ec_dry_run && count > 10) {
break;
}
auto y = 0_vl;
while (los != nullptr
&& los->list_value_for_overlay(
*tc, y, tc->get_inner_height(), *iter, ov_al))
{
write_line_to(outfile, ov_al);
++y;
}
tc->listview_value_for_rows(*tc, *iter, rows);
++y;
if (anonymize) {
rows[0].al_attrs.clear();
rows[0].al_string = ta.next(rows[0].al_string);
}
write_line_to(outfile, rows[0]);
while (los != nullptr
&& los->list_value_for_overlay(
*tc, y, tc->get_inner_height(), *iter, ov_al))
{
write_line_to(outfile, ov_al);
++y;
y = 0_vl;
std::vector<attr_line_t> row_overlay_content;
if (los != nullptr) {
los->list_value_for_overlay(*tc, (*iter), row_overlay_content);
for (const auto& ov_row : row_overlay_content) {
write_line_to(outfile, ov_row);
line_count += 1;
++y;
}
}
line_count += 1;
}

@ -393,11 +393,10 @@ apply(vis_line_t vl, std::vector<intern_string_t> annos)
lnav_data.ld_views[LNV_LOG].set_needs_update();
};
lnav_data.ld_child_pollers.emplace_back(child_poller{
lnav_data.ld_child_pollers.emplace_back(
(*ld)->get_file_ptr()->get_filename(),
std::move(child),
std::move(finalizer),
});
std::move(finalizer));
}
lf->get_bookmark_metadata()[line_number].bm_annotations = la;
return Ok();

@ -3353,6 +3353,10 @@ external_log_format::get_pattern_name(uint64_t line_number) const
int
log_format::pattern_index_for_line(uint64_t line_number) const
{
if (this->lf_pattern_locks.empty()) {
return -1;
}
auto iter = lower_bound(this->lf_pattern_locks.cbegin(),
this->lf_pattern_locks.cend(),
line_number,
@ -3380,7 +3384,9 @@ log_format::get_pattern_name(uint64_t line_number) const
char pat_str[128];
int pat_index = this->pattern_index_for_line(line_number);
snprintf(pat_str, sizeof(pat_str), "builtin (%d)", pat_index);
auto to_n_res = fmt::format_to_n(
pat_str, sizeof(pat_str) - 1, FMT_STRING("builtin ({})"), pat_index);
pat_str[to_n_res.size] = '\0';
return intern_string::lookup(pat_str);
}

@ -518,10 +518,6 @@ load_time_bookmarks()
continue;
}
if (part_name == nullptr) {
continue;
}
if (!dts.scan(log_time,
strlen(log_time),
nullptr,
@ -532,13 +528,11 @@ load_time_bookmarks()
}
auto line_iter
= lower_bound(lf->begin(), lf->end(), log_tv);
= std::lower_bound(lf->begin(), lf->end(), log_tv);
while (line_iter != lf->end()) {
struct timeval line_tv = line_iter->get_timeval();
if ((line_tv.tv_sec != log_tv.tv_sec)
|| (line_tv.tv_usec != log_tv.tv_usec))
{
if (line_tv != log_tv) {
break;
}

@ -90,17 +90,14 @@ public:
});
}
bool list_value_for_overlay(const listview_curses& lv,
int y,
int bottom,
void list_value_for_overlay(const listview_curses& lv,
vis_line_t line,
attr_line_t& value_out) override
std::vector<attr_line_t>& value_out) override
{
if (this->fss_overlay_delegate != nullptr) {
return this->fss_overlay_delegate->list_value_for_overlay(
lv, y, bottom, line, value_out);
this->fss_overlay_delegate->list_value_for_overlay(
lv, line, value_out);
}
return false;
}
text_sub_source* fss_delegate;

@ -176,12 +176,10 @@ spectrogram_source::list_input_handle_key(listview_curses& lv, int ch)
}
}
bool
void
spectrogram_source::list_value_for_overlay(const listview_curses& lv,
int y,
int bottom,
vis_line_t row,
attr_line_t& value_out)
std::vector<attr_line_t>& value_out)
{
vis_line_t height;
unsigned long width;
@ -189,140 +187,81 @@ spectrogram_source::list_value_for_overlay(const listview_curses& lv,
lv.get_dimensions(height, width);
width -= 2;
if (y > 0) {
auto sel = lv.get_selection();
auto selected_y = sel - lv.get_top() + 2;
if (y == selected_y && this->ss_cursor_column) {
const auto& s_row = this->load_row(lv, sel);
const auto& bucket
= s_row.sr_values[this->ss_cursor_column.value()];
auto& sb = this->ss_cached_bounds;
spectrogram_request sr(sb);
auto sel_time = rounddown(sb.sb_begin_time, this->ss_granularity)
+ sel * this->ss_granularity;
sr.sr_width = width;
sr.sr_begin_time = sel_time;
sr.sr_end_time = sel_time + this->ss_granularity;
sr.sr_column_size = (sb.sb_max_value_out - sb.sb_min_value_out)
/ (double) (width - 1);
auto range_min = sb.sb_min_value_out
+ this->ss_cursor_column.value() * sr.sr_column_size;
auto range_max = range_min + sr.sr_column_size;
auto desc
= attr_line_t()
.append(lnav::roles::number(
fmt::to_string(bucket.rb_counter)))
.append(fmt::format(FMT_STRING(" value{} in the range "),
bucket.rb_counter == 1 ? "" : "s"))
.append(lnav::roles::number(
fmt::format(FMT_STRING("{:.2Lf}"), range_min)))
.append("-")
.append(lnav::roles::number(
fmt::format(FMT_STRING("{:.2Lf}"), range_max)))
.append(" ");
auto mark_offset = this->ss_cursor_column.value();
auto mark_is_before = true;
value_out.al_attrs.emplace_back(
line_range{0, -1}, VC_ROLE.value(role_t::VCR_STATUS_INFO));
if (desc.length() + 8 > width) {
desc.clear();
}
auto sel = lv.get_selection();
if (row == sel && this->ss_cursor_column) {
const auto& s_row = this->load_row(lv, sel);
const auto& bucket = s_row.sr_values[this->ss_cursor_column.value()];
auto& sb = this->ss_cached_bounds;
spectrogram_request sr(sb);
attr_line_t retval;
auto sel_time = rounddown(sb.sb_begin_time, this->ss_granularity)
+ sel * this->ss_granularity;
sr.sr_width = width;
sr.sr_begin_time = sel_time;
sr.sr_end_time = sel_time + this->ss_granularity;
sr.sr_column_size = (sb.sb_max_value_out - sb.sb_min_value_out)
/ (double) (width - 1);
auto range_min = sb.sb_min_value_out
+ this->ss_cursor_column.value() * sr.sr_column_size;
auto range_max = range_min + sr.sr_column_size;
auto desc
= attr_line_t()
.append(
lnav::roles::number(fmt::to_string(bucket.rb_counter)))
.append(fmt::format(FMT_STRING(" value{} in the range "),
bucket.rb_counter == 1 ? "" : "s"))
.append(lnav::roles::number(
fmt::format(FMT_STRING("{:.2Lf}"), range_min)))
.append("-")
.append(lnav::roles::number(
fmt::format(FMT_STRING("{:.2Lf}"), range_max)))
.append(" ");
auto mark_offset = this->ss_cursor_column.value();
auto mark_is_before = true;
retval.al_attrs.emplace_back(line_range{0, -1},
VC_ROLE.value(role_t::VCR_STATUS_INFO));
if (desc.length() + 8 > width) {
desc.clear();
}
if (this->ss_cursor_column.value() + desc.length() + 1 > width) {
mark_offset -= desc.length();
mark_is_before = false;
}
value_out.append(mark_offset, ' ');
if (mark_is_before) {
value_out.append("\u25b2 ");
}
value_out.append(desc);
if (!mark_is_before) {
value_out.append("\u25b2 ");
}
if (this->ss_cursor_column.value() + desc.length() + 1 > width) {
mark_offset -= desc.length();
mark_is_before = false;
}
retval.append(mark_offset, ' ');
if (mark_is_before) {
retval.append("\u25b2 ");
}
retval.append(desc);
if (!mark_is_before) {
retval.append("\u25b2 ");
}
if (this->ss_details_view != nullptr) {
if (s_row.sr_details_source_provider) {
auto row_details_source = s_row.sr_details_source_provider(
sr, range_min, range_max);
this->ss_details_view->set_sub_source(
row_details_source.get());
this->ss_details_source = std::move(row_details_source);
auto* overlay_source = dynamic_cast<list_overlay_source*>(
this->ss_details_source.get());
if (overlay_source != nullptr) {
this->ss_details_view->set_overlay_source(
overlay_source);
}
} else {
this->ss_details_view->set_sub_source(
this->ss_no_details_source);
this->ss_details_view->set_overlay_source(nullptr);
if (this->ss_details_view != nullptr) {
if (s_row.sr_details_source_provider) {
auto row_details_source = s_row.sr_details_source_provider(
sr, range_min, range_max);
this->ss_details_view->set_sub_source(row_details_source.get());
this->ss_details_source = std::move(row_details_source);
auto* overlay_source = dynamic_cast<list_overlay_source*>(
this->ss_details_source.get());
if (overlay_source != nullptr) {
this->ss_details_view->set_overlay_source(overlay_source);
}
} else {
this->ss_details_view->set_sub_source(
this->ss_no_details_source);
this->ss_details_view->set_overlay_source(nullptr);
}
return true;
}
return false;
}
auto& line = value_out.get_string();
char buf[128];
this->cache_bounds();
if (this->ss_cached_line_count == 0) {
value_out
.append(lnav::roles::error("error: no data available, use the "))
.append_quoted(lnav::roles::keyword(":spectrogram"))
.append(lnav::roles::error(" command to visualize numeric data"));
return true;
value_out.emplace_back(retval);
}
auto& sb = this->ss_cached_bounds;
auto& st = this->ss_cached_thresholds;
snprintf(buf, sizeof(buf), "Min: %'.10lg", sb.sb_min_value_out);
line = buf;
snprintf(buf,
sizeof(buf),
ANSI_ROLE(" ") " 1-%'d " ANSI_ROLE(" ") " %'d-%'d " ANSI_ROLE(
" ") " %'d+",
role_t::VCR_LOW_THRESHOLD,
st.st_green_threshold - 1,
role_t::VCR_MED_THRESHOLD,
st.st_green_threshold,
st.st_yellow_threshold - 1,
role_t::VCR_HIGH_THRESHOLD,
st.st_yellow_threshold);
auto buflen = strlen(buf);
if (line.length() + buflen + 20 < width) {
line.append(width / 2 - buflen / 3 - line.length(), ' ');
} else {
line.append(" ");
}
line.append(buf);
scrub_ansi_string(line, &value_out.get_attrs());
snprintf(buf, sizeof(buf), "Max: %'.10lg", sb.sb_max_value_out);
buflen = strlen(buf);
if (line.length() + buflen + 4 < width) {
line.append(width - buflen - line.length() - 2, ' ');
} else {
line.append(" ");
}
line.append(buf);
value_out.with_attr(string_attr(line_range(0, -1),
VC_STYLE.value(text_attrs{A_UNDERLINE})));
return true;
}
size_t
@ -590,6 +529,75 @@ spectrogram_source::text_selection_changed(textview_curses& tc)
= s_row.nearest_column(this->ss_cursor_column.value_or(0));
}
bool
spectrogram_source::list_static_overlay(const listview_curses& lv,
int y,
int bottom,
attr_line_t& value_out)
{
if (y != 0) {
return false;
}
auto& line = value_out.get_string();
vis_line_t height;
unsigned long width;
char buf[128];
lv.get_dimensions(height, width);
width -= 2;
this->cache_bounds();
if (this->ss_cached_line_count == 0) {
value_out
.append(lnav::roles::error("error: no data available, use the "))
.append_quoted(lnav::roles::keyword(":spectrogram"))
.append(lnav::roles::error(" command to visualize numeric data"));
return true;
}
auto& sb = this->ss_cached_bounds;
auto& st = this->ss_cached_thresholds;
snprintf(buf, sizeof(buf), "Min: %'.10lg", sb.sb_min_value_out);
line = buf;
snprintf(buf,
sizeof(buf),
ANSI_ROLE(" ") " 1-%'d " ANSI_ROLE(" ") " %'d-%'d " ANSI_ROLE(
" ") " %'d+",
role_t::VCR_LOW_THRESHOLD,
st.st_green_threshold - 1,
role_t::VCR_MED_THRESHOLD,
st.st_green_threshold,
st.st_yellow_threshold - 1,
role_t::VCR_HIGH_THRESHOLD,
st.st_yellow_threshold);
auto buflen = strlen(buf);
if (line.length() + buflen + 20 < width) {
line.append(width / 2 - buflen / 3 - line.length(), ' ');
} else {
line.append(" ");
}
line.append(buf);
scrub_ansi_string(line, &value_out.get_attrs());
snprintf(buf, sizeof(buf), "Max: %'.10lg", sb.sb_max_value_out);
buflen = strlen(buf);
if (line.length() + buflen + 4 < width) {
line.append(width - buflen - line.length() - 2, ' ');
} else {
line.append(" ");
}
line.append(buf);
value_out.with_attr(string_attr(line_range(0, -1),
VC_STYLE.value(text_attrs{A_UNDERLINE})));
return true;
}
spectro_status_source::spectro_status_source()
{
this->sss_fields[F_TITLE].set_width(9);

@ -130,11 +130,14 @@ public:
bool list_input_handle_key(listview_curses& lv, int ch) override;
bool list_value_for_overlay(const listview_curses& lv,
int y,
int bottom,
bool list_static_overlay(const listview_curses& lv,
int y,
int bottom,
attr_line_t& value_out) override;
void list_value_for_overlay(const listview_curses& lv,
vis_line_t row,
attr_line_t& value_out) override;
std::vector<attr_line_t>& value_out) override;
size_t text_line_count() override;

@ -850,7 +850,7 @@ execute_examples()
result.append(dls.dls_rows[0][0]);
} else {
attr_line_t al;
dos.list_value_for_overlay(db_tc, 0, 1, 0_vl, al);
dos.list_static_overlay(db_tc, 0, 1, al);
result.append(al);
for (int lpc = 0; lpc < (int) dls.text_line_count();
lpc++)

@ -2,6 +2,6 @@
 ├ org.lnav.test:
 ╰ Hello, World!
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
📝 Annotations available, use :annotate to apply them to this line
 📝 Annotations available, use :annotate to apply them to this line
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
📝 Annotations available, use :annotate to apply them to this line
 📝 Annotations available, use :annotate to apply them to this line

@ -1,4 +1,4 @@
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
└ #foo
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"

@ -1,4 +1,4 @@
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
Hello, World!
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"

Loading…
Cancel
Save