Replace most calls to help_list and cmd_show_list
[deliverable/binutils-gdb.git] / gdb / tui / tui-layout.c
index 6077a9cc68c02e11349180e3ed2c55dae63d1de8..491ce275acbed2756e8fbeac341f607d42cef85e 100644 (file)
@@ -29,6 +29,7 @@
 #include "cli/cli-decode.h"
 #include "cli/cli-utils.h"
 #include <ctype.h>
+#include <unordered_map>
 #include <unordered_set>
 
 #include "tui/tui.h"
@@ -44,7 +45,6 @@
 #include "tui/tui-source.h"
 #include "gdb_curses.h"
 
-static void tui_layout_command (const char *, int);
 static void extract_display_start_addr (struct gdbarch **, CORE_ADDR *);
 
 /* The layouts.  */
@@ -61,12 +61,58 @@ static tui_layout_split *applied_skeleton;
 static tui_layout_split *src_regs_layout;
 static tui_layout_split *asm_regs_layout;
 
+/* See tui-data.h.  */
+std::vector<tui_win_info *> tui_windows;
+
+/* When applying a layout, this is the list of all windows that were
+   in the previous layout.  This is used to re-use windows when
+   changing a layout.  */
+static std::vector<tui_win_info *> saved_tui_windows;
+
 /* See tui-layout.h.  */
 
 void
 tui_apply_current_layout ()
 {
+  struct gdbarch *gdbarch;
+  CORE_ADDR addr;
+
+  extract_display_start_addr (&gdbarch, &addr);
+
+  saved_tui_windows = std::move (tui_windows);
+  tui_windows.clear ();
+
+  for (tui_win_info *win_info : saved_tui_windows)
+    win_info->make_visible (false);
+
   applied_layout->apply (0, 0, tui_term_width (), tui_term_height ());
+
+  /* Keep the list of internal windows up-to-date.  */
+  for (int win_type = SRC_WIN; (win_type < MAX_MAJOR_WINDOWS); win_type++)
+    if (tui_win_list[win_type] != nullptr
+       && !tui_win_list[win_type]->is_visible ())
+      tui_win_list[win_type] = nullptr;
+
+  /* This should always be made visible by a layout.  */
+  gdb_assert (TUI_CMD_WIN->is_visible ());
+
+  /* Now delete any window that was not re-applied.  */
+  tui_win_info *focus = tui_win_with_focus ();
+  for (tui_win_info *win_info : saved_tui_windows)
+    {
+      if (!win_info->is_visible ())
+       {
+         if (focus == win_info)
+           tui_set_win_focus_to (tui_windows[0]);
+         delete win_info;
+       }
+    }
+
+  if (gdbarch == nullptr && TUI_DISASM_WIN != nullptr)
+    tui_get_begin_asm_address (&gdbarch, &addr);
+  tui_update_source_windows_with_addr (gdbarch, addr);
+
+  saved_tui_windows.clear ();
 }
 
 /* See tui-layout.  */
@@ -82,19 +128,9 @@ tui_adjust_window_height (struct tui_win_info *win, int new_height)
 static void
 tui_set_layout (tui_layout_split *layout)
 {
-  struct gdbarch *gdbarch;
-  CORE_ADDR addr;
-
-  extract_display_start_addr (&gdbarch, &addr);
-  tui_make_all_invisible ();
   applied_skeleton = layout;
   applied_layout = layout->clone ();
   tui_apply_current_layout ();
-  tui_delete_invisible_windows ();
-
-  if (gdbarch == nullptr && TUI_DISASM_WIN != nullptr)
-    tui_get_begin_asm_address (&gdbarch, &addr);
-  tui_update_source_windows_with_addr (gdbarch, addr);
 }
 
 /* See tui-layout.h.  */
@@ -117,7 +153,6 @@ tui_add_win_to_layout (enum tui_win_type type)
   const char *name = type == SRC_WIN ? SRC_NAME : DISASSEM_NAME;
   applied_layout->replace_window (tui_win_list[other]->name (), name);
   tui_apply_current_layout ();
