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-2026 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 #include "final/widget/fscrollbar.h"
67 
68 namespace finalcut
69 {
70 
71 // class forward declaration
72 class FScrollBar;
73 
74 // Global using-declaration
75 using FScrollBarPtr = std::shared_ptr<FScrollBar>;
76 
77 //----------------------------------------------------------------------
78 // class FTextView
79 //----------------------------------------------------------------------
80 
81 class FTextView : public FWidget
82 {
83  public:
85  {
86  // Constants
87  static constexpr std::size_t EOL = std::numeric_limits<std::size_t>::max();
88 
89  FTextHighlight (std::size_t i, std::size_t l, const FChar& fchar) noexcept
90  : index{i}
91  , length{l}
92  , attributes{fchar}
93  { }
94 
95  FTextHighlight (std::size_t i, const FChar& fchar) noexcept
96  : FTextHighlight{i, EOL, fchar}
97  { }
98 
99  FTextHighlight (std::size_t i, std::size_t l, const FStyle& s) noexcept
100  : index{i}
101  , length{l}
102  {
103  auto wc_dialog = getColorTheme()->dialog;
104  attributes.color = {wc_dialog.fg, 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.color = {c, getColorTheme()->dialog.bg};
117  attributes.attr = s.toFAttribute();
118  }
119 
120  FTextHighlight (std::size_t i, FColor c, const FStyle& s = FStyle()) noexcept
121  : FTextHighlight{i, EOL, c, s}
122  { }
123 
124  FTextHighlight (std::size_t i, std::size_t l, const FColorPair& cpair, const FStyle& s = FStyle()) noexcept
125  : index{i}
126  , length{l}
127  {
128  attributes.color = \
129  {cpair.getForegroundColor(), cpair.getBackgroundColor()};
130  attributes.attr = s.toFAttribute();
131  }
132 
133  FTextHighlight (std::size_t i, const FColorPair& cpair, const FStyle& s = FStyle()) noexcept
134  : FTextHighlight{i, EOL, cpair, s}
135  { }
136 
137  // Data members
138  std::size_t index{};
139  std::size_t length{};
140  FChar attributes{};
141  };
142 
144  {
145  explicit FTextViewLine (FString&& s, std::vector<FTextHighlight>&& v = {}) noexcept
146  : text{std::move(s)}
147  , highlight{std::move(v)}
148  { }
149 
150  FString text{};
151  std::vector<FTextHighlight> highlight{};
152  };
153 
154  // Using-declarations
155  using FTextViewList = std::vector<FTextViewLine>;
156  using FWidget::setGeometry;
157 
159  {
160  FTextViewList::size_type row{UNINITIALIZED_ROW};
161  FString::size_type column{UNINITIALIZED_COLUMN};
162  };
163 
164  // Constructor
165  explicit FTextView (FWidget* = nullptr);
166 
167  // Destructor
168  ~FTextView() noexcept override;
169 
170  // Overloaded operators
171  auto operator = (const FString&) -> FTextView&;
172  template <typename typeT>
173  auto operator << (const typeT&) -> FTextView&;
174  auto operator << (const UniChar&) -> FTextView&;
175  auto operator << (const std::string&) -> FTextView&;
176 
177  // Accessors
178  auto getClassName() const -> FString override;
179  auto getColumns() const noexcept -> std::size_t;
180  auto getRows() const -> std::size_t;
181  auto getScrollPos() const -> FPoint;
182  auto getTextVisibleSize() const -> FSize;
183  auto getText() const -> FString;
184  auto getSelectedText() const -> FString;
185  auto getSelectionStart() const -> FTextPosition;
186  auto getSelectionEnd() const -> FTextPosition;
187  auto getLine (FTextViewList::size_type) -> FTextViewLine&;
188  auto getLine (FTextViewList::size_type) const -> const FTextViewLine&;
189  auto getLines() const & -> const FTextViewList&;
190 
191  // Mutators
192  void setSize (const FSize&, bool = true) override;
193  void setGeometry (const FPoint&, const FSize&, bool = true) override;
194  void resetColors() override;
195  void setText (const FString&);
196  void addHighlight (std::size_t, const FTextHighlight&);
197  void resetHighlight (std::size_t);
198  void setSelectionStart (const FTextViewList::size_type, const FString::size_type);
199  void setSelectionEnd (const FTextViewList::size_type, const FString::size_type);
200  void resetSelection();
201  template <typename T>
202  void setLines (T&&);
203  void setSelectable (bool = true);
204  void unsetSelectable();
205  void scrollToX (int);
206  void scrollToY (int);
207  void scrollTo (const FPoint&);
208  void scrollTo (int, int);
209  void scrollToBegin();
210  void scrollToEnd();
211  void scrollBy (int, int);
212 
213  // Predicate
214  auto hasSelectedText() const -> bool;
215  auto isSelectable() const -> bool;
216 
217  // Methods
218  void hide() override;
219  void clear();
220  template <typename T>
221  void append (const std::initializer_list<T>&);
222  void append (const FString&);
223  template <typename T>
224  void insert (const std::initializer_list<T>&, int);
225  void insert (const FString&, int);
226  void replaceRange (const FString&, int, int);
227  void deleteRange (int, int);
228  void deleteLine (int);
229 
230  // Event handlers
231  void onKeyPress (FKeyEvent*) override;
232  void onMouseDown (FMouseEvent*) override;
233  void onMouseUp (FMouseEvent*) override;
234  void onMouseMove (FMouseEvent*) override;
235  void onMouseDoubleClick (FMouseEvent*) override;
236  void onWheel (FWheelEvent*) override;
237  void onTimer (FTimerEvent*) override;
238 
239  protected:
240  // Method
241  void initLayout() override;
242  void adjustSize() override;
243 
244  // Predicate
245  auto isHorizontallyScrollable() const -> bool;
246  auto isVerticallyScrollable() const -> bool;
247 
248  // Accessors
249  auto getTextHeight() const -> std::size_t;
250  auto getTextWidth() const -> std::size_t;
251 
252  private:
253  // Constants
254  static constexpr auto UNINITIALIZED_ROW = static_cast<FTextViewList::size_type>(-1);
255  static constexpr auto UNINITIALIZED_COLUMN = static_cast<FString::size_type>(-1);
256 
257  // Using-declaration
258  using KeyMap = std::unordered_map<FKey, std::function<void()>, EnumHash<FKey>>;
259 
260  // Predicate
261  auto isWithinTextBounds (const FPoint&) const -> bool;
262  auto isLowerRightResizeCorner (const FPoint&) const -> bool;
263  auto hasWrongSelectionOrder() const -> bool;
264 
265  // Methods
266  void init();
267  void mapKeyFunctions();
268  void draw() override;
269  void drawBorder() override;
270  void drawScrollBars() const;
271  void drawText();
272  auto canSkipDrawing() const -> bool;
273  void printLine (std::size_t);
274  void addHighlighting ( FVTermBuffer&
275  , const std::vector<FTextHighlight>& ) const;
276  void addSelection (FVTermBuffer&, std::size_t) const;
277  auto useFDialogBorder() const -> bool;
278  auto isPrintable (wchar_t) const -> bool;
279  auto splitTextLines (const FString&) const -> FStringList;
280  void processLine (FString&&, int);
281  template<typename T1, typename T2>
282  void setSelectionStartInt (T1&&, T2&&);
283  template<typename T1, typename T2>
284  void setSelectionEndInt (T1&&, T2&&);
285  auto getScrollBarMaxHorizontal() const noexcept -> int;
286  auto getScrollBarMaxVertical() const noexcept -> int;
287  void updateVerticalScrollBar() const;
288  void updateHorizontalScrollBar (std::size_t);
289  auto convertMouse2TextPos (const FPoint&) const -> FPoint;
290  void handleMouseWithinListBounds (const FPoint&);
291  void handleMouseDragging (const FMouseEvent*);
292  void handleLeftDragScroll();
293  void handleRightDragScroll();
294  void handleUpDragScroll();
295  void handleDownDragScroll();
296  void dragLeft();
297  void dragRight();
298  void dragUp();
299  void dragDown();
300  void stopDragScroll();
301  void processChanged() const;
302  void changeOnResize() const;
303  auto shouldUpdateScrollBar (FScrollBar::ScrollType) const -> bool;
304  auto getVerticalScrollDistance (const FScrollBar::ScrollType) const -> int;
305  auto getHorizontalScrollDistance (const FScrollBar::ScrollType) const -> int;
306 
307  // Callback methods
308  void cb_vbarChange (const FWidget*);
309  void cb_hbarChange (const FWidget*);
310 
311  // Data members
312  FTextViewList data{};
313  FScrollBarPtr vbar{nullptr};
314  FScrollBarPtr hbar{nullptr};
315  FTextPosition selection_start{};
316  FTextPosition selection_end{};
317  FPoint select_click_pos{-1, -1};
318  KeyMap key_map{};
319  DragScrollMode drag_scroll{DragScrollMode::None};
320  bool update_scroll_bar{true};
321  bool pass_to_dialog{false};
322  bool selectable{false};
323  int scroll_repeat{100};
324  int xoffset{0};
325  int yoffset{0};
326  int nf_offset{0};
327  std::size_t max_line_width{0};
328 };
329 
330 // FListBox inline functions
331 //----------------------------------------------------------------------
332 inline auto FTextView::operator = (const FString& s) -> FTextView&
333 {
334  setText(s);
335  return *this;
336 }
337 
338 //----------------------------------------------------------------------
339 template <typename typeT>
340 inline auto FTextView::operator << (const typeT& s) -> FTextView&
341 {
342  FStringStream outstream{std::ios_base::out};
343  outstream << s;
344 
345  if ( ! outstream.str().isEmpty() )
346  append (outstream.str());
347 
348  return *this;
349 }
350 
351 //----------------------------------------------------------------------
352 inline auto FTextView::operator << (const UniChar& c) -> FTextView&
353 {
354  append (static_cast<wchar_t>(c)); // Required under Solaris
355  return *this;
356 }
357 
358 //----------------------------------------------------------------------
359 inline auto FTextView::operator << (const std::string& string) -> FTextView&
360 {
361  append (string);
362  return *this;
363 }
364 
365 //----------------------------------------------------------------------
366 inline auto FTextView::getClassName() const -> FString
367 { return "FTextView"; }
368 
369 //----------------------------------------------------------------------
370 inline auto FTextView::getColumns() const noexcept -> std::size_t
371 { return max_line_width; }
372 
373 //----------------------------------------------------------------------
374 inline auto FTextView::getRows() const -> std::size_t
375 { return data.size(); }
376 
377 //----------------------------------------------------------------------
378 inline auto FTextView::getScrollPos() const -> FPoint
379 { return {xoffset, yoffset}; }
380 
381 //----------------------------------------------------------------------
382 inline auto FTextView::getTextVisibleSize() const -> FSize
383 { return { FSize{getTextWidth(), getTextHeight()} }; }
384 
385 //----------------------------------------------------------------------
386 inline auto FTextView::getSelectionStart() const -> FTextPosition
387 { return selection_start; }
388 
389 //----------------------------------------------------------------------
390 inline auto FTextView::getSelectionEnd() const -> FTextPosition
391 { return selection_end; }
392 
393 //----------------------------------------------------------------------
394 inline auto FTextView::getLine (FTextViewList::size_type line) -> FTextViewLine&
395 { return data.at(line); }
396 
397 //----------------------------------------------------------------------
398 inline auto FTextView::getLine (FTextViewList::size_type line) const -> const FTextViewLine&
399 { return data.at(line); }
400 
401 //----------------------------------------------------------------------
402 inline auto FTextView::getLines() const & -> const FTextViewList&
403 { return data; }
404 
405 //----------------------------------------------------------------------
406 inline void FTextView::setSelectionStart ( const FTextViewList::size_type row
407  , const FString::size_type col )
408 { selection_start = {row, col}; }
409 
410 //----------------------------------------------------------------------
411 inline void FTextView::setSelectionEnd ( const FTextViewList::size_type row
412  , const FString::size_type col )
413 { selection_end = {row, col}; }
414 
415 //----------------------------------------------------------------------
416 inline void FTextView::resetSelection()
417 {
418  selection_start = {UNINITIALIZED_ROW, UNINITIALIZED_COLUMN};
419  selection_end = {UNINITIALIZED_ROW, UNINITIALIZED_COLUMN};
420 }
421 
422 //----------------------------------------------------------------------
423 template <typename T>
424 inline void FTextView::setLines (T&& list)
425 {
426  clear();
427  data = std::forward<T>(list);
428  updateVerticalScrollBar();
429  processChanged();
430 }
431 
432 //----------------------------------------------------------------------
433 inline void FTextView::setSelectable (bool enable)
434 { selectable = enable; }
435 
436 //----------------------------------------------------------------------
437 inline void FTextView::unsetSelectable()
438 { selectable = false; }
439 
440 //----------------------------------------------------------------------
441 inline void FTextView::scrollTo (const FPoint& pos)
442 { scrollTo(pos.getX(), pos.getY()); }
443 
444 //---------------------------------------------------------------
445 inline auto FTextView::hasSelectedText() const -> bool
446 {
447  return selection_start.row != UNINITIALIZED_ROW
448  && selection_end.row != UNINITIALIZED_ROW
449  && selection_start.column != UNINITIALIZED_COLUMN
450  && selection_end.column != UNINITIALIZED_COLUMN;
451 }
452 
453 //----------------------------------------------------------------------
454 inline auto FTextView::isSelectable() const -> bool
455 { return selectable; }
456 
457 //----------------------------------------------------------------------
458 template <typename T>
459 void FTextView::append (const std::initializer_list<T>& list)
460 {
461  for (const auto& str : list)
462  insert(str, -1);
463 }
464 
465 //----------------------------------------------------------------------
466 template <typename T>
467 void FTextView::insert (const std::initializer_list<T>& list, int pos)
468 {
469  for (const auto& str : list)
470  {
471  insert(str, pos);
472  pos++;
473  }
474 }
475 
476 //----------------------------------------------------------------------
477 inline void FTextView::deleteLine (int pos)
478 { deleteRange (pos, pos); }
479 
480 //----------------------------------------------------------------------
481 inline auto FTextView::isHorizontallyScrollable() const -> bool
482 { return max_line_width > getTextWidth(); }
483 
484 //----------------------------------------------------------------------
485 inline auto FTextView::isVerticallyScrollable() const -> bool
486 { return getRows() > getTextHeight(); }
487 
488 //----------------------------------------------------------------------
489 template<typename T1, typename T2>
490 inline void FTextView::setSelectionStartInt (T1&& row, T2&& col)
491 {
492  selection_start = { static_cast<const FTextViewList::size_type>(std::forward<T1>(row))
493  , static_cast<const FString::size_type>(std::forward<T2>(col)) };
494 }
495 
496 //----------------------------------------------------------------------
497 template<typename T1, typename T2>
498 inline void FTextView::setSelectionEndInt (T1&& row, T2&& col)
499 {
500  selection_end = { static_cast<const FTextViewList::size_type>(std::forward<T1>(row))
501  , static_cast<const FString::size_type>(std::forward<T2>(col)) };
502 }
503 
504 } // namespace finalcut
505 
506 #endif // FTEXTVIEW_H
Definition: ftextview.h:81
Definition: fevent.h:144
Definition: fevent.h:311
Definition: fvtermbuffer.h:56
Definition: fevent.h:182
Definition: class_template.cpp:25
Definition: fpoint.h:50
Definition: fsize.h:55
Definition: ftypes.h:1081
Definition: ftextview.h:158
Definition: fstring.h:82
Definition: fstyle.h:49
Definition: fwidget.h:129
Definition: ftextview.h:84
Definition: fstringstream.h:56
Definition: fcolorpair.h:49
Definition: fevent.h:124
Definition: ftypes.h:161
Definition: ftextview.h:143