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