1 /** 2 * Copyright (c) 2007-2012, Timothy Stack 3 * 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * * Redistributions of source code must retain the above copyright notice, this 10 * list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * * Neither the name of Timothy Stack nor the names of its contributors 15 * may be used to endorse or promote products derived from this software 16 * without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY 22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * 29 * @file textview_curses.hh 30 */ 31 32 #ifndef textview_curses_hh 33 #define textview_curses_hh 34 35 #include <utility> 36 #include <vector> 37 38 #include "base/func_util.hh" 39 #include "ring_span.hh" 40 #include "grep_proc.hh" 41 #include "bookmarks.hh" 42 #include "listview_curses.hh" 43 #include "base/lnav_log.hh" 44 #include "text_format.hh" 45 #include "logfile.hh" 46 #include "highlighter.hh" 47 #include "lnav_config_fwd.hh" 48 #include "textview_curses_fwd.hh" 49 50 class logline; 51 class textview_curses; 52 53 using vis_bookmarks = bookmarks<vis_line_t>::type; 54 55 class logfile_filter_state { 56 public: logfile_filter_state(std::shared_ptr<logfile> lf=nullptr)57 logfile_filter_state(std::shared_ptr<logfile> lf = nullptr) : tfs_logfile( 58 std::move(lf)) { 59 memset(this->tfs_filter_count, 0, sizeof(this->tfs_filter_count)); 60 memset(this->tfs_filter_hits, 0, sizeof(this->tfs_filter_hits)); 61 memset(this->tfs_message_matched, 0, sizeof(this->tfs_message_matched)); 62 memset(this->tfs_lines_for_message, 0, sizeof(this->tfs_lines_for_message)); 63 memset(this->tfs_last_message_matched, 0, sizeof(this->tfs_last_message_matched)); 64 memset(this->tfs_last_lines_for_message, 0, sizeof(this->tfs_last_lines_for_message)); 65 this->tfs_mask.reserve(64 * 1024); 66 }; 67 clear()68 void clear() { 69 this->tfs_logfile = nullptr; 70 memset(this->tfs_filter_count, 0, sizeof(this->tfs_filter_count)); 71 memset(this->tfs_filter_hits, 0, sizeof(this->tfs_filter_hits)); 72 memset(this->tfs_message_matched, 0, sizeof(this->tfs_message_matched)); 73 memset(this->tfs_lines_for_message, 0, sizeof(this->tfs_lines_for_message)); 74 memset(this->tfs_last_message_matched, 0, sizeof(this->tfs_last_message_matched)); 75 memset(this->tfs_last_lines_for_message, 0, sizeof(this->tfs_last_lines_for_message)); 76 this->tfs_mask.clear(); 77 this->tfs_index.clear(); 78 }; 79 clear_filter_state(size_t index)80 void clear_filter_state(size_t index) { 81 this->tfs_filter_count[index] = 0; 82 this->tfs_filter_hits[index] = 0; 83 this->tfs_message_matched[index] = false; 84 this->tfs_lines_for_message[index] = 0; 85 this->tfs_last_message_matched[index] = false; 86 this->tfs_last_lines_for_message[index] = 0; 87 }; 88 clear_deleted_filter_state(uint32_t used_mask)89 void clear_deleted_filter_state(uint32_t used_mask) { 90 for (int lpc = 0; lpc < MAX_FILTERS; lpc++) { 91 if (!(used_mask & (1L << lpc))) { 92 this->clear_filter_state(lpc); 93 } 94 } 95 for (size_t lpc = 0; lpc < this->tfs_mask.size(); lpc++) { 96 this->tfs_mask[lpc] &= used_mask; 97 } 98 } 99 resize(size_t newsize)100 void resize(size_t newsize) { 101 size_t old_mask_size = this->tfs_mask.size(); 102 103 this->tfs_mask.resize(newsize); 104 if (newsize > old_mask_size) { 105 memset(&this->tfs_mask[old_mask_size], 106 0, 107 sizeof(uint32_t) * (newsize - old_mask_size)); 108 } 109 }; 110 111 const static int MAX_FILTERS = 32; 112 113 std::shared_ptr<logfile> tfs_logfile; 114 size_t tfs_filter_count[MAX_FILTERS]; 115 int tfs_filter_hits[MAX_FILTERS]; 116 bool tfs_message_matched[MAX_FILTERS]; 117 size_t tfs_lines_for_message[MAX_FILTERS]; 118 bool tfs_last_message_matched[MAX_FILTERS]; 119 size_t tfs_last_lines_for_message[MAX_FILTERS]; 120 std::vector<uint32_t> tfs_mask; 121 std::vector<uint32_t> tfs_index; 122 }; 123 124 enum class filter_lang_t : int { 125 NONE, 126 REGEX, 127 SQL, 128 }; 129 130 class text_filter { 131 public: 132 typedef enum { 133 MAYBE, 134 INCLUDE, 135 EXCLUDE, 136 137 LFT__MAX, 138 139 LFT__MASK = (MAYBE|INCLUDE|EXCLUDE) 140 } type_t; 141 text_filter(type_t type,filter_lang_t lang,std::string id,size_t index)142 text_filter(type_t type, filter_lang_t lang, std::string id, size_t index) 143 : lf_type(type), 144 lf_lang(lang), 145 lf_id(std::move(id)), 146 lf_index(index) { }; 147 virtual ~text_filter() = default; 148 get_type() const149 type_t get_type() const { return this->lf_type; } get_lang() const150 filter_lang_t get_lang() const { return this->lf_lang; } set_type(type_t t)151 void set_type(type_t t) { this->lf_type = t; }; get_id() const152 std::string get_id() const { return this->lf_id; }; set_id(std::string id)153 void set_id(std::string id) { 154 this->lf_id = std::move(id); 155 } get_index() const156 size_t get_index() const { return this->lf_index; }; 157 is_enabled() const158 bool is_enabled() const { return this->lf_enabled; }; enable()159 void enable() { this->lf_enabled = true; }; disable()160 void disable() { this->lf_enabled = false; }; set_enabled(bool value)161 void set_enabled(bool value) { 162 this->lf_enabled = value; 163 } 164 165 void revert_to_last(logfile_filter_state &lfs, size_t rollback_size); 166 167 void add_line(logfile_filter_state &lfs, logfile::const_iterator ll, shared_buffer_ref &line); 168 169 void end_of_message(logfile_filter_state &lfs); 170 171 virtual bool matches(const logfile &lf, logfile::const_iterator ll, shared_buffer_ref &line) = 0; 172 173 virtual std::string to_command() = 0; 174 operator ==(const std::string & rhs)175 bool operator==(const std::string &rhs) { 176 return this->lf_id == rhs; 177 }; 178 179 bool lf_deleted{false}; 180 181 protected: 182 bool lf_enabled{true}; 183 type_t lf_type; 184 filter_lang_t lf_lang; 185 std::string lf_id; 186 size_t lf_index; 187 }; 188 189 class empty_filter : public text_filter { 190 public: empty_filter(type_t type,size_t index)191 empty_filter(type_t type, size_t index) 192 : text_filter(type, filter_lang_t::REGEX, "", index) { 193 } 194 195 bool matches(const logfile &lf, logfile::const_iterator ll, 196 shared_buffer_ref &line) override; 197 198 std::string to_command() override; 199 }; 200 201 class filter_stack { 202 public: 203 typedef std::vector<std::shared_ptr<text_filter>>::iterator iterator; 204 filter_stack(size_t reserved=0)205 explicit filter_stack(size_t reserved = 0) : fs_reserved(reserved) { 206 } 207 begin()208 iterator begin() { 209 return this->fs_filters.begin(); 210 } 211 end()212 iterator end() { 213 return this->fs_filters.end(); 214 } 215 size() const216 size_t size() const { 217 return this->fs_filters.size(); 218 } 219 empty() const220 bool empty() const { 221 return this->fs_filters.empty(); 222 }; 223 full() const224 bool full() const { 225 return (this->fs_reserved + this->fs_filters.size()) == 226 logfile_filter_state::MAX_FILTERS; 227 } 228 next_index()229 nonstd::optional<size_t> next_index() { 230 bool used[32]; 231 232 memset(used, 0, sizeof(used)); 233 for (auto &iter : *this) { 234 if (iter->lf_deleted) { 235 continue; 236 } 237 238 size_t index = iter->get_index(); 239 240 require(used[index] == false); 241 242 used[index] = true; 243 } 244 for (size_t lpc = this->fs_reserved; 245 lpc < logfile_filter_state::MAX_FILTERS; 246 lpc++) { 247 if (!used[lpc]) { 248 return lpc; 249 } 250 } 251 return nonstd::nullopt; 252 }; 253 add_filter(const std::shared_ptr<text_filter> & filter)254 void add_filter(const std::shared_ptr<text_filter> &filter) { 255 this->fs_filters.push_back(filter); 256 }; 257 clear_filters()258 void clear_filters() { 259 while (!this->fs_filters.empty()) { 260 this->fs_filters.pop_back(); 261 } 262 }; 263 set_filter_enabled(const std::shared_ptr<text_filter> & filter,bool enabled)264 void set_filter_enabled(const std::shared_ptr<text_filter> &filter, bool enabled) { 265 if (enabled) { 266 filter->enable(); 267 } 268 else { 269 filter->disable(); 270 } 271 } 272 get_filter(const std::string & id)273 std::shared_ptr<text_filter> get_filter(const std::string &id) 274 { 275 auto iter = this->fs_filters.begin(); 276 std::shared_ptr<text_filter> retval; 277 278 for (; 279 iter != this->fs_filters.end() && (*iter)->get_id() != id; 280 iter++) { } 281 if (iter != this->fs_filters.end()) { 282 retval = *iter; 283 } 284 285 return retval; 286 }; 287 delete_filter(const std::string & id)288 bool delete_filter(const std::string &id) { 289 auto iter = this->fs_filters.begin(); 290 291 for (; 292 iter != this->fs_filters.end() && (*iter)->get_id() != id; 293 iter++) { 294 295 } 296 if (iter != this->fs_filters.end()) { 297 this->fs_filters.erase(iter); 298 return true; 299 } 300 301 return false; 302 }; 303 get_mask(uint32_t & filter_mask)304 void get_mask(uint32_t &filter_mask) { 305 filter_mask = 0; 306 for (auto &iter : *this) { 307 std::shared_ptr<text_filter> tf = iter; 308 309 if (tf->lf_deleted) { 310 continue; 311 } 312 if (tf->is_enabled()) { 313 uint32_t bit = (1UL << tf->get_index()); 314 315 switch (tf->get_type()) { 316 case text_filter::EXCLUDE: 317 case text_filter::INCLUDE: 318 filter_mask |= bit; 319 break; 320 default: 321 ensure(0); 322 break; 323 } 324 } 325 } 326 } 327 get_enabled_mask(uint32_t & filter_in_mask,uint32_t & filter_out_mask)328 void get_enabled_mask(uint32_t &filter_in_mask, uint32_t &filter_out_mask) { 329 filter_in_mask = filter_out_mask = 0; 330 for (auto &iter : *this) { 331 std::shared_ptr<text_filter> tf = iter; 332 333 if (tf->lf_deleted) { 334 continue; 335 } 336 if (tf->is_enabled()) { 337 uint32_t bit = (1UL << tf->get_index()); 338 339 switch (tf->get_type()) { 340 case text_filter::EXCLUDE: 341 filter_out_mask |= bit; 342 break; 343 case text_filter::INCLUDE: 344 filter_in_mask |= bit; 345 break; 346 default: 347 ensure(0); 348 break; 349 } 350 } 351 } 352 }; 353 354 private: 355 const size_t fs_reserved; 356 std::vector<std::shared_ptr<text_filter>> fs_filters; 357 }; 358 359 class text_time_translator { 360 public: 361 virtual ~text_time_translator() = default; 362 363 virtual nonstd::optional<vis_line_t> row_for_time(struct timeval time_bucket) = 0; 364 365 virtual nonstd::optional<struct timeval> time_for_row(vis_line_t row) = 0; 366 367 void scroll_invoked(textview_curses *tc); 368 369 void data_reloaded(textview_curses *tc); 370 protected: 371 struct timeval ttt_top_time{0, 0}; 372 }; 373 374 class location_history { 375 public: 376 virtual ~location_history() = default; 377 378 virtual void loc_history_append(vis_line_t top) = 0; 379 380 virtual nonstd::optional<vis_line_t> 381 loc_history_back(vis_line_t current_top) = 0; 382 383 virtual nonstd::optional<vis_line_t> 384 loc_history_forward(vis_line_t current_top) = 0; 385 386 const static int MAX_SIZE = 100; 387 protected: 388 size_t lh_history_position{0}; 389 }; 390 391 /** 392 * Source for the text to be shown in a textview_curses view. 393 */ 394 class text_sub_source { 395 public: 396 virtual ~text_sub_source() = default; 397 398 enum { 399 RB_RAW, 400 RB_FULL, 401 RB_REWRITE, 402 }; 403 404 enum { 405 RF_RAW = (1UL << RB_RAW), 406 RF_FULL = (1UL << RB_FULL), 407 RF_REWRITE = (1UL << RB_REWRITE), 408 }; 409 410 typedef long line_flags_t; 411 text_sub_source(size_t reserved_filters=0)412 text_sub_source(size_t reserved_filters = 0) 413 : tss_filters(reserved_filters) { 414 } 415 register_view(textview_curses * tc)416 void register_view(textview_curses *tc) { 417 this->tss_view = tc; 418 }; 419 420 /** 421 * @return The total number of lines available from the source. 422 */ 423 virtual size_t text_line_count() = 0; 424 text_line_width(textview_curses & curses)425 virtual size_t text_line_width(textview_curses &curses) { 426 return INT_MAX; 427 }; 428 429 /** 430 * Get the value for a line. 431 * 432 * @param tc The textview_curses object that is delegating control. 433 * @param line The line number to retrieve. 434 * @param value_out The string object that should be set to the line 435 * contents. 436 * @param raw Indicates that the raw contents of the line should be returned 437 * without any post processing. 438 */ 439 virtual void text_value_for_line(textview_curses &tc, 440 int line, 441 std::string &value_out, 442 line_flags_t flags = 0) = 0; 443 444 virtual size_t text_size_for_line(textview_curses &tc, int line, line_flags_t raw = 0) = 0; 445 446 /** 447 * Inform the source that the given line has been marked/unmarked. This 448 * callback function can be used to translate between between visible line 449 * numbers and content line numbers. For example, when viewing a log file 450 * with filters being applied, we want the bookmarked lines to be stable 451 * across changes in the filters. 452 * 453 * @param bm The type of bookmark. 454 * @param line The line that has been marked/unmarked. 455 * @param added True if the line was bookmarked and false if it was 456 * unmarked. 457 */ text_mark(bookmark_type_t * bm,vis_line_t line,bool added)458 virtual void text_mark(bookmark_type_t *bm, vis_line_t line, bool added) {}; 459 460 /** 461 * Clear the bookmarks for a particular type in the text source. 462 * 463 * @param bm The type of bookmarks to clear. 464 */ text_clear_marks(bookmark_type_t * bm)465 virtual void text_clear_marks(bookmark_type_t *bm) {}; 466 467 /** 468 * Get the attributes for a line of text. 469 * 470 * @param tc The textview_curses object that is delegating control. 471 * @param line The line number to retrieve. 472 * @param value_out A string_attrs_t object that should be updated with the 473 * attributes for the line. 474 */ text_attrs_for_line(textview_curses & tc,int line,string_attrs_t & value_out)475 virtual void text_attrs_for_line(textview_curses &tc, 476 int line, 477 string_attrs_t &value_out) {}; 478 479 /** 480 * Update the bookmarks used by the text view based on the bookmarks 481 * maintained by the text source. 482 * 483 * @param bm The bookmarks data structure used by the text view. 484 */ text_update_marks(vis_bookmarks & bm)485 virtual void text_update_marks(vis_bookmarks &bm) { }; 486 text_source_name(const textview_curses & tv)487 virtual std::string text_source_name(const textview_curses &tv) { 488 return ""; 489 }; 490 get_filters()491 filter_stack &get_filters() { 492 return this->tss_filters; 493 }; 494 text_filters_changed()495 virtual void text_filters_changed() { 496 497 }; 498 get_filtered_count() const499 virtual int get_filtered_count() const { 500 return 0; 501 }; 502 get_filtered_count_for(size_t filter_index) const503 virtual int get_filtered_count_for(size_t filter_index) const { 504 return 0; 505 } 506 get_text_format() const507 virtual text_format_t get_text_format() const { 508 return text_format_t::TF_UNKNOWN; 509 }; 510 get_grepper()511 virtual nonstd::optional<std::pair<grep_proc_source<vis_line_t> *, grep_proc_sink<vis_line_t> *>> get_grepper() { 512 return nonstd::nullopt; 513 } 514 get_location_history()515 virtual nonstd::optional<location_history *> get_location_history() { 516 return nonstd::nullopt; 517 } 518 toggle_apply_filters()519 void toggle_apply_filters() { 520 this->tss_apply_filters = !this->tss_apply_filters; 521 this->text_filters_changed(); 522 } 523 524 bool tss_supports_filtering{false}; 525 bool tss_apply_filters{true}; 526 protected: 527 textview_curses *tss_view{nullptr}; 528 filter_stack tss_filters; 529 }; 530 531 class vis_location_history : public location_history { 532 public: vis_location_history()533 vis_location_history() 534 : vlh_history(std::begin(this->vlh_backing), std::end(this->vlh_backing)) 535 { 536 } 537 loc_history_append(vis_line_t top)538 void loc_history_append(vis_line_t top) override { 539 auto iter = this->vlh_history.begin(); 540 iter += this->vlh_history.size() - this->lh_history_position; 541 this->vlh_history.erase_from(iter); 542 this->lh_history_position = 0; 543 this->vlh_history.push_back(top); 544 } 545 546 nonstd::optional<vis_line_t> loc_history_back(vis_line_t current_top)547 loc_history_back(vis_line_t current_top) override { 548 if (this->lh_history_position == 0) { 549 vis_line_t history_top = this->current_position(); 550 if (history_top != current_top) { 551 return history_top; 552 } 553 } 554 555 if (this->lh_history_position + 1 >= this->vlh_history.size()) { 556 return nonstd::nullopt; 557 } 558 559 this->lh_history_position += 1; 560 561 return this->current_position(); 562 } 563 564 nonstd::optional<vis_line_t> loc_history_forward(vis_line_t current_top)565 loc_history_forward(vis_line_t current_top) override { 566 if (this->lh_history_position == 0) { 567 return nonstd::nullopt; 568 } 569 570 this->lh_history_position -= 1; 571 572 return this->current_position(); 573 } 574 575 nonstd::ring_span<vis_line_t> vlh_history; 576 private: current_position()577 vis_line_t current_position() { 578 auto iter = this->vlh_history.rbegin(); 579 580 iter += this->lh_history_position; 581 582 return *iter; 583 } 584 585 vis_line_t vlh_backing[MAX_SIZE]; 586 }; 587 588 class text_delegate { 589 public: 590 virtual ~text_delegate() = default; 591 text_overlay(textview_curses & tc)592 virtual void text_overlay(textview_curses &tc) { }; 593 text_handle_mouse(textview_curses & tc,mouse_event & me)594 virtual bool text_handle_mouse(textview_curses &tc, mouse_event &me) { 595 return false; 596 }; 597 }; 598 599 /** 600 * The textview_curses class adds user bookmarks and searching to the standard 601 * list view interface. 602 */ 603 class textview_curses 604 : public listview_curses, 605 public list_data_source, 606 public grep_proc_source<vis_line_t>, 607 public grep_proc_sink<vis_line_t>, 608 public lnav_config_listener { 609 public: 610 611 using action = std::function<void(textview_curses*)>; 612 613 static bookmark_type_t BM_USER; 614 static bookmark_type_t BM_USER_EXPR; 615 static bookmark_type_t BM_SEARCH; 616 static bookmark_type_t BM_META; 617 618 textview_curses(); 619 620 ~textview_curses(); 621 622 void reload_config(error_reporter &reporter); 623 set_paused(bool paused)624 void set_paused(bool paused) { 625 this->tc_paused = paused; 626 if (this->tc_state_event_handler) { 627 this->tc_state_event_handler(*this); 628 } 629 } 630 is_paused() const631 bool is_paused() const { 632 return this->tc_paused; 633 } 634 get_bookmarks()635 vis_bookmarks &get_bookmarks() { return this->tc_bookmarks; }; 636 get_bookmarks() const637 const vis_bookmarks &get_bookmarks() const { return this->tc_bookmarks; }; 638 639 void toggle_user_mark(bookmark_type_t *bm, 640 vis_line_t start_line, 641 vis_line_t end_line = vis_line_t(-1)); 642 643 void set_user_mark(bookmark_type_t *bm, vis_line_t vl, bool marked); 644 set_sub_source(text_sub_source * src)645 textview_curses &set_sub_source(text_sub_source *src) { 646 this->tc_sub_source = src; 647 if (src) { 648 src->register_view(this); 649 } 650 this->reload_data(); 651 return *this; 652 }; 653 get_sub_source() const654 text_sub_source *get_sub_source() const { return this->tc_sub_source; }; 655 set_delegate(text_delegate * del)656 textview_curses &set_delegate(text_delegate *del) { 657 this->tc_delegate = del; 658 659 return *this; 660 }; 661 get_delegate() const662 text_delegate *get_delegate() const { return this->tc_delegate; }; 663 664 void horiz_shift(vis_line_t start, vis_line_t end, 665 int off_start, 666 std::pair<int, int> &range_out); 667 set_search_action(action sa)668 void set_search_action(action sa) { this->tc_search_action = std::move(sa); }; 669 670 void grep_end_batch(grep_proc<vis_line_t> &gp); 671 void grep_end(grep_proc<vis_line_t> &gp); 672 listview_rows(const listview_curses & lv)673 size_t listview_rows(const listview_curses &lv) 674 { 675 return this->tc_sub_source == nullptr ? 0 : 676 this->tc_sub_source->text_line_count(); 677 }; 678 listview_width(const listview_curses & lv)679 size_t listview_width(const listview_curses &lv) { 680 return this->tc_sub_source == nullptr ? 0 : 681 this->tc_sub_source->text_line_width(*this); 682 }; 683 684 void listview_value_for_rows(const listview_curses &lv, 685 vis_line_t line, 686 std::vector<attr_line_t> &rows_out); 687 688 void textview_value_for_row(vis_line_t line, attr_line_t &value_out); 689 listview_size_for_row(const listview_curses & lv,vis_line_t row)690 size_t listview_size_for_row(const listview_curses &lv, vis_line_t row) { 691 return this->tc_sub_source->text_size_for_line(*this, row); 692 }; 693 listview_source_name(const listview_curses & lv)694 std::string listview_source_name(const listview_curses &lv) { 695 return this->tc_sub_source == nullptr ? "" : 696 this->tc_sub_source->text_source_name(*this); 697 }; 698 grep_value_for_line(vis_line_t line,std::string & value_out)699 bool grep_value_for_line(vis_line_t line, std::string &value_out) 700 { 701 bool retval = false; 702 703 if (this->tc_sub_source && 704 line < (int)this->tc_sub_source->text_line_count()) { 705 this->tc_sub_source->text_value_for_line(*this, 706 line, 707 value_out, 708 text_sub_source::RF_RAW); 709 retval = true; 710 } 711 712 return retval; 713 }; 714 715 void grep_begin(grep_proc<vis_line_t> &gp, vis_line_t start, vis_line_t stop); 716 void grep_match(grep_proc<vis_line_t> &gp, 717 vis_line_t line, 718 int start, 719 int end); 720 is_searching() const721 bool is_searching() const { return this->tc_searching > 0; }; 722 set_follow_search_for(int64_t ms_to_deadline,std::function<bool ()> func)723 void set_follow_search_for(int64_t ms_to_deadline, 724 std::function<bool()> func) { 725 struct timeval now, tv; 726 727 tv.tv_sec = ms_to_deadline / 1000; 728 tv.tv_usec = (ms_to_deadline % 1000) * 1000; 729 gettimeofday(&now, nullptr); 730 timeradd(&now, &tv, &this->tc_follow_deadline); 731 this->tc_follow_top = this->get_top(); 732 this->tc_follow_func = func; 733 }; 734 get_match_count()735 size_t get_match_count() 736 { 737 return this->tc_bookmarks[&BM_SEARCH].size(); 738 }; 739 match_reset()740 void match_reset() 741 { 742 this->tc_bookmarks[&BM_SEARCH].clear(); 743 if (this->tc_sub_source != nullptr) { 744 this->tc_sub_source->text_clear_marks(&BM_SEARCH); 745 } 746 }; 747 get_highlights()748 highlight_map_t &get_highlights() { return this->tc_highlights; }; 749 get_highlights() const750 const highlight_map_t &get_highlights() const { return this->tc_highlights; }; 751 get_disabled_highlights()752 std::set<highlight_source_t> &get_disabled_highlights() { 753 return this->tc_disabled_highlights; 754 } 755 756 bool handle_mouse(mouse_event &me); 757 758 void reload_data(); 759 do_update()760 void do_update() { 761 this->listview_curses::do_update(); 762 if (this->tc_delegate != nullptr) { 763 this->tc_delegate->text_overlay(*this); 764 } 765 }; 766 toggle_hide_fields()767 bool toggle_hide_fields() { 768 bool retval = this->tc_hide_fields; 769 770 this->tc_hide_fields = !this->tc_hide_fields; 771 772 return retval; 773 }; 774 get_hide_fields() const775 bool get_hide_fields() const { 776 return this->tc_hide_fields; 777 } 778 779 void execute_search(const std::string ®ex_orig); 780 redo_search()781 void redo_search() { 782 if (this->tc_search_child) { 783 grep_proc<vis_line_t> *gp = this->tc_search_child->get_grep_proc(); 784 785 gp->invalidate(); 786 this->match_reset(); 787 gp->queue_request(0_vl) 788 .start(); 789 790 if (this->tc_source_search_child) { 791 this->tc_source_search_child->invalidate() 792 .queue_request(0_vl) 793 .start(); 794 } 795 } 796 }; 797 search_range(vis_line_t start,vis_line_t stop=-1_vl)798 void search_range(vis_line_t start, vis_line_t stop = -1_vl) { 799 if (this->tc_search_child) { 800 this->tc_search_child->get_grep_proc()->queue_request(start, stop); 801 } 802 if (this->tc_source_search_child) { 803 this->tc_source_search_child->queue_request(start, stop); 804 } 805 } 806 search_new_data(vis_line_t start=-1_vl)807 void search_new_data(vis_line_t start = -1_vl) { 808 this->search_range(start); 809 if (this->tc_search_child) { 810 this->tc_search_child->get_grep_proc()->start(); 811 } 812 if (this->tc_source_search_child) { 813 this->tc_source_search_child->start(); 814 } 815 } 816 update_poll_set(std::vector<struct pollfd> & pollfds)817 void update_poll_set(std::vector<struct pollfd> &pollfds) { 818 if (this->tc_search_child) { 819 this->tc_search_child->get_grep_proc()->update_poll_set(pollfds); 820 } 821 if (this->tc_source_search_child) { 822 this->tc_source_search_child->update_poll_set(pollfds); 823 } 824 } 825 check_poll_set(const std::vector<struct pollfd> & pollfds)826 void check_poll_set(const std::vector<struct pollfd> &pollfds) { 827 if (this->tc_search_child) { 828 this->tc_search_child->get_grep_proc()->check_poll_set(pollfds); 829 } 830 if (this->tc_source_search_child) { 831 this->tc_source_search_child->check_poll_set(pollfds); 832 } 833 } 834 get_current_search() const835 std::string get_current_search() const { 836 return this->tc_current_search; 837 } 838 revert_search()839 void revert_search() { 840 this->execute_search(this->tc_previous_search); 841 } 842 invoke_scroll()843 void invoke_scroll() { 844 if (this->tc_sub_source != nullptr) { 845 auto ttt = dynamic_cast<text_time_translator *>(this->tc_sub_source); 846 847 if (ttt != nullptr) { 848 ttt->scroll_invoked(this); 849 } 850 } 851 852 listview_curses::invoke_scroll(); 853 } 854 855 std::function<void(textview_curses &)> tc_state_event_handler; 856 857 protected: 858 859 class grep_highlighter { 860 public: grep_highlighter(std::unique_ptr<grep_proc<vis_line_t>> & gp,highlight_source_t source,std::string hl_name,highlight_map_t & hl_map)861 grep_highlighter(std::unique_ptr<grep_proc<vis_line_t>> &gp, 862 highlight_source_t source, 863 std::string hl_name, 864 highlight_map_t &hl_map) 865 : gh_grep_proc(std::move(gp)), 866 gh_hl_source(source), 867 gh_hl_name(std::move(hl_name)), 868 gh_hl_map(hl_map) { }; 869 ~grep_highlighter()870 ~grep_highlighter() 871 { 872 this->gh_hl_map.erase(this->gh_hl_map.find( 873 {this->gh_hl_source, this->gh_hl_name})); 874 }; 875 get_grep_proc()876 grep_proc<vis_line_t> *get_grep_proc() { return this->gh_grep_proc.get(); }; 877 878 private: 879 std::unique_ptr<grep_proc<vis_line_t>> gh_grep_proc; 880 highlight_source_t gh_hl_source; 881 std::string gh_hl_name; 882 highlight_map_t &gh_hl_map; 883 }; 884 885 text_sub_source *tc_sub_source{nullptr}; 886 text_delegate *tc_delegate{nullptr}; 887 888 vis_bookmarks tc_bookmarks; 889 890 int tc_searching{0}; 891 struct timeval tc_follow_deadline{0, 0}; 892 vis_line_t tc_follow_top{-1_vl}; 893 std::function<bool()> tc_follow_func; 894 action tc_search_action; 895 896 highlight_map_t tc_highlights; 897 std::set<highlight_source_t> tc_disabled_highlights; 898 899 vis_line_t tc_selection_start{-1_vl}; 900 vis_line_t tc_selection_last{-1_vl}; 901 bool tc_selection_cleared{false}; 902 bool tc_hide_fields{true}; 903 bool tc_paused{false}; 904 905 std::string tc_current_search; 906 std::string tc_previous_search; 907 std::unique_ptr<grep_highlighter> tc_search_child; 908 std::shared_ptr<grep_proc<vis_line_t>> tc_source_search_child; 909 }; 910 911 #endif 912