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