Add horizontal splitting to TUI layout
[deliverable/binutils-gdb.git] / gdb / tui / tui-layout.c
CommitLineData
f377b406 1/* TUI layout window management.
f33c6cbf 2
b811d2c2 3 Copyright (C) 1998-2020 Free Software Foundation, Inc.
f33c6cbf 4
f377b406 5 Contributed by Hewlett-Packard Company.
c906108c 6
f377b406
SC
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
a9762ec7 11 the Free Software Foundation; either version 3 of the License, or
f377b406
SC
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
a9762ec7 20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
c906108c
SS
21
22#include "defs.h"
957b8b5a 23#include "arch-utils.h"
c906108c
SS
24#include "command.h"
25#include "symtab.h"
26#include "frame.h"
52575520 27#include "source.h"
416eb92d
TT
28#include "cli/cli-cmds.h"
29#include "cli/cli-decode.h"
ee325b61 30#include "cli/cli-utils.h"
84b1e7c7 31#include <ctype.h>
ee325b61 32#include <unordered_set>
c906108c 33
d7b2e967 34#include "tui/tui.h"
ce38393b 35#include "tui/tui-command.h"
d7b2e967 36#include "tui/tui-data.h"
d7b2e967
AC
37#include "tui/tui-wingeneral.h"
38#include "tui/tui-stack.h"
39#include "tui/tui-regs.h"
40#include "tui/tui-win.h"
41#include "tui/tui-winsource.h"
42#include "tui/tui-disasm.h"
2c0b251b 43#include "tui/tui-layout.h"
bfad4537 44#include "tui/tui-source.h"
6a83354a 45#include "gdb_curses.h"
96ec9981 46
0b39b52e 47static void tui_layout_command (const char *, int);
13274fc3 48static void extract_display_start_addr (struct gdbarch **, CORE_ADDR *);
c906108c 49
416eb92d
TT
50/* The layouts. */
51static std::vector<std::unique_ptr<tui_layout_split>> layouts;
2192a9d3
TT
52
53/* The layout that is currently applied. */
54static std::unique_ptr<tui_layout_base> applied_layout;
55
416eb92d
TT
56/* The "skeleton" version of the layout that is currently applied. */
57static tui_layout_split *applied_skeleton;
62cf57fe 58
416eb92d
TT
59/* The two special "regs" layouts. Note that these aren't registered
60 as commands and so can never be deleted. */
61static tui_layout_split *src_regs_layout;
62static tui_layout_split *asm_regs_layout;
62cf57fe 63
2192a9d3
TT
64/* See tui-layout.h. */
65
66void
67tui_apply_current_layout ()
68{
69 applied_layout->apply (0, 0, tui_term_width (), tui_term_height ());
70}
c906108c 71
d4eeccfe
TT
72/* See tui-layout. */
73
74void
75tui_adjust_window_height (struct tui_win_info *win, int new_height)
76{
77 applied_layout->adjust_size (win->name (), new_height);
78}
79
416eb92d 80/* Set the current layout to LAYOUT. */
c906108c 81
416eb92d
TT
82static void
83tui_set_layout (tui_layout_split *layout)
c906108c 84{
b7fbad91
TT
85 struct gdbarch *gdbarch;
86 CORE_ADDR addr;
c906108c 87
b7fbad91 88 extract_display_start_addr (&gdbarch, &addr);
416eb92d
TT
89 tui_make_all_invisible ();
90 applied_skeleton = layout;
91 applied_layout = layout->clone ();
92 tui_apply_current_layout ();
93 tui_delete_invisible_windows ();
c906108c 94
416eb92d
TT
95 if (gdbarch == nullptr && TUI_DISASM_WIN != nullptr)
96 tui_get_begin_asm_address (&gdbarch, &addr);
97 tui_update_source_windows_with_addr (gdbarch, addr);
bc712bbf 98}
c906108c 99
59b8b5d2
TT
100/* See tui-layout.h. */
101
c906108c 102void
080ce8c0 103tui_add_win_to_layout (enum tui_win_type type)
c906108c 104{
59b8b5d2
TT
105 gdb_assert (type == SRC_WIN || type == DISASSEM_WIN);
106
416eb92d
TT
107 /* If the window already exists, no need to add it. */
108 if (tui_win_list[type] != nullptr)
109 return;
110
111 /* If the window we are trying to replace doesn't exist, we're
112 done. */
113 enum tui_win_type other = type == SRC_WIN ? DISASSEM_WIN : SRC_WIN;
114 if (tui_win_list[other] == nullptr)
115 return;
116
117 const char *name = type == SRC_WIN ? SRC_NAME : DISASSEM_NAME;
118 applied_layout->replace_window (tui_win_list[other]->name (), name);
119 tui_apply_current_layout ();
120 tui_delete_invisible_windows ();
121}
122
123/* Find LAYOUT in the "layouts" global and return its index. */
c906108c 124
416eb92d
TT
125static size_t
126find_layout (tui_layout_split *layout)
127{
128 for (size_t i = 0; i < layouts.size (); ++i)
c906108c 129 {
416eb92d
TT
130 if (layout == layouts[i].get ())
131 return i;
c906108c 132 }
416eb92d 133 gdb_assert_not_reached (_("layout not found!?"));
6ba8e26f 134}
c906108c 135
416eb92d 136/* Function to set the layout. */
a0145030 137
eb3ff9a5 138static void
416eb92d
TT
139tui_apply_layout (struct cmd_list_element *command,
140 const char *args, int from_tty)
a0145030 141{
416eb92d
TT
142 tui_layout_split *layout
143 = (tui_layout_split *) get_cmd_context (command);
a0145030 144
416eb92d
TT
145 /* Make sure the curses mode is enabled. */
146 tui_enable ();
147 tui_set_layout (layout);
a0145030
AB
148}
149
416eb92d 150/* See tui-layout.h. */
c906108c 151
416eb92d
TT
152void
153tui_next_layout ()
154{
155 size_t index = find_layout (applied_skeleton);
156 ++index;
157 if (index == layouts.size ())
158 index = 0;
159 tui_set_layout (layouts[index].get ());
160}
c906108c 161
416eb92d 162/* Implement the "layout next" command. */
c906108c 163
416eb92d
TT
164static void
165tui_next_layout_command (const char *arg, int from_tty)
166{
0379b883 167 tui_enable ();
416eb92d 168 tui_next_layout ();
e8b915dc 169}
c906108c 170
427326a8
TT
171/* See tui-layout.h. */
172
173void
416eb92d
TT
174tui_set_initial_layout ()
175{
176 tui_set_layout (layouts[0].get ());
177}
178
179/* Implement the "layout prev" command. */
180
181static void
182tui_prev_layout_command (const char *arg, int from_tty)
427326a8 183{
416eb92d
TT
184 tui_enable ();
185 size_t index = find_layout (applied_skeleton);
186 if (index == 0)
187 index = layouts.size ();
188 --index;
189 tui_set_layout (layouts[index].get ());
427326a8 190}
c906108c 191
416eb92d 192
5afe342e
TT
193/* See tui-layout.h. */
194
0dbc2fc7
TT
195void
196tui_regs_layout ()
197{
416eb92d
TT
198 /* If there's already a register window, we're done. */
199 if (TUI_DATA_WIN != nullptr)
200 return;
201
202 tui_set_layout (TUI_DISASM_WIN != nullptr
203 ? asm_regs_layout
204 : src_regs_layout);
205}
206
207/* Implement the "layout regs" command. */
208
209static void
210tui_regs_layout_command (const char *arg, int from_tty)
211{
212 tui_enable ();
213 tui_regs_layout ();
0dbc2fc7
TT
214}
215
216/* See tui-layout.h. */
217
5afe342e
TT
218void
219tui_remove_some_windows ()
220{
221 tui_win_info *focus = tui_win_with_focus ();
222
223 if (strcmp (focus->name (), "cmd") == 0)
224 {
225 /* Try leaving the source or disassembly window. If neither
226 exists, just do nothing. */
227 focus = TUI_SRC_WIN;
228 if (focus == nullptr)
229 focus = TUI_DISASM_WIN;
230 if (focus == nullptr)
231 return;
232 }
233
234 applied_layout->remove_windows (focus->name ());
235 tui_apply_current_layout ();
236}
237
13274fc3
UW
238static void
239extract_display_start_addr (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
c906108c 240{
416eb92d
TT
241 struct gdbarch *gdbarch = nullptr;
242 CORE_ADDR addr = 0;
84b1e7c7 243 CORE_ADDR pc;
52575520 244 struct symtab_and_line cursal = get_current_source_symtab_and_line ();
c906108c 245
416eb92d 246 if (TUI_SRC_WIN != nullptr)
c906108c 247 {
e6e41501 248 gdbarch = TUI_SRC_WIN->gdbarch;
52575520 249 find_line_pc (cursal.symtab,
e6e41501 250 TUI_SRC_WIN->start_line_or_addr.u.line_no,
84b1e7c7 251 &pc);
c774cec6 252 addr = pc;
416eb92d
TT
253 }
254 else if (TUI_DISASM_WIN != nullptr)
255 {
e6e41501
TT
256 gdbarch = TUI_DISASM_WIN->gdbarch;
257 addr = TUI_DISASM_WIN->start_line_or_addr.u.addr;
c906108c
SS
258 }
259
13274fc3
UW
260 *gdbarch_p = gdbarch;
261 *addr_p = addr;
6ba8e26f 262}
c906108c 263
d6ba6a11 264void
ee556432
TT
265tui_gen_win_info::resize (int height_, int width_,
266 int origin_x_, int origin_y_)
c906108c 267{
cdaa6eb4 268 if (width == width_ && height == height_
fb3184d8 269 && x == origin_x_ && y == origin_y_
cdaa6eb4
TT
270 && handle != nullptr)
271 return;
272
d6ba6a11 273 width = width_;
db502012 274 height = height_;
fb3184d8
TT
275 x = origin_x_;
276 y = origin_y_;
db502012
TT
277
278 if (handle != nullptr)
279 {
280#ifdef HAVE_WRESIZE
7523da63 281 wresize (handle.get (), height, width);
fb3184d8 282 mvwin (handle.get (), y, x);
7523da63 283 wmove (handle.get (), 0, 0);
db502012 284#else
7523da63 285 handle.reset (nullptr);
db502012
TT
286#endif
287 }
288
289 if (handle == nullptr)
ab0e1f1a 290 make_window ();
3df505f6
TT
291
292 rerender ();
d6ba6a11 293}
c906108c 294
d9fcefd5
TT
295\f
296
389e7ddb
TT
297/* Helper function that returns a TUI window, given its name. */
298
299static tui_gen_win_info *
300tui_get_window_by_name (const std::string &name)
301{
302 if (name == "src")
303 {
304 if (tui_win_list[SRC_WIN] == nullptr)
305 tui_win_list[SRC_WIN] = new tui_source_window ();
306 return tui_win_list[SRC_WIN];
307 }
308 else if (name == "cmd")
309 {
310 if (tui_win_list[CMD_WIN] == nullptr)
311 tui_win_list[CMD_WIN] = new tui_cmd_window ();
312 return tui_win_list[CMD_WIN];
313 }
314 else if (name == "regs")
315 {
316 if (tui_win_list[DATA_WIN] == nullptr)
317 tui_win_list[DATA_WIN] = new tui_data_window ();
318 return tui_win_list[DATA_WIN];
319 }
320 else if (name == "asm")
321 {
322 if (tui_win_list[DISASSEM_WIN] == nullptr)
323 tui_win_list[DISASSEM_WIN] = new tui_disasm_window ();
324 return tui_win_list[DISASSEM_WIN];
325 }
326 else
327 {
416eb92d 328 gdb_assert (name == "status");
389e7ddb
TT
329 return tui_locator_win_info_ptr ();
330 }
331}
332
333/* See tui-layout.h. */
334
335std::unique_ptr<tui_layout_base>
336tui_layout_window::clone () const
337{
338 tui_layout_window *result = new tui_layout_window (m_contents.c_str ());
339 return std::unique_ptr<tui_layout_base> (result);
340}
341
342/* See tui-layout.h. */
343
344void
345tui_layout_window::apply (int x_, int y_, int width_, int height_)
346{
347 x = x_;
348 y = y_;
349 width = width_;
350 height = height_;
351 gdb_assert (m_window != nullptr);
352 m_window->resize (height, width, x, y);
353}
354
355/* See tui-layout.h. */
356
357void
7c043ba6 358tui_layout_window::get_sizes (bool height, int *min_value, int *max_value)
389e7ddb
TT
359{
360 if (m_window == nullptr)
361 m_window = tui_get_window_by_name (m_contents);
7c043ba6
TT
362 if (height)
363 {
364 *min_value = m_window->min_height ();
365 *max_value = m_window->max_height ();
366 }
367 else
368 {
369 *min_value = m_window->min_width ();
370 *max_value = m_window->max_width ();
371 }
389e7ddb
TT
372}
373
374/* See tui-layout.h. */
375
376bool
377tui_layout_window::top_boxed_p () const
378{
379 gdb_assert (m_window != nullptr);
380 return m_window->can_box ();
381}
382
383/* See tui-layout.h. */
384
385bool
386tui_layout_window::bottom_boxed_p () const
387{
388 gdb_assert (m_window != nullptr);
389 return m_window->can_box ();
390}
391
392/* See tui-layout.h. */
393
416eb92d
TT
394void
395tui_layout_window::replace_window (const char *name, const char *new_window)
396{
397 if (m_contents == name)
398 {
399 m_contents = new_window;
400 if (m_window != nullptr)
401 {
402 m_window->make_visible (false);
403 m_window = tui_get_window_by_name (m_contents);
404 }
405 }
406}
407
408/* See tui-layout.h. */
409
ee325b61 410void
c22fef7e 411tui_layout_window::specification (ui_file *output, int depth)
ee325b61
TT
412{
413 fputs_unfiltered (get_name (), output);
414}
415
416/* See tui-layout.h. */
417
c22fef7e
TT
418void
419tui_layout_split::add_split (std::unique_ptr<tui_layout_split> &&layout,
420 int weight)
389e7ddb 421{
c22fef7e 422 split s = {weight, std::move (layout)};
389e7ddb 423 m_splits.push_back (std::move (s));
389e7ddb
TT
424}
425
426/* See tui-layout.h. */
427
428void
429tui_layout_split::add_window (const char *name, int weight)
430{
431 tui_layout_window *result = new tui_layout_window (name);
432 split s = {weight, std::unique_ptr<tui_layout_base> (result)};
433 m_splits.push_back (std::move (s));
434}
435
436/* See tui-layout.h. */
437
438std::unique_ptr<tui_layout_base>
439tui_layout_split::clone () const
440{
7c043ba6 441 tui_layout_split *result = new tui_layout_split (m_vertical);
389e7ddb
TT
442 for (const split &item : m_splits)
443 {
444 std::unique_ptr<tui_layout_base> next = item.layout->clone ();
445 split s = {item.weight, std::move (next)};
446 result->m_splits.push_back (std::move (s));
447 }
448 return std::unique_ptr<tui_layout_base> (result);
449}
450
451/* See tui-layout.h. */
452
453void
7c043ba6 454tui_layout_split::get_sizes (bool height, int *min_value, int *max_value)
389e7ddb 455{
7c043ba6
TT
456 *min_value = 0;
457 *max_value = 0;
458 bool first_time = true;
389e7ddb
TT
459 for (const split &item : m_splits)
460 {
461 int new_min, new_max;
7c043ba6
TT
462 item.layout->get_sizes (height, &new_min, &new_max);
463 /* For the mismatch case, the first time through we want to set
464 the min and max to the computed values -- the "first_time"
465 check here is just a funny way of doing that. */
466 if (height == m_vertical || first_time)
467 {
468 *min_value += new_min;
469 *max_value += new_max;
470 }
471 else
472 {
473 *min_value = std::max (*min_value, new_min);
474 *max_value = std::min (*max_value, new_max);
475 }
476 first_time = false;
389e7ddb
TT
477 }
478}
479
480/* See tui-layout.h. */
481
482bool
483tui_layout_split::top_boxed_p () const
484{
485 if (m_splits.empty ())
486 return false;
487 return m_splits[0].layout->top_boxed_p ();
488}
489
490/* See tui-layout.h. */
491
492bool
493tui_layout_split::bottom_boxed_p () const
494{
495 if (m_splits.empty ())
496 return false;
497 return m_splits.back ().layout->top_boxed_p ();
498}
499
500/* See tui-layout.h. */
501
502void
503tui_layout_split::set_weights_from_heights ()
504{
505 for (int i = 0; i < m_splits.size (); ++i)
506 m_splits[i].weight = m_splits[i].layout->height;
507}
508
509/* See tui-layout.h. */
510
6bc56648 511tui_adjust_result
389e7ddb
TT
512tui_layout_split::adjust_size (const char *name, int new_height)
513{
514 /* Look through the children. If one is a layout holding the named
515 window, we're done; or if one actually is the named window,
516 update it. */
517 int found_index = -1;
518 for (int i = 0; i < m_splits.size (); ++i)
519 {
6bc56648
TT
520 tui_adjust_result adjusted
521 = m_splits[i].layout->adjust_size (name, new_height);
522 if (adjusted == HANDLED)
523 return HANDLED;
524 if (adjusted == FOUND)
389e7ddb 525 {
7c043ba6
TT
526 if (!m_vertical)
527 return FOUND;
389e7ddb
TT
528 found_index = i;
529 break;
530 }
531 }
532
533 if (found_index == -1)
6bc56648 534 return NOT_FOUND;
389e7ddb 535 if (m_splits[found_index].layout->height == new_height)
6bc56648 536 return HANDLED;
389e7ddb
TT
537
538 set_weights_from_heights ();
539 int delta = m_splits[found_index].weight - new_height;
540 m_splits[found_index].weight = new_height;
541
542 /* Distribute the "delta" over the next window; but if the next
543 window cannot hold it all, keep going until we either find a
544 window that does, or until we loop all the way around. */
545 for (int i = 0; delta != 0 && i < m_splits.size () - 1; ++i)
546 {
547 int index = (found_index + 1 + i) % m_splits.size ();
548
549 int new_min, new_max;
7c043ba6 550 m_splits[index].layout->get_sizes (m_vertical, &new_min, &new_max);
389e7ddb
TT
551
552 if (delta < 0)
553 {
554 /* The primary window grew, so we are trying to shrink other
555 windows. */
556 int available = m_splits[index].weight - new_min;
557 int shrink_by = std::min (available, -delta);
558 m_splits[index].weight -= shrink_by;
559 delta += shrink_by;
560 }
561 else
562 {
563 /* The primary window shrank, so we are trying to grow other
564 windows. */
565 int available = new_max - m_splits[index].weight;
566 int grow_by = std::min (available, delta);
567 m_splits[index].weight += grow_by;
568 delta -= grow_by;
569 }
570 }
571
572 if (delta != 0)
573 {
574 warning (_("Invalid window height specified"));
575 /* Effectively undo any modifications made here. */
576 set_weights_from_heights ();
577 }
578 else
579 {
580 /* Simply re-apply the updated layout. */
581 apply (x, y, width, height);
582 }
583
6bc56648 584 return HANDLED;
389e7ddb
TT
585}
586
587/* See tui-layout.h. */
588
589void
590tui_layout_split::apply (int x_, int y_, int width_, int height_)
591{
592 x = x_;
593 y = y_;
594 width = width_;
595 height = height_;
596
7c043ba6 597 struct size_info
389e7ddb 598 {
7c043ba6
TT
599 int size;
600 int min_size;
601 int max_size;
389e7ddb
TT
602 /* True if this window will share a box border with the previous
603 window in the list. */
604 bool share_box;
605 };
606
7c043ba6 607 std::vector<size_info> info (m_splits.size ());
389e7ddb 608
7c043ba6
TT
609 /* Step 1: Find the min and max size of each sub-layout.
610 Fixed-sized layouts are given their desired size, and then the
389e7ddb
TT
611 remaining space is distributed among the remaining windows
612 according to the weights given. */
7c043ba6 613 int available_size = m_vertical ? height : width;
389e7ddb
TT
614 int last_index = -1;
615 int total_weight = 0;
616 for (int i = 0; i < m_splits.size (); ++i)
617 {
618 bool cmd_win_already_exists = TUI_CMD_WIN != nullptr;
619
620 /* Always call get_sizes, to ensure that the window is
621 instantiated. This is a bit gross but less gross than adding
622 special cases for this in other places. */
7c043ba6
TT
623 m_splits[i].layout->get_sizes (m_vertical, &info[i].min_size,
624 &info[i].max_size);
389e7ddb
TT
625
626 if (!m_applied
627 && cmd_win_already_exists
628 && m_splits[i].layout->get_name () != nullptr
629 && strcmp (m_splits[i].layout->get_name (), "cmd") == 0)
630 {
631 /* If this layout has never been applied, then it means the
632 user just changed the layout. In this situation, it's
633 desirable to keep the size of the command window the
7c043ba6 634 same. Setting the min and max sizes this way ensures
389e7ddb
TT
635 that the resizing step, below, does the right thing with
636 this window. */
7c043ba6
TT
637 info[i].min_size = (m_vertical
638 ? TUI_CMD_WIN->height
639 : TUI_CMD_WIN->width);
640 info[i].max_size = info[i].min_size;
389e7ddb
TT
641 }
642
7c043ba6
TT
643 if (info[i].min_size == info[i].max_size)
644 available_size -= info[i].min_size;
389e7ddb
TT
645 else
646 {
647 last_index = i;
648 total_weight += m_splits[i].weight;
649 }
650
651 /* Two adjacent boxed windows will share a border, making a bit
7c043ba6 652 more size available. */
389e7ddb
TT
653 if (i > 0
654 && m_splits[i - 1].layout->bottom_boxed_p ()
655 && m_splits[i].layout->top_boxed_p ())
656 info[i].share_box = true;
657 }
658
7c043ba6 659 /* Step 2: Compute the size of each sub-layout. Fixed-sized items
389e7ddb
TT
660 are given their fixed size, while others are resized according to
661 their weight. */
7c043ba6 662 int used_size = 0;
389e7ddb
TT
663 for (int i = 0; i < m_splits.size (); ++i)
664 {
665 /* Compute the height and clamp to the allowable range. */
7c043ba6
TT
666 info[i].size = available_size * m_splits[i].weight / total_weight;
667 if (info[i].size > info[i].max_size)
668 info[i].size = info[i].max_size;
669 if (info[i].size < info[i].min_size)
670 info[i].size = info[i].min_size;
671 /* If there is any leftover size, just redistribute it to the
389e7ddb 672 last resizeable window, by dropping it from the allocated
7c043ba6
TT
673 size. We could try to be fancier here perhaps, by
674 redistributing this size among all windows, not just the
389e7ddb 675 last window. */
7c043ba6 676 if (info[i].min_size != info[i].max_size)
389e7ddb 677 {
7c043ba6 678 used_size += info[i].size;
389e7ddb 679 if (info[i].share_box)
7c043ba6 680 --used_size;
389e7ddb
TT
681 }
682 }
683
7c043ba6
TT
684 /* Allocate any leftover size. */
685 if (available_size >= used_size && last_index != -1)
686 info[last_index].size += available_size - used_size;
389e7ddb
TT
687
688 /* Step 3: Resize. */
7c043ba6
TT
689 int size_accum = 0;
690 const int maximum = m_vertical ? height : width;
389e7ddb
TT
691 for (int i = 0; i < m_splits.size (); ++i)
692 {
693 /* If we fall off the bottom, just make allocations overlap.
694 GIGO. */
7c043ba6
TT
695 if (size_accum + info[i].size > maximum)
696 size_accum = maximum - info[i].size;
389e7ddb 697 else if (info[i].share_box)
7c043ba6
TT
698 --size_accum;
699 if (m_vertical)
700 m_splits[i].layout->apply (x, y + size_accum, width, info[i].size);
701 else
702 m_splits[i].layout->apply (x + size_accum, y, info[i].size, height);
703 size_accum += info[i].size;
389e7ddb
TT
704 }
705
706 m_applied = true;
707}
708
5afe342e
TT
709/* See tui-layout.h. */
710
711void
712tui_layout_split::remove_windows (const char *name)
713{
714 for (int i = 0; i < m_splits.size (); ++i)
715 {
716 const char *this_name = m_splits[i].layout->get_name ();
717 if (this_name == nullptr)
718 m_splits[i].layout->remove_windows (name);
719 else
720 {
721 if (strcmp (this_name, name) == 0
722 || strcmp (this_name, "cmd") == 0)
723 {
724 /* Keep. */
725 }
726 m_splits.erase (m_splits.begin () + i);
727 --i;
728 }
729 }
730}
731
416eb92d
TT
732/* See tui-layout.h. */
733
734void
735tui_layout_split::replace_window (const char *name, const char *new_window)
736{
737 for (auto &item : m_splits)
738 item.layout->replace_window (name, new_window);
739}
740
ee325b61
TT
741/* See tui-layout.h. */
742
743void
c22fef7e 744tui_layout_split::specification (ui_file *output, int depth)
ee325b61 745{
c22fef7e
TT
746 if (depth > 0)
747 fputs_unfiltered ("{", output);
748
7c043ba6
TT
749 if (!m_vertical)
750 fputs_unfiltered ("-horizontal ", output);
751
ee325b61
TT
752 bool first = true;
753 for (auto &item : m_splits)
754 {
755 if (!first)
756 fputs_unfiltered (" ", output);
757 first = false;
c22fef7e 758 item.layout->specification (output, depth + 1);
ee325b61
TT
759 fprintf_unfiltered (output, " %d", item.weight);
760 }
c22fef7e
TT
761
762 if (depth > 0)
763 fputs_unfiltered ("}", output);
ee325b61
TT
764}
765
416eb92d
TT
766/* Destroy the layout associated with SELF. */
767
2192a9d3 768static void
416eb92d
TT
769destroy_layout (struct cmd_list_element *self, void *context)
770{
771 tui_layout_split *layout = (tui_layout_split *) context;
772 size_t index = find_layout (layout);
773 layouts.erase (layouts.begin () + index);
774}
775
776/* List holding the sub-commands of "layout". */
777
778static struct cmd_list_element *layout_list;
779
780/* Add a "layout" command with name NAME that switches to LAYOUT. */
781
ee325b61 782static struct cmd_list_element *
416eb92d 783add_layout_command (const char *name, tui_layout_split *layout)
2192a9d3 784{
416eb92d 785 struct cmd_list_element *cmd;
2192a9d3 786
ee325b61 787 string_file spec;
c22fef7e 788 layout->specification (&spec, 0);
ee325b61
TT
789
790 gdb::unique_xmalloc_ptr<char> doc
791 (xstrprintf (_("Apply the \"%s\" layout.\n\
792This layout was created using:\n\
793 tui new-layout %s %s"),
794 name, name, spec.c_str ()));
2192a9d3 795
416eb92d
TT
796 cmd = add_cmd (name, class_tui, nullptr, doc.get (), &layout_list);
797 set_cmd_context (cmd, layout);
798 /* There is no API to set this. */
799 cmd->func = tui_apply_layout;
800 cmd->destroyer = destroy_layout;
801 cmd->doc_allocated = 1;
802 doc.release ();
803 layouts.emplace_back (layout);
ee325b61
TT
804
805 return cmd;
416eb92d 806}
2192a9d3 807
416eb92d 808/* Initialize the standard layouts. */
2192a9d3 809
416eb92d
TT
810static void
811initialize_layouts ()
812{
813 tui_layout_split *layout;
814
815 layout = new tui_layout_split ();
816 layout->add_window ("src", 2);
817 layout->add_window ("status", 0);
818 layout->add_window ("cmd", 1);
819 add_layout_command ("src", layout);
820
821 layout = new tui_layout_split ();
822 layout->add_window ("asm", 2);
823 layout->add_window ("status", 0);
824 layout->add_window ("cmd", 1);
825 add_layout_command ("asm", layout);
826
827 layout = new tui_layout_split ();
828 layout->add_window ("src", 1);
829 layout->add_window ("asm", 1);
830 layout->add_window ("status", 0);
831 layout->add_window ("cmd", 1);
832 add_layout_command ("split", layout);
833
834 layout = new tui_layout_split ();
835 layout->add_window ("regs", 1);
836 layout->add_window ("src", 1);
837 layout->add_window ("status", 0);
838 layout->add_window ("cmd", 1);
839 layouts.emplace_back (layout);
840 src_regs_layout = layout;
841
842 layout = new tui_layout_split ();
843 layout->add_window ("regs", 1);
844 layout->add_window ("asm", 1);
845 layout->add_window ("status", 0);
846 layout->add_window ("cmd", 1);
847 layouts.emplace_back (layout);
848 asm_regs_layout = layout;
2192a9d3
TT
849}
850
389e7ddb
TT
851\f
852
ee325b61
TT
853/* A helper function that returns true if NAME is the name of an
854 available window. */
855
856static bool
857validate_window_name (const std::string &name)
858{
859 return (name == "src" || name == "cmd"
860 || name == "regs" || name == "asm"
861 || name == "status");
862}
863
864/* Implementation of the "tui new-layout" command. */
865
866static void
867tui_new_layout_command (const char *spec, int from_tty)
868{
869 std::string new_name = extract_arg (&spec);
870 if (new_name.empty ())
871 error (_("No layout name specified"));
872 if (new_name[0] == '-')
873 error (_("Layout name cannot start with '-'"));
874
7c043ba6
TT
875 bool is_vertical = true;
876 spec = skip_spaces (spec);
877 if (check_for_argument (&spec, "-horizontal"))
878 is_vertical = false;
879
c22fef7e 880 std::vector<std::unique_ptr<tui_layout_split>> splits;
7c043ba6 881 splits.emplace_back (new tui_layout_split (is_vertical));
ee325b61
TT
882 std::unordered_set<std::string> seen_windows;
883 while (true)
884 {
c22fef7e
TT
885 spec = skip_spaces (spec);
886 if (spec[0] == '\0')
ee325b61 887 break;
c22fef7e
TT
888
889 if (spec[0] == '{')
890 {
7c043ba6
TT
891 is_vertical = true;
892 spec = skip_spaces (spec + 1);
893 if (check_for_argument (&spec, "-horizontal"))
894 is_vertical = false;
895 splits.emplace_back (new tui_layout_split (is_vertical));
c22fef7e
TT
896 continue;
897 }
898
899 bool is_close = false;
900 std::string name;
901 if (spec[0] == '}')
902 {
903 is_close = true;
904 ++spec;
905 if (splits.size () == 1)
906 error (_("Extra '}' in layout specification"));
907 }
908 else
909 {
910 name = extract_arg (&spec);
911 if (name.empty ())
912 break;
913 if (!validate_window_name (name))
914 error (_("Unknown window \"%s\""), name.c_str ());
915 if (seen_windows.find (name) != seen_windows.end ())
916 error (_("Window \"%s\" seen twice in layout"), name.c_str ());
917 }
918
919 ULONGEST weight = get_ulongest (&spec, '}');
ee325b61
TT
920 if ((int) weight != weight)
921 error (_("Weight out of range: %s"), pulongest (weight));
c22fef7e
TT
922 if (is_close)
923 {
924 std::unique_ptr<tui_layout_split> last_split
925 = std::move (splits.back ());
926 splits.pop_back ();
927 splits.back ()->add_split (std::move (last_split), weight);
928 }
929 else
930 {
931 splits.back ()->add_window (name.c_str (), weight);
932 seen_windows.insert (name);
933 }
ee325b61 934 }
c22fef7e
TT
935 if (splits.size () > 1)
936 error (_("Missing '}' in layout specification"));
ee325b61
TT
937 if (seen_windows.empty ())
938 error (_("New layout does not contain any windows"));
939 if (seen_windows.find ("cmd") == seen_windows.end ())
940 error (_("New layout does not contain the \"cmd\" window"));
941
942 gdb::unique_xmalloc_ptr<char> cmd_name
943 = make_unique_xstrdup (new_name.c_str ());
c22fef7e 944 std::unique_ptr<tui_layout_split> new_layout = std::move (splits.back ());
ee325b61
TT
945 struct cmd_list_element *cmd
946 = add_layout_command (cmd_name.get (), new_layout.get ());
947 cmd->name_allocated = 1;
948 cmd_name.release ();
949 new_layout.release ();
950}
951
416eb92d
TT
952/* Base command for "layout". */
953
954static void
955tui_layout_command (const char *layout_name, int from_tty)
956{
957 help_list (layout_list, "layout ", all_commands, gdb_stdout);
958}
959
d9fcefd5
TT
960/* Function to initialize gdb commands, for tui window layout
961 manipulation. */
962
6c265988 963void _initialize_tui_layout ();
d9fcefd5 964void
6c265988 965_initialize_tui_layout ()
d9fcefd5 966{
416eb92d 967 add_prefix_cmd ("layout", class_tui, tui_layout_command, _("\
d9fcefd5 968Change the layout of windows.\n\
416eb92d
TT
969Usage: layout prev | next | LAYOUT-NAME"),
970 &layout_list, "layout ", 0, &cmdlist);
971
972 add_cmd ("next", class_tui, tui_next_layout_command,
973 _("Apply the next TUI layout"),
974 &layout_list);
975 add_cmd ("prev", class_tui, tui_prev_layout_command,
976 _("Apply the previous TUI layout"),
977 &layout_list);
978 add_cmd ("regs", class_tui, tui_regs_layout_command,
979 _("Apply the TUI register layout"),
980 &layout_list);
2192a9d3 981
ee325b61
TT
982 add_cmd ("new-layout", class_tui, tui_new_layout_command,
983 _("Create a new TUI layout.\n\
7c043ba6 984Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
ee325b61
TT
985Create a new TUI layout. The new layout will be named NAME,\n\
986and can be accessed using \"layout NAME\".\n\
987The windows will be displayed in the specified order.\n\
c22fef7e 988A WINDOW can also be of the form:\n\
7c043ba6 989 { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
c22fef7e 990This form indicates a sub-frame.\n\
ee325b61
TT
991Each WEIGHT is an integer, which holds the relative size\n\
992to be allocated to the window."),
993 tui_get_cmd_list ());
994
2192a9d3 995 initialize_layouts ();
d9fcefd5 996}
This page took 2.218943 seconds and 4 git commands to generate.