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 &regex_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