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