Change TUI window iteration
[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 tui_windows.clear ();
73 applied_layout->apply (0, 0, tui_term_width (), tui_term_height ());
74 }
75
76 /* See tui-layout. */
77
78 void
79 tui_adjust_window_height (struct tui_win_info *win, int new_height)
80 {
81 applied_layout->adjust_size (win->name (), new_height);
82 }
83
84 /* Set the current layout to LAYOUT. */
85
86 static void
87 tui_set_layout (tui_layout_split *layout)
88 {
89 struct gdbarch *gdbarch;
90 CORE_ADDR addr;
91
92 extract_display_start_addr (&gdbarch, &addr);
93 tui_make_all_invisible ();
94 applied_skeleton = layout;
95 applied_layout = layout->clone ();
96 tui_apply_current_layout ();
97 tui_delete_invisible_windows ();
98
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);
102 }
103
104 /* See tui-layout.h. */
105
106 void
107 tui_add_win_to_layout (enum tui_win_type type)
108 {
109 gdb_assert (type == SRC_WIN || type == DISASSEM_WIN);
110
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. */
128
129 static size_t
130 find_layout (tui_layout_split *layout)
131 {
132 for (size_t i = 0; i < layouts.size (); ++i)
133 {
134 if (layout == layouts[i].get ())
135 return i;
136 }
137 gdb_assert_not_reached (_("layout not found!?"));
138 }
139
140 /* Function to set the layout. */
141
142 static void
143 tui_apply_layout (struct cmd_list_element *command,
144 const char *args, int from_tty)
145 {
146 tui_layout_split *layout
147 = (tui_layout_split *) get_cmd_context (command);
148
149 /* Make sure the curses mode is enabled. */
150 tui_enable ();
151 tui_set_layout (layout);
152 }
153
154 /* See tui-layout.h. */
155
156 void
157 tui_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 }
165
166 /* Implement the "layout next" command. */
167
168 static void
169 tui_next_layout_command (const char *arg, int from_tty)
170 {
171 tui_enable ();
172 tui_next_layout ();
173 }
174
175 /* See tui-layout.h. */
176
177 void
178 tui_set_initial_layout ()
179 {
180 tui_set_layout (layouts[0].get ());
181 }
182
183 /* Implement the "layout prev" command. */
184
185 static void
186 tui_prev_layout_command (const char *arg, int from_tty)
187 {
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 ());
194 }
195
196
197 /* See tui-layout.h. */
198
199 void
200 tui_regs_layout ()
201 {
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
213 static void
214 tui_regs_layout_command (const char *arg, int from_tty)
215 {
216 tui_enable ();
217 tui_regs_layout ();
218 }
219
220 /* See tui-layout.h. */
221
222 void
223 tui_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
242 static void
243 extract_display_start_addr (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
244 {
245 struct gdbarch *gdbarch = nullptr;
246 CORE_ADDR addr = 0;
247 CORE_ADDR pc;
248 struct symtab_and_line cursal = get_current_source_symtab_and_line ();
249
250 if (TUI_SRC_WIN != nullptr)
251 {
252 gdbarch = TUI_SRC_WIN->gdbarch;
253 find_line_pc (cursal.symtab,
254 TUI_SRC_WIN->start_line_or_addr.u.line_no,
255 &pc);
256 addr = pc;
257 }
258 else if (TUI_DISASM_WIN != nullptr)
259 {
260 gdbarch = TUI_DISASM_WIN->gdbarch;
261 addr = TUI_DISASM_WIN->start_line_or_addr.u.addr;
262 }
263
264 *gdbarch_p = gdbarch;
265 *addr_p = addr;
266 }
267
268 void
269 tui_gen_win_info::resize (int height_, int width_,
270 int origin_x_, int origin_y_)
271 {
272 if (width == width_ && height == height_
273 && x == origin_x_ && y == origin_y_
274 && handle != nullptr)
275 return;
276
277 width = width_;
278 height = height_;
279 x = origin_x_;
280 y = origin_y_;
281
282 if (handle != nullptr)
283 {
284 #ifdef HAVE_WRESIZE
285 wresize (handle.get (), height, width);
286 mvwin (handle.get (), y, x);
287 wmove (handle.get (), 0, 0);
288 #else
289 handle.reset (nullptr);
290 #endif
291 }
292
293 if (handle == nullptr)
294 make_window ();
295
296 rerender ();
297 }
298
299 \f
300
301 /* Helper function that returns a TUI window, given its name. */
302
303 static tui_gen_win_info *
304 tui_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 {
332 gdb_assert (name == "status");
333 return tui_locator_win_info_ptr ();
334 }
335 }
336
337 /* See tui-layout.h. */
338
339 std::unique_ptr<tui_layout_base>
340 tui_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
348 void
349 tui_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);
357 if (dynamic_cast<tui_win_info *> (m_window) != nullptr)
358 tui_windows.push_back ((tui_win_info *) m_window);
359 }
360
361 /* See tui-layout.h. */
362
363 void
364 tui_layout_window::get_sizes (bool height, int *min_value, int *max_value)
365 {
366 if (m_window == nullptr)
367 m_window = tui_get_window_by_name (m_contents);
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 }
378 }
379
380 /* See tui-layout.h. */
381
382 bool
383 tui_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
391 bool
392 tui_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
400 void
401 tui_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
416 void
417 tui_layout_window::specification (ui_file *output, int depth)
418 {
419 fputs_unfiltered (get_name (), output);
420 }
421
422 /* See tui-layout.h. */
423
424 void
425 tui_layout_split::add_split (std::unique_ptr<tui_layout_split> &&layout,
426 int weight)
427 {
428 split s = {weight, std::move (layout)};
429 m_splits.push_back (std::move (s));
430 }
431
432 /* See tui-layout.h. */
433
434 void
435 tui_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
444 std::unique_ptr<tui_layout_base>
445 tui_layout_split::clone () const
446 {
447 tui_layout_split *result = new tui_layout_split (m_vertical);
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
459 void
460 tui_layout_split::get_sizes (bool height, int *min_value, int *max_value)
461 {
462 *min_value = 0;
463 *max_value = 0;
464 bool first_time = true;
465 for (const split &item : m_splits)
466 {
467 int new_min, new_max;
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;
483 }
484 }
485
486 /* See tui-layout.h. */
487
488 bool
489 tui_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
498 bool
499 tui_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
508 void
509 tui_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
517 tui_adjust_result
518 tui_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 {
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)
531 {
532 if (!m_vertical)
533 return FOUND;
534 found_index = i;
535 break;
536 }
537 }
538
539 if (found_index == -1)
540 return NOT_FOUND;
541 if (m_splits[found_index].layout->height == new_height)
542 return HANDLED;
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;
556 m_splits[index].layout->get_sizes (m_vertical, &new_min, &new_max);
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
590 return HANDLED;
591 }
592
593 /* See tui-layout.h. */
594
595 void
596 tui_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
603 struct size_info
604 {
605 int size;
606 int min_size;
607 int max_size;
608 /* True if this window will share a box border with the previous
609 window in the list. */
610 bool share_box;
611 };
612
613 std::vector<size_info> info (m_splits.size ());
614
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
617 remaining space is distributed among the remaining windows
618 according to the weights given. */
619 int available_size = m_vertical ? height : width;
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. */
629 m_splits[i].layout->get_sizes (m_vertical, &info[i].min_size,
630 &info[i].max_size);
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
640 same. Setting the min and max sizes this way ensures
641 that the resizing step, below, does the right thing with
642 this window. */
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;
647 }
648
649 if (info[i].min_size == info[i].max_size)
650 available_size -= info[i].min_size;
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
658 more size available. */
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
665 /* Step 2: Compute the size of each sub-layout. Fixed-sized items
666 are given their fixed size, while others are resized according to
667 their weight. */
668 int used_size = 0;
669 for (int i = 0; i < m_splits.size (); ++i)
670 {
671 /* Compute the height and clamp to the allowable range. */
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
678 last resizeable window, by dropping it from the allocated
679 size. We could try to be fancier here perhaps, by
680 redistributing this size among all windows, not just the
681 last window. */
682 if (info[i].min_size != info[i].max_size)
683 {
684 used_size += info[i].size;
685 if (info[i].share_box)
686 --used_size;
687 }
688 }
689
690 /* Allocate any leftover size. */
691 if (available_size >= used_size && last_index != -1)
692 info[last_index].size += available_size - used_size;
693
694 /* Step 3: Resize. */
695 int size_accum = 0;
696 const int maximum = m_vertical ? height : width;
697 for (int i = 0; i < m_splits.size (); ++i)
698 {
699 /* If we fall off the bottom, just make allocations overlap.
700 GIGO. */
701 if (size_accum + info[i].size > maximum)
702 size_accum = maximum - info[i].size;
703 else if (info[i].share_box)
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;
710 }
711
712 m_applied = true;
713 }
714
715 /* See tui-layout.h. */
716
717 void
718 tui_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
738 /* See tui-layout.h. */
739
740 void
741 tui_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
747 /* See tui-layout.h. */
748
749 void
750 tui_layout_split::specification (ui_file *output, int depth)
751 {
752 if (depth > 0)
753 fputs_unfiltered ("{", output);
754
755 if (!m_vertical)
756 fputs_unfiltered ("-horizontal ", output);
757
758 bool first = true;
759 for (auto &item : m_splits)
760 {
761 if (!first)
762 fputs_unfiltered (" ", output);
763 first = false;
764 item.layout->specification (output, depth + 1);
765 fprintf_unfiltered (output, " %d", item.weight);
766 }
767
768 if (depth > 0)
769 fputs_unfiltered ("}", output);
770 }
771
772 /* Destroy the layout associated with SELF. */
773
774 static void
775 destroy_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
784 static struct cmd_list_element *layout_list;
785
786 /* Add a "layout" command with name NAME that switches to LAYOUT. */
787
788 static struct cmd_list_element *
789 add_layout_command (const char *name, tui_layout_split *layout)
790 {
791 struct cmd_list_element *cmd;
792
793 string_file spec;
794 layout->specification (&spec, 0);
795
796 gdb::unique_xmalloc_ptr<char> doc
797 (xstrprintf (_("Apply the \"%s\" layout.\n\
798 This layout was created using:\n\
799 tui new-layout %s %s"),
800 name, name, spec.c_str ()));
801
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);
810
811 return cmd;
812 }
813
814 /* Initialize the standard layouts. */
815
816 static void
817 initialize_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;
855 }
856
857 \f
858
859 /* A helper function that returns true if NAME is the name of an
860 available window. */
861
862 static bool
863 validate_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
872 static void
873 tui_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
881 bool is_vertical = true;
882 spec = skip_spaces (spec);
883 if (check_for_argument (&spec, "-horizontal"))
884 is_vertical = false;
885
886 std::vector<std::unique_ptr<tui_layout_split>> splits;
887 splits.emplace_back (new tui_layout_split (is_vertical));
888 std::unordered_set<std::string> seen_windows;
889 while (true)
890 {
891 spec = skip_spaces (spec);
892 if (spec[0] == '\0')
893 break;
894
895 if (spec[0] == '{')
896 {
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));
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, '}');
926 if ((int) weight != weight)
927 error (_("Weight out of range: %s"), pulongest (weight));
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 }
940 }
941 if (splits.size () > 1)
942 error (_("Missing '}' in layout specification"));
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 ());
950 std::unique_ptr<tui_layout_split> new_layout = std::move (splits.back ());
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
958 /* Base command for "layout". */
959
960 static void
961 tui_layout_command (const char *layout_name, int from_tty)
962 {
963 help_list (layout_list, "layout ", all_commands, gdb_stdout);
964 }
965
966 /* Function to initialize gdb commands, for tui window layout
967 manipulation. */
968
969 void _initialize_tui_layout ();
970 void
971 _initialize_tui_layout ()
972 {
973 add_prefix_cmd ("layout", class_tui, tui_layout_command, _("\
974 Change the layout of windows.\n\
975 Usage: 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);
987
988 add_cmd ("new-layout", class_tui, tui_new_layout_command,
989 _("Create a new TUI layout.\n\
990 Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
991 Create a new TUI layout. The new layout will be named NAME,\n\
992 and can be accessed using \"layout NAME\".\n\
993 The windows will be displayed in the specified order.\n\
994 A WINDOW can also be of the form:\n\
995 { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
996 This form indicates a sub-frame.\n\
997 Each WEIGHT is an integer, which holds the relative size\n\
998 to be allocated to the window."),
999 tui_get_cmd_list ());
1000
1001 initialize_layouts ();
1002 }
This page took 0.066018 seconds and 5 git commands to generate.