Commit | Line | Data |
---|---|---|
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" |
84b1e7c7 | 28 | #include <ctype.h> |
c906108c | 29 | |
d7b2e967 | 30 | #include "tui/tui.h" |
ce38393b | 31 | #include "tui/tui-command.h" |
d7b2e967 | 32 | #include "tui/tui-data.h" |
d7b2e967 AC |
33 | #include "tui/tui-wingeneral.h" |
34 | #include "tui/tui-stack.h" | |
35 | #include "tui/tui-regs.h" | |
36 | #include "tui/tui-win.h" | |
37 | #include "tui/tui-winsource.h" | |
38 | #include "tui/tui-disasm.h" | |
2c0b251b | 39 | #include "tui/tui-layout.h" |
bfad4537 | 40 | #include "tui/tui-source.h" |
6a83354a | 41 | #include "gdb_curses.h" |
96ec9981 | 42 | |
6ba8e26f | 43 | static void show_layout (enum tui_layout_type); |
6ba8e26f AC |
44 | static enum tui_layout_type next_layout (void); |
45 | static enum tui_layout_type prev_layout (void); | |
0b39b52e | 46 | static void tui_layout_command (const char *, int); |
13274fc3 | 47 | static void extract_display_start_addr (struct gdbarch **, CORE_ADDR *); |
c906108c SS |
48 | |
49 | ||
2192a9d3 TT |
50 | /* The pre-defined layouts. */ |
51 | static tui_layout_split *standard_layouts[UNDEFINED_LAYOUT]; | |
52 | ||
53 | /* The layout that is currently applied. */ | |
54 | static std::unique_ptr<tui_layout_base> applied_layout; | |
55 | ||
62cf57fe TT |
56 | static enum tui_layout_type current_layout = UNDEFINED_LAYOUT; |
57 | ||
58 | /* Accessor for the current layout. */ | |
59 | enum tui_layout_type | |
60 | tui_current_layout (void) | |
61 | { | |
62 | return current_layout; | |
63 | } | |
64 | ||
2192a9d3 TT |
65 | /* See tui-layout.h. */ |
66 | ||
67 | void | |
68 | tui_apply_current_layout () | |
69 | { | |
70 | applied_layout->apply (0, 0, tui_term_width (), tui_term_height ()); | |
71 | } | |
c906108c | 72 | |
d4eeccfe TT |
73 | /* See tui-layout. */ |
74 | ||
75 | void | |
76 | tui_adjust_window_height (struct tui_win_info *win, int new_height) | |
77 | { | |
78 | applied_layout->adjust_size (win->name (), new_height); | |
79 | } | |
80 | ||
c7037be1 SC |
81 | /* Show the screen layout defined. */ |
82 | static void | |
6ba8e26f | 83 | show_layout (enum tui_layout_type layout) |
c906108c | 84 | { |
6ba8e26f | 85 | enum tui_layout_type cur_layout = tui_current_layout (); |
c906108c | 86 | |
6ba8e26f | 87 | if (layout != cur_layout) |
c906108c | 88 | { |
3f3ffe54 | 89 | tui_make_all_invisible (); |
2192a9d3 TT |
90 | applied_layout = standard_layouts[layout]->clone (); |
91 | tui_apply_current_layout (); | |
ed2c82c3 | 92 | current_layout = layout; |
fede5273 | 93 | tui_delete_invisible_windows (); |
c906108c | 94 | } |
bc712bbf | 95 | } |
c906108c SS |
96 | |
97 | ||
080ce8c0 | 98 | /* Function to set the layout to SRC_COMMAND, DISASSEM_COMMAND, |
7bd0be3a | 99 | SRC_DISASSEM_COMMAND, SRC_DATA_COMMAND, or DISASSEM_DATA_COMMAND. */ |
b7fbad91 | 100 | void |
7bd0be3a | 101 | tui_set_layout (enum tui_layout_type layout_type) |
c906108c | 102 | { |
b7fbad91 | 103 | gdb_assert (layout_type != UNDEFINED_LAYOUT); |
c906108c | 104 | |
b7fbad91 TT |
105 | enum tui_layout_type cur_layout = tui_current_layout (); |
106 | struct gdbarch *gdbarch; | |
107 | CORE_ADDR addr; | |
108 | struct tui_win_info *win_with_focus = tui_win_with_focus (); | |
c906108c | 109 | |
b7fbad91 | 110 | extract_display_start_addr (&gdbarch, &addr); |
c906108c | 111 | |
b7fbad91 | 112 | enum tui_layout_type new_layout = layout_type; |
c906108c | 113 | |
b7fbad91 TT |
114 | if (new_layout != cur_layout) |
115 | { | |
45bbae5c TT |
116 | tui_suppress_output suppress; |
117 | ||
b7fbad91 | 118 | show_layout (new_layout); |
ef5eab5a | 119 | |
b7fbad91 TT |
120 | /* Now determine where focus should be. */ |
121 | if (win_with_focus != TUI_CMD_WIN) | |
122 | { | |
123 | switch (new_layout) | |
7bd0be3a | 124 | { |
b7fbad91 TT |
125 | case SRC_COMMAND: |
126 | tui_set_win_focus_to (TUI_SRC_WIN); | |
b7fbad91 TT |
127 | break; |
128 | case DISASSEM_COMMAND: | |
129 | /* The previous layout was not showing code. | |
130 | This can happen if there is no source | |
131 | available: | |
132 | ||
133 | 1. if the source file is in another dir OR | |
134 | 2. if target was compiled without -g | |
135 | We still want to show the assembly though! */ | |
136 | ||
137 | tui_get_begin_asm_address (&gdbarch, &addr); | |
138 | tui_set_win_focus_to (TUI_DISASM_WIN); | |
b7fbad91 TT |
139 | break; |
140 | case SRC_DISASSEM_COMMAND: | |
141 | /* The previous layout was not showing code. | |
142 | This can happen if there is no source | |
143 | available: | |
144 | ||
145 | 1. if the source file is in another dir OR | |
146 | 2. if target was compiled without -g | |
147 | We still want to show the assembly though! */ | |
148 | ||
149 | tui_get_begin_asm_address (&gdbarch, &addr); | |
150 | if (win_with_focus == TUI_SRC_WIN) | |
151 | tui_set_win_focus_to (TUI_SRC_WIN); | |
152 | else | |
153 | tui_set_win_focus_to (TUI_DISASM_WIN); | |
154 | break; | |
155 | case SRC_DATA_COMMAND: | |
156 | if (win_with_focus != TUI_DATA_WIN) | |
157 | tui_set_win_focus_to (TUI_SRC_WIN); | |
158 | else | |
159 | tui_set_win_focus_to (TUI_DATA_WIN); | |
b7fbad91 TT |
160 | break; |
161 | case DISASSEM_DATA_COMMAND: | |
162 | /* The previous layout was not showing code. | |
163 | This can happen if there is no source | |
164 | available: | |
165 | ||
166 | 1. if the source file is in another dir OR | |
167 | 2. if target was compiled without -g | |
168 | We still want to show the assembly though! */ | |
169 | ||
170 | tui_get_begin_asm_address (&gdbarch, &addr); | |
171 | if (win_with_focus != TUI_DATA_WIN) | |
172 | tui_set_win_focus_to (TUI_DISASM_WIN); | |
173 | else | |
174 | tui_set_win_focus_to (TUI_DATA_WIN); | |
b7fbad91 TT |
175 | break; |
176 | default: | |
177 | break; | |
c906108c | 178 | } |
c906108c | 179 | } |
b7fbad91 TT |
180 | /* |
181 | * Now update the window content. | |
182 | */ | |
183 | tui_update_source_windows_with_addr (gdbarch, addr); | |
184 | if (new_layout == SRC_DATA_COMMAND | |
185 | || new_layout == DISASSEM_DATA_COMMAND) | |
89df7f90 | 186 | TUI_DATA_WIN->show_registers (TUI_DATA_WIN->get_current_group ()); |
c906108c | 187 | } |
bc712bbf | 188 | } |
c906108c | 189 | |
59b8b5d2 TT |
190 | /* See tui-layout.h. */ |
191 | ||
c906108c | 192 | void |
080ce8c0 | 193 | tui_add_win_to_layout (enum tui_win_type type) |
c906108c | 194 | { |
59b8b5d2 TT |
195 | gdb_assert (type == SRC_WIN || type == DISASSEM_WIN); |
196 | ||
6ba8e26f | 197 | enum tui_layout_type cur_layout = tui_current_layout (); |
c906108c SS |
198 | |
199 | switch (type) | |
200 | { | |
201 | case SRC_WIN: | |
e5908723 MS |
202 | if (cur_layout != SRC_COMMAND |
203 | && cur_layout != SRC_DISASSEM_COMMAND | |
204 | && cur_layout != SRC_DATA_COMMAND) | |
c906108c | 205 | { |
6ba8e26f | 206 | if (cur_layout == DISASSEM_DATA_COMMAND) |
f2302a34 | 207 | tui_set_layout (SRC_DATA_COMMAND); |
c906108c | 208 | else |
f2302a34 | 209 | tui_set_layout (SRC_COMMAND); |
c906108c SS |
210 | } |
211 | break; | |
212 | case DISASSEM_WIN: | |
e5908723 MS |
213 | if (cur_layout != DISASSEM_COMMAND |
214 | && cur_layout != SRC_DISASSEM_COMMAND | |
215 | && cur_layout != DISASSEM_DATA_COMMAND) | |
c906108c | 216 | { |
6ba8e26f | 217 | if (cur_layout == SRC_DATA_COMMAND) |
f2302a34 | 218 | tui_set_layout (DISASSEM_DATA_COMMAND); |
c906108c | 219 | else |
f2302a34 | 220 | tui_set_layout (DISASSEM_COMMAND); |
c906108c SS |
221 | } |
222 | break; | |
c906108c | 223 | } |
6ba8e26f | 224 | } |
c906108c | 225 | |
a0145030 AB |
226 | /* Complete possible layout names. TEXT is the complete text entered so |
227 | far, WORD is the word currently being completed. */ | |
228 | ||
eb3ff9a5 | 229 | static void |
a0145030 | 230 | layout_completer (struct cmd_list_element *ignore, |
eb3ff9a5 | 231 | completion_tracker &tracker, |
a0145030 AB |
232 | const char *text, const char *word) |
233 | { | |
234 | static const char *layout_names [] = | |
235 | { "src", "asm", "split", "regs", "next", "prev", NULL }; | |
236 | ||
eb3ff9a5 | 237 | complete_on_enum (tracker, layout_names, text, word); |
a0145030 AB |
238 | } |
239 | ||
7bd0be3a AB |
240 | /* Function to set the layout to SRC, ASM, SPLIT, NEXT, PREV, DATA, or |
241 | REGS. */ | |
0379b883 TT |
242 | static void |
243 | tui_layout_command (const char *layout_name, int from_tty) | |
c906108c | 244 | { |
0379b883 TT |
245 | enum tui_layout_type new_layout = UNDEFINED_LAYOUT; |
246 | enum tui_layout_type cur_layout = tui_current_layout (); | |
c906108c | 247 | |
78e8cb91 | 248 | if (layout_name == NULL || *layout_name == '\0') |
0379b883 | 249 | error (_("Usage: layout prev | next | LAYOUT-NAME")); |
c906108c | 250 | |
0379b883 | 251 | /* First check for ambiguous input. */ |
78e8cb91 | 252 | if (strcmp (layout_name, "s") == 0) |
0379b883 | 253 | error (_("Ambiguous command input.")); |
c906108c | 254 | |
78e8cb91 | 255 | if (subset_compare (layout_name, "src")) |
0379b883 | 256 | new_layout = SRC_COMMAND; |
78e8cb91 | 257 | else if (subset_compare (layout_name, "asm")) |
0379b883 | 258 | new_layout = DISASSEM_COMMAND; |
78e8cb91 | 259 | else if (subset_compare (layout_name, "split")) |
0379b883 | 260 | new_layout = SRC_DISASSEM_COMMAND; |
78e8cb91 | 261 | else if (subset_compare (layout_name, "regs")) |
0379b883 TT |
262 | { |
263 | if (cur_layout == SRC_COMMAND | |
264 | || cur_layout == SRC_DATA_COMMAND) | |
265 | new_layout = SRC_DATA_COMMAND; | |
266 | else | |
267 | new_layout = DISASSEM_DATA_COMMAND; | |
c906108c | 268 | } |
78e8cb91 | 269 | else if (subset_compare (layout_name, "next")) |
0379b883 | 270 | new_layout = next_layout (); |
78e8cb91 | 271 | else if (subset_compare (layout_name, "prev")) |
0379b883 | 272 | new_layout = prev_layout (); |
c906108c | 273 | else |
0379b883 | 274 | error (_("Unrecognized layout: %s"), layout_name); |
c906108c | 275 | |
0379b883 TT |
276 | /* Make sure the curses mode is enabled. */ |
277 | tui_enable (); | |
278 | tui_set_layout (new_layout); | |
e8b915dc | 279 | } |
c906108c | 280 | |
427326a8 TT |
281 | /* See tui-layout.h. */ |
282 | ||
283 | void | |
284 | tui_next_layout () | |
285 | { | |
286 | tui_layout_command ("next", 0); | |
287 | } | |
c906108c | 288 | |
5afe342e TT |
289 | /* See tui-layout.h. */ |
290 | ||
0dbc2fc7 TT |
291 | void |
292 | tui_regs_layout () | |
293 | { | |
294 | tui_layout_command ("regs", 0); | |
295 | } | |
296 | ||
297 | /* See tui-layout.h. */ | |
298 | ||
5afe342e TT |
299 | void |
300 | tui_remove_some_windows () | |
301 | { | |
302 | tui_win_info *focus = tui_win_with_focus (); | |
303 | ||
304 | if (strcmp (focus->name (), "cmd") == 0) | |
305 | { | |
306 | /* Try leaving the source or disassembly window. If neither | |
307 | exists, just do nothing. */ | |
308 | focus = TUI_SRC_WIN; | |
309 | if (focus == nullptr) | |
310 | focus = TUI_DISASM_WIN; | |
311 | if (focus == nullptr) | |
312 | return; | |
313 | } | |
314 | ||
315 | applied_layout->remove_windows (focus->name ()); | |
316 | tui_apply_current_layout (); | |
317 | } | |
318 | ||
13274fc3 UW |
319 | static void |
320 | extract_display_start_addr (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p) | |
c906108c | 321 | { |
6ba8e26f | 322 | enum tui_layout_type cur_layout = tui_current_layout (); |
957b8b5a | 323 | struct gdbarch *gdbarch = get_current_arch (); |
c774cec6 | 324 | CORE_ADDR addr; |
84b1e7c7 | 325 | CORE_ADDR pc; |
52575520 | 326 | struct symtab_and_line cursal = get_current_source_symtab_and_line (); |
c906108c | 327 | |
6ba8e26f | 328 | switch (cur_layout) |
c906108c SS |
329 | { |
330 | case SRC_COMMAND: | |
331 | case SRC_DATA_COMMAND: | |
e6e41501 | 332 | gdbarch = TUI_SRC_WIN->gdbarch; |
52575520 | 333 | find_line_pc (cursal.symtab, |
e6e41501 | 334 | TUI_SRC_WIN->start_line_or_addr.u.line_no, |
84b1e7c7 | 335 | &pc); |
c774cec6 | 336 | addr = pc; |
c906108c SS |
337 | break; |
338 | case DISASSEM_COMMAND: | |
339 | case SRC_DISASSEM_COMMAND: | |
340 | case DISASSEM_DATA_COMMAND: | |
e6e41501 TT |
341 | gdbarch = TUI_DISASM_WIN->gdbarch; |
342 | addr = TUI_DISASM_WIN->start_line_or_addr.u.addr; | |
c906108c SS |
343 | break; |
344 | default: | |
c774cec6 | 345 | addr = 0; |
c906108c SS |
346 | break; |
347 | } | |
348 | ||
13274fc3 UW |
349 | *gdbarch_p = gdbarch; |
350 | *addr_p = addr; | |
6ba8e26f | 351 | } |
c906108c SS |
352 | |
353 | ||
6ba8e26f | 354 | /* Answer the previous layout to cycle to. */ |
2a8854a7 | 355 | static enum tui_layout_type |
6ba8e26f | 356 | next_layout (void) |
c906108c | 357 | { |
570dc176 | 358 | int new_layout; |
c906108c | 359 | |
6ba8e26f AC |
360 | new_layout = tui_current_layout (); |
361 | if (new_layout == UNDEFINED_LAYOUT) | |
362 | new_layout = SRC_COMMAND; | |
c906108c SS |
363 | else |
364 | { | |
6ba8e26f AC |
365 | new_layout++; |
366 | if (new_layout == UNDEFINED_LAYOUT) | |
367 | new_layout = SRC_COMMAND; | |
c906108c SS |
368 | } |
369 | ||
570dc176 | 370 | return (enum tui_layout_type) new_layout; |
6ba8e26f | 371 | } |
c906108c SS |
372 | |
373 | ||
6ba8e26f | 374 | /* Answer the next layout to cycle to. */ |
2a8854a7 | 375 | static enum tui_layout_type |
6ba8e26f | 376 | prev_layout (void) |
c906108c | 377 | { |
570dc176 | 378 | int new_layout; |
c906108c | 379 | |
6ba8e26f AC |
380 | new_layout = tui_current_layout (); |
381 | if (new_layout == SRC_COMMAND) | |
382 | new_layout = DISASSEM_DATA_COMMAND; | |
c906108c SS |
383 | else |
384 | { | |
6ba8e26f AC |
385 | new_layout--; |
386 | if (new_layout == UNDEFINED_LAYOUT) | |
387 | new_layout = DISASSEM_DATA_COMMAND; | |
c906108c SS |
388 | } |
389 | ||
570dc176 | 390 | return (enum tui_layout_type) new_layout; |
6ba8e26f | 391 | } |
c906108c | 392 | |
d6ba6a11 | 393 | void |
ee556432 TT |
394 | tui_gen_win_info::resize (int height_, int width_, |
395 | int origin_x_, int origin_y_) | |
c906108c | 396 | { |
cdaa6eb4 | 397 | if (width == width_ && height == height_ |
fb3184d8 | 398 | && x == origin_x_ && y == origin_y_ |
cdaa6eb4 TT |
399 | && handle != nullptr) |
400 | return; | |
401 | ||
d6ba6a11 | 402 | width = width_; |
db502012 | 403 | height = height_; |
fb3184d8 TT |
404 | x = origin_x_; |
405 | y = origin_y_; | |
db502012 TT |
406 | |
407 | if (handle != nullptr) | |
408 | { | |
409 | #ifdef HAVE_WRESIZE | |
7523da63 | 410 | wresize (handle.get (), height, width); |
fb3184d8 | 411 | mvwin (handle.get (), y, x); |
7523da63 | 412 | wmove (handle.get (), 0, 0); |
db502012 | 413 | #else |
7523da63 | 414 | handle.reset (nullptr); |
db502012 TT |
415 | #endif |
416 | } | |
417 | ||
418 | if (handle == nullptr) | |
ab0e1f1a | 419 | make_window (); |
3df505f6 TT |
420 | |
421 | rerender (); | |
d6ba6a11 | 422 | } |
c906108c | 423 | |
d9fcefd5 TT |
424 | \f |
425 | ||
389e7ddb TT |
426 | /* Helper function that returns a TUI window, given its name. */ |
427 | ||
428 | static tui_gen_win_info * | |
429 | tui_get_window_by_name (const std::string &name) | |
430 | { | |
431 | if (name == "src") | |
432 | { | |
433 | if (tui_win_list[SRC_WIN] == nullptr) | |
434 | tui_win_list[SRC_WIN] = new tui_source_window (); | |
435 | return tui_win_list[SRC_WIN]; | |
436 | } | |
437 | else if (name == "cmd") | |
438 | { | |
439 | if (tui_win_list[CMD_WIN] == nullptr) | |
440 | tui_win_list[CMD_WIN] = new tui_cmd_window (); | |
441 | return tui_win_list[CMD_WIN]; | |
442 | } | |
443 | else if (name == "regs") | |
444 | { | |
445 | if (tui_win_list[DATA_WIN] == nullptr) | |
446 | tui_win_list[DATA_WIN] = new tui_data_window (); | |
447 | return tui_win_list[DATA_WIN]; | |
448 | } | |
449 | else if (name == "asm") | |
450 | { | |
451 | if (tui_win_list[DISASSEM_WIN] == nullptr) | |
452 | tui_win_list[DISASSEM_WIN] = new tui_disasm_window (); | |
453 | return tui_win_list[DISASSEM_WIN]; | |
454 | } | |
455 | else | |
456 | { | |
457 | gdb_assert (name == "locator"); | |
458 | return tui_locator_win_info_ptr (); | |
459 | } | |
460 | } | |
461 | ||
462 | /* See tui-layout.h. */ | |
463 | ||
464 | std::unique_ptr<tui_layout_base> | |
465 | tui_layout_window::clone () const | |
466 | { | |
467 | tui_layout_window *result = new tui_layout_window (m_contents.c_str ()); | |
468 | return std::unique_ptr<tui_layout_base> (result); | |
469 | } | |
470 | ||
471 | /* See tui-layout.h. */ | |
472 | ||
473 | void | |
474 | tui_layout_window::apply (int x_, int y_, int width_, int height_) | |
475 | { | |
476 | x = x_; | |
477 | y = y_; | |
478 | width = width_; | |
479 | height = height_; | |
480 | gdb_assert (m_window != nullptr); | |
481 | m_window->resize (height, width, x, y); | |
482 | } | |
483 | ||
484 | /* See tui-layout.h. */ | |
485 | ||
486 | void | |
487 | tui_layout_window::get_sizes (int *min_height, int *max_height) | |
488 | { | |
489 | if (m_window == nullptr) | |
490 | m_window = tui_get_window_by_name (m_contents); | |
491 | *min_height = m_window->min_height (); | |
492 | *max_height = m_window->max_height (); | |
493 | } | |
494 | ||
495 | /* See tui-layout.h. */ | |
496 | ||
497 | bool | |
498 | tui_layout_window::top_boxed_p () const | |
499 | { | |
500 | gdb_assert (m_window != nullptr); | |
501 | return m_window->can_box (); | |
502 | } | |
503 | ||
504 | /* See tui-layout.h. */ | |
505 | ||
506 | bool | |
507 | tui_layout_window::bottom_boxed_p () const | |
508 | { | |
509 | gdb_assert (m_window != nullptr); | |
510 | return m_window->can_box (); | |
511 | } | |
512 | ||
513 | /* See tui-layout.h. */ | |
514 | ||
515 | tui_layout_split * | |
516 | tui_layout_split::add_split (int weight) | |
517 | { | |
518 | tui_layout_split *result = new tui_layout_split (); | |
519 | split s = {weight, std::unique_ptr<tui_layout_base> (result)}; | |
520 | m_splits.push_back (std::move (s)); | |
521 | return result; | |
522 | } | |
523 | ||
524 | /* See tui-layout.h. */ | |
525 | ||
526 | void | |
527 | tui_layout_split::add_window (const char *name, int weight) | |
528 | { | |
529 | tui_layout_window *result = new tui_layout_window (name); | |
530 | split s = {weight, std::unique_ptr<tui_layout_base> (result)}; | |
531 | m_splits.push_back (std::move (s)); | |
532 | } | |
533 | ||
534 | /* See tui-layout.h. */ | |
535 | ||
536 | std::unique_ptr<tui_layout_base> | |
537 | tui_layout_split::clone () const | |
538 | { | |
539 | tui_layout_split *result = new tui_layout_split (); | |
540 | for (const split &item : m_splits) | |
541 | { | |
542 | std::unique_ptr<tui_layout_base> next = item.layout->clone (); | |
543 | split s = {item.weight, std::move (next)}; | |
544 | result->m_splits.push_back (std::move (s)); | |
545 | } | |
546 | return std::unique_ptr<tui_layout_base> (result); | |
547 | } | |
548 | ||
549 | /* See tui-layout.h. */ | |
550 | ||
551 | void | |
552 | tui_layout_split::get_sizes (int *min_height, int *max_height) | |
553 | { | |
554 | *min_height = 0; | |
555 | *max_height = 0; | |
556 | for (const split &item : m_splits) | |
557 | { | |
558 | int new_min, new_max; | |
559 | item.layout->get_sizes (&new_min, &new_max); | |
560 | *min_height += new_min; | |
561 | *max_height += new_max; | |
562 | } | |
563 | } | |
564 | ||
565 | /* See tui-layout.h. */ | |
566 | ||
567 | bool | |
568 | tui_layout_split::top_boxed_p () const | |
569 | { | |
570 | if (m_splits.empty ()) | |
571 | return false; | |
572 | return m_splits[0].layout->top_boxed_p (); | |
573 | } | |
574 | ||
575 | /* See tui-layout.h. */ | |
576 | ||
577 | bool | |
578 | tui_layout_split::bottom_boxed_p () const | |
579 | { | |
580 | if (m_splits.empty ()) | |
581 | return false; | |
582 | return m_splits.back ().layout->top_boxed_p (); | |
583 | } | |
584 | ||
585 | /* See tui-layout.h. */ | |
586 | ||
587 | void | |
588 | tui_layout_split::set_weights_from_heights () | |
589 | { | |
590 | for (int i = 0; i < m_splits.size (); ++i) | |
591 | m_splits[i].weight = m_splits[i].layout->height; | |
592 | } | |
593 | ||
594 | /* See tui-layout.h. */ | |
595 | ||
596 | bool | |
597 | tui_layout_split::adjust_size (const char *name, int new_height) | |
598 | { | |
599 | /* Look through the children. If one is a layout holding the named | |
600 | window, we're done; or if one actually is the named window, | |
601 | update it. */ | |
602 | int found_index = -1; | |
603 | for (int i = 0; i < m_splits.size (); ++i) | |
604 | { | |
605 | if (m_splits[i].layout->adjust_size (name, new_height)) | |
606 | return true; | |
607 | const char *win_name = m_splits[i].layout->get_name (); | |
608 | if (win_name != nullptr && strcmp (name, win_name) == 0) | |
609 | { | |
610 | found_index = i; | |
611 | break; | |
612 | } | |
613 | } | |
614 | ||
615 | if (found_index == -1) | |
616 | return false; | |
617 | if (m_splits[found_index].layout->height == new_height) | |
618 | return true; | |
619 | ||
620 | set_weights_from_heights (); | |
621 | int delta = m_splits[found_index].weight - new_height; | |
622 | m_splits[found_index].weight = new_height; | |
623 | ||
624 | /* Distribute the "delta" over the next window; but if the next | |
625 | window cannot hold it all, keep going until we either find a | |
626 | window that does, or until we loop all the way around. */ | |
627 | for (int i = 0; delta != 0 && i < m_splits.size () - 1; ++i) | |
628 | { | |
629 | int index = (found_index + 1 + i) % m_splits.size (); | |
630 | ||
631 | int new_min, new_max; | |
632 | m_splits[index].layout->get_sizes (&new_min, &new_max); | |
633 | ||
634 | if (delta < 0) | |
635 | { | |
636 | /* The primary window grew, so we are trying to shrink other | |
637 | windows. */ | |
638 | int available = m_splits[index].weight - new_min; | |
639 | int shrink_by = std::min (available, -delta); | |
640 | m_splits[index].weight -= shrink_by; | |
641 | delta += shrink_by; | |
642 | } | |
643 | else | |
644 | { | |
645 | /* The primary window shrank, so we are trying to grow other | |
646 | windows. */ | |
647 | int available = new_max - m_splits[index].weight; | |
648 | int grow_by = std::min (available, delta); | |
649 | m_splits[index].weight += grow_by; | |
650 | delta -= grow_by; | |
651 | } | |
652 | } | |
653 | ||
654 | if (delta != 0) | |
655 | { | |
656 | warning (_("Invalid window height specified")); | |
657 | /* Effectively undo any modifications made here. */ | |
658 | set_weights_from_heights (); | |
659 | } | |
660 | else | |
661 | { | |
662 | /* Simply re-apply the updated layout. */ | |
663 | apply (x, y, width, height); | |
664 | } | |
665 | ||
666 | return true; | |
667 | } | |
668 | ||
669 | /* See tui-layout.h. */ | |
670 | ||
671 | void | |
672 | tui_layout_split::apply (int x_, int y_, int width_, int height_) | |
673 | { | |
674 | x = x_; | |
675 | y = y_; | |
676 | width = width_; | |
677 | height = height_; | |
678 | ||
679 | struct height_info | |
680 | { | |
681 | int height; | |
682 | int min_height; | |
683 | int max_height; | |
684 | /* True if this window will share a box border with the previous | |
685 | window in the list. */ | |
686 | bool share_box; | |
687 | }; | |
688 | ||
689 | std::vector<height_info> info (m_splits.size ()); | |
690 | ||
691 | /* Step 1: Find the min and max height of each sub-layout. | |
692 | Fixed-sized layouts are given their desired height, and then the | |
693 | remaining space is distributed among the remaining windows | |
694 | according to the weights given. */ | |
695 | int available_height = height; | |
696 | int last_index = -1; | |
697 | int total_weight = 0; | |
698 | for (int i = 0; i < m_splits.size (); ++i) | |
699 | { | |
700 | bool cmd_win_already_exists = TUI_CMD_WIN != nullptr; | |
701 | ||
702 | /* Always call get_sizes, to ensure that the window is | |
703 | instantiated. This is a bit gross but less gross than adding | |
704 | special cases for this in other places. */ | |
705 | m_splits[i].layout->get_sizes (&info[i].min_height, &info[i].max_height); | |
706 | ||
707 | if (!m_applied | |
708 | && cmd_win_already_exists | |
709 | && m_splits[i].layout->get_name () != nullptr | |
710 | && strcmp (m_splits[i].layout->get_name (), "cmd") == 0) | |
711 | { | |
712 | /* If this layout has never been applied, then it means the | |
713 | user just changed the layout. In this situation, it's | |
714 | desirable to keep the size of the command window the | |
715 | same. Setting the min and max heights this way ensures | |
716 | that the resizing step, below, does the right thing with | |
717 | this window. */ | |
718 | info[i].min_height = TUI_CMD_WIN->height; | |
719 | info[i].max_height = TUI_CMD_WIN->height; | |
720 | } | |
721 | ||
722 | if (info[i].min_height == info[i].max_height) | |
723 | available_height -= info[i].min_height; | |
724 | else | |
725 | { | |
726 | last_index = i; | |
727 | total_weight += m_splits[i].weight; | |
728 | } | |
729 | ||
730 | /* Two adjacent boxed windows will share a border, making a bit | |
731 | more height available. */ | |
732 | if (i > 0 | |
733 | && m_splits[i - 1].layout->bottom_boxed_p () | |
734 | && m_splits[i].layout->top_boxed_p ()) | |
735 | info[i].share_box = true; | |
736 | } | |
737 | ||
738 | /* Step 2: Compute the height of each sub-layout. Fixed-sized items | |
739 | are given their fixed size, while others are resized according to | |
740 | their weight. */ | |
741 | int used_height = 0; | |
742 | for (int i = 0; i < m_splits.size (); ++i) | |
743 | { | |
744 | /* Compute the height and clamp to the allowable range. */ | |
745 | info[i].height = available_height * m_splits[i].weight / total_weight; | |
746 | if (info[i].height > info[i].max_height) | |
747 | info[i].height = info[i].max_height; | |
748 | if (info[i].height < info[i].min_height) | |
749 | info[i].height = info[i].min_height; | |
750 | /* If there is any leftover height, just redistribute it to the | |
751 | last resizeable window, by dropping it from the allocated | |
752 | height. We could try to be fancier here perhaps, by | |
753 | redistributing this height among all windows, not just the | |
754 | last window. */ | |
755 | if (info[i].min_height != info[i].max_height) | |
756 | { | |
757 | used_height += info[i].height; | |
758 | if (info[i].share_box) | |
759 | --used_height; | |
760 | } | |
761 | } | |
762 | ||
763 | /* Allocate any leftover height. */ | |
764 | if (available_height >= used_height && last_index != -1) | |
765 | info[last_index].height += available_height - used_height; | |
766 | ||
767 | /* Step 3: Resize. */ | |
768 | int height_accum = 0; | |
769 | for (int i = 0; i < m_splits.size (); ++i) | |
770 | { | |
771 | /* If we fall off the bottom, just make allocations overlap. | |
772 | GIGO. */ | |
773 | if (height_accum + info[i].height > height) | |
774 | height_accum = height - info[i].height; | |
775 | else if (info[i].share_box) | |
776 | --height_accum; | |
777 | m_splits[i].layout->apply (x, y + height_accum, width, info[i].height); | |
778 | height_accum += info[i].height; | |
779 | } | |
780 | ||
781 | m_applied = true; | |
782 | } | |
783 | ||
5afe342e TT |
784 | /* See tui-layout.h. */ |
785 | ||
786 | void | |
787 | tui_layout_split::remove_windows (const char *name) | |
788 | { | |
789 | for (int i = 0; i < m_splits.size (); ++i) | |
790 | { | |
791 | const char *this_name = m_splits[i].layout->get_name (); | |
792 | if (this_name == nullptr) | |
793 | m_splits[i].layout->remove_windows (name); | |
794 | else | |
795 | { | |
796 | if (strcmp (this_name, name) == 0 | |
797 | || strcmp (this_name, "cmd") == 0) | |
798 | { | |
799 | /* Keep. */ | |
800 | } | |
801 | m_splits.erase (m_splits.begin () + i); | |
802 | --i; | |
803 | } | |
804 | } | |
805 | } | |
806 | ||
2192a9d3 TT |
807 | static void |
808 | initialize_layouts () | |
809 | { | |
810 | standard_layouts[SRC_COMMAND] = new tui_layout_split (); | |
811 | standard_layouts[SRC_COMMAND]->add_window ("src", 2); | |
812 | standard_layouts[SRC_COMMAND]->add_window ("locator", 0); | |
813 | standard_layouts[SRC_COMMAND]->add_window ("cmd", 1); | |
814 | ||
815 | standard_layouts[DISASSEM_COMMAND] = new tui_layout_split (); | |
816 | standard_layouts[DISASSEM_COMMAND]->add_window ("asm", 2); | |
817 | standard_layouts[DISASSEM_COMMAND]->add_window ("locator", 0); | |
818 | standard_layouts[DISASSEM_COMMAND]->add_window ("cmd", 1); | |
819 | ||
820 | standard_layouts[SRC_DATA_COMMAND] = new tui_layout_split (); | |
821 | standard_layouts[SRC_DATA_COMMAND]->add_window ("regs", 1); | |
822 | standard_layouts[SRC_DATA_COMMAND]->add_window ("src", 1); | |
823 | standard_layouts[SRC_DATA_COMMAND]->add_window ("locator", 0); | |
824 | standard_layouts[SRC_DATA_COMMAND]->add_window ("cmd", 1); | |
825 | ||
826 | standard_layouts[DISASSEM_DATA_COMMAND] = new tui_layout_split (); | |
827 | standard_layouts[DISASSEM_DATA_COMMAND]->add_window ("regs", 1); | |
828 | standard_layouts[DISASSEM_DATA_COMMAND]->add_window ("asm", 1); | |
829 | standard_layouts[DISASSEM_DATA_COMMAND]->add_window ("locator", 0); | |
830 | standard_layouts[DISASSEM_DATA_COMMAND]->add_window ("cmd", 1); | |
831 | ||
832 | standard_layouts[SRC_DISASSEM_COMMAND] = new tui_layout_split (); | |
833 | standard_layouts[SRC_DISASSEM_COMMAND]->add_window ("src", 1); | |
834 | standard_layouts[SRC_DISASSEM_COMMAND]->add_window ("asm", 1); | |
835 | standard_layouts[SRC_DISASSEM_COMMAND]->add_window ("locator", 0); | |
836 | standard_layouts[SRC_DISASSEM_COMMAND]->add_window ("cmd", 1); | |
837 | } | |
838 | ||
389e7ddb TT |
839 | \f |
840 | ||
d9fcefd5 TT |
841 | /* Function to initialize gdb commands, for tui window layout |
842 | manipulation. */ | |
843 | ||
6c265988 | 844 | void _initialize_tui_layout (); |
d9fcefd5 | 845 | void |
6c265988 | 846 | _initialize_tui_layout () |
d9fcefd5 TT |
847 | { |
848 | struct cmd_list_element *cmd; | |
849 | ||
850 | cmd = add_com ("layout", class_tui, tui_layout_command, _("\ | |
851 | Change the layout of windows.\n\ | |
852 | Usage: layout prev | next | LAYOUT-NAME\n\ | |
853 | Layout names are:\n\ | |
854 | src : Displays source and command windows.\n\ | |
855 | asm : Displays disassembly and command windows.\n\ | |
856 | split : Displays source, disassembly and command windows.\n\ | |
857 | regs : Displays register window. If existing layout\n\ | |
858 | is source/command or assembly/command, the \n\ | |
859 | register window is displayed. If the\n\ | |
860 | source/assembly/command (split) is displayed, \n\ | |
861 | the register window is displayed with \n\ | |
862 | the window that has current logical focus.")); | |
863 | set_cmd_completer (cmd, layout_completer); | |
2192a9d3 TT |
864 | |
865 | initialize_layouts (); | |
d9fcefd5 | 866 | } |