+2020-02-22 Tom Tromey <tom@tromey.com>
+
+ * NEWS: Add "tui new-layout" item.
+ * tui/tui-layout.c (add_layout_command): Return cmd_list_element.
+ Add new-layout command to help text.
+ (validate_window_name): New function.
+ (tui_new_layout_command): New function.
+ (_initialize_tui_layout): Register "new-layout".
+ (tui_layout_window::specification): New method.
+ (tui_layout_window::specification): New method.
+ * tui/tui-layout.h (class tui_layout_base) <specification>: New
+ method.
+ (class tui_layout_window) <specification>: New method.
+ (class tui_layout_split) <specification>: New method.
+
2020-02-22 Tom Tromey <tom@tromey.com>
* tui/tui.c (tui_enable): Call tui_set_initial_layout.
whether to load the process executable file; if 'warn', just display
a warning; if 'off', don't attempt to detect a mismatch.
+tui new-layout NAME WINDOW WEIGHT [WINDOW WEIGHT]...
+ Define a new TUI layout, specifying its name and the windows that
+ will be displayed.
+
* New targets
GNU/Linux/RISC-V (gdbserver) riscv*-*-linux*
+2020-02-22 Tom Tromey <tom@tromey.com>
+
+ * gdb.texinfo (TUI Overview): Mention user layouts.
+ (TUI Commands): Document "tui new-layout".
+
2020-01-26 Tom Tromey <tromey@adacore.com>
* gdb.texinfo (M68K Features): Document floating-point feature
assembly and registers.
@end itemize
+These are the standard layouts, but other layouts can be defined.
+
A status line above the command window shows the following information:
@table @emph
@kindex info win
List and give the size of all displayed windows.
+@item tui new-layout @var{name} @var{window} @var{weight} @r{[}@var{window} @var{weight}@dots{}@r{]}
+@kindex tui new-layout
+Create a new TUI layout. The new layout will be named @var{name}, and
+can be accessed using the @code{layout} command (see below).
+
+Each @var{window} parameter is the name of a window to display. The
+windows will be displayed from top to bottom in the order listed. The
+names of the windows are the same as the ones given to the
+@code{focus} command (see below); additional, the @code{status}
+window can be specified.
+
+Each @var{weight} is an integer. It is the weight of this window
+relative to all the other windows in the layout. These numbers are
+used to calculate how much of the screen is given to each window.
+
+For example:
+
+@example
+(gdb) tui new-layout example src 1 regs 1 status 0 cmd 1
+@end example
+
+Here, the new layout is called @samp{example}. It shows the source
+and register windows, followed by the status window, and then finally
+the command window. The non-status windows all have the same weight,
+so the terminal will be split into three roughly equal sections.
+
@item layout @var{name}
@kindex layout
-Changes which TUI windows are displayed. In each layout the command
-window is always displayed, the @var{name} parameter controls which
-additional windows are displayed, and can be any of the following:
+Changes which TUI windows are displayed. The @var{name} parameter
+controls which layout is shown. It can be either one of the built-in
+layout names, or the name of a layout defined by the user using
+@code{tui new-layout}.
+
+The built-in layouts are as follows:
@table @code
@item next
+2020-02-22 Tom Tromey <tom@tromey.com>
+
+ * gdb.tui/new-layout.exp: New file.
+
2020-02-22 Tom Tromey <tom@tromey.com>
* gdb.rust/rust-style.rs: New file.
--- /dev/null
+# Copyright 2020 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Test "tui new-layout".
+
+load_lib "tuiterm.exp"
+
+standard_testfile tui-layout.c
+
+if {[build_executable "failed to prepare" ${testfile} ${srcfile}] == -1} {
+ return -1
+}
+
+Term::clean_restart 24 80 $testfile
+
+gdb_test "tui new-layout" \
+ "No layout name specified"
+gdb_test "tui new-layout example" \
+ "New layout does not contain any windows"
+gdb_test "tui new-layout example zzq" \
+ "Unknown window \"zzq\""
+gdb_test "tui new-layout example src 1 src 1" \
+ "Window \"src\" seen twice in layout"
+gdb_test "tui new-layout example src 1" \
+ "New layout does not contain the \"cmd\" window"
+
+gdb_test_no_output "tui new-layout example asm 1 status 0 cmd 1"
+
+gdb_test "help layout example" \
+ "Apply the \"example\" layout.*tui new-layout example asm 1 status 0 cmd 1"
+
+if {![Term::enter_tui]} {
+ unsupported "TUI not supported"
+}
+
+set text [Term::get_all_lines]
+gdb_assert {![string match "No Source Available" $text]} \
+ "initial source listing"
+
+Term::command "layout example"
+Term::check_contents "example layout shows assembly" \
+ "No Assembly Available"
#include "source.h"
#include "cli/cli-cmds.h"
#include "cli/cli-decode.h"
+#include "cli/cli-utils.h"
#include <ctype.h>
+#include <unordered_set>
#include "tui/tui.h"
#include "tui/tui-command.h"
/* See tui-layout.h. */
+void
+tui_layout_window::specification (ui_file *output)
+{
+ fputs_unfiltered (get_name (), output);
+}
+
+/* See tui-layout.h. */
+
tui_layout_split *
tui_layout_split::add_split (int weight)
{
item.layout->replace_window (name, new_window);
}
+/* See tui-layout.h. */
+
+void
+tui_layout_split::specification (ui_file *output)
+{
+ bool first = true;
+ for (auto &item : m_splits)
+ {
+ if (!first)
+ fputs_unfiltered (" ", output);
+ first = false;
+ item.layout->specification (output);
+ fprintf_unfiltered (output, " %d", item.weight);
+ }
+}
+
/* Destroy the layout associated with SELF. */
static void
/* Add a "layout" command with name NAME that switches to LAYOUT. */
-static void
+static struct cmd_list_element *
add_layout_command (const char *name, tui_layout_split *layout)
{
struct cmd_list_element *cmd;
- gdb::unique_xmalloc_ptr<char> doc (xstrprintf (_("Apply the \"%s\" layout"),
- name));
+ string_file spec;
+ layout->specification (&spec);
+
+ gdb::unique_xmalloc_ptr<char> doc
+ (xstrprintf (_("Apply the \"%s\" layout.\n\
+This layout was created using:\n\
+ tui new-layout %s %s"),
+ name, name, spec.c_str ()));
cmd = add_cmd (name, class_tui, nullptr, doc.get (), &layout_list);
set_cmd_context (cmd, layout);
cmd->doc_allocated = 1;
doc.release ();
layouts.emplace_back (layout);
+
+ return cmd;
}
/* Initialize the standard layouts. */
\f
+/* A helper function that returns true if NAME is the name of an
+ available window. */
+
+static bool
+validate_window_name (const std::string &name)
+{
+ return (name == "src" || name == "cmd"
+ || name == "regs" || name == "asm"
+ || name == "status");
+}
+
+/* Implementation of the "tui new-layout" command. */
+
+static void
+tui_new_layout_command (const char *spec, int from_tty)
+{
+ std::string new_name = extract_arg (&spec);
+ if (new_name.empty ())
+ error (_("No layout name specified"));
+ if (new_name[0] == '-')
+ error (_("Layout name cannot start with '-'"));
+
+ std::unique_ptr<tui_layout_split> new_layout (new tui_layout_split);
+ std::unordered_set<std::string> seen_windows;
+ while (true)
+ {
+ std::string name = extract_arg (&spec);
+ if (name.empty ())
+ break;
+ if (!validate_window_name (name))
+ error (_("Unknown window \"%s\""), name.c_str ());
+ if (seen_windows.find (name) != seen_windows.end ())
+ error (_("Window \"%s\" seen twice in layout"), name.c_str ());
+ ULONGEST weight = get_ulongest (&spec);
+ if ((int) weight != weight)
+ error (_("Weight out of range: %s"), pulongest (weight));
+ new_layout->add_window (name.c_str (), weight);
+ seen_windows.insert (name);
+ }
+ if (seen_windows.empty ())
+ error (_("New layout does not contain any windows"));
+ if (seen_windows.find ("cmd") == seen_windows.end ())
+ error (_("New layout does not contain the \"cmd\" window"));
+
+ gdb::unique_xmalloc_ptr<char> cmd_name
+ = make_unique_xstrdup (new_name.c_str ());
+ struct cmd_list_element *cmd
+ = add_layout_command (cmd_name.get (), new_layout.get ());
+ cmd->name_allocated = 1;
+ cmd_name.release ();
+ new_layout.release ();
+}
+
/* Base command for "layout". */
static void
_("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\
+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\
+Each WEIGHT is an integer, which holds the relative size\n\
+to be allocated to the window."),
+ tui_get_cmd_list ());
+
initialize_layouts ();
}
#ifndef TUI_TUI_LAYOUT_H
#define TUI_TUI_LAYOUT_H
+#include "ui-file.h"
+
#include "tui/tui.h"
#include "tui/tui-data.h"
NEW_WINDOW. */
virtual void replace_window (const char *name, const char *new_window) = 0;
+ /* Append the specification to this window to OUTPUT. */
+ virtual void specification (ui_file *output) = 0;
+
/* The most recent space allocation. */
int x = 0;
int y = 0;
void replace_window (const char *name, const char *new_window) override;
+ void specification (ui_file *output) override;
+
protected:
void get_sizes (int *min_height, int *max_height) override;
void replace_window (const char *name, const char *new_window) override;
+ void specification (ui_file *output) override;
+
protected:
void get_sizes (int *min_height, int *max_height) override;