FINAL CUT
ftextview.h
1 /***********************************************************************
2 * ftextview.h - Widget FTextView (a multiline text viewer) *
3 * *
4 * This file is part of the FINAL CUT widget toolkit *
5 * *
6 * Copyright 2014-2024 Markus Gans *
7 * *
8 * FINAL CUT is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU Lesser General Public License as *
10 * published by the Free Software Foundation; either version 3 of *
11 * the License, or (at your option) any later version. *
12 * *
13 * FINAL CUT is distributed in the hope that it will be useful, but *
14 * WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU Lesser General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU Lesser General Public *
19 * License along with this program. If not, see *
20 * <http://www.gnu.org/licenses/>. *
21 ***********************************************************************/
22 
23 /* Inheritance diagram
24  * ═══════════════════
25  *
26  * ▕▔▔▔▔▔▔▔▔▔▏ ▕▔▔▔▔▔▔▔▔▔▏
27  * ▕ FVTerm ▏ ▕ FObject ▏
28  * ▕▁▁▁▁▁▁▁▁▁▏ ▕▁▁▁▁▁▁▁▁▁▏
29  * ▲ ▲
30  * │ │
31  * └─────┬─────┘
32  * │
33  * ▕▔▔▔▔▔▔▔▔▔▏
34  * ▕ FWidget ▏
35  * ▕▁▁▁▁▁▁▁▁▁▏
36  * ▲
37  * │
38  * ▕▔▔▔▔▔▔▔▔▔▔▔▏
39  * ▕ FTextView ▏
40  * ▕▁▁▁▁▁▁▁▁▁▁▁▏
41  */
42 
43 #ifndef FTEXTVIEW_H
44 #define FTEXTVIEW_H
45 
46 #if !defined (USE_FINAL_H) && !defined (COMPILE_FINAL_CUT)
47  #error "Only <final/final.h> can be included directly."
48 #endif
49 
50 #include <limits>
51 #include <limits>
52 #include <memory>
53 #include <memory>
54 #include <string>
55 #include <unordered_map>
56 #include <utility>
57 #include <utility>
58 #include <vector>
59 
60 #include "final/fwidgetcolors.h"
61 #include "final/fwidget.h"
62 #include "final/util/fstring.h"
63 #include "final/util/fstringstream.h"
64 #include "final/vterm/fcolorpair.h"
65 #include "final/vterm/fstyle.h"
66 
67 namespace finalcut
68 {
69 
70 // class forward declaration
71 class FScrollbar;
72 
73 // Global using-declaration
74 using FScrollbarPtr = std::shared_ptr<FScrollbar>;
75 
76 //----------------------------------------------------------------------
77 // class FTextView
78 //----------------------------------------------------------------------
79 
80 class FTextView : public FWidget
81 {
82  public:
84  {
85  // Constants
86  static constexpr std::size_t EOL = std::numeric_limits<std::size_t>::max();
87 
88  FTextHighlight (std::size_t i, std::size_t l, const FChar& fchar) noexcept
89  : index{i}
90  , length{l}
91  , attributes{fchar}
92  { }
93 
94  FTextHighlight (std::size_t i, const FChar& fchar) noexcept
95  : FTextHighlight{i, EOL, fchar}
96  { }
97 
98  FTextHighlight (std::size_t i, std::size_t l, const FStyle& s) noexcept
99  : index{i}
100  , length{l}
101  {
102  auto wc = getColorTheme();
103  attributes.fg_color = wc->dialog.fg;
104  attributes.bg_color = wc->dialog.bg;
105  attributes.attr = s.toFAttribute();
106  }
107 
108  FTextHighlight (std::size_t i, const FStyle& s) noexcept
109  : FTextHighlight{i, EOL, s}
110  { }
111 
112  FTextHighlight (std::size_t i, std::size_t l, FColor c, const FStyle& s = FStyle()) noexcept
113  : index{i}
114  , length{l}
115  {
116  attributes.fg_color = c;
117  attributes.bg_color = getColorTheme()->dialog.bg;
118  attributes.attr = s.toFAttribute();
119  }
120 
121  FTextHighlight (std::size_t i, FColor c, const FStyle& s = FStyle()) noexcept
122  : FTextHighlight{i, EOL, c, s}
123  { }
124 
125  FTextHighlight (std::size_t i, std::size_t l, const FColorPair& cpair, const FStyle& s = FStyle()) noexcept
126  : index{i}
127  , length{l}
128  {
129  attributes.fg_color = cpair.getForegroundColor();
130  attributes.bg_color = cpair.getBackgroundColor();
131  attributes.attr = s.toFAttribute();
132  }
133 
134  FTextHighlight (std::size_t i, const FColorPair& cpair, const FStyle& s = FStyle()) noexcept
135  : FTextHighlight{i, EOL, cpair, s}
136  { }
137 
138  // Data members
139  std::size_t index{};
140  std::size_t length{};
141  FChar attributes{};
142  };
143 
145  {
146  explicit FTextViewLine (FString&& s, std::vector<FTextHighlight>&& v = {}) noexcept
147  : text{std::move(s)}
148  , highlight{std::move(v)}
149  { }
150 
151  FString text{};
152  std::vector<FTextHighlight> highlight{};
153  };
154 
155  // Using-declarations
156  using FTextViewList = std::vector<FTextViewLine>;
157  using FWidget::setGeometry;
158 
160  {
161  FTextViewList::size_type row{UNINITIALIZED_ROW};
162  FString::size_type column{UNINITIALIZED_COLUMN};
163  };
164 
165  // Constructor
166  explicit FTextView (FWidget* = nullptr);
167 
168  // Destructor
169  ~FTextView() noexcept override;
170 
171  // Overloaded operators
172  auto operator = (const FString&) -> FTextView&;
173  template <typename typeT>
174  auto operator << (const typeT&) -> FTextView&;
175  auto operator << (const UniChar&) -> FTextView&;
176  auto operator << (const std::string&) -> FTextView&;
177 
178  // Accessors
179  auto getClassName() const -> FString override;
180  auto getColumns() const noexcept -> std::size_t;
181  auto getRows() const -> std::size_t;
182  auto getScrollPos() const -> FPoint;
183  auto getTextVisibleSize() const -> FSize;
184  auto getText() const -> FString;
185  auto getSelectedText() const -> FString;
186  auto getSelectionStart() const -> FTextPosition;
187  auto getSelectionEnd() const -> FTextPosition;
188  auto getLine (FTextViewList::size_type) -> FTextViewLine&;
189  auto getLine (FTextViewList::size_type) const -> const FTextViewLine&;
190  auto getLines() const & -> const FTextViewList&;
191 
192  // Mutators
193  void setSize (const FSize&, bool = true) override;
194  void setGeometry (const FPoint&, const FSize&, bool = true) override;
195  void resetColors() override;
196  void setText (const FString&);
197  void addHighlight (std::size_t, const FTextHighlight&);
198  void resetHighlight (std::size_t);
199  void setSelectionStart (const FTextViewList::size_type, const FString::size_type);
200  void setSelectionEnd (const FTextViewList::size_type, const FString::size_type);
201  void resetSelection();
202  template <typename T>
203  void setLines (T&&);
204  void setSelectable (bool = true);
205  void unsetSelectable();
206  void scrollToX (int);
207  void scrollToY (int);
208  void scrollTo (const FPoint&);
209  void scrollTo (int, int);
210  void scrollToBegin();
211  void scrollToEnd();
212  void scrollBy (int, int);
213 
214  // Inquiry
215  auto hasSelectedText() const -> bool;
216  auto isSelectable() const -> bool;
217 
218  // Methods
219  void hide() override;
220  void clear();
221  template <typename T>
222  void append (const std::initializer_list<T>&);
223  void append (const FString&);
224  template <typename T>
225  void insert (const std::initializer_list<T>&, int);
226  void insert (const FString&, int);
227  void replaceRange (const FString&, int, int);
228  void deleteRange (int, int);
229  void deleteLine (int);
230 
231  // Event handlers
232  void onKeyPress (FKeyEvent*) override;
233  void onMouseDown (FMouseEvent*) override;
234  void onMouseUp (FMouseEvent*) override;
235  void onMouseMove (FMouseEvent*) override;
236  void onMouseDoubleClick (FMouseEvent*) override;
237  void onWheel (FWheelEvent*) override;
238  void onTimer (FTimerEvent*) override;
239 
240  protected:
241  // Method
242  void initLayout() override;
243  void adjustSize() override;
244 
245  // Inquiry
246  auto isHorizontallyScrollable() const -> bool;
247  auto isVerticallyScrollable() const -> bool;
248 
249  // Accessors
250  auto getTextHeight() const -> std::size_t;
251  auto getTextWidth() const -> std::size_t;
252 
253  private:
254  // Constants
255  static constexpr auto UNINITIALIZED_ROW = static_cast<FTextViewList::size_type>(-1);
256  static constexpr auto UNINITIALIZED_COLUMN = static_cast<FString::size_type>(-1);
257 
258  // Using-declaration
259  using KeyMap = std::unordered_map<FKey, std::function<void()>, EnumHash<FKey>>;
260 
261  // Inquiry
262  auto isWithinTextBounds (const FPoint&) const -> bool;
263  auto isLowerRightResizeCorner (const FPoint&) const -> bool;
264  auto hasWrongSelectionOrder() const -> bool;
265 
266  // Methods
267  void init();
268  void mapKeyFunctions();
269  void draw() override;
270  void drawBorder() override;
271  void drawScrollbars() const;
272  void drawText();
273  auto canSkipDrawing() const -> bool;
274  void printLine (std::size_t);
275  void addHighlighting ( FVTermBuffer&
276  , const std::vector<FTextHighlight>& ) const;
277  void addSelection (FVTermBuffer&, std::size_t) const;
278  auto useFDialogBorder() const -> bool;
279  auto isPrintable (wchar_t) const -> bool;
280  auto splitTextLines (const FString&) const -> FStringList;
281  void processLine (FString&&, int);
282  template<typename T1, typename T2>
283  void setSelectionStartInt (T1&&, T2&&);
284  template<typename T1, typename T2>
285  void setSelectionEndInt (T1&&, T2&&);
286  auto getScrollBarMaxHorizontal() const noexcept -> int;
287  auto getScrollBarMaxVertical() const noexcept -> int;
288  void updateVerticalScrollBar() const;
289  void updateHorizontalScrollBar (std::size_t);
290  auto convertMouse2TextPos (const FPoint&) const -> FPoint;
291  void handleMouseWithinListBounds (const FPoint&);
292  void handleMouseDragging (const FMouseEvent*);
293  void handleLeftDragScroll();
294  void handleRightDragScroll();
295  void handleUpDragScroll();
296  void handleDownDragScroll();
297  void dragLeft();
298  void dragRight();
299  void dragUp();
300  void dragDown();
301  void stopDragScroll();
302  void processChanged() const;
303  void changeOnResize() const;
304  auto shouldUpdateScrollbar (FScrollbar::ScrollType) const -> bool;
305  auto getVerticalScrollDistance (const FScrollbar::ScrollType) const -> int;
306  auto getHorizontalScrollDistance (const FScrollbar::ScrollType) const -> int;
307 
308  // Callback methods
309  void cb_vbarChange (const FWidget*);
310  void cb_hbarChange (const FWidget*);
311 
312  // Data members
313  FTextViewList data{};
314  FScrollbarPtr vbar{nullptr};
315  FScrollbarPtr hbar{nullptr};
316  FTextPosition selection_start{};
317  FTextPosition selection_end{};
318  FPoint select_click_pos{-1, -1};
319  KeyMap key_map{};
320  DragScrollMode drag_scroll{DragScrollMode::None};
321  bool update_scrollbar{true};
322  bool pass_to_dialog{false};
323  bool selectable{false};
324  int scroll_repeat{100};
325  int xoffset{0};
326  int yoffset{0};
327  int nf_offset{0};
328  std::size_t max_line_width{0};
329 };
330 
331 // FListBox inline functions
332 //----------------------------------------------------------------------
333 inline auto FTextView::operator = (const FString& s) -> FTextView&
334 {
335  setText(s);
336  return *this;
337 }
338 
339 //----------------------------------------------------------------------
340 template <typename typeT>
341 inline auto FTextView::operator << (const typeT& s) -> FTextView&
342 {
343  FStringStream outstream{std::ios_base::out};
344  outstream << s;
345 
346  if ( ! outstream.str().isEmpty() )
347  append (outstream.str());
348 
349  return *this;
350 }
351 
352 //----------------------------------------------------------------------
353 inline auto FTextView::operator << (const UniChar& c) -> FTextView&
354 {
355  append (static_cast<wchar_t>(c)); // Required under Solaris
356  return *this;
357 }
358 
359 //----------------------------------------------------------------------
360 inline auto FTextView::operator << (const std::string& string) -> FTextView&
361 {
362  append (string);
363  return *this;
364 }
365 
366 //----------------------------------------------------------------------
367 inline auto FTextView::getClassName() const -> FString
368 { return "FTextView"; }
369 
370 //----------------------------------------------------------------------
371 inline auto FTextView::getColumns() const noexcept -> std::size_t
372 { return max_line_width; }
373 
374 //----------------------------------------------------------------------
375 inline auto FTextView::getRows() const -> std::size_t
376 { return data.size(); }
377 
378 //----------------------------------------------------------------------
379 inline auto FTextView::getScrollPos() const -> FPoint
380 { return {xoffset, yoffset}; }
381 
382 //----------------------------------------------------------------------
383 inline auto FTextView::getTextVisibleSize() const -> FSize
384 { return { FSize{getTextWidth(), getTextHeight()} }; }
385 
386 //----------------------------------------------------------------------
387 inline auto FTextView::getSelectionStart() const -> FTextPosition
388 { return selection_start; }
389 
390 //----------------------------------------------------------------------
391 inline auto FTextView::getSelectionEnd() const -> FTextPosition
392 { return selection_end; }
393 
394 //----------------------------------------------------------------------
395 inline auto FTextView::getLine (FTextViewList::size_type line) -> FTextViewLine&
396 { return data.at(line); }
397 
398 //----------------------------------------------------------------------
399 inline auto FTextView::getLine (FTextViewList::size_type line) const -> const FTextViewLine&
400 { return data.at(line); }
401 
402 //----------------------------------------------------------------------
403 inline auto FTextView::getLines() const & -> const FTextViewList&
404 { return data; }
405 
406 //----------------------------------------------------------------------
407 inline void FTextView::setSelectionStart ( const FTextViewList::size_type row
408  , const FString::size_type col )
409 { selection_start = {row, col}; }
410 
411 //----------------------------------------------------------------------
412 inline void FTextView::setSelectionEnd ( const FTextViewList::size_type row
413  , const FString::size_type col )
414 { selection_end = {row, col}; }
415 
416 //----------------------------------------------------------------------
417 inline void FTextView::resetSelection()
418 {
419  selection_start = {UNINITIALIZED_ROW, UNINITIALIZED_COLUMN};
420  selection_end = {UNINITIALIZED_ROW, UNINITIALIZED_COLUMN};
421 }
422 
423 //----------------------------------------------------------------------
424 template <typename T>
425 inline void FTextView::setLines (T&& list)
426 {
427  clear();
428  data = std::forward<T>(list);
429  updateVerticalScrollBar();
430  processChanged();
431 }
432 
433 //----------------------------------------------------------------------
434 inline void FTextView::setSelectable (bool enable)
435 { selectable = enable; }
436 
437 //----------------------------------------------------------------------
438 inline void FTextView::unsetSelectable()
439 { selectable = false; }
440 
441 //----------------------------------------------------------------------
442 inline void FTextView::scrollTo (const FPoint& pos)
443 { scrollTo(pos.getX(), pos.getY()); }
444 
445 //---------------------------------------------------------------
446 inline auto FTextView::hasSelectedText() const -> bool
447 {
448  return selection_start.row != UNINITIALIZED_ROW
449  && selection_end.row != UNINITIALIZED_ROW
450  && selection_start.column != UNINITIALIZED_COLUMN
451  && selection_end.column != UNINITIALIZED_COLUMN;
452 }
453 
454 //----------------------------------------------------------------------
455 inline auto FTextView::isSelectable() const -> bool
456 { return selectable; }
457 
458 //----------------------------------------------------------------------
459 template <typename T>
460 void FTextView::append (const std::initializer_list<T>& list)
461 {
462  for (const auto& str : list)
463  insert(str, -1);
464 }
465 
466 //----------------------------------------------------------------------
467 template <typename T>
468 void FTextView::insert (const std::initializer_list<T>& list, int pos)
469 {
470  for (const auto& str : list)
471  {
472  insert(str, pos);
473  pos++;
474  }
475 }
476 
477 //----------------------------------------------------------------------
478 inline void FTextView::deleteLine (int pos)
479 { deleteRange (pos, pos); }
480 
481 //----------------------------------------------------------------------
482 inline auto FTextView::isHorizontallyScrollable() const -> bool
483 { return max_line_width > getTextWidth(); }
484 
485 //----------------------------------------------------------------------
486 inline auto FTextView::isVerticallyScrollable() const -> bool
487 { return getRows() > getTextHeight(); }
488 
489 //----------------------------------------------------------------------
490 template<typename T1, typename T2>
491 inline void FTextView::setSelectionStartInt (T1&& row, T2&& col)
492 {
493  selection_start = { static_cast<const FTextViewList::size_type>(std::forward<T1>(row))
494  , static_cast<const FString::size_type>(std::forward<T2>(col)) };
495 }
496 
497 //----------------------------------------------------------------------
498 template<typename T1, typename T2>
499 inline void FTextView::setSelectionEndInt (T1&& row, T2&& col)
500 {
501  selection_end = { static_cast<const FTextViewList::size_type>(std::forward<T1>(row))
502  , static_cast<const FString::size_type>(std::forward<T2>(col)) };
503 }
504 
505 } // namespace finalcut
506 
507 #endif // FTEXTVIEW_H
Definition: ftextview.h:80
Definition: fevent.h:144
Definition: fevent.h:300
Definition: fvtermbuffer.h:56
Definition: fevent.h:171
Definition: class_template.cpp:25
Definition: fpoint.h:50
Definition: fsize.h:55
Definition: ftypes.h:423
Definition: ftextview.h:159
Definition: fstring.h:79
Definition: fstyle.h:49
Definition: fwidget.h:129
Definition: ftextview.h:83
Definition: fstringstream.h:56
Definition: fcolorpair.h:49
Definition: fevent.h:124
Definition: ftypes.h:160
Definition: ftextview.h:144