Home | History | Annotate | Line # | Download | only in tui
      1 /* TUI layout window management.
      2 
      3    Copyright (C) 1998-2024 Free Software Foundation, Inc.
      4 
      5    Contributed by Hewlett-Packard Company.
      6 
      7    This file is part of GDB.
      8 
      9    This program is free software; you can redistribute it and/or modify
     10    it under the terms of the GNU General Public License as published by
     11    the Free Software Foundation; either version 3 of the License, or
     12    (at your option) any later version.
     13 
     14    This program is distributed in the hope that it will be useful,
     15    but WITHOUT ANY WARRANTY; without even the implied warranty of
     16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17    GNU General Public License for more details.
     18 
     19    You should have received a copy of the GNU General Public License
     20    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
     21 
     22 #ifndef GDB_TUI_TUI_LAYOUT_H
     23 #define GDB_TUI_TUI_LAYOUT_H
     24 
     25 #include "ui-file.h"
     26 
     27 #include "tui/tui.h"
     28 #include "tui/tui-data.h"
     29 #include "gdbsupport/iterator-range.h"
     30 
     31 #include <unordered_map>
     32 
     33 /* Values that can be returned when handling a request to adjust a
     34    window's size.  */
     35 enum tui_adjust_result
     36 {
     37   /* Requested window was not found here.  */
     38   NOT_FOUND,
     39   /* Window was found but not handled.  */
     40   FOUND,
     41   /* Window was found and handled.  */
     42   HANDLED
     43 };
     44 
     45 /* The basic object in a TUI layout.  This represents a single piece
     46    of screen real estate.  Subclasses determine the exact
     47    behavior.  */
     48 class tui_layout_base
     49 {
     50 public:
     51 
     52   DISABLE_COPY_AND_ASSIGN (tui_layout_base);
     53 
     54   virtual ~tui_layout_base () = default;
     55 
     56   /* Clone this object.  Ordinarily a layout is cloned before it is
     57      used, so that any necessary modifications do not affect the
     58      "skeleton" layout.  */
     59   virtual std::unique_ptr<tui_layout_base> clone () const = 0;
     60 
     61   /* Change the size and location of this layout.  When
     62      PRESERVE_CMD_WIN_SIZE_P is true the current size of the command window
     63      is preserved, otherwise, the command window will resize just like any
     64      other window.  */
     65   virtual void apply (int x, int y, int width, int height,
     66 		      bool preserve_cmd_win_size_p) = 0;
     67 
     68   /* Return the minimum and maximum height or width of this layout.
     69      HEIGHT is true to fetch height, false to fetch width.  */
     70   virtual void get_sizes (bool height, int *min_value, int *max_value) = 0;
     71 
     72   /* True if the topmost (for vertical layouts), or the leftmost (for
     73      horizontal layouts) item in this layout is boxed.  */
     74   virtual bool first_edge_has_border_p () const = 0;
     75 
     76   /* True if the bottommost (for vertical layouts), or the rightmost (for
     77      horizontal layouts) item in this layout is boxed.  */
     78   virtual bool last_edge_has_border_p () const = 0;
     79 
     80   /* Return the name of this layout's window, or nullptr if this
     81      layout does not represent a single window.  */
     82   virtual const char *get_name () const
     83   {
     84     return nullptr;
     85   }
     86 
     87   /* Set the height of the window named NAME to NEW_HEIGHT, updating
     88      the sizes of the other windows around it.  */
     89   virtual tui_adjust_result set_height (const char *name, int new_height) = 0;
     90 
     91   /* Set the width of the window named NAME to NEW_WIDTH, updating
     92      the sizes of the other windows around it.  */
     93   virtual tui_adjust_result set_width (const char *name, int new_width) = 0;
     94 
     95   /* Remove some windows from the layout, leaving the command window
     96      and the window being passed in here.  */
     97   virtual void remove_windows (const char *name) = 0;
     98 
     99   /* Replace the window named NAME in the layout with the window named
    100      NEW_WINDOW.  */
    101   virtual void replace_window (const char *name, const char *new_window) = 0;
    102 
    103   /* Append the specification to this window to OUTPUT.  DEPTH is the
    104      depth of this layout in the hierarchy (zero-based).  */
    105   virtual void specification (ui_file *output, int depth) = 0;
    106 
    107   /* Return a FINGERPRINT string containing an abstract representation of
    108      the location of the cmd window in this layout.
    109 
    110      When called on a complete, top-level layout, the fingerprint will be a
    111      non-empty string made of 'V' and 'H' characters, followed by a single
    112      'C' character.  Each 'V' and 'H' represents a vertical or horizontal
    113      layout that must be passed through in order to find the cmd
    114      window.  A vertical or horizontal layout of just one window does not add
    115      a 'V' or 'H' character.
    116 
    117      Of course, layouts are built recursively, so, when called on a partial
    118      layout, if this object represents a single window, then either the
    119      empty string is returned (for non-cmd windows), or a string
    120      containing a single 'C' is returned.
    121 
    122      For object representing layouts, if the layout contains the cmd
    123      window then we will get back a valid fingerprint string (may contain 'V'
    124      and 'H', ends with 'C'), or, if this layout doesn't contain the cmd
    125      window, an empty string is returned.  */
    126   virtual std::string layout_fingerprint () const = 0;
    127 
    128   /* Add all windows to the WINDOWS vector.  */
    129   virtual void get_windows (std::vector<tui_win_info *> *windows) = 0;
    130 
    131   /* The most recent space allocation.  */
    132   int x = 0;
    133   int y = 0;
    134   int width = 0;
    135   int height = 0;
    136 
    137 protected:
    138 
    139   tui_layout_base () = default;
    140 };
    141 
    142 /* A TUI layout object that displays a single window.  The window is
    143    given by name.  */
    144 class tui_layout_window : public tui_layout_base
    145 {
    146 public:
    147 
    148   explicit tui_layout_window (const char *name)
    149     : m_contents (name)
    150   {
    151   }
    152 
    153   DISABLE_COPY_AND_ASSIGN (tui_layout_window);
    154 
    155   std::unique_ptr<tui_layout_base> clone () const override;
    156 
    157   void apply (int x, int y, int width, int height,
    158 	      bool preserve_cmd_win_size_p) override;
    159 
    160   const char *get_name () const override
    161   {
    162     return m_contents.c_str ();
    163   }
    164 
    165   tui_adjust_result set_height (const char *name, int new_height) override
    166   {
    167     return m_contents == name ? FOUND : NOT_FOUND;
    168   }
    169 
    170   tui_adjust_result set_width (const char *name, int new_width) override
    171   {
    172     return m_contents == name ? FOUND : NOT_FOUND;
    173   }
    174 
    175   bool first_edge_has_border_p () const override;
    176 
    177   bool last_edge_has_border_p () const override;
    178 
    179   void remove_windows (const char *name) override
    180   {
    181   }
    182 
    183   void replace_window (const char *name, const char *new_window) override;
    184 
    185   void specification (ui_file *output, int depth) override;
    186 
    187   std::string layout_fingerprint () const override;
    188 
    189   /* See tui_layout_base::get_windows.  */
    190   void get_windows (std::vector<tui_win_info *> *windows) override
    191   {
    192     if (m_window != nullptr && m_window->is_visible ())
    193       {
    194 	/* Only get visible windows.  */
    195 	windows->push_back (m_window);
    196       }
    197   }
    198 
    199 protected:
    200 
    201   void get_sizes (bool height, int *min_value, int *max_value) override;
    202 
    203 private:
    204 
    205   /* Type of content to display.  */
    206   std::string m_contents;
    207 
    208   /* When a layout is applied, this is updated to point to the window
    209      object.  */
    210   tui_win_info *m_window = nullptr;
    211 };
    212 
    213 /* A TUI layout that holds other layouts.  */
    214 class tui_layout_split : public tui_layout_base
    215 {
    216 public:
    217 
    218   /* Create a new layout.  If VERTICAL is true, then windows in this
    219      layout will be arranged vertically.  */
    220   explicit tui_layout_split (bool vertical = true)
    221     : m_vertical (vertical)
    222   {
    223   }
    224 
    225   DISABLE_COPY_AND_ASSIGN (tui_layout_split);
    226 
    227   /* Add a new split layout to this layout.  WEIGHT is the desired
    228      size, which is relative to the other weights given in this
    229      layout.  */
    230   void add_split (std::unique_ptr<tui_layout_split> &&layout, int weight);
    231 
    232   /* Add a new window to this layout.  NAME is the name of the window
    233      to add.  WEIGHT is the desired size, which is relative to the
    234      other weights given in this layout.  */
    235   void add_window (const char *name, int weight);
    236 
    237   std::unique_ptr<tui_layout_base> clone () const override;
    238 
    239   void apply (int x, int y, int width, int height,
    240 	      bool preserve_cmd_win_size_p) override;
    241 
    242   tui_adjust_result set_height (const char *name, int new_height) override
    243   {
    244     /* Pass false as the final argument to indicate change of height.  */
    245     return set_size (name, new_height, false);
    246   }
    247 
    248   tui_adjust_result set_width (const char *name, int new_width) override
    249   {
    250     /* Pass true as the final argument to indicate change of width.  */
    251     return set_size (name, new_width, true);
    252   }
    253 
    254   bool first_edge_has_border_p () const override;
    255 
    256   bool last_edge_has_border_p () const override;
    257 
    258   void remove_windows (const char *name) override;
    259 
    260   void replace_window (const char *name, const char *new_window) override;
    261 
    262   void specification (ui_file *output, int depth) override;
    263 
    264   std::string layout_fingerprint () const override;
    265 
    266   /* See tui_layout_base::get_windows.  */
    267   void get_windows (std::vector<tui_win_info *> *windows) override
    268   {
    269     for (auto &item : m_splits)
    270       item.layout->get_windows (windows);
    271   }
    272 
    273 protected:
    274 
    275   void get_sizes (bool height, int *min_value, int *max_value) override;
    276 
    277 private:
    278 
    279   /* Used to implement set_height and set_width member functions.  When
    280      SET_WIDTH_P is true, set the width, otherwise, set the height of the
    281      window named NAME to NEW_SIZE, updating the sizes of the other windows
    282      around it as needed.  The result indicates if the window NAME was
    283      found and had its size adjusted, was found but was not adjusted, or
    284      was not found at all.  */
    285   tui_adjust_result set_size (const char *name, int new_size,
    286 			      bool set_width_p);
    287 
    288   /* Set the weights from the current heights (when m_vertical is true) or
    289      widths (when m_vertical is false).  */
    290   void set_weights_from_sizes ();
    291 
    292   /* Structure used when resizing, or applying a layout.  An instance of
    293      this structure is created for each sub-layout.  */
    294   struct size_info
    295   {
    296     /* The calculated size for this sub-layout.  */
    297     int size;
    298 
    299     /* The minimum and maximum sizes for this sub-layout, obtained by
    300        calling the get_sizes member function.  */
    301     int min_size;
    302     int max_size;
    303 
    304     /* True if this window will share a box border with the previous
    305        window in the list.  */
    306     bool share_box;
    307   };
    308 
    309   /* Used for debug, prints the contents of INFO using tui_debug_printf.
    310      Only call this when the global debug_tui is true.  */
    311   static void tui_debug_print_size_info (const std::vector<size_info> &info);
    312 
    313   /* Used for debug, returns a string describing the current weight of each
    314      sub-layout.  */
    315   std::string tui_debug_weights_to_string () const;
    316 
    317   struct split
    318   {
    319     /* The requested weight.  */
    320     int weight;
    321     /* The layout.  */
    322     std::unique_ptr<tui_layout_base> layout;
    323   };
    324 
    325   /* The splits.  */
    326   std::vector<split> m_splits;
    327 
    328   /* True if the windows in this split are arranged vertically.  */
    329   bool m_vertical;
    330 };
    331 
    332 /* Add the specified window to the layout in a logical way.  This
    333    means setting up the most logical layout given the window to be
    334    added.  Only the source or disassembly window can be added this
    335    way.  */
    336 extern void tui_add_win_to_layout (enum tui_win_type);
    337 
    338 /* Set the initial layout.  */
    339 extern void tui_set_initial_layout ();
    340 
    341 /* Switch to the next layout.  */
    342 extern void tui_next_layout ();
    343 
    344 /* Show the register window.  Like "layout regs".  */
    345 extern void tui_regs_layout ();
    346 
    347 /* Remove some windows from the layout, leaving only the focused
    348    window and the command window; if no window has the focus, then
    349    some other window is chosen to remain.  */
    350 extern void tui_remove_some_windows ();
    351 
    352 /* Apply the current layout.  When PRESERVE_CMD_WIN_SIZE_P is true the
    353    current size of the command window is preserved, otherwise, the command
    354    window will resize just like any other window.  */
    355 extern void tui_apply_current_layout (bool);
    356 
    357 /* Adjust the window height of WIN to NEW_HEIGHT.  */
    358 extern void tui_adjust_window_height (struct tui_win_info *win,
    359 				      int new_height);
    360 
    361 /* Adjust the window width of WIN to NEW_WIDTH.  */
    362 extern void tui_adjust_window_width (struct tui_win_info *win,
    363 				     int new_width);
    364 
    365 /* The type of a function that is used to create a TUI window.  */
    366 
    367 typedef std::function<tui_win_info * (const char *name)> window_factory;
    368 
    369 /* The type for a data structure that maps a window name to that window's
    370    factory function.  */
    371 typedef std::unordered_map<std::string, window_factory> window_types_map;
    372 
    373 /* Register a new TUI window type.  NAME is the name of the window
    374    type.  FACTORY is a function that can be called to instantiate the
    375    window.  */
    376 
    377 extern void tui_register_window (const char *name, window_factory &&factory);
    378 
    379 /* An iterator class that exposes just the window names from the
    380    known_window_types map in tui-layout.c.  This is just a wrapper around
    381    an iterator of the underlying known_window_types map, but this just
    382    exposes the window names.  */
    383 
    384 struct known_window_names_iterator
    385 {
    386   using known_window_types_iterator = window_types_map::iterator;
    387 
    388   known_window_names_iterator (known_window_types_iterator &&iter)
    389     : m_iter (std::move (iter))
    390   { /* Nothing.  */ }
    391 
    392   known_window_names_iterator &operator++ ()
    393   {
    394     ++m_iter;
    395     return *this;
    396   }
    397 
    398   const std::string &operator* () const
    399   { return (*m_iter).first; }
    400 
    401   bool operator!= (const known_window_names_iterator &other) const
    402   { return m_iter != other.m_iter; }
    403 
    404 private:
    405 
    406   /* The underlying iterator.  */
    407   known_window_types_iterator m_iter;
    408 };
    409 
    410 /* A range adapter that makes it possible to iterate over the names of all
    411    known tui windows.  */
    412 
    413 using known_window_names_range
    414   = iterator_range<known_window_names_iterator>;
    415 
    416 /* Return a range that can be used to walk over the name of all known tui
    417    windows in a range-for loop.  */
    418 
    419 extern known_window_names_range all_known_window_names ();
    420 
    421 #endif /* GDB_TUI_TUI_LAYOUT_H */
    422