-  tui_delete_invisible_windows ();
 }
 
 /* Find LAYOUT in the "layouts" global and return its index.  */
@@ -238,27 +273,15 @@ tui_remove_some_windows ()
 static void
 extract_display_start_addr (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
 {
-  struct gdbarch *gdbarch = nullptr;
-  CORE_ADDR addr = 0;
-  CORE_ADDR pc;
-  struct symtab_and_line cursal = get_current_source_symtab_and_line ();
-
   if (TUI_SRC_WIN != nullptr)
-    {
-      gdbarch = TUI_SRC_WIN->gdbarch;
-      find_line_pc (cursal.symtab,
-                   TUI_SRC_WIN->start_line_or_addr.u.line_no,
-                   &pc);
-      addr = pc;
-    }
+    TUI_SRC_WIN->display_start_addr (gdbarch_p, addr_p);
   else if (TUI_DISASM_WIN != nullptr)
+    TUI_DISASM_WIN->display_start_addr (gdbarch_p, addr_p);
+  else
     {
-      gdbarch = TUI_DISASM_WIN->gdbarch;
-      addr = TUI_DISASM_WIN->start_line_or_addr.u.addr;
+      *gdbarch_p = nullptr;
+      *addr_p = 0;
     }
-
-  *gdbarch_p = gdbarch;
-  *addr_p = addr;
 }
 
 void
@@ -294,40 +317,89 @@ tui_gen_win_info::resize (int height_, int width_,
 
 \f
 
+/* Helper function to create one of the built-in (non-locator)
+   windows.  */
+
+template<enum tui_win_type V, class T>
+static tui_gen_win_info *
+make_standard_window (const char *)
+{
+  if (tui_win_list[V] == nullptr)
+    tui_win_list[V] = new T ();
+  return tui_win_list[V];
+}
+
+/* Helper function to wrap tui_locator_win_info_ptr for
+   tui_get_window_by_name.  */
+
+static tui_gen_win_info *
+get_locator_window (const char *)
+{
+  return tui_locator_win_info_ptr ();
+}
+
+/* A map holding all the known window types, keyed by name.  Note that
+   this is heap-allocated and "leaked" at gdb exit.  This avoids
+   ordering issues with destroying elements in the map at shutdown.
+   In particular, destroying this map can occur after Python has been
+   shut down, causing crashes if any window destruction requires
+   running Python code.  */
+
+static std::unordered_map<std::string, window_factory> *known_window_types;
+
 /* Helper function that returns a TUI window, given its name.  */
 
 static tui_gen_win_info *
 tui_get_window_by_name (const std::string &name)
 {
-  if (name == "src")
-    {
-      if (tui_win_list[SRC_WIN] == nullptr)
-       tui_win_list[SRC_WIN] = new tui_source_window ();
-      return tui_win_list[SRC_WIN];
-    }
-  else if (name == "cmd")
-    {
-      if (tui_win_list[CMD_WIN] == nullptr)
-       tui_win_list[CMD_WIN] = new tui_cmd_window ();
-      return tui_win_list[CMD_WIN];
-    }
-  else if (name == "regs")
-    {
-      if (tui_win_list[DATA_WIN] == nullptr)
-       tui_win_list[DATA_WIN] = new tui_data_window ();
-      return tui_win_list[DATA_WIN];
-    }
-  else if (name == "asm")
-    {
-      if (tui_win_list[DISASSEM_WIN] == nullptr)
-       tui_win_list[DISASSEM_WIN] = new tui_disasm_window ();
-      return tui_win_list[DISASSEM_WIN];
-    }
-  else
-    {
-      gdb_assert (name == "status");
-      return tui_locator_win_info_ptr ();
-    }
+  for (tui_win_info *window : saved_tui_windows)
+    if (name == window->name ())
+      return window;
+
+  auto iter = known_window_types->find (name);
+  if (iter == known_window_types->end ())
+    error (_("Unknown window type \"%s\""), name.c_str ());
+
+  tui_gen_win_info *result = iter->second (name.c_str ());
+  if (result == nullptr)
+    error (_("Could not create window \"%s\""), name.c_str ());
+  return result;
+}
+
+/* Initialize the known window types.  */
+
+static void
+initialize_known_windows ()
+{
+  known_window_types = new std::unordered_map<std::string, window_factory>;
+
+  known_window_types->emplace ("src",
+                              make_standard_window<SRC_WIN,
+                                                   tui_source_window>);
+  known_window_types->emplace ("cmd",
+                              make_standard_window<CMD_WIN, tui_cmd_window>);
+  known_window_types->emplace ("regs",
+                              make_standard_window<DATA_WIN,
+                                                   tui_data_window>);
+  known_window_types->emplace ("asm",
+                              make_standard_window<DISASSEM_WIN,
+                                                   tui_disasm_window>);
+  known_window_types->emplace ("status", get_locator_window);
+}
+
+/* See tui-layout.h.  */
+
+void
+tui_register_window (const char *name, window_factory &&factory)
+{
+  std::string name_copy = name;
+
+  if (name_copy == "src" || name_copy == "cmd" || name_copy == "regs"
+      || name_copy == "asm" || name_copy == "status")
+    error (_("Window type \"%s\" is built-in"), name);
+
+  known_window_types->emplace (std::move (name_copy),
+                              std::move (factory));
 }
 
 /* See tui-layout.h.  */
@@ -350,17 +422,27 @@ tui_layout_window::apply (int x_, int y_, int width_, int height_)
   height = height_;
   gdb_assert (m_window != nullptr);
   m_window->resize (height, width, x, y);
+  if (dynamic_cast<tui_win_info *> (m_window) != nullptr)
+    tui_windows.push_back ((tui_win_info *) m_window);
 }
 
 /* See tui-layout.h.  */
 
 void
-tui_layout_window::get_sizes (int *min_height, int *max_height)
+tui_layout_window::get_sizes (bool height, int *min_value, int *max_value)
 {
   if (m_window == nullptr)
     m_window = tui_get_window_by_name (m_contents);
-  *min_height = m_window->min_height ();
-  *max_height = m_window->max_height ();
+  if (height)
+    {
+      *min_value = m_window->min_height ();
+      *max_value = m_window->max_height ();
+    }
+  else
+    {
+      *min_value = m_window->min_width ();
+      *max_value = m_window->max_width ();
+    }
 }
 
 /* See tui-layout.h.  */
@@ -430,7 +512,7 @@ tui_layout_split::add_window (const char *name, int weight)
 std::unique_ptr<tui_layout_base>
 tui_layout_split::clone () const
 {
-  tui_layout_split *result = new tui_layout_split ();
+  tui_layout_split *result = new tui_layout_split (m_vertical);
   for (const split &item : m_splits)
     {
       std::unique_ptr<tui_layout_base> next = item.layout->clone ();
@@ -443,16 +525,29 @@ tui_layout_split::clone () const
 /* See tui-layout.h.  */
 
 void
-tui_layout_split::get_sizes (int *min_height, int *max_height)
+tui_layout_split::get_sizes (bool height, int *min_value, int *max_value)
 {
-  *min_height = 0;
-  *max_height = 0;
+  *min_value = 0;
+  *max_value = 0;
+  bool first_time = true;
   for (const split &item : m_splits)
     {
       int new_min, new_max;
-      item.layout->get_sizes (&new_min, &new_max);
-      *min_height += new_min;
-      *max_height += new_max;
+      item.layout->get_sizes (height, &new_min, &new_max);
+      /* For the mismatch case, the first time through we want to set
+        the min and max to the computed values -- the "first_time"
+        check here is just a funny way of doing that.  */
+      if (height == m_vertical || first_time)
+       {
+         *min_value += new_min;
+         *max_value += new_max;
+       }
+      else
+       {
+         *min_value = std::max (*min_value, new_min);
+         *max_value = std::min (*max_value, new_max);
+       }
+      first_time = false;
     }
 }
 
@@ -487,7 +582,7 @@ tui_layout_split::set_weights_from_heights ()
 
 /* See tui-layout.h.  */
 
-bool
+tui_adjust_result
 tui_layout_split::adjust_size (const char *name, int new_height)
 {
   /* Look through the children.  If one is a layout holding the named
@@ -496,20 +591,23 @@ tui_layout_split::adjust_size (const char *name, int new_height)
   int found_index = -1;
   for (int i = 0; i < m_splits.size (); ++i)
     {
-      if (m_splits[i].layout->adjust_size (name, new_height))
-       return true;
-      const char *win_name = m_splits[i].layout->get_name ();
-      if (win_name != nullptr && strcmp (name, win_name) == 0)
+      tui_adjust_result adjusted
+       = m_splits[i].layout->adjust_size (name, new_height);
+      if (adjusted == HANDLED)
+       return HANDLED;
+      if (adjusted == FOUND)
        {
+         if (!m_vertical)
+           return FOUND;
          found_index = i;
          break;
        }
     }
 
   if (found_index == -1)
-    return false;
+    return NOT_FOUND;
   if (m_splits[found_index].layout->height == new_height)
-    return true;
+    return HANDLED;
 
   set_weights_from_heights ();
   int delta = m_splits[found_index].weight - new_height;
@@ -523,7 +621,7 @@ tui_layout_split::adjust_size (const char *name, int new_height)
       int index = (found_index + 1 + i) % m_splits.size ();
 
       int new_min, new_max;
-      m_splits[index].layout->get_sizes (&new_min, &new_max);
+      m_splits[index].layout->get_sizes (m_vertical, &new_min, &new_max);
 
       if (delta < 0)
        {
@@ -557,7 +655,7 @@ tui_layout_split::adjust_size (const char *name, int new_height)
       apply (x, y, width, height);
     }
 
-  return true;
+  return HANDLED;
 }
 
 /* See tui-layout.h.  */
@@ -570,23 +668,23 @@ tui_layout_split::apply (int x_, int y_, int width_, int height_)
   width = width_;
   height = height_;
 
-  struct height_info
+  struct size_info
   {
-    int height;
-    int min_height;
-    int max_height;
+    int size;
+    int min_size;
+    int max_size;
     /* True if this window will share a box border with the previous
        window in the list.  */
     bool share_box;
   };
 
-  std::vector<height_info> info (m_splits.size ());
+  std::vector<size_info> info (m_splits.size ());
 
-  /* Step 1: Find the min and max height of each sub-layout.
-     Fixed-sized layouts are given their desired height, and then the
+  /* Step 1: Find the min and max size of each sub-layout.
+     Fixed-sized layouts are given their desired size, and then the
      remaining space is distributed among the remaining windows
      according to the weights given.  */
-  int available_height = height;
+  int available_size = m_vertical ? height : width;
   int last_index = -1;
   int total_weight = 0;
   for (int i = 0; i < m_splits.size (); ++i)
@@ -596,7 +694,8 @@ tui_layout_split::apply (int x_, int y_, int width_, int height_)
       /* Always call get_sizes, to ensure that the window is
         instantiated.  This is a bit gross but less gross than adding
         special cases for this in other places.  */
-      m_splits[i].layout->get_sizes (&info[i].min_height, &info[i].max_height);
+      m_splits[i].layout->get_sizes (m_vertical, &info[i].min_size,
+                                    &info[i].max_size);
 
       if (!m_applied
          && cmd_win_already_exists
@@ -606,15 +705,17 @@ tui_layout_split::apply (int x_, int y_, int width_, int height_)
          /* If this layout has never been applied, then it means the
             user just changed the layout.  In this situation, it's
             desirable to keep the size of the command window the
-            same.  Setting the min and max heights this way ensures
+            same.  Setting the min and max sizes this way ensures
             that the resizing step, below, does the right thing with
             this window.  */
-         info[i].min_height = TUI_CMD_WIN->height;
-         info[i].max_height = TUI_CMD_WIN->height;
+         info[i].min_size = (m_vertical
+                             ? TUI_CMD_WIN->height
+                             : TUI_CMD_WIN->width);
+         info[i].max_size = info[i].min_size;
        }
 
-      if (info[i].min_height == info[i].max_height)
-       available_height -= info[i].min_height;
+      if (info[i].min_size == info[i].max_size)
+       available_size -= info[i].min_size;
       else
        {
          last_index = i;
@@ -622,54 +723,58 @@ tui_layout_split::apply (int x_, int y_, int width_, int height_)
        }
 
       /* Two adjacent boxed windows will share a border, making a bit
-        more height available.  */
+        more size available.  */
       if (i > 0
          && m_splits[i - 1].layout->bottom_boxed_p ()
          && m_splits[i].layout->top_boxed_p ())
        info[i].share_box = true;
     }
 
-  /* Step 2: Compute the height of each sub-layout.  Fixed-sized items
+  /* Step 2: Compute the size of each sub-layout.  Fixed-sized items
      are given their fixed size, while others are resized according to
      their weight.  */
-  int used_height = 0;
+  int used_size = 0;
   for (int i = 0; i < m_splits.size (); ++i)
     {
       /* Compute the height and clamp to the allowable range.  */
-      info[i].height = available_height * m_splits[i].weight / total_weight;
-      if (info[i].height > info[i].max_height)
-       info[i].height = info[i].max_height;
-      if (info[i].height < info[i].min_height)
-       info[i].height = info[i].min_height;
-      /* If there is any leftover height, just redistribute it to the
+      info[i].size = available_size * m_splits[i].weight / total_weight;
+      if (info[i].size > info[i].max_size)
+       info[i].size = info[i].max_size;
+      if (info[i].size < info[i].min_size)
+       info[i].size = info[i].min_size;
+      /* If there is any leftover size, just redistribute it to the
         last resizeable window, by dropping it from the allocated
-        height.  We could try to be fancier here perhaps, by
-        redistributing this height among all windows, not just the
+        size.  We could try to be fancier here perhaps, by
+        redistributing this size among all windows, not just the
         last window.  */
-      if (info[i].min_height != info[i].max_height)
+      if (info[i].min_size != info[i].max_size)
        {
-         used_height += info[i].height;
+         used_size += info[i].size;
          if (info[i].share_box)
-           --used_height;
+           --used_size;
        }
     }
 
-  /* Allocate any leftover height.  */
-  if (available_height >= used_height && last_index != -1)
-    info[last_index].height += available_height - used_height;
+  /* Allocate any leftover size.  */
+  if (available_size >= used_size && last_index != -1)
+    info[last_index].size += available_size - used_size;
 
   /* Step 3: Resize.  */
-  int height_accum = 0;
+  int size_accum = 0;
+  const int maximum = m_vertical ? height : width;
   for (int i = 0; i < m_splits.size (); ++i)
     {
       /* If we fall off the bottom, just make allocations overlap.
         GIGO.  */
-      if (height_accum + info[i].height > height)
-       height_accum = height - info[i].height;
+      if (size_accum + info[i].size > maximum)
+       size_accum = maximum - info[i].size;
       else if (info[i].share_box)
-       --height_accum;
-      m_splits[i].layout->apply (x, y + height_accum, width, info[i].height);
-      height_accum += info[i].height;
+       --size_accum;
+      if (m_vertical)
+       m_splits[i].layout->apply (x, y + size_accum, width, info[i].size);
+      else
+       m_splits[i].layout->apply (x + size_accum, y, info[i].size, height);
+      size_accum += info[i].size;
     }
 
   m_applied = true;
@@ -715,6 +820,9 @@ tui_layout_split::specification (ui_file *output, int depth)
   if (depth > 0)
     fputs_unfiltered ("{", output);
 
+  if (!m_vertical)
+    fputs_unfiltered ("-horizontal ", output);
+
   bool first = true;
   for (auto &item : m_splits)
     {
@@ -822,9 +930,8 @@ initialize_layouts ()
 static bool
 validate_window_name (const std::string &name)
 {
-  return (name == "src" || name == "cmd"
-         || name == "regs" || name == "asm"
-         || name == "status");
+  auto iter = known_window_types->find (name);
+  return iter != known_window_types->end ();
 }
 
 /* Implementation of the "tui new-layout" command.  */
@@ -838,8 +945,13 @@ tui_new_layout_command (const char *spec, int from_tty)
   if (new_name[0] == '-')
     error (_("Layout name cannot start with '-'"));
 
+  bool is_vertical = true;
+  spec = skip_spaces (spec);
+  if (check_for_argument (&spec, "-horizontal"))
+    is_vertical = false;
+
   std::vector<std::unique_ptr<tui_layout_split>> splits;
-  splits.emplace_back (new tui_layout_split);
+  splits.emplace_back (new tui_layout_split (is_vertical));
   std::unordered_set<std::string> seen_windows;
   while (true)
     {
@@ -849,8 +961,11 @@ tui_new_layout_command (const char *spec, int from_tty)
 
       if (spec[0] == '{')
        {
-         splits.emplace_back (new tui_layout_split);
-         ++spec;
+         is_vertical = true;
+         spec = skip_spaces (spec + 1);
+         if (check_for_argument (&spec, "-horizontal"))
+           is_vertical = false;
+         splits.emplace_back (new tui_layout_split (is_vertical));
          continue;
        }
 
@@ -907,14 +1022,6 @@ tui_new_layout_command (const char *spec, int from_tty)
   new_layout.release ();
 }
 
-/* Base command for "layout".  */
-
-static void
-tui_layout_command (const char *layout_name, int from_tty)
-{
-  help_list (layout_list, "layout ", all_commands, gdb_stdout);
-}
-
 /* Function to initialize gdb commands, for tui window layout
    manipulation.  */
 
@@ -922,33 +1029,34 @@ void _initialize_tui_layout ();
 void
 _initialize_tui_layout ()
 {
-  add_prefix_cmd ("layout", class_tui, tui_layout_command, _("\
+  add_basic_prefix_cmd ("layout", class_tui, _("\
 Change the layout of windows.\n\
 Usage: layout prev | next | LAYOUT-NAME"),
-                 &layout_list, "layout ", 0, &cmdlist);
+                       &layout_list, "layout ", 0, &cmdlist);
 
   add_cmd ("next", class_tui, tui_next_layout_command,
-          _("Apply the next TUI layout"),
+          _("Apply the next TUI layout."),
           &layout_list);
   add_cmd ("prev", class_tui, tui_prev_layout_command,
-          _("Apply the previous TUI layout"),
+          _("Apply the previous TUI layout."),
           &layout_list);
   add_cmd ("regs", class_tui, tui_regs_layout_command,
-          _("Apply the TUI register layout"),
+          _("Apply the TUI register layout."),
           &layout_list);
 
   add_cmd ("new-layout", class_tui, tui_new_layout_command,
           _("Create a new TUI layout.\n\
-Usage: tui new-layout NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
+Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
 Create a new TUI layout.  The new layout will be named NAME,\n\
 and can be accessed using \"layout NAME\".\n\
 The windows will be displayed in the specified order.\n\
 A WINDOW can also be of the form:\n\
-  { NAME WEIGHT [NAME WEIGHT]... }\n\
+  { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
 This form indicates a sub-frame.\n\
 Each WEIGHT is an integer, which holds the relative size\n\
 to be allocated to the window."),
           tui_get_cmd_list ());
 
   initialize_layouts ();
+  initialize_known_windows ();
 }
This page took 0.033285 seconds and 4 git commands to generate.