xref: /src/contrib/llvm-project/lldb/source/Core/IOHandlerCursesGUI.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1cfca06d7SDimitry Andric //===-- IOHandlerCursesGUI.cpp --------------------------------------------===//
2706b4fc4SDimitry Andric //
3706b4fc4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4706b4fc4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5706b4fc4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6706b4fc4SDimitry Andric //
7706b4fc4SDimitry Andric //===----------------------------------------------------------------------===//
8706b4fc4SDimitry Andric 
9706b4fc4SDimitry Andric #include "lldb/Core/IOHandlerCursesGUI.h"
10706b4fc4SDimitry Andric #include "lldb/Host/Config.h"
11706b4fc4SDimitry Andric 
12706b4fc4SDimitry Andric #if LLDB_ENABLE_CURSES
13b60736ecSDimitry Andric #if CURSES_HAVE_NCURSES_CURSES_H
14b60736ecSDimitry Andric #include <ncurses/curses.h>
15b60736ecSDimitry Andric #include <ncurses/panel.h>
16b60736ecSDimitry Andric #else
17706b4fc4SDimitry Andric #include <curses.h>
18706b4fc4SDimitry Andric #include <panel.h>
19706b4fc4SDimitry Andric #endif
20b60736ecSDimitry Andric #endif
21706b4fc4SDimitry Andric 
22706b4fc4SDimitry Andric #if defined(__APPLE__)
23706b4fc4SDimitry Andric #include <deque>
24706b4fc4SDimitry Andric #endif
25706b4fc4SDimitry Andric #include <string>
26706b4fc4SDimitry Andric 
27706b4fc4SDimitry Andric #include "lldb/Core/Debugger.h"
28344a3780SDimitry Andric #include "lldb/Core/ValueObjectUpdater.h"
29706b4fc4SDimitry Andric #include "lldb/Host/File.h"
30145449b1SDimitry Andric #include "lldb/Utility/AnsiTerminal.h"
31706b4fc4SDimitry Andric #include "lldb/Utility/Predicate.h"
32706b4fc4SDimitry Andric #include "lldb/Utility/Status.h"
33706b4fc4SDimitry Andric #include "lldb/Utility/StreamString.h"
34706b4fc4SDimitry Andric #include "lldb/Utility/StringList.h"
35706b4fc4SDimitry Andric #include "lldb/lldb-forward.h"
36706b4fc4SDimitry Andric 
37706b4fc4SDimitry Andric #include "lldb/Interpreter/CommandCompletions.h"
38706b4fc4SDimitry Andric #include "lldb/Interpreter/CommandInterpreter.h"
39c0981da4SDimitry Andric #include "lldb/Interpreter/OptionGroupPlatform.h"
40706b4fc4SDimitry Andric 
41706b4fc4SDimitry Andric #if LLDB_ENABLE_CURSES
42706b4fc4SDimitry Andric #include "lldb/Breakpoint/BreakpointLocation.h"
43706b4fc4SDimitry Andric #include "lldb/Core/Module.h"
44344a3780SDimitry Andric #include "lldb/Core/PluginManager.h"
45706b4fc4SDimitry Andric #include "lldb/Core/ValueObject.h"
46706b4fc4SDimitry Andric #include "lldb/Core/ValueObjectRegister.h"
47706b4fc4SDimitry Andric #include "lldb/Symbol/Block.h"
48c0981da4SDimitry Andric #include "lldb/Symbol/CompileUnit.h"
49706b4fc4SDimitry Andric #include "lldb/Symbol/Function.h"
50706b4fc4SDimitry Andric #include "lldb/Symbol/Symbol.h"
51706b4fc4SDimitry Andric #include "lldb/Symbol/VariableList.h"
52706b4fc4SDimitry Andric #include "lldb/Target/Process.h"
53706b4fc4SDimitry Andric #include "lldb/Target/RegisterContext.h"
54706b4fc4SDimitry Andric #include "lldb/Target/StackFrame.h"
55706b4fc4SDimitry Andric #include "lldb/Target/StopInfo.h"
56706b4fc4SDimitry Andric #include "lldb/Target/Target.h"
57706b4fc4SDimitry Andric #include "lldb/Target/Thread.h"
58706b4fc4SDimitry Andric #include "lldb/Utility/State.h"
59706b4fc4SDimitry Andric #endif
60706b4fc4SDimitry Andric 
61706b4fc4SDimitry Andric #include "llvm/ADT/StringRef.h"
62706b4fc4SDimitry Andric 
63706b4fc4SDimitry Andric #ifdef _WIN32
64706b4fc4SDimitry Andric #include "lldb/Host/windows/windows.h"
65706b4fc4SDimitry Andric #endif
66706b4fc4SDimitry Andric 
67706b4fc4SDimitry Andric #include <memory>
68706b4fc4SDimitry Andric #include <mutex>
69706b4fc4SDimitry Andric 
70344a3780SDimitry Andric #include <cassert>
71344a3780SDimitry Andric #include <cctype>
72344a3780SDimitry Andric #include <cerrno>
73344a3780SDimitry Andric #include <cstdint>
74344a3780SDimitry Andric #include <cstdio>
75344a3780SDimitry Andric #include <cstring>
76344a3780SDimitry Andric #include <functional>
77e3b55780SDimitry Andric #include <optional>
78706b4fc4SDimitry Andric #include <type_traits>
79706b4fc4SDimitry Andric 
80706b4fc4SDimitry Andric using namespace lldb;
81706b4fc4SDimitry Andric using namespace lldb_private;
82706b4fc4SDimitry Andric using llvm::StringRef;
83706b4fc4SDimitry Andric 
84706b4fc4SDimitry Andric // we may want curses to be disabled for some builds for instance, windows
85706b4fc4SDimitry Andric #if LLDB_ENABLE_CURSES
86706b4fc4SDimitry Andric 
87c0981da4SDimitry Andric #define KEY_CTRL_A 1
88c0981da4SDimitry Andric #define KEY_CTRL_E 5
89c0981da4SDimitry Andric #define KEY_CTRL_K 11
90706b4fc4SDimitry Andric #define KEY_RETURN 10
91706b4fc4SDimitry Andric #define KEY_ESCAPE 27
92c0981da4SDimitry Andric #define KEY_DELETE 127
93706b4fc4SDimitry Andric 
94344a3780SDimitry Andric #define KEY_SHIFT_TAB (KEY_MAX + 1)
95c0981da4SDimitry Andric #define KEY_ALT_ENTER (KEY_MAX + 2)
96344a3780SDimitry Andric 
97706b4fc4SDimitry Andric namespace curses {
98706b4fc4SDimitry Andric class Menu;
99706b4fc4SDimitry Andric class MenuDelegate;
100706b4fc4SDimitry Andric class Window;
101706b4fc4SDimitry Andric class WindowDelegate;
102706b4fc4SDimitry Andric typedef std::shared_ptr<Menu> MenuSP;
103706b4fc4SDimitry Andric typedef std::shared_ptr<MenuDelegate> MenuDelegateSP;
104706b4fc4SDimitry Andric typedef std::shared_ptr<Window> WindowSP;
105706b4fc4SDimitry Andric typedef std::shared_ptr<WindowDelegate> WindowDelegateSP;
106706b4fc4SDimitry Andric typedef std::vector<MenuSP> Menus;
107706b4fc4SDimitry Andric typedef std::vector<WindowSP> Windows;
108706b4fc4SDimitry Andric typedef std::vector<WindowDelegateSP> WindowDelegates;
109706b4fc4SDimitry Andric 
110706b4fc4SDimitry Andric #if 0
111706b4fc4SDimitry Andric type summary add -s "x=${var.x}, y=${var.y}" curses::Point
112706b4fc4SDimitry Andric type summary add -s "w=${var.width}, h=${var.height}" curses::Size
113706b4fc4SDimitry Andric type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect
114706b4fc4SDimitry Andric #endif
115706b4fc4SDimitry Andric 
116706b4fc4SDimitry Andric struct Point {
117706b4fc4SDimitry Andric   int x;
118706b4fc4SDimitry Andric   int y;
119706b4fc4SDimitry Andric 
Pointcurses::Point120706b4fc4SDimitry Andric   Point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
121706b4fc4SDimitry Andric 
Clearcurses::Point122706b4fc4SDimitry Andric   void Clear() {
123706b4fc4SDimitry Andric     x = 0;
124706b4fc4SDimitry Andric     y = 0;
125706b4fc4SDimitry Andric   }
126706b4fc4SDimitry Andric 
operator +=curses::Point127706b4fc4SDimitry Andric   Point &operator+=(const Point &rhs) {
128706b4fc4SDimitry Andric     x += rhs.x;
129706b4fc4SDimitry Andric     y += rhs.y;
130706b4fc4SDimitry Andric     return *this;
131706b4fc4SDimitry Andric   }
132706b4fc4SDimitry Andric 
Dumpcurses::Point133706b4fc4SDimitry Andric   void Dump() { printf("(x=%i, y=%i)\n", x, y); }
134706b4fc4SDimitry Andric };
135706b4fc4SDimitry Andric 
operator ==(const Point & lhs,const Point & rhs)136706b4fc4SDimitry Andric bool operator==(const Point &lhs, const Point &rhs) {
137706b4fc4SDimitry Andric   return lhs.x == rhs.x && lhs.y == rhs.y;
138706b4fc4SDimitry Andric }
139706b4fc4SDimitry Andric 
operator !=(const Point & lhs,const Point & rhs)140706b4fc4SDimitry Andric bool operator!=(const Point &lhs, const Point &rhs) {
141706b4fc4SDimitry Andric   return lhs.x != rhs.x || lhs.y != rhs.y;
142706b4fc4SDimitry Andric }
143706b4fc4SDimitry Andric 
144706b4fc4SDimitry Andric struct Size {
145706b4fc4SDimitry Andric   int width;
146706b4fc4SDimitry Andric   int height;
Sizecurses::Size147706b4fc4SDimitry Andric   Size(int w = 0, int h = 0) : width(w), height(h) {}
148706b4fc4SDimitry Andric 
Clearcurses::Size149706b4fc4SDimitry Andric   void Clear() {
150706b4fc4SDimitry Andric     width = 0;
151706b4fc4SDimitry Andric     height = 0;
152706b4fc4SDimitry Andric   }
153706b4fc4SDimitry Andric 
Dumpcurses::Size154706b4fc4SDimitry Andric   void Dump() { printf("(w=%i, h=%i)\n", width, height); }
155706b4fc4SDimitry Andric };
156706b4fc4SDimitry Andric 
operator ==(const Size & lhs,const Size & rhs)157706b4fc4SDimitry Andric bool operator==(const Size &lhs, const Size &rhs) {
158706b4fc4SDimitry Andric   return lhs.width == rhs.width && lhs.height == rhs.height;
159706b4fc4SDimitry Andric }
160706b4fc4SDimitry Andric 
operator !=(const Size & lhs,const Size & rhs)161706b4fc4SDimitry Andric bool operator!=(const Size &lhs, const Size &rhs) {
162706b4fc4SDimitry Andric   return lhs.width != rhs.width || lhs.height != rhs.height;
163706b4fc4SDimitry Andric }
164706b4fc4SDimitry Andric 
165706b4fc4SDimitry Andric struct Rect {
166706b4fc4SDimitry Andric   Point origin;
167706b4fc4SDimitry Andric   Size size;
168706b4fc4SDimitry Andric 
Rectcurses::Rect169706b4fc4SDimitry Andric   Rect() : origin(), size() {}
170706b4fc4SDimitry Andric 
Rectcurses::Rect171706b4fc4SDimitry Andric   Rect(const Point &p, const Size &s) : origin(p), size(s) {}
172706b4fc4SDimitry Andric 
Clearcurses::Rect173706b4fc4SDimitry Andric   void Clear() {
174706b4fc4SDimitry Andric     origin.Clear();
175706b4fc4SDimitry Andric     size.Clear();
176706b4fc4SDimitry Andric   }
177706b4fc4SDimitry Andric 
Dumpcurses::Rect178706b4fc4SDimitry Andric   void Dump() {
179706b4fc4SDimitry Andric     printf("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width,
180706b4fc4SDimitry Andric            size.height);
181706b4fc4SDimitry Andric   }
182706b4fc4SDimitry Andric 
Insetcurses::Rect183706b4fc4SDimitry Andric   void Inset(int w, int h) {
184706b4fc4SDimitry Andric     if (size.width > w * 2)
185706b4fc4SDimitry Andric       size.width -= w * 2;
186706b4fc4SDimitry Andric     origin.x += w;
187706b4fc4SDimitry Andric 
188706b4fc4SDimitry Andric     if (size.height > h * 2)
189706b4fc4SDimitry Andric       size.height -= h * 2;
190706b4fc4SDimitry Andric     origin.y += h;
191706b4fc4SDimitry Andric   }
192706b4fc4SDimitry Andric 
193706b4fc4SDimitry Andric   // Return a status bar rectangle which is the last line of this rectangle.
194706b4fc4SDimitry Andric   // This rectangle will be modified to not include the status bar area.
MakeStatusBarcurses::Rect195706b4fc4SDimitry Andric   Rect MakeStatusBar() {
196706b4fc4SDimitry Andric     Rect status_bar;
197706b4fc4SDimitry Andric     if (size.height > 1) {
198706b4fc4SDimitry Andric       status_bar.origin.x = origin.x;
199706b4fc4SDimitry Andric       status_bar.origin.y = size.height;
200706b4fc4SDimitry Andric       status_bar.size.width = size.width;
201706b4fc4SDimitry Andric       status_bar.size.height = 1;
202706b4fc4SDimitry Andric       --size.height;
203706b4fc4SDimitry Andric     }
204706b4fc4SDimitry Andric     return status_bar;
205706b4fc4SDimitry Andric   }
206706b4fc4SDimitry Andric 
207706b4fc4SDimitry Andric   // Return a menubar rectangle which is the first line of this rectangle. This
208706b4fc4SDimitry Andric   // rectangle will be modified to not include the menubar area.
MakeMenuBarcurses::Rect209706b4fc4SDimitry Andric   Rect MakeMenuBar() {
210706b4fc4SDimitry Andric     Rect menubar;
211706b4fc4SDimitry Andric     if (size.height > 1) {
212706b4fc4SDimitry Andric       menubar.origin.x = origin.x;
213706b4fc4SDimitry Andric       menubar.origin.y = origin.y;
214706b4fc4SDimitry Andric       menubar.size.width = size.width;
215706b4fc4SDimitry Andric       menubar.size.height = 1;
216706b4fc4SDimitry Andric       ++origin.y;
217706b4fc4SDimitry Andric       --size.height;
218706b4fc4SDimitry Andric     }
219706b4fc4SDimitry Andric     return menubar;
220706b4fc4SDimitry Andric   }
221706b4fc4SDimitry Andric 
HorizontalSplitPercentagecurses::Rect222706b4fc4SDimitry Andric   void HorizontalSplitPercentage(float top_percentage, Rect &top,
223706b4fc4SDimitry Andric                                  Rect &bottom) const {
224706b4fc4SDimitry Andric     float top_height = top_percentage * size.height;
225706b4fc4SDimitry Andric     HorizontalSplit(top_height, top, bottom);
226706b4fc4SDimitry Andric   }
227706b4fc4SDimitry Andric 
HorizontalSplitcurses::Rect228706b4fc4SDimitry Andric   void HorizontalSplit(int top_height, Rect &top, Rect &bottom) const {
229706b4fc4SDimitry Andric     top = *this;
230706b4fc4SDimitry Andric     if (top_height < size.height) {
231706b4fc4SDimitry Andric       top.size.height = top_height;
232706b4fc4SDimitry Andric       bottom.origin.x = origin.x;
233706b4fc4SDimitry Andric       bottom.origin.y = origin.y + top.size.height;
234706b4fc4SDimitry Andric       bottom.size.width = size.width;
235706b4fc4SDimitry Andric       bottom.size.height = size.height - top.size.height;
236706b4fc4SDimitry Andric     } else {
237706b4fc4SDimitry Andric       bottom.Clear();
238706b4fc4SDimitry Andric     }
239706b4fc4SDimitry Andric   }
240706b4fc4SDimitry Andric 
VerticalSplitPercentagecurses::Rect241706b4fc4SDimitry Andric   void VerticalSplitPercentage(float left_percentage, Rect &left,
242706b4fc4SDimitry Andric                                Rect &right) const {
243706b4fc4SDimitry Andric     float left_width = left_percentage * size.width;
244706b4fc4SDimitry Andric     VerticalSplit(left_width, left, right);
245706b4fc4SDimitry Andric   }
246706b4fc4SDimitry Andric 
VerticalSplitcurses::Rect247706b4fc4SDimitry Andric   void VerticalSplit(int left_width, Rect &left, Rect &right) const {
248706b4fc4SDimitry Andric     left = *this;
249706b4fc4SDimitry Andric     if (left_width < size.width) {
250706b4fc4SDimitry Andric       left.size.width = left_width;
251706b4fc4SDimitry Andric       right.origin.x = origin.x + left.size.width;
252706b4fc4SDimitry Andric       right.origin.y = origin.y;
253706b4fc4SDimitry Andric       right.size.width = size.width - left.size.width;
254706b4fc4SDimitry Andric       right.size.height = size.height;
255706b4fc4SDimitry Andric     } else {
256706b4fc4SDimitry Andric       right.Clear();
257706b4fc4SDimitry Andric     }
258706b4fc4SDimitry Andric   }
259706b4fc4SDimitry Andric };
260706b4fc4SDimitry Andric 
operator ==(const Rect & lhs,const Rect & rhs)261706b4fc4SDimitry Andric bool operator==(const Rect &lhs, const Rect &rhs) {
262706b4fc4SDimitry Andric   return lhs.origin == rhs.origin && lhs.size == rhs.size;
263706b4fc4SDimitry Andric }
264706b4fc4SDimitry Andric 
operator !=(const Rect & lhs,const Rect & rhs)265706b4fc4SDimitry Andric bool operator!=(const Rect &lhs, const Rect &rhs) {
266706b4fc4SDimitry Andric   return lhs.origin != rhs.origin || lhs.size != rhs.size;
267706b4fc4SDimitry Andric }
268706b4fc4SDimitry Andric 
269706b4fc4SDimitry Andric enum HandleCharResult {
270706b4fc4SDimitry Andric   eKeyNotHandled = 0,
271706b4fc4SDimitry Andric   eKeyHandled = 1,
272706b4fc4SDimitry Andric   eQuitApplication = 2
273706b4fc4SDimitry Andric };
274706b4fc4SDimitry Andric 
275706b4fc4SDimitry Andric enum class MenuActionResult {
276706b4fc4SDimitry Andric   Handled,
277706b4fc4SDimitry Andric   NotHandled,
278706b4fc4SDimitry Andric   Quit // Exit all menus and quit
279706b4fc4SDimitry Andric };
280706b4fc4SDimitry Andric 
281706b4fc4SDimitry Andric struct KeyHelp {
282706b4fc4SDimitry Andric   int ch;
283706b4fc4SDimitry Andric   const char *description;
284706b4fc4SDimitry Andric };
285706b4fc4SDimitry Andric 
286b60736ecSDimitry Andric // COLOR_PAIR index names
287b60736ecSDimitry Andric enum {
288b60736ecSDimitry Andric   // First 16 colors are 8 black background and 8 blue background colors,
289b60736ecSDimitry Andric   // needed by OutputColoredStringTruncated().
290b60736ecSDimitry Andric   BlackOnBlack = 1,
291b60736ecSDimitry Andric   RedOnBlack,
292b60736ecSDimitry Andric   GreenOnBlack,
293b60736ecSDimitry Andric   YellowOnBlack,
294b60736ecSDimitry Andric   BlueOnBlack,
295b60736ecSDimitry Andric   MagentaOnBlack,
296b60736ecSDimitry Andric   CyanOnBlack,
297b60736ecSDimitry Andric   WhiteOnBlack,
298b60736ecSDimitry Andric   BlackOnBlue,
299b60736ecSDimitry Andric   RedOnBlue,
300b60736ecSDimitry Andric   GreenOnBlue,
301b60736ecSDimitry Andric   YellowOnBlue,
302b60736ecSDimitry Andric   BlueOnBlue,
303b60736ecSDimitry Andric   MagentaOnBlue,
304b60736ecSDimitry Andric   CyanOnBlue,
305b60736ecSDimitry Andric   WhiteOnBlue,
306b60736ecSDimitry Andric   // Other colors, as needed.
307b60736ecSDimitry Andric   BlackOnWhite,
308b60736ecSDimitry Andric   MagentaOnWhite,
309b60736ecSDimitry Andric   LastColorPairIndex = MagentaOnWhite
310b60736ecSDimitry Andric };
311b60736ecSDimitry Andric 
312706b4fc4SDimitry Andric class WindowDelegate {
313706b4fc4SDimitry Andric public:
314706b4fc4SDimitry Andric   virtual ~WindowDelegate() = default;
315706b4fc4SDimitry Andric 
WindowDelegateDraw(Window & window,bool force)316706b4fc4SDimitry Andric   virtual bool WindowDelegateDraw(Window &window, bool force) {
317706b4fc4SDimitry Andric     return false; // Drawing not handled
318706b4fc4SDimitry Andric   }
319706b4fc4SDimitry Andric 
WindowDelegateHandleChar(Window & window,int key)320706b4fc4SDimitry Andric   virtual HandleCharResult WindowDelegateHandleChar(Window &window, int key) {
321706b4fc4SDimitry Andric     return eKeyNotHandled;
322706b4fc4SDimitry Andric   }
323706b4fc4SDimitry Andric 
WindowDelegateGetHelpText()324706b4fc4SDimitry Andric   virtual const char *WindowDelegateGetHelpText() { return nullptr; }
325706b4fc4SDimitry Andric 
WindowDelegateGetKeyHelp()326706b4fc4SDimitry Andric   virtual KeyHelp *WindowDelegateGetKeyHelp() { return nullptr; }
327706b4fc4SDimitry Andric };
328706b4fc4SDimitry Andric 
329706b4fc4SDimitry Andric class HelpDialogDelegate : public WindowDelegate {
330706b4fc4SDimitry Andric public:
331706b4fc4SDimitry Andric   HelpDialogDelegate(const char *text, KeyHelp *key_help_array);
332706b4fc4SDimitry Andric 
333706b4fc4SDimitry Andric   ~HelpDialogDelegate() override;
334706b4fc4SDimitry Andric 
335706b4fc4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override;
336706b4fc4SDimitry Andric 
337706b4fc4SDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
338706b4fc4SDimitry Andric 
GetNumLines() const339706b4fc4SDimitry Andric   size_t GetNumLines() const { return m_text.GetSize(); }
340706b4fc4SDimitry Andric 
GetMaxLineLength() const341706b4fc4SDimitry Andric   size_t GetMaxLineLength() const { return m_text.GetMaxStringLength(); }
342706b4fc4SDimitry Andric 
343706b4fc4SDimitry Andric protected:
344706b4fc4SDimitry Andric   StringList m_text;
345145449b1SDimitry Andric   int m_first_visible_line = 0;
346706b4fc4SDimitry Andric };
347706b4fc4SDimitry Andric 
348344a3780SDimitry Andric // A surface is an abstraction for something than can be drawn on. The surface
349344a3780SDimitry Andric // have a width, a height, a cursor position, and a multitude of drawing
350344a3780SDimitry Andric // operations. This type should be sub-classed to get an actually useful ncurses
351c0981da4SDimitry Andric // object, such as a Window or a Pad.
352344a3780SDimitry Andric class Surface {
353706b4fc4SDimitry Andric public:
354c0981da4SDimitry Andric   enum class Type { Window, Pad };
355c0981da4SDimitry Andric 
Surface(Surface::Type type)356145449b1SDimitry Andric   Surface(Surface::Type type) : m_type(type) {}
357706b4fc4SDimitry Andric 
get()358344a3780SDimitry Andric   WINDOW *get() { return m_window; }
359344a3780SDimitry Andric 
operator WINDOW*()360344a3780SDimitry Andric   operator WINDOW *() { return m_window; }
361344a3780SDimitry Andric 
SubSurface(Rect bounds)362c0981da4SDimitry Andric   Surface SubSurface(Rect bounds) {
363c0981da4SDimitry Andric     Surface subSurface(m_type);
364c0981da4SDimitry Andric     if (m_type == Type::Pad)
365c0981da4SDimitry Andric       subSurface.m_window =
366c0981da4SDimitry Andric           ::subpad(m_window, bounds.size.height, bounds.size.width,
367c0981da4SDimitry Andric                    bounds.origin.y, bounds.origin.x);
368c0981da4SDimitry Andric     else
369c0981da4SDimitry Andric       subSurface.m_window =
370c0981da4SDimitry Andric           ::derwin(m_window, bounds.size.height, bounds.size.width,
371c0981da4SDimitry Andric                    bounds.origin.y, bounds.origin.x);
372c0981da4SDimitry Andric     return subSurface;
373c0981da4SDimitry Andric   }
374c0981da4SDimitry Andric 
375344a3780SDimitry Andric   // Copy a region of the surface to another surface.
CopyToSurface(Surface & target,Point source_origin,Point target_origin,Size size)376344a3780SDimitry Andric   void CopyToSurface(Surface &target, Point source_origin, Point target_origin,
377344a3780SDimitry Andric                      Size size) {
378344a3780SDimitry Andric     ::copywin(m_window, target.get(), source_origin.y, source_origin.x,
379344a3780SDimitry Andric               target_origin.y, target_origin.x,
380344a3780SDimitry Andric               target_origin.y + size.height - 1,
381344a3780SDimitry Andric               target_origin.x + size.width - 1, false);
382706b4fc4SDimitry Andric   }
383706b4fc4SDimitry Andric 
GetCursorX() const384344a3780SDimitry Andric   int GetCursorX() const { return getcurx(m_window); }
GetCursorY() const385344a3780SDimitry Andric   int GetCursorY() const { return getcury(m_window); }
MoveCursor(int x,int y)386344a3780SDimitry Andric   void MoveCursor(int x, int y) { ::wmove(m_window, y, x); }
387706b4fc4SDimitry Andric 
AttributeOn(attr_t attr)388706b4fc4SDimitry Andric   void AttributeOn(attr_t attr) { ::wattron(m_window, attr); }
AttributeOff(attr_t attr)389706b4fc4SDimitry Andric   void AttributeOff(attr_t attr) { ::wattroff(m_window, attr); }
390344a3780SDimitry Andric 
GetMaxX() const391b60736ecSDimitry Andric   int GetMaxX() const { return getmaxx(m_window); }
GetMaxY() const392b60736ecSDimitry Andric   int GetMaxY() const { return getmaxy(m_window); }
GetWidth() const393b60736ecSDimitry Andric   int GetWidth() const { return GetMaxX(); }
GetHeight() const394b60736ecSDimitry Andric   int GetHeight() const { return GetMaxY(); }
GetSize() const395344a3780SDimitry Andric   Size GetSize() const { return Size(GetWidth(), GetHeight()); }
396344a3780SDimitry Andric   // Get a zero origin rectangle width the surface size.
GetFrame() const397344a3780SDimitry Andric   Rect GetFrame() const { return Rect(Point(), GetSize()); }
398344a3780SDimitry Andric 
Clear()399344a3780SDimitry Andric   void Clear() { ::wclear(m_window); }
Erase()400344a3780SDimitry Andric   void Erase() { ::werase(m_window); }
401344a3780SDimitry Andric 
SetBackground(int color_pair_idx)402706b4fc4SDimitry Andric   void SetBackground(int color_pair_idx) {
403706b4fc4SDimitry Andric     ::wbkgd(m_window, COLOR_PAIR(color_pair_idx));
404706b4fc4SDimitry Andric   }
405706b4fc4SDimitry Andric 
PutChar(int ch)406344a3780SDimitry Andric   void PutChar(int ch) { ::waddch(m_window, ch); }
PutCString(const char * s,int len=-1)407344a3780SDimitry Andric   void PutCString(const char *s, int len = -1) { ::waddnstr(m_window, s, len); }
408344a3780SDimitry Andric 
PutCStringTruncated(int right_pad,const char * s,int len=-1)409b60736ecSDimitry Andric   void PutCStringTruncated(int right_pad, const char *s, int len = -1) {
410706b4fc4SDimitry Andric     int bytes_left = GetWidth() - GetCursorX();
411706b4fc4SDimitry Andric     if (bytes_left > right_pad) {
412706b4fc4SDimitry Andric       bytes_left -= right_pad;
413b60736ecSDimitry Andric       ::waddnstr(m_window, s, len < 0 ? bytes_left : std::min(bytes_left, len));
414706b4fc4SDimitry Andric     }
415706b4fc4SDimitry Andric   }
416706b4fc4SDimitry Andric 
Printf(const char * format,...)417706b4fc4SDimitry Andric   void Printf(const char *format, ...) __attribute__((format(printf, 2, 3))) {
418706b4fc4SDimitry Andric     va_list args;
419706b4fc4SDimitry Andric     va_start(args, format);
420344a3780SDimitry Andric     vw_printw(m_window, format, args);
421706b4fc4SDimitry Andric     va_end(args);
422706b4fc4SDimitry Andric   }
423706b4fc4SDimitry Andric 
PrintfTruncated(int right_pad,const char * format,...)424b60736ecSDimitry Andric   void PrintfTruncated(int right_pad, const char *format, ...)
425b60736ecSDimitry Andric       __attribute__((format(printf, 3, 4))) {
426b60736ecSDimitry Andric     va_list args;
427b60736ecSDimitry Andric     va_start(args, format);
428b60736ecSDimitry Andric     StreamString strm;
429b60736ecSDimitry Andric     strm.PrintfVarArg(format, args);
430b60736ecSDimitry Andric     va_end(args);
431b60736ecSDimitry Andric     PutCStringTruncated(right_pad, strm.GetData());
432b60736ecSDimitry Andric   }
433b60736ecSDimitry Andric 
VerticalLine(int n,chtype v_char=ACS_VLINE)434344a3780SDimitry Andric   void VerticalLine(int n, chtype v_char = ACS_VLINE) {
435344a3780SDimitry Andric     ::wvline(m_window, v_char, n);
436344a3780SDimitry Andric   }
HorizontalLine(int n,chtype h_char=ACS_HLINE)437344a3780SDimitry Andric   void HorizontalLine(int n, chtype h_char = ACS_HLINE) {
438344a3780SDimitry Andric     ::whline(m_window, h_char, n);
439344a3780SDimitry Andric   }
Box(chtype v_char=ACS_VLINE,chtype h_char=ACS_HLINE)440344a3780SDimitry Andric   void Box(chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) {
441344a3780SDimitry Andric     ::box(m_window, v_char, h_char);
442344a3780SDimitry Andric   }
443344a3780SDimitry Andric 
TitledBox(const char * title,chtype v_char=ACS_VLINE,chtype h_char=ACS_HLINE)444344a3780SDimitry Andric   void TitledBox(const char *title, chtype v_char = ACS_VLINE,
445344a3780SDimitry Andric                  chtype h_char = ACS_HLINE) {
446344a3780SDimitry Andric     Box(v_char, h_char);
447344a3780SDimitry Andric     int title_offset = 2;
448344a3780SDimitry Andric     MoveCursor(title_offset, 0);
449344a3780SDimitry Andric     PutChar('[');
450344a3780SDimitry Andric     PutCString(title, GetWidth() - title_offset);
451344a3780SDimitry Andric     PutChar(']');
452344a3780SDimitry Andric   }
453344a3780SDimitry Andric 
Box(const Rect & bounds,chtype v_char=ACS_VLINE,chtype h_char=ACS_HLINE)454344a3780SDimitry Andric   void Box(const Rect &bounds, chtype v_char = ACS_VLINE,
455344a3780SDimitry Andric            chtype h_char = ACS_HLINE) {
456344a3780SDimitry Andric     MoveCursor(bounds.origin.x, bounds.origin.y);
457344a3780SDimitry Andric     VerticalLine(bounds.size.height);
458344a3780SDimitry Andric     HorizontalLine(bounds.size.width);
459344a3780SDimitry Andric     PutChar(ACS_ULCORNER);
460344a3780SDimitry Andric 
461344a3780SDimitry Andric     MoveCursor(bounds.origin.x + bounds.size.width - 1, bounds.origin.y);
462344a3780SDimitry Andric     VerticalLine(bounds.size.height);
463344a3780SDimitry Andric     PutChar(ACS_URCORNER);
464344a3780SDimitry Andric 
465344a3780SDimitry Andric     MoveCursor(bounds.origin.x, bounds.origin.y + bounds.size.height - 1);
466344a3780SDimitry Andric     HorizontalLine(bounds.size.width);
467344a3780SDimitry Andric     PutChar(ACS_LLCORNER);
468344a3780SDimitry Andric 
469344a3780SDimitry Andric     MoveCursor(bounds.origin.x + bounds.size.width - 1,
470344a3780SDimitry Andric                bounds.origin.y + bounds.size.height - 1);
471344a3780SDimitry Andric     PutChar(ACS_LRCORNER);
472344a3780SDimitry Andric   }
473344a3780SDimitry Andric 
TitledBox(const Rect & bounds,const char * title,chtype v_char=ACS_VLINE,chtype h_char=ACS_HLINE)474344a3780SDimitry Andric   void TitledBox(const Rect &bounds, const char *title,
475344a3780SDimitry Andric                  chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) {
476344a3780SDimitry Andric     Box(bounds, v_char, h_char);
477344a3780SDimitry Andric     int title_offset = 2;
478344a3780SDimitry Andric     MoveCursor(bounds.origin.x + title_offset, bounds.origin.y);
479344a3780SDimitry Andric     PutChar('[');
480344a3780SDimitry Andric     PutCString(title, bounds.size.width - title_offset);
481344a3780SDimitry Andric     PutChar(']');
482b60736ecSDimitry Andric   }
483b60736ecSDimitry Andric 
484b60736ecSDimitry Andric   // Curses doesn't allow direct output of color escape sequences, but that's
485b60736ecSDimitry Andric   // how we get source lines from the Highligher class. Read the line and
486b60736ecSDimitry Andric   // convert color escape sequences to curses color attributes. Use
487b60736ecSDimitry Andric   // first_skip_count to skip leading visible characters. Returns false if all
488b60736ecSDimitry Andric   // visible characters were skipped due to first_skip_count.
OutputColoredStringTruncated(int right_pad,StringRef string,size_t skip_first_count,bool use_blue_background)489b60736ecSDimitry Andric   bool OutputColoredStringTruncated(int right_pad, StringRef string,
490b60736ecSDimitry Andric                                     size_t skip_first_count,
491b60736ecSDimitry Andric                                     bool use_blue_background) {
492b60736ecSDimitry Andric     attr_t saved_attr;
493b60736ecSDimitry Andric     short saved_pair;
494b60736ecSDimitry Andric     bool result = false;
495b60736ecSDimitry Andric     wattr_get(m_window, &saved_attr, &saved_pair, nullptr);
496b60736ecSDimitry Andric     if (use_blue_background)
497b60736ecSDimitry Andric       ::wattron(m_window, COLOR_PAIR(WhiteOnBlue));
498b60736ecSDimitry Andric     while (!string.empty()) {
499145449b1SDimitry Andric       size_t esc_pos = string.find(ANSI_ESC_START);
500b60736ecSDimitry Andric       if (esc_pos == StringRef::npos) {
501b60736ecSDimitry Andric         string = string.substr(skip_first_count);
502b60736ecSDimitry Andric         if (!string.empty()) {
503b60736ecSDimitry Andric           PutCStringTruncated(right_pad, string.data(), string.size());
504b60736ecSDimitry Andric           result = true;
505b60736ecSDimitry Andric         }
506b60736ecSDimitry Andric         break;
507b60736ecSDimitry Andric       }
508b60736ecSDimitry Andric       if (esc_pos > 0) {
509b60736ecSDimitry Andric         if (skip_first_count > 0) {
510b60736ecSDimitry Andric           int skip = std::min(esc_pos, skip_first_count);
511b60736ecSDimitry Andric           string = string.substr(skip);
512b60736ecSDimitry Andric           skip_first_count -= skip;
513b60736ecSDimitry Andric           esc_pos -= skip;
514b60736ecSDimitry Andric         }
515b60736ecSDimitry Andric         if (esc_pos > 0) {
516b60736ecSDimitry Andric           PutCStringTruncated(right_pad, string.data(), esc_pos);
517b60736ecSDimitry Andric           result = true;
518b60736ecSDimitry Andric           string = string.drop_front(esc_pos);
519b60736ecSDimitry Andric         }
520b60736ecSDimitry Andric       }
521145449b1SDimitry Andric       bool consumed = string.consume_front(ANSI_ESC_START);
522b60736ecSDimitry Andric       assert(consumed);
523b60736ecSDimitry Andric       UNUSED_IF_ASSERT_DISABLED(consumed);
524b60736ecSDimitry Andric       // This is written to match our Highlighter classes, which seem to
525b60736ecSDimitry Andric       // generate only foreground color escape sequences. If necessary, this
526b60736ecSDimitry Andric       // will need to be extended.
527145449b1SDimitry Andric       // Only 8 basic foreground colors, underline and reset, our Highlighter
528145449b1SDimitry Andric       // doesn't use anything else.
529b60736ecSDimitry Andric       int value;
530b60736ecSDimitry Andric       if (!!string.consumeInteger(10, value) || // Returns false on success.
531145449b1SDimitry Andric           !(value == 0 || value == ANSI_CTRL_UNDERLINE ||
532145449b1SDimitry Andric             (value >= ANSI_FG_COLOR_BLACK && value <= ANSI_FG_COLOR_WHITE))) {
533b60736ecSDimitry Andric         llvm::errs() << "No valid color code in color escape sequence.\n";
534b60736ecSDimitry Andric         continue;
535b60736ecSDimitry Andric       }
536145449b1SDimitry Andric       if (!string.consume_front(ANSI_ESC_END)) {
537145449b1SDimitry Andric         llvm::errs() << "Missing '" << ANSI_ESC_END
538145449b1SDimitry Andric                      << "' in color escape sequence.\n";
539b60736ecSDimitry Andric         continue;
540b60736ecSDimitry Andric       }
541b60736ecSDimitry Andric       if (value == 0) { // Reset.
542b60736ecSDimitry Andric         wattr_set(m_window, saved_attr, saved_pair, nullptr);
543b60736ecSDimitry Andric         if (use_blue_background)
544b60736ecSDimitry Andric           ::wattron(m_window, COLOR_PAIR(WhiteOnBlue));
545145449b1SDimitry Andric       } else if (value == ANSI_CTRL_UNDERLINE) {
546145449b1SDimitry Andric         ::wattron(m_window, A_UNDERLINE);
547b60736ecSDimitry Andric       } else {
548b60736ecSDimitry Andric         // Mapped directly to first 16 color pairs (black/blue background).
549145449b1SDimitry Andric         ::wattron(m_window, COLOR_PAIR(value - ANSI_FG_COLOR_BLACK + 1 +
550145449b1SDimitry Andric                                        (use_blue_background ? 8 : 0)));
551b60736ecSDimitry Andric       }
552b60736ecSDimitry Andric     }
553b60736ecSDimitry Andric     wattr_set(m_window, saved_attr, saved_pair, nullptr);
554b60736ecSDimitry Andric     return result;
555b60736ecSDimitry Andric   }
556b60736ecSDimitry Andric 
557344a3780SDimitry Andric protected:
558c0981da4SDimitry Andric   Type m_type;
559145449b1SDimitry Andric   WINDOW *m_window = nullptr;
560344a3780SDimitry Andric };
561344a3780SDimitry Andric 
562344a3780SDimitry Andric class Pad : public Surface {
563344a3780SDimitry Andric public:
Pad(Size size)564c0981da4SDimitry Andric   Pad(Size size) : Surface(Surface::Type::Pad) {
565c0981da4SDimitry Andric     m_window = ::newpad(size.height, size.width);
566c0981da4SDimitry Andric   }
567344a3780SDimitry Andric 
~Pad()568344a3780SDimitry Andric   ~Pad() { ::delwin(m_window); }
569344a3780SDimitry Andric };
570344a3780SDimitry Andric 
571344a3780SDimitry Andric class Window : public Surface {
572344a3780SDimitry Andric public:
Window(const char * name)573344a3780SDimitry Andric   Window(const char *name)
574c0981da4SDimitry Andric       : Surface(Surface::Type::Window), m_name(name), m_panel(nullptr),
575c0981da4SDimitry Andric         m_parent(nullptr), m_subwindows(), m_delegate_sp(),
576c0981da4SDimitry Andric         m_curr_active_window_idx(UINT32_MAX),
577344a3780SDimitry Andric         m_prev_active_window_idx(UINT32_MAX), m_delete(false),
578344a3780SDimitry Andric         m_needs_update(true), m_can_activate(true), m_is_subwin(false) {}
579344a3780SDimitry Andric 
Window(const char * name,WINDOW * w,bool del=true)580344a3780SDimitry Andric   Window(const char *name, WINDOW *w, bool del = true)
581c0981da4SDimitry Andric       : Surface(Surface::Type::Window), m_name(name), m_panel(nullptr),
582c0981da4SDimitry Andric         m_parent(nullptr), m_subwindows(), m_delegate_sp(),
583c0981da4SDimitry Andric         m_curr_active_window_idx(UINT32_MAX),
584344a3780SDimitry Andric         m_prev_active_window_idx(UINT32_MAX), m_delete(del),
585344a3780SDimitry Andric         m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
586344a3780SDimitry Andric     if (w)
587344a3780SDimitry Andric       Reset(w);
588344a3780SDimitry Andric   }
589344a3780SDimitry Andric 
Window(const char * name,const Rect & bounds)590344a3780SDimitry Andric   Window(const char *name, const Rect &bounds)
591e3b55780SDimitry Andric       : Surface(Surface::Type::Window), m_name(name), m_panel(nullptr),
592e3b55780SDimitry Andric         m_parent(nullptr), m_subwindows(), m_delegate_sp(),
593e3b55780SDimitry Andric         m_curr_active_window_idx(UINT32_MAX),
594e3b55780SDimitry Andric         m_prev_active_window_idx(UINT32_MAX), m_delete(false),
595344a3780SDimitry Andric         m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
596344a3780SDimitry Andric     Reset(::newwin(bounds.size.height, bounds.size.width, bounds.origin.y,
597344a3780SDimitry Andric                    bounds.origin.y));
598344a3780SDimitry Andric   }
599344a3780SDimitry Andric 
~Window()600344a3780SDimitry Andric   virtual ~Window() {
601344a3780SDimitry Andric     RemoveSubWindows();
602344a3780SDimitry Andric     Reset();
603344a3780SDimitry Andric   }
604344a3780SDimitry Andric 
Reset(WINDOW * w=nullptr,bool del=true)605344a3780SDimitry Andric   void Reset(WINDOW *w = nullptr, bool del = true) {
606344a3780SDimitry Andric     if (m_window == w)
607344a3780SDimitry Andric       return;
608344a3780SDimitry Andric 
609344a3780SDimitry Andric     if (m_panel) {
610344a3780SDimitry Andric       ::del_panel(m_panel);
611344a3780SDimitry Andric       m_panel = nullptr;
612344a3780SDimitry Andric     }
613344a3780SDimitry Andric     if (m_window && m_delete) {
614344a3780SDimitry Andric       ::delwin(m_window);
615344a3780SDimitry Andric       m_window = nullptr;
616344a3780SDimitry Andric       m_delete = false;
617344a3780SDimitry Andric     }
618344a3780SDimitry Andric     if (w) {
619344a3780SDimitry Andric       m_window = w;
620344a3780SDimitry Andric       m_panel = ::new_panel(m_window);
621344a3780SDimitry Andric       m_delete = del;
622344a3780SDimitry Andric     }
623344a3780SDimitry Andric   }
624344a3780SDimitry Andric 
625344a3780SDimitry Andric   // Get the rectangle in our parent window
GetBounds() const626344a3780SDimitry Andric   Rect GetBounds() const { return Rect(GetParentOrigin(), GetSize()); }
627344a3780SDimitry Andric 
GetCenteredRect(int width,int height)628344a3780SDimitry Andric   Rect GetCenteredRect(int width, int height) {
629344a3780SDimitry Andric     Size size = GetSize();
630344a3780SDimitry Andric     width = std::min(size.width, width);
631344a3780SDimitry Andric     height = std::min(size.height, height);
632344a3780SDimitry Andric     int x = (size.width - width) / 2;
633344a3780SDimitry Andric     int y = (size.height - height) / 2;
634344a3780SDimitry Andric     return Rect(Point(x, y), Size(width, height));
635344a3780SDimitry Andric   }
636344a3780SDimitry Andric 
GetChar()637344a3780SDimitry Andric   int GetChar() { return ::wgetch(m_window); }
GetParentOrigin() const638344a3780SDimitry Andric   Point GetParentOrigin() const { return Point(GetParentX(), GetParentY()); }
GetParentX() const639344a3780SDimitry Andric   int GetParentX() const { return getparx(m_window); }
GetParentY() const640344a3780SDimitry Andric   int GetParentY() const { return getpary(m_window); }
MoveWindow(int x,int y)641344a3780SDimitry Andric   void MoveWindow(int x, int y) { MoveWindow(Point(x, y)); }
Resize(int w,int h)642344a3780SDimitry Andric   void Resize(int w, int h) { ::wresize(m_window, h, w); }
Resize(const Size & size)643344a3780SDimitry Andric   void Resize(const Size &size) {
644344a3780SDimitry Andric     ::wresize(m_window, size.height, size.width);
645344a3780SDimitry Andric   }
MoveWindow(const Point & origin)646344a3780SDimitry Andric   void MoveWindow(const Point &origin) {
647344a3780SDimitry Andric     const bool moving_window = origin != GetParentOrigin();
648344a3780SDimitry Andric     if (m_is_subwin && moving_window) {
649344a3780SDimitry Andric       // Can't move subwindows, must delete and re-create
650344a3780SDimitry Andric       Size size = GetSize();
651344a3780SDimitry Andric       Reset(::subwin(m_parent->m_window, size.height, size.width, origin.y,
652344a3780SDimitry Andric                      origin.x),
653344a3780SDimitry Andric             true);
654344a3780SDimitry Andric     } else {
655344a3780SDimitry Andric       ::mvwin(m_window, origin.y, origin.x);
656344a3780SDimitry Andric     }
657344a3780SDimitry Andric   }
658344a3780SDimitry Andric 
SetBounds(const Rect & bounds)659344a3780SDimitry Andric   void SetBounds(const Rect &bounds) {
660344a3780SDimitry Andric     const bool moving_window = bounds.origin != GetParentOrigin();
661344a3780SDimitry Andric     if (m_is_subwin && moving_window) {
662344a3780SDimitry Andric       // Can't move subwindows, must delete and re-create
663344a3780SDimitry Andric       Reset(::subwin(m_parent->m_window, bounds.size.height, bounds.size.width,
664344a3780SDimitry Andric                      bounds.origin.y, bounds.origin.x),
665344a3780SDimitry Andric             true);
666344a3780SDimitry Andric     } else {
667344a3780SDimitry Andric       if (moving_window)
668344a3780SDimitry Andric         MoveWindow(bounds.origin);
669344a3780SDimitry Andric       Resize(bounds.size);
670344a3780SDimitry Andric     }
671344a3780SDimitry Andric   }
672344a3780SDimitry Andric 
Touch()673706b4fc4SDimitry Andric   void Touch() {
674706b4fc4SDimitry Andric     ::touchwin(m_window);
675706b4fc4SDimitry Andric     if (m_parent)
676706b4fc4SDimitry Andric       m_parent->Touch();
677706b4fc4SDimitry Andric   }
678706b4fc4SDimitry Andric 
CreateSubWindow(const char * name,const Rect & bounds,bool make_active)679706b4fc4SDimitry Andric   WindowSP CreateSubWindow(const char *name, const Rect &bounds,
680706b4fc4SDimitry Andric                            bool make_active) {
681706b4fc4SDimitry Andric     auto get_window = [this, &bounds]() {
682706b4fc4SDimitry Andric       return m_window
683706b4fc4SDimitry Andric                  ? ::subwin(m_window, bounds.size.height, bounds.size.width,
684706b4fc4SDimitry Andric                             bounds.origin.y, bounds.origin.x)
685706b4fc4SDimitry Andric                  : ::newwin(bounds.size.height, bounds.size.width,
686706b4fc4SDimitry Andric                             bounds.origin.y, bounds.origin.x);
687706b4fc4SDimitry Andric     };
688706b4fc4SDimitry Andric     WindowSP subwindow_sp = std::make_shared<Window>(name, get_window(), true);
689706b4fc4SDimitry Andric     subwindow_sp->m_is_subwin = subwindow_sp.operator bool();
690706b4fc4SDimitry Andric     subwindow_sp->m_parent = this;
691706b4fc4SDimitry Andric     if (make_active) {
692706b4fc4SDimitry Andric       m_prev_active_window_idx = m_curr_active_window_idx;
693706b4fc4SDimitry Andric       m_curr_active_window_idx = m_subwindows.size();
694706b4fc4SDimitry Andric     }
695706b4fc4SDimitry Andric     m_subwindows.push_back(subwindow_sp);
696706b4fc4SDimitry Andric     ::top_panel(subwindow_sp->m_panel);
697706b4fc4SDimitry Andric     m_needs_update = true;
698706b4fc4SDimitry Andric     return subwindow_sp;
699706b4fc4SDimitry Andric   }
700706b4fc4SDimitry Andric 
RemoveSubWindow(Window * window)701706b4fc4SDimitry Andric   bool RemoveSubWindow(Window *window) {
702706b4fc4SDimitry Andric     Windows::iterator pos, end = m_subwindows.end();
703706b4fc4SDimitry Andric     size_t i = 0;
704706b4fc4SDimitry Andric     for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
705706b4fc4SDimitry Andric       if ((*pos).get() == window) {
706706b4fc4SDimitry Andric         if (m_prev_active_window_idx == i)
707706b4fc4SDimitry Andric           m_prev_active_window_idx = UINT32_MAX;
708706b4fc4SDimitry Andric         else if (m_prev_active_window_idx != UINT32_MAX &&
709706b4fc4SDimitry Andric                  m_prev_active_window_idx > i)
710706b4fc4SDimitry Andric           --m_prev_active_window_idx;
711706b4fc4SDimitry Andric 
712706b4fc4SDimitry Andric         if (m_curr_active_window_idx == i)
713706b4fc4SDimitry Andric           m_curr_active_window_idx = UINT32_MAX;
714706b4fc4SDimitry Andric         else if (m_curr_active_window_idx != UINT32_MAX &&
715706b4fc4SDimitry Andric                  m_curr_active_window_idx > i)
716706b4fc4SDimitry Andric           --m_curr_active_window_idx;
717706b4fc4SDimitry Andric         window->Erase();
718706b4fc4SDimitry Andric         m_subwindows.erase(pos);
719706b4fc4SDimitry Andric         m_needs_update = true;
720706b4fc4SDimitry Andric         if (m_parent)
721706b4fc4SDimitry Andric           m_parent->Touch();
722706b4fc4SDimitry Andric         else
723706b4fc4SDimitry Andric           ::touchwin(stdscr);
724706b4fc4SDimitry Andric         return true;
725706b4fc4SDimitry Andric       }
726706b4fc4SDimitry Andric     }
727706b4fc4SDimitry Andric     return false;
728706b4fc4SDimitry Andric   }
729706b4fc4SDimitry Andric 
FindSubWindow(const char * name)730706b4fc4SDimitry Andric   WindowSP FindSubWindow(const char *name) {
731706b4fc4SDimitry Andric     Windows::iterator pos, end = m_subwindows.end();
732706b4fc4SDimitry Andric     size_t i = 0;
733706b4fc4SDimitry Andric     for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
734706b4fc4SDimitry Andric       if ((*pos)->m_name == name)
735706b4fc4SDimitry Andric         return *pos;
736706b4fc4SDimitry Andric     }
737706b4fc4SDimitry Andric     return WindowSP();
738706b4fc4SDimitry Andric   }
739706b4fc4SDimitry Andric 
RemoveSubWindows()740706b4fc4SDimitry Andric   void RemoveSubWindows() {
741706b4fc4SDimitry Andric     m_curr_active_window_idx = UINT32_MAX;
742706b4fc4SDimitry Andric     m_prev_active_window_idx = UINT32_MAX;
743706b4fc4SDimitry Andric     for (Windows::iterator pos = m_subwindows.begin();
744706b4fc4SDimitry Andric          pos != m_subwindows.end(); pos = m_subwindows.erase(pos)) {
745706b4fc4SDimitry Andric       (*pos)->Erase();
746706b4fc4SDimitry Andric     }
747706b4fc4SDimitry Andric     if (m_parent)
748706b4fc4SDimitry Andric       m_parent->Touch();
749706b4fc4SDimitry Andric     else
750706b4fc4SDimitry Andric       ::touchwin(stdscr);
751706b4fc4SDimitry Andric   }
752706b4fc4SDimitry Andric 
753706b4fc4SDimitry Andric   // Window drawing utilities
DrawTitleBox(const char * title,const char * bottom_message=nullptr)754706b4fc4SDimitry Andric   void DrawTitleBox(const char *title, const char *bottom_message = nullptr) {
755706b4fc4SDimitry Andric     attr_t attr = 0;
756706b4fc4SDimitry Andric     if (IsActive())
757b60736ecSDimitry Andric       attr = A_BOLD | COLOR_PAIR(BlackOnWhite);
758706b4fc4SDimitry Andric     else
759706b4fc4SDimitry Andric       attr = 0;
760706b4fc4SDimitry Andric     if (attr)
761706b4fc4SDimitry Andric       AttributeOn(attr);
762706b4fc4SDimitry Andric 
763706b4fc4SDimitry Andric     Box();
764706b4fc4SDimitry Andric     MoveCursor(3, 0);
765706b4fc4SDimitry Andric 
766706b4fc4SDimitry Andric     if (title && title[0]) {
767706b4fc4SDimitry Andric       PutChar('<');
768706b4fc4SDimitry Andric       PutCString(title);
769706b4fc4SDimitry Andric       PutChar('>');
770706b4fc4SDimitry Andric     }
771706b4fc4SDimitry Andric 
772706b4fc4SDimitry Andric     if (bottom_message && bottom_message[0]) {
773706b4fc4SDimitry Andric       int bottom_message_length = strlen(bottom_message);
774706b4fc4SDimitry Andric       int x = GetWidth() - 3 - (bottom_message_length + 2);
775706b4fc4SDimitry Andric 
776706b4fc4SDimitry Andric       if (x > 0) {
777706b4fc4SDimitry Andric         MoveCursor(x, GetHeight() - 1);
778706b4fc4SDimitry Andric         PutChar('[');
779706b4fc4SDimitry Andric         PutCString(bottom_message);
780706b4fc4SDimitry Andric         PutChar(']');
781706b4fc4SDimitry Andric       } else {
782706b4fc4SDimitry Andric         MoveCursor(1, GetHeight() - 1);
783706b4fc4SDimitry Andric         PutChar('[');
784b60736ecSDimitry Andric         PutCStringTruncated(1, bottom_message);
785706b4fc4SDimitry Andric       }
786706b4fc4SDimitry Andric     }
787706b4fc4SDimitry Andric     if (attr)
788706b4fc4SDimitry Andric       AttributeOff(attr);
789706b4fc4SDimitry Andric   }
790706b4fc4SDimitry Andric 
Draw(bool force)791706b4fc4SDimitry Andric   virtual void Draw(bool force) {
792706b4fc4SDimitry Andric     if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw(*this, force))
793706b4fc4SDimitry Andric       return;
794706b4fc4SDimitry Andric 
795706b4fc4SDimitry Andric     for (auto &subwindow_sp : m_subwindows)
796706b4fc4SDimitry Andric       subwindow_sp->Draw(force);
797706b4fc4SDimitry Andric   }
798706b4fc4SDimitry Andric 
CreateHelpSubwindow()799706b4fc4SDimitry Andric   bool CreateHelpSubwindow() {
800706b4fc4SDimitry Andric     if (m_delegate_sp) {
801706b4fc4SDimitry Andric       const char *text = m_delegate_sp->WindowDelegateGetHelpText();
802706b4fc4SDimitry Andric       KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp();
803706b4fc4SDimitry Andric       if ((text && text[0]) || key_help) {
804706b4fc4SDimitry Andric         std::unique_ptr<HelpDialogDelegate> help_delegate_up(
805706b4fc4SDimitry Andric             new HelpDialogDelegate(text, key_help));
806706b4fc4SDimitry Andric         const size_t num_lines = help_delegate_up->GetNumLines();
807706b4fc4SDimitry Andric         const size_t max_length = help_delegate_up->GetMaxLineLength();
808706b4fc4SDimitry Andric         Rect bounds = GetBounds();
809706b4fc4SDimitry Andric         bounds.Inset(1, 1);
810706b4fc4SDimitry Andric         if (max_length + 4 < static_cast<size_t>(bounds.size.width)) {
811706b4fc4SDimitry Andric           bounds.origin.x += (bounds.size.width - max_length + 4) / 2;
812706b4fc4SDimitry Andric           bounds.size.width = max_length + 4;
813706b4fc4SDimitry Andric         } else {
814706b4fc4SDimitry Andric           if (bounds.size.width > 100) {
815706b4fc4SDimitry Andric             const int inset_w = bounds.size.width / 4;
816706b4fc4SDimitry Andric             bounds.origin.x += inset_w;
817706b4fc4SDimitry Andric             bounds.size.width -= 2 * inset_w;
818706b4fc4SDimitry Andric           }
819706b4fc4SDimitry Andric         }
820706b4fc4SDimitry Andric 
821706b4fc4SDimitry Andric         if (num_lines + 2 < static_cast<size_t>(bounds.size.height)) {
822706b4fc4SDimitry Andric           bounds.origin.y += (bounds.size.height - num_lines + 2) / 2;
823706b4fc4SDimitry Andric           bounds.size.height = num_lines + 2;
824706b4fc4SDimitry Andric         } else {
825706b4fc4SDimitry Andric           if (bounds.size.height > 100) {
826706b4fc4SDimitry Andric             const int inset_h = bounds.size.height / 4;
827706b4fc4SDimitry Andric             bounds.origin.y += inset_h;
828706b4fc4SDimitry Andric             bounds.size.height -= 2 * inset_h;
829706b4fc4SDimitry Andric           }
830706b4fc4SDimitry Andric         }
831706b4fc4SDimitry Andric         WindowSP help_window_sp;
832706b4fc4SDimitry Andric         Window *parent_window = GetParent();
833706b4fc4SDimitry Andric         if (parent_window)
834706b4fc4SDimitry Andric           help_window_sp = parent_window->CreateSubWindow("Help", bounds, true);
835706b4fc4SDimitry Andric         else
836706b4fc4SDimitry Andric           help_window_sp = CreateSubWindow("Help", bounds, true);
837706b4fc4SDimitry Andric         help_window_sp->SetDelegate(
838706b4fc4SDimitry Andric             WindowDelegateSP(help_delegate_up.release()));
839706b4fc4SDimitry Andric         return true;
840706b4fc4SDimitry Andric       }
841706b4fc4SDimitry Andric     }
842706b4fc4SDimitry Andric     return false;
843706b4fc4SDimitry Andric   }
844706b4fc4SDimitry Andric 
HandleChar(int key)845706b4fc4SDimitry Andric   virtual HandleCharResult HandleChar(int key) {
846706b4fc4SDimitry Andric     // Always check the active window first
847706b4fc4SDimitry Andric     HandleCharResult result = eKeyNotHandled;
848706b4fc4SDimitry Andric     WindowSP active_window_sp = GetActiveWindow();
849706b4fc4SDimitry Andric     if (active_window_sp) {
850706b4fc4SDimitry Andric       result = active_window_sp->HandleChar(key);
851706b4fc4SDimitry Andric       if (result != eKeyNotHandled)
852706b4fc4SDimitry Andric         return result;
853706b4fc4SDimitry Andric     }
854706b4fc4SDimitry Andric 
855706b4fc4SDimitry Andric     if (m_delegate_sp) {
856706b4fc4SDimitry Andric       result = m_delegate_sp->WindowDelegateHandleChar(*this, key);
857706b4fc4SDimitry Andric       if (result != eKeyNotHandled)
858706b4fc4SDimitry Andric         return result;
859706b4fc4SDimitry Andric     }
860706b4fc4SDimitry Andric 
861706b4fc4SDimitry Andric     // Then check for any windows that want any keys that weren't handled. This
862706b4fc4SDimitry Andric     // is typically only for a menubar. Make a copy of the subwindows in case
863706b4fc4SDimitry Andric     // any HandleChar() functions muck with the subwindows. If we don't do
864706b4fc4SDimitry Andric     // this, we can crash when iterating over the subwindows.
865706b4fc4SDimitry Andric     Windows subwindows(m_subwindows);
866706b4fc4SDimitry Andric     for (auto subwindow_sp : subwindows) {
867706b4fc4SDimitry Andric       if (!subwindow_sp->m_can_activate) {
868706b4fc4SDimitry Andric         HandleCharResult result = subwindow_sp->HandleChar(key);
869706b4fc4SDimitry Andric         if (result != eKeyNotHandled)
870706b4fc4SDimitry Andric           return result;
871706b4fc4SDimitry Andric       }
872706b4fc4SDimitry Andric     }
873706b4fc4SDimitry Andric 
874706b4fc4SDimitry Andric     return eKeyNotHandled;
875706b4fc4SDimitry Andric   }
876706b4fc4SDimitry Andric 
GetActiveWindow()877706b4fc4SDimitry Andric   WindowSP GetActiveWindow() {
878706b4fc4SDimitry Andric     if (!m_subwindows.empty()) {
879706b4fc4SDimitry Andric       if (m_curr_active_window_idx >= m_subwindows.size()) {
880706b4fc4SDimitry Andric         if (m_prev_active_window_idx < m_subwindows.size()) {
881706b4fc4SDimitry Andric           m_curr_active_window_idx = m_prev_active_window_idx;
882706b4fc4SDimitry Andric           m_prev_active_window_idx = UINT32_MAX;
883706b4fc4SDimitry Andric         } else if (IsActive()) {
884706b4fc4SDimitry Andric           m_prev_active_window_idx = UINT32_MAX;
885706b4fc4SDimitry Andric           m_curr_active_window_idx = UINT32_MAX;
886706b4fc4SDimitry Andric 
887706b4fc4SDimitry Andric           // Find first window that wants to be active if this window is active
888706b4fc4SDimitry Andric           const size_t num_subwindows = m_subwindows.size();
889706b4fc4SDimitry Andric           for (size_t i = 0; i < num_subwindows; ++i) {
890706b4fc4SDimitry Andric             if (m_subwindows[i]->GetCanBeActive()) {
891706b4fc4SDimitry Andric               m_curr_active_window_idx = i;
892706b4fc4SDimitry Andric               break;
893706b4fc4SDimitry Andric             }
894706b4fc4SDimitry Andric           }
895706b4fc4SDimitry Andric         }
896706b4fc4SDimitry Andric       }
897706b4fc4SDimitry Andric 
898706b4fc4SDimitry Andric       if (m_curr_active_window_idx < m_subwindows.size())
899706b4fc4SDimitry Andric         return m_subwindows[m_curr_active_window_idx];
900706b4fc4SDimitry Andric     }
901706b4fc4SDimitry Andric     return WindowSP();
902706b4fc4SDimitry Andric   }
903706b4fc4SDimitry Andric 
GetCanBeActive() const904706b4fc4SDimitry Andric   bool GetCanBeActive() const { return m_can_activate; }
905706b4fc4SDimitry Andric 
SetCanBeActive(bool b)906706b4fc4SDimitry Andric   void SetCanBeActive(bool b) { m_can_activate = b; }
907706b4fc4SDimitry Andric 
SetDelegate(const WindowDelegateSP & delegate_sp)908706b4fc4SDimitry Andric   void SetDelegate(const WindowDelegateSP &delegate_sp) {
909706b4fc4SDimitry Andric     m_delegate_sp = delegate_sp;
910706b4fc4SDimitry Andric   }
911706b4fc4SDimitry Andric 
GetParent() const912706b4fc4SDimitry Andric   Window *GetParent() const { return m_parent; }
913706b4fc4SDimitry Andric 
IsActive() const914706b4fc4SDimitry Andric   bool IsActive() const {
915706b4fc4SDimitry Andric     if (m_parent)
916706b4fc4SDimitry Andric       return m_parent->GetActiveWindow().get() == this;
917706b4fc4SDimitry Andric     else
918706b4fc4SDimitry Andric       return true; // Top level window is always active
919706b4fc4SDimitry Andric   }
920706b4fc4SDimitry Andric 
SelectNextWindowAsActive()921706b4fc4SDimitry Andric   void SelectNextWindowAsActive() {
922706b4fc4SDimitry Andric     // Move active focus to next window
923b60736ecSDimitry Andric     const int num_subwindows = m_subwindows.size();
924b60736ecSDimitry Andric     int start_idx = 0;
925b60736ecSDimitry Andric     if (m_curr_active_window_idx != UINT32_MAX) {
926706b4fc4SDimitry Andric       m_prev_active_window_idx = m_curr_active_window_idx;
927b60736ecSDimitry Andric       start_idx = m_curr_active_window_idx + 1;
928b60736ecSDimitry Andric     }
929b60736ecSDimitry Andric     for (int idx = start_idx; idx < num_subwindows; ++idx) {
930706b4fc4SDimitry Andric       if (m_subwindows[idx]->GetCanBeActive()) {
931706b4fc4SDimitry Andric         m_curr_active_window_idx = idx;
932b60736ecSDimitry Andric         return;
933706b4fc4SDimitry Andric       }
934706b4fc4SDimitry Andric     }
935b60736ecSDimitry Andric     for (int idx = 0; idx < start_idx; ++idx) {
936706b4fc4SDimitry Andric       if (m_subwindows[idx]->GetCanBeActive()) {
937706b4fc4SDimitry Andric         m_curr_active_window_idx = idx;
938706b4fc4SDimitry Andric         break;
939706b4fc4SDimitry Andric       }
940706b4fc4SDimitry Andric     }
941706b4fc4SDimitry Andric   }
942b60736ecSDimitry Andric 
SelectPreviousWindowAsActive()943b60736ecSDimitry Andric   void SelectPreviousWindowAsActive() {
944b60736ecSDimitry Andric     // Move active focus to previous window
945b60736ecSDimitry Andric     const int num_subwindows = m_subwindows.size();
946b60736ecSDimitry Andric     int start_idx = num_subwindows - 1;
947b60736ecSDimitry Andric     if (m_curr_active_window_idx != UINT32_MAX) {
948706b4fc4SDimitry Andric       m_prev_active_window_idx = m_curr_active_window_idx;
949b60736ecSDimitry Andric       start_idx = m_curr_active_window_idx - 1;
950b60736ecSDimitry Andric     }
951b60736ecSDimitry Andric     for (int idx = start_idx; idx >= 0; --idx) {
952b60736ecSDimitry Andric       if (m_subwindows[idx]->GetCanBeActive()) {
953b60736ecSDimitry Andric         m_curr_active_window_idx = idx;
954b60736ecSDimitry Andric         return;
955b60736ecSDimitry Andric       }
956b60736ecSDimitry Andric     }
957b60736ecSDimitry Andric     for (int idx = num_subwindows - 1; idx > start_idx; --idx) {
958706b4fc4SDimitry Andric       if (m_subwindows[idx]->GetCanBeActive()) {
959706b4fc4SDimitry Andric         m_curr_active_window_idx = idx;
960706b4fc4SDimitry Andric         break;
961706b4fc4SDimitry Andric       }
962706b4fc4SDimitry Andric     }
963706b4fc4SDimitry Andric   }
964706b4fc4SDimitry Andric 
GetName() const965706b4fc4SDimitry Andric   const char *GetName() const { return m_name.c_str(); }
966706b4fc4SDimitry Andric 
967706b4fc4SDimitry Andric protected:
968706b4fc4SDimitry Andric   std::string m_name;
969706b4fc4SDimitry Andric   PANEL *m_panel;
970706b4fc4SDimitry Andric   Window *m_parent;
971706b4fc4SDimitry Andric   Windows m_subwindows;
972706b4fc4SDimitry Andric   WindowDelegateSP m_delegate_sp;
973706b4fc4SDimitry Andric   uint32_t m_curr_active_window_idx;
974706b4fc4SDimitry Andric   uint32_t m_prev_active_window_idx;
975706b4fc4SDimitry Andric   bool m_delete;
976706b4fc4SDimitry Andric   bool m_needs_update;
977706b4fc4SDimitry Andric   bool m_can_activate;
978706b4fc4SDimitry Andric   bool m_is_subwin;
979706b4fc4SDimitry Andric 
980706b4fc4SDimitry Andric private:
981cfca06d7SDimitry Andric   Window(const Window &) = delete;
982cfca06d7SDimitry Andric   const Window &operator=(const Window &) = delete;
983706b4fc4SDimitry Andric };
984706b4fc4SDimitry Andric 
985344a3780SDimitry Andric /////////
986344a3780SDimitry Andric // Forms
987344a3780SDimitry Andric /////////
988344a3780SDimitry Andric 
989344a3780SDimitry Andric // A scroll context defines a vertical region that needs to be visible in a
990344a3780SDimitry Andric // scrolling area. The region is defined by the index of the start and end lines
991344a3780SDimitry Andric // of the region. The start and end lines may be equal, in which case, the
992344a3780SDimitry Andric // region is a single line.
993344a3780SDimitry Andric struct ScrollContext {
994344a3780SDimitry Andric   int start;
995344a3780SDimitry Andric   int end;
996344a3780SDimitry Andric 
ScrollContextcurses::ScrollContext997344a3780SDimitry Andric   ScrollContext(int line) : start(line), end(line) {}
ScrollContextcurses::ScrollContext998344a3780SDimitry Andric   ScrollContext(int _start, int _end) : start(_start), end(_end) {}
999344a3780SDimitry Andric 
Offsetcurses::ScrollContext1000344a3780SDimitry Andric   void Offset(int offset) {
1001344a3780SDimitry Andric     start += offset;
1002344a3780SDimitry Andric     end += offset;
1003344a3780SDimitry Andric   }
1004344a3780SDimitry Andric };
1005344a3780SDimitry Andric 
1006344a3780SDimitry Andric class FieldDelegate {
1007344a3780SDimitry Andric public:
1008344a3780SDimitry Andric   virtual ~FieldDelegate() = default;
1009344a3780SDimitry Andric 
1010344a3780SDimitry Andric   // Returns the number of lines needed to draw the field. The draw method will
1011344a3780SDimitry Andric   // be given a surface that have exactly this number of lines.
1012344a3780SDimitry Andric   virtual int FieldDelegateGetHeight() = 0;
1013344a3780SDimitry Andric 
1014344a3780SDimitry Andric   // Returns the scroll context in the local coordinates of the field. By
1015344a3780SDimitry Andric   // default, the scroll context spans the whole field. Bigger fields with
1016344a3780SDimitry Andric   // internal navigation should override this method to provide a finer context.
1017344a3780SDimitry Andric   // Typical override methods would first get the scroll context of the internal
1018344a3780SDimitry Andric   // element then add the offset of the element in the field.
FieldDelegateGetScrollContext()1019344a3780SDimitry Andric   virtual ScrollContext FieldDelegateGetScrollContext() {
1020344a3780SDimitry Andric     return ScrollContext(0, FieldDelegateGetHeight() - 1);
1021344a3780SDimitry Andric   }
1022344a3780SDimitry Andric 
1023344a3780SDimitry Andric   // Draw the field in the given subpad surface. The surface have a height that
1024344a3780SDimitry Andric   // is equal to the height returned by FieldDelegateGetHeight(). If the field
1025344a3780SDimitry Andric   // is selected in the form window, then is_selected will be true.
1026c0981da4SDimitry Andric   virtual void FieldDelegateDraw(Surface &surface, bool is_selected) = 0;
1027344a3780SDimitry Andric 
1028344a3780SDimitry Andric   // Handle the key that wasn't handled by the form window or a container field.
FieldDelegateHandleChar(int key)1029344a3780SDimitry Andric   virtual HandleCharResult FieldDelegateHandleChar(int key) {
1030344a3780SDimitry Andric     return eKeyNotHandled;
1031344a3780SDimitry Andric   }
1032344a3780SDimitry Andric 
1033344a3780SDimitry Andric   // This is executed once the user exists the field, that is, once the user
1034344a3780SDimitry Andric   // navigates to the next or the previous field. This is particularly useful to
1035344a3780SDimitry Andric   // do in-field validation and error setting. Fields with internal navigation
1036344a3780SDimitry Andric   // should call this method on their fields.
FieldDelegateExitCallback()103777fc4c14SDimitry Andric   virtual void FieldDelegateExitCallback() {}
1038344a3780SDimitry Andric 
1039344a3780SDimitry Andric   // Fields may have internal navigation, for instance, a List Field have
1040344a3780SDimitry Andric   // multiple internal elements, which needs to be navigated. To allow for this
1041344a3780SDimitry Andric   // mechanism, the window shouldn't handle the navigation keys all the time,
1042344a3780SDimitry Andric   // and instead call the key handing method of the selected field. It should
1043344a3780SDimitry Andric   // only handle the navigation keys when the field contains a single element or
1044344a3780SDimitry Andric   // have the last or first element selected depending on if the user is
1045344a3780SDimitry Andric   // navigating forward or backward. Additionally, once a field is selected in
1046344a3780SDimitry Andric   // the forward or backward direction, its first or last internal element
1047344a3780SDimitry Andric   // should be selected. The following methods implements those mechanisms.
1048344a3780SDimitry Andric 
1049344a3780SDimitry Andric   // Returns true if the first element in the field is selected or if the field
1050344a3780SDimitry Andric   // contains a single element.
FieldDelegateOnFirstOrOnlyElement()1051344a3780SDimitry Andric   virtual bool FieldDelegateOnFirstOrOnlyElement() { return true; }
1052344a3780SDimitry Andric 
1053344a3780SDimitry Andric   // Returns true if the last element in the field is selected or if the field
1054344a3780SDimitry Andric   // contains a single element.
FieldDelegateOnLastOrOnlyElement()1055344a3780SDimitry Andric   virtual bool FieldDelegateOnLastOrOnlyElement() { return true; }
1056344a3780SDimitry Andric 
1057344a3780SDimitry Andric   // Select the first element in the field if multiple elements exists.
FieldDelegateSelectFirstElement()105877fc4c14SDimitry Andric   virtual void FieldDelegateSelectFirstElement() {}
1059344a3780SDimitry Andric 
1060344a3780SDimitry Andric   // Select the last element in the field if multiple elements exists.
FieldDelegateSelectLastElement()106177fc4c14SDimitry Andric   virtual void FieldDelegateSelectLastElement() {}
1062344a3780SDimitry Andric 
1063344a3780SDimitry Andric   // Returns true if the field has an error, false otherwise.
FieldDelegateHasError()1064344a3780SDimitry Andric   virtual bool FieldDelegateHasError() { return false; }
1065344a3780SDimitry Andric 
FieldDelegateIsVisible()1066344a3780SDimitry Andric   bool FieldDelegateIsVisible() { return m_is_visible; }
1067344a3780SDimitry Andric 
FieldDelegateHide()1068344a3780SDimitry Andric   void FieldDelegateHide() { m_is_visible = false; }
1069344a3780SDimitry Andric 
FieldDelegateShow()1070344a3780SDimitry Andric   void FieldDelegateShow() { m_is_visible = true; }
1071344a3780SDimitry Andric 
1072344a3780SDimitry Andric protected:
1073344a3780SDimitry Andric   bool m_is_visible = true;
1074344a3780SDimitry Andric };
1075344a3780SDimitry Andric 
1076344a3780SDimitry Andric typedef std::unique_ptr<FieldDelegate> FieldDelegateUP;
1077344a3780SDimitry Andric 
1078344a3780SDimitry Andric class TextFieldDelegate : public FieldDelegate {
1079344a3780SDimitry Andric public:
TextFieldDelegate(const char * label,const char * content,bool required)1080344a3780SDimitry Andric   TextFieldDelegate(const char *label, const char *content, bool required)
1081145449b1SDimitry Andric       : m_label(label), m_required(required) {
1082344a3780SDimitry Andric     if (content)
1083344a3780SDimitry Andric       m_content = content;
1084344a3780SDimitry Andric   }
1085344a3780SDimitry Andric 
1086344a3780SDimitry Andric   // Text fields are drawn as titled boxes of a single line, with a possible
1087344a3780SDimitry Andric   // error messages at the end.
1088344a3780SDimitry Andric   //
1089344a3780SDimitry Andric   // __[Label]___________
1090344a3780SDimitry Andric   // |                  |
1091344a3780SDimitry Andric   // |__________________|
1092344a3780SDimitry Andric   // - Error message if it exists.
1093344a3780SDimitry Andric 
1094344a3780SDimitry Andric   // The text field has a height of 3 lines. 2 lines for borders and 1 line for
1095344a3780SDimitry Andric   // the content.
GetFieldHeight()1096344a3780SDimitry Andric   int GetFieldHeight() { return 3; }
1097344a3780SDimitry Andric 
1098344a3780SDimitry Andric   // The text field has a full height of 3 or 4 lines. 3 lines for the actual
1099344a3780SDimitry Andric   // field and an optional line for an error if it exists.
FieldDelegateGetHeight()1100344a3780SDimitry Andric   int FieldDelegateGetHeight() override {
1101344a3780SDimitry Andric     int height = GetFieldHeight();
1102344a3780SDimitry Andric     if (FieldDelegateHasError())
1103344a3780SDimitry Andric       height++;
1104344a3780SDimitry Andric     return height;
1105344a3780SDimitry Andric   }
1106344a3780SDimitry Andric 
1107344a3780SDimitry Andric   // Get the cursor X position in the surface coordinate.
GetCursorXPosition()1108344a3780SDimitry Andric   int GetCursorXPosition() { return m_cursor_position - m_first_visibile_char; }
1109344a3780SDimitry Andric 
GetContentLength()1110344a3780SDimitry Andric   int GetContentLength() { return m_content.length(); }
1111344a3780SDimitry Andric 
DrawContent(Surface & surface,bool is_selected)1112c0981da4SDimitry Andric   void DrawContent(Surface &surface, bool is_selected) {
1113c0981da4SDimitry Andric     UpdateScrolling(surface.GetWidth());
1114c0981da4SDimitry Andric 
1115344a3780SDimitry Andric     surface.MoveCursor(0, 0);
1116344a3780SDimitry Andric     const char *text = m_content.c_str() + m_first_visibile_char;
1117344a3780SDimitry Andric     surface.PutCString(text, surface.GetWidth());
1118344a3780SDimitry Andric 
1119344a3780SDimitry Andric     // Highlight the cursor.
1120344a3780SDimitry Andric     surface.MoveCursor(GetCursorXPosition(), 0);
1121344a3780SDimitry Andric     if (is_selected)
1122344a3780SDimitry Andric       surface.AttributeOn(A_REVERSE);
1123344a3780SDimitry Andric     if (m_cursor_position == GetContentLength())
1124344a3780SDimitry Andric       // Cursor is past the last character. Highlight an empty space.
1125344a3780SDimitry Andric       surface.PutChar(' ');
1126344a3780SDimitry Andric     else
1127344a3780SDimitry Andric       surface.PutChar(m_content[m_cursor_position]);
1128344a3780SDimitry Andric     if (is_selected)
1129344a3780SDimitry Andric       surface.AttributeOff(A_REVERSE);
1130344a3780SDimitry Andric   }
1131344a3780SDimitry Andric 
DrawField(Surface & surface,bool is_selected)1132c0981da4SDimitry Andric   void DrawField(Surface &surface, bool is_selected) {
1133344a3780SDimitry Andric     surface.TitledBox(m_label.c_str());
1134344a3780SDimitry Andric 
1135344a3780SDimitry Andric     Rect content_bounds = surface.GetFrame();
1136344a3780SDimitry Andric     content_bounds.Inset(1, 1);
1137c0981da4SDimitry Andric     Surface content_surface = surface.SubSurface(content_bounds);
1138344a3780SDimitry Andric 
1139344a3780SDimitry Andric     DrawContent(content_surface, is_selected);
1140344a3780SDimitry Andric   }
1141344a3780SDimitry Andric 
DrawError(Surface & surface)1142c0981da4SDimitry Andric   void DrawError(Surface &surface) {
1143344a3780SDimitry Andric     if (!FieldDelegateHasError())
1144344a3780SDimitry Andric       return;
1145344a3780SDimitry Andric     surface.MoveCursor(0, 0);
1146344a3780SDimitry Andric     surface.AttributeOn(COLOR_PAIR(RedOnBlack));
1147344a3780SDimitry Andric     surface.PutChar(ACS_DIAMOND);
1148344a3780SDimitry Andric     surface.PutChar(' ');
1149344a3780SDimitry Andric     surface.PutCStringTruncated(1, GetError().c_str());
1150344a3780SDimitry Andric     surface.AttributeOff(COLOR_PAIR(RedOnBlack));
1151344a3780SDimitry Andric   }
1152344a3780SDimitry Andric 
FieldDelegateDraw(Surface & surface,bool is_selected)1153c0981da4SDimitry Andric   void FieldDelegateDraw(Surface &surface, bool is_selected) override {
1154344a3780SDimitry Andric     Rect frame = surface.GetFrame();
1155344a3780SDimitry Andric     Rect field_bounds, error_bounds;
1156344a3780SDimitry Andric     frame.HorizontalSplit(GetFieldHeight(), field_bounds, error_bounds);
1157c0981da4SDimitry Andric     Surface field_surface = surface.SubSurface(field_bounds);
1158c0981da4SDimitry Andric     Surface error_surface = surface.SubSurface(error_bounds);
1159344a3780SDimitry Andric 
1160344a3780SDimitry Andric     DrawField(field_surface, is_selected);
1161344a3780SDimitry Andric     DrawError(error_surface);
1162344a3780SDimitry Andric   }
1163344a3780SDimitry Andric 
1164c0981da4SDimitry Andric   // Get the position of the last visible character.
GetLastVisibleCharPosition(int width)1165c0981da4SDimitry Andric   int GetLastVisibleCharPosition(int width) {
1166c0981da4SDimitry Andric     int position = m_first_visibile_char + width - 1;
1167c0981da4SDimitry Andric     return std::min(position, GetContentLength());
1168c0981da4SDimitry Andric   }
1169c0981da4SDimitry Andric 
UpdateScrolling(int width)1170c0981da4SDimitry Andric   void UpdateScrolling(int width) {
1171c0981da4SDimitry Andric     if (m_cursor_position < m_first_visibile_char) {
1172c0981da4SDimitry Andric       m_first_visibile_char = m_cursor_position;
1173c0981da4SDimitry Andric       return;
1174c0981da4SDimitry Andric     }
1175c0981da4SDimitry Andric 
1176c0981da4SDimitry Andric     if (m_cursor_position > GetLastVisibleCharPosition(width))
1177c0981da4SDimitry Andric       m_first_visibile_char = m_cursor_position - (width - 1);
1178c0981da4SDimitry Andric   }
1179c0981da4SDimitry Andric 
1180344a3780SDimitry Andric   // The cursor is allowed to move one character past the string.
1181344a3780SDimitry Andric   // m_cursor_position is in range [0, GetContentLength()].
MoveCursorRight()1182344a3780SDimitry Andric   void MoveCursorRight() {
1183344a3780SDimitry Andric     if (m_cursor_position < GetContentLength())
1184344a3780SDimitry Andric       m_cursor_position++;
1185344a3780SDimitry Andric   }
1186344a3780SDimitry Andric 
MoveCursorLeft()1187344a3780SDimitry Andric   void MoveCursorLeft() {
1188344a3780SDimitry Andric     if (m_cursor_position > 0)
1189344a3780SDimitry Andric       m_cursor_position--;
1190344a3780SDimitry Andric   }
1191344a3780SDimitry Andric 
MoveCursorToStart()1192c0981da4SDimitry Andric   void MoveCursorToStart() { m_cursor_position = 0; }
1193c0981da4SDimitry Andric 
MoveCursorToEnd()1194c0981da4SDimitry Andric   void MoveCursorToEnd() { m_cursor_position = GetContentLength(); }
1195344a3780SDimitry Andric 
ScrollLeft()1196344a3780SDimitry Andric   void ScrollLeft() {
1197344a3780SDimitry Andric     if (m_first_visibile_char > 0)
1198344a3780SDimitry Andric       m_first_visibile_char--;
1199344a3780SDimitry Andric   }
1200344a3780SDimitry Andric 
1201c0981da4SDimitry Andric   // Insert a character at the current cursor position and advance the cursor
1202c0981da4SDimitry Andric   // position.
InsertChar(char character)1203344a3780SDimitry Andric   void InsertChar(char character) {
1204344a3780SDimitry Andric     m_content.insert(m_cursor_position, 1, character);
1205344a3780SDimitry Andric     m_cursor_position++;
1206c0981da4SDimitry Andric     ClearError();
1207344a3780SDimitry Andric   }
1208344a3780SDimitry Andric 
1209344a3780SDimitry Andric   // Remove the character before the cursor position, retreat the cursor
1210c0981da4SDimitry Andric   // position, and scroll left.
RemovePreviousChar()1211c0981da4SDimitry Andric   void RemovePreviousChar() {
1212344a3780SDimitry Andric     if (m_cursor_position == 0)
1213344a3780SDimitry Andric       return;
1214344a3780SDimitry Andric 
1215344a3780SDimitry Andric     m_content.erase(m_cursor_position - 1, 1);
1216344a3780SDimitry Andric     m_cursor_position--;
1217344a3780SDimitry Andric     ScrollLeft();
1218c0981da4SDimitry Andric     ClearError();
1219c0981da4SDimitry Andric   }
1220c0981da4SDimitry Andric 
1221c0981da4SDimitry Andric   // Remove the character after the cursor position.
RemoveNextChar()1222c0981da4SDimitry Andric   void RemoveNextChar() {
1223c0981da4SDimitry Andric     if (m_cursor_position == GetContentLength())
1224c0981da4SDimitry Andric       return;
1225c0981da4SDimitry Andric 
1226c0981da4SDimitry Andric     m_content.erase(m_cursor_position, 1);
1227c0981da4SDimitry Andric     ClearError();
1228c0981da4SDimitry Andric   }
1229c0981da4SDimitry Andric 
1230c0981da4SDimitry Andric   // Clear characters from the current cursor position to the end.
ClearToEnd()1231c0981da4SDimitry Andric   void ClearToEnd() {
1232c0981da4SDimitry Andric     m_content.erase(m_cursor_position);
1233c0981da4SDimitry Andric     ClearError();
1234c0981da4SDimitry Andric   }
1235c0981da4SDimitry Andric 
Clear()1236c0981da4SDimitry Andric   void Clear() {
1237c0981da4SDimitry Andric     m_content.clear();
1238c0981da4SDimitry Andric     m_cursor_position = 0;
1239c0981da4SDimitry Andric     ClearError();
1240344a3780SDimitry Andric   }
1241344a3780SDimitry Andric 
1242344a3780SDimitry Andric   // True if the key represents a char that can be inserted in the field
1243344a3780SDimitry Andric   // content, false otherwise.
IsAcceptableChar(int key)1244c0981da4SDimitry Andric   virtual bool IsAcceptableChar(int key) {
1245c0981da4SDimitry Andric     // The behavior of isprint is undefined when the value is not representable
1246c0981da4SDimitry Andric     // as an unsigned char. So explicitly check for non-ascii key codes.
1247c0981da4SDimitry Andric     if (key > 127)
1248c0981da4SDimitry Andric       return false;
1249c0981da4SDimitry Andric     return isprint(key);
1250c0981da4SDimitry Andric   }
1251344a3780SDimitry Andric 
FieldDelegateHandleChar(int key)1252344a3780SDimitry Andric   HandleCharResult FieldDelegateHandleChar(int key) override {
1253344a3780SDimitry Andric     if (IsAcceptableChar(key)) {
1254344a3780SDimitry Andric       ClearError();
1255344a3780SDimitry Andric       InsertChar((char)key);
1256344a3780SDimitry Andric       return eKeyHandled;
1257344a3780SDimitry Andric     }
1258344a3780SDimitry Andric 
1259344a3780SDimitry Andric     switch (key) {
1260c0981da4SDimitry Andric     case KEY_HOME:
1261c0981da4SDimitry Andric     case KEY_CTRL_A:
1262c0981da4SDimitry Andric       MoveCursorToStart();
1263c0981da4SDimitry Andric       return eKeyHandled;
1264c0981da4SDimitry Andric     case KEY_END:
1265c0981da4SDimitry Andric     case KEY_CTRL_E:
1266c0981da4SDimitry Andric       MoveCursorToEnd();
1267c0981da4SDimitry Andric       return eKeyHandled;
1268344a3780SDimitry Andric     case KEY_RIGHT:
1269c0981da4SDimitry Andric     case KEY_SF:
1270344a3780SDimitry Andric       MoveCursorRight();
1271344a3780SDimitry Andric       return eKeyHandled;
1272344a3780SDimitry Andric     case KEY_LEFT:
1273c0981da4SDimitry Andric     case KEY_SR:
1274344a3780SDimitry Andric       MoveCursorLeft();
1275344a3780SDimitry Andric       return eKeyHandled;
1276344a3780SDimitry Andric     case KEY_BACKSPACE:
1277c0981da4SDimitry Andric     case KEY_DELETE:
1278c0981da4SDimitry Andric       RemovePreviousChar();
1279c0981da4SDimitry Andric       return eKeyHandled;
1280c0981da4SDimitry Andric     case KEY_DC:
1281c0981da4SDimitry Andric       RemoveNextChar();
1282c0981da4SDimitry Andric       return eKeyHandled;
1283c0981da4SDimitry Andric     case KEY_EOL:
1284c0981da4SDimitry Andric     case KEY_CTRL_K:
1285c0981da4SDimitry Andric       ClearToEnd();
1286c0981da4SDimitry Andric       return eKeyHandled;
1287c0981da4SDimitry Andric     case KEY_DL:
1288c0981da4SDimitry Andric     case KEY_CLEAR:
1289c0981da4SDimitry Andric       Clear();
1290344a3780SDimitry Andric       return eKeyHandled;
1291344a3780SDimitry Andric     default:
1292344a3780SDimitry Andric       break;
1293344a3780SDimitry Andric     }
1294344a3780SDimitry Andric     return eKeyNotHandled;
1295344a3780SDimitry Andric   }
1296344a3780SDimitry Andric 
FieldDelegateHasError()1297344a3780SDimitry Andric   bool FieldDelegateHasError() override { return !m_error.empty(); }
1298344a3780SDimitry Andric 
FieldDelegateExitCallback()1299344a3780SDimitry Andric   void FieldDelegateExitCallback() override {
1300344a3780SDimitry Andric     if (!IsSpecified() && m_required)
1301344a3780SDimitry Andric       SetError("This field is required!");
1302344a3780SDimitry Andric   }
1303344a3780SDimitry Andric 
IsSpecified()1304344a3780SDimitry Andric   bool IsSpecified() { return !m_content.empty(); }
1305344a3780SDimitry Andric 
ClearError()1306344a3780SDimitry Andric   void ClearError() { m_error.clear(); }
1307344a3780SDimitry Andric 
GetError()1308344a3780SDimitry Andric   const std::string &GetError() { return m_error; }
1309344a3780SDimitry Andric 
SetError(const char * error)1310344a3780SDimitry Andric   void SetError(const char *error) { m_error = error; }
1311344a3780SDimitry Andric 
GetText()1312344a3780SDimitry Andric   const std::string &GetText() { return m_content; }
1313344a3780SDimitry Andric 
SetText(const char * text)1314c0981da4SDimitry Andric   void SetText(const char *text) {
1315c0981da4SDimitry Andric     if (text == nullptr) {
1316c0981da4SDimitry Andric       m_content.clear();
1317c0981da4SDimitry Andric       return;
1318c0981da4SDimitry Andric     }
1319c0981da4SDimitry Andric     m_content = text;
1320c0981da4SDimitry Andric   }
1321c0981da4SDimitry Andric 
1322344a3780SDimitry Andric protected:
1323344a3780SDimitry Andric   std::string m_label;
1324344a3780SDimitry Andric   bool m_required;
1325344a3780SDimitry Andric   // The position of the top left corner character of the border.
1326344a3780SDimitry Andric   std::string m_content;
1327344a3780SDimitry Andric   // The cursor position in the content string itself. Can be in the range
1328344a3780SDimitry Andric   // [0, GetContentLength()].
1329145449b1SDimitry Andric   int m_cursor_position = 0;
1330344a3780SDimitry Andric   // The index of the first visible character in the content.
1331145449b1SDimitry Andric   int m_first_visibile_char = 0;
1332344a3780SDimitry Andric   // Optional error message. If empty, field is considered to have no error.
1333344a3780SDimitry Andric   std::string m_error;
1334344a3780SDimitry Andric };
1335344a3780SDimitry Andric 
1336344a3780SDimitry Andric class IntegerFieldDelegate : public TextFieldDelegate {
1337344a3780SDimitry Andric public:
IntegerFieldDelegate(const char * label,int content,bool required)1338344a3780SDimitry Andric   IntegerFieldDelegate(const char *label, int content, bool required)
1339344a3780SDimitry Andric       : TextFieldDelegate(label, std::to_string(content).c_str(), required) {}
1340344a3780SDimitry Andric 
1341344a3780SDimitry Andric   // Only accept digits.
IsAcceptableChar(int key)1342344a3780SDimitry Andric   bool IsAcceptableChar(int key) override { return isdigit(key); }
1343344a3780SDimitry Andric 
1344344a3780SDimitry Andric   // Returns the integer content of the field.
GetInteger()1345344a3780SDimitry Andric   int GetInteger() { return std::stoi(m_content); }
1346344a3780SDimitry Andric };
1347344a3780SDimitry Andric 
1348344a3780SDimitry Andric class FileFieldDelegate : public TextFieldDelegate {
1349344a3780SDimitry Andric public:
FileFieldDelegate(const char * label,const char * content,bool need_to_exist,bool required)1350344a3780SDimitry Andric   FileFieldDelegate(const char *label, const char *content, bool need_to_exist,
1351344a3780SDimitry Andric                     bool required)
1352344a3780SDimitry Andric       : TextFieldDelegate(label, content, required),
1353344a3780SDimitry Andric         m_need_to_exist(need_to_exist) {}
1354344a3780SDimitry Andric 
FieldDelegateExitCallback()1355344a3780SDimitry Andric   void FieldDelegateExitCallback() override {
1356344a3780SDimitry Andric     TextFieldDelegate::FieldDelegateExitCallback();
1357344a3780SDimitry Andric     if (!IsSpecified())
1358344a3780SDimitry Andric       return;
1359344a3780SDimitry Andric 
1360344a3780SDimitry Andric     if (!m_need_to_exist)
1361344a3780SDimitry Andric       return;
1362344a3780SDimitry Andric 
1363344a3780SDimitry Andric     FileSpec file = GetResolvedFileSpec();
1364344a3780SDimitry Andric     if (!FileSystem::Instance().Exists(file)) {
1365344a3780SDimitry Andric       SetError("File doesn't exist!");
1366344a3780SDimitry Andric       return;
1367344a3780SDimitry Andric     }
1368344a3780SDimitry Andric     if (FileSystem::Instance().IsDirectory(file)) {
1369344a3780SDimitry Andric       SetError("Not a file!");
1370344a3780SDimitry Andric       return;
1371344a3780SDimitry Andric     }
1372344a3780SDimitry Andric   }
1373344a3780SDimitry Andric 
GetFileSpec()1374344a3780SDimitry Andric   FileSpec GetFileSpec() {
1375344a3780SDimitry Andric     FileSpec file_spec(GetPath());
1376344a3780SDimitry Andric     return file_spec;
1377344a3780SDimitry Andric   }
1378344a3780SDimitry Andric 
GetResolvedFileSpec()1379344a3780SDimitry Andric   FileSpec GetResolvedFileSpec() {
1380344a3780SDimitry Andric     FileSpec file_spec(GetPath());
1381344a3780SDimitry Andric     FileSystem::Instance().Resolve(file_spec);
1382344a3780SDimitry Andric     return file_spec;
1383344a3780SDimitry Andric   }
1384344a3780SDimitry Andric 
GetPath()1385344a3780SDimitry Andric   const std::string &GetPath() { return m_content; }
1386344a3780SDimitry Andric 
1387344a3780SDimitry Andric protected:
1388344a3780SDimitry Andric   bool m_need_to_exist;
1389344a3780SDimitry Andric };
1390344a3780SDimitry Andric 
1391344a3780SDimitry Andric class DirectoryFieldDelegate : public TextFieldDelegate {
1392344a3780SDimitry Andric public:
DirectoryFieldDelegate(const char * label,const char * content,bool need_to_exist,bool required)1393344a3780SDimitry Andric   DirectoryFieldDelegate(const char *label, const char *content,
1394344a3780SDimitry Andric                          bool need_to_exist, bool required)
1395344a3780SDimitry Andric       : TextFieldDelegate(label, content, required),
1396344a3780SDimitry Andric         m_need_to_exist(need_to_exist) {}
1397344a3780SDimitry Andric 
FieldDelegateExitCallback()1398344a3780SDimitry Andric   void FieldDelegateExitCallback() override {
1399344a3780SDimitry Andric     TextFieldDelegate::FieldDelegateExitCallback();
1400344a3780SDimitry Andric     if (!IsSpecified())
1401344a3780SDimitry Andric       return;
1402344a3780SDimitry Andric 
1403344a3780SDimitry Andric     if (!m_need_to_exist)
1404344a3780SDimitry Andric       return;
1405344a3780SDimitry Andric 
1406344a3780SDimitry Andric     FileSpec file = GetResolvedFileSpec();
1407344a3780SDimitry Andric     if (!FileSystem::Instance().Exists(file)) {
1408344a3780SDimitry Andric       SetError("Directory doesn't exist!");
1409344a3780SDimitry Andric       return;
1410344a3780SDimitry Andric     }
1411344a3780SDimitry Andric     if (!FileSystem::Instance().IsDirectory(file)) {
1412344a3780SDimitry Andric       SetError("Not a directory!");
1413344a3780SDimitry Andric       return;
1414344a3780SDimitry Andric     }
1415344a3780SDimitry Andric   }
1416344a3780SDimitry Andric 
GetFileSpec()1417344a3780SDimitry Andric   FileSpec GetFileSpec() {
1418344a3780SDimitry Andric     FileSpec file_spec(GetPath());
1419344a3780SDimitry Andric     return file_spec;
1420344a3780SDimitry Andric   }
1421344a3780SDimitry Andric 
GetResolvedFileSpec()1422344a3780SDimitry Andric   FileSpec GetResolvedFileSpec() {
1423344a3780SDimitry Andric     FileSpec file_spec(GetPath());
1424344a3780SDimitry Andric     FileSystem::Instance().Resolve(file_spec);
1425344a3780SDimitry Andric     return file_spec;
1426344a3780SDimitry Andric   }
1427344a3780SDimitry Andric 
GetPath()1428344a3780SDimitry Andric   const std::string &GetPath() { return m_content; }
1429344a3780SDimitry Andric 
1430344a3780SDimitry Andric protected:
1431344a3780SDimitry Andric   bool m_need_to_exist;
1432344a3780SDimitry Andric };
1433344a3780SDimitry Andric 
1434344a3780SDimitry Andric class ArchFieldDelegate : public TextFieldDelegate {
1435344a3780SDimitry Andric public:
ArchFieldDelegate(const char * label,const char * content,bool required)1436344a3780SDimitry Andric   ArchFieldDelegate(const char *label, const char *content, bool required)
1437344a3780SDimitry Andric       : TextFieldDelegate(label, content, required) {}
1438344a3780SDimitry Andric 
FieldDelegateExitCallback()1439344a3780SDimitry Andric   void FieldDelegateExitCallback() override {
1440344a3780SDimitry Andric     TextFieldDelegate::FieldDelegateExitCallback();
1441344a3780SDimitry Andric     if (!IsSpecified())
1442344a3780SDimitry Andric       return;
1443344a3780SDimitry Andric 
1444344a3780SDimitry Andric     if (!GetArchSpec().IsValid())
1445344a3780SDimitry Andric       SetError("Not a valid arch!");
1446344a3780SDimitry Andric   }
1447344a3780SDimitry Andric 
GetArchString()1448344a3780SDimitry Andric   const std::string &GetArchString() { return m_content; }
1449344a3780SDimitry Andric 
GetArchSpec()1450344a3780SDimitry Andric   ArchSpec GetArchSpec() { return ArchSpec(GetArchString()); }
1451344a3780SDimitry Andric };
1452344a3780SDimitry Andric 
1453344a3780SDimitry Andric class BooleanFieldDelegate : public FieldDelegate {
1454344a3780SDimitry Andric public:
BooleanFieldDelegate(const char * label,bool content)1455344a3780SDimitry Andric   BooleanFieldDelegate(const char *label, bool content)
1456344a3780SDimitry Andric       : m_label(label), m_content(content) {}
1457344a3780SDimitry Andric 
1458344a3780SDimitry Andric   // Boolean fields are drawn as checkboxes.
1459344a3780SDimitry Andric   //
1460344a3780SDimitry Andric   // [X] Label  or [ ] Label
1461344a3780SDimitry Andric 
1462344a3780SDimitry Andric   // Boolean fields are have a single line.
FieldDelegateGetHeight()1463344a3780SDimitry Andric   int FieldDelegateGetHeight() override { return 1; }
1464344a3780SDimitry Andric 
FieldDelegateDraw(Surface & surface,bool is_selected)1465c0981da4SDimitry Andric   void FieldDelegateDraw(Surface &surface, bool is_selected) override {
1466344a3780SDimitry Andric     surface.MoveCursor(0, 0);
1467344a3780SDimitry Andric     surface.PutChar('[');
1468344a3780SDimitry Andric     if (is_selected)
1469344a3780SDimitry Andric       surface.AttributeOn(A_REVERSE);
1470344a3780SDimitry Andric     surface.PutChar(m_content ? ACS_DIAMOND : ' ');
1471344a3780SDimitry Andric     if (is_selected)
1472344a3780SDimitry Andric       surface.AttributeOff(A_REVERSE);
1473344a3780SDimitry Andric     surface.PutChar(']');
1474344a3780SDimitry Andric     surface.PutChar(' ');
1475344a3780SDimitry Andric     surface.PutCString(m_label.c_str());
1476344a3780SDimitry Andric   }
1477344a3780SDimitry Andric 
ToggleContent()1478344a3780SDimitry Andric   void ToggleContent() { m_content = !m_content; }
1479344a3780SDimitry Andric 
SetContentToTrue()1480344a3780SDimitry Andric   void SetContentToTrue() { m_content = true; }
1481344a3780SDimitry Andric 
SetContentToFalse()1482344a3780SDimitry Andric   void SetContentToFalse() { m_content = false; }
1483344a3780SDimitry Andric 
FieldDelegateHandleChar(int key)1484344a3780SDimitry Andric   HandleCharResult FieldDelegateHandleChar(int key) override {
1485344a3780SDimitry Andric     switch (key) {
1486344a3780SDimitry Andric     case 't':
1487344a3780SDimitry Andric     case '1':
1488344a3780SDimitry Andric       SetContentToTrue();
1489344a3780SDimitry Andric       return eKeyHandled;
1490344a3780SDimitry Andric     case 'f':
1491344a3780SDimitry Andric     case '0':
1492344a3780SDimitry Andric       SetContentToFalse();
1493344a3780SDimitry Andric       return eKeyHandled;
1494344a3780SDimitry Andric     case ' ':
1495344a3780SDimitry Andric     case '\r':
1496344a3780SDimitry Andric     case '\n':
1497344a3780SDimitry Andric     case KEY_ENTER:
1498344a3780SDimitry Andric       ToggleContent();
1499344a3780SDimitry Andric       return eKeyHandled;
1500344a3780SDimitry Andric     default:
1501344a3780SDimitry Andric       break;
1502344a3780SDimitry Andric     }
1503344a3780SDimitry Andric     return eKeyNotHandled;
1504344a3780SDimitry Andric   }
1505344a3780SDimitry Andric 
1506344a3780SDimitry Andric   // Returns the boolean content of the field.
GetBoolean()1507344a3780SDimitry Andric   bool GetBoolean() { return m_content; }
1508344a3780SDimitry Andric 
1509344a3780SDimitry Andric protected:
1510344a3780SDimitry Andric   std::string m_label;
1511344a3780SDimitry Andric   bool m_content;
1512344a3780SDimitry Andric };
1513344a3780SDimitry Andric 
1514344a3780SDimitry Andric class ChoicesFieldDelegate : public FieldDelegate {
1515344a3780SDimitry Andric public:
ChoicesFieldDelegate(const char * label,int number_of_visible_choices,std::vector<std::string> choices)1516344a3780SDimitry Andric   ChoicesFieldDelegate(const char *label, int number_of_visible_choices,
1517344a3780SDimitry Andric                        std::vector<std::string> choices)
1518344a3780SDimitry Andric       : m_label(label), m_number_of_visible_choices(number_of_visible_choices),
1519145449b1SDimitry Andric         m_choices(choices) {}
1520344a3780SDimitry Andric 
1521344a3780SDimitry Andric   // Choices fields are drawn as titles boxses of a number of visible choices.
1522344a3780SDimitry Andric   // The rest of the choices become visible as the user scroll. The selected
1523344a3780SDimitry Andric   // choice is denoted by a diamond as the first character.
1524344a3780SDimitry Andric   //
1525344a3780SDimitry Andric   // __[Label]___________
1526344a3780SDimitry Andric   // |-Choice 1         |
1527344a3780SDimitry Andric   // | Choice 2         |
1528344a3780SDimitry Andric   // | Choice 3         |
1529344a3780SDimitry Andric   // |__________________|
1530344a3780SDimitry Andric 
1531344a3780SDimitry Andric   // Choices field have two border characters plus the number of visible
1532344a3780SDimitry Andric   // choices.
FieldDelegateGetHeight()1533344a3780SDimitry Andric   int FieldDelegateGetHeight() override {
1534344a3780SDimitry Andric     return m_number_of_visible_choices + 2;
1535344a3780SDimitry Andric   }
1536344a3780SDimitry Andric 
GetNumberOfChoices()1537344a3780SDimitry Andric   int GetNumberOfChoices() { return m_choices.size(); }
1538344a3780SDimitry Andric 
1539344a3780SDimitry Andric   // Get the index of the last visible choice.
GetLastVisibleChoice()1540344a3780SDimitry Andric   int GetLastVisibleChoice() {
1541344a3780SDimitry Andric     int index = m_first_visibile_choice + m_number_of_visible_choices;
1542344a3780SDimitry Andric     return std::min(index, GetNumberOfChoices()) - 1;
1543344a3780SDimitry Andric   }
1544344a3780SDimitry Andric 
DrawContent(Surface & surface,bool is_selected)1545c0981da4SDimitry Andric   void DrawContent(Surface &surface, bool is_selected) {
1546344a3780SDimitry Andric     int choices_to_draw = GetLastVisibleChoice() - m_first_visibile_choice + 1;
1547344a3780SDimitry Andric     for (int i = 0; i < choices_to_draw; i++) {
1548344a3780SDimitry Andric       surface.MoveCursor(0, i);
1549344a3780SDimitry Andric       int current_choice = m_first_visibile_choice + i;
1550344a3780SDimitry Andric       const char *text = m_choices[current_choice].c_str();
1551344a3780SDimitry Andric       bool highlight = is_selected && current_choice == m_choice;
1552344a3780SDimitry Andric       if (highlight)
1553344a3780SDimitry Andric         surface.AttributeOn(A_REVERSE);
1554344a3780SDimitry Andric       surface.PutChar(current_choice == m_choice ? ACS_DIAMOND : ' ');
1555344a3780SDimitry Andric       surface.PutCString(text);
1556344a3780SDimitry Andric       if (highlight)
1557344a3780SDimitry Andric         surface.AttributeOff(A_REVERSE);
1558344a3780SDimitry Andric     }
1559344a3780SDimitry Andric   }
1560344a3780SDimitry Andric 
FieldDelegateDraw(Surface & surface,bool is_selected)1561c0981da4SDimitry Andric   void FieldDelegateDraw(Surface &surface, bool is_selected) override {
1562344a3780SDimitry Andric     UpdateScrolling();
1563344a3780SDimitry Andric 
1564344a3780SDimitry Andric     surface.TitledBox(m_label.c_str());
1565344a3780SDimitry Andric 
1566344a3780SDimitry Andric     Rect content_bounds = surface.GetFrame();
1567344a3780SDimitry Andric     content_bounds.Inset(1, 1);
1568c0981da4SDimitry Andric     Surface content_surface = surface.SubSurface(content_bounds);
1569344a3780SDimitry Andric 
1570344a3780SDimitry Andric     DrawContent(content_surface, is_selected);
1571344a3780SDimitry Andric   }
1572344a3780SDimitry Andric 
SelectPrevious()1573344a3780SDimitry Andric   void SelectPrevious() {
1574344a3780SDimitry Andric     if (m_choice > 0)
1575344a3780SDimitry Andric       m_choice--;
1576344a3780SDimitry Andric   }
1577344a3780SDimitry Andric 
SelectNext()1578344a3780SDimitry Andric   void SelectNext() {
1579344a3780SDimitry Andric     if (m_choice < GetNumberOfChoices() - 1)
1580344a3780SDimitry Andric       m_choice++;
1581344a3780SDimitry Andric   }
1582344a3780SDimitry Andric 
UpdateScrolling()1583344a3780SDimitry Andric   void UpdateScrolling() {
1584344a3780SDimitry Andric     if (m_choice > GetLastVisibleChoice()) {
1585344a3780SDimitry Andric       m_first_visibile_choice = m_choice - (m_number_of_visible_choices - 1);
1586344a3780SDimitry Andric       return;
1587344a3780SDimitry Andric     }
1588344a3780SDimitry Andric 
1589344a3780SDimitry Andric     if (m_choice < m_first_visibile_choice)
1590344a3780SDimitry Andric       m_first_visibile_choice = m_choice;
1591344a3780SDimitry Andric   }
1592344a3780SDimitry Andric 
FieldDelegateHandleChar(int key)1593344a3780SDimitry Andric   HandleCharResult FieldDelegateHandleChar(int key) override {
1594344a3780SDimitry Andric     switch (key) {
1595344a3780SDimitry Andric     case KEY_UP:
1596344a3780SDimitry Andric       SelectPrevious();
1597344a3780SDimitry Andric       return eKeyHandled;
1598344a3780SDimitry Andric     case KEY_DOWN:
1599344a3780SDimitry Andric       SelectNext();
1600344a3780SDimitry Andric       return eKeyHandled;
1601344a3780SDimitry Andric     default:
1602344a3780SDimitry Andric       break;
1603344a3780SDimitry Andric     }
1604344a3780SDimitry Andric     return eKeyNotHandled;
1605344a3780SDimitry Andric   }
1606344a3780SDimitry Andric 
1607344a3780SDimitry Andric   // Returns the content of the choice as a string.
GetChoiceContent()1608344a3780SDimitry Andric   std::string GetChoiceContent() { return m_choices[m_choice]; }
1609344a3780SDimitry Andric 
1610344a3780SDimitry Andric   // Returns the index of the choice.
GetChoice()1611344a3780SDimitry Andric   int GetChoice() { return m_choice; }
1612344a3780SDimitry Andric 
SetChoice(llvm::StringRef choice)1613145449b1SDimitry Andric   void SetChoice(llvm::StringRef choice) {
1614344a3780SDimitry Andric     for (int i = 0; i < GetNumberOfChoices(); i++) {
1615344a3780SDimitry Andric       if (choice == m_choices[i]) {
1616344a3780SDimitry Andric         m_choice = i;
1617344a3780SDimitry Andric         return;
1618344a3780SDimitry Andric       }
1619344a3780SDimitry Andric     }
1620344a3780SDimitry Andric   }
1621344a3780SDimitry Andric 
1622344a3780SDimitry Andric protected:
1623344a3780SDimitry Andric   std::string m_label;
1624344a3780SDimitry Andric   int m_number_of_visible_choices;
1625344a3780SDimitry Andric   std::vector<std::string> m_choices;
1626344a3780SDimitry Andric   // The index of the selected choice.
1627145449b1SDimitry Andric   int m_choice = 0;
1628344a3780SDimitry Andric   // The index of the first visible choice in the field.
1629145449b1SDimitry Andric   int m_first_visibile_choice = 0;
1630344a3780SDimitry Andric };
1631344a3780SDimitry Andric 
1632344a3780SDimitry Andric class PlatformPluginFieldDelegate : public ChoicesFieldDelegate {
1633344a3780SDimitry Andric public:
PlatformPluginFieldDelegate(Debugger & debugger)1634344a3780SDimitry Andric   PlatformPluginFieldDelegate(Debugger &debugger)
1635344a3780SDimitry Andric       : ChoicesFieldDelegate("Platform Plugin", 3, GetPossiblePluginNames()) {
1636344a3780SDimitry Andric     PlatformSP platform_sp = debugger.GetPlatformList().GetSelectedPlatform();
1637344a3780SDimitry Andric     if (platform_sp)
1638145449b1SDimitry Andric       SetChoice(platform_sp->GetPluginName());
1639344a3780SDimitry Andric   }
1640344a3780SDimitry Andric 
GetPossiblePluginNames()1641344a3780SDimitry Andric   std::vector<std::string> GetPossiblePluginNames() {
1642344a3780SDimitry Andric     std::vector<std::string> names;
1643344a3780SDimitry Andric     size_t i = 0;
1644c0981da4SDimitry Andric     for (llvm::StringRef name =
1645c0981da4SDimitry Andric              PluginManager::GetPlatformPluginNameAtIndex(i++);
1646c0981da4SDimitry Andric          !name.empty(); name = PluginManager::GetProcessPluginNameAtIndex(i++))
1647c0981da4SDimitry Andric       names.push_back(name.str());
1648344a3780SDimitry Andric     return names;
1649344a3780SDimitry Andric   }
1650344a3780SDimitry Andric 
GetPluginName()1651344a3780SDimitry Andric   std::string GetPluginName() {
1652344a3780SDimitry Andric     std::string plugin_name = GetChoiceContent();
1653344a3780SDimitry Andric     return plugin_name;
1654344a3780SDimitry Andric   }
1655344a3780SDimitry Andric };
1656344a3780SDimitry Andric 
1657344a3780SDimitry Andric class ProcessPluginFieldDelegate : public ChoicesFieldDelegate {
1658344a3780SDimitry Andric public:
ProcessPluginFieldDelegate()1659344a3780SDimitry Andric   ProcessPluginFieldDelegate()
1660344a3780SDimitry Andric       : ChoicesFieldDelegate("Process Plugin", 3, GetPossiblePluginNames()) {}
1661344a3780SDimitry Andric 
GetPossiblePluginNames()1662344a3780SDimitry Andric   std::vector<std::string> GetPossiblePluginNames() {
1663344a3780SDimitry Andric     std::vector<std::string> names;
1664344a3780SDimitry Andric     names.push_back("<default>");
1665344a3780SDimitry Andric 
1666344a3780SDimitry Andric     size_t i = 0;
1667c0981da4SDimitry Andric     for (llvm::StringRef name = PluginManager::GetProcessPluginNameAtIndex(i++);
1668c0981da4SDimitry Andric          !name.empty(); name = PluginManager::GetProcessPluginNameAtIndex(i++))
1669c0981da4SDimitry Andric       names.push_back(name.str());
1670344a3780SDimitry Andric     return names;
1671344a3780SDimitry Andric   }
1672344a3780SDimitry Andric 
GetPluginName()1673344a3780SDimitry Andric   std::string GetPluginName() {
1674344a3780SDimitry Andric     std::string plugin_name = GetChoiceContent();
1675344a3780SDimitry Andric     if (plugin_name == "<default>")
1676344a3780SDimitry Andric       return "";
1677344a3780SDimitry Andric     return plugin_name;
1678344a3780SDimitry Andric   }
1679344a3780SDimitry Andric };
1680344a3780SDimitry Andric 
1681c0981da4SDimitry Andric class LazyBooleanFieldDelegate : public ChoicesFieldDelegate {
1682c0981da4SDimitry Andric public:
LazyBooleanFieldDelegate(const char * label,const char * calculate_label)1683c0981da4SDimitry Andric   LazyBooleanFieldDelegate(const char *label, const char *calculate_label)
1684c0981da4SDimitry Andric       : ChoicesFieldDelegate(label, 3, GetPossibleOptions(calculate_label)) {}
1685c0981da4SDimitry Andric 
1686c0981da4SDimitry Andric   static constexpr const char *kNo = "No";
1687c0981da4SDimitry Andric   static constexpr const char *kYes = "Yes";
1688c0981da4SDimitry Andric 
GetPossibleOptions(const char * calculate_label)1689c0981da4SDimitry Andric   std::vector<std::string> GetPossibleOptions(const char *calculate_label) {
1690c0981da4SDimitry Andric     std::vector<std::string> options;
1691c0981da4SDimitry Andric     options.push_back(calculate_label);
1692c0981da4SDimitry Andric     options.push_back(kYes);
1693c0981da4SDimitry Andric     options.push_back(kNo);
1694c0981da4SDimitry Andric     return options;
1695c0981da4SDimitry Andric   }
1696c0981da4SDimitry Andric 
GetLazyBoolean()1697c0981da4SDimitry Andric   LazyBool GetLazyBoolean() {
1698c0981da4SDimitry Andric     std::string choice = GetChoiceContent();
1699c0981da4SDimitry Andric     if (choice == kNo)
1700c0981da4SDimitry Andric       return eLazyBoolNo;
1701c0981da4SDimitry Andric     else if (choice == kYes)
1702c0981da4SDimitry Andric       return eLazyBoolYes;
1703c0981da4SDimitry Andric     else
1704c0981da4SDimitry Andric       return eLazyBoolCalculate;
1705c0981da4SDimitry Andric   }
1706c0981da4SDimitry Andric };
1707c0981da4SDimitry Andric 
1708344a3780SDimitry Andric template <class T> class ListFieldDelegate : public FieldDelegate {
1709344a3780SDimitry Andric public:
ListFieldDelegate(const char * label,T default_field)1710344a3780SDimitry Andric   ListFieldDelegate(const char *label, T default_field)
1711145449b1SDimitry Andric       : m_label(label), m_default_field(default_field),
1712344a3780SDimitry Andric         m_selection_type(SelectionType::NewButton) {}
1713344a3780SDimitry Andric 
1714344a3780SDimitry Andric   // Signify which element is selected. If a field or a remove button is
1715344a3780SDimitry Andric   // selected, then m_selection_index signifies the particular field that
1716344a3780SDimitry Andric   // is selected or the field that the remove button belongs to.
1717344a3780SDimitry Andric   enum class SelectionType { Field, RemoveButton, NewButton };
1718344a3780SDimitry Andric 
1719344a3780SDimitry Andric   // A List field is drawn as a titled box of a number of other fields of the
1720344a3780SDimitry Andric   // same type. Each field has a Remove button next to it that removes the
1721344a3780SDimitry Andric   // corresponding field. Finally, the last line contains a New button to add a
1722344a3780SDimitry Andric   // new field.
1723344a3780SDimitry Andric   //
1724344a3780SDimitry Andric   // __[Label]___________
1725344a3780SDimitry Andric   // | Field 0 [Remove] |
1726344a3780SDimitry Andric   // | Field 1 [Remove] |
1727344a3780SDimitry Andric   // | Field 2 [Remove] |
1728344a3780SDimitry Andric   // |      [New]       |
1729344a3780SDimitry Andric   // |__________________|
1730344a3780SDimitry Andric 
1731344a3780SDimitry Andric   // List fields have two lines for border characters, 1 line for the New
1732344a3780SDimitry Andric   // button, and the total height of the available fields.
FieldDelegateGetHeight()1733344a3780SDimitry Andric   int FieldDelegateGetHeight() override {
1734344a3780SDimitry Andric     // 2 border characters.
1735344a3780SDimitry Andric     int height = 2;
1736344a3780SDimitry Andric     // Total height of the fields.
1737344a3780SDimitry Andric     for (int i = 0; i < GetNumberOfFields(); i++) {
1738344a3780SDimitry Andric       height += m_fields[i].FieldDelegateGetHeight();
1739344a3780SDimitry Andric     }
1740344a3780SDimitry Andric     // A line for the New button.
1741344a3780SDimitry Andric     height++;
1742344a3780SDimitry Andric     return height;
1743344a3780SDimitry Andric   }
1744344a3780SDimitry Andric 
FieldDelegateGetScrollContext()1745344a3780SDimitry Andric   ScrollContext FieldDelegateGetScrollContext() override {
1746344a3780SDimitry Andric     int height = FieldDelegateGetHeight();
1747344a3780SDimitry Andric     if (m_selection_type == SelectionType::NewButton)
1748344a3780SDimitry Andric       return ScrollContext(height - 2, height - 1);
1749344a3780SDimitry Andric 
1750344a3780SDimitry Andric     FieldDelegate &field = m_fields[m_selection_index];
1751344a3780SDimitry Andric     ScrollContext context = field.FieldDelegateGetScrollContext();
1752344a3780SDimitry Andric 
1753344a3780SDimitry Andric     // Start at 1 because of the top border.
1754344a3780SDimitry Andric     int offset = 1;
1755344a3780SDimitry Andric     for (int i = 0; i < m_selection_index; i++) {
1756344a3780SDimitry Andric       offset += m_fields[i].FieldDelegateGetHeight();
1757344a3780SDimitry Andric     }
1758344a3780SDimitry Andric     context.Offset(offset);
1759344a3780SDimitry Andric 
1760344a3780SDimitry Andric     // If the scroll context is touching the top border, include it in the
1761344a3780SDimitry Andric     // context to show the label.
1762344a3780SDimitry Andric     if (context.start == 1)
1763344a3780SDimitry Andric       context.start--;
1764344a3780SDimitry Andric 
1765344a3780SDimitry Andric     // If the scroll context is touching the new button, include it as well as
1766344a3780SDimitry Andric     // the bottom border in the context.
1767344a3780SDimitry Andric     if (context.end == height - 3)
1768344a3780SDimitry Andric       context.end += 2;
1769344a3780SDimitry Andric 
1770344a3780SDimitry Andric     return context;
1771344a3780SDimitry Andric   }
1772344a3780SDimitry Andric 
DrawRemoveButton(Surface & surface,int highlight)1773c0981da4SDimitry Andric   void DrawRemoveButton(Surface &surface, int highlight) {
1774344a3780SDimitry Andric     surface.MoveCursor(1, surface.GetHeight() / 2);
1775344a3780SDimitry Andric     if (highlight)
1776344a3780SDimitry Andric       surface.AttributeOn(A_REVERSE);
1777344a3780SDimitry Andric     surface.PutCString("[Remove]");
1778344a3780SDimitry Andric     if (highlight)
1779344a3780SDimitry Andric       surface.AttributeOff(A_REVERSE);
1780344a3780SDimitry Andric   }
1781344a3780SDimitry Andric 
DrawFields(Surface & surface,bool is_selected)1782c0981da4SDimitry Andric   void DrawFields(Surface &surface, bool is_selected) {
1783344a3780SDimitry Andric     int line = 0;
1784344a3780SDimitry Andric     int width = surface.GetWidth();
1785344a3780SDimitry Andric     for (int i = 0; i < GetNumberOfFields(); i++) {
1786344a3780SDimitry Andric       int height = m_fields[i].FieldDelegateGetHeight();
1787344a3780SDimitry Andric       Rect bounds = Rect(Point(0, line), Size(width, height));
1788344a3780SDimitry Andric       Rect field_bounds, remove_button_bounds;
1789344a3780SDimitry Andric       bounds.VerticalSplit(bounds.size.width - sizeof(" [Remove]"),
1790344a3780SDimitry Andric                            field_bounds, remove_button_bounds);
1791c0981da4SDimitry Andric       Surface field_surface = surface.SubSurface(field_bounds);
1792c0981da4SDimitry Andric       Surface remove_button_surface = surface.SubSurface(remove_button_bounds);
1793344a3780SDimitry Andric 
1794344a3780SDimitry Andric       bool is_element_selected = m_selection_index == i && is_selected;
1795344a3780SDimitry Andric       bool is_field_selected =
1796344a3780SDimitry Andric           is_element_selected && m_selection_type == SelectionType::Field;
1797344a3780SDimitry Andric       bool is_remove_button_selected =
1798344a3780SDimitry Andric           is_element_selected &&
1799344a3780SDimitry Andric           m_selection_type == SelectionType::RemoveButton;
1800344a3780SDimitry Andric       m_fields[i].FieldDelegateDraw(field_surface, is_field_selected);
1801344a3780SDimitry Andric       DrawRemoveButton(remove_button_surface, is_remove_button_selected);
1802344a3780SDimitry Andric 
1803344a3780SDimitry Andric       line += height;
1804344a3780SDimitry Andric     }
1805344a3780SDimitry Andric   }
1806344a3780SDimitry Andric 
DrawNewButton(Surface & surface,bool is_selected)1807c0981da4SDimitry Andric   void DrawNewButton(Surface &surface, bool is_selected) {
1808344a3780SDimitry Andric     const char *button_text = "[New]";
1809344a3780SDimitry Andric     int x = (surface.GetWidth() - sizeof(button_text) - 1) / 2;
1810344a3780SDimitry Andric     surface.MoveCursor(x, 0);
1811344a3780SDimitry Andric     bool highlight =
1812344a3780SDimitry Andric         is_selected && m_selection_type == SelectionType::NewButton;
1813344a3780SDimitry Andric     if (highlight)
1814344a3780SDimitry Andric       surface.AttributeOn(A_REVERSE);
1815344a3780SDimitry Andric     surface.PutCString(button_text);
1816344a3780SDimitry Andric     if (highlight)
1817344a3780SDimitry Andric       surface.AttributeOff(A_REVERSE);
1818344a3780SDimitry Andric   }
1819344a3780SDimitry Andric 
FieldDelegateDraw(Surface & surface,bool is_selected)1820c0981da4SDimitry Andric   void FieldDelegateDraw(Surface &surface, bool is_selected) override {
1821344a3780SDimitry Andric     surface.TitledBox(m_label.c_str());
1822344a3780SDimitry Andric 
1823344a3780SDimitry Andric     Rect content_bounds = surface.GetFrame();
1824344a3780SDimitry Andric     content_bounds.Inset(1, 1);
1825344a3780SDimitry Andric     Rect fields_bounds, new_button_bounds;
1826344a3780SDimitry Andric     content_bounds.HorizontalSplit(content_bounds.size.height - 1,
1827344a3780SDimitry Andric                                    fields_bounds, new_button_bounds);
1828c0981da4SDimitry Andric     Surface fields_surface = surface.SubSurface(fields_bounds);
1829c0981da4SDimitry Andric     Surface new_button_surface = surface.SubSurface(new_button_bounds);
1830344a3780SDimitry Andric 
1831344a3780SDimitry Andric     DrawFields(fields_surface, is_selected);
1832344a3780SDimitry Andric     DrawNewButton(new_button_surface, is_selected);
1833344a3780SDimitry Andric   }
1834344a3780SDimitry Andric 
AddNewField()1835344a3780SDimitry Andric   void AddNewField() {
1836344a3780SDimitry Andric     m_fields.push_back(m_default_field);
1837344a3780SDimitry Andric     m_selection_index = GetNumberOfFields() - 1;
1838344a3780SDimitry Andric     m_selection_type = SelectionType::Field;
1839344a3780SDimitry Andric     FieldDelegate &field = m_fields[m_selection_index];
1840344a3780SDimitry Andric     field.FieldDelegateSelectFirstElement();
1841344a3780SDimitry Andric   }
1842344a3780SDimitry Andric 
RemoveField()1843344a3780SDimitry Andric   void RemoveField() {
1844344a3780SDimitry Andric     m_fields.erase(m_fields.begin() + m_selection_index);
1845344a3780SDimitry Andric     if (m_selection_index != 0)
1846344a3780SDimitry Andric       m_selection_index--;
1847344a3780SDimitry Andric 
1848344a3780SDimitry Andric     if (GetNumberOfFields() > 0) {
1849344a3780SDimitry Andric       m_selection_type = SelectionType::Field;
1850344a3780SDimitry Andric       FieldDelegate &field = m_fields[m_selection_index];
1851344a3780SDimitry Andric       field.FieldDelegateSelectFirstElement();
1852344a3780SDimitry Andric     } else
1853344a3780SDimitry Andric       m_selection_type = SelectionType::NewButton;
1854344a3780SDimitry Andric   }
1855344a3780SDimitry Andric 
SelectNext(int key)1856344a3780SDimitry Andric   HandleCharResult SelectNext(int key) {
1857344a3780SDimitry Andric     if (m_selection_type == SelectionType::NewButton)
1858344a3780SDimitry Andric       return eKeyNotHandled;
1859344a3780SDimitry Andric 
1860344a3780SDimitry Andric     if (m_selection_type == SelectionType::RemoveButton) {
1861344a3780SDimitry Andric       if (m_selection_index == GetNumberOfFields() - 1) {
1862344a3780SDimitry Andric         m_selection_type = SelectionType::NewButton;
1863344a3780SDimitry Andric         return eKeyHandled;
1864344a3780SDimitry Andric       }
1865344a3780SDimitry Andric       m_selection_index++;
1866344a3780SDimitry Andric       m_selection_type = SelectionType::Field;
1867344a3780SDimitry Andric       FieldDelegate &next_field = m_fields[m_selection_index];
1868344a3780SDimitry Andric       next_field.FieldDelegateSelectFirstElement();
1869344a3780SDimitry Andric       return eKeyHandled;
1870344a3780SDimitry Andric     }
1871344a3780SDimitry Andric 
1872344a3780SDimitry Andric     FieldDelegate &field = m_fields[m_selection_index];
1873344a3780SDimitry Andric     if (!field.FieldDelegateOnLastOrOnlyElement()) {
1874344a3780SDimitry Andric       return field.FieldDelegateHandleChar(key);
1875344a3780SDimitry Andric     }
1876344a3780SDimitry Andric 
1877344a3780SDimitry Andric     field.FieldDelegateExitCallback();
1878344a3780SDimitry Andric 
1879344a3780SDimitry Andric     m_selection_type = SelectionType::RemoveButton;
1880344a3780SDimitry Andric     return eKeyHandled;
1881344a3780SDimitry Andric   }
1882344a3780SDimitry Andric 
SelectPrevious(int key)1883344a3780SDimitry Andric   HandleCharResult SelectPrevious(int key) {
1884344a3780SDimitry Andric     if (FieldDelegateOnFirstOrOnlyElement())
1885344a3780SDimitry Andric       return eKeyNotHandled;
1886344a3780SDimitry Andric 
1887344a3780SDimitry Andric     if (m_selection_type == SelectionType::RemoveButton) {
1888344a3780SDimitry Andric       m_selection_type = SelectionType::Field;
1889344a3780SDimitry Andric       FieldDelegate &field = m_fields[m_selection_index];
1890344a3780SDimitry Andric       field.FieldDelegateSelectLastElement();
1891344a3780SDimitry Andric       return eKeyHandled;
1892344a3780SDimitry Andric     }
1893344a3780SDimitry Andric 
1894344a3780SDimitry Andric     if (m_selection_type == SelectionType::NewButton) {
1895344a3780SDimitry Andric       m_selection_type = SelectionType::RemoveButton;
1896344a3780SDimitry Andric       m_selection_index = GetNumberOfFields() - 1;
1897344a3780SDimitry Andric       return eKeyHandled;
1898344a3780SDimitry Andric     }
1899344a3780SDimitry Andric 
1900344a3780SDimitry Andric     FieldDelegate &field = m_fields[m_selection_index];
1901344a3780SDimitry Andric     if (!field.FieldDelegateOnFirstOrOnlyElement()) {
1902344a3780SDimitry Andric       return field.FieldDelegateHandleChar(key);
1903344a3780SDimitry Andric     }
1904344a3780SDimitry Andric 
1905344a3780SDimitry Andric     field.FieldDelegateExitCallback();
1906344a3780SDimitry Andric 
1907344a3780SDimitry Andric     m_selection_type = SelectionType::RemoveButton;
1908344a3780SDimitry Andric     m_selection_index--;
1909344a3780SDimitry Andric     return eKeyHandled;
1910344a3780SDimitry Andric   }
1911344a3780SDimitry Andric 
1912c0981da4SDimitry Andric   // If the last element of the field is selected and it didn't handle the key.
1913c0981da4SDimitry Andric   // Select the next field or new button if the selected field is the last one.
SelectNextInList(int key)1914c0981da4SDimitry Andric   HandleCharResult SelectNextInList(int key) {
1915c0981da4SDimitry Andric     assert(m_selection_type == SelectionType::Field);
1916c0981da4SDimitry Andric 
1917c0981da4SDimitry Andric     FieldDelegate &field = m_fields[m_selection_index];
1918c0981da4SDimitry Andric     if (field.FieldDelegateHandleChar(key) == eKeyHandled)
1919c0981da4SDimitry Andric       return eKeyHandled;
1920c0981da4SDimitry Andric 
1921c0981da4SDimitry Andric     if (!field.FieldDelegateOnLastOrOnlyElement())
1922c0981da4SDimitry Andric       return eKeyNotHandled;
1923c0981da4SDimitry Andric 
1924c0981da4SDimitry Andric     field.FieldDelegateExitCallback();
1925c0981da4SDimitry Andric 
1926c0981da4SDimitry Andric     if (m_selection_index == GetNumberOfFields() - 1) {
1927c0981da4SDimitry Andric       m_selection_type = SelectionType::NewButton;
1928c0981da4SDimitry Andric       return eKeyHandled;
1929c0981da4SDimitry Andric     }
1930c0981da4SDimitry Andric 
1931c0981da4SDimitry Andric     m_selection_index++;
1932c0981da4SDimitry Andric     FieldDelegate &next_field = m_fields[m_selection_index];
1933c0981da4SDimitry Andric     next_field.FieldDelegateSelectFirstElement();
1934c0981da4SDimitry Andric     return eKeyHandled;
1935c0981da4SDimitry Andric   }
1936c0981da4SDimitry Andric 
FieldDelegateHandleChar(int key)1937344a3780SDimitry Andric   HandleCharResult FieldDelegateHandleChar(int key) override {
1938344a3780SDimitry Andric     switch (key) {
1939344a3780SDimitry Andric     case '\r':
1940344a3780SDimitry Andric     case '\n':
1941344a3780SDimitry Andric     case KEY_ENTER:
1942344a3780SDimitry Andric       switch (m_selection_type) {
1943344a3780SDimitry Andric       case SelectionType::NewButton:
1944344a3780SDimitry Andric         AddNewField();
1945344a3780SDimitry Andric         return eKeyHandled;
1946344a3780SDimitry Andric       case SelectionType::RemoveButton:
1947344a3780SDimitry Andric         RemoveField();
1948344a3780SDimitry Andric         return eKeyHandled;
1949c0981da4SDimitry Andric       case SelectionType::Field:
1950c0981da4SDimitry Andric         return SelectNextInList(key);
1951344a3780SDimitry Andric       }
1952344a3780SDimitry Andric       break;
1953344a3780SDimitry Andric     case '\t':
1954c0981da4SDimitry Andric       return SelectNext(key);
1955344a3780SDimitry Andric     case KEY_SHIFT_TAB:
1956c0981da4SDimitry Andric       return SelectPrevious(key);
1957344a3780SDimitry Andric     default:
1958344a3780SDimitry Andric       break;
1959344a3780SDimitry Andric     }
1960344a3780SDimitry Andric 
1961344a3780SDimitry Andric     // If the key wasn't handled and one of the fields is selected, pass the key
1962344a3780SDimitry Andric     // to that field.
1963344a3780SDimitry Andric     if (m_selection_type == SelectionType::Field) {
1964344a3780SDimitry Andric       return m_fields[m_selection_index].FieldDelegateHandleChar(key);
1965344a3780SDimitry Andric     }
1966344a3780SDimitry Andric 
1967344a3780SDimitry Andric     return eKeyNotHandled;
1968344a3780SDimitry Andric   }
1969344a3780SDimitry Andric 
FieldDelegateOnLastOrOnlyElement()1970344a3780SDimitry Andric   bool FieldDelegateOnLastOrOnlyElement() override {
1971344a3780SDimitry Andric     if (m_selection_type == SelectionType::NewButton) {
1972344a3780SDimitry Andric       return true;
1973344a3780SDimitry Andric     }
1974344a3780SDimitry Andric     return false;
1975344a3780SDimitry Andric   }
1976344a3780SDimitry Andric 
FieldDelegateOnFirstOrOnlyElement()1977344a3780SDimitry Andric   bool FieldDelegateOnFirstOrOnlyElement() override {
1978344a3780SDimitry Andric     if (m_selection_type == SelectionType::NewButton &&
1979344a3780SDimitry Andric         GetNumberOfFields() == 0)
1980344a3780SDimitry Andric       return true;
1981344a3780SDimitry Andric 
1982344a3780SDimitry Andric     if (m_selection_type == SelectionType::Field && m_selection_index == 0) {
1983344a3780SDimitry Andric       FieldDelegate &field = m_fields[m_selection_index];
1984344a3780SDimitry Andric       return field.FieldDelegateOnFirstOrOnlyElement();
1985344a3780SDimitry Andric     }
1986344a3780SDimitry Andric 
1987344a3780SDimitry Andric     return false;
1988344a3780SDimitry Andric   }
1989344a3780SDimitry Andric 
FieldDelegateSelectFirstElement()1990344a3780SDimitry Andric   void FieldDelegateSelectFirstElement() override {
1991344a3780SDimitry Andric     if (GetNumberOfFields() == 0) {
1992344a3780SDimitry Andric       m_selection_type = SelectionType::NewButton;
1993344a3780SDimitry Andric       return;
1994344a3780SDimitry Andric     }
1995344a3780SDimitry Andric 
1996344a3780SDimitry Andric     m_selection_type = SelectionType::Field;
1997344a3780SDimitry Andric     m_selection_index = 0;
1998344a3780SDimitry Andric   }
1999344a3780SDimitry Andric 
FieldDelegateSelectLastElement()2000344a3780SDimitry Andric   void FieldDelegateSelectLastElement() override {
2001344a3780SDimitry Andric     m_selection_type = SelectionType::NewButton;
2002344a3780SDimitry Andric   }
2003344a3780SDimitry Andric 
GetNumberOfFields()2004344a3780SDimitry Andric   int GetNumberOfFields() { return m_fields.size(); }
2005344a3780SDimitry Andric 
2006344a3780SDimitry Andric   // Returns the form delegate at the current index.
GetField(int index)2007344a3780SDimitry Andric   T &GetField(int index) { return m_fields[index]; }
2008344a3780SDimitry Andric 
2009344a3780SDimitry Andric protected:
2010344a3780SDimitry Andric   std::string m_label;
2011344a3780SDimitry Andric   // The default field delegate instance from which new field delegates will be
2012344a3780SDimitry Andric   // created though a copy.
2013344a3780SDimitry Andric   T m_default_field;
2014344a3780SDimitry Andric   std::vector<T> m_fields;
2015145449b1SDimitry Andric   int m_selection_index = 0;
2016344a3780SDimitry Andric   // See SelectionType class enum.
2017344a3780SDimitry Andric   SelectionType m_selection_type;
2018344a3780SDimitry Andric };
2019344a3780SDimitry Andric 
2020c0981da4SDimitry Andric class ArgumentsFieldDelegate : public ListFieldDelegate<TextFieldDelegate> {
2021c0981da4SDimitry Andric public:
ArgumentsFieldDelegate()2022c0981da4SDimitry Andric   ArgumentsFieldDelegate()
2023c0981da4SDimitry Andric       : ListFieldDelegate("Arguments",
2024c0981da4SDimitry Andric                           TextFieldDelegate("Argument", "", false)) {}
2025c0981da4SDimitry Andric 
GetArguments()2026c0981da4SDimitry Andric   Args GetArguments() {
2027c0981da4SDimitry Andric     Args arguments;
2028c0981da4SDimitry Andric     for (int i = 0; i < GetNumberOfFields(); i++) {
2029c0981da4SDimitry Andric       arguments.AppendArgument(GetField(i).GetText());
2030c0981da4SDimitry Andric     }
2031c0981da4SDimitry Andric     return arguments;
2032c0981da4SDimitry Andric   }
2033c0981da4SDimitry Andric 
AddArguments(const Args & arguments)2034c0981da4SDimitry Andric   void AddArguments(const Args &arguments) {
2035c0981da4SDimitry Andric     for (size_t i = 0; i < arguments.GetArgumentCount(); i++) {
2036c0981da4SDimitry Andric       AddNewField();
2037c0981da4SDimitry Andric       TextFieldDelegate &field = GetField(GetNumberOfFields() - 1);
2038c0981da4SDimitry Andric       field.SetText(arguments.GetArgumentAtIndex(i));
2039c0981da4SDimitry Andric     }
2040c0981da4SDimitry Andric   }
2041c0981da4SDimitry Andric };
2042c0981da4SDimitry Andric 
2043c0981da4SDimitry Andric template <class KeyFieldDelegateType, class ValueFieldDelegateType>
2044c0981da4SDimitry Andric class MappingFieldDelegate : public FieldDelegate {
2045c0981da4SDimitry Andric public:
MappingFieldDelegate(KeyFieldDelegateType key_field,ValueFieldDelegateType value_field)2046c0981da4SDimitry Andric   MappingFieldDelegate(KeyFieldDelegateType key_field,
2047c0981da4SDimitry Andric                        ValueFieldDelegateType value_field)
2048c0981da4SDimitry Andric       : m_key_field(key_field), m_value_field(value_field),
2049c0981da4SDimitry Andric         m_selection_type(SelectionType::Key) {}
2050c0981da4SDimitry Andric 
2051c0981da4SDimitry Andric   // Signify which element is selected. The key field or its value field.
2052c0981da4SDimitry Andric   enum class SelectionType { Key, Value };
2053c0981da4SDimitry Andric 
2054c0981da4SDimitry Andric   // A mapping field is drawn as two text fields with a right arrow in between.
2055c0981da4SDimitry Andric   // The first field stores the key of the mapping and the second stores the
2056c0981da4SDimitry Andric   // value if the mapping.
2057c0981da4SDimitry Andric   //
2058c0981da4SDimitry Andric   // __[Key]_____________   __[Value]___________
2059c0981da4SDimitry Andric   // |                  | > |                  |
2060c0981da4SDimitry Andric   // |__________________|   |__________________|
2061c0981da4SDimitry Andric   // - Error message if it exists.
2062c0981da4SDimitry Andric 
2063c0981da4SDimitry Andric   // The mapping field has a height that is equal to the maximum height between
2064c0981da4SDimitry Andric   // the key and value fields.
FieldDelegateGetHeight()2065c0981da4SDimitry Andric   int FieldDelegateGetHeight() override {
2066c0981da4SDimitry Andric     return std::max(m_key_field.FieldDelegateGetHeight(),
2067c0981da4SDimitry Andric                     m_value_field.FieldDelegateGetHeight());
2068c0981da4SDimitry Andric   }
2069c0981da4SDimitry Andric 
DrawArrow(Surface & surface)2070c0981da4SDimitry Andric   void DrawArrow(Surface &surface) {
2071c0981da4SDimitry Andric     surface.MoveCursor(0, 1);
2072c0981da4SDimitry Andric     surface.PutChar(ACS_RARROW);
2073c0981da4SDimitry Andric   }
2074c0981da4SDimitry Andric 
FieldDelegateDraw(Surface & surface,bool is_selected)2075c0981da4SDimitry Andric   void FieldDelegateDraw(Surface &surface, bool is_selected) override {
2076c0981da4SDimitry Andric     Rect bounds = surface.GetFrame();
2077c0981da4SDimitry Andric     Rect key_field_bounds, arrow_and_value_field_bounds;
2078c0981da4SDimitry Andric     bounds.VerticalSplit(bounds.size.width / 2, key_field_bounds,
2079c0981da4SDimitry Andric                          arrow_and_value_field_bounds);
2080c0981da4SDimitry Andric     Rect arrow_bounds, value_field_bounds;
2081c0981da4SDimitry Andric     arrow_and_value_field_bounds.VerticalSplit(1, arrow_bounds,
2082c0981da4SDimitry Andric                                                value_field_bounds);
2083c0981da4SDimitry Andric 
2084c0981da4SDimitry Andric     Surface key_field_surface = surface.SubSurface(key_field_bounds);
2085c0981da4SDimitry Andric     Surface arrow_surface = surface.SubSurface(arrow_bounds);
2086c0981da4SDimitry Andric     Surface value_field_surface = surface.SubSurface(value_field_bounds);
2087c0981da4SDimitry Andric 
2088c0981da4SDimitry Andric     bool key_is_selected =
2089c0981da4SDimitry Andric         m_selection_type == SelectionType::Key && is_selected;
2090c0981da4SDimitry Andric     m_key_field.FieldDelegateDraw(key_field_surface, key_is_selected);
2091c0981da4SDimitry Andric     DrawArrow(arrow_surface);
2092c0981da4SDimitry Andric     bool value_is_selected =
2093c0981da4SDimitry Andric         m_selection_type == SelectionType::Value && is_selected;
2094c0981da4SDimitry Andric     m_value_field.FieldDelegateDraw(value_field_surface, value_is_selected);
2095c0981da4SDimitry Andric   }
2096c0981da4SDimitry Andric 
SelectNext(int key)2097c0981da4SDimitry Andric   HandleCharResult SelectNext(int key) {
2098c0981da4SDimitry Andric     if (FieldDelegateOnLastOrOnlyElement())
2099c0981da4SDimitry Andric       return eKeyNotHandled;
2100c0981da4SDimitry Andric 
2101c0981da4SDimitry Andric     if (!m_key_field.FieldDelegateOnLastOrOnlyElement()) {
2102c0981da4SDimitry Andric       return m_key_field.FieldDelegateHandleChar(key);
2103c0981da4SDimitry Andric     }
2104c0981da4SDimitry Andric 
2105c0981da4SDimitry Andric     m_key_field.FieldDelegateExitCallback();
2106c0981da4SDimitry Andric     m_selection_type = SelectionType::Value;
2107c0981da4SDimitry Andric     m_value_field.FieldDelegateSelectFirstElement();
2108c0981da4SDimitry Andric     return eKeyHandled;
2109c0981da4SDimitry Andric   }
2110c0981da4SDimitry Andric 
SelectPrevious(int key)2111c0981da4SDimitry Andric   HandleCharResult SelectPrevious(int key) {
2112c0981da4SDimitry Andric     if (FieldDelegateOnFirstOrOnlyElement())
2113c0981da4SDimitry Andric       return eKeyNotHandled;
2114c0981da4SDimitry Andric 
2115c0981da4SDimitry Andric     if (!m_value_field.FieldDelegateOnFirstOrOnlyElement()) {
2116c0981da4SDimitry Andric       return m_value_field.FieldDelegateHandleChar(key);
2117c0981da4SDimitry Andric     }
2118c0981da4SDimitry Andric 
2119c0981da4SDimitry Andric     m_value_field.FieldDelegateExitCallback();
2120c0981da4SDimitry Andric     m_selection_type = SelectionType::Key;
2121c0981da4SDimitry Andric     m_key_field.FieldDelegateSelectLastElement();
2122c0981da4SDimitry Andric     return eKeyHandled;
2123c0981da4SDimitry Andric   }
2124c0981da4SDimitry Andric 
2125c0981da4SDimitry Andric   // If the value field is selected, pass the key to it. If the key field is
2126c0981da4SDimitry Andric   // selected, its last element is selected, and it didn't handle the key, then
2127c0981da4SDimitry Andric   // select its corresponding value field.
SelectNextField(int key)2128c0981da4SDimitry Andric   HandleCharResult SelectNextField(int key) {
2129c0981da4SDimitry Andric     if (m_selection_type == SelectionType::Value) {
2130c0981da4SDimitry Andric       return m_value_field.FieldDelegateHandleChar(key);
2131c0981da4SDimitry Andric     }
2132c0981da4SDimitry Andric 
2133c0981da4SDimitry Andric     if (m_key_field.FieldDelegateHandleChar(key) == eKeyHandled)
2134c0981da4SDimitry Andric       return eKeyHandled;
2135c0981da4SDimitry Andric 
2136c0981da4SDimitry Andric     if (!m_key_field.FieldDelegateOnLastOrOnlyElement())
2137c0981da4SDimitry Andric       return eKeyNotHandled;
2138c0981da4SDimitry Andric 
2139c0981da4SDimitry Andric     m_key_field.FieldDelegateExitCallback();
2140c0981da4SDimitry Andric     m_selection_type = SelectionType::Value;
2141c0981da4SDimitry Andric     m_value_field.FieldDelegateSelectFirstElement();
2142c0981da4SDimitry Andric     return eKeyHandled;
2143c0981da4SDimitry Andric   }
2144c0981da4SDimitry Andric 
FieldDelegateHandleChar(int key)2145c0981da4SDimitry Andric   HandleCharResult FieldDelegateHandleChar(int key) override {
2146c0981da4SDimitry Andric     switch (key) {
2147c0981da4SDimitry Andric     case KEY_RETURN:
2148c0981da4SDimitry Andric       return SelectNextField(key);
2149c0981da4SDimitry Andric     case '\t':
2150c0981da4SDimitry Andric       return SelectNext(key);
2151c0981da4SDimitry Andric     case KEY_SHIFT_TAB:
2152c0981da4SDimitry Andric       return SelectPrevious(key);
2153c0981da4SDimitry Andric     default:
2154c0981da4SDimitry Andric       break;
2155c0981da4SDimitry Andric     }
2156c0981da4SDimitry Andric 
2157c0981da4SDimitry Andric     // If the key wasn't handled, pass the key to the selected field.
2158c0981da4SDimitry Andric     if (m_selection_type == SelectionType::Key)
2159c0981da4SDimitry Andric       return m_key_field.FieldDelegateHandleChar(key);
2160c0981da4SDimitry Andric     else
2161c0981da4SDimitry Andric       return m_value_field.FieldDelegateHandleChar(key);
2162c0981da4SDimitry Andric 
2163c0981da4SDimitry Andric     return eKeyNotHandled;
2164c0981da4SDimitry Andric   }
2165c0981da4SDimitry Andric 
FieldDelegateOnFirstOrOnlyElement()2166c0981da4SDimitry Andric   bool FieldDelegateOnFirstOrOnlyElement() override {
2167c0981da4SDimitry Andric     return m_selection_type == SelectionType::Key;
2168c0981da4SDimitry Andric   }
2169c0981da4SDimitry Andric 
FieldDelegateOnLastOrOnlyElement()2170c0981da4SDimitry Andric   bool FieldDelegateOnLastOrOnlyElement() override {
2171c0981da4SDimitry Andric     return m_selection_type == SelectionType::Value;
2172c0981da4SDimitry Andric   }
2173c0981da4SDimitry Andric 
FieldDelegateSelectFirstElement()2174c0981da4SDimitry Andric   void FieldDelegateSelectFirstElement() override {
2175c0981da4SDimitry Andric     m_selection_type = SelectionType::Key;
2176c0981da4SDimitry Andric   }
2177c0981da4SDimitry Andric 
FieldDelegateSelectLastElement()2178c0981da4SDimitry Andric   void FieldDelegateSelectLastElement() override {
2179c0981da4SDimitry Andric     m_selection_type = SelectionType::Value;
2180c0981da4SDimitry Andric   }
2181c0981da4SDimitry Andric 
FieldDelegateHasError()2182c0981da4SDimitry Andric   bool FieldDelegateHasError() override {
2183c0981da4SDimitry Andric     return m_key_field.FieldDelegateHasError() ||
2184c0981da4SDimitry Andric            m_value_field.FieldDelegateHasError();
2185c0981da4SDimitry Andric   }
2186c0981da4SDimitry Andric 
GetKeyField()2187c0981da4SDimitry Andric   KeyFieldDelegateType &GetKeyField() { return m_key_field; }
2188c0981da4SDimitry Andric 
GetValueField()2189c0981da4SDimitry Andric   ValueFieldDelegateType &GetValueField() { return m_value_field; }
2190c0981da4SDimitry Andric 
2191c0981da4SDimitry Andric protected:
2192c0981da4SDimitry Andric   KeyFieldDelegateType m_key_field;
2193c0981da4SDimitry Andric   ValueFieldDelegateType m_value_field;
2194c0981da4SDimitry Andric   // See SelectionType class enum.
2195c0981da4SDimitry Andric   SelectionType m_selection_type;
2196c0981da4SDimitry Andric };
2197c0981da4SDimitry Andric 
2198c0981da4SDimitry Andric class EnvironmentVariableNameFieldDelegate : public TextFieldDelegate {
2199c0981da4SDimitry Andric public:
EnvironmentVariableNameFieldDelegate(const char * content)2200c0981da4SDimitry Andric   EnvironmentVariableNameFieldDelegate(const char *content)
2201c0981da4SDimitry Andric       : TextFieldDelegate("Name", content, true) {}
2202c0981da4SDimitry Andric 
2203c0981da4SDimitry Andric   // Environment variable names can't contain an equal sign.
IsAcceptableChar(int key)2204c0981da4SDimitry Andric   bool IsAcceptableChar(int key) override {
2205c0981da4SDimitry Andric     return TextFieldDelegate::IsAcceptableChar(key) && key != '=';
2206c0981da4SDimitry Andric   }
2207c0981da4SDimitry Andric 
GetName()2208c0981da4SDimitry Andric   const std::string &GetName() { return m_content; }
2209c0981da4SDimitry Andric };
2210c0981da4SDimitry Andric 
2211c0981da4SDimitry Andric class EnvironmentVariableFieldDelegate
2212c0981da4SDimitry Andric     : public MappingFieldDelegate<EnvironmentVariableNameFieldDelegate,
2213c0981da4SDimitry Andric                                   TextFieldDelegate> {
2214c0981da4SDimitry Andric public:
EnvironmentVariableFieldDelegate()2215c0981da4SDimitry Andric   EnvironmentVariableFieldDelegate()
2216c0981da4SDimitry Andric       : MappingFieldDelegate(
2217c0981da4SDimitry Andric             EnvironmentVariableNameFieldDelegate(""),
2218c0981da4SDimitry Andric             TextFieldDelegate("Value", "", /*required=*/false)) {}
2219c0981da4SDimitry Andric 
GetName()2220c0981da4SDimitry Andric   const std::string &GetName() { return GetKeyField().GetName(); }
2221c0981da4SDimitry Andric 
GetValue()2222c0981da4SDimitry Andric   const std::string &GetValue() { return GetValueField().GetText(); }
2223c0981da4SDimitry Andric 
SetName(const char * name)2224c0981da4SDimitry Andric   void SetName(const char *name) { return GetKeyField().SetText(name); }
2225c0981da4SDimitry Andric 
SetValue(const char * value)2226c0981da4SDimitry Andric   void SetValue(const char *value) { return GetValueField().SetText(value); }
2227c0981da4SDimitry Andric };
2228c0981da4SDimitry Andric 
2229c0981da4SDimitry Andric class EnvironmentVariableListFieldDelegate
2230c0981da4SDimitry Andric     : public ListFieldDelegate<EnvironmentVariableFieldDelegate> {
2231c0981da4SDimitry Andric public:
EnvironmentVariableListFieldDelegate(const char * label)2232c0981da4SDimitry Andric   EnvironmentVariableListFieldDelegate(const char *label)
2233c0981da4SDimitry Andric       : ListFieldDelegate(label, EnvironmentVariableFieldDelegate()) {}
2234c0981da4SDimitry Andric 
GetEnvironment()2235c0981da4SDimitry Andric   Environment GetEnvironment() {
2236c0981da4SDimitry Andric     Environment environment;
2237c0981da4SDimitry Andric     for (int i = 0; i < GetNumberOfFields(); i++) {
2238c0981da4SDimitry Andric       environment.insert(
2239c0981da4SDimitry Andric           std::make_pair(GetField(i).GetName(), GetField(i).GetValue()));
2240c0981da4SDimitry Andric     }
2241c0981da4SDimitry Andric     return environment;
2242c0981da4SDimitry Andric   }
2243c0981da4SDimitry Andric 
AddEnvironmentVariables(const Environment & environment)2244c0981da4SDimitry Andric   void AddEnvironmentVariables(const Environment &environment) {
2245c0981da4SDimitry Andric     for (auto &variable : environment) {
2246c0981da4SDimitry Andric       AddNewField();
2247c0981da4SDimitry Andric       EnvironmentVariableFieldDelegate &field =
2248c0981da4SDimitry Andric           GetField(GetNumberOfFields() - 1);
2249c0981da4SDimitry Andric       field.SetName(variable.getKey().str().c_str());
2250c0981da4SDimitry Andric       field.SetValue(variable.getValue().c_str());
2251c0981da4SDimitry Andric     }
2252c0981da4SDimitry Andric   }
2253c0981da4SDimitry Andric };
2254c0981da4SDimitry Andric 
2255344a3780SDimitry Andric class FormAction {
2256344a3780SDimitry Andric public:
FormAction(const char * label,std::function<void (Window &)> action)2257344a3780SDimitry Andric   FormAction(const char *label, std::function<void(Window &)> action)
2258344a3780SDimitry Andric       : m_action(action) {
2259344a3780SDimitry Andric     if (label)
2260344a3780SDimitry Andric       m_label = label;
2261344a3780SDimitry Andric   }
2262344a3780SDimitry Andric 
2263344a3780SDimitry Andric   // Draw a centered [Label].
Draw(Surface & surface,bool is_selected)2264c0981da4SDimitry Andric   void Draw(Surface &surface, bool is_selected) {
2265344a3780SDimitry Andric     int x = (surface.GetWidth() - m_label.length()) / 2;
2266344a3780SDimitry Andric     surface.MoveCursor(x, 0);
2267344a3780SDimitry Andric     if (is_selected)
2268344a3780SDimitry Andric       surface.AttributeOn(A_REVERSE);
2269344a3780SDimitry Andric     surface.PutChar('[');
2270344a3780SDimitry Andric     surface.PutCString(m_label.c_str());
2271344a3780SDimitry Andric     surface.PutChar(']');
2272344a3780SDimitry Andric     if (is_selected)
2273344a3780SDimitry Andric       surface.AttributeOff(A_REVERSE);
2274344a3780SDimitry Andric   }
2275344a3780SDimitry Andric 
Execute(Window & window)2276344a3780SDimitry Andric   void Execute(Window &window) { m_action(window); }
2277344a3780SDimitry Andric 
GetLabel()2278344a3780SDimitry Andric   const std::string &GetLabel() { return m_label; }
2279344a3780SDimitry Andric 
2280344a3780SDimitry Andric protected:
2281344a3780SDimitry Andric   std::string m_label;
2282344a3780SDimitry Andric   std::function<void(Window &)> m_action;
2283344a3780SDimitry Andric };
2284344a3780SDimitry Andric 
2285344a3780SDimitry Andric class FormDelegate {
2286344a3780SDimitry Andric public:
2287145449b1SDimitry Andric   FormDelegate() = default;
2288344a3780SDimitry Andric 
2289344a3780SDimitry Andric   virtual ~FormDelegate() = default;
2290344a3780SDimitry Andric 
2291344a3780SDimitry Andric   virtual std::string GetName() = 0;
2292344a3780SDimitry Andric 
UpdateFieldsVisibility()229377fc4c14SDimitry Andric   virtual void UpdateFieldsVisibility() {}
2294344a3780SDimitry Andric 
GetField(uint32_t field_index)2295344a3780SDimitry Andric   FieldDelegate *GetField(uint32_t field_index) {
2296344a3780SDimitry Andric     if (field_index < m_fields.size())
2297344a3780SDimitry Andric       return m_fields[field_index].get();
2298344a3780SDimitry Andric     return nullptr;
2299344a3780SDimitry Andric   }
2300344a3780SDimitry Andric 
GetAction(int action_index)2301344a3780SDimitry Andric   FormAction &GetAction(int action_index) { return m_actions[action_index]; }
2302344a3780SDimitry Andric 
GetNumberOfFields()2303344a3780SDimitry Andric   int GetNumberOfFields() { return m_fields.size(); }
2304344a3780SDimitry Andric 
GetNumberOfActions()2305344a3780SDimitry Andric   int GetNumberOfActions() { return m_actions.size(); }
2306344a3780SDimitry Andric 
HasError()2307344a3780SDimitry Andric   bool HasError() { return !m_error.empty(); }
2308344a3780SDimitry Andric 
ClearError()2309344a3780SDimitry Andric   void ClearError() { m_error.clear(); }
2310344a3780SDimitry Andric 
GetError()2311344a3780SDimitry Andric   const std::string &GetError() { return m_error; }
2312344a3780SDimitry Andric 
SetError(const char * error)2313344a3780SDimitry Andric   void SetError(const char *error) { m_error = error; }
2314344a3780SDimitry Andric 
2315344a3780SDimitry Andric   // If all fields are valid, true is returned. Otherwise, an error message is
2316344a3780SDimitry Andric   // set and false is returned. This method is usually called at the start of an
2317344a3780SDimitry Andric   // action that requires valid fields.
CheckFieldsValidity()2318344a3780SDimitry Andric   bool CheckFieldsValidity() {
2319344a3780SDimitry Andric     for (int i = 0; i < GetNumberOfFields(); i++) {
2320c0981da4SDimitry Andric       GetField(i)->FieldDelegateExitCallback();
2321344a3780SDimitry Andric       if (GetField(i)->FieldDelegateHasError()) {
2322344a3780SDimitry Andric         SetError("Some fields are invalid!");
2323344a3780SDimitry Andric         return false;
2324344a3780SDimitry Andric       }
2325344a3780SDimitry Andric     }
2326344a3780SDimitry Andric     return true;
2327344a3780SDimitry Andric   }
2328344a3780SDimitry Andric 
2329344a3780SDimitry Andric   // Factory methods to create and add fields of specific types.
2330344a3780SDimitry Andric 
AddTextField(const char * label,const char * content,bool required)2331344a3780SDimitry Andric   TextFieldDelegate *AddTextField(const char *label, const char *content,
2332344a3780SDimitry Andric                                   bool required) {
2333344a3780SDimitry Andric     TextFieldDelegate *delegate =
2334344a3780SDimitry Andric         new TextFieldDelegate(label, content, required);
2335344a3780SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2336344a3780SDimitry Andric     return delegate;
2337344a3780SDimitry Andric   }
2338344a3780SDimitry Andric 
AddFileField(const char * label,const char * content,bool need_to_exist,bool required)2339344a3780SDimitry Andric   FileFieldDelegate *AddFileField(const char *label, const char *content,
2340344a3780SDimitry Andric                                   bool need_to_exist, bool required) {
2341344a3780SDimitry Andric     FileFieldDelegate *delegate =
2342344a3780SDimitry Andric         new FileFieldDelegate(label, content, need_to_exist, required);
2343344a3780SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2344344a3780SDimitry Andric     return delegate;
2345344a3780SDimitry Andric   }
2346344a3780SDimitry Andric 
AddDirectoryField(const char * label,const char * content,bool need_to_exist,bool required)2347344a3780SDimitry Andric   DirectoryFieldDelegate *AddDirectoryField(const char *label,
2348344a3780SDimitry Andric                                             const char *content,
2349344a3780SDimitry Andric                                             bool need_to_exist, bool required) {
2350344a3780SDimitry Andric     DirectoryFieldDelegate *delegate =
2351344a3780SDimitry Andric         new DirectoryFieldDelegate(label, content, need_to_exist, required);
2352344a3780SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2353344a3780SDimitry Andric     return delegate;
2354344a3780SDimitry Andric   }
2355344a3780SDimitry Andric 
AddArchField(const char * label,const char * content,bool required)2356344a3780SDimitry Andric   ArchFieldDelegate *AddArchField(const char *label, const char *content,
2357344a3780SDimitry Andric                                   bool required) {
2358344a3780SDimitry Andric     ArchFieldDelegate *delegate =
2359344a3780SDimitry Andric         new ArchFieldDelegate(label, content, required);
2360344a3780SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2361344a3780SDimitry Andric     return delegate;
2362344a3780SDimitry Andric   }
2363344a3780SDimitry Andric 
AddIntegerField(const char * label,int content,bool required)2364344a3780SDimitry Andric   IntegerFieldDelegate *AddIntegerField(const char *label, int content,
2365344a3780SDimitry Andric                                         bool required) {
2366344a3780SDimitry Andric     IntegerFieldDelegate *delegate =
2367344a3780SDimitry Andric         new IntegerFieldDelegate(label, content, required);
2368344a3780SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2369344a3780SDimitry Andric     return delegate;
2370344a3780SDimitry Andric   }
2371344a3780SDimitry Andric 
AddBooleanField(const char * label,bool content)2372344a3780SDimitry Andric   BooleanFieldDelegate *AddBooleanField(const char *label, bool content) {
2373344a3780SDimitry Andric     BooleanFieldDelegate *delegate = new BooleanFieldDelegate(label, content);
2374344a3780SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2375344a3780SDimitry Andric     return delegate;
2376344a3780SDimitry Andric   }
2377344a3780SDimitry Andric 
AddLazyBooleanField(const char * label,const char * calculate_label)2378c0981da4SDimitry Andric   LazyBooleanFieldDelegate *AddLazyBooleanField(const char *label,
2379c0981da4SDimitry Andric                                                 const char *calculate_label) {
2380c0981da4SDimitry Andric     LazyBooleanFieldDelegate *delegate =
2381c0981da4SDimitry Andric         new LazyBooleanFieldDelegate(label, calculate_label);
2382c0981da4SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2383c0981da4SDimitry Andric     return delegate;
2384c0981da4SDimitry Andric   }
2385c0981da4SDimitry Andric 
AddChoicesField(const char * label,int height,std::vector<std::string> choices)2386344a3780SDimitry Andric   ChoicesFieldDelegate *AddChoicesField(const char *label, int height,
2387344a3780SDimitry Andric                                         std::vector<std::string> choices) {
2388344a3780SDimitry Andric     ChoicesFieldDelegate *delegate =
2389344a3780SDimitry Andric         new ChoicesFieldDelegate(label, height, choices);
2390344a3780SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2391344a3780SDimitry Andric     return delegate;
2392344a3780SDimitry Andric   }
2393344a3780SDimitry Andric 
AddPlatformPluginField(Debugger & debugger)2394344a3780SDimitry Andric   PlatformPluginFieldDelegate *AddPlatformPluginField(Debugger &debugger) {
2395344a3780SDimitry Andric     PlatformPluginFieldDelegate *delegate =
2396344a3780SDimitry Andric         new PlatformPluginFieldDelegate(debugger);
2397344a3780SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2398344a3780SDimitry Andric     return delegate;
2399344a3780SDimitry Andric   }
2400344a3780SDimitry Andric 
AddProcessPluginField()2401344a3780SDimitry Andric   ProcessPluginFieldDelegate *AddProcessPluginField() {
2402344a3780SDimitry Andric     ProcessPluginFieldDelegate *delegate = new ProcessPluginFieldDelegate();
2403344a3780SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2404344a3780SDimitry Andric     return delegate;
2405344a3780SDimitry Andric   }
2406344a3780SDimitry Andric 
2407344a3780SDimitry Andric   template <class T>
AddListField(const char * label,T default_field)2408344a3780SDimitry Andric   ListFieldDelegate<T> *AddListField(const char *label, T default_field) {
2409344a3780SDimitry Andric     ListFieldDelegate<T> *delegate =
2410344a3780SDimitry Andric         new ListFieldDelegate<T>(label, default_field);
2411344a3780SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2412344a3780SDimitry Andric     return delegate;
2413344a3780SDimitry Andric   }
2414344a3780SDimitry Andric 
AddArgumentsField()2415c0981da4SDimitry Andric   ArgumentsFieldDelegate *AddArgumentsField() {
2416c0981da4SDimitry Andric     ArgumentsFieldDelegate *delegate = new ArgumentsFieldDelegate();
2417c0981da4SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2418c0981da4SDimitry Andric     return delegate;
2419c0981da4SDimitry Andric   }
2420c0981da4SDimitry Andric 
2421c0981da4SDimitry Andric   template <class K, class V>
AddMappingField(K key_field,V value_field)2422c0981da4SDimitry Andric   MappingFieldDelegate<K, V> *AddMappingField(K key_field, V value_field) {
2423c0981da4SDimitry Andric     MappingFieldDelegate<K, V> *delegate =
2424c0981da4SDimitry Andric         new MappingFieldDelegate<K, V>(key_field, value_field);
2425c0981da4SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2426c0981da4SDimitry Andric     return delegate;
2427c0981da4SDimitry Andric   }
2428c0981da4SDimitry Andric 
2429c0981da4SDimitry Andric   EnvironmentVariableNameFieldDelegate *
AddEnvironmentVariableNameField(const char * content)2430c0981da4SDimitry Andric   AddEnvironmentVariableNameField(const char *content) {
2431c0981da4SDimitry Andric     EnvironmentVariableNameFieldDelegate *delegate =
2432c0981da4SDimitry Andric         new EnvironmentVariableNameFieldDelegate(content);
2433c0981da4SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2434c0981da4SDimitry Andric     return delegate;
2435c0981da4SDimitry Andric   }
2436c0981da4SDimitry Andric 
AddEnvironmentVariableField()2437c0981da4SDimitry Andric   EnvironmentVariableFieldDelegate *AddEnvironmentVariableField() {
2438c0981da4SDimitry Andric     EnvironmentVariableFieldDelegate *delegate =
2439c0981da4SDimitry Andric         new EnvironmentVariableFieldDelegate();
2440c0981da4SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2441c0981da4SDimitry Andric     return delegate;
2442c0981da4SDimitry Andric   }
2443c0981da4SDimitry Andric 
2444c0981da4SDimitry Andric   EnvironmentVariableListFieldDelegate *
AddEnvironmentVariableListField(const char * label)2445c0981da4SDimitry Andric   AddEnvironmentVariableListField(const char *label) {
2446c0981da4SDimitry Andric     EnvironmentVariableListFieldDelegate *delegate =
2447c0981da4SDimitry Andric         new EnvironmentVariableListFieldDelegate(label);
2448c0981da4SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2449c0981da4SDimitry Andric     return delegate;
2450c0981da4SDimitry Andric   }
2451c0981da4SDimitry Andric 
2452344a3780SDimitry Andric   // Factory methods for adding actions.
2453344a3780SDimitry Andric 
AddAction(const char * label,std::function<void (Window &)> action)2454344a3780SDimitry Andric   void AddAction(const char *label, std::function<void(Window &)> action) {
2455344a3780SDimitry Andric     m_actions.push_back(FormAction(label, action));
2456344a3780SDimitry Andric   }
2457344a3780SDimitry Andric 
2458344a3780SDimitry Andric protected:
2459344a3780SDimitry Andric   std::vector<FieldDelegateUP> m_fields;
2460344a3780SDimitry Andric   std::vector<FormAction> m_actions;
2461344a3780SDimitry Andric   // Optional error message. If empty, form is considered to have no error.
2462344a3780SDimitry Andric   std::string m_error;
2463344a3780SDimitry Andric };
2464344a3780SDimitry Andric 
2465344a3780SDimitry Andric typedef std::shared_ptr<FormDelegate> FormDelegateSP;
2466344a3780SDimitry Andric 
2467344a3780SDimitry Andric class FormWindowDelegate : public WindowDelegate {
2468344a3780SDimitry Andric public:
FormWindowDelegate(FormDelegateSP & delegate_sp)2469145449b1SDimitry Andric   FormWindowDelegate(FormDelegateSP &delegate_sp) : m_delegate_sp(delegate_sp) {
2470344a3780SDimitry Andric     assert(m_delegate_sp->GetNumberOfActions() > 0);
2471344a3780SDimitry Andric     if (m_delegate_sp->GetNumberOfFields() > 0)
2472344a3780SDimitry Andric       m_selection_type = SelectionType::Field;
2473344a3780SDimitry Andric     else
2474344a3780SDimitry Andric       m_selection_type = SelectionType::Action;
2475344a3780SDimitry Andric   }
2476344a3780SDimitry Andric 
2477344a3780SDimitry Andric   // Signify which element is selected. If a field or an action is selected,
2478344a3780SDimitry Andric   // then m_selection_index signifies the particular field or action that is
2479344a3780SDimitry Andric   // selected.
2480344a3780SDimitry Andric   enum class SelectionType { Field, Action };
2481344a3780SDimitry Andric 
2482344a3780SDimitry Andric   // A form window is padded by one character from all sides. First, if an error
2483344a3780SDimitry Andric   // message exists, it is drawn followed by a separator. Then one or more
2484344a3780SDimitry Andric   // fields are drawn. Finally, all available actions are drawn on a single
2485344a3780SDimitry Andric   // line.
2486344a3780SDimitry Andric   //
2487344a3780SDimitry Andric   // ___<Form Name>_________________________________________________
2488344a3780SDimitry Andric   // |                                                             |
2489344a3780SDimitry Andric   // | - Error message if it exists.                               |
2490344a3780SDimitry Andric   // |-------------------------------------------------------------|
2491344a3780SDimitry Andric   // | Form elements here.                                         |
2492344a3780SDimitry Andric   // |                       Form actions here.                    |
2493344a3780SDimitry Andric   // |                                                             |
2494344a3780SDimitry Andric   // |______________________________________[Press Esc to cancel]__|
2495344a3780SDimitry Andric   //
2496344a3780SDimitry Andric 
2497344a3780SDimitry Andric   // One line for the error and another for the horizontal line.
GetErrorHeight()2498344a3780SDimitry Andric   int GetErrorHeight() {
2499344a3780SDimitry Andric     if (m_delegate_sp->HasError())
2500344a3780SDimitry Andric       return 2;
2501344a3780SDimitry Andric     return 0;
2502344a3780SDimitry Andric   }
2503344a3780SDimitry Andric 
2504344a3780SDimitry Andric   // Actions span a single line.
GetActionsHeight()2505344a3780SDimitry Andric   int GetActionsHeight() {
2506344a3780SDimitry Andric     if (m_delegate_sp->GetNumberOfActions() > 0)
2507344a3780SDimitry Andric       return 1;
2508344a3780SDimitry Andric     return 0;
2509344a3780SDimitry Andric   }
2510344a3780SDimitry Andric 
2511344a3780SDimitry Andric   // Get the total number of needed lines to draw the contents.
GetContentHeight()2512344a3780SDimitry Andric   int GetContentHeight() {
2513344a3780SDimitry Andric     int height = 0;
2514344a3780SDimitry Andric     height += GetErrorHeight();
2515344a3780SDimitry Andric     for (int i = 0; i < m_delegate_sp->GetNumberOfFields(); i++) {
2516344a3780SDimitry Andric       if (!m_delegate_sp->GetField(i)->FieldDelegateIsVisible())
2517344a3780SDimitry Andric         continue;
2518344a3780SDimitry Andric       height += m_delegate_sp->GetField(i)->FieldDelegateGetHeight();
2519344a3780SDimitry Andric     }
2520344a3780SDimitry Andric     height += GetActionsHeight();
2521344a3780SDimitry Andric     return height;
2522344a3780SDimitry Andric   }
2523344a3780SDimitry Andric 
GetScrollContext()2524344a3780SDimitry Andric   ScrollContext GetScrollContext() {
2525344a3780SDimitry Andric     if (m_selection_type == SelectionType::Action)
2526344a3780SDimitry Andric       return ScrollContext(GetContentHeight() - 1);
2527344a3780SDimitry Andric 
2528344a3780SDimitry Andric     FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2529344a3780SDimitry Andric     ScrollContext context = field->FieldDelegateGetScrollContext();
2530344a3780SDimitry Andric 
2531344a3780SDimitry Andric     int offset = GetErrorHeight();
2532344a3780SDimitry Andric     for (int i = 0; i < m_selection_index; i++) {
2533344a3780SDimitry Andric       if (!m_delegate_sp->GetField(i)->FieldDelegateIsVisible())
2534344a3780SDimitry Andric         continue;
2535344a3780SDimitry Andric       offset += m_delegate_sp->GetField(i)->FieldDelegateGetHeight();
2536344a3780SDimitry Andric     }
2537344a3780SDimitry Andric     context.Offset(offset);
2538344a3780SDimitry Andric 
2539344a3780SDimitry Andric     // If the context is touching the error, include the error in the context as
2540344a3780SDimitry Andric     // well.
2541344a3780SDimitry Andric     if (context.start == GetErrorHeight())
2542344a3780SDimitry Andric       context.start = 0;
2543344a3780SDimitry Andric 
2544344a3780SDimitry Andric     return context;
2545344a3780SDimitry Andric   }
2546344a3780SDimitry Andric 
UpdateScrolling(Surface & surface)2547c0981da4SDimitry Andric   void UpdateScrolling(Surface &surface) {
2548344a3780SDimitry Andric     ScrollContext context = GetScrollContext();
2549344a3780SDimitry Andric     int content_height = GetContentHeight();
2550344a3780SDimitry Andric     int surface_height = surface.GetHeight();
2551344a3780SDimitry Andric     int visible_height = std::min(content_height, surface_height);
2552344a3780SDimitry Andric     int last_visible_line = m_first_visible_line + visible_height - 1;
2553344a3780SDimitry Andric 
2554344a3780SDimitry Andric     // If the last visible line is bigger than the content, then it is invalid
2555344a3780SDimitry Andric     // and needs to be set to the last line in the content. This can happen when
2556344a3780SDimitry Andric     // a field has shrunk in height.
2557344a3780SDimitry Andric     if (last_visible_line > content_height - 1) {
2558344a3780SDimitry Andric       m_first_visible_line = content_height - visible_height;
2559344a3780SDimitry Andric     }
2560344a3780SDimitry Andric 
2561344a3780SDimitry Andric     if (context.start < m_first_visible_line) {
2562344a3780SDimitry Andric       m_first_visible_line = context.start;
2563344a3780SDimitry Andric       return;
2564344a3780SDimitry Andric     }
2565344a3780SDimitry Andric 
2566344a3780SDimitry Andric     if (context.end > last_visible_line) {
2567344a3780SDimitry Andric       m_first_visible_line = context.end - visible_height + 1;
2568344a3780SDimitry Andric     }
2569344a3780SDimitry Andric   }
2570344a3780SDimitry Andric 
DrawError(Surface & surface)2571c0981da4SDimitry Andric   void DrawError(Surface &surface) {
2572344a3780SDimitry Andric     if (!m_delegate_sp->HasError())
2573344a3780SDimitry Andric       return;
2574344a3780SDimitry Andric     surface.MoveCursor(0, 0);
2575344a3780SDimitry Andric     surface.AttributeOn(COLOR_PAIR(RedOnBlack));
2576344a3780SDimitry Andric     surface.PutChar(ACS_DIAMOND);
2577344a3780SDimitry Andric     surface.PutChar(' ');
2578344a3780SDimitry Andric     surface.PutCStringTruncated(1, m_delegate_sp->GetError().c_str());
2579344a3780SDimitry Andric     surface.AttributeOff(COLOR_PAIR(RedOnBlack));
2580344a3780SDimitry Andric 
2581344a3780SDimitry Andric     surface.MoveCursor(0, 1);
2582344a3780SDimitry Andric     surface.HorizontalLine(surface.GetWidth());
2583344a3780SDimitry Andric   }
2584344a3780SDimitry Andric 
DrawFields(Surface & surface)2585c0981da4SDimitry Andric   void DrawFields(Surface &surface) {
2586344a3780SDimitry Andric     int line = 0;
2587344a3780SDimitry Andric     int width = surface.GetWidth();
2588344a3780SDimitry Andric     bool a_field_is_selected = m_selection_type == SelectionType::Field;
2589344a3780SDimitry Andric     for (int i = 0; i < m_delegate_sp->GetNumberOfFields(); i++) {
2590344a3780SDimitry Andric       FieldDelegate *field = m_delegate_sp->GetField(i);
2591344a3780SDimitry Andric       if (!field->FieldDelegateIsVisible())
2592344a3780SDimitry Andric         continue;
2593344a3780SDimitry Andric       bool is_field_selected = a_field_is_selected && m_selection_index == i;
2594344a3780SDimitry Andric       int height = field->FieldDelegateGetHeight();
2595344a3780SDimitry Andric       Rect bounds = Rect(Point(0, line), Size(width, height));
2596c0981da4SDimitry Andric       Surface field_surface = surface.SubSurface(bounds);
2597344a3780SDimitry Andric       field->FieldDelegateDraw(field_surface, is_field_selected);
2598344a3780SDimitry Andric       line += height;
2599344a3780SDimitry Andric     }
2600344a3780SDimitry Andric   }
2601344a3780SDimitry Andric 
DrawActions(Surface & surface)2602c0981da4SDimitry Andric   void DrawActions(Surface &surface) {
2603344a3780SDimitry Andric     int number_of_actions = m_delegate_sp->GetNumberOfActions();
2604344a3780SDimitry Andric     int width = surface.GetWidth() / number_of_actions;
2605344a3780SDimitry Andric     bool an_action_is_selected = m_selection_type == SelectionType::Action;
2606344a3780SDimitry Andric     int x = 0;
2607344a3780SDimitry Andric     for (int i = 0; i < number_of_actions; i++) {
2608344a3780SDimitry Andric       bool is_action_selected = an_action_is_selected && m_selection_index == i;
2609344a3780SDimitry Andric       FormAction &action = m_delegate_sp->GetAction(i);
2610344a3780SDimitry Andric       Rect bounds = Rect(Point(x, 0), Size(width, 1));
2611c0981da4SDimitry Andric       Surface action_surface = surface.SubSurface(bounds);
2612344a3780SDimitry Andric       action.Draw(action_surface, is_action_selected);
2613344a3780SDimitry Andric       x += width;
2614344a3780SDimitry Andric     }
2615344a3780SDimitry Andric   }
2616344a3780SDimitry Andric 
DrawElements(Surface & surface)2617c0981da4SDimitry Andric   void DrawElements(Surface &surface) {
2618344a3780SDimitry Andric     Rect frame = surface.GetFrame();
2619344a3780SDimitry Andric     Rect fields_bounds, actions_bounds;
2620344a3780SDimitry Andric     frame.HorizontalSplit(surface.GetHeight() - GetActionsHeight(),
2621344a3780SDimitry Andric                           fields_bounds, actions_bounds);
2622c0981da4SDimitry Andric     Surface fields_surface = surface.SubSurface(fields_bounds);
2623c0981da4SDimitry Andric     Surface actions_surface = surface.SubSurface(actions_bounds);
2624344a3780SDimitry Andric 
2625344a3780SDimitry Andric     DrawFields(fields_surface);
2626344a3780SDimitry Andric     DrawActions(actions_surface);
2627344a3780SDimitry Andric   }
2628344a3780SDimitry Andric 
2629344a3780SDimitry Andric   // Contents are first drawn on a pad. Then a subset of that pad is copied to
2630344a3780SDimitry Andric   // the derived window starting at the first visible line. This essentially
2631344a3780SDimitry Andric   // provides scrolling functionality.
DrawContent(Surface & surface)2632c0981da4SDimitry Andric   void DrawContent(Surface &surface) {
2633344a3780SDimitry Andric     UpdateScrolling(surface);
2634344a3780SDimitry Andric 
2635344a3780SDimitry Andric     int width = surface.GetWidth();
2636344a3780SDimitry Andric     int height = GetContentHeight();
2637344a3780SDimitry Andric     Pad pad = Pad(Size(width, height));
2638344a3780SDimitry Andric 
2639344a3780SDimitry Andric     Rect frame = pad.GetFrame();
2640344a3780SDimitry Andric     Rect error_bounds, elements_bounds;
2641344a3780SDimitry Andric     frame.HorizontalSplit(GetErrorHeight(), error_bounds, elements_bounds);
2642c0981da4SDimitry Andric     Surface error_surface = pad.SubSurface(error_bounds);
2643c0981da4SDimitry Andric     Surface elements_surface = pad.SubSurface(elements_bounds);
2644344a3780SDimitry Andric 
2645344a3780SDimitry Andric     DrawError(error_surface);
2646344a3780SDimitry Andric     DrawElements(elements_surface);
2647344a3780SDimitry Andric 
2648344a3780SDimitry Andric     int copy_height = std::min(surface.GetHeight(), pad.GetHeight());
2649344a3780SDimitry Andric     pad.CopyToSurface(surface, Point(0, m_first_visible_line), Point(),
2650344a3780SDimitry Andric                       Size(width, copy_height));
2651344a3780SDimitry Andric   }
2652344a3780SDimitry Andric 
DrawSubmitHint(Surface & surface,bool is_active)2653c0981da4SDimitry Andric   void DrawSubmitHint(Surface &surface, bool is_active) {
2654c0981da4SDimitry Andric     surface.MoveCursor(2, surface.GetHeight() - 1);
2655c0981da4SDimitry Andric     if (is_active)
2656c0981da4SDimitry Andric       surface.AttributeOn(A_BOLD | COLOR_PAIR(BlackOnWhite));
2657c0981da4SDimitry Andric     surface.Printf("[Press Alt+Enter to %s]",
2658c0981da4SDimitry Andric                    m_delegate_sp->GetAction(0).GetLabel().c_str());
2659c0981da4SDimitry Andric     if (is_active)
2660c0981da4SDimitry Andric       surface.AttributeOff(A_BOLD | COLOR_PAIR(BlackOnWhite));
2661c0981da4SDimitry Andric   }
2662c0981da4SDimitry Andric 
WindowDelegateDraw(Window & window,bool force)2663344a3780SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
2664344a3780SDimitry Andric     m_delegate_sp->UpdateFieldsVisibility();
2665344a3780SDimitry Andric 
2666344a3780SDimitry Andric     window.Erase();
2667344a3780SDimitry Andric 
2668344a3780SDimitry Andric     window.DrawTitleBox(m_delegate_sp->GetName().c_str(),
2669c0981da4SDimitry Andric                         "Press Esc to Cancel");
2670c0981da4SDimitry Andric     DrawSubmitHint(window, window.IsActive());
2671344a3780SDimitry Andric 
2672344a3780SDimitry Andric     Rect content_bounds = window.GetFrame();
2673344a3780SDimitry Andric     content_bounds.Inset(2, 2);
2674c0981da4SDimitry Andric     Surface content_surface = window.SubSurface(content_bounds);
2675344a3780SDimitry Andric 
2676344a3780SDimitry Andric     DrawContent(content_surface);
2677344a3780SDimitry Andric     return true;
2678344a3780SDimitry Andric   }
2679344a3780SDimitry Andric 
SkipNextHiddenFields()2680344a3780SDimitry Andric   void SkipNextHiddenFields() {
2681344a3780SDimitry Andric     while (true) {
2682344a3780SDimitry Andric       if (m_delegate_sp->GetField(m_selection_index)->FieldDelegateIsVisible())
2683344a3780SDimitry Andric         return;
2684344a3780SDimitry Andric 
2685344a3780SDimitry Andric       if (m_selection_index == m_delegate_sp->GetNumberOfFields() - 1) {
2686344a3780SDimitry Andric         m_selection_type = SelectionType::Action;
2687344a3780SDimitry Andric         m_selection_index = 0;
2688344a3780SDimitry Andric         return;
2689344a3780SDimitry Andric       }
2690344a3780SDimitry Andric 
2691344a3780SDimitry Andric       m_selection_index++;
2692344a3780SDimitry Andric     }
2693344a3780SDimitry Andric   }
2694344a3780SDimitry Andric 
SelectNext(int key)2695344a3780SDimitry Andric   HandleCharResult SelectNext(int key) {
2696344a3780SDimitry Andric     if (m_selection_type == SelectionType::Action) {
2697344a3780SDimitry Andric       if (m_selection_index < m_delegate_sp->GetNumberOfActions() - 1) {
2698344a3780SDimitry Andric         m_selection_index++;
2699344a3780SDimitry Andric         return eKeyHandled;
2700344a3780SDimitry Andric       }
2701344a3780SDimitry Andric 
2702344a3780SDimitry Andric       m_selection_index = 0;
2703344a3780SDimitry Andric       m_selection_type = SelectionType::Field;
2704344a3780SDimitry Andric       SkipNextHiddenFields();
2705344a3780SDimitry Andric       if (m_selection_type == SelectionType::Field) {
2706344a3780SDimitry Andric         FieldDelegate *next_field = m_delegate_sp->GetField(m_selection_index);
2707344a3780SDimitry Andric         next_field->FieldDelegateSelectFirstElement();
2708344a3780SDimitry Andric       }
2709344a3780SDimitry Andric       return eKeyHandled;
2710344a3780SDimitry Andric     }
2711344a3780SDimitry Andric 
2712344a3780SDimitry Andric     FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2713344a3780SDimitry Andric     if (!field->FieldDelegateOnLastOrOnlyElement()) {
2714344a3780SDimitry Andric       return field->FieldDelegateHandleChar(key);
2715344a3780SDimitry Andric     }
2716344a3780SDimitry Andric 
2717344a3780SDimitry Andric     field->FieldDelegateExitCallback();
2718344a3780SDimitry Andric 
2719344a3780SDimitry Andric     if (m_selection_index == m_delegate_sp->GetNumberOfFields() - 1) {
2720344a3780SDimitry Andric       m_selection_type = SelectionType::Action;
2721344a3780SDimitry Andric       m_selection_index = 0;
2722344a3780SDimitry Andric       return eKeyHandled;
2723344a3780SDimitry Andric     }
2724344a3780SDimitry Andric 
2725344a3780SDimitry Andric     m_selection_index++;
2726344a3780SDimitry Andric     SkipNextHiddenFields();
2727344a3780SDimitry Andric 
2728344a3780SDimitry Andric     if (m_selection_type == SelectionType::Field) {
2729344a3780SDimitry Andric       FieldDelegate *next_field = m_delegate_sp->GetField(m_selection_index);
2730344a3780SDimitry Andric       next_field->FieldDelegateSelectFirstElement();
2731344a3780SDimitry Andric     }
2732344a3780SDimitry Andric 
2733344a3780SDimitry Andric     return eKeyHandled;
2734344a3780SDimitry Andric   }
2735344a3780SDimitry Andric 
SkipPreviousHiddenFields()2736344a3780SDimitry Andric   void SkipPreviousHiddenFields() {
2737344a3780SDimitry Andric     while (true) {
2738344a3780SDimitry Andric       if (m_delegate_sp->GetField(m_selection_index)->FieldDelegateIsVisible())
2739344a3780SDimitry Andric         return;
2740344a3780SDimitry Andric 
2741344a3780SDimitry Andric       if (m_selection_index == 0) {
2742344a3780SDimitry Andric         m_selection_type = SelectionType::Action;
2743344a3780SDimitry Andric         m_selection_index = 0;
2744344a3780SDimitry Andric         return;
2745344a3780SDimitry Andric       }
2746344a3780SDimitry Andric 
2747344a3780SDimitry Andric       m_selection_index--;
2748344a3780SDimitry Andric     }
2749344a3780SDimitry Andric   }
2750344a3780SDimitry Andric 
SelectPrevious(int key)2751344a3780SDimitry Andric   HandleCharResult SelectPrevious(int key) {
2752344a3780SDimitry Andric     if (m_selection_type == SelectionType::Action) {
2753344a3780SDimitry Andric       if (m_selection_index > 0) {
2754344a3780SDimitry Andric         m_selection_index--;
2755344a3780SDimitry Andric         return eKeyHandled;
2756344a3780SDimitry Andric       }
2757344a3780SDimitry Andric       m_selection_index = m_delegate_sp->GetNumberOfFields() - 1;
2758344a3780SDimitry Andric       m_selection_type = SelectionType::Field;
2759344a3780SDimitry Andric       SkipPreviousHiddenFields();
2760344a3780SDimitry Andric       if (m_selection_type == SelectionType::Field) {
2761344a3780SDimitry Andric         FieldDelegate *previous_field =
2762344a3780SDimitry Andric             m_delegate_sp->GetField(m_selection_index);
2763344a3780SDimitry Andric         previous_field->FieldDelegateSelectLastElement();
2764344a3780SDimitry Andric       }
2765344a3780SDimitry Andric       return eKeyHandled;
2766344a3780SDimitry Andric     }
2767344a3780SDimitry Andric 
2768344a3780SDimitry Andric     FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2769344a3780SDimitry Andric     if (!field->FieldDelegateOnFirstOrOnlyElement()) {
2770344a3780SDimitry Andric       return field->FieldDelegateHandleChar(key);
2771344a3780SDimitry Andric     }
2772344a3780SDimitry Andric 
2773344a3780SDimitry Andric     field->FieldDelegateExitCallback();
2774344a3780SDimitry Andric 
2775344a3780SDimitry Andric     if (m_selection_index == 0) {
2776344a3780SDimitry Andric       m_selection_type = SelectionType::Action;
2777344a3780SDimitry Andric       m_selection_index = m_delegate_sp->GetNumberOfActions() - 1;
2778344a3780SDimitry Andric       return eKeyHandled;
2779344a3780SDimitry Andric     }
2780344a3780SDimitry Andric 
2781344a3780SDimitry Andric     m_selection_index--;
2782344a3780SDimitry Andric     SkipPreviousHiddenFields();
2783344a3780SDimitry Andric 
2784344a3780SDimitry Andric     if (m_selection_type == SelectionType::Field) {
2785344a3780SDimitry Andric       FieldDelegate *previous_field =
2786344a3780SDimitry Andric           m_delegate_sp->GetField(m_selection_index);
2787344a3780SDimitry Andric       previous_field->FieldDelegateSelectLastElement();
2788344a3780SDimitry Andric     }
2789344a3780SDimitry Andric 
2790344a3780SDimitry Andric     return eKeyHandled;
2791344a3780SDimitry Andric   }
2792344a3780SDimitry Andric 
ExecuteAction(Window & window,int index)2793c0981da4SDimitry Andric   void ExecuteAction(Window &window, int index) {
2794c0981da4SDimitry Andric     FormAction &action = m_delegate_sp->GetAction(index);
2795344a3780SDimitry Andric     action.Execute(window);
2796344a3780SDimitry Andric     if (m_delegate_sp->HasError()) {
2797344a3780SDimitry Andric       m_first_visible_line = 0;
2798344a3780SDimitry Andric       m_selection_index = 0;
2799344a3780SDimitry Andric       m_selection_type = SelectionType::Field;
2800344a3780SDimitry Andric     }
2801344a3780SDimitry Andric   }
2802344a3780SDimitry Andric 
2803c0981da4SDimitry Andric   // Always return eKeyHandled to absorb all events since forms are always
2804c0981da4SDimitry Andric   // added as pop-ups that should take full control until canceled or submitted.
WindowDelegateHandleChar(Window & window,int key)2805344a3780SDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
2806344a3780SDimitry Andric     switch (key) {
2807344a3780SDimitry Andric     case '\r':
2808344a3780SDimitry Andric     case '\n':
2809344a3780SDimitry Andric     case KEY_ENTER:
2810344a3780SDimitry Andric       if (m_selection_type == SelectionType::Action) {
2811c0981da4SDimitry Andric         ExecuteAction(window, m_selection_index);
2812344a3780SDimitry Andric         return eKeyHandled;
2813344a3780SDimitry Andric       }
2814344a3780SDimitry Andric       break;
2815c0981da4SDimitry Andric     case KEY_ALT_ENTER:
2816c0981da4SDimitry Andric       ExecuteAction(window, 0);
2817c0981da4SDimitry Andric       return eKeyHandled;
2818344a3780SDimitry Andric     case '\t':
2819c0981da4SDimitry Andric       SelectNext(key);
2820c0981da4SDimitry Andric       return eKeyHandled;
2821344a3780SDimitry Andric     case KEY_SHIFT_TAB:
2822c0981da4SDimitry Andric       SelectPrevious(key);
2823c0981da4SDimitry Andric       return eKeyHandled;
2824344a3780SDimitry Andric     case KEY_ESCAPE:
2825344a3780SDimitry Andric       window.GetParent()->RemoveSubWindow(&window);
2826344a3780SDimitry Andric       return eKeyHandled;
2827344a3780SDimitry Andric     default:
2828344a3780SDimitry Andric       break;
2829344a3780SDimitry Andric     }
2830344a3780SDimitry Andric 
2831344a3780SDimitry Andric     // If the key wasn't handled and one of the fields is selected, pass the key
2832344a3780SDimitry Andric     // to that field.
2833344a3780SDimitry Andric     if (m_selection_type == SelectionType::Field) {
2834344a3780SDimitry Andric       FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2835c0981da4SDimitry Andric       if (field->FieldDelegateHandleChar(key) == eKeyHandled)
2836c0981da4SDimitry Andric         return eKeyHandled;
2837344a3780SDimitry Andric     }
2838344a3780SDimitry Andric 
2839c0981da4SDimitry Andric     // If the key wasn't handled by the possibly selected field, handle some
2840c0981da4SDimitry Andric     // extra keys for navigation.
2841c0981da4SDimitry Andric     switch (key) {
2842c0981da4SDimitry Andric     case KEY_DOWN:
2843c0981da4SDimitry Andric       SelectNext(key);
2844c0981da4SDimitry Andric       return eKeyHandled;
2845c0981da4SDimitry Andric     case KEY_UP:
2846c0981da4SDimitry Andric       SelectPrevious(key);
2847c0981da4SDimitry Andric       return eKeyHandled;
2848c0981da4SDimitry Andric     default:
2849c0981da4SDimitry Andric       break;
2850c0981da4SDimitry Andric     }
2851c0981da4SDimitry Andric 
2852c0981da4SDimitry Andric     return eKeyHandled;
2853344a3780SDimitry Andric   }
2854344a3780SDimitry Andric 
2855344a3780SDimitry Andric protected:
2856344a3780SDimitry Andric   FormDelegateSP m_delegate_sp;
2857344a3780SDimitry Andric   // The index of the currently selected SelectionType.
2858145449b1SDimitry Andric   int m_selection_index = 0;
2859344a3780SDimitry Andric   // See SelectionType class enum.
2860344a3780SDimitry Andric   SelectionType m_selection_type;
2861344a3780SDimitry Andric   // The first visible line from the pad.
2862145449b1SDimitry Andric   int m_first_visible_line = 0;
2863344a3780SDimitry Andric };
2864344a3780SDimitry Andric 
2865344a3780SDimitry Andric ///////////////////////////
2866344a3780SDimitry Andric // Form Delegate Instances
2867344a3780SDimitry Andric ///////////////////////////
2868344a3780SDimitry Andric 
2869344a3780SDimitry Andric class DetachOrKillProcessFormDelegate : public FormDelegate {
2870344a3780SDimitry Andric public:
DetachOrKillProcessFormDelegate(Process * process)2871344a3780SDimitry Andric   DetachOrKillProcessFormDelegate(Process *process) : m_process(process) {
2872344a3780SDimitry Andric     SetError("There is a running process, either detach or kill it.");
2873344a3780SDimitry Andric 
2874344a3780SDimitry Andric     m_keep_stopped_field =
2875344a3780SDimitry Andric         AddBooleanField("Keep process stopped when detaching.", false);
2876344a3780SDimitry Andric 
2877344a3780SDimitry Andric     AddAction("Detach", [this](Window &window) { Detach(window); });
2878344a3780SDimitry Andric     AddAction("Kill", [this](Window &window) { Kill(window); });
2879344a3780SDimitry Andric   }
2880344a3780SDimitry Andric 
GetName()2881344a3780SDimitry Andric   std::string GetName() override { return "Detach/Kill Process"; }
2882344a3780SDimitry Andric 
Kill(Window & window)2883344a3780SDimitry Andric   void Kill(Window &window) {
2884344a3780SDimitry Andric     Status destroy_status(m_process->Destroy(false));
2885344a3780SDimitry Andric     if (destroy_status.Fail()) {
2886344a3780SDimitry Andric       SetError("Failed to kill process.");
2887344a3780SDimitry Andric       return;
2888344a3780SDimitry Andric     }
2889344a3780SDimitry Andric     window.GetParent()->RemoveSubWindow(&window);
2890344a3780SDimitry Andric   }
2891344a3780SDimitry Andric 
Detach(Window & window)2892344a3780SDimitry Andric   void Detach(Window &window) {
2893344a3780SDimitry Andric     Status detach_status(m_process->Detach(m_keep_stopped_field->GetBoolean()));
2894344a3780SDimitry Andric     if (detach_status.Fail()) {
2895344a3780SDimitry Andric       SetError("Failed to detach from process.");
2896344a3780SDimitry Andric       return;
2897344a3780SDimitry Andric     }
2898344a3780SDimitry Andric     window.GetParent()->RemoveSubWindow(&window);
2899344a3780SDimitry Andric   }
2900344a3780SDimitry Andric 
2901344a3780SDimitry Andric protected:
2902344a3780SDimitry Andric   Process *m_process;
2903344a3780SDimitry Andric   BooleanFieldDelegate *m_keep_stopped_field;
2904344a3780SDimitry Andric };
2905344a3780SDimitry Andric 
2906344a3780SDimitry Andric class ProcessAttachFormDelegate : public FormDelegate {
2907344a3780SDimitry Andric public:
ProcessAttachFormDelegate(Debugger & debugger,WindowSP main_window_sp)2908344a3780SDimitry Andric   ProcessAttachFormDelegate(Debugger &debugger, WindowSP main_window_sp)
2909344a3780SDimitry Andric       : m_debugger(debugger), m_main_window_sp(main_window_sp) {
2910344a3780SDimitry Andric     std::vector<std::string> types;
2911344a3780SDimitry Andric     types.push_back(std::string("Name"));
2912344a3780SDimitry Andric     types.push_back(std::string("PID"));
2913344a3780SDimitry Andric     m_type_field = AddChoicesField("Attach By", 2, types);
2914344a3780SDimitry Andric     m_pid_field = AddIntegerField("PID", 0, true);
2915344a3780SDimitry Andric     m_name_field =
2916344a3780SDimitry Andric         AddTextField("Process Name", GetDefaultProcessName().c_str(), true);
2917344a3780SDimitry Andric     m_continue_field = AddBooleanField("Continue once attached.", false);
2918344a3780SDimitry Andric     m_wait_for_field = AddBooleanField("Wait for process to launch.", false);
2919344a3780SDimitry Andric     m_include_existing_field =
2920344a3780SDimitry Andric         AddBooleanField("Include existing processes.", false);
2921344a3780SDimitry Andric     m_show_advanced_field = AddBooleanField("Show advanced settings.", false);
2922344a3780SDimitry Andric     m_plugin_field = AddProcessPluginField();
2923344a3780SDimitry Andric 
2924344a3780SDimitry Andric     AddAction("Attach", [this](Window &window) { Attach(window); });
2925344a3780SDimitry Andric   }
2926344a3780SDimitry Andric 
GetName()2927344a3780SDimitry Andric   std::string GetName() override { return "Attach Process"; }
2928344a3780SDimitry Andric 
UpdateFieldsVisibility()2929344a3780SDimitry Andric   void UpdateFieldsVisibility() override {
2930344a3780SDimitry Andric     if (m_type_field->GetChoiceContent() == "Name") {
2931344a3780SDimitry Andric       m_pid_field->FieldDelegateHide();
2932344a3780SDimitry Andric       m_name_field->FieldDelegateShow();
2933344a3780SDimitry Andric       m_wait_for_field->FieldDelegateShow();
2934344a3780SDimitry Andric       if (m_wait_for_field->GetBoolean())
2935344a3780SDimitry Andric         m_include_existing_field->FieldDelegateShow();
2936344a3780SDimitry Andric       else
2937344a3780SDimitry Andric         m_include_existing_field->FieldDelegateHide();
2938344a3780SDimitry Andric     } else {
2939344a3780SDimitry Andric       m_pid_field->FieldDelegateShow();
2940344a3780SDimitry Andric       m_name_field->FieldDelegateHide();
2941344a3780SDimitry Andric       m_wait_for_field->FieldDelegateHide();
2942344a3780SDimitry Andric       m_include_existing_field->FieldDelegateHide();
2943344a3780SDimitry Andric     }
2944344a3780SDimitry Andric     if (m_show_advanced_field->GetBoolean())
2945344a3780SDimitry Andric       m_plugin_field->FieldDelegateShow();
2946344a3780SDimitry Andric     else
2947344a3780SDimitry Andric       m_plugin_field->FieldDelegateHide();
2948344a3780SDimitry Andric   }
2949344a3780SDimitry Andric 
2950344a3780SDimitry Andric   // Get the basename of the target's main executable if available, empty string
2951344a3780SDimitry Andric   // otherwise.
GetDefaultProcessName()2952344a3780SDimitry Andric   std::string GetDefaultProcessName() {
2953344a3780SDimitry Andric     Target *target = m_debugger.GetSelectedTarget().get();
2954344a3780SDimitry Andric     if (target == nullptr)
2955344a3780SDimitry Andric       return "";
2956344a3780SDimitry Andric 
2957344a3780SDimitry Andric     ModuleSP module_sp = target->GetExecutableModule();
2958344a3780SDimitry Andric     if (!module_sp->IsExecutable())
2959344a3780SDimitry Andric       return "";
2960344a3780SDimitry Andric 
2961344a3780SDimitry Andric     return module_sp->GetFileSpec().GetFilename().AsCString();
2962344a3780SDimitry Andric   }
2963344a3780SDimitry Andric 
StopRunningProcess()2964344a3780SDimitry Andric   bool StopRunningProcess() {
2965344a3780SDimitry Andric     ExecutionContext exe_ctx =
2966344a3780SDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext();
2967344a3780SDimitry Andric 
2968344a3780SDimitry Andric     if (!exe_ctx.HasProcessScope())
2969344a3780SDimitry Andric       return false;
2970344a3780SDimitry Andric 
2971344a3780SDimitry Andric     Process *process = exe_ctx.GetProcessPtr();
2972344a3780SDimitry Andric     if (!(process && process->IsAlive()))
2973344a3780SDimitry Andric       return false;
2974344a3780SDimitry Andric 
2975344a3780SDimitry Andric     FormDelegateSP form_delegate_sp =
2976344a3780SDimitry Andric         FormDelegateSP(new DetachOrKillProcessFormDelegate(process));
2977344a3780SDimitry Andric     Rect bounds = m_main_window_sp->GetCenteredRect(85, 8);
2978344a3780SDimitry Andric     WindowSP form_window_sp = m_main_window_sp->CreateSubWindow(
2979344a3780SDimitry Andric         form_delegate_sp->GetName().c_str(), bounds, true);
2980344a3780SDimitry Andric     WindowDelegateSP window_delegate_sp =
2981344a3780SDimitry Andric         WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
2982344a3780SDimitry Andric     form_window_sp->SetDelegate(window_delegate_sp);
2983344a3780SDimitry Andric 
2984344a3780SDimitry Andric     return true;
2985344a3780SDimitry Andric   }
2986344a3780SDimitry Andric 
GetTarget()2987344a3780SDimitry Andric   Target *GetTarget() {
2988344a3780SDimitry Andric     Target *target = m_debugger.GetSelectedTarget().get();
2989344a3780SDimitry Andric 
2990344a3780SDimitry Andric     if (target != nullptr)
2991344a3780SDimitry Andric       return target;
2992344a3780SDimitry Andric 
2993344a3780SDimitry Andric     TargetSP new_target_sp;
2994344a3780SDimitry Andric     m_debugger.GetTargetList().CreateTarget(
2995344a3780SDimitry Andric         m_debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
2996344a3780SDimitry Andric 
2997344a3780SDimitry Andric     target = new_target_sp.get();
2998344a3780SDimitry Andric 
2999344a3780SDimitry Andric     if (target == nullptr)
3000344a3780SDimitry Andric       SetError("Failed to create target.");
3001344a3780SDimitry Andric 
3002344a3780SDimitry Andric     m_debugger.GetTargetList().SetSelectedTarget(new_target_sp);
3003344a3780SDimitry Andric 
3004344a3780SDimitry Andric     return target;
3005344a3780SDimitry Andric   }
3006344a3780SDimitry Andric 
GetAttachInfo()3007344a3780SDimitry Andric   ProcessAttachInfo GetAttachInfo() {
3008344a3780SDimitry Andric     ProcessAttachInfo attach_info;
3009344a3780SDimitry Andric     attach_info.SetContinueOnceAttached(m_continue_field->GetBoolean());
3010344a3780SDimitry Andric     if (m_type_field->GetChoiceContent() == "Name") {
3011344a3780SDimitry Andric       attach_info.GetExecutableFile().SetFile(m_name_field->GetText(),
3012344a3780SDimitry Andric                                               FileSpec::Style::native);
3013344a3780SDimitry Andric       attach_info.SetWaitForLaunch(m_wait_for_field->GetBoolean());
3014344a3780SDimitry Andric       if (m_wait_for_field->GetBoolean())
3015344a3780SDimitry Andric         attach_info.SetIgnoreExisting(!m_include_existing_field->GetBoolean());
3016344a3780SDimitry Andric     } else {
3017344a3780SDimitry Andric       attach_info.SetProcessID(m_pid_field->GetInteger());
3018344a3780SDimitry Andric     }
3019344a3780SDimitry Andric     attach_info.SetProcessPluginName(m_plugin_field->GetPluginName());
3020344a3780SDimitry Andric 
3021344a3780SDimitry Andric     return attach_info;
3022344a3780SDimitry Andric   }
3023344a3780SDimitry Andric 
Attach(Window & window)3024344a3780SDimitry Andric   void Attach(Window &window) {
3025344a3780SDimitry Andric     ClearError();
3026344a3780SDimitry Andric 
3027344a3780SDimitry Andric     bool all_fields_are_valid = CheckFieldsValidity();
3028344a3780SDimitry Andric     if (!all_fields_are_valid)
3029344a3780SDimitry Andric       return;
3030344a3780SDimitry Andric 
3031344a3780SDimitry Andric     bool process_is_running = StopRunningProcess();
3032344a3780SDimitry Andric     if (process_is_running)
3033344a3780SDimitry Andric       return;
3034344a3780SDimitry Andric 
3035344a3780SDimitry Andric     Target *target = GetTarget();
3036344a3780SDimitry Andric     if (HasError())
3037344a3780SDimitry Andric       return;
3038344a3780SDimitry Andric 
3039344a3780SDimitry Andric     StreamString stream;
3040344a3780SDimitry Andric     ProcessAttachInfo attach_info = GetAttachInfo();
3041344a3780SDimitry Andric     Status status = target->Attach(attach_info, &stream);
3042344a3780SDimitry Andric 
3043344a3780SDimitry Andric     if (status.Fail()) {
3044344a3780SDimitry Andric       SetError(status.AsCString());
3045344a3780SDimitry Andric       return;
3046344a3780SDimitry Andric     }
3047344a3780SDimitry Andric 
3048344a3780SDimitry Andric     ProcessSP process_sp(target->GetProcessSP());
3049344a3780SDimitry Andric     if (!process_sp) {
3050344a3780SDimitry Andric       SetError("Attached sucessfully but target has no process.");
3051344a3780SDimitry Andric       return;
3052344a3780SDimitry Andric     }
3053344a3780SDimitry Andric 
3054344a3780SDimitry Andric     if (attach_info.GetContinueOnceAttached())
3055344a3780SDimitry Andric       process_sp->Resume();
3056344a3780SDimitry Andric 
3057344a3780SDimitry Andric     window.GetParent()->RemoveSubWindow(&window);
3058344a3780SDimitry Andric   }
3059344a3780SDimitry Andric 
3060344a3780SDimitry Andric protected:
3061344a3780SDimitry Andric   Debugger &m_debugger;
3062344a3780SDimitry Andric   WindowSP m_main_window_sp;
3063344a3780SDimitry Andric 
3064344a3780SDimitry Andric   ChoicesFieldDelegate *m_type_field;
3065344a3780SDimitry Andric   IntegerFieldDelegate *m_pid_field;
3066344a3780SDimitry Andric   TextFieldDelegate *m_name_field;
3067344a3780SDimitry Andric   BooleanFieldDelegate *m_continue_field;
3068344a3780SDimitry Andric   BooleanFieldDelegate *m_wait_for_field;
3069344a3780SDimitry Andric   BooleanFieldDelegate *m_include_existing_field;
3070344a3780SDimitry Andric   BooleanFieldDelegate *m_show_advanced_field;
3071344a3780SDimitry Andric   ProcessPluginFieldDelegate *m_plugin_field;
3072344a3780SDimitry Andric };
3073344a3780SDimitry Andric 
3074c0981da4SDimitry Andric class TargetCreateFormDelegate : public FormDelegate {
3075c0981da4SDimitry Andric public:
TargetCreateFormDelegate(Debugger & debugger)3076c0981da4SDimitry Andric   TargetCreateFormDelegate(Debugger &debugger) : m_debugger(debugger) {
3077c0981da4SDimitry Andric     m_executable_field = AddFileField("Executable", "", /*need_to_exist=*/true,
3078c0981da4SDimitry Andric                                       /*required=*/true);
3079c0981da4SDimitry Andric     m_core_file_field = AddFileField("Core File", "", /*need_to_exist=*/true,
3080c0981da4SDimitry Andric                                      /*required=*/false);
3081c0981da4SDimitry Andric     m_symbol_file_field = AddFileField(
3082c0981da4SDimitry Andric         "Symbol File", "", /*need_to_exist=*/true, /*required=*/false);
3083c0981da4SDimitry Andric     m_show_advanced_field = AddBooleanField("Show advanced settings.", false);
3084c0981da4SDimitry Andric     m_remote_file_field = AddFileField(
3085c0981da4SDimitry Andric         "Remote File", "", /*need_to_exist=*/false, /*required=*/false);
3086c0981da4SDimitry Andric     m_arch_field = AddArchField("Architecture", "", /*required=*/false);
3087c0981da4SDimitry Andric     m_platform_field = AddPlatformPluginField(debugger);
3088c0981da4SDimitry Andric     m_load_dependent_files_field =
3089c0981da4SDimitry Andric         AddChoicesField("Load Dependents", 3, GetLoadDependentFilesChoices());
3090c0981da4SDimitry Andric 
3091c0981da4SDimitry Andric     AddAction("Create", [this](Window &window) { CreateTarget(window); });
3092c0981da4SDimitry Andric   }
3093c0981da4SDimitry Andric 
GetName()3094c0981da4SDimitry Andric   std::string GetName() override { return "Create Target"; }
3095c0981da4SDimitry Andric 
UpdateFieldsVisibility()3096c0981da4SDimitry Andric   void UpdateFieldsVisibility() override {
3097c0981da4SDimitry Andric     if (m_show_advanced_field->GetBoolean()) {
3098c0981da4SDimitry Andric       m_remote_file_field->FieldDelegateShow();
3099c0981da4SDimitry Andric       m_arch_field->FieldDelegateShow();
3100c0981da4SDimitry Andric       m_platform_field->FieldDelegateShow();
3101c0981da4SDimitry Andric       m_load_dependent_files_field->FieldDelegateShow();
3102c0981da4SDimitry Andric     } else {
3103c0981da4SDimitry Andric       m_remote_file_field->FieldDelegateHide();
3104c0981da4SDimitry Andric       m_arch_field->FieldDelegateHide();
3105c0981da4SDimitry Andric       m_platform_field->FieldDelegateHide();
3106c0981da4SDimitry Andric       m_load_dependent_files_field->FieldDelegateHide();
3107c0981da4SDimitry Andric     }
3108c0981da4SDimitry Andric   }
3109c0981da4SDimitry Andric 
3110c0981da4SDimitry Andric   static constexpr const char *kLoadDependentFilesNo = "No";
3111c0981da4SDimitry Andric   static constexpr const char *kLoadDependentFilesYes = "Yes";
3112c0981da4SDimitry Andric   static constexpr const char *kLoadDependentFilesExecOnly = "Executable only";
3113c0981da4SDimitry Andric 
GetLoadDependentFilesChoices()3114c0981da4SDimitry Andric   std::vector<std::string> GetLoadDependentFilesChoices() {
3115e3b55780SDimitry Andric     std::vector<std::string> load_dependents_options;
3116e3b55780SDimitry Andric     load_dependents_options.push_back(kLoadDependentFilesExecOnly);
3117e3b55780SDimitry Andric     load_dependents_options.push_back(kLoadDependentFilesYes);
3118e3b55780SDimitry Andric     load_dependents_options.push_back(kLoadDependentFilesNo);
3119e3b55780SDimitry Andric     return load_dependents_options;
3120c0981da4SDimitry Andric   }
3121c0981da4SDimitry Andric 
GetLoadDependentFiles()3122c0981da4SDimitry Andric   LoadDependentFiles GetLoadDependentFiles() {
3123c0981da4SDimitry Andric     std::string choice = m_load_dependent_files_field->GetChoiceContent();
3124c0981da4SDimitry Andric     if (choice == kLoadDependentFilesNo)
3125c0981da4SDimitry Andric       return eLoadDependentsNo;
3126c0981da4SDimitry Andric     if (choice == kLoadDependentFilesYes)
3127c0981da4SDimitry Andric       return eLoadDependentsYes;
3128c0981da4SDimitry Andric     return eLoadDependentsDefault;
3129c0981da4SDimitry Andric   }
3130c0981da4SDimitry Andric 
GetPlatformOptions()3131c0981da4SDimitry Andric   OptionGroupPlatform GetPlatformOptions() {
3132c0981da4SDimitry Andric     OptionGroupPlatform platform_options(false);
3133c0981da4SDimitry Andric     platform_options.SetPlatformName(m_platform_field->GetPluginName().c_str());
3134c0981da4SDimitry Andric     return platform_options;
3135c0981da4SDimitry Andric   }
3136c0981da4SDimitry Andric 
GetTarget()3137c0981da4SDimitry Andric   TargetSP GetTarget() {
3138c0981da4SDimitry Andric     OptionGroupPlatform platform_options = GetPlatformOptions();
3139c0981da4SDimitry Andric     TargetSP target_sp;
3140c0981da4SDimitry Andric     Status status = m_debugger.GetTargetList().CreateTarget(
3141c0981da4SDimitry Andric         m_debugger, m_executable_field->GetPath(),
3142c0981da4SDimitry Andric         m_arch_field->GetArchString(), GetLoadDependentFiles(),
3143c0981da4SDimitry Andric         &platform_options, target_sp);
3144c0981da4SDimitry Andric 
3145c0981da4SDimitry Andric     if (status.Fail()) {
3146c0981da4SDimitry Andric       SetError(status.AsCString());
3147c0981da4SDimitry Andric       return nullptr;
3148c0981da4SDimitry Andric     }
3149c0981da4SDimitry Andric 
3150c0981da4SDimitry Andric     m_debugger.GetTargetList().SetSelectedTarget(target_sp);
3151c0981da4SDimitry Andric 
3152c0981da4SDimitry Andric     return target_sp;
3153c0981da4SDimitry Andric   }
3154c0981da4SDimitry Andric 
SetSymbolFile(TargetSP target_sp)3155c0981da4SDimitry Andric   void SetSymbolFile(TargetSP target_sp) {
3156c0981da4SDimitry Andric     if (!m_symbol_file_field->IsSpecified())
3157c0981da4SDimitry Andric       return;
3158c0981da4SDimitry Andric 
3159c0981da4SDimitry Andric     ModuleSP module_sp(target_sp->GetExecutableModule());
3160c0981da4SDimitry Andric     if (!module_sp)
3161c0981da4SDimitry Andric       return;
3162c0981da4SDimitry Andric 
3163c0981da4SDimitry Andric     module_sp->SetSymbolFileFileSpec(
3164c0981da4SDimitry Andric         m_symbol_file_field->GetResolvedFileSpec());
3165c0981da4SDimitry Andric   }
3166c0981da4SDimitry Andric 
SetCoreFile(TargetSP target_sp)3167c0981da4SDimitry Andric   void SetCoreFile(TargetSP target_sp) {
3168c0981da4SDimitry Andric     if (!m_core_file_field->IsSpecified())
3169c0981da4SDimitry Andric       return;
3170c0981da4SDimitry Andric 
3171c0981da4SDimitry Andric     FileSpec core_file_spec = m_core_file_field->GetResolvedFileSpec();
3172c0981da4SDimitry Andric 
3173c0981da4SDimitry Andric     FileSpec core_file_directory_spec;
3174e3b55780SDimitry Andric     core_file_directory_spec.SetDirectory(core_file_spec.GetDirectory());
3175c0981da4SDimitry Andric     target_sp->AppendExecutableSearchPaths(core_file_directory_spec);
3176c0981da4SDimitry Andric 
3177c0981da4SDimitry Andric     ProcessSP process_sp(target_sp->CreateProcess(
3178c0981da4SDimitry Andric         m_debugger.GetListener(), llvm::StringRef(), &core_file_spec, false));
3179c0981da4SDimitry Andric 
3180c0981da4SDimitry Andric     if (!process_sp) {
3181b1c73532SDimitry Andric       SetError("Unknown core file format!");
3182c0981da4SDimitry Andric       return;
3183c0981da4SDimitry Andric     }
3184c0981da4SDimitry Andric 
3185c0981da4SDimitry Andric     Status status = process_sp->LoadCore();
3186c0981da4SDimitry Andric     if (status.Fail()) {
3187b1c73532SDimitry Andric       SetError("Unknown core file format!");
3188c0981da4SDimitry Andric       return;
3189c0981da4SDimitry Andric     }
3190c0981da4SDimitry Andric   }
3191c0981da4SDimitry Andric 
SetRemoteFile(TargetSP target_sp)3192c0981da4SDimitry Andric   void SetRemoteFile(TargetSP target_sp) {
3193c0981da4SDimitry Andric     if (!m_remote_file_field->IsSpecified())
3194c0981da4SDimitry Andric       return;
3195c0981da4SDimitry Andric 
3196c0981da4SDimitry Andric     ModuleSP module_sp(target_sp->GetExecutableModule());
3197c0981da4SDimitry Andric     if (!module_sp)
3198c0981da4SDimitry Andric       return;
3199c0981da4SDimitry Andric 
3200c0981da4SDimitry Andric     FileSpec remote_file_spec = m_remote_file_field->GetFileSpec();
3201c0981da4SDimitry Andric     module_sp->SetPlatformFileSpec(remote_file_spec);
3202c0981da4SDimitry Andric   }
3203c0981da4SDimitry Andric 
RemoveTarget(TargetSP target_sp)3204c0981da4SDimitry Andric   void RemoveTarget(TargetSP target_sp) {
3205c0981da4SDimitry Andric     m_debugger.GetTargetList().DeleteTarget(target_sp);
3206c0981da4SDimitry Andric   }
3207c0981da4SDimitry Andric 
CreateTarget(Window & window)3208c0981da4SDimitry Andric   void CreateTarget(Window &window) {
3209c0981da4SDimitry Andric     ClearError();
3210c0981da4SDimitry Andric 
3211c0981da4SDimitry Andric     bool all_fields_are_valid = CheckFieldsValidity();
3212c0981da4SDimitry Andric     if (!all_fields_are_valid)
3213c0981da4SDimitry Andric       return;
3214c0981da4SDimitry Andric 
3215c0981da4SDimitry Andric     TargetSP target_sp = GetTarget();
3216c0981da4SDimitry Andric     if (HasError())
3217c0981da4SDimitry Andric       return;
3218c0981da4SDimitry Andric 
3219c0981da4SDimitry Andric     SetSymbolFile(target_sp);
3220c0981da4SDimitry Andric     if (HasError()) {
3221c0981da4SDimitry Andric       RemoveTarget(target_sp);
3222c0981da4SDimitry Andric       return;
3223c0981da4SDimitry Andric     }
3224c0981da4SDimitry Andric 
3225c0981da4SDimitry Andric     SetCoreFile(target_sp);
3226c0981da4SDimitry Andric     if (HasError()) {
3227c0981da4SDimitry Andric       RemoveTarget(target_sp);
3228c0981da4SDimitry Andric       return;
3229c0981da4SDimitry Andric     }
3230c0981da4SDimitry Andric 
3231c0981da4SDimitry Andric     SetRemoteFile(target_sp);
3232c0981da4SDimitry Andric     if (HasError()) {
3233c0981da4SDimitry Andric       RemoveTarget(target_sp);
3234c0981da4SDimitry Andric       return;
3235c0981da4SDimitry Andric     }
3236c0981da4SDimitry Andric 
3237c0981da4SDimitry Andric     window.GetParent()->RemoveSubWindow(&window);
3238c0981da4SDimitry Andric   }
3239c0981da4SDimitry Andric 
3240c0981da4SDimitry Andric protected:
3241c0981da4SDimitry Andric   Debugger &m_debugger;
3242c0981da4SDimitry Andric 
3243c0981da4SDimitry Andric   FileFieldDelegate *m_executable_field;
3244c0981da4SDimitry Andric   FileFieldDelegate *m_core_file_field;
3245c0981da4SDimitry Andric   FileFieldDelegate *m_symbol_file_field;
3246c0981da4SDimitry Andric   BooleanFieldDelegate *m_show_advanced_field;
3247c0981da4SDimitry Andric   FileFieldDelegate *m_remote_file_field;
3248c0981da4SDimitry Andric   ArchFieldDelegate *m_arch_field;
3249c0981da4SDimitry Andric   PlatformPluginFieldDelegate *m_platform_field;
3250c0981da4SDimitry Andric   ChoicesFieldDelegate *m_load_dependent_files_field;
3251c0981da4SDimitry Andric };
3252c0981da4SDimitry Andric 
3253c0981da4SDimitry Andric class ProcessLaunchFormDelegate : public FormDelegate {
3254c0981da4SDimitry Andric public:
ProcessLaunchFormDelegate(Debugger & debugger,WindowSP main_window_sp)3255c0981da4SDimitry Andric   ProcessLaunchFormDelegate(Debugger &debugger, WindowSP main_window_sp)
3256c0981da4SDimitry Andric       : m_debugger(debugger), m_main_window_sp(main_window_sp) {
3257c0981da4SDimitry Andric 
3258c0981da4SDimitry Andric     m_arguments_field = AddArgumentsField();
3259c0981da4SDimitry Andric     SetArgumentsFieldDefaultValue();
3260c0981da4SDimitry Andric     m_target_environment_field =
3261c0981da4SDimitry Andric         AddEnvironmentVariableListField("Target Environment Variables");
3262c0981da4SDimitry Andric     SetTargetEnvironmentFieldDefaultValue();
3263c0981da4SDimitry Andric     m_working_directory_field = AddDirectoryField(
3264c0981da4SDimitry Andric         "Working Directory", GetDefaultWorkingDirectory().c_str(), true, false);
3265c0981da4SDimitry Andric 
3266c0981da4SDimitry Andric     m_show_advanced_field = AddBooleanField("Show advanced settings.", false);
3267c0981da4SDimitry Andric 
3268c0981da4SDimitry Andric     m_stop_at_entry_field = AddBooleanField("Stop at entry point.", false);
3269c0981da4SDimitry Andric     m_detach_on_error_field =
3270c0981da4SDimitry Andric         AddBooleanField("Detach on error.", GetDefaultDetachOnError());
3271c0981da4SDimitry Andric     m_disable_aslr_field =
3272c0981da4SDimitry Andric         AddBooleanField("Disable ASLR", GetDefaultDisableASLR());
3273c0981da4SDimitry Andric     m_plugin_field = AddProcessPluginField();
3274c0981da4SDimitry Andric     m_arch_field = AddArchField("Architecture", "", false);
3275c0981da4SDimitry Andric     m_shell_field = AddFileField("Shell", "", true, false);
3276c0981da4SDimitry Andric     m_expand_shell_arguments_field =
3277c0981da4SDimitry Andric         AddBooleanField("Expand shell arguments.", false);
3278c0981da4SDimitry Andric 
3279c0981da4SDimitry Andric     m_disable_standard_io_field =
3280c0981da4SDimitry Andric         AddBooleanField("Disable Standard IO", GetDefaultDisableStandardIO());
3281c0981da4SDimitry Andric     m_standard_output_field =
3282c0981da4SDimitry Andric         AddFileField("Standard Output File", "", /*need_to_exist=*/false,
3283c0981da4SDimitry Andric                      /*required=*/false);
3284c0981da4SDimitry Andric     m_standard_error_field =
3285c0981da4SDimitry Andric         AddFileField("Standard Error File", "", /*need_to_exist=*/false,
3286c0981da4SDimitry Andric                      /*required=*/false);
3287c0981da4SDimitry Andric     m_standard_input_field =
3288c0981da4SDimitry Andric         AddFileField("Standard Input File", "", /*need_to_exist=*/false,
3289c0981da4SDimitry Andric                      /*required=*/false);
3290c0981da4SDimitry Andric 
3291c0981da4SDimitry Andric     m_show_inherited_environment_field =
3292c0981da4SDimitry Andric         AddBooleanField("Show inherited environment variables.", false);
3293c0981da4SDimitry Andric     m_inherited_environment_field =
3294c0981da4SDimitry Andric         AddEnvironmentVariableListField("Inherited Environment Variables");
3295c0981da4SDimitry Andric     SetInheritedEnvironmentFieldDefaultValue();
3296c0981da4SDimitry Andric 
3297c0981da4SDimitry Andric     AddAction("Launch", [this](Window &window) { Launch(window); });
3298c0981da4SDimitry Andric   }
3299c0981da4SDimitry Andric 
GetName()3300c0981da4SDimitry Andric   std::string GetName() override { return "Launch Process"; }
3301c0981da4SDimitry Andric 
UpdateFieldsVisibility()3302c0981da4SDimitry Andric   void UpdateFieldsVisibility() override {
3303c0981da4SDimitry Andric     if (m_show_advanced_field->GetBoolean()) {
3304c0981da4SDimitry Andric       m_stop_at_entry_field->FieldDelegateShow();
3305c0981da4SDimitry Andric       m_detach_on_error_field->FieldDelegateShow();
3306c0981da4SDimitry Andric       m_disable_aslr_field->FieldDelegateShow();
3307c0981da4SDimitry Andric       m_plugin_field->FieldDelegateShow();
3308c0981da4SDimitry Andric       m_arch_field->FieldDelegateShow();
3309c0981da4SDimitry Andric       m_shell_field->FieldDelegateShow();
3310c0981da4SDimitry Andric       m_expand_shell_arguments_field->FieldDelegateShow();
3311c0981da4SDimitry Andric       m_disable_standard_io_field->FieldDelegateShow();
3312c0981da4SDimitry Andric       if (m_disable_standard_io_field->GetBoolean()) {
3313c0981da4SDimitry Andric         m_standard_input_field->FieldDelegateHide();
3314c0981da4SDimitry Andric         m_standard_output_field->FieldDelegateHide();
3315c0981da4SDimitry Andric         m_standard_error_field->FieldDelegateHide();
3316c0981da4SDimitry Andric       } else {
3317c0981da4SDimitry Andric         m_standard_input_field->FieldDelegateShow();
3318c0981da4SDimitry Andric         m_standard_output_field->FieldDelegateShow();
3319c0981da4SDimitry Andric         m_standard_error_field->FieldDelegateShow();
3320c0981da4SDimitry Andric       }
3321c0981da4SDimitry Andric       m_show_inherited_environment_field->FieldDelegateShow();
3322c0981da4SDimitry Andric       if (m_show_inherited_environment_field->GetBoolean())
3323c0981da4SDimitry Andric         m_inherited_environment_field->FieldDelegateShow();
3324c0981da4SDimitry Andric       else
3325c0981da4SDimitry Andric         m_inherited_environment_field->FieldDelegateHide();
3326c0981da4SDimitry Andric     } else {
3327c0981da4SDimitry Andric       m_stop_at_entry_field->FieldDelegateHide();
3328c0981da4SDimitry Andric       m_detach_on_error_field->FieldDelegateHide();
3329c0981da4SDimitry Andric       m_disable_aslr_field->FieldDelegateHide();
3330c0981da4SDimitry Andric       m_plugin_field->FieldDelegateHide();
3331c0981da4SDimitry Andric       m_arch_field->FieldDelegateHide();
3332c0981da4SDimitry Andric       m_shell_field->FieldDelegateHide();
3333c0981da4SDimitry Andric       m_expand_shell_arguments_field->FieldDelegateHide();
3334c0981da4SDimitry Andric       m_disable_standard_io_field->FieldDelegateHide();
3335c0981da4SDimitry Andric       m_standard_input_field->FieldDelegateHide();
3336c0981da4SDimitry Andric       m_standard_output_field->FieldDelegateHide();
3337c0981da4SDimitry Andric       m_standard_error_field->FieldDelegateHide();
3338c0981da4SDimitry Andric       m_show_inherited_environment_field->FieldDelegateHide();
3339c0981da4SDimitry Andric       m_inherited_environment_field->FieldDelegateHide();
3340c0981da4SDimitry Andric     }
3341c0981da4SDimitry Andric   }
3342c0981da4SDimitry Andric 
3343c0981da4SDimitry Andric   // Methods for setting the default value of the fields.
3344c0981da4SDimitry Andric 
SetArgumentsFieldDefaultValue()3345c0981da4SDimitry Andric   void SetArgumentsFieldDefaultValue() {
3346c0981da4SDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3347c0981da4SDimitry Andric     if (target == nullptr)
3348c0981da4SDimitry Andric       return;
3349c0981da4SDimitry Andric 
3350c0981da4SDimitry Andric     const Args &target_arguments =
3351c0981da4SDimitry Andric         target->GetProcessLaunchInfo().GetArguments();
3352c0981da4SDimitry Andric     m_arguments_field->AddArguments(target_arguments);
3353c0981da4SDimitry Andric   }
3354c0981da4SDimitry Andric 
SetTargetEnvironmentFieldDefaultValue()3355c0981da4SDimitry Andric   void SetTargetEnvironmentFieldDefaultValue() {
3356c0981da4SDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3357c0981da4SDimitry Andric     if (target == nullptr)
3358c0981da4SDimitry Andric       return;
3359c0981da4SDimitry Andric 
3360c0981da4SDimitry Andric     const Environment &target_environment = target->GetTargetEnvironment();
3361c0981da4SDimitry Andric     m_target_environment_field->AddEnvironmentVariables(target_environment);
3362c0981da4SDimitry Andric   }
3363c0981da4SDimitry Andric 
SetInheritedEnvironmentFieldDefaultValue()3364c0981da4SDimitry Andric   void SetInheritedEnvironmentFieldDefaultValue() {
3365c0981da4SDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3366c0981da4SDimitry Andric     if (target == nullptr)
3367c0981da4SDimitry Andric       return;
3368c0981da4SDimitry Andric 
3369c0981da4SDimitry Andric     const Environment &inherited_environment =
3370c0981da4SDimitry Andric         target->GetInheritedEnvironment();
3371c0981da4SDimitry Andric     m_inherited_environment_field->AddEnvironmentVariables(
3372c0981da4SDimitry Andric         inherited_environment);
3373c0981da4SDimitry Andric   }
3374c0981da4SDimitry Andric 
GetDefaultWorkingDirectory()3375c0981da4SDimitry Andric   std::string GetDefaultWorkingDirectory() {
3376c0981da4SDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3377c0981da4SDimitry Andric     if (target == nullptr)
3378c0981da4SDimitry Andric       return "";
3379c0981da4SDimitry Andric 
3380c0981da4SDimitry Andric     PlatformSP platform = target->GetPlatform();
3381c0981da4SDimitry Andric     return platform->GetWorkingDirectory().GetPath();
3382c0981da4SDimitry Andric   }
3383c0981da4SDimitry Andric 
GetDefaultDisableASLR()3384c0981da4SDimitry Andric   bool GetDefaultDisableASLR() {
3385c0981da4SDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3386c0981da4SDimitry Andric     if (target == nullptr)
3387c0981da4SDimitry Andric       return false;
3388c0981da4SDimitry Andric 
3389c0981da4SDimitry Andric     return target->GetDisableASLR();
3390c0981da4SDimitry Andric   }
3391c0981da4SDimitry Andric 
GetDefaultDisableStandardIO()3392c0981da4SDimitry Andric   bool GetDefaultDisableStandardIO() {
3393c0981da4SDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3394c0981da4SDimitry Andric     if (target == nullptr)
3395c0981da4SDimitry Andric       return true;
3396c0981da4SDimitry Andric 
3397c0981da4SDimitry Andric     return target->GetDisableSTDIO();
3398c0981da4SDimitry Andric   }
3399c0981da4SDimitry Andric 
GetDefaultDetachOnError()3400c0981da4SDimitry Andric   bool GetDefaultDetachOnError() {
3401c0981da4SDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3402c0981da4SDimitry Andric     if (target == nullptr)
3403c0981da4SDimitry Andric       return true;
3404c0981da4SDimitry Andric 
3405c0981da4SDimitry Andric     return target->GetDetachOnError();
3406c0981da4SDimitry Andric   }
3407c0981da4SDimitry Andric 
3408c0981da4SDimitry Andric   // Methods for getting the necessary information and setting them to the
3409c0981da4SDimitry Andric   // ProcessLaunchInfo.
3410c0981da4SDimitry Andric 
GetExecutableSettings(ProcessLaunchInfo & launch_info)3411c0981da4SDimitry Andric   void GetExecutableSettings(ProcessLaunchInfo &launch_info) {
3412c0981da4SDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3413c0981da4SDimitry Andric     ModuleSP executable_module = target->GetExecutableModule();
3414c0981da4SDimitry Andric     llvm::StringRef target_settings_argv0 = target->GetArg0();
3415c0981da4SDimitry Andric 
3416c0981da4SDimitry Andric     if (!target_settings_argv0.empty()) {
3417c0981da4SDimitry Andric       launch_info.GetArguments().AppendArgument(target_settings_argv0);
3418c0981da4SDimitry Andric       launch_info.SetExecutableFile(executable_module->GetPlatformFileSpec(),
3419c0981da4SDimitry Andric                                     false);
3420c0981da4SDimitry Andric       return;
3421c0981da4SDimitry Andric     }
3422c0981da4SDimitry Andric 
3423c0981da4SDimitry Andric     launch_info.SetExecutableFile(executable_module->GetPlatformFileSpec(),
3424c0981da4SDimitry Andric                                   true);
3425c0981da4SDimitry Andric   }
3426c0981da4SDimitry Andric 
GetArguments(ProcessLaunchInfo & launch_info)3427c0981da4SDimitry Andric   void GetArguments(ProcessLaunchInfo &launch_info) {
3428c0981da4SDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3429c0981da4SDimitry Andric     Args arguments = m_arguments_field->GetArguments();
3430c0981da4SDimitry Andric     launch_info.GetArguments().AppendArguments(arguments);
3431c0981da4SDimitry Andric   }
3432c0981da4SDimitry Andric 
GetEnvironment(ProcessLaunchInfo & launch_info)3433c0981da4SDimitry Andric   void GetEnvironment(ProcessLaunchInfo &launch_info) {
3434c0981da4SDimitry Andric     Environment target_environment =
3435c0981da4SDimitry Andric         m_target_environment_field->GetEnvironment();
3436c0981da4SDimitry Andric     Environment inherited_environment =
3437c0981da4SDimitry Andric         m_inherited_environment_field->GetEnvironment();
3438c0981da4SDimitry Andric     launch_info.GetEnvironment().insert(target_environment.begin(),
3439c0981da4SDimitry Andric                                         target_environment.end());
3440c0981da4SDimitry Andric     launch_info.GetEnvironment().insert(inherited_environment.begin(),
3441c0981da4SDimitry Andric                                         inherited_environment.end());
3442c0981da4SDimitry Andric   }
3443c0981da4SDimitry Andric 
GetWorkingDirectory(ProcessLaunchInfo & launch_info)3444c0981da4SDimitry Andric   void GetWorkingDirectory(ProcessLaunchInfo &launch_info) {
3445c0981da4SDimitry Andric     if (m_working_directory_field->IsSpecified())
3446c0981da4SDimitry Andric       launch_info.SetWorkingDirectory(
3447c0981da4SDimitry Andric           m_working_directory_field->GetResolvedFileSpec());
3448c0981da4SDimitry Andric   }
3449c0981da4SDimitry Andric 
GetStopAtEntry(ProcessLaunchInfo & launch_info)3450c0981da4SDimitry Andric   void GetStopAtEntry(ProcessLaunchInfo &launch_info) {
3451c0981da4SDimitry Andric     if (m_stop_at_entry_field->GetBoolean())
3452c0981da4SDimitry Andric       launch_info.GetFlags().Set(eLaunchFlagStopAtEntry);
3453c0981da4SDimitry Andric     else
3454c0981da4SDimitry Andric       launch_info.GetFlags().Clear(eLaunchFlagStopAtEntry);
3455c0981da4SDimitry Andric   }
3456c0981da4SDimitry Andric 
GetDetachOnError(ProcessLaunchInfo & launch_info)3457c0981da4SDimitry Andric   void GetDetachOnError(ProcessLaunchInfo &launch_info) {
3458c0981da4SDimitry Andric     if (m_detach_on_error_field->GetBoolean())
3459c0981da4SDimitry Andric       launch_info.GetFlags().Set(eLaunchFlagDetachOnError);
3460c0981da4SDimitry Andric     else
3461c0981da4SDimitry Andric       launch_info.GetFlags().Clear(eLaunchFlagDetachOnError);
3462c0981da4SDimitry Andric   }
3463c0981da4SDimitry Andric 
GetDisableASLR(ProcessLaunchInfo & launch_info)3464c0981da4SDimitry Andric   void GetDisableASLR(ProcessLaunchInfo &launch_info) {
3465c0981da4SDimitry Andric     if (m_disable_aslr_field->GetBoolean())
3466c0981da4SDimitry Andric       launch_info.GetFlags().Set(eLaunchFlagDisableASLR);
3467c0981da4SDimitry Andric     else
3468c0981da4SDimitry Andric       launch_info.GetFlags().Clear(eLaunchFlagDisableASLR);
3469c0981da4SDimitry Andric   }
3470c0981da4SDimitry Andric 
GetPlugin(ProcessLaunchInfo & launch_info)3471c0981da4SDimitry Andric   void GetPlugin(ProcessLaunchInfo &launch_info) {
3472c0981da4SDimitry Andric     launch_info.SetProcessPluginName(m_plugin_field->GetPluginName());
3473c0981da4SDimitry Andric   }
3474c0981da4SDimitry Andric 
GetArch(ProcessLaunchInfo & launch_info)3475c0981da4SDimitry Andric   void GetArch(ProcessLaunchInfo &launch_info) {
3476c0981da4SDimitry Andric     if (!m_arch_field->IsSpecified())
3477c0981da4SDimitry Andric       return;
3478c0981da4SDimitry Andric 
3479c0981da4SDimitry Andric     TargetSP target_sp = m_debugger.GetSelectedTarget();
3480c0981da4SDimitry Andric     PlatformSP platform_sp =
3481c0981da4SDimitry Andric         target_sp ? target_sp->GetPlatform() : PlatformSP();
3482c0981da4SDimitry Andric     launch_info.GetArchitecture() = Platform::GetAugmentedArchSpec(
3483c0981da4SDimitry Andric         platform_sp.get(), m_arch_field->GetArchString());
3484c0981da4SDimitry Andric   }
3485c0981da4SDimitry Andric 
GetShell(ProcessLaunchInfo & launch_info)3486c0981da4SDimitry Andric   void GetShell(ProcessLaunchInfo &launch_info) {
3487c0981da4SDimitry Andric     if (!m_shell_field->IsSpecified())
3488c0981da4SDimitry Andric       return;
3489c0981da4SDimitry Andric 
3490c0981da4SDimitry Andric     launch_info.SetShell(m_shell_field->GetResolvedFileSpec());
3491c0981da4SDimitry Andric     launch_info.SetShellExpandArguments(
3492c0981da4SDimitry Andric         m_expand_shell_arguments_field->GetBoolean());
3493c0981da4SDimitry Andric   }
3494c0981da4SDimitry Andric 
GetStandardIO(ProcessLaunchInfo & launch_info)3495c0981da4SDimitry Andric   void GetStandardIO(ProcessLaunchInfo &launch_info) {
3496c0981da4SDimitry Andric     if (m_disable_standard_io_field->GetBoolean()) {
3497c0981da4SDimitry Andric       launch_info.GetFlags().Set(eLaunchFlagDisableSTDIO);
3498c0981da4SDimitry Andric       return;
3499c0981da4SDimitry Andric     }
3500c0981da4SDimitry Andric 
3501c0981da4SDimitry Andric     FileAction action;
3502c0981da4SDimitry Andric     if (m_standard_input_field->IsSpecified()) {
3503e3b55780SDimitry Andric       if (action.Open(STDIN_FILENO, m_standard_input_field->GetFileSpec(), true,
3504e3b55780SDimitry Andric                       false))
3505c0981da4SDimitry Andric         launch_info.AppendFileAction(action);
3506c0981da4SDimitry Andric     }
3507c0981da4SDimitry Andric     if (m_standard_output_field->IsSpecified()) {
3508e3b55780SDimitry Andric       if (action.Open(STDOUT_FILENO, m_standard_output_field->GetFileSpec(),
3509e3b55780SDimitry Andric                       false, true))
3510c0981da4SDimitry Andric         launch_info.AppendFileAction(action);
3511c0981da4SDimitry Andric     }
3512c0981da4SDimitry Andric     if (m_standard_error_field->IsSpecified()) {
3513e3b55780SDimitry Andric       if (action.Open(STDERR_FILENO, m_standard_error_field->GetFileSpec(),
3514e3b55780SDimitry Andric                       false, true))
3515c0981da4SDimitry Andric         launch_info.AppendFileAction(action);
3516c0981da4SDimitry Andric     }
3517c0981da4SDimitry Andric   }
3518c0981da4SDimitry Andric 
GetInheritTCC(ProcessLaunchInfo & launch_info)3519c0981da4SDimitry Andric   void GetInheritTCC(ProcessLaunchInfo &launch_info) {
3520c0981da4SDimitry Andric     if (m_debugger.GetSelectedTarget()->GetInheritTCC())
3521c0981da4SDimitry Andric       launch_info.GetFlags().Set(eLaunchFlagInheritTCCFromParent);
3522c0981da4SDimitry Andric   }
3523c0981da4SDimitry Andric 
GetLaunchInfo()3524c0981da4SDimitry Andric   ProcessLaunchInfo GetLaunchInfo() {
3525c0981da4SDimitry Andric     ProcessLaunchInfo launch_info;
3526c0981da4SDimitry Andric 
3527c0981da4SDimitry Andric     GetExecutableSettings(launch_info);
3528c0981da4SDimitry Andric     GetArguments(launch_info);
3529c0981da4SDimitry Andric     GetEnvironment(launch_info);
3530c0981da4SDimitry Andric     GetWorkingDirectory(launch_info);
3531c0981da4SDimitry Andric     GetStopAtEntry(launch_info);
3532c0981da4SDimitry Andric     GetDetachOnError(launch_info);
3533c0981da4SDimitry Andric     GetDisableASLR(launch_info);
3534c0981da4SDimitry Andric     GetPlugin(launch_info);
3535c0981da4SDimitry Andric     GetArch(launch_info);
3536c0981da4SDimitry Andric     GetShell(launch_info);
3537c0981da4SDimitry Andric     GetStandardIO(launch_info);
3538c0981da4SDimitry Andric     GetInheritTCC(launch_info);
3539c0981da4SDimitry Andric 
3540c0981da4SDimitry Andric     return launch_info;
3541c0981da4SDimitry Andric   }
3542c0981da4SDimitry Andric 
StopRunningProcess()3543c0981da4SDimitry Andric   bool StopRunningProcess() {
3544c0981da4SDimitry Andric     ExecutionContext exe_ctx =
3545c0981da4SDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext();
3546c0981da4SDimitry Andric 
3547c0981da4SDimitry Andric     if (!exe_ctx.HasProcessScope())
3548c0981da4SDimitry Andric       return false;
3549c0981da4SDimitry Andric 
3550c0981da4SDimitry Andric     Process *process = exe_ctx.GetProcessPtr();
3551c0981da4SDimitry Andric     if (!(process && process->IsAlive()))
3552c0981da4SDimitry Andric       return false;
3553c0981da4SDimitry Andric 
3554c0981da4SDimitry Andric     FormDelegateSP form_delegate_sp =
3555c0981da4SDimitry Andric         FormDelegateSP(new DetachOrKillProcessFormDelegate(process));
3556c0981da4SDimitry Andric     Rect bounds = m_main_window_sp->GetCenteredRect(85, 8);
3557c0981da4SDimitry Andric     WindowSP form_window_sp = m_main_window_sp->CreateSubWindow(
3558c0981da4SDimitry Andric         form_delegate_sp->GetName().c_str(), bounds, true);
3559c0981da4SDimitry Andric     WindowDelegateSP window_delegate_sp =
3560c0981da4SDimitry Andric         WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
3561c0981da4SDimitry Andric     form_window_sp->SetDelegate(window_delegate_sp);
3562c0981da4SDimitry Andric 
3563c0981da4SDimitry Andric     return true;
3564c0981da4SDimitry Andric   }
3565c0981da4SDimitry Andric 
GetTarget()3566c0981da4SDimitry Andric   Target *GetTarget() {
3567c0981da4SDimitry Andric     Target *target = m_debugger.GetSelectedTarget().get();
3568c0981da4SDimitry Andric 
3569c0981da4SDimitry Andric     if (target == nullptr) {
3570c0981da4SDimitry Andric       SetError("No target exists!");
3571c0981da4SDimitry Andric       return nullptr;
3572c0981da4SDimitry Andric     }
3573c0981da4SDimitry Andric 
3574c0981da4SDimitry Andric     ModuleSP exe_module_sp = target->GetExecutableModule();
3575c0981da4SDimitry Andric 
3576c0981da4SDimitry Andric     if (exe_module_sp == nullptr) {
3577c0981da4SDimitry Andric       SetError("No executable in target!");
3578c0981da4SDimitry Andric       return nullptr;
3579c0981da4SDimitry Andric     }
3580c0981da4SDimitry Andric 
3581c0981da4SDimitry Andric     return target;
3582c0981da4SDimitry Andric   }
3583c0981da4SDimitry Andric 
Launch(Window & window)3584c0981da4SDimitry Andric   void Launch(Window &window) {
3585c0981da4SDimitry Andric     ClearError();
3586c0981da4SDimitry Andric 
3587c0981da4SDimitry Andric     bool all_fields_are_valid = CheckFieldsValidity();
3588c0981da4SDimitry Andric     if (!all_fields_are_valid)
3589c0981da4SDimitry Andric       return;
3590c0981da4SDimitry Andric 
3591c0981da4SDimitry Andric     bool process_is_running = StopRunningProcess();
3592c0981da4SDimitry Andric     if (process_is_running)
3593c0981da4SDimitry Andric       return;
3594c0981da4SDimitry Andric 
3595c0981da4SDimitry Andric     Target *target = GetTarget();
3596c0981da4SDimitry Andric     if (HasError())
3597c0981da4SDimitry Andric       return;
3598c0981da4SDimitry Andric 
3599c0981da4SDimitry Andric     StreamString stream;
3600c0981da4SDimitry Andric     ProcessLaunchInfo launch_info = GetLaunchInfo();
3601c0981da4SDimitry Andric     Status status = target->Launch(launch_info, &stream);
3602c0981da4SDimitry Andric 
3603c0981da4SDimitry Andric     if (status.Fail()) {
3604c0981da4SDimitry Andric       SetError(status.AsCString());
3605c0981da4SDimitry Andric       return;
3606c0981da4SDimitry Andric     }
3607c0981da4SDimitry Andric 
3608c0981da4SDimitry Andric     ProcessSP process_sp(target->GetProcessSP());
3609c0981da4SDimitry Andric     if (!process_sp) {
3610c0981da4SDimitry Andric       SetError("Launched successfully but target has no process!");
3611c0981da4SDimitry Andric       return;
3612c0981da4SDimitry Andric     }
3613c0981da4SDimitry Andric 
3614c0981da4SDimitry Andric     window.GetParent()->RemoveSubWindow(&window);
3615c0981da4SDimitry Andric   }
3616c0981da4SDimitry Andric 
3617c0981da4SDimitry Andric protected:
3618c0981da4SDimitry Andric   Debugger &m_debugger;
3619c0981da4SDimitry Andric   WindowSP m_main_window_sp;
3620c0981da4SDimitry Andric 
3621c0981da4SDimitry Andric   ArgumentsFieldDelegate *m_arguments_field;
3622c0981da4SDimitry Andric   EnvironmentVariableListFieldDelegate *m_target_environment_field;
3623c0981da4SDimitry Andric   DirectoryFieldDelegate *m_working_directory_field;
3624c0981da4SDimitry Andric 
3625c0981da4SDimitry Andric   BooleanFieldDelegate *m_show_advanced_field;
3626c0981da4SDimitry Andric 
3627c0981da4SDimitry Andric   BooleanFieldDelegate *m_stop_at_entry_field;
3628c0981da4SDimitry Andric   BooleanFieldDelegate *m_detach_on_error_field;
3629c0981da4SDimitry Andric   BooleanFieldDelegate *m_disable_aslr_field;
3630c0981da4SDimitry Andric   ProcessPluginFieldDelegate *m_plugin_field;
3631c0981da4SDimitry Andric   ArchFieldDelegate *m_arch_field;
3632c0981da4SDimitry Andric   FileFieldDelegate *m_shell_field;
3633c0981da4SDimitry Andric   BooleanFieldDelegate *m_expand_shell_arguments_field;
3634c0981da4SDimitry Andric   BooleanFieldDelegate *m_disable_standard_io_field;
3635c0981da4SDimitry Andric   FileFieldDelegate *m_standard_input_field;
3636c0981da4SDimitry Andric   FileFieldDelegate *m_standard_output_field;
3637c0981da4SDimitry Andric   FileFieldDelegate *m_standard_error_field;
3638c0981da4SDimitry Andric 
3639c0981da4SDimitry Andric   BooleanFieldDelegate *m_show_inherited_environment_field;
3640c0981da4SDimitry Andric   EnvironmentVariableListFieldDelegate *m_inherited_environment_field;
3641c0981da4SDimitry Andric };
3642c0981da4SDimitry Andric 
3643c0981da4SDimitry Andric ////////////
3644c0981da4SDimitry Andric // Searchers
3645c0981da4SDimitry Andric ////////////
3646c0981da4SDimitry Andric 
3647c0981da4SDimitry Andric class SearcherDelegate {
3648c0981da4SDimitry Andric public:
3649145449b1SDimitry Andric   SearcherDelegate() = default;
3650c0981da4SDimitry Andric 
3651c0981da4SDimitry Andric   virtual ~SearcherDelegate() = default;
3652c0981da4SDimitry Andric 
3653c0981da4SDimitry Andric   virtual int GetNumberOfMatches() = 0;
3654c0981da4SDimitry Andric 
3655c0981da4SDimitry Andric   // Get the string that will be displayed for the match at the input index.
3656c0981da4SDimitry Andric   virtual const std::string &GetMatchTextAtIndex(int index) = 0;
3657c0981da4SDimitry Andric 
3658c0981da4SDimitry Andric   // Update the matches of the search. This is executed every time the text
3659c0981da4SDimitry Andric   // field handles an event.
3660c0981da4SDimitry Andric   virtual void UpdateMatches(const std::string &text) = 0;
3661c0981da4SDimitry Andric 
3662c0981da4SDimitry Andric   // Execute the user callback given the index of some match. This is executed
3663c0981da4SDimitry Andric   // once the user selects a match.
3664c0981da4SDimitry Andric   virtual void ExecuteCallback(int match_index) = 0;
3665c0981da4SDimitry Andric };
3666c0981da4SDimitry Andric 
3667c0981da4SDimitry Andric typedef std::shared_ptr<SearcherDelegate> SearcherDelegateSP;
3668c0981da4SDimitry Andric 
3669c0981da4SDimitry Andric class SearcherWindowDelegate : public WindowDelegate {
3670c0981da4SDimitry Andric public:
SearcherWindowDelegate(SearcherDelegateSP & delegate_sp)3671c0981da4SDimitry Andric   SearcherWindowDelegate(SearcherDelegateSP &delegate_sp)
3672145449b1SDimitry Andric       : m_delegate_sp(delegate_sp), m_text_field("Search", "", false) {
3673c0981da4SDimitry Andric     ;
3674c0981da4SDimitry Andric   }
3675c0981da4SDimitry Andric 
3676c0981da4SDimitry Andric   // A completion window is padded by one character from all sides. A text field
3677c0981da4SDimitry Andric   // is first drawn for inputting the searcher request, then a list of matches
3678c0981da4SDimitry Andric   // are displayed in a scrollable list.
3679c0981da4SDimitry Andric   //
3680c0981da4SDimitry Andric   // ___<Searcher Window Name>____________________________
3681c0981da4SDimitry Andric   // |                                                   |
3682c0981da4SDimitry Andric   // | __[Search]_______________________________________ |
3683c0981da4SDimitry Andric   // | |                                               | |
3684c0981da4SDimitry Andric   // | |_______________________________________________| |
3685c0981da4SDimitry Andric   // | - Match 1.                                        |
3686c0981da4SDimitry Andric   // | - Match 2.                                        |
3687c0981da4SDimitry Andric   // | - ...                                             |
3688c0981da4SDimitry Andric   // |                                                   |
3689c0981da4SDimitry Andric   // |____________________________[Press Esc to Cancel]__|
3690c0981da4SDimitry Andric   //
3691c0981da4SDimitry Andric 
3692c0981da4SDimitry Andric   // Get the index of the last visible match. Assuming at least one match
3693c0981da4SDimitry Andric   // exists.
GetLastVisibleMatch(int height)3694c0981da4SDimitry Andric   int GetLastVisibleMatch(int height) {
3695c0981da4SDimitry Andric     int index = m_first_visible_match + height;
3696c0981da4SDimitry Andric     return std::min(index, m_delegate_sp->GetNumberOfMatches()) - 1;
3697c0981da4SDimitry Andric   }
3698c0981da4SDimitry Andric 
GetNumberOfVisibleMatches(int height)3699c0981da4SDimitry Andric   int GetNumberOfVisibleMatches(int height) {
3700c0981da4SDimitry Andric     return GetLastVisibleMatch(height) - m_first_visible_match + 1;
3701c0981da4SDimitry Andric   }
3702c0981da4SDimitry Andric 
UpdateScrolling(Surface & surface)3703c0981da4SDimitry Andric   void UpdateScrolling(Surface &surface) {
3704c0981da4SDimitry Andric     if (m_selected_match < m_first_visible_match) {
3705c0981da4SDimitry Andric       m_first_visible_match = m_selected_match;
3706c0981da4SDimitry Andric       return;
3707c0981da4SDimitry Andric     }
3708c0981da4SDimitry Andric 
3709c0981da4SDimitry Andric     int height = surface.GetHeight();
3710c0981da4SDimitry Andric     int last_visible_match = GetLastVisibleMatch(height);
3711c0981da4SDimitry Andric     if (m_selected_match > last_visible_match) {
3712c0981da4SDimitry Andric       m_first_visible_match = m_selected_match - height + 1;
3713c0981da4SDimitry Andric     }
3714c0981da4SDimitry Andric   }
3715c0981da4SDimitry Andric 
DrawMatches(Surface & surface)3716c0981da4SDimitry Andric   void DrawMatches(Surface &surface) {
3717c0981da4SDimitry Andric     if (m_delegate_sp->GetNumberOfMatches() == 0)
3718c0981da4SDimitry Andric       return;
3719c0981da4SDimitry Andric 
3720c0981da4SDimitry Andric     UpdateScrolling(surface);
3721c0981da4SDimitry Andric 
3722c0981da4SDimitry Andric     int count = GetNumberOfVisibleMatches(surface.GetHeight());
3723c0981da4SDimitry Andric     for (int i = 0; i < count; i++) {
3724c0981da4SDimitry Andric       surface.MoveCursor(1, i);
3725c0981da4SDimitry Andric       int current_match = m_first_visible_match + i;
3726c0981da4SDimitry Andric       if (current_match == m_selected_match)
3727c0981da4SDimitry Andric         surface.AttributeOn(A_REVERSE);
3728c0981da4SDimitry Andric       surface.PutCString(
3729c0981da4SDimitry Andric           m_delegate_sp->GetMatchTextAtIndex(current_match).c_str());
3730c0981da4SDimitry Andric       if (current_match == m_selected_match)
3731c0981da4SDimitry Andric         surface.AttributeOff(A_REVERSE);
3732c0981da4SDimitry Andric     }
3733c0981da4SDimitry Andric   }
3734c0981da4SDimitry Andric 
DrawContent(Surface & surface)3735c0981da4SDimitry Andric   void DrawContent(Surface &surface) {
3736c0981da4SDimitry Andric     Rect content_bounds = surface.GetFrame();
3737c0981da4SDimitry Andric     Rect text_field_bounds, matchs_bounds;
3738c0981da4SDimitry Andric     content_bounds.HorizontalSplit(m_text_field.FieldDelegateGetHeight(),
3739c0981da4SDimitry Andric                                    text_field_bounds, matchs_bounds);
3740c0981da4SDimitry Andric     Surface text_field_surface = surface.SubSurface(text_field_bounds);
3741c0981da4SDimitry Andric     Surface matches_surface = surface.SubSurface(matchs_bounds);
3742c0981da4SDimitry Andric 
3743c0981da4SDimitry Andric     m_text_field.FieldDelegateDraw(text_field_surface, true);
3744c0981da4SDimitry Andric     DrawMatches(matches_surface);
3745c0981da4SDimitry Andric   }
3746c0981da4SDimitry Andric 
WindowDelegateDraw(Window & window,bool force)3747c0981da4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
3748c0981da4SDimitry Andric     window.Erase();
3749c0981da4SDimitry Andric 
3750c0981da4SDimitry Andric     window.DrawTitleBox(window.GetName(), "Press Esc to Cancel");
3751c0981da4SDimitry Andric 
3752c0981da4SDimitry Andric     Rect content_bounds = window.GetFrame();
3753c0981da4SDimitry Andric     content_bounds.Inset(2, 2);
3754c0981da4SDimitry Andric     Surface content_surface = window.SubSurface(content_bounds);
3755c0981da4SDimitry Andric 
3756c0981da4SDimitry Andric     DrawContent(content_surface);
3757c0981da4SDimitry Andric     return true;
3758c0981da4SDimitry Andric   }
3759c0981da4SDimitry Andric 
SelectNext()3760c0981da4SDimitry Andric   void SelectNext() {
3761c0981da4SDimitry Andric     if (m_selected_match != m_delegate_sp->GetNumberOfMatches() - 1)
3762c0981da4SDimitry Andric       m_selected_match++;
3763c0981da4SDimitry Andric   }
3764c0981da4SDimitry Andric 
SelectPrevious()3765c0981da4SDimitry Andric   void SelectPrevious() {
3766c0981da4SDimitry Andric     if (m_selected_match != 0)
3767c0981da4SDimitry Andric       m_selected_match--;
3768c0981da4SDimitry Andric   }
3769c0981da4SDimitry Andric 
ExecuteCallback(Window & window)3770c0981da4SDimitry Andric   void ExecuteCallback(Window &window) {
3771c0981da4SDimitry Andric     m_delegate_sp->ExecuteCallback(m_selected_match);
3772c0981da4SDimitry Andric     window.GetParent()->RemoveSubWindow(&window);
3773c0981da4SDimitry Andric   }
3774c0981da4SDimitry Andric 
UpdateMatches()3775c0981da4SDimitry Andric   void UpdateMatches() {
3776c0981da4SDimitry Andric     m_delegate_sp->UpdateMatches(m_text_field.GetText());
3777c0981da4SDimitry Andric     m_selected_match = 0;
3778c0981da4SDimitry Andric   }
3779c0981da4SDimitry Andric 
WindowDelegateHandleChar(Window & window,int key)3780c0981da4SDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
3781c0981da4SDimitry Andric     switch (key) {
3782c0981da4SDimitry Andric     case '\r':
3783c0981da4SDimitry Andric     case '\n':
3784c0981da4SDimitry Andric     case KEY_ENTER:
3785c0981da4SDimitry Andric       ExecuteCallback(window);
3786c0981da4SDimitry Andric       return eKeyHandled;
3787c0981da4SDimitry Andric     case '\t':
3788c0981da4SDimitry Andric     case KEY_DOWN:
3789c0981da4SDimitry Andric       SelectNext();
3790c0981da4SDimitry Andric       return eKeyHandled;
3791c0981da4SDimitry Andric     case KEY_SHIFT_TAB:
3792c0981da4SDimitry Andric     case KEY_UP:
3793c0981da4SDimitry Andric       SelectPrevious();
3794c0981da4SDimitry Andric       return eKeyHandled;
3795c0981da4SDimitry Andric     case KEY_ESCAPE:
3796c0981da4SDimitry Andric       window.GetParent()->RemoveSubWindow(&window);
3797c0981da4SDimitry Andric       return eKeyHandled;
3798c0981da4SDimitry Andric     default:
3799c0981da4SDimitry Andric       break;
3800c0981da4SDimitry Andric     }
3801c0981da4SDimitry Andric 
3802c0981da4SDimitry Andric     if (m_text_field.FieldDelegateHandleChar(key) == eKeyHandled)
3803c0981da4SDimitry Andric       UpdateMatches();
3804c0981da4SDimitry Andric 
3805c0981da4SDimitry Andric     return eKeyHandled;
3806c0981da4SDimitry Andric   }
3807c0981da4SDimitry Andric 
3808c0981da4SDimitry Andric protected:
3809c0981da4SDimitry Andric   SearcherDelegateSP m_delegate_sp;
3810c0981da4SDimitry Andric   TextFieldDelegate m_text_field;
3811c0981da4SDimitry Andric   // The index of the currently selected match.
3812145449b1SDimitry Andric   int m_selected_match = 0;
3813c0981da4SDimitry Andric   // The index of the first visible match.
3814145449b1SDimitry Andric   int m_first_visible_match = 0;
3815c0981da4SDimitry Andric };
3816c0981da4SDimitry Andric 
3817c0981da4SDimitry Andric //////////////////////////////
3818c0981da4SDimitry Andric // Searcher Delegate Instances
3819c0981da4SDimitry Andric //////////////////////////////
3820c0981da4SDimitry Andric 
3821c0981da4SDimitry Andric // This is a searcher delegate wrapper around CommandCompletions common
3822c0981da4SDimitry Andric // callbacks. The callbacks are only given the match string. The completion_mask
38237fa27ce4SDimitry Andric // can be a combination of lldb::CompletionType.
3824c0981da4SDimitry Andric class CommonCompletionSearcherDelegate : public SearcherDelegate {
3825c0981da4SDimitry Andric public:
3826c0981da4SDimitry Andric   typedef std::function<void(const std::string &)> CallbackType;
3827c0981da4SDimitry Andric 
CommonCompletionSearcherDelegate(Debugger & debugger,uint32_t completion_mask,CallbackType callback)3828c0981da4SDimitry Andric   CommonCompletionSearcherDelegate(Debugger &debugger, uint32_t completion_mask,
3829c0981da4SDimitry Andric                                    CallbackType callback)
3830c0981da4SDimitry Andric       : m_debugger(debugger), m_completion_mask(completion_mask),
3831c0981da4SDimitry Andric         m_callback(callback) {}
3832c0981da4SDimitry Andric 
GetNumberOfMatches()3833c0981da4SDimitry Andric   int GetNumberOfMatches() override { return m_matches.GetSize(); }
3834c0981da4SDimitry Andric 
GetMatchTextAtIndex(int index)3835c0981da4SDimitry Andric   const std::string &GetMatchTextAtIndex(int index) override {
3836c0981da4SDimitry Andric     return m_matches[index];
3837c0981da4SDimitry Andric   }
3838c0981da4SDimitry Andric 
UpdateMatches(const std::string & text)3839c0981da4SDimitry Andric   void UpdateMatches(const std::string &text) override {
3840c0981da4SDimitry Andric     CompletionResult result;
3841c0981da4SDimitry Andric     CompletionRequest request(text.c_str(), text.size(), result);
38427fa27ce4SDimitry Andric     lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
3843c0981da4SDimitry Andric         m_debugger.GetCommandInterpreter(), m_completion_mask, request,
3844c0981da4SDimitry Andric         nullptr);
3845c0981da4SDimitry Andric     result.GetMatches(m_matches);
3846c0981da4SDimitry Andric   }
3847c0981da4SDimitry Andric 
ExecuteCallback(int match_index)3848c0981da4SDimitry Andric   void ExecuteCallback(int match_index) override {
3849c0981da4SDimitry Andric     m_callback(m_matches[match_index]);
3850c0981da4SDimitry Andric   }
3851c0981da4SDimitry Andric 
3852c0981da4SDimitry Andric protected:
3853c0981da4SDimitry Andric   Debugger &m_debugger;
38547fa27ce4SDimitry Andric   // A compound mask from lldb::CompletionType.
3855c0981da4SDimitry Andric   uint32_t m_completion_mask;
3856c0981da4SDimitry Andric   // A callback to execute once the user selects a match. The match is passed to
3857c0981da4SDimitry Andric   // the callback as a string.
3858c0981da4SDimitry Andric   CallbackType m_callback;
3859c0981da4SDimitry Andric   StringList m_matches;
3860c0981da4SDimitry Andric };
3861c0981da4SDimitry Andric 
3862c0981da4SDimitry Andric ////////
3863c0981da4SDimitry Andric // Menus
3864c0981da4SDimitry Andric ////////
3865c0981da4SDimitry Andric 
3866706b4fc4SDimitry Andric class MenuDelegate {
3867706b4fc4SDimitry Andric public:
3868706b4fc4SDimitry Andric   virtual ~MenuDelegate() = default;
3869706b4fc4SDimitry Andric 
3870706b4fc4SDimitry Andric   virtual MenuActionResult MenuDelegateAction(Menu &menu) = 0;
3871706b4fc4SDimitry Andric };
3872706b4fc4SDimitry Andric 
3873706b4fc4SDimitry Andric class Menu : public WindowDelegate {
3874706b4fc4SDimitry Andric public:
3875706b4fc4SDimitry Andric   enum class Type { Invalid, Bar, Item, Separator };
3876706b4fc4SDimitry Andric 
3877706b4fc4SDimitry Andric   // Menubar or separator constructor
3878706b4fc4SDimitry Andric   Menu(Type type);
3879706b4fc4SDimitry Andric 
3880706b4fc4SDimitry Andric   // Menuitem constructor
3881706b4fc4SDimitry Andric   Menu(const char *name, const char *key_name, int key_value,
3882706b4fc4SDimitry Andric        uint64_t identifier);
3883706b4fc4SDimitry Andric 
3884706b4fc4SDimitry Andric   ~Menu() override = default;
3885706b4fc4SDimitry Andric 
GetDelegate() const3886706b4fc4SDimitry Andric   const MenuDelegateSP &GetDelegate() const { return m_delegate_sp; }
3887706b4fc4SDimitry Andric 
SetDelegate(const MenuDelegateSP & delegate_sp)3888706b4fc4SDimitry Andric   void SetDelegate(const MenuDelegateSP &delegate_sp) {
3889706b4fc4SDimitry Andric     m_delegate_sp = delegate_sp;
3890706b4fc4SDimitry Andric   }
3891706b4fc4SDimitry Andric 
3892706b4fc4SDimitry Andric   void RecalculateNameLengths();
3893706b4fc4SDimitry Andric 
3894706b4fc4SDimitry Andric   void AddSubmenu(const MenuSP &menu_sp);
3895706b4fc4SDimitry Andric 
3896706b4fc4SDimitry Andric   int DrawAndRunMenu(Window &window);
3897706b4fc4SDimitry Andric 
3898706b4fc4SDimitry Andric   void DrawMenuTitle(Window &window, bool highlight);
3899706b4fc4SDimitry Andric 
3900706b4fc4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override;
3901706b4fc4SDimitry Andric 
3902706b4fc4SDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
3903706b4fc4SDimitry Andric 
ActionPrivate(Menu & menu)3904706b4fc4SDimitry Andric   MenuActionResult ActionPrivate(Menu &menu) {
3905706b4fc4SDimitry Andric     MenuActionResult result = MenuActionResult::NotHandled;
3906706b4fc4SDimitry Andric     if (m_delegate_sp) {
3907706b4fc4SDimitry Andric       result = m_delegate_sp->MenuDelegateAction(menu);
3908706b4fc4SDimitry Andric       if (result != MenuActionResult::NotHandled)
3909706b4fc4SDimitry Andric         return result;
3910706b4fc4SDimitry Andric     } else if (m_parent) {
3911706b4fc4SDimitry Andric       result = m_parent->ActionPrivate(menu);
3912706b4fc4SDimitry Andric       if (result != MenuActionResult::NotHandled)
3913706b4fc4SDimitry Andric         return result;
3914706b4fc4SDimitry Andric     }
3915706b4fc4SDimitry Andric     return m_canned_result;
3916706b4fc4SDimitry Andric   }
3917706b4fc4SDimitry Andric 
Action()3918706b4fc4SDimitry Andric   MenuActionResult Action() {
3919706b4fc4SDimitry Andric     // Call the recursive action so it can try to handle it with the menu
3920706b4fc4SDimitry Andric     // delegate, and if not, try our parent menu
3921706b4fc4SDimitry Andric     return ActionPrivate(*this);
3922706b4fc4SDimitry Andric   }
3923706b4fc4SDimitry Andric 
SetCannedResult(MenuActionResult result)3924706b4fc4SDimitry Andric   void SetCannedResult(MenuActionResult result) { m_canned_result = result; }
3925706b4fc4SDimitry Andric 
GetSubmenus()3926706b4fc4SDimitry Andric   Menus &GetSubmenus() { return m_submenus; }
3927706b4fc4SDimitry Andric 
GetSubmenus() const3928706b4fc4SDimitry Andric   const Menus &GetSubmenus() const { return m_submenus; }
3929706b4fc4SDimitry Andric 
GetSelectedSubmenuIndex() const3930706b4fc4SDimitry Andric   int GetSelectedSubmenuIndex() const { return m_selected; }
3931706b4fc4SDimitry Andric 
SetSelectedSubmenuIndex(int idx)3932706b4fc4SDimitry Andric   void SetSelectedSubmenuIndex(int idx) { m_selected = idx; }
3933706b4fc4SDimitry Andric 
GetType() const3934706b4fc4SDimitry Andric   Type GetType() const { return m_type; }
3935706b4fc4SDimitry Andric 
GetStartingColumn() const3936706b4fc4SDimitry Andric   int GetStartingColumn() const { return m_start_col; }
3937706b4fc4SDimitry Andric 
SetStartingColumn(int col)3938706b4fc4SDimitry Andric   void SetStartingColumn(int col) { m_start_col = col; }
3939706b4fc4SDimitry Andric 
GetKeyValue() const3940706b4fc4SDimitry Andric   int GetKeyValue() const { return m_key_value; }
3941706b4fc4SDimitry Andric 
GetName()3942706b4fc4SDimitry Andric   std::string &GetName() { return m_name; }
3943706b4fc4SDimitry Andric 
GetDrawWidth() const3944706b4fc4SDimitry Andric   int GetDrawWidth() const {
3945706b4fc4SDimitry Andric     return m_max_submenu_name_length + m_max_submenu_key_name_length + 8;
3946706b4fc4SDimitry Andric   }
3947706b4fc4SDimitry Andric 
GetIdentifier() const3948706b4fc4SDimitry Andric   uint64_t GetIdentifier() const { return m_identifier; }
3949706b4fc4SDimitry Andric 
SetIdentifier(uint64_t identifier)3950706b4fc4SDimitry Andric   void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
3951706b4fc4SDimitry Andric 
3952706b4fc4SDimitry Andric protected:
3953706b4fc4SDimitry Andric   std::string m_name;
3954706b4fc4SDimitry Andric   std::string m_key_name;
3955706b4fc4SDimitry Andric   uint64_t m_identifier;
3956706b4fc4SDimitry Andric   Type m_type;
3957706b4fc4SDimitry Andric   int m_key_value;
3958706b4fc4SDimitry Andric   int m_start_col;
3959706b4fc4SDimitry Andric   int m_max_submenu_name_length;
3960706b4fc4SDimitry Andric   int m_max_submenu_key_name_length;
3961706b4fc4SDimitry Andric   int m_selected;
3962706b4fc4SDimitry Andric   Menu *m_parent;
3963706b4fc4SDimitry Andric   Menus m_submenus;
3964706b4fc4SDimitry Andric   WindowSP m_menu_window_sp;
3965706b4fc4SDimitry Andric   MenuActionResult m_canned_result;
3966706b4fc4SDimitry Andric   MenuDelegateSP m_delegate_sp;
3967706b4fc4SDimitry Andric };
3968706b4fc4SDimitry Andric 
3969706b4fc4SDimitry Andric // Menubar or separator constructor
Menu(Type type)3970706b4fc4SDimitry Andric Menu::Menu(Type type)
3971706b4fc4SDimitry Andric     : m_name(), m_key_name(), m_identifier(0), m_type(type), m_key_value(0),
3972706b4fc4SDimitry Andric       m_start_col(0), m_max_submenu_name_length(0),
3973706b4fc4SDimitry Andric       m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
3974706b4fc4SDimitry Andric       m_submenus(), m_canned_result(MenuActionResult::NotHandled),
3975706b4fc4SDimitry Andric       m_delegate_sp() {}
3976706b4fc4SDimitry Andric 
3977706b4fc4SDimitry Andric // Menuitem constructor
Menu(const char * name,const char * key_name,int key_value,uint64_t identifier)3978706b4fc4SDimitry Andric Menu::Menu(const char *name, const char *key_name, int key_value,
3979706b4fc4SDimitry Andric            uint64_t identifier)
3980706b4fc4SDimitry Andric     : m_name(), m_key_name(), m_identifier(identifier), m_type(Type::Invalid),
3981706b4fc4SDimitry Andric       m_key_value(key_value), m_start_col(0), m_max_submenu_name_length(0),
3982706b4fc4SDimitry Andric       m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
3983706b4fc4SDimitry Andric       m_submenus(), m_canned_result(MenuActionResult::NotHandled),
3984706b4fc4SDimitry Andric       m_delegate_sp() {
3985706b4fc4SDimitry Andric   if (name && name[0]) {
3986706b4fc4SDimitry Andric     m_name = name;
3987706b4fc4SDimitry Andric     m_type = Type::Item;
3988706b4fc4SDimitry Andric     if (key_name && key_name[0])
3989706b4fc4SDimitry Andric       m_key_name = key_name;
3990706b4fc4SDimitry Andric   } else {
3991706b4fc4SDimitry Andric     m_type = Type::Separator;
3992706b4fc4SDimitry Andric   }
3993706b4fc4SDimitry Andric }
3994706b4fc4SDimitry Andric 
RecalculateNameLengths()3995706b4fc4SDimitry Andric void Menu::RecalculateNameLengths() {
3996706b4fc4SDimitry Andric   m_max_submenu_name_length = 0;
3997706b4fc4SDimitry Andric   m_max_submenu_key_name_length = 0;
3998706b4fc4SDimitry Andric   Menus &submenus = GetSubmenus();
3999706b4fc4SDimitry Andric   const size_t num_submenus = submenus.size();
4000706b4fc4SDimitry Andric   for (size_t i = 0; i < num_submenus; ++i) {
4001706b4fc4SDimitry Andric     Menu *submenu = submenus[i].get();
4002706b4fc4SDimitry Andric     if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size())
4003706b4fc4SDimitry Andric       m_max_submenu_name_length = submenu->m_name.size();
4004706b4fc4SDimitry Andric     if (static_cast<size_t>(m_max_submenu_key_name_length) <
4005706b4fc4SDimitry Andric         submenu->m_key_name.size())
4006706b4fc4SDimitry Andric       m_max_submenu_key_name_length = submenu->m_key_name.size();
4007706b4fc4SDimitry Andric   }
4008706b4fc4SDimitry Andric }
4009706b4fc4SDimitry Andric 
AddSubmenu(const MenuSP & menu_sp)4010706b4fc4SDimitry Andric void Menu::AddSubmenu(const MenuSP &menu_sp) {
4011706b4fc4SDimitry Andric   menu_sp->m_parent = this;
4012706b4fc4SDimitry Andric   if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size())
4013706b4fc4SDimitry Andric     m_max_submenu_name_length = menu_sp->m_name.size();
4014706b4fc4SDimitry Andric   if (static_cast<size_t>(m_max_submenu_key_name_length) <
4015706b4fc4SDimitry Andric       menu_sp->m_key_name.size())
4016706b4fc4SDimitry Andric     m_max_submenu_key_name_length = menu_sp->m_key_name.size();
4017706b4fc4SDimitry Andric   m_submenus.push_back(menu_sp);
4018706b4fc4SDimitry Andric }
4019706b4fc4SDimitry Andric 
DrawMenuTitle(Window & window,bool highlight)4020706b4fc4SDimitry Andric void Menu::DrawMenuTitle(Window &window, bool highlight) {
4021706b4fc4SDimitry Andric   if (m_type == Type::Separator) {
4022706b4fc4SDimitry Andric     window.MoveCursor(0, window.GetCursorY());
4023706b4fc4SDimitry Andric     window.PutChar(ACS_LTEE);
4024706b4fc4SDimitry Andric     int width = window.GetWidth();
4025706b4fc4SDimitry Andric     if (width > 2) {
4026706b4fc4SDimitry Andric       width -= 2;
4027706b4fc4SDimitry Andric       for (int i = 0; i < width; ++i)
4028706b4fc4SDimitry Andric         window.PutChar(ACS_HLINE);
4029706b4fc4SDimitry Andric     }
4030706b4fc4SDimitry Andric     window.PutChar(ACS_RTEE);
4031706b4fc4SDimitry Andric   } else {
4032706b4fc4SDimitry Andric     const int shortcut_key = m_key_value;
4033706b4fc4SDimitry Andric     bool underlined_shortcut = false;
4034b60736ecSDimitry Andric     const attr_t highlight_attr = A_REVERSE;
4035706b4fc4SDimitry Andric     if (highlight)
4036b60736ecSDimitry Andric       window.AttributeOn(highlight_attr);
4037cfca06d7SDimitry Andric     if (llvm::isPrint(shortcut_key)) {
4038706b4fc4SDimitry Andric       size_t lower_pos = m_name.find(tolower(shortcut_key));
4039706b4fc4SDimitry Andric       size_t upper_pos = m_name.find(toupper(shortcut_key));
4040706b4fc4SDimitry Andric       const char *name = m_name.c_str();
4041706b4fc4SDimitry Andric       size_t pos = std::min<size_t>(lower_pos, upper_pos);
4042706b4fc4SDimitry Andric       if (pos != std::string::npos) {
4043706b4fc4SDimitry Andric         underlined_shortcut = true;
4044706b4fc4SDimitry Andric         if (pos > 0) {
4045706b4fc4SDimitry Andric           window.PutCString(name, pos);
4046706b4fc4SDimitry Andric           name += pos;
4047706b4fc4SDimitry Andric         }
4048706b4fc4SDimitry Andric         const attr_t shortcut_attr = A_UNDERLINE | A_BOLD;
4049706b4fc4SDimitry Andric         window.AttributeOn(shortcut_attr);
4050706b4fc4SDimitry Andric         window.PutChar(name[0]);
4051706b4fc4SDimitry Andric         window.AttributeOff(shortcut_attr);
4052706b4fc4SDimitry Andric         name++;
4053706b4fc4SDimitry Andric         if (name[0])
4054706b4fc4SDimitry Andric           window.PutCString(name);
4055706b4fc4SDimitry Andric       }
4056706b4fc4SDimitry Andric     }
4057706b4fc4SDimitry Andric 
4058706b4fc4SDimitry Andric     if (!underlined_shortcut) {
4059706b4fc4SDimitry Andric       window.PutCString(m_name.c_str());
4060706b4fc4SDimitry Andric     }
4061706b4fc4SDimitry Andric 
4062706b4fc4SDimitry Andric     if (highlight)
4063b60736ecSDimitry Andric       window.AttributeOff(highlight_attr);
4064706b4fc4SDimitry Andric 
4065706b4fc4SDimitry Andric     if (m_key_name.empty()) {
4066cfca06d7SDimitry Andric       if (!underlined_shortcut && llvm::isPrint(m_key_value)) {
4067b60736ecSDimitry Andric         window.AttributeOn(COLOR_PAIR(MagentaOnWhite));
4068706b4fc4SDimitry Andric         window.Printf(" (%c)", m_key_value);
4069b60736ecSDimitry Andric         window.AttributeOff(COLOR_PAIR(MagentaOnWhite));
4070706b4fc4SDimitry Andric       }
4071706b4fc4SDimitry Andric     } else {
4072b60736ecSDimitry Andric       window.AttributeOn(COLOR_PAIR(MagentaOnWhite));
4073706b4fc4SDimitry Andric       window.Printf(" (%s)", m_key_name.c_str());
4074b60736ecSDimitry Andric       window.AttributeOff(COLOR_PAIR(MagentaOnWhite));
4075706b4fc4SDimitry Andric     }
4076706b4fc4SDimitry Andric   }
4077706b4fc4SDimitry Andric }
4078706b4fc4SDimitry Andric 
WindowDelegateDraw(Window & window,bool force)4079706b4fc4SDimitry Andric bool Menu::WindowDelegateDraw(Window &window, bool force) {
4080706b4fc4SDimitry Andric   Menus &submenus = GetSubmenus();
4081706b4fc4SDimitry Andric   const size_t num_submenus = submenus.size();
4082706b4fc4SDimitry Andric   const int selected_idx = GetSelectedSubmenuIndex();
4083706b4fc4SDimitry Andric   Menu::Type menu_type = GetType();
4084706b4fc4SDimitry Andric   switch (menu_type) {
4085706b4fc4SDimitry Andric   case Menu::Type::Bar: {
4086b60736ecSDimitry Andric     window.SetBackground(BlackOnWhite);
4087706b4fc4SDimitry Andric     window.MoveCursor(0, 0);
4088706b4fc4SDimitry Andric     for (size_t i = 0; i < num_submenus; ++i) {
4089706b4fc4SDimitry Andric       Menu *menu = submenus[i].get();
4090706b4fc4SDimitry Andric       if (i > 0)
4091706b4fc4SDimitry Andric         window.PutChar(' ');
4092706b4fc4SDimitry Andric       menu->SetStartingColumn(window.GetCursorX());
4093706b4fc4SDimitry Andric       window.PutCString("| ");
4094706b4fc4SDimitry Andric       menu->DrawMenuTitle(window, false);
4095706b4fc4SDimitry Andric     }
4096706b4fc4SDimitry Andric     window.PutCString(" |");
4097706b4fc4SDimitry Andric   } break;
4098706b4fc4SDimitry Andric 
4099706b4fc4SDimitry Andric   case Menu::Type::Item: {
4100706b4fc4SDimitry Andric     int y = 1;
4101706b4fc4SDimitry Andric     int x = 3;
4102706b4fc4SDimitry Andric     // Draw the menu
4103706b4fc4SDimitry Andric     int cursor_x = 0;
4104706b4fc4SDimitry Andric     int cursor_y = 0;
4105706b4fc4SDimitry Andric     window.Erase();
4106b60736ecSDimitry Andric     window.SetBackground(BlackOnWhite);
4107706b4fc4SDimitry Andric     window.Box();
4108706b4fc4SDimitry Andric     for (size_t i = 0; i < num_submenus; ++i) {
4109706b4fc4SDimitry Andric       const bool is_selected = (i == static_cast<size_t>(selected_idx));
4110706b4fc4SDimitry Andric       window.MoveCursor(x, y + i);
4111706b4fc4SDimitry Andric       if (is_selected) {
4112706b4fc4SDimitry Andric         // Remember where we want the cursor to be
4113706b4fc4SDimitry Andric         cursor_x = x - 1;
4114706b4fc4SDimitry Andric         cursor_y = y + i;
4115706b4fc4SDimitry Andric       }
4116706b4fc4SDimitry Andric       submenus[i]->DrawMenuTitle(window, is_selected);
4117706b4fc4SDimitry Andric     }
4118706b4fc4SDimitry Andric     window.MoveCursor(cursor_x, cursor_y);
4119706b4fc4SDimitry Andric   } break;
4120706b4fc4SDimitry Andric 
4121706b4fc4SDimitry Andric   default:
4122706b4fc4SDimitry Andric   case Menu::Type::Separator:
4123706b4fc4SDimitry Andric     break;
4124706b4fc4SDimitry Andric   }
4125706b4fc4SDimitry Andric   return true; // Drawing handled...
4126706b4fc4SDimitry Andric }
4127706b4fc4SDimitry Andric 
WindowDelegateHandleChar(Window & window,int key)4128706b4fc4SDimitry Andric HandleCharResult Menu::WindowDelegateHandleChar(Window &window, int key) {
4129706b4fc4SDimitry Andric   HandleCharResult result = eKeyNotHandled;
4130706b4fc4SDimitry Andric 
4131706b4fc4SDimitry Andric   Menus &submenus = GetSubmenus();
4132706b4fc4SDimitry Andric   const size_t num_submenus = submenus.size();
4133706b4fc4SDimitry Andric   const int selected_idx = GetSelectedSubmenuIndex();
4134706b4fc4SDimitry Andric   Menu::Type menu_type = GetType();
4135706b4fc4SDimitry Andric   if (menu_type == Menu::Type::Bar) {
4136706b4fc4SDimitry Andric     MenuSP run_menu_sp;
4137706b4fc4SDimitry Andric     switch (key) {
4138706b4fc4SDimitry Andric     case KEY_DOWN:
4139706b4fc4SDimitry Andric     case KEY_UP:
4140706b4fc4SDimitry Andric       // Show last menu or first menu
4141706b4fc4SDimitry Andric       if (selected_idx < static_cast<int>(num_submenus))
4142706b4fc4SDimitry Andric         run_menu_sp = submenus[selected_idx];
4143706b4fc4SDimitry Andric       else if (!submenus.empty())
4144706b4fc4SDimitry Andric         run_menu_sp = submenus.front();
4145706b4fc4SDimitry Andric       result = eKeyHandled;
4146706b4fc4SDimitry Andric       break;
4147706b4fc4SDimitry Andric 
4148706b4fc4SDimitry Andric     case KEY_RIGHT:
4149706b4fc4SDimitry Andric       ++m_selected;
4150706b4fc4SDimitry Andric       if (m_selected >= static_cast<int>(num_submenus))
4151706b4fc4SDimitry Andric         m_selected = 0;
4152706b4fc4SDimitry Andric       if (m_selected < static_cast<int>(num_submenus))
4153706b4fc4SDimitry Andric         run_menu_sp = submenus[m_selected];
4154706b4fc4SDimitry Andric       else if (!submenus.empty())
4155706b4fc4SDimitry Andric         run_menu_sp = submenus.front();
4156706b4fc4SDimitry Andric       result = eKeyHandled;
4157706b4fc4SDimitry Andric       break;
4158706b4fc4SDimitry Andric 
4159706b4fc4SDimitry Andric     case KEY_LEFT:
4160706b4fc4SDimitry Andric       --m_selected;
4161706b4fc4SDimitry Andric       if (m_selected < 0)
4162706b4fc4SDimitry Andric         m_selected = num_submenus - 1;
4163706b4fc4SDimitry Andric       if (m_selected < static_cast<int>(num_submenus))
4164706b4fc4SDimitry Andric         run_menu_sp = submenus[m_selected];
4165706b4fc4SDimitry Andric       else if (!submenus.empty())
4166706b4fc4SDimitry Andric         run_menu_sp = submenus.front();
4167706b4fc4SDimitry Andric       result = eKeyHandled;
4168706b4fc4SDimitry Andric       break;
4169706b4fc4SDimitry Andric 
4170706b4fc4SDimitry Andric     default:
4171706b4fc4SDimitry Andric       for (size_t i = 0; i < num_submenus; ++i) {
4172706b4fc4SDimitry Andric         if (submenus[i]->GetKeyValue() == key) {
4173706b4fc4SDimitry Andric           SetSelectedSubmenuIndex(i);
4174706b4fc4SDimitry Andric           run_menu_sp = submenus[i];
4175706b4fc4SDimitry Andric           result = eKeyHandled;
4176706b4fc4SDimitry Andric           break;
4177706b4fc4SDimitry Andric         }
4178706b4fc4SDimitry Andric       }
4179706b4fc4SDimitry Andric       break;
4180706b4fc4SDimitry Andric     }
4181706b4fc4SDimitry Andric 
4182706b4fc4SDimitry Andric     if (run_menu_sp) {
4183706b4fc4SDimitry Andric       // Run the action on this menu in case we need to populate the menu with
4184706b4fc4SDimitry Andric       // dynamic content and also in case check marks, and any other menu
4185706b4fc4SDimitry Andric       // decorations need to be calculated
4186706b4fc4SDimitry Andric       if (run_menu_sp->Action() == MenuActionResult::Quit)
4187706b4fc4SDimitry Andric         return eQuitApplication;
4188706b4fc4SDimitry Andric 
4189706b4fc4SDimitry Andric       Rect menu_bounds;
4190706b4fc4SDimitry Andric       menu_bounds.origin.x = run_menu_sp->GetStartingColumn();
4191706b4fc4SDimitry Andric       menu_bounds.origin.y = 1;
4192706b4fc4SDimitry Andric       menu_bounds.size.width = run_menu_sp->GetDrawWidth();
4193706b4fc4SDimitry Andric       menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2;
4194706b4fc4SDimitry Andric       if (m_menu_window_sp)
4195706b4fc4SDimitry Andric         window.GetParent()->RemoveSubWindow(m_menu_window_sp.get());
4196706b4fc4SDimitry Andric 
4197706b4fc4SDimitry Andric       m_menu_window_sp = window.GetParent()->CreateSubWindow(
4198706b4fc4SDimitry Andric           run_menu_sp->GetName().c_str(), menu_bounds, true);
4199706b4fc4SDimitry Andric       m_menu_window_sp->SetDelegate(run_menu_sp);
4200706b4fc4SDimitry Andric     }
4201706b4fc4SDimitry Andric   } else if (menu_type == Menu::Type::Item) {
4202706b4fc4SDimitry Andric     switch (key) {
4203706b4fc4SDimitry Andric     case KEY_DOWN:
4204706b4fc4SDimitry Andric       if (m_submenus.size() > 1) {
4205706b4fc4SDimitry Andric         const int start_select = m_selected;
4206706b4fc4SDimitry Andric         while (++m_selected != start_select) {
4207706b4fc4SDimitry Andric           if (static_cast<size_t>(m_selected) >= num_submenus)
4208706b4fc4SDimitry Andric             m_selected = 0;
4209706b4fc4SDimitry Andric           if (m_submenus[m_selected]->GetType() == Type::Separator)
4210706b4fc4SDimitry Andric             continue;
4211706b4fc4SDimitry Andric           else
4212706b4fc4SDimitry Andric             break;
4213706b4fc4SDimitry Andric         }
4214706b4fc4SDimitry Andric         return eKeyHandled;
4215706b4fc4SDimitry Andric       }
4216706b4fc4SDimitry Andric       break;
4217706b4fc4SDimitry Andric 
4218706b4fc4SDimitry Andric     case KEY_UP:
4219706b4fc4SDimitry Andric       if (m_submenus.size() > 1) {
4220706b4fc4SDimitry Andric         const int start_select = m_selected;
4221706b4fc4SDimitry Andric         while (--m_selected != start_select) {
4222706b4fc4SDimitry Andric           if (m_selected < static_cast<int>(0))
4223706b4fc4SDimitry Andric             m_selected = num_submenus - 1;
4224706b4fc4SDimitry Andric           if (m_submenus[m_selected]->GetType() == Type::Separator)
4225706b4fc4SDimitry Andric             continue;
4226706b4fc4SDimitry Andric           else
4227706b4fc4SDimitry Andric             break;
4228706b4fc4SDimitry Andric         }
4229706b4fc4SDimitry Andric         return eKeyHandled;
4230706b4fc4SDimitry Andric       }
4231706b4fc4SDimitry Andric       break;
4232706b4fc4SDimitry Andric 
4233706b4fc4SDimitry Andric     case KEY_RETURN:
4234706b4fc4SDimitry Andric       if (static_cast<size_t>(selected_idx) < num_submenus) {
4235706b4fc4SDimitry Andric         if (submenus[selected_idx]->Action() == MenuActionResult::Quit)
4236706b4fc4SDimitry Andric           return eQuitApplication;
4237706b4fc4SDimitry Andric         window.GetParent()->RemoveSubWindow(&window);
4238706b4fc4SDimitry Andric         return eKeyHandled;
4239706b4fc4SDimitry Andric       }
4240706b4fc4SDimitry Andric       break;
4241706b4fc4SDimitry Andric 
4242706b4fc4SDimitry Andric     case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in
4243706b4fc4SDimitry Andric                      // case other chars are entered for escaped sequences
4244706b4fc4SDimitry Andric       window.GetParent()->RemoveSubWindow(&window);
4245706b4fc4SDimitry Andric       return eKeyHandled;
4246706b4fc4SDimitry Andric 
4247706b4fc4SDimitry Andric     default:
4248706b4fc4SDimitry Andric       for (size_t i = 0; i < num_submenus; ++i) {
4249706b4fc4SDimitry Andric         Menu *menu = submenus[i].get();
4250706b4fc4SDimitry Andric         if (menu->GetKeyValue() == key) {
4251706b4fc4SDimitry Andric           SetSelectedSubmenuIndex(i);
4252706b4fc4SDimitry Andric           window.GetParent()->RemoveSubWindow(&window);
4253706b4fc4SDimitry Andric           if (menu->Action() == MenuActionResult::Quit)
4254706b4fc4SDimitry Andric             return eQuitApplication;
4255706b4fc4SDimitry Andric           return eKeyHandled;
4256706b4fc4SDimitry Andric         }
4257706b4fc4SDimitry Andric       }
4258706b4fc4SDimitry Andric       break;
4259706b4fc4SDimitry Andric     }
4260706b4fc4SDimitry Andric   } else if (menu_type == Menu::Type::Separator) {
4261706b4fc4SDimitry Andric   }
4262706b4fc4SDimitry Andric   return result;
4263706b4fc4SDimitry Andric }
4264706b4fc4SDimitry Andric 
4265706b4fc4SDimitry Andric class Application {
4266706b4fc4SDimitry Andric public:
Application(FILE * in,FILE * out)4267145449b1SDimitry Andric   Application(FILE *in, FILE *out) : m_window_sp(), m_in(in), m_out(out) {}
4268706b4fc4SDimitry Andric 
~Application()4269706b4fc4SDimitry Andric   ~Application() {
4270706b4fc4SDimitry Andric     m_window_delegates.clear();
4271706b4fc4SDimitry Andric     m_window_sp.reset();
4272706b4fc4SDimitry Andric     if (m_screen) {
4273706b4fc4SDimitry Andric       ::delscreen(m_screen);
4274706b4fc4SDimitry Andric       m_screen = nullptr;
4275706b4fc4SDimitry Andric     }
4276706b4fc4SDimitry Andric   }
4277706b4fc4SDimitry Andric 
Initialize()4278706b4fc4SDimitry Andric   void Initialize() {
4279706b4fc4SDimitry Andric     m_screen = ::newterm(nullptr, m_out, m_in);
4280706b4fc4SDimitry Andric     ::start_color();
4281706b4fc4SDimitry Andric     ::curs_set(0);
4282706b4fc4SDimitry Andric     ::noecho();
4283706b4fc4SDimitry Andric     ::keypad(stdscr, TRUE);
4284706b4fc4SDimitry Andric   }
4285706b4fc4SDimitry Andric 
Terminate()4286706b4fc4SDimitry Andric   void Terminate() { ::endwin(); }
4287706b4fc4SDimitry Andric 
Run(Debugger & debugger)4288706b4fc4SDimitry Andric   void Run(Debugger &debugger) {
4289706b4fc4SDimitry Andric     bool done = false;
4290706b4fc4SDimitry Andric     int delay_in_tenths_of_a_second = 1;
4291706b4fc4SDimitry Andric 
4292c0981da4SDimitry Andric     // Alas the threading model in curses is a bit lame so we need to resort
4293c0981da4SDimitry Andric     // to polling every 0.5 seconds. We could poll for stdin ourselves and
4294c0981da4SDimitry Andric     // then pass the keys down but then we need to translate all of the escape
4295706b4fc4SDimitry Andric     // sequences ourselves. So we resort to polling for input because we need
4296706b4fc4SDimitry Andric     // to receive async process events while in this loop.
4297706b4fc4SDimitry Andric 
4298c0981da4SDimitry Andric     halfdelay(delay_in_tenths_of_a_second); // Poll using some number of
4299c0981da4SDimitry Andric                                             // tenths of seconds seconds when
4300c0981da4SDimitry Andric                                             // calling Window::GetChar()
4301706b4fc4SDimitry Andric 
4302706b4fc4SDimitry Andric     ListenerSP listener_sp(
4303706b4fc4SDimitry Andric         Listener::MakeListener("lldb.IOHandler.curses.Application"));
4304706b4fc4SDimitry Andric     ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
4305706b4fc4SDimitry Andric     debugger.EnableForwardEvents(listener_sp);
4306706b4fc4SDimitry Andric 
4307b60736ecSDimitry Andric     m_update_screen = true;
4308706b4fc4SDimitry Andric #if defined(__APPLE__)
4309706b4fc4SDimitry Andric     std::deque<int> escape_chars;
4310706b4fc4SDimitry Andric #endif
4311706b4fc4SDimitry Andric 
4312706b4fc4SDimitry Andric     while (!done) {
4313b60736ecSDimitry Andric       if (m_update_screen) {
4314706b4fc4SDimitry Andric         m_window_sp->Draw(false);
4315706b4fc4SDimitry Andric         // All windows should be calling Window::DeferredRefresh() instead of
4316706b4fc4SDimitry Andric         // Window::Refresh() so we can do a single update and avoid any screen
4317706b4fc4SDimitry Andric         // blinking
4318706b4fc4SDimitry Andric         update_panels();
4319706b4fc4SDimitry Andric 
4320706b4fc4SDimitry Andric         // Cursor hiding isn't working on MacOSX, so hide it in the top left
4321706b4fc4SDimitry Andric         // corner
4322706b4fc4SDimitry Andric         m_window_sp->MoveCursor(0, 0);
4323706b4fc4SDimitry Andric 
4324706b4fc4SDimitry Andric         doupdate();
4325b60736ecSDimitry Andric         m_update_screen = false;
4326706b4fc4SDimitry Andric       }
4327706b4fc4SDimitry Andric 
4328706b4fc4SDimitry Andric #if defined(__APPLE__)
4329706b4fc4SDimitry Andric       // Terminal.app doesn't map its function keys correctly, F1-F4 default
4330706b4fc4SDimitry Andric       // to: \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if
4331706b4fc4SDimitry Andric       // possible
4332706b4fc4SDimitry Andric       int ch;
4333706b4fc4SDimitry Andric       if (escape_chars.empty())
4334706b4fc4SDimitry Andric         ch = m_window_sp->GetChar();
4335706b4fc4SDimitry Andric       else {
4336706b4fc4SDimitry Andric         ch = escape_chars.front();
4337706b4fc4SDimitry Andric         escape_chars.pop_front();
4338706b4fc4SDimitry Andric       }
4339706b4fc4SDimitry Andric       if (ch == KEY_ESCAPE) {
4340706b4fc4SDimitry Andric         int ch2 = m_window_sp->GetChar();
4341706b4fc4SDimitry Andric         if (ch2 == 'O') {
4342706b4fc4SDimitry Andric           int ch3 = m_window_sp->GetChar();
4343706b4fc4SDimitry Andric           switch (ch3) {
4344706b4fc4SDimitry Andric           case 'P':
4345706b4fc4SDimitry Andric             ch = KEY_F(1);
4346706b4fc4SDimitry Andric             break;
4347706b4fc4SDimitry Andric           case 'Q':
4348706b4fc4SDimitry Andric             ch = KEY_F(2);
4349706b4fc4SDimitry Andric             break;
4350706b4fc4SDimitry Andric           case 'R':
4351706b4fc4SDimitry Andric             ch = KEY_F(3);
4352706b4fc4SDimitry Andric             break;
4353706b4fc4SDimitry Andric           case 'S':
4354706b4fc4SDimitry Andric             ch = KEY_F(4);
4355706b4fc4SDimitry Andric             break;
4356706b4fc4SDimitry Andric           default:
4357706b4fc4SDimitry Andric             escape_chars.push_back(ch2);
4358706b4fc4SDimitry Andric             if (ch3 != -1)
4359706b4fc4SDimitry Andric               escape_chars.push_back(ch3);
4360706b4fc4SDimitry Andric             break;
4361706b4fc4SDimitry Andric           }
4362706b4fc4SDimitry Andric         } else if (ch2 != -1)
4363706b4fc4SDimitry Andric           escape_chars.push_back(ch2);
4364706b4fc4SDimitry Andric       }
4365706b4fc4SDimitry Andric #else
4366706b4fc4SDimitry Andric       int ch = m_window_sp->GetChar();
4367706b4fc4SDimitry Andric 
4368706b4fc4SDimitry Andric #endif
4369706b4fc4SDimitry Andric       if (ch == -1) {
4370706b4fc4SDimitry Andric         if (feof(m_in) || ferror(m_in)) {
4371706b4fc4SDimitry Andric           done = true;
4372706b4fc4SDimitry Andric         } else {
4373706b4fc4SDimitry Andric           // Just a timeout from using halfdelay(), check for events
4374706b4fc4SDimitry Andric           EventSP event_sp;
4375706b4fc4SDimitry Andric           while (listener_sp->PeekAtNextEvent()) {
4376706b4fc4SDimitry Andric             listener_sp->GetEvent(event_sp, std::chrono::seconds(0));
4377706b4fc4SDimitry Andric 
4378706b4fc4SDimitry Andric             if (event_sp) {
4379706b4fc4SDimitry Andric               Broadcaster *broadcaster = event_sp->GetBroadcaster();
4380706b4fc4SDimitry Andric               if (broadcaster) {
4381706b4fc4SDimitry Andric                 // uint32_t event_type = event_sp->GetType();
4382706b4fc4SDimitry Andric                 ConstString broadcaster_class(
4383706b4fc4SDimitry Andric                     broadcaster->GetBroadcasterClass());
4384706b4fc4SDimitry Andric                 if (broadcaster_class == broadcaster_class_process) {
4385b60736ecSDimitry Andric                   m_update_screen = true;
4386706b4fc4SDimitry Andric                   continue; // Don't get any key, just update our view
4387706b4fc4SDimitry Andric                 }
4388706b4fc4SDimitry Andric               }
4389706b4fc4SDimitry Andric             }
4390706b4fc4SDimitry Andric           }
4391706b4fc4SDimitry Andric         }
4392706b4fc4SDimitry Andric       } else {
4393706b4fc4SDimitry Andric         HandleCharResult key_result = m_window_sp->HandleChar(ch);
4394706b4fc4SDimitry Andric         switch (key_result) {
4395706b4fc4SDimitry Andric         case eKeyHandled:
4396b60736ecSDimitry Andric           m_update_screen = true;
4397706b4fc4SDimitry Andric           break;
4398706b4fc4SDimitry Andric         case eKeyNotHandled:
4399b60736ecSDimitry Andric           if (ch == 12) { // Ctrl+L, force full redraw
4400b60736ecSDimitry Andric             redrawwin(m_window_sp->get());
4401b60736ecSDimitry Andric             m_update_screen = true;
4402b60736ecSDimitry Andric           }
4403706b4fc4SDimitry Andric           break;
4404706b4fc4SDimitry Andric         case eQuitApplication:
4405706b4fc4SDimitry Andric           done = true;
4406706b4fc4SDimitry Andric           break;
4407706b4fc4SDimitry Andric         }
4408706b4fc4SDimitry Andric       }
4409706b4fc4SDimitry Andric     }
4410706b4fc4SDimitry Andric 
4411706b4fc4SDimitry Andric     debugger.CancelForwardEvents(listener_sp);
4412706b4fc4SDimitry Andric   }
4413706b4fc4SDimitry Andric 
GetMainWindow()4414706b4fc4SDimitry Andric   WindowSP &GetMainWindow() {
4415706b4fc4SDimitry Andric     if (!m_window_sp)
4416706b4fc4SDimitry Andric       m_window_sp = std::make_shared<Window>("main", stdscr, false);
4417706b4fc4SDimitry Andric     return m_window_sp;
4418706b4fc4SDimitry Andric   }
4419706b4fc4SDimitry Andric 
TerminalSizeChanged()4420b60736ecSDimitry Andric   void TerminalSizeChanged() {
4421b60736ecSDimitry Andric     ::endwin();
4422b60736ecSDimitry Andric     ::refresh();
4423b60736ecSDimitry Andric     Rect content_bounds = m_window_sp->GetFrame();
4424b60736ecSDimitry Andric     m_window_sp->SetBounds(content_bounds);
4425b60736ecSDimitry Andric     if (WindowSP menubar_window_sp = m_window_sp->FindSubWindow("Menubar"))
4426b60736ecSDimitry Andric       menubar_window_sp->SetBounds(content_bounds.MakeMenuBar());
4427b60736ecSDimitry Andric     if (WindowSP status_window_sp = m_window_sp->FindSubWindow("Status"))
4428b60736ecSDimitry Andric       status_window_sp->SetBounds(content_bounds.MakeStatusBar());
4429b60736ecSDimitry Andric 
4430b60736ecSDimitry Andric     WindowSP source_window_sp = m_window_sp->FindSubWindow("Source");
4431b60736ecSDimitry Andric     WindowSP variables_window_sp = m_window_sp->FindSubWindow("Variables");
4432b60736ecSDimitry Andric     WindowSP registers_window_sp = m_window_sp->FindSubWindow("Registers");
4433b60736ecSDimitry Andric     WindowSP threads_window_sp = m_window_sp->FindSubWindow("Threads");
4434b60736ecSDimitry Andric 
4435b60736ecSDimitry Andric     Rect threads_bounds;
4436b60736ecSDimitry Andric     Rect source_variables_bounds;
4437b60736ecSDimitry Andric     content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
4438b60736ecSDimitry Andric                                            threads_bounds);
4439b60736ecSDimitry Andric     if (threads_window_sp)
4440b60736ecSDimitry Andric       threads_window_sp->SetBounds(threads_bounds);
4441b60736ecSDimitry Andric     else
4442b60736ecSDimitry Andric       source_variables_bounds = content_bounds;
4443b60736ecSDimitry Andric 
4444b60736ecSDimitry Andric     Rect source_bounds;
4445b60736ecSDimitry Andric     Rect variables_registers_bounds;
4446b60736ecSDimitry Andric     source_variables_bounds.HorizontalSplitPercentage(
4447b60736ecSDimitry Andric         0.70, source_bounds, variables_registers_bounds);
4448b60736ecSDimitry Andric     if (variables_window_sp || registers_window_sp) {
4449b60736ecSDimitry Andric       if (variables_window_sp && registers_window_sp) {
4450b60736ecSDimitry Andric         Rect variables_bounds;
4451b60736ecSDimitry Andric         Rect registers_bounds;
4452b60736ecSDimitry Andric         variables_registers_bounds.VerticalSplitPercentage(
4453b60736ecSDimitry Andric             0.50, variables_bounds, registers_bounds);
4454b60736ecSDimitry Andric         variables_window_sp->SetBounds(variables_bounds);
4455b60736ecSDimitry Andric         registers_window_sp->SetBounds(registers_bounds);
4456b60736ecSDimitry Andric       } else if (variables_window_sp) {
4457b60736ecSDimitry Andric         variables_window_sp->SetBounds(variables_registers_bounds);
4458b60736ecSDimitry Andric       } else {
4459b60736ecSDimitry Andric         registers_window_sp->SetBounds(variables_registers_bounds);
4460b60736ecSDimitry Andric       }
4461b60736ecSDimitry Andric     } else {
4462b60736ecSDimitry Andric       source_bounds = source_variables_bounds;
4463b60736ecSDimitry Andric     }
4464b60736ecSDimitry Andric 
4465b60736ecSDimitry Andric     source_window_sp->SetBounds(source_bounds);
4466b60736ecSDimitry Andric 
4467b60736ecSDimitry Andric     touchwin(stdscr);
4468b60736ecSDimitry Andric     redrawwin(m_window_sp->get());
4469b60736ecSDimitry Andric     m_update_screen = true;
4470b60736ecSDimitry Andric   }
4471b60736ecSDimitry Andric 
4472706b4fc4SDimitry Andric protected:
4473706b4fc4SDimitry Andric   WindowSP m_window_sp;
4474706b4fc4SDimitry Andric   WindowDelegates m_window_delegates;
4475145449b1SDimitry Andric   SCREEN *m_screen = nullptr;
4476706b4fc4SDimitry Andric   FILE *m_in;
4477706b4fc4SDimitry Andric   FILE *m_out;
4478b60736ecSDimitry Andric   bool m_update_screen = false;
4479706b4fc4SDimitry Andric };
4480706b4fc4SDimitry Andric 
4481706b4fc4SDimitry Andric } // namespace curses
4482706b4fc4SDimitry Andric 
4483706b4fc4SDimitry Andric using namespace curses;
4484706b4fc4SDimitry Andric 
4485706b4fc4SDimitry Andric struct Row {
4486344a3780SDimitry Andric   ValueObjectUpdater value;
4487706b4fc4SDimitry Andric   Row *parent;
4488706b4fc4SDimitry Andric   // The process stop ID when the children were calculated.
4489b60736ecSDimitry Andric   uint32_t children_stop_id = 0;
4490b60736ecSDimitry Andric   int row_idx = 0;
4491b60736ecSDimitry Andric   int x = 1;
4492b60736ecSDimitry Andric   int y = 1;
4493706b4fc4SDimitry Andric   bool might_have_children;
4494b60736ecSDimitry Andric   bool expanded = false;
4495b60736ecSDimitry Andric   bool calculated_children = false;
4496706b4fc4SDimitry Andric   std::vector<Row> children;
4497706b4fc4SDimitry Andric 
RowRow4498706b4fc4SDimitry Andric   Row(const ValueObjectSP &v, Row *p)
4499344a3780SDimitry Andric       : value(v), parent(p),
4500b60736ecSDimitry Andric         might_have_children(v ? v->MightHaveChildren() : false) {}
4501706b4fc4SDimitry Andric 
GetDepthRow4502706b4fc4SDimitry Andric   size_t GetDepth() const {
4503706b4fc4SDimitry Andric     if (parent)
4504706b4fc4SDimitry Andric       return 1 + parent->GetDepth();
4505706b4fc4SDimitry Andric     return 0;
4506706b4fc4SDimitry Andric   }
4507706b4fc4SDimitry Andric 
ExpandRow4508706b4fc4SDimitry Andric   void Expand() { expanded = true; }
4509706b4fc4SDimitry Andric 
GetChildrenRow4510706b4fc4SDimitry Andric   std::vector<Row> &GetChildren() {
4511706b4fc4SDimitry Andric     ProcessSP process_sp = value.GetProcessSP();
4512706b4fc4SDimitry Andric     auto stop_id = process_sp->GetStopID();
4513706b4fc4SDimitry Andric     if (process_sp && stop_id != children_stop_id) {
4514706b4fc4SDimitry Andric       children_stop_id = stop_id;
4515706b4fc4SDimitry Andric       calculated_children = false;
4516706b4fc4SDimitry Andric     }
4517706b4fc4SDimitry Andric     if (!calculated_children) {
4518706b4fc4SDimitry Andric       children.clear();
4519706b4fc4SDimitry Andric       calculated_children = true;
4520706b4fc4SDimitry Andric       ValueObjectSP valobj = value.GetSP();
4521706b4fc4SDimitry Andric       if (valobj) {
4522ac9a064cSDimitry Andric         const uint32_t num_children = valobj->GetNumChildrenIgnoringErrors();
4523706b4fc4SDimitry Andric         for (size_t i = 0; i < num_children; ++i) {
45247fa27ce4SDimitry Andric           children.push_back(Row(valobj->GetChildAtIndex(i), this));
4525706b4fc4SDimitry Andric         }
4526706b4fc4SDimitry Andric       }
4527706b4fc4SDimitry Andric     }
4528706b4fc4SDimitry Andric     return children;
4529706b4fc4SDimitry Andric   }
4530706b4fc4SDimitry Andric 
UnexpandRow4531706b4fc4SDimitry Andric   void Unexpand() {
4532706b4fc4SDimitry Andric     expanded = false;
4533706b4fc4SDimitry Andric     calculated_children = false;
4534706b4fc4SDimitry Andric     children.clear();
4535706b4fc4SDimitry Andric   }
4536706b4fc4SDimitry Andric 
DrawTreeRow4537706b4fc4SDimitry Andric   void DrawTree(Window &window) {
4538706b4fc4SDimitry Andric     if (parent)
4539706b4fc4SDimitry Andric       parent->DrawTreeForChild(window, this, 0);
4540706b4fc4SDimitry Andric 
4541145449b1SDimitry Andric     if (might_have_children &&
4542145449b1SDimitry Andric         (!calculated_children || !GetChildren().empty())) {
4543706b4fc4SDimitry Andric       // It we can get UTF8 characters to work we should try to use the
4544706b4fc4SDimitry Andric       // "symbol" UTF8 string below
4545706b4fc4SDimitry Andric       //            const char *symbol = "";
4546706b4fc4SDimitry Andric       //            if (row.expanded)
4547706b4fc4SDimitry Andric       //                symbol = "\xe2\x96\xbd ";
4548706b4fc4SDimitry Andric       //            else
4549706b4fc4SDimitry Andric       //                symbol = "\xe2\x96\xb7 ";
4550706b4fc4SDimitry Andric       //            window.PutCString (symbol);
4551706b4fc4SDimitry Andric 
4552706b4fc4SDimitry Andric       // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 'v'
4553706b4fc4SDimitry Andric       // or '>' character...
4554706b4fc4SDimitry Andric       //            if (expanded)
4555706b4fc4SDimitry Andric       //                window.PutChar (ACS_DARROW);
4556706b4fc4SDimitry Andric       //            else
4557706b4fc4SDimitry Andric       //                window.PutChar (ACS_RARROW);
4558706b4fc4SDimitry Andric       // Since we can't find any good looking right arrow/down arrow symbols,
4559706b4fc4SDimitry Andric       // just use a diamond...
4560706b4fc4SDimitry Andric       window.PutChar(ACS_DIAMOND);
4561706b4fc4SDimitry Andric       window.PutChar(ACS_HLINE);
4562706b4fc4SDimitry Andric     }
4563706b4fc4SDimitry Andric   }
4564706b4fc4SDimitry Andric 
DrawTreeForChildRow4565706b4fc4SDimitry Andric   void DrawTreeForChild(Window &window, Row *child, uint32_t reverse_depth) {
4566706b4fc4SDimitry Andric     if (parent)
4567706b4fc4SDimitry Andric       parent->DrawTreeForChild(window, this, reverse_depth + 1);
4568706b4fc4SDimitry Andric 
4569706b4fc4SDimitry Andric     if (&GetChildren().back() == child) {
4570706b4fc4SDimitry Andric       // Last child
4571706b4fc4SDimitry Andric       if (reverse_depth == 0) {
4572706b4fc4SDimitry Andric         window.PutChar(ACS_LLCORNER);
4573706b4fc4SDimitry Andric         window.PutChar(ACS_HLINE);
4574706b4fc4SDimitry Andric       } else {
4575706b4fc4SDimitry Andric         window.PutChar(' ');
4576706b4fc4SDimitry Andric         window.PutChar(' ');
4577706b4fc4SDimitry Andric       }
4578706b4fc4SDimitry Andric     } else {
4579706b4fc4SDimitry Andric       if (reverse_depth == 0) {
4580706b4fc4SDimitry Andric         window.PutChar(ACS_LTEE);
4581706b4fc4SDimitry Andric         window.PutChar(ACS_HLINE);
4582706b4fc4SDimitry Andric       } else {
4583706b4fc4SDimitry Andric         window.PutChar(ACS_VLINE);
4584706b4fc4SDimitry Andric         window.PutChar(' ');
4585706b4fc4SDimitry Andric       }
4586706b4fc4SDimitry Andric     }
4587706b4fc4SDimitry Andric   }
4588706b4fc4SDimitry Andric };
4589706b4fc4SDimitry Andric 
4590706b4fc4SDimitry Andric struct DisplayOptions {
4591706b4fc4SDimitry Andric   bool show_types;
4592706b4fc4SDimitry Andric };
4593706b4fc4SDimitry Andric 
4594706b4fc4SDimitry Andric class TreeItem;
4595706b4fc4SDimitry Andric 
4596706b4fc4SDimitry Andric class TreeDelegate {
4597706b4fc4SDimitry Andric public:
4598706b4fc4SDimitry Andric   TreeDelegate() = default;
4599706b4fc4SDimitry Andric   virtual ~TreeDelegate() = default;
4600706b4fc4SDimitry Andric 
4601706b4fc4SDimitry Andric   virtual void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) = 0;
4602706b4fc4SDimitry Andric   virtual void TreeDelegateGenerateChildren(TreeItem &item) = 0;
TreeDelegateUpdateSelection(TreeItem & root,int & selection_index,TreeItem * & selected_item)4603344a3780SDimitry Andric   virtual void TreeDelegateUpdateSelection(TreeItem &root, int &selection_index,
460477fc4c14SDimitry Andric                                            TreeItem *&selected_item) {}
4605c0981da4SDimitry Andric   // This is invoked when a tree item is selected. If true is returned, the
4606c0981da4SDimitry Andric   // views are updated.
4607c0981da4SDimitry Andric   virtual bool TreeDelegateItemSelected(TreeItem &item) = 0;
TreeDelegateExpandRootByDefault()4608344a3780SDimitry Andric   virtual bool TreeDelegateExpandRootByDefault() { return false; }
4609c0981da4SDimitry Andric   // This is mostly useful for root tree delegates. If false is returned,
4610c0981da4SDimitry Andric   // drawing will be skipped completely. This is needed, for instance, in
4611c0981da4SDimitry Andric   // skipping drawing of the threads tree if there is no running process.
TreeDelegateShouldDraw()4612c0981da4SDimitry Andric   virtual bool TreeDelegateShouldDraw() { return true; }
4613706b4fc4SDimitry Andric };
4614706b4fc4SDimitry Andric 
4615706b4fc4SDimitry Andric typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
4616706b4fc4SDimitry Andric 
4617b1c73532SDimitry Andric struct TreeItemData {
TreeItemDataTreeItemData4618b1c73532SDimitry Andric   TreeItemData(TreeItem *parent, TreeDelegate &delegate,
4619b1c73532SDimitry Andric                bool might_have_children, bool is_expanded)
4620b1c73532SDimitry Andric       : m_parent(parent), m_delegate(&delegate),
4621b1c73532SDimitry Andric         m_might_have_children(might_have_children), m_is_expanded(is_expanded) {
4622344a3780SDimitry Andric   }
4623706b4fc4SDimitry Andric 
4624b1c73532SDimitry Andric protected:
4625b1c73532SDimitry Andric   TreeItem *m_parent;
4626b1c73532SDimitry Andric   TreeDelegate *m_delegate;
4627b1c73532SDimitry Andric   void *m_user_data = nullptr;
4628b1c73532SDimitry Andric   uint64_t m_identifier = 0;
4629b1c73532SDimitry Andric   std::string m_text;
4630b1c73532SDimitry Andric   int m_row_idx = -1; // Zero based visible row index, -1 if not visible or for
4631b1c73532SDimitry Andric                       // the root item
4632b1c73532SDimitry Andric   bool m_might_have_children;
4633b1c73532SDimitry Andric   bool m_is_expanded = false;
4634b1c73532SDimitry Andric };
4635b1c73532SDimitry Andric 
4636b1c73532SDimitry Andric class TreeItem : public TreeItemData {
4637b1c73532SDimitry Andric public:
TreeItem(TreeItem * parent,TreeDelegate & delegate,bool might_have_children)4638b1c73532SDimitry Andric   TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children)
4639b1c73532SDimitry Andric       : TreeItemData(parent, delegate, might_have_children,
4640b1c73532SDimitry Andric                      parent == nullptr
4641b1c73532SDimitry Andric                          ? delegate.TreeDelegateExpandRootByDefault()
4642b1c73532SDimitry Andric                          : false),
4643b1c73532SDimitry Andric         m_children() {}
4644b1c73532SDimitry Andric 
4645b1c73532SDimitry Andric   TreeItem(const TreeItem &) = delete;
4646b1c73532SDimitry Andric   TreeItem &operator=(const TreeItem &rhs) = delete;
4647b1c73532SDimitry Andric 
operator =(TreeItem && rhs)4648b1c73532SDimitry Andric   TreeItem &operator=(TreeItem &&rhs) {
4649706b4fc4SDimitry Andric     if (this != &rhs) {
4650b1c73532SDimitry Andric       TreeItemData::operator=(std::move(rhs));
4651b1c73532SDimitry Andric       AdoptChildren(rhs.m_children);
4652706b4fc4SDimitry Andric     }
4653706b4fc4SDimitry Andric     return *this;
4654706b4fc4SDimitry Andric   }
4655706b4fc4SDimitry Andric 
TreeItem(TreeItem && rhs)4656b1c73532SDimitry Andric   TreeItem(TreeItem &&rhs) : TreeItemData(std::move(rhs)) {
4657b1c73532SDimitry Andric     AdoptChildren(rhs.m_children);
4658b1c73532SDimitry Andric   }
4659706b4fc4SDimitry Andric 
GetDepth() const4660706b4fc4SDimitry Andric   size_t GetDepth() const {
4661706b4fc4SDimitry Andric     if (m_parent)
4662706b4fc4SDimitry Andric       return 1 + m_parent->GetDepth();
4663706b4fc4SDimitry Andric     return 0;
4664706b4fc4SDimitry Andric   }
4665706b4fc4SDimitry Andric 
GetRowIndex() const4666706b4fc4SDimitry Andric   int GetRowIndex() const { return m_row_idx; }
4667706b4fc4SDimitry Andric 
ClearChildren()4668706b4fc4SDimitry Andric   void ClearChildren() { m_children.clear(); }
4669706b4fc4SDimitry Andric 
Resize(size_t n,TreeDelegate & delegate,bool might_have_children)4670b1c73532SDimitry Andric   void Resize(size_t n, TreeDelegate &delegate, bool might_have_children) {
4671b1c73532SDimitry Andric     if (m_children.size() >= n) {
4672b1c73532SDimitry Andric       m_children.erase(m_children.begin() + n, m_children.end());
4673b1c73532SDimitry Andric       return;
4674b1c73532SDimitry Andric     }
4675b1c73532SDimitry Andric     m_children.reserve(n);
4676b1c73532SDimitry Andric     std::generate_n(std::back_inserter(m_children), n - m_children.size(),
4677b1c73532SDimitry Andric                     [&, parent = this]() {
4678b1c73532SDimitry Andric                       return TreeItem(parent, delegate, might_have_children);
4679b1c73532SDimitry Andric                     });
4680b1c73532SDimitry Andric   }
4681706b4fc4SDimitry Andric 
operator [](size_t i)4682706b4fc4SDimitry Andric   TreeItem &operator[](size_t i) { return m_children[i]; }
4683706b4fc4SDimitry Andric 
SetRowIndex(int row_idx)4684706b4fc4SDimitry Andric   void SetRowIndex(int row_idx) { m_row_idx = row_idx; }
4685706b4fc4SDimitry Andric 
GetNumChildren()4686706b4fc4SDimitry Andric   size_t GetNumChildren() {
4687b1c73532SDimitry Andric     m_delegate->TreeDelegateGenerateChildren(*this);
4688706b4fc4SDimitry Andric     return m_children.size();
4689706b4fc4SDimitry Andric   }
4690706b4fc4SDimitry Andric 
ItemWasSelected()4691b1c73532SDimitry Andric   void ItemWasSelected() { m_delegate->TreeDelegateItemSelected(*this); }
4692706b4fc4SDimitry Andric 
CalculateRowIndexes(int & row_idx)4693706b4fc4SDimitry Andric   void CalculateRowIndexes(int &row_idx) {
4694706b4fc4SDimitry Andric     SetRowIndex(row_idx);
4695706b4fc4SDimitry Andric     ++row_idx;
4696706b4fc4SDimitry Andric 
4697706b4fc4SDimitry Andric     const bool expanded = IsExpanded();
4698706b4fc4SDimitry Andric 
4699706b4fc4SDimitry Andric     // The root item must calculate its children, or we must calculate the
4700706b4fc4SDimitry Andric     // number of children if the item is expanded
4701706b4fc4SDimitry Andric     if (m_parent == nullptr || expanded)
4702706b4fc4SDimitry Andric       GetNumChildren();
4703706b4fc4SDimitry Andric 
4704706b4fc4SDimitry Andric     for (auto &item : m_children) {
4705706b4fc4SDimitry Andric       if (expanded)
4706706b4fc4SDimitry Andric         item.CalculateRowIndexes(row_idx);
4707706b4fc4SDimitry Andric       else
4708706b4fc4SDimitry Andric         item.SetRowIndex(-1);
4709706b4fc4SDimitry Andric     }
4710706b4fc4SDimitry Andric   }
4711706b4fc4SDimitry Andric 
GetParent()4712706b4fc4SDimitry Andric   TreeItem *GetParent() { return m_parent; }
4713706b4fc4SDimitry Andric 
IsExpanded() const4714706b4fc4SDimitry Andric   bool IsExpanded() const { return m_is_expanded; }
4715706b4fc4SDimitry Andric 
Expand()4716706b4fc4SDimitry Andric   void Expand() { m_is_expanded = true; }
4717706b4fc4SDimitry Andric 
Unexpand()4718706b4fc4SDimitry Andric   void Unexpand() { m_is_expanded = false; }
4719706b4fc4SDimitry Andric 
Draw(Window & window,const int first_visible_row,const uint32_t selected_row_idx,int & row_idx,int & num_rows_left)4720706b4fc4SDimitry Andric   bool Draw(Window &window, const int first_visible_row,
4721706b4fc4SDimitry Andric             const uint32_t selected_row_idx, int &row_idx, int &num_rows_left) {
4722706b4fc4SDimitry Andric     if (num_rows_left <= 0)
4723706b4fc4SDimitry Andric       return false;
4724706b4fc4SDimitry Andric 
4725706b4fc4SDimitry Andric     if (m_row_idx >= first_visible_row) {
4726706b4fc4SDimitry Andric       window.MoveCursor(2, row_idx + 1);
4727706b4fc4SDimitry Andric 
4728706b4fc4SDimitry Andric       if (m_parent)
4729706b4fc4SDimitry Andric         m_parent->DrawTreeForChild(window, this, 0);
4730706b4fc4SDimitry Andric 
4731706b4fc4SDimitry Andric       if (m_might_have_children) {
4732706b4fc4SDimitry Andric         // It we can get UTF8 characters to work we should try to use the
4733706b4fc4SDimitry Andric         // "symbol" UTF8 string below
4734706b4fc4SDimitry Andric         //            const char *symbol = "";
4735706b4fc4SDimitry Andric         //            if (row.expanded)
4736706b4fc4SDimitry Andric         //                symbol = "\xe2\x96\xbd ";
4737706b4fc4SDimitry Andric         //            else
4738706b4fc4SDimitry Andric         //                symbol = "\xe2\x96\xb7 ";
4739706b4fc4SDimitry Andric         //            window.PutCString (symbol);
4740706b4fc4SDimitry Andric 
4741706b4fc4SDimitry Andric         // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
4742706b4fc4SDimitry Andric         // 'v' or '>' character...
4743706b4fc4SDimitry Andric         //            if (expanded)
4744706b4fc4SDimitry Andric         //                window.PutChar (ACS_DARROW);
4745706b4fc4SDimitry Andric         //            else
4746706b4fc4SDimitry Andric         //                window.PutChar (ACS_RARROW);
4747706b4fc4SDimitry Andric         // Since we can't find any good looking right arrow/down arrow symbols,
4748706b4fc4SDimitry Andric         // just use a diamond...
4749706b4fc4SDimitry Andric         window.PutChar(ACS_DIAMOND);
4750706b4fc4SDimitry Andric         window.PutChar(ACS_HLINE);
4751706b4fc4SDimitry Andric       }
4752706b4fc4SDimitry Andric       bool highlight = (selected_row_idx == static_cast<size_t>(m_row_idx)) &&
4753706b4fc4SDimitry Andric                        window.IsActive();
4754706b4fc4SDimitry Andric 
4755706b4fc4SDimitry Andric       if (highlight)
4756706b4fc4SDimitry Andric         window.AttributeOn(A_REVERSE);
4757706b4fc4SDimitry Andric 
4758b1c73532SDimitry Andric       m_delegate->TreeDelegateDrawTreeItem(*this, window);
4759706b4fc4SDimitry Andric 
4760706b4fc4SDimitry Andric       if (highlight)
4761706b4fc4SDimitry Andric         window.AttributeOff(A_REVERSE);
4762706b4fc4SDimitry Andric       ++row_idx;
4763706b4fc4SDimitry Andric       --num_rows_left;
4764706b4fc4SDimitry Andric     }
4765706b4fc4SDimitry Andric 
4766706b4fc4SDimitry Andric     if (num_rows_left <= 0)
4767706b4fc4SDimitry Andric       return false; // We are done drawing...
4768706b4fc4SDimitry Andric 
4769706b4fc4SDimitry Andric     if (IsExpanded()) {
4770706b4fc4SDimitry Andric       for (auto &item : m_children) {
4771706b4fc4SDimitry Andric         // If we displayed all the rows and item.Draw() returns false we are
4772706b4fc4SDimitry Andric         // done drawing and can exit this for loop
4773706b4fc4SDimitry Andric         if (!item.Draw(window, first_visible_row, selected_row_idx, row_idx,
4774706b4fc4SDimitry Andric                        num_rows_left))
4775706b4fc4SDimitry Andric           break;
4776706b4fc4SDimitry Andric       }
4777706b4fc4SDimitry Andric     }
4778706b4fc4SDimitry Andric     return num_rows_left >= 0; // Return true if not done drawing yet
4779706b4fc4SDimitry Andric   }
4780706b4fc4SDimitry Andric 
DrawTreeForChild(Window & window,TreeItem * child,uint32_t reverse_depth)4781706b4fc4SDimitry Andric   void DrawTreeForChild(Window &window, TreeItem *child,
4782706b4fc4SDimitry Andric                         uint32_t reverse_depth) {
4783706b4fc4SDimitry Andric     if (m_parent)
4784706b4fc4SDimitry Andric       m_parent->DrawTreeForChild(window, this, reverse_depth + 1);
4785706b4fc4SDimitry Andric 
4786706b4fc4SDimitry Andric     if (&m_children.back() == child) {
4787706b4fc4SDimitry Andric       // Last child
4788706b4fc4SDimitry Andric       if (reverse_depth == 0) {
4789706b4fc4SDimitry Andric         window.PutChar(ACS_LLCORNER);
4790706b4fc4SDimitry Andric         window.PutChar(ACS_HLINE);
4791706b4fc4SDimitry Andric       } else {
4792706b4fc4SDimitry Andric         window.PutChar(' ');
4793706b4fc4SDimitry Andric         window.PutChar(' ');
4794706b4fc4SDimitry Andric       }
4795706b4fc4SDimitry Andric     } else {
4796706b4fc4SDimitry Andric       if (reverse_depth == 0) {
4797706b4fc4SDimitry Andric         window.PutChar(ACS_LTEE);
4798706b4fc4SDimitry Andric         window.PutChar(ACS_HLINE);
4799706b4fc4SDimitry Andric       } else {
4800706b4fc4SDimitry Andric         window.PutChar(ACS_VLINE);
4801706b4fc4SDimitry Andric         window.PutChar(' ');
4802706b4fc4SDimitry Andric       }
4803706b4fc4SDimitry Andric     }
4804706b4fc4SDimitry Andric   }
4805706b4fc4SDimitry Andric 
GetItemForRowIndex(uint32_t row_idx)4806706b4fc4SDimitry Andric   TreeItem *GetItemForRowIndex(uint32_t row_idx) {
4807706b4fc4SDimitry Andric     if (static_cast<uint32_t>(m_row_idx) == row_idx)
4808706b4fc4SDimitry Andric       return this;
4809706b4fc4SDimitry Andric     if (m_children.empty())
4810706b4fc4SDimitry Andric       return nullptr;
4811706b4fc4SDimitry Andric     if (IsExpanded()) {
4812706b4fc4SDimitry Andric       for (auto &item : m_children) {
4813706b4fc4SDimitry Andric         TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
4814706b4fc4SDimitry Andric         if (selected_item_ptr)
4815706b4fc4SDimitry Andric           return selected_item_ptr;
4816706b4fc4SDimitry Andric       }
4817706b4fc4SDimitry Andric     }
4818706b4fc4SDimitry Andric     return nullptr;
4819706b4fc4SDimitry Andric   }
4820706b4fc4SDimitry Andric 
GetUserData() const4821706b4fc4SDimitry Andric   void *GetUserData() const { return m_user_data; }
4822706b4fc4SDimitry Andric 
SetUserData(void * user_data)4823706b4fc4SDimitry Andric   void SetUserData(void *user_data) { m_user_data = user_data; }
4824706b4fc4SDimitry Andric 
GetIdentifier() const4825706b4fc4SDimitry Andric   uint64_t GetIdentifier() const { return m_identifier; }
4826706b4fc4SDimitry Andric 
SetIdentifier(uint64_t identifier)4827706b4fc4SDimitry Andric   void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
4828706b4fc4SDimitry Andric 
GetText() const4829c0981da4SDimitry Andric   const std::string &GetText() const { return m_text; }
4830c0981da4SDimitry Andric 
SetText(const char * text)4831c0981da4SDimitry Andric   void SetText(const char *text) {
4832c0981da4SDimitry Andric     if (text == nullptr) {
4833c0981da4SDimitry Andric       m_text.clear();
4834c0981da4SDimitry Andric       return;
4835c0981da4SDimitry Andric     }
4836c0981da4SDimitry Andric     m_text = text;
4837c0981da4SDimitry Andric   }
4838c0981da4SDimitry Andric 
SetMightHaveChildren(bool b)4839706b4fc4SDimitry Andric   void SetMightHaveChildren(bool b) { m_might_have_children = b; }
4840706b4fc4SDimitry Andric 
4841706b4fc4SDimitry Andric protected:
AdoptChildren(std::vector<TreeItem> & children)4842b1c73532SDimitry Andric   void AdoptChildren(std::vector<TreeItem> &children) {
4843b1c73532SDimitry Andric     m_children = std::move(children);
4844b1c73532SDimitry Andric     for (auto &child : m_children)
4845b1c73532SDimitry Andric       child.m_parent = this;
4846b1c73532SDimitry Andric   }
4847b1c73532SDimitry Andric 
4848706b4fc4SDimitry Andric   std::vector<TreeItem> m_children;
4849706b4fc4SDimitry Andric };
4850706b4fc4SDimitry Andric 
4851706b4fc4SDimitry Andric class TreeWindowDelegate : public WindowDelegate {
4852706b4fc4SDimitry Andric public:
TreeWindowDelegate(Debugger & debugger,const TreeDelegateSP & delegate_sp)4853706b4fc4SDimitry Andric   TreeWindowDelegate(Debugger &debugger, const TreeDelegateSP &delegate_sp)
4854706b4fc4SDimitry Andric       : m_debugger(debugger), m_delegate_sp(delegate_sp),
4855145449b1SDimitry Andric         m_root(nullptr, *delegate_sp, true) {}
4856706b4fc4SDimitry Andric 
NumVisibleRows() const4857706b4fc4SDimitry Andric   int NumVisibleRows() const { return m_max_y - m_min_y; }
4858706b4fc4SDimitry Andric 
WindowDelegateDraw(Window & window,bool force)4859706b4fc4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
4860706b4fc4SDimitry Andric     m_min_x = 2;
4861706b4fc4SDimitry Andric     m_min_y = 1;
4862706b4fc4SDimitry Andric     m_max_x = window.GetWidth() - 1;
4863706b4fc4SDimitry Andric     m_max_y = window.GetHeight() - 1;
4864706b4fc4SDimitry Andric 
4865706b4fc4SDimitry Andric     window.Erase();
4866706b4fc4SDimitry Andric     window.DrawTitleBox(window.GetName());
4867706b4fc4SDimitry Andric 
4868c0981da4SDimitry Andric     if (!m_delegate_sp->TreeDelegateShouldDraw()) {
4869c0981da4SDimitry Andric       m_selected_item = nullptr;
4870c0981da4SDimitry Andric       return true;
4871c0981da4SDimitry Andric     }
4872c0981da4SDimitry Andric 
4873706b4fc4SDimitry Andric     const int num_visible_rows = NumVisibleRows();
4874706b4fc4SDimitry Andric     m_num_rows = 0;
4875706b4fc4SDimitry Andric     m_root.CalculateRowIndexes(m_num_rows);
4876344a3780SDimitry Andric     m_delegate_sp->TreeDelegateUpdateSelection(m_root, m_selected_row_idx,
4877344a3780SDimitry Andric                                                m_selected_item);
4878706b4fc4SDimitry Andric 
4879706b4fc4SDimitry Andric     // If we unexpanded while having something selected our total number of
4880706b4fc4SDimitry Andric     // rows is less than the num visible rows, then make sure we show all the
4881706b4fc4SDimitry Andric     // rows by setting the first visible row accordingly.
4882706b4fc4SDimitry Andric     if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
4883706b4fc4SDimitry Andric       m_first_visible_row = 0;
4884706b4fc4SDimitry Andric 
4885706b4fc4SDimitry Andric     // Make sure the selected row is always visible
4886706b4fc4SDimitry Andric     if (m_selected_row_idx < m_first_visible_row)
4887706b4fc4SDimitry Andric       m_first_visible_row = m_selected_row_idx;
4888706b4fc4SDimitry Andric     else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
4889706b4fc4SDimitry Andric       m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
4890706b4fc4SDimitry Andric 
4891706b4fc4SDimitry Andric     int row_idx = 0;
4892706b4fc4SDimitry Andric     int num_rows_left = num_visible_rows;
4893706b4fc4SDimitry Andric     m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx,
4894706b4fc4SDimitry Andric                 num_rows_left);
4895706b4fc4SDimitry Andric     // Get the selected row
4896706b4fc4SDimitry Andric     m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4897706b4fc4SDimitry Andric 
4898706b4fc4SDimitry Andric     return true; // Drawing handled
4899706b4fc4SDimitry Andric   }
4900706b4fc4SDimitry Andric 
WindowDelegateGetHelpText()4901706b4fc4SDimitry Andric   const char *WindowDelegateGetHelpText() override {
4902706b4fc4SDimitry Andric     return "Thread window keyboard shortcuts:";
4903706b4fc4SDimitry Andric   }
4904706b4fc4SDimitry Andric 
WindowDelegateGetKeyHelp()4905706b4fc4SDimitry Andric   KeyHelp *WindowDelegateGetKeyHelp() override {
4906706b4fc4SDimitry Andric     static curses::KeyHelp g_source_view_key_help[] = {
4907706b4fc4SDimitry Andric         {KEY_UP, "Select previous item"},
4908706b4fc4SDimitry Andric         {KEY_DOWN, "Select next item"},
4909706b4fc4SDimitry Andric         {KEY_RIGHT, "Expand the selected item"},
4910706b4fc4SDimitry Andric         {KEY_LEFT,
4911706b4fc4SDimitry Andric          "Unexpand the selected item or select parent if not expanded"},
4912706b4fc4SDimitry Andric         {KEY_PPAGE, "Page up"},
4913706b4fc4SDimitry Andric         {KEY_NPAGE, "Page down"},
4914706b4fc4SDimitry Andric         {'h', "Show help dialog"},
4915706b4fc4SDimitry Andric         {' ', "Toggle item expansion"},
4916706b4fc4SDimitry Andric         {',', "Page up"},
4917706b4fc4SDimitry Andric         {'.', "Page down"},
4918706b4fc4SDimitry Andric         {'\0', nullptr}};
4919706b4fc4SDimitry Andric     return g_source_view_key_help;
4920706b4fc4SDimitry Andric   }
4921706b4fc4SDimitry Andric 
WindowDelegateHandleChar(Window & window,int c)4922706b4fc4SDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
4923706b4fc4SDimitry Andric     switch (c) {
4924706b4fc4SDimitry Andric     case ',':
4925706b4fc4SDimitry Andric     case KEY_PPAGE:
4926706b4fc4SDimitry Andric       // Page up key
4927706b4fc4SDimitry Andric       if (m_first_visible_row > 0) {
4928706b4fc4SDimitry Andric         if (m_first_visible_row > m_max_y)
4929706b4fc4SDimitry Andric           m_first_visible_row -= m_max_y;
4930706b4fc4SDimitry Andric         else
4931706b4fc4SDimitry Andric           m_first_visible_row = 0;
4932706b4fc4SDimitry Andric         m_selected_row_idx = m_first_visible_row;
4933706b4fc4SDimitry Andric         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4934706b4fc4SDimitry Andric         if (m_selected_item)
4935706b4fc4SDimitry Andric           m_selected_item->ItemWasSelected();
4936706b4fc4SDimitry Andric       }
4937706b4fc4SDimitry Andric       return eKeyHandled;
4938706b4fc4SDimitry Andric 
4939706b4fc4SDimitry Andric     case '.':
4940706b4fc4SDimitry Andric     case KEY_NPAGE:
4941706b4fc4SDimitry Andric       // Page down key
4942706b4fc4SDimitry Andric       if (m_num_rows > m_max_y) {
4943706b4fc4SDimitry Andric         if (m_first_visible_row + m_max_y < m_num_rows) {
4944706b4fc4SDimitry Andric           m_first_visible_row += m_max_y;
4945706b4fc4SDimitry Andric           m_selected_row_idx = m_first_visible_row;
4946706b4fc4SDimitry Andric           m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4947706b4fc4SDimitry Andric           if (m_selected_item)
4948706b4fc4SDimitry Andric             m_selected_item->ItemWasSelected();
4949706b4fc4SDimitry Andric         }
4950706b4fc4SDimitry Andric       }
4951706b4fc4SDimitry Andric       return eKeyHandled;
4952706b4fc4SDimitry Andric 
4953706b4fc4SDimitry Andric     case KEY_UP:
4954706b4fc4SDimitry Andric       if (m_selected_row_idx > 0) {
4955706b4fc4SDimitry Andric         --m_selected_row_idx;
4956706b4fc4SDimitry Andric         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4957706b4fc4SDimitry Andric         if (m_selected_item)
4958706b4fc4SDimitry Andric           m_selected_item->ItemWasSelected();
4959706b4fc4SDimitry Andric       }
4960706b4fc4SDimitry Andric       return eKeyHandled;
4961706b4fc4SDimitry Andric 
4962706b4fc4SDimitry Andric     case KEY_DOWN:
4963706b4fc4SDimitry Andric       if (m_selected_row_idx + 1 < m_num_rows) {
4964706b4fc4SDimitry Andric         ++m_selected_row_idx;
4965706b4fc4SDimitry Andric         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4966706b4fc4SDimitry Andric         if (m_selected_item)
4967706b4fc4SDimitry Andric           m_selected_item->ItemWasSelected();
4968706b4fc4SDimitry Andric       }
4969706b4fc4SDimitry Andric       return eKeyHandled;
4970706b4fc4SDimitry Andric 
4971706b4fc4SDimitry Andric     case KEY_RIGHT:
4972706b4fc4SDimitry Andric       if (m_selected_item) {
4973706b4fc4SDimitry Andric         if (!m_selected_item->IsExpanded())
4974706b4fc4SDimitry Andric           m_selected_item->Expand();
4975706b4fc4SDimitry Andric       }
4976706b4fc4SDimitry Andric       return eKeyHandled;
4977706b4fc4SDimitry Andric 
4978706b4fc4SDimitry Andric     case KEY_LEFT:
4979706b4fc4SDimitry Andric       if (m_selected_item) {
4980706b4fc4SDimitry Andric         if (m_selected_item->IsExpanded())
4981706b4fc4SDimitry Andric           m_selected_item->Unexpand();
4982706b4fc4SDimitry Andric         else if (m_selected_item->GetParent()) {
4983706b4fc4SDimitry Andric           m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
4984706b4fc4SDimitry Andric           m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4985706b4fc4SDimitry Andric           if (m_selected_item)
4986706b4fc4SDimitry Andric             m_selected_item->ItemWasSelected();
4987706b4fc4SDimitry Andric         }
4988706b4fc4SDimitry Andric       }
4989706b4fc4SDimitry Andric       return eKeyHandled;
4990706b4fc4SDimitry Andric 
4991706b4fc4SDimitry Andric     case ' ':
4992706b4fc4SDimitry Andric       // Toggle expansion state when SPACE is pressed
4993706b4fc4SDimitry Andric       if (m_selected_item) {
4994706b4fc4SDimitry Andric         if (m_selected_item->IsExpanded())
4995706b4fc4SDimitry Andric           m_selected_item->Unexpand();
4996706b4fc4SDimitry Andric         else
4997706b4fc4SDimitry Andric           m_selected_item->Expand();
4998706b4fc4SDimitry Andric       }
4999706b4fc4SDimitry Andric       return eKeyHandled;
5000706b4fc4SDimitry Andric 
5001706b4fc4SDimitry Andric     case 'h':
5002706b4fc4SDimitry Andric       window.CreateHelpSubwindow();
5003706b4fc4SDimitry Andric       return eKeyHandled;
5004706b4fc4SDimitry Andric 
5005706b4fc4SDimitry Andric     default:
5006706b4fc4SDimitry Andric       break;
5007706b4fc4SDimitry Andric     }
5008706b4fc4SDimitry Andric     return eKeyNotHandled;
5009706b4fc4SDimitry Andric   }
5010706b4fc4SDimitry Andric 
5011706b4fc4SDimitry Andric protected:
5012706b4fc4SDimitry Andric   Debugger &m_debugger;
5013706b4fc4SDimitry Andric   TreeDelegateSP m_delegate_sp;
5014706b4fc4SDimitry Andric   TreeItem m_root;
5015145449b1SDimitry Andric   TreeItem *m_selected_item = nullptr;
5016145449b1SDimitry Andric   int m_num_rows = 0;
5017145449b1SDimitry Andric   int m_selected_row_idx = 0;
5018145449b1SDimitry Andric   int m_first_visible_row = 0;
5019145449b1SDimitry Andric   int m_min_x = 0;
5020145449b1SDimitry Andric   int m_min_y = 0;
5021145449b1SDimitry Andric   int m_max_x = 0;
5022145449b1SDimitry Andric   int m_max_y = 0;
5023706b4fc4SDimitry Andric };
5024706b4fc4SDimitry Andric 
5025c0981da4SDimitry Andric // A tree delegate that just draws the text member of the tree item, it doesn't
5026c0981da4SDimitry Andric // have any children or actions.
5027c0981da4SDimitry Andric class TextTreeDelegate : public TreeDelegate {
5028c0981da4SDimitry Andric public:
TextTreeDelegate()5029c0981da4SDimitry Andric   TextTreeDelegate() : TreeDelegate() {}
5030c0981da4SDimitry Andric 
5031c0981da4SDimitry Andric   ~TextTreeDelegate() override = default;
5032c0981da4SDimitry Andric 
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5033c0981da4SDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5034c0981da4SDimitry Andric     window.PutCStringTruncated(1, item.GetText().c_str());
5035c0981da4SDimitry Andric   }
5036c0981da4SDimitry Andric 
TreeDelegateGenerateChildren(TreeItem & item)5037c0981da4SDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {}
5038c0981da4SDimitry Andric 
TreeDelegateItemSelected(TreeItem & item)5039c0981da4SDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5040c0981da4SDimitry Andric };
5041c0981da4SDimitry Andric 
5042706b4fc4SDimitry Andric class FrameTreeDelegate : public TreeDelegate {
5043706b4fc4SDimitry Andric public:
FrameTreeDelegate()5044706b4fc4SDimitry Andric   FrameTreeDelegate() : TreeDelegate() {
5045706b4fc4SDimitry Andric     FormatEntity::Parse(
5046145449b1SDimitry Andric         "#${frame.index}: {${function.name}${function.pc-offset}}}", m_format);
5047706b4fc4SDimitry Andric   }
5048706b4fc4SDimitry Andric 
5049706b4fc4SDimitry Andric   ~FrameTreeDelegate() override = default;
5050706b4fc4SDimitry Andric 
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5051706b4fc4SDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5052706b4fc4SDimitry Andric     Thread *thread = (Thread *)item.GetUserData();
5053706b4fc4SDimitry Andric     if (thread) {
5054706b4fc4SDimitry Andric       const uint64_t frame_idx = item.GetIdentifier();
5055706b4fc4SDimitry Andric       StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
5056706b4fc4SDimitry Andric       if (frame_sp) {
5057706b4fc4SDimitry Andric         StreamString strm;
5058706b4fc4SDimitry Andric         const SymbolContext &sc =
5059706b4fc4SDimitry Andric             frame_sp->GetSymbolContext(eSymbolContextEverything);
5060706b4fc4SDimitry Andric         ExecutionContext exe_ctx(frame_sp);
5061706b4fc4SDimitry Andric         if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr,
5062706b4fc4SDimitry Andric                                  nullptr, false, false)) {
5063706b4fc4SDimitry Andric           int right_pad = 1;
5064b60736ecSDimitry Andric           window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
5065706b4fc4SDimitry Andric         }
5066706b4fc4SDimitry Andric       }
5067706b4fc4SDimitry Andric     }
5068706b4fc4SDimitry Andric   }
5069706b4fc4SDimitry Andric 
TreeDelegateGenerateChildren(TreeItem & item)5070706b4fc4SDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {
5071706b4fc4SDimitry Andric     // No children for frames yet...
5072706b4fc4SDimitry Andric   }
5073706b4fc4SDimitry Andric 
TreeDelegateItemSelected(TreeItem & item)5074706b4fc4SDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override {
5075706b4fc4SDimitry Andric     Thread *thread = (Thread *)item.GetUserData();
5076706b4fc4SDimitry Andric     if (thread) {
5077706b4fc4SDimitry Andric       thread->GetProcess()->GetThreadList().SetSelectedThreadByID(
5078706b4fc4SDimitry Andric           thread->GetID());
5079706b4fc4SDimitry Andric       const uint64_t frame_idx = item.GetIdentifier();
5080706b4fc4SDimitry Andric       thread->SetSelectedFrameByIndex(frame_idx);
5081706b4fc4SDimitry Andric       return true;
5082706b4fc4SDimitry Andric     }
5083706b4fc4SDimitry Andric     return false;
5084706b4fc4SDimitry Andric   }
5085706b4fc4SDimitry Andric 
5086706b4fc4SDimitry Andric protected:
5087706b4fc4SDimitry Andric   FormatEntity::Entry m_format;
5088706b4fc4SDimitry Andric };
5089706b4fc4SDimitry Andric 
5090706b4fc4SDimitry Andric class ThreadTreeDelegate : public TreeDelegate {
5091706b4fc4SDimitry Andric public:
ThreadTreeDelegate(Debugger & debugger)5092706b4fc4SDimitry Andric   ThreadTreeDelegate(Debugger &debugger)
5093145449b1SDimitry Andric       : TreeDelegate(), m_debugger(debugger) {
5094706b4fc4SDimitry Andric     FormatEntity::Parse("thread #${thread.index}: tid = ${thread.id}{, stop "
5095706b4fc4SDimitry Andric                         "reason = ${thread.stop-reason}}",
5096706b4fc4SDimitry Andric                         m_format);
5097706b4fc4SDimitry Andric   }
5098706b4fc4SDimitry Andric 
5099706b4fc4SDimitry Andric   ~ThreadTreeDelegate() override = default;
5100706b4fc4SDimitry Andric 
GetProcess()5101706b4fc4SDimitry Andric   ProcessSP GetProcess() {
5102706b4fc4SDimitry Andric     return m_debugger.GetCommandInterpreter()
5103706b4fc4SDimitry Andric         .GetExecutionContext()
5104706b4fc4SDimitry Andric         .GetProcessSP();
5105706b4fc4SDimitry Andric   }
5106706b4fc4SDimitry Andric 
GetThread(const TreeItem & item)5107706b4fc4SDimitry Andric   ThreadSP GetThread(const TreeItem &item) {
5108706b4fc4SDimitry Andric     ProcessSP process_sp = GetProcess();
5109706b4fc4SDimitry Andric     if (process_sp)
5110706b4fc4SDimitry Andric       return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
5111706b4fc4SDimitry Andric     return ThreadSP();
5112706b4fc4SDimitry Andric   }
5113706b4fc4SDimitry Andric 
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5114706b4fc4SDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5115706b4fc4SDimitry Andric     ThreadSP thread_sp = GetThread(item);
5116706b4fc4SDimitry Andric     if (thread_sp) {
5117706b4fc4SDimitry Andric       StreamString strm;
5118706b4fc4SDimitry Andric       ExecutionContext exe_ctx(thread_sp);
5119706b4fc4SDimitry Andric       if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
5120706b4fc4SDimitry Andric                                nullptr, false, false)) {
5121706b4fc4SDimitry Andric         int right_pad = 1;
5122b60736ecSDimitry Andric         window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
5123706b4fc4SDimitry Andric       }
5124706b4fc4SDimitry Andric     }
5125706b4fc4SDimitry Andric   }
5126706b4fc4SDimitry Andric 
TreeDelegateGenerateChildren(TreeItem & item)5127706b4fc4SDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {
5128706b4fc4SDimitry Andric     ProcessSP process_sp = GetProcess();
5129706b4fc4SDimitry Andric     if (process_sp && process_sp->IsAlive()) {
5130706b4fc4SDimitry Andric       StateType state = process_sp->GetState();
5131706b4fc4SDimitry Andric       if (StateIsStoppedState(state, true)) {
5132706b4fc4SDimitry Andric         ThreadSP thread_sp = GetThread(item);
5133706b4fc4SDimitry Andric         if (thread_sp) {
5134706b4fc4SDimitry Andric           if (m_stop_id == process_sp->GetStopID() &&
5135706b4fc4SDimitry Andric               thread_sp->GetID() == m_tid)
5136706b4fc4SDimitry Andric             return; // Children are already up to date
5137706b4fc4SDimitry Andric           if (!m_frame_delegate_sp) {
5138706b4fc4SDimitry Andric             // Always expand the thread item the first time we show it
5139706b4fc4SDimitry Andric             m_frame_delegate_sp = std::make_shared<FrameTreeDelegate>();
5140706b4fc4SDimitry Andric           }
5141706b4fc4SDimitry Andric 
5142706b4fc4SDimitry Andric           m_stop_id = process_sp->GetStopID();
5143706b4fc4SDimitry Andric           m_tid = thread_sp->GetID();
5144706b4fc4SDimitry Andric 
5145706b4fc4SDimitry Andric           size_t num_frames = thread_sp->GetStackFrameCount();
5146b1c73532SDimitry Andric           item.Resize(num_frames, *m_frame_delegate_sp, false);
5147706b4fc4SDimitry Andric           for (size_t i = 0; i < num_frames; ++i) {
5148706b4fc4SDimitry Andric             item[i].SetUserData(thread_sp.get());
5149706b4fc4SDimitry Andric             item[i].SetIdentifier(i);
5150706b4fc4SDimitry Andric           }
5151706b4fc4SDimitry Andric         }
5152706b4fc4SDimitry Andric         return;
5153706b4fc4SDimitry Andric       }
5154706b4fc4SDimitry Andric     }
5155706b4fc4SDimitry Andric     item.ClearChildren();
5156706b4fc4SDimitry Andric   }
5157706b4fc4SDimitry Andric 
TreeDelegateItemSelected(TreeItem & item)5158706b4fc4SDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override {
5159706b4fc4SDimitry Andric     ProcessSP process_sp = GetProcess();
5160706b4fc4SDimitry Andric     if (process_sp && process_sp->IsAlive()) {
5161706b4fc4SDimitry Andric       StateType state = process_sp->GetState();
5162706b4fc4SDimitry Andric       if (StateIsStoppedState(state, true)) {
5163706b4fc4SDimitry Andric         ThreadSP thread_sp = GetThread(item);
5164706b4fc4SDimitry Andric         if (thread_sp) {
5165706b4fc4SDimitry Andric           ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
5166706b4fc4SDimitry Andric           std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex());
5167706b4fc4SDimitry Andric           ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
5168706b4fc4SDimitry Andric           if (selected_thread_sp->GetID() != thread_sp->GetID()) {
5169706b4fc4SDimitry Andric             thread_list.SetSelectedThreadByID(thread_sp->GetID());
5170706b4fc4SDimitry Andric             return true;
5171706b4fc4SDimitry Andric           }
5172706b4fc4SDimitry Andric         }
5173706b4fc4SDimitry Andric       }
5174706b4fc4SDimitry Andric     }
5175706b4fc4SDimitry Andric     return false;
5176706b4fc4SDimitry Andric   }
5177706b4fc4SDimitry Andric 
5178706b4fc4SDimitry Andric protected:
5179706b4fc4SDimitry Andric   Debugger &m_debugger;
5180706b4fc4SDimitry Andric   std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
5181145449b1SDimitry Andric   lldb::user_id_t m_tid = LLDB_INVALID_THREAD_ID;
5182145449b1SDimitry Andric   uint32_t m_stop_id = UINT32_MAX;
5183706b4fc4SDimitry Andric   FormatEntity::Entry m_format;
5184706b4fc4SDimitry Andric };
5185706b4fc4SDimitry Andric 
5186706b4fc4SDimitry Andric class ThreadsTreeDelegate : public TreeDelegate {
5187706b4fc4SDimitry Andric public:
ThreadsTreeDelegate(Debugger & debugger)5188706b4fc4SDimitry Andric   ThreadsTreeDelegate(Debugger &debugger)
5189145449b1SDimitry Andric       : TreeDelegate(), m_thread_delegate_sp(), m_debugger(debugger) {
5190706b4fc4SDimitry Andric     FormatEntity::Parse("process ${process.id}{, name = ${process.name}}",
5191706b4fc4SDimitry Andric                         m_format);
5192706b4fc4SDimitry Andric   }
5193706b4fc4SDimitry Andric 
5194706b4fc4SDimitry Andric   ~ThreadsTreeDelegate() override = default;
5195706b4fc4SDimitry Andric 
GetProcess()5196706b4fc4SDimitry Andric   ProcessSP GetProcess() {
5197706b4fc4SDimitry Andric     return m_debugger.GetCommandInterpreter()
5198706b4fc4SDimitry Andric         .GetExecutionContext()
5199706b4fc4SDimitry Andric         .GetProcessSP();
5200706b4fc4SDimitry Andric   }
5201706b4fc4SDimitry Andric 
TreeDelegateShouldDraw()5202c0981da4SDimitry Andric   bool TreeDelegateShouldDraw() override {
5203c0981da4SDimitry Andric     ProcessSP process = GetProcess();
5204c0981da4SDimitry Andric     if (!process)
5205c0981da4SDimitry Andric       return false;
5206c0981da4SDimitry Andric 
5207c0981da4SDimitry Andric     if (StateIsRunningState(process->GetState()))
5208c0981da4SDimitry Andric       return false;
5209c0981da4SDimitry Andric 
5210c0981da4SDimitry Andric     return true;
5211c0981da4SDimitry Andric   }
5212c0981da4SDimitry Andric 
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5213706b4fc4SDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5214706b4fc4SDimitry Andric     ProcessSP process_sp = GetProcess();
5215706b4fc4SDimitry Andric     if (process_sp && process_sp->IsAlive()) {
5216706b4fc4SDimitry Andric       StreamString strm;
5217706b4fc4SDimitry Andric       ExecutionContext exe_ctx(process_sp);
5218706b4fc4SDimitry Andric       if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
5219706b4fc4SDimitry Andric                                nullptr, false, false)) {
5220706b4fc4SDimitry Andric         int right_pad = 1;
5221b60736ecSDimitry Andric         window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
5222706b4fc4SDimitry Andric       }
5223706b4fc4SDimitry Andric     }
5224706b4fc4SDimitry Andric   }
5225706b4fc4SDimitry Andric 
TreeDelegateGenerateChildren(TreeItem & item)5226706b4fc4SDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {
5227706b4fc4SDimitry Andric     ProcessSP process_sp = GetProcess();
5228344a3780SDimitry Andric     m_update_selection = false;
5229706b4fc4SDimitry Andric     if (process_sp && process_sp->IsAlive()) {
5230706b4fc4SDimitry Andric       StateType state = process_sp->GetState();
5231706b4fc4SDimitry Andric       if (StateIsStoppedState(state, true)) {
5232706b4fc4SDimitry Andric         const uint32_t stop_id = process_sp->GetStopID();
5233706b4fc4SDimitry Andric         if (m_stop_id == stop_id)
5234706b4fc4SDimitry Andric           return; // Children are already up to date
5235706b4fc4SDimitry Andric 
5236706b4fc4SDimitry Andric         m_stop_id = stop_id;
5237344a3780SDimitry Andric         m_update_selection = true;
5238706b4fc4SDimitry Andric 
5239706b4fc4SDimitry Andric         if (!m_thread_delegate_sp) {
5240706b4fc4SDimitry Andric           // Always expand the thread item the first time we show it
5241706b4fc4SDimitry Andric           // item.Expand();
5242706b4fc4SDimitry Andric           m_thread_delegate_sp =
5243706b4fc4SDimitry Andric               std::make_shared<ThreadTreeDelegate>(m_debugger);
5244706b4fc4SDimitry Andric         }
5245706b4fc4SDimitry Andric 
5246706b4fc4SDimitry Andric         ThreadList &threads = process_sp->GetThreadList();
5247706b4fc4SDimitry Andric         std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
5248344a3780SDimitry Andric         ThreadSP selected_thread = threads.GetSelectedThread();
5249706b4fc4SDimitry Andric         size_t num_threads = threads.GetSize();
5250b1c73532SDimitry Andric         item.Resize(num_threads, *m_thread_delegate_sp, false);
5251706b4fc4SDimitry Andric         for (size_t i = 0; i < num_threads; ++i) {
5252344a3780SDimitry Andric           ThreadSP thread = threads.GetThreadAtIndex(i);
5253344a3780SDimitry Andric           item[i].SetIdentifier(thread->GetID());
5254706b4fc4SDimitry Andric           item[i].SetMightHaveChildren(true);
5255344a3780SDimitry Andric           if (selected_thread->GetID() == thread->GetID())
5256344a3780SDimitry Andric             item[i].Expand();
5257706b4fc4SDimitry Andric         }
5258706b4fc4SDimitry Andric         return;
5259706b4fc4SDimitry Andric       }
5260706b4fc4SDimitry Andric     }
5261706b4fc4SDimitry Andric     item.ClearChildren();
5262706b4fc4SDimitry Andric   }
5263706b4fc4SDimitry Andric 
TreeDelegateUpdateSelection(TreeItem & root,int & selection_index,TreeItem * & selected_item)5264344a3780SDimitry Andric   void TreeDelegateUpdateSelection(TreeItem &root, int &selection_index,
5265344a3780SDimitry Andric                                    TreeItem *&selected_item) override {
5266344a3780SDimitry Andric     if (!m_update_selection)
5267344a3780SDimitry Andric       return;
5268344a3780SDimitry Andric 
5269344a3780SDimitry Andric     ProcessSP process_sp = GetProcess();
5270344a3780SDimitry Andric     if (!(process_sp && process_sp->IsAlive()))
5271344a3780SDimitry Andric       return;
5272344a3780SDimitry Andric 
5273344a3780SDimitry Andric     StateType state = process_sp->GetState();
5274344a3780SDimitry Andric     if (!StateIsStoppedState(state, true))
5275344a3780SDimitry Andric       return;
5276344a3780SDimitry Andric 
5277344a3780SDimitry Andric     ThreadList &threads = process_sp->GetThreadList();
5278344a3780SDimitry Andric     std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
5279344a3780SDimitry Andric     ThreadSP selected_thread = threads.GetSelectedThread();
5280344a3780SDimitry Andric     size_t num_threads = threads.GetSize();
5281344a3780SDimitry Andric     for (size_t i = 0; i < num_threads; ++i) {
5282344a3780SDimitry Andric       ThreadSP thread = threads.GetThreadAtIndex(i);
5283344a3780SDimitry Andric       if (selected_thread->GetID() == thread->GetID()) {
52847fa27ce4SDimitry Andric         selected_item =
52857fa27ce4SDimitry Andric             &root[i][thread->GetSelectedFrameIndex(SelectMostRelevantFrame)];
5286344a3780SDimitry Andric         selection_index = selected_item->GetRowIndex();
5287344a3780SDimitry Andric         return;
5288344a3780SDimitry Andric       }
5289344a3780SDimitry Andric     }
5290344a3780SDimitry Andric   }
5291344a3780SDimitry Andric 
TreeDelegateItemSelected(TreeItem & item)5292706b4fc4SDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5293706b4fc4SDimitry Andric 
TreeDelegateExpandRootByDefault()5294344a3780SDimitry Andric   bool TreeDelegateExpandRootByDefault() override { return true; }
5295344a3780SDimitry Andric 
5296706b4fc4SDimitry Andric protected:
5297706b4fc4SDimitry Andric   std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
5298706b4fc4SDimitry Andric   Debugger &m_debugger;
5299145449b1SDimitry Andric   uint32_t m_stop_id = UINT32_MAX;
5300145449b1SDimitry Andric   bool m_update_selection = false;
5301706b4fc4SDimitry Andric   FormatEntity::Entry m_format;
5302706b4fc4SDimitry Andric };
5303706b4fc4SDimitry Andric 
5304c0981da4SDimitry Andric class BreakpointLocationTreeDelegate : public TreeDelegate {
5305c0981da4SDimitry Andric public:
BreakpointLocationTreeDelegate(Debugger & debugger)5306c0981da4SDimitry Andric   BreakpointLocationTreeDelegate(Debugger &debugger)
5307c0981da4SDimitry Andric       : TreeDelegate(), m_debugger(debugger) {}
5308c0981da4SDimitry Andric 
5309c0981da4SDimitry Andric   ~BreakpointLocationTreeDelegate() override = default;
5310c0981da4SDimitry Andric 
GetProcess()5311c0981da4SDimitry Andric   Process *GetProcess() {
5312c0981da4SDimitry Andric     ExecutionContext exe_ctx(
5313c0981da4SDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext());
5314c0981da4SDimitry Andric     return exe_ctx.GetProcessPtr();
5315c0981da4SDimitry Andric   }
5316c0981da4SDimitry Andric 
GetBreakpointLocation(const TreeItem & item)5317c0981da4SDimitry Andric   BreakpointLocationSP GetBreakpointLocation(const TreeItem &item) {
5318c0981da4SDimitry Andric     Breakpoint *breakpoint = (Breakpoint *)item.GetUserData();
5319c0981da4SDimitry Andric     return breakpoint->GetLocationAtIndex(item.GetIdentifier());
5320c0981da4SDimitry Andric   }
5321c0981da4SDimitry Andric 
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5322c0981da4SDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5323c0981da4SDimitry Andric     BreakpointLocationSP breakpoint_location = GetBreakpointLocation(item);
5324c0981da4SDimitry Andric     Process *process = GetProcess();
5325c0981da4SDimitry Andric     StreamString stream;
5326c0981da4SDimitry Andric     stream.Printf("%i.%i: ", breakpoint_location->GetBreakpoint().GetID(),
5327c0981da4SDimitry Andric                   breakpoint_location->GetID());
5328c0981da4SDimitry Andric     Address address = breakpoint_location->GetAddress();
5329c0981da4SDimitry Andric     address.Dump(&stream, process, Address::DumpStyleResolvedDescription,
5330c0981da4SDimitry Andric                  Address::DumpStyleInvalid);
5331c0981da4SDimitry Andric     window.PutCStringTruncated(1, stream.GetString().str().c_str());
5332c0981da4SDimitry Andric   }
5333c0981da4SDimitry Andric 
ComputeDetailsList(BreakpointLocationSP breakpoint_location)5334c0981da4SDimitry Andric   StringList ComputeDetailsList(BreakpointLocationSP breakpoint_location) {
5335c0981da4SDimitry Andric     StringList details;
5336c0981da4SDimitry Andric 
5337c0981da4SDimitry Andric     Address address = breakpoint_location->GetAddress();
5338c0981da4SDimitry Andric     SymbolContext symbol_context;
5339c0981da4SDimitry Andric     address.CalculateSymbolContext(&symbol_context);
5340c0981da4SDimitry Andric 
5341c0981da4SDimitry Andric     if (symbol_context.module_sp) {
5342c0981da4SDimitry Andric       StreamString module_stream;
5343c0981da4SDimitry Andric       module_stream.PutCString("module = ");
5344c0981da4SDimitry Andric       symbol_context.module_sp->GetFileSpec().Dump(
5345c0981da4SDimitry Andric           module_stream.AsRawOstream());
5346c0981da4SDimitry Andric       details.AppendString(module_stream.GetString());
5347c0981da4SDimitry Andric     }
5348c0981da4SDimitry Andric 
5349c0981da4SDimitry Andric     if (symbol_context.comp_unit != nullptr) {
5350c0981da4SDimitry Andric       StreamString compile_unit_stream;
5351c0981da4SDimitry Andric       compile_unit_stream.PutCString("compile unit = ");
5352c0981da4SDimitry Andric       symbol_context.comp_unit->GetPrimaryFile().GetFilename().Dump(
5353c0981da4SDimitry Andric           &compile_unit_stream);
5354c0981da4SDimitry Andric       details.AppendString(compile_unit_stream.GetString());
5355c0981da4SDimitry Andric 
5356c0981da4SDimitry Andric       if (symbol_context.function != nullptr) {
5357c0981da4SDimitry Andric         StreamString function_stream;
5358c0981da4SDimitry Andric         function_stream.PutCString("function = ");
5359c0981da4SDimitry Andric         function_stream.PutCString(
5360c0981da4SDimitry Andric             symbol_context.function->GetName().AsCString("<unknown>"));
5361c0981da4SDimitry Andric         details.AppendString(function_stream.GetString());
5362c0981da4SDimitry Andric       }
5363c0981da4SDimitry Andric 
5364c0981da4SDimitry Andric       if (symbol_context.line_entry.line > 0) {
5365c0981da4SDimitry Andric         StreamString location_stream;
5366c0981da4SDimitry Andric         location_stream.PutCString("location = ");
5367c0981da4SDimitry Andric         symbol_context.line_entry.DumpStopContext(&location_stream, true);
5368c0981da4SDimitry Andric         details.AppendString(location_stream.GetString());
5369c0981da4SDimitry Andric       }
5370c0981da4SDimitry Andric 
5371c0981da4SDimitry Andric     } else {
5372c0981da4SDimitry Andric       if (symbol_context.symbol) {
5373c0981da4SDimitry Andric         StreamString symbol_stream;
5374c0981da4SDimitry Andric         if (breakpoint_location->IsReExported())
5375c0981da4SDimitry Andric           symbol_stream.PutCString("re-exported target = ");
5376c0981da4SDimitry Andric         else
5377c0981da4SDimitry Andric           symbol_stream.PutCString("symbol = ");
5378c0981da4SDimitry Andric         symbol_stream.PutCString(
5379c0981da4SDimitry Andric             symbol_context.symbol->GetName().AsCString("<unknown>"));
5380c0981da4SDimitry Andric         details.AppendString(symbol_stream.GetString());
5381c0981da4SDimitry Andric       }
5382c0981da4SDimitry Andric     }
5383c0981da4SDimitry Andric 
5384c0981da4SDimitry Andric     Process *process = GetProcess();
5385c0981da4SDimitry Andric 
5386c0981da4SDimitry Andric     StreamString address_stream;
5387c0981da4SDimitry Andric     address.Dump(&address_stream, process, Address::DumpStyleLoadAddress,
5388c0981da4SDimitry Andric                  Address::DumpStyleModuleWithFileAddress);
5389c0981da4SDimitry Andric     details.AppendString(address_stream.GetString());
5390c0981da4SDimitry Andric 
5391c0981da4SDimitry Andric     BreakpointSiteSP breakpoint_site = breakpoint_location->GetBreakpointSite();
5392c0981da4SDimitry Andric     if (breakpoint_location->IsIndirect() && breakpoint_site) {
5393c0981da4SDimitry Andric       Address resolved_address;
5394c0981da4SDimitry Andric       resolved_address.SetLoadAddress(breakpoint_site->GetLoadAddress(),
5395c0981da4SDimitry Andric                                       &breakpoint_location->GetTarget());
5396c0981da4SDimitry Andric       Symbol *resolved_symbol = resolved_address.CalculateSymbolContextSymbol();
5397c0981da4SDimitry Andric       if (resolved_symbol) {
5398c0981da4SDimitry Andric         StreamString indirect_target_stream;
5399c0981da4SDimitry Andric         indirect_target_stream.PutCString("indirect target = ");
5400c0981da4SDimitry Andric         indirect_target_stream.PutCString(
5401c0981da4SDimitry Andric             resolved_symbol->GetName().GetCString());
5402c0981da4SDimitry Andric         details.AppendString(indirect_target_stream.GetString());
5403c0981da4SDimitry Andric       }
5404c0981da4SDimitry Andric     }
5405c0981da4SDimitry Andric 
5406c0981da4SDimitry Andric     bool is_resolved = breakpoint_location->IsResolved();
5407c0981da4SDimitry Andric     StreamString resolved_stream;
5408c0981da4SDimitry Andric     resolved_stream.Printf("resolved = %s", is_resolved ? "true" : "false");
5409c0981da4SDimitry Andric     details.AppendString(resolved_stream.GetString());
5410c0981da4SDimitry Andric 
5411c0981da4SDimitry Andric     bool is_hardware = is_resolved && breakpoint_site->IsHardware();
5412c0981da4SDimitry Andric     StreamString hardware_stream;
5413c0981da4SDimitry Andric     hardware_stream.Printf("hardware = %s", is_hardware ? "true" : "false");
5414c0981da4SDimitry Andric     details.AppendString(hardware_stream.GetString());
5415c0981da4SDimitry Andric 
5416c0981da4SDimitry Andric     StreamString hit_count_stream;
5417c0981da4SDimitry Andric     hit_count_stream.Printf("hit count = %-4u",
5418c0981da4SDimitry Andric                             breakpoint_location->GetHitCount());
5419c0981da4SDimitry Andric     details.AppendString(hit_count_stream.GetString());
5420c0981da4SDimitry Andric 
5421c0981da4SDimitry Andric     return details;
5422c0981da4SDimitry Andric   }
5423c0981da4SDimitry Andric 
TreeDelegateGenerateChildren(TreeItem & item)5424c0981da4SDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {
5425c0981da4SDimitry Andric     BreakpointLocationSP breakpoint_location = GetBreakpointLocation(item);
5426c0981da4SDimitry Andric     StringList details = ComputeDetailsList(breakpoint_location);
5427c0981da4SDimitry Andric 
5428c0981da4SDimitry Andric     if (!m_string_delegate_sp)
5429c0981da4SDimitry Andric       m_string_delegate_sp = std::make_shared<TextTreeDelegate>();
5430c0981da4SDimitry Andric 
5431b1c73532SDimitry Andric     item.Resize(details.GetSize(), *m_string_delegate_sp, false);
5432c0981da4SDimitry Andric     for (size_t i = 0; i < details.GetSize(); i++) {
5433c0981da4SDimitry Andric       item[i].SetText(details.GetStringAtIndex(i));
5434c0981da4SDimitry Andric     }
5435c0981da4SDimitry Andric   }
5436c0981da4SDimitry Andric 
TreeDelegateItemSelected(TreeItem & item)5437c0981da4SDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5438c0981da4SDimitry Andric 
5439c0981da4SDimitry Andric protected:
5440c0981da4SDimitry Andric   Debugger &m_debugger;
5441c0981da4SDimitry Andric   std::shared_ptr<TextTreeDelegate> m_string_delegate_sp;
5442c0981da4SDimitry Andric };
5443c0981da4SDimitry Andric 
5444c0981da4SDimitry Andric class BreakpointTreeDelegate : public TreeDelegate {
5445c0981da4SDimitry Andric public:
BreakpointTreeDelegate(Debugger & debugger)5446c0981da4SDimitry Andric   BreakpointTreeDelegate(Debugger &debugger)
5447c0981da4SDimitry Andric       : TreeDelegate(), m_debugger(debugger),
5448c0981da4SDimitry Andric         m_breakpoint_location_delegate_sp() {}
5449c0981da4SDimitry Andric 
5450c0981da4SDimitry Andric   ~BreakpointTreeDelegate() override = default;
5451c0981da4SDimitry Andric 
GetBreakpoint(const TreeItem & item)5452c0981da4SDimitry Andric   BreakpointSP GetBreakpoint(const TreeItem &item) {
5453c0981da4SDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
5454c0981da4SDimitry Andric     BreakpointList &breakpoints = target->GetBreakpointList(false);
5455c0981da4SDimitry Andric     return breakpoints.GetBreakpointAtIndex(item.GetIdentifier());
5456c0981da4SDimitry Andric   }
5457c0981da4SDimitry Andric 
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5458c0981da4SDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5459c0981da4SDimitry Andric     BreakpointSP breakpoint = GetBreakpoint(item);
5460c0981da4SDimitry Andric     StreamString stream;
5461c0981da4SDimitry Andric     stream.Format("{0}: ", breakpoint->GetID());
5462c0981da4SDimitry Andric     breakpoint->GetResolverDescription(&stream);
5463c0981da4SDimitry Andric     breakpoint->GetFilterDescription(&stream);
5464c0981da4SDimitry Andric     window.PutCStringTruncated(1, stream.GetString().str().c_str());
5465c0981da4SDimitry Andric   }
5466c0981da4SDimitry Andric 
TreeDelegateGenerateChildren(TreeItem & item)5467c0981da4SDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {
5468c0981da4SDimitry Andric     BreakpointSP breakpoint = GetBreakpoint(item);
5469c0981da4SDimitry Andric 
5470c0981da4SDimitry Andric     if (!m_breakpoint_location_delegate_sp)
5471c0981da4SDimitry Andric       m_breakpoint_location_delegate_sp =
5472c0981da4SDimitry Andric           std::make_shared<BreakpointLocationTreeDelegate>(m_debugger);
5473c0981da4SDimitry Andric 
5474b1c73532SDimitry Andric     item.Resize(breakpoint->GetNumLocations(),
5475b1c73532SDimitry Andric                 *m_breakpoint_location_delegate_sp, true);
5476c0981da4SDimitry Andric     for (size_t i = 0; i < breakpoint->GetNumLocations(); i++) {
5477c0981da4SDimitry Andric       item[i].SetIdentifier(i);
5478c0981da4SDimitry Andric       item[i].SetUserData(breakpoint.get());
5479c0981da4SDimitry Andric     }
5480c0981da4SDimitry Andric   }
5481c0981da4SDimitry Andric 
TreeDelegateItemSelected(TreeItem & item)5482c0981da4SDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5483c0981da4SDimitry Andric 
5484c0981da4SDimitry Andric protected:
5485c0981da4SDimitry Andric   Debugger &m_debugger;
5486c0981da4SDimitry Andric   std::shared_ptr<BreakpointLocationTreeDelegate>
5487c0981da4SDimitry Andric       m_breakpoint_location_delegate_sp;
5488c0981da4SDimitry Andric };
5489c0981da4SDimitry Andric 
5490c0981da4SDimitry Andric class BreakpointsTreeDelegate : public TreeDelegate {
5491c0981da4SDimitry Andric public:
BreakpointsTreeDelegate(Debugger & debugger)5492c0981da4SDimitry Andric   BreakpointsTreeDelegate(Debugger &debugger)
5493c0981da4SDimitry Andric       : TreeDelegate(), m_debugger(debugger), m_breakpoint_delegate_sp() {}
5494c0981da4SDimitry Andric 
5495c0981da4SDimitry Andric   ~BreakpointsTreeDelegate() override = default;
5496c0981da4SDimitry Andric 
TreeDelegateShouldDraw()5497c0981da4SDimitry Andric   bool TreeDelegateShouldDraw() override {
5498c0981da4SDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
5499c0981da4SDimitry Andric     if (!target)
5500c0981da4SDimitry Andric       return false;
5501c0981da4SDimitry Andric 
5502c0981da4SDimitry Andric     return true;
5503c0981da4SDimitry Andric   }
5504c0981da4SDimitry Andric 
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5505c0981da4SDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5506c0981da4SDimitry Andric     window.PutCString("Breakpoints");
5507c0981da4SDimitry Andric   }
5508c0981da4SDimitry Andric 
TreeDelegateGenerateChildren(TreeItem & item)5509c0981da4SDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {
5510c0981da4SDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
5511c0981da4SDimitry Andric 
5512c0981da4SDimitry Andric     BreakpointList &breakpoints = target->GetBreakpointList(false);
5513c0981da4SDimitry Andric     std::unique_lock<std::recursive_mutex> lock;
5514c0981da4SDimitry Andric     breakpoints.GetListMutex(lock);
5515c0981da4SDimitry Andric 
5516c0981da4SDimitry Andric     if (!m_breakpoint_delegate_sp)
5517c0981da4SDimitry Andric       m_breakpoint_delegate_sp =
5518c0981da4SDimitry Andric           std::make_shared<BreakpointTreeDelegate>(m_debugger);
5519c0981da4SDimitry Andric 
5520b1c73532SDimitry Andric     item.Resize(breakpoints.GetSize(), *m_breakpoint_delegate_sp, true);
5521c0981da4SDimitry Andric     for (size_t i = 0; i < breakpoints.GetSize(); i++) {
5522c0981da4SDimitry Andric       item[i].SetIdentifier(i);
5523c0981da4SDimitry Andric     }
5524c0981da4SDimitry Andric   }
5525c0981da4SDimitry Andric 
TreeDelegateItemSelected(TreeItem & item)5526c0981da4SDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5527c0981da4SDimitry Andric 
TreeDelegateExpandRootByDefault()5528c0981da4SDimitry Andric   bool TreeDelegateExpandRootByDefault() override { return true; }
5529c0981da4SDimitry Andric 
5530c0981da4SDimitry Andric protected:
5531c0981da4SDimitry Andric   Debugger &m_debugger;
5532c0981da4SDimitry Andric   std::shared_ptr<BreakpointTreeDelegate> m_breakpoint_delegate_sp;
5533c0981da4SDimitry Andric };
5534c0981da4SDimitry Andric 
5535706b4fc4SDimitry Andric class ValueObjectListDelegate : public WindowDelegate {
5536706b4fc4SDimitry Andric public:
ValueObjectListDelegate()5537344a3780SDimitry Andric   ValueObjectListDelegate() : m_rows() {}
5538706b4fc4SDimitry Andric 
ValueObjectListDelegate(ValueObjectList & valobj_list)5539145449b1SDimitry Andric   ValueObjectListDelegate(ValueObjectList &valobj_list) : m_rows() {
5540706b4fc4SDimitry Andric     SetValues(valobj_list);
5541706b4fc4SDimitry Andric   }
5542706b4fc4SDimitry Andric 
5543706b4fc4SDimitry Andric   ~ValueObjectListDelegate() override = default;
5544706b4fc4SDimitry Andric 
SetValues(ValueObjectList & valobj_list)5545706b4fc4SDimitry Andric   void SetValues(ValueObjectList &valobj_list) {
5546706b4fc4SDimitry Andric     m_selected_row = nullptr;
5547706b4fc4SDimitry Andric     m_selected_row_idx = 0;
5548706b4fc4SDimitry Andric     m_first_visible_row = 0;
5549706b4fc4SDimitry Andric     m_num_rows = 0;
5550706b4fc4SDimitry Andric     m_rows.clear();
5551706b4fc4SDimitry Andric     for (auto &valobj_sp : valobj_list.GetObjects())
5552706b4fc4SDimitry Andric       m_rows.push_back(Row(valobj_sp, nullptr));
5553706b4fc4SDimitry Andric   }
5554706b4fc4SDimitry Andric 
WindowDelegateDraw(Window & window,bool force)5555706b4fc4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
5556706b4fc4SDimitry Andric     m_num_rows = 0;
5557706b4fc4SDimitry Andric     m_min_x = 2;
5558706b4fc4SDimitry Andric     m_min_y = 1;
5559706b4fc4SDimitry Andric     m_max_x = window.GetWidth() - 1;
5560706b4fc4SDimitry Andric     m_max_y = window.GetHeight() - 1;
5561706b4fc4SDimitry Andric 
5562706b4fc4SDimitry Andric     window.Erase();
5563706b4fc4SDimitry Andric     window.DrawTitleBox(window.GetName());
5564706b4fc4SDimitry Andric 
5565706b4fc4SDimitry Andric     const int num_visible_rows = NumVisibleRows();
5566706b4fc4SDimitry Andric     const int num_rows = CalculateTotalNumberRows(m_rows);
5567706b4fc4SDimitry Andric 
5568706b4fc4SDimitry Andric     // If we unexpanded while having something selected our total number of
5569706b4fc4SDimitry Andric     // rows is less than the num visible rows, then make sure we show all the
5570706b4fc4SDimitry Andric     // rows by setting the first visible row accordingly.
5571706b4fc4SDimitry Andric     if (m_first_visible_row > 0 && num_rows < num_visible_rows)
5572706b4fc4SDimitry Andric       m_first_visible_row = 0;
5573706b4fc4SDimitry Andric 
5574706b4fc4SDimitry Andric     // Make sure the selected row is always visible
5575706b4fc4SDimitry Andric     if (m_selected_row_idx < m_first_visible_row)
5576706b4fc4SDimitry Andric       m_first_visible_row = m_selected_row_idx;
5577706b4fc4SDimitry Andric     else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
5578706b4fc4SDimitry Andric       m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
5579706b4fc4SDimitry Andric 
5580706b4fc4SDimitry Andric     DisplayRows(window, m_rows, g_options);
5581706b4fc4SDimitry Andric 
5582706b4fc4SDimitry Andric     // Get the selected row
5583706b4fc4SDimitry Andric     m_selected_row = GetRowForRowIndex(m_selected_row_idx);
5584706b4fc4SDimitry Andric     // Keep the cursor on the selected row so the highlight and the cursor are
5585706b4fc4SDimitry Andric     // always on the same line
5586706b4fc4SDimitry Andric     if (m_selected_row)
5587706b4fc4SDimitry Andric       window.MoveCursor(m_selected_row->x, m_selected_row->y);
5588706b4fc4SDimitry Andric 
5589706b4fc4SDimitry Andric     return true; // Drawing handled
5590706b4fc4SDimitry Andric   }
5591706b4fc4SDimitry Andric 
WindowDelegateGetKeyHelp()5592706b4fc4SDimitry Andric   KeyHelp *WindowDelegateGetKeyHelp() override {
5593706b4fc4SDimitry Andric     static curses::KeyHelp g_source_view_key_help[] = {
5594706b4fc4SDimitry Andric         {KEY_UP, "Select previous item"},
5595706b4fc4SDimitry Andric         {KEY_DOWN, "Select next item"},
5596706b4fc4SDimitry Andric         {KEY_RIGHT, "Expand selected item"},
5597706b4fc4SDimitry Andric         {KEY_LEFT, "Unexpand selected item or select parent if not expanded"},
5598706b4fc4SDimitry Andric         {KEY_PPAGE, "Page up"},
5599706b4fc4SDimitry Andric         {KEY_NPAGE, "Page down"},
5600706b4fc4SDimitry Andric         {'A', "Format as annotated address"},
5601706b4fc4SDimitry Andric         {'b', "Format as binary"},
5602706b4fc4SDimitry Andric         {'B', "Format as hex bytes with ASCII"},
5603706b4fc4SDimitry Andric         {'c', "Format as character"},
5604706b4fc4SDimitry Andric         {'d', "Format as a signed integer"},
5605706b4fc4SDimitry Andric         {'D', "Format selected value using the default format for the type"},
5606706b4fc4SDimitry Andric         {'f', "Format as float"},
5607706b4fc4SDimitry Andric         {'h', "Show help dialog"},
5608706b4fc4SDimitry Andric         {'i', "Format as instructions"},
5609706b4fc4SDimitry Andric         {'o', "Format as octal"},
5610706b4fc4SDimitry Andric         {'p', "Format as pointer"},
5611706b4fc4SDimitry Andric         {'s', "Format as C string"},
5612706b4fc4SDimitry Andric         {'t', "Toggle showing/hiding type names"},
5613706b4fc4SDimitry Andric         {'u', "Format as an unsigned integer"},
5614706b4fc4SDimitry Andric         {'x', "Format as hex"},
5615706b4fc4SDimitry Andric         {'X', "Format as uppercase hex"},
5616706b4fc4SDimitry Andric         {' ', "Toggle item expansion"},
5617706b4fc4SDimitry Andric         {',', "Page up"},
5618706b4fc4SDimitry Andric         {'.', "Page down"},
5619706b4fc4SDimitry Andric         {'\0', nullptr}};
5620706b4fc4SDimitry Andric     return g_source_view_key_help;
5621706b4fc4SDimitry Andric   }
5622706b4fc4SDimitry Andric 
WindowDelegateHandleChar(Window & window,int c)5623706b4fc4SDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
5624706b4fc4SDimitry Andric     switch (c) {
5625706b4fc4SDimitry Andric     case 'x':
5626706b4fc4SDimitry Andric     case 'X':
5627706b4fc4SDimitry Andric     case 'o':
5628706b4fc4SDimitry Andric     case 's':
5629706b4fc4SDimitry Andric     case 'u':
5630706b4fc4SDimitry Andric     case 'd':
5631706b4fc4SDimitry Andric     case 'D':
5632706b4fc4SDimitry Andric     case 'i':
5633706b4fc4SDimitry Andric     case 'A':
5634706b4fc4SDimitry Andric     case 'p':
5635706b4fc4SDimitry Andric     case 'c':
5636706b4fc4SDimitry Andric     case 'b':
5637706b4fc4SDimitry Andric     case 'B':
5638706b4fc4SDimitry Andric     case 'f':
5639706b4fc4SDimitry Andric       // Change the format for the currently selected item
5640706b4fc4SDimitry Andric       if (m_selected_row) {
5641706b4fc4SDimitry Andric         auto valobj_sp = m_selected_row->value.GetSP();
5642706b4fc4SDimitry Andric         if (valobj_sp)
5643706b4fc4SDimitry Andric           valobj_sp->SetFormat(FormatForChar(c));
5644706b4fc4SDimitry Andric       }
5645706b4fc4SDimitry Andric       return eKeyHandled;
5646706b4fc4SDimitry Andric 
5647706b4fc4SDimitry Andric     case 't':
5648706b4fc4SDimitry Andric       // Toggle showing type names
5649706b4fc4SDimitry Andric       g_options.show_types = !g_options.show_types;
5650706b4fc4SDimitry Andric       return eKeyHandled;
5651706b4fc4SDimitry Andric 
5652706b4fc4SDimitry Andric     case ',':
5653706b4fc4SDimitry Andric     case KEY_PPAGE:
5654706b4fc4SDimitry Andric       // Page up key
5655706b4fc4SDimitry Andric       if (m_first_visible_row > 0) {
5656706b4fc4SDimitry Andric         if (static_cast<int>(m_first_visible_row) > m_max_y)
5657706b4fc4SDimitry Andric           m_first_visible_row -= m_max_y;
5658706b4fc4SDimitry Andric         else
5659706b4fc4SDimitry Andric           m_first_visible_row = 0;
5660706b4fc4SDimitry Andric         m_selected_row_idx = m_first_visible_row;
5661706b4fc4SDimitry Andric       }
5662706b4fc4SDimitry Andric       return eKeyHandled;
5663706b4fc4SDimitry Andric 
5664706b4fc4SDimitry Andric     case '.':
5665706b4fc4SDimitry Andric     case KEY_NPAGE:
5666706b4fc4SDimitry Andric       // Page down key
5667706b4fc4SDimitry Andric       if (m_num_rows > static_cast<size_t>(m_max_y)) {
5668706b4fc4SDimitry Andric         if (m_first_visible_row + m_max_y < m_num_rows) {
5669706b4fc4SDimitry Andric           m_first_visible_row += m_max_y;
5670706b4fc4SDimitry Andric           m_selected_row_idx = m_first_visible_row;
5671706b4fc4SDimitry Andric         }
5672706b4fc4SDimitry Andric       }
5673706b4fc4SDimitry Andric       return eKeyHandled;
5674706b4fc4SDimitry Andric 
5675706b4fc4SDimitry Andric     case KEY_UP:
5676706b4fc4SDimitry Andric       if (m_selected_row_idx > 0)
5677706b4fc4SDimitry Andric         --m_selected_row_idx;
5678706b4fc4SDimitry Andric       return eKeyHandled;
5679706b4fc4SDimitry Andric 
5680706b4fc4SDimitry Andric     case KEY_DOWN:
5681706b4fc4SDimitry Andric       if (m_selected_row_idx + 1 < m_num_rows)
5682706b4fc4SDimitry Andric         ++m_selected_row_idx;
5683706b4fc4SDimitry Andric       return eKeyHandled;
5684706b4fc4SDimitry Andric 
5685706b4fc4SDimitry Andric     case KEY_RIGHT:
5686706b4fc4SDimitry Andric       if (m_selected_row) {
5687706b4fc4SDimitry Andric         if (!m_selected_row->expanded)
5688706b4fc4SDimitry Andric           m_selected_row->Expand();
5689706b4fc4SDimitry Andric       }
5690706b4fc4SDimitry Andric       return eKeyHandled;
5691706b4fc4SDimitry Andric 
5692706b4fc4SDimitry Andric     case KEY_LEFT:
5693706b4fc4SDimitry Andric       if (m_selected_row) {
5694706b4fc4SDimitry Andric         if (m_selected_row->expanded)
5695706b4fc4SDimitry Andric           m_selected_row->Unexpand();
5696706b4fc4SDimitry Andric         else if (m_selected_row->parent)
5697706b4fc4SDimitry Andric           m_selected_row_idx = m_selected_row->parent->row_idx;
5698706b4fc4SDimitry Andric       }
5699706b4fc4SDimitry Andric       return eKeyHandled;
5700706b4fc4SDimitry Andric 
5701706b4fc4SDimitry Andric     case ' ':
5702706b4fc4SDimitry Andric       // Toggle expansion state when SPACE is pressed
5703706b4fc4SDimitry Andric       if (m_selected_row) {
5704706b4fc4SDimitry Andric         if (m_selected_row->expanded)
5705706b4fc4SDimitry Andric           m_selected_row->Unexpand();
5706706b4fc4SDimitry Andric         else
5707706b4fc4SDimitry Andric           m_selected_row->Expand();
5708706b4fc4SDimitry Andric       }
5709706b4fc4SDimitry Andric       return eKeyHandled;
5710706b4fc4SDimitry Andric 
5711706b4fc4SDimitry Andric     case 'h':
5712706b4fc4SDimitry Andric       window.CreateHelpSubwindow();
5713706b4fc4SDimitry Andric       return eKeyHandled;
5714706b4fc4SDimitry Andric 
5715706b4fc4SDimitry Andric     default:
5716706b4fc4SDimitry Andric       break;
5717706b4fc4SDimitry Andric     }
5718706b4fc4SDimitry Andric     return eKeyNotHandled;
5719706b4fc4SDimitry Andric   }
5720706b4fc4SDimitry Andric 
5721706b4fc4SDimitry Andric protected:
5722706b4fc4SDimitry Andric   std::vector<Row> m_rows;
5723344a3780SDimitry Andric   Row *m_selected_row = nullptr;
5724344a3780SDimitry Andric   uint32_t m_selected_row_idx = 0;
5725344a3780SDimitry Andric   uint32_t m_first_visible_row = 0;
5726344a3780SDimitry Andric   uint32_t m_num_rows = 0;
5727e3b55780SDimitry Andric   int m_min_x = 0;
5728e3b55780SDimitry Andric   int m_min_y = 0;
5729344a3780SDimitry Andric   int m_max_x = 0;
5730344a3780SDimitry Andric   int m_max_y = 0;
5731706b4fc4SDimitry Andric 
FormatForChar(int c)5732706b4fc4SDimitry Andric   static Format FormatForChar(int c) {
5733706b4fc4SDimitry Andric     switch (c) {
5734706b4fc4SDimitry Andric     case 'x':
5735706b4fc4SDimitry Andric       return eFormatHex;
5736706b4fc4SDimitry Andric     case 'X':
5737706b4fc4SDimitry Andric       return eFormatHexUppercase;
5738706b4fc4SDimitry Andric     case 'o':
5739706b4fc4SDimitry Andric       return eFormatOctal;
5740706b4fc4SDimitry Andric     case 's':
5741706b4fc4SDimitry Andric       return eFormatCString;
5742706b4fc4SDimitry Andric     case 'u':
5743706b4fc4SDimitry Andric       return eFormatUnsigned;
5744706b4fc4SDimitry Andric     case 'd':
5745706b4fc4SDimitry Andric       return eFormatDecimal;
5746706b4fc4SDimitry Andric     case 'D':
5747706b4fc4SDimitry Andric       return eFormatDefault;
5748706b4fc4SDimitry Andric     case 'i':
5749706b4fc4SDimitry Andric       return eFormatInstruction;
5750706b4fc4SDimitry Andric     case 'A':
5751706b4fc4SDimitry Andric       return eFormatAddressInfo;
5752706b4fc4SDimitry Andric     case 'p':
5753706b4fc4SDimitry Andric       return eFormatPointer;
5754706b4fc4SDimitry Andric     case 'c':
5755706b4fc4SDimitry Andric       return eFormatChar;
5756706b4fc4SDimitry Andric     case 'b':
5757706b4fc4SDimitry Andric       return eFormatBinary;
5758706b4fc4SDimitry Andric     case 'B':
5759706b4fc4SDimitry Andric       return eFormatBytesWithASCII;
5760706b4fc4SDimitry Andric     case 'f':
5761706b4fc4SDimitry Andric       return eFormatFloat;
5762706b4fc4SDimitry Andric     }
5763706b4fc4SDimitry Andric     return eFormatDefault;
5764706b4fc4SDimitry Andric   }
5765706b4fc4SDimitry Andric 
DisplayRowObject(Window & window,Row & row,DisplayOptions & options,bool highlight,bool last_child)5766706b4fc4SDimitry Andric   bool DisplayRowObject(Window &window, Row &row, DisplayOptions &options,
5767706b4fc4SDimitry Andric                         bool highlight, bool last_child) {
5768706b4fc4SDimitry Andric     ValueObject *valobj = row.value.GetSP().get();
5769706b4fc4SDimitry Andric 
5770706b4fc4SDimitry Andric     if (valobj == nullptr)
5771706b4fc4SDimitry Andric       return false;
5772706b4fc4SDimitry Andric 
5773706b4fc4SDimitry Andric     const char *type_name =
5774706b4fc4SDimitry Andric         options.show_types ? valobj->GetTypeName().GetCString() : nullptr;
5775706b4fc4SDimitry Andric     const char *name = valobj->GetName().GetCString();
5776706b4fc4SDimitry Andric     const char *value = valobj->GetValueAsCString();
5777706b4fc4SDimitry Andric     const char *summary = valobj->GetSummaryAsCString();
5778706b4fc4SDimitry Andric 
5779706b4fc4SDimitry Andric     window.MoveCursor(row.x, row.y);
5780706b4fc4SDimitry Andric 
5781706b4fc4SDimitry Andric     row.DrawTree(window);
5782706b4fc4SDimitry Andric 
5783706b4fc4SDimitry Andric     if (highlight)
5784706b4fc4SDimitry Andric       window.AttributeOn(A_REVERSE);
5785706b4fc4SDimitry Andric 
5786706b4fc4SDimitry Andric     if (type_name && type_name[0])
5787b60736ecSDimitry Andric       window.PrintfTruncated(1, "(%s) ", type_name);
5788706b4fc4SDimitry Andric 
5789706b4fc4SDimitry Andric     if (name && name[0])
5790b60736ecSDimitry Andric       window.PutCStringTruncated(1, name);
5791706b4fc4SDimitry Andric 
5792706b4fc4SDimitry Andric     attr_t changd_attr = 0;
5793706b4fc4SDimitry Andric     if (valobj->GetValueDidChange())
5794b60736ecSDimitry Andric       changd_attr = COLOR_PAIR(RedOnBlack) | A_BOLD;
5795706b4fc4SDimitry Andric 
5796706b4fc4SDimitry Andric     if (value && value[0]) {
5797b60736ecSDimitry Andric       window.PutCStringTruncated(1, " = ");
5798706b4fc4SDimitry Andric       if (changd_attr)
5799706b4fc4SDimitry Andric         window.AttributeOn(changd_attr);
5800b60736ecSDimitry Andric       window.PutCStringTruncated(1, value);
5801706b4fc4SDimitry Andric       if (changd_attr)
5802706b4fc4SDimitry Andric         window.AttributeOff(changd_attr);
5803706b4fc4SDimitry Andric     }
5804706b4fc4SDimitry Andric 
5805706b4fc4SDimitry Andric     if (summary && summary[0]) {
5806b60736ecSDimitry Andric       window.PutCStringTruncated(1, " ");
5807706b4fc4SDimitry Andric       if (changd_attr)
5808706b4fc4SDimitry Andric         window.AttributeOn(changd_attr);
5809b60736ecSDimitry Andric       window.PutCStringTruncated(1, summary);
5810706b4fc4SDimitry Andric       if (changd_attr)
5811706b4fc4SDimitry Andric         window.AttributeOff(changd_attr);
5812706b4fc4SDimitry Andric     }
5813706b4fc4SDimitry Andric 
5814706b4fc4SDimitry Andric     if (highlight)
5815706b4fc4SDimitry Andric       window.AttributeOff(A_REVERSE);
5816706b4fc4SDimitry Andric 
5817706b4fc4SDimitry Andric     return true;
5818706b4fc4SDimitry Andric   }
5819706b4fc4SDimitry Andric 
DisplayRows(Window & window,std::vector<Row> & rows,DisplayOptions & options)5820706b4fc4SDimitry Andric   void DisplayRows(Window &window, std::vector<Row> &rows,
5821706b4fc4SDimitry Andric                    DisplayOptions &options) {
5822706b4fc4SDimitry Andric     // >   0x25B7
5823706b4fc4SDimitry Andric     // \/  0x25BD
5824706b4fc4SDimitry Andric 
5825706b4fc4SDimitry Andric     bool window_is_active = window.IsActive();
5826706b4fc4SDimitry Andric     for (auto &row : rows) {
5827706b4fc4SDimitry Andric       const bool last_child = row.parent && &rows[rows.size() - 1] == &row;
5828706b4fc4SDimitry Andric       // Save the row index in each Row structure
5829706b4fc4SDimitry Andric       row.row_idx = m_num_rows;
5830706b4fc4SDimitry Andric       if ((m_num_rows >= m_first_visible_row) &&
5831706b4fc4SDimitry Andric           ((m_num_rows - m_first_visible_row) <
5832706b4fc4SDimitry Andric            static_cast<size_t>(NumVisibleRows()))) {
5833706b4fc4SDimitry Andric         row.x = m_min_x;
5834706b4fc4SDimitry Andric         row.y = m_num_rows - m_first_visible_row + 1;
5835706b4fc4SDimitry Andric         if (DisplayRowObject(window, row, options,
5836706b4fc4SDimitry Andric                              window_is_active &&
5837706b4fc4SDimitry Andric                                  m_num_rows == m_selected_row_idx,
5838706b4fc4SDimitry Andric                              last_child)) {
5839706b4fc4SDimitry Andric           ++m_num_rows;
5840706b4fc4SDimitry Andric         } else {
5841706b4fc4SDimitry Andric           row.x = 0;
5842706b4fc4SDimitry Andric           row.y = 0;
5843706b4fc4SDimitry Andric         }
5844706b4fc4SDimitry Andric       } else {
5845706b4fc4SDimitry Andric         row.x = 0;
5846706b4fc4SDimitry Andric         row.y = 0;
5847706b4fc4SDimitry Andric         ++m_num_rows;
5848706b4fc4SDimitry Andric       }
5849706b4fc4SDimitry Andric 
5850145449b1SDimitry Andric       if (row.expanded) {
5851706b4fc4SDimitry Andric         auto &children = row.GetChildren();
5852145449b1SDimitry Andric         if (!children.empty()) {
5853706b4fc4SDimitry Andric           DisplayRows(window, children, options);
5854706b4fc4SDimitry Andric         }
5855706b4fc4SDimitry Andric       }
5856706b4fc4SDimitry Andric     }
5857145449b1SDimitry Andric   }
5858706b4fc4SDimitry Andric 
CalculateTotalNumberRows(std::vector<Row> & rows)5859706b4fc4SDimitry Andric   int CalculateTotalNumberRows(std::vector<Row> &rows) {
5860706b4fc4SDimitry Andric     int row_count = 0;
5861706b4fc4SDimitry Andric     for (auto &row : rows) {
5862706b4fc4SDimitry Andric       ++row_count;
5863706b4fc4SDimitry Andric       if (row.expanded)
5864706b4fc4SDimitry Andric         row_count += CalculateTotalNumberRows(row.GetChildren());
5865706b4fc4SDimitry Andric     }
5866706b4fc4SDimitry Andric     return row_count;
5867706b4fc4SDimitry Andric   }
5868706b4fc4SDimitry Andric 
GetRowForRowIndexImpl(std::vector<Row> & rows,size_t & row_index)5869706b4fc4SDimitry Andric   static Row *GetRowForRowIndexImpl(std::vector<Row> &rows, size_t &row_index) {
5870706b4fc4SDimitry Andric     for (auto &row : rows) {
5871706b4fc4SDimitry Andric       if (row_index == 0)
5872706b4fc4SDimitry Andric         return &row;
5873706b4fc4SDimitry Andric       else {
5874706b4fc4SDimitry Andric         --row_index;
5875145449b1SDimitry Andric         if (row.expanded) {
5876706b4fc4SDimitry Andric           auto &children = row.GetChildren();
5877145449b1SDimitry Andric           if (!children.empty()) {
5878706b4fc4SDimitry Andric             Row *result = GetRowForRowIndexImpl(children, row_index);
5879706b4fc4SDimitry Andric             if (result)
5880706b4fc4SDimitry Andric               return result;
5881706b4fc4SDimitry Andric           }
5882706b4fc4SDimitry Andric         }
5883706b4fc4SDimitry Andric       }
5884145449b1SDimitry Andric     }
5885706b4fc4SDimitry Andric     return nullptr;
5886706b4fc4SDimitry Andric   }
5887706b4fc4SDimitry Andric 
GetRowForRowIndex(size_t row_index)5888706b4fc4SDimitry Andric   Row *GetRowForRowIndex(size_t row_index) {
5889706b4fc4SDimitry Andric     return GetRowForRowIndexImpl(m_rows, row_index);
5890706b4fc4SDimitry Andric   }
5891706b4fc4SDimitry Andric 
NumVisibleRows() const5892706b4fc4SDimitry Andric   int NumVisibleRows() const { return m_max_y - m_min_y; }
5893706b4fc4SDimitry Andric 
5894706b4fc4SDimitry Andric   static DisplayOptions g_options;
5895706b4fc4SDimitry Andric };
5896706b4fc4SDimitry Andric 
5897706b4fc4SDimitry Andric class FrameVariablesWindowDelegate : public ValueObjectListDelegate {
5898706b4fc4SDimitry Andric public:
FrameVariablesWindowDelegate(Debugger & debugger)5899706b4fc4SDimitry Andric   FrameVariablesWindowDelegate(Debugger &debugger)
5900145449b1SDimitry Andric       : ValueObjectListDelegate(), m_debugger(debugger) {}
5901706b4fc4SDimitry Andric 
5902706b4fc4SDimitry Andric   ~FrameVariablesWindowDelegate() override = default;
5903706b4fc4SDimitry Andric 
WindowDelegateGetHelpText()5904706b4fc4SDimitry Andric   const char *WindowDelegateGetHelpText() override {
5905706b4fc4SDimitry Andric     return "Frame variable window keyboard shortcuts:";
5906706b4fc4SDimitry Andric   }
5907706b4fc4SDimitry Andric 
WindowDelegateDraw(Window & window,bool force)5908706b4fc4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
5909706b4fc4SDimitry Andric     ExecutionContext exe_ctx(
5910706b4fc4SDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext());
5911706b4fc4SDimitry Andric     Process *process = exe_ctx.GetProcessPtr();
5912706b4fc4SDimitry Andric     Block *frame_block = nullptr;
5913706b4fc4SDimitry Andric     StackFrame *frame = nullptr;
5914706b4fc4SDimitry Andric 
5915706b4fc4SDimitry Andric     if (process) {
5916706b4fc4SDimitry Andric       StateType state = process->GetState();
5917706b4fc4SDimitry Andric       if (StateIsStoppedState(state, true)) {
5918706b4fc4SDimitry Andric         frame = exe_ctx.GetFramePtr();
5919706b4fc4SDimitry Andric         if (frame)
5920706b4fc4SDimitry Andric           frame_block = frame->GetFrameBlock();
5921706b4fc4SDimitry Andric       } else if (StateIsRunningState(state)) {
5922706b4fc4SDimitry Andric         return true; // Don't do any updating when we are running
5923706b4fc4SDimitry Andric       }
5924706b4fc4SDimitry Andric     }
5925706b4fc4SDimitry Andric 
5926706b4fc4SDimitry Andric     ValueObjectList local_values;
5927706b4fc4SDimitry Andric     if (frame_block) {
5928706b4fc4SDimitry Andric       // Only update the variables if they have changed
5929706b4fc4SDimitry Andric       if (m_frame_block != frame_block) {
5930706b4fc4SDimitry Andric         m_frame_block = frame_block;
5931706b4fc4SDimitry Andric 
5932e3b55780SDimitry Andric         VariableList *locals = frame->GetVariableList(true, nullptr);
5933706b4fc4SDimitry Andric         if (locals) {
5934706b4fc4SDimitry Andric           const DynamicValueType use_dynamic = eDynamicDontRunTarget;
5935706b4fc4SDimitry Andric           for (const VariableSP &local_sp : *locals) {
5936706b4fc4SDimitry Andric             ValueObjectSP value_sp =
5937706b4fc4SDimitry Andric                 frame->GetValueObjectForFrameVariable(local_sp, use_dynamic);
5938706b4fc4SDimitry Andric             if (value_sp) {
5939706b4fc4SDimitry Andric               ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue();
5940706b4fc4SDimitry Andric               if (synthetic_value_sp)
5941706b4fc4SDimitry Andric                 local_values.Append(synthetic_value_sp);
5942706b4fc4SDimitry Andric               else
5943706b4fc4SDimitry Andric                 local_values.Append(value_sp);
5944706b4fc4SDimitry Andric             }
5945706b4fc4SDimitry Andric           }
5946706b4fc4SDimitry Andric           // Update the values
5947706b4fc4SDimitry Andric           SetValues(local_values);
5948706b4fc4SDimitry Andric         }
5949706b4fc4SDimitry Andric       }
5950706b4fc4SDimitry Andric     } else {
5951706b4fc4SDimitry Andric       m_frame_block = nullptr;
5952706b4fc4SDimitry Andric       // Update the values with an empty list if there is no frame
5953706b4fc4SDimitry Andric       SetValues(local_values);
5954706b4fc4SDimitry Andric     }
5955706b4fc4SDimitry Andric 
5956706b4fc4SDimitry Andric     return ValueObjectListDelegate::WindowDelegateDraw(window, force);
5957706b4fc4SDimitry Andric   }
5958706b4fc4SDimitry Andric 
5959706b4fc4SDimitry Andric protected:
5960706b4fc4SDimitry Andric   Debugger &m_debugger;
5961145449b1SDimitry Andric   Block *m_frame_block = nullptr;
5962706b4fc4SDimitry Andric };
5963706b4fc4SDimitry Andric 
5964706b4fc4SDimitry Andric class RegistersWindowDelegate : public ValueObjectListDelegate {
5965706b4fc4SDimitry Andric public:
RegistersWindowDelegate(Debugger & debugger)5966706b4fc4SDimitry Andric   RegistersWindowDelegate(Debugger &debugger)
5967706b4fc4SDimitry Andric       : ValueObjectListDelegate(), m_debugger(debugger) {}
5968706b4fc4SDimitry Andric 
5969706b4fc4SDimitry Andric   ~RegistersWindowDelegate() override = default;
5970706b4fc4SDimitry Andric 
WindowDelegateGetHelpText()5971706b4fc4SDimitry Andric   const char *WindowDelegateGetHelpText() override {
5972706b4fc4SDimitry Andric     return "Register window keyboard shortcuts:";
5973706b4fc4SDimitry Andric   }
5974706b4fc4SDimitry Andric 
WindowDelegateDraw(Window & window,bool force)5975706b4fc4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
5976706b4fc4SDimitry Andric     ExecutionContext exe_ctx(
5977706b4fc4SDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext());
5978706b4fc4SDimitry Andric     StackFrame *frame = exe_ctx.GetFramePtr();
5979706b4fc4SDimitry Andric 
5980706b4fc4SDimitry Andric     ValueObjectList value_list;
5981706b4fc4SDimitry Andric     if (frame) {
5982706b4fc4SDimitry Andric       if (frame->GetStackID() != m_stack_id) {
5983706b4fc4SDimitry Andric         m_stack_id = frame->GetStackID();
5984706b4fc4SDimitry Andric         RegisterContextSP reg_ctx(frame->GetRegisterContext());
5985706b4fc4SDimitry Andric         if (reg_ctx) {
5986706b4fc4SDimitry Andric           const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
5987706b4fc4SDimitry Andric           for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) {
5988706b4fc4SDimitry Andric             value_list.Append(
5989706b4fc4SDimitry Andric                 ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx));
5990706b4fc4SDimitry Andric           }
5991706b4fc4SDimitry Andric         }
5992706b4fc4SDimitry Andric         SetValues(value_list);
5993706b4fc4SDimitry Andric       }
5994706b4fc4SDimitry Andric     } else {
5995706b4fc4SDimitry Andric       Process *process = exe_ctx.GetProcessPtr();
5996706b4fc4SDimitry Andric       if (process && process->IsAlive())
5997706b4fc4SDimitry Andric         return true; // Don't do any updating if we are running
5998706b4fc4SDimitry Andric       else {
5999706b4fc4SDimitry Andric         // Update the values with an empty list if there is no process or the
6000706b4fc4SDimitry Andric         // process isn't alive anymore
6001706b4fc4SDimitry Andric         SetValues(value_list);
6002706b4fc4SDimitry Andric       }
6003706b4fc4SDimitry Andric     }
6004706b4fc4SDimitry Andric     return ValueObjectListDelegate::WindowDelegateDraw(window, force);
6005706b4fc4SDimitry Andric   }
6006706b4fc4SDimitry Andric 
6007706b4fc4SDimitry Andric protected:
6008706b4fc4SDimitry Andric   Debugger &m_debugger;
6009706b4fc4SDimitry Andric   StackID m_stack_id;
6010706b4fc4SDimitry Andric };
6011706b4fc4SDimitry Andric 
CursesKeyToCString(int ch)6012706b4fc4SDimitry Andric static const char *CursesKeyToCString(int ch) {
6013706b4fc4SDimitry Andric   static char g_desc[32];
6014706b4fc4SDimitry Andric   if (ch >= KEY_F0 && ch < KEY_F0 + 64) {
6015706b4fc4SDimitry Andric     snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
6016706b4fc4SDimitry Andric     return g_desc;
6017706b4fc4SDimitry Andric   }
6018706b4fc4SDimitry Andric   switch (ch) {
6019706b4fc4SDimitry Andric   case KEY_DOWN:
6020706b4fc4SDimitry Andric     return "down";
6021706b4fc4SDimitry Andric   case KEY_UP:
6022706b4fc4SDimitry Andric     return "up";
6023706b4fc4SDimitry Andric   case KEY_LEFT:
6024706b4fc4SDimitry Andric     return "left";
6025706b4fc4SDimitry Andric   case KEY_RIGHT:
6026706b4fc4SDimitry Andric     return "right";
6027706b4fc4SDimitry Andric   case KEY_HOME:
6028706b4fc4SDimitry Andric     return "home";
6029706b4fc4SDimitry Andric   case KEY_BACKSPACE:
6030706b4fc4SDimitry Andric     return "backspace";
6031706b4fc4SDimitry Andric   case KEY_DL:
6032706b4fc4SDimitry Andric     return "delete-line";
6033706b4fc4SDimitry Andric   case KEY_IL:
6034706b4fc4SDimitry Andric     return "insert-line";
6035706b4fc4SDimitry Andric   case KEY_DC:
6036706b4fc4SDimitry Andric     return "delete-char";
6037706b4fc4SDimitry Andric   case KEY_IC:
6038706b4fc4SDimitry Andric     return "insert-char";
6039706b4fc4SDimitry Andric   case KEY_CLEAR:
6040706b4fc4SDimitry Andric     return "clear";
6041706b4fc4SDimitry Andric   case KEY_EOS:
6042706b4fc4SDimitry Andric     return "clear-to-eos";
6043706b4fc4SDimitry Andric   case KEY_EOL:
6044706b4fc4SDimitry Andric     return "clear-to-eol";
6045706b4fc4SDimitry Andric   case KEY_SF:
6046706b4fc4SDimitry Andric     return "scroll-forward";
6047706b4fc4SDimitry Andric   case KEY_SR:
6048706b4fc4SDimitry Andric     return "scroll-backward";
6049706b4fc4SDimitry Andric   case KEY_NPAGE:
6050706b4fc4SDimitry Andric     return "page-down";
6051706b4fc4SDimitry Andric   case KEY_PPAGE:
6052706b4fc4SDimitry Andric     return "page-up";
6053706b4fc4SDimitry Andric   case KEY_STAB:
6054706b4fc4SDimitry Andric     return "set-tab";
6055706b4fc4SDimitry Andric   case KEY_CTAB:
6056706b4fc4SDimitry Andric     return "clear-tab";
6057706b4fc4SDimitry Andric   case KEY_CATAB:
6058706b4fc4SDimitry Andric     return "clear-all-tabs";
6059706b4fc4SDimitry Andric   case KEY_ENTER:
6060706b4fc4SDimitry Andric     return "enter";
6061706b4fc4SDimitry Andric   case KEY_PRINT:
6062706b4fc4SDimitry Andric     return "print";
6063706b4fc4SDimitry Andric   case KEY_LL:
6064706b4fc4SDimitry Andric     return "lower-left key";
6065706b4fc4SDimitry Andric   case KEY_A1:
6066706b4fc4SDimitry Andric     return "upper left of keypad";
6067706b4fc4SDimitry Andric   case KEY_A3:
6068706b4fc4SDimitry Andric     return "upper right of keypad";
6069706b4fc4SDimitry Andric   case KEY_B2:
6070706b4fc4SDimitry Andric     return "center of keypad";
6071706b4fc4SDimitry Andric   case KEY_C1:
6072706b4fc4SDimitry Andric     return "lower left of keypad";
6073706b4fc4SDimitry Andric   case KEY_C3:
6074706b4fc4SDimitry Andric     return "lower right of keypad";
6075706b4fc4SDimitry Andric   case KEY_BTAB:
6076706b4fc4SDimitry Andric     return "back-tab key";
6077706b4fc4SDimitry Andric   case KEY_BEG:
6078706b4fc4SDimitry Andric     return "begin key";
6079706b4fc4SDimitry Andric   case KEY_CANCEL:
6080706b4fc4SDimitry Andric     return "cancel key";
6081706b4fc4SDimitry Andric   case KEY_CLOSE:
6082706b4fc4SDimitry Andric     return "close key";
6083706b4fc4SDimitry Andric   case KEY_COMMAND:
6084706b4fc4SDimitry Andric     return "command key";
6085706b4fc4SDimitry Andric   case KEY_COPY:
6086706b4fc4SDimitry Andric     return "copy key";
6087706b4fc4SDimitry Andric   case KEY_CREATE:
6088706b4fc4SDimitry Andric     return "create key";
6089706b4fc4SDimitry Andric   case KEY_END:
6090706b4fc4SDimitry Andric     return "end key";
6091706b4fc4SDimitry Andric   case KEY_EXIT:
6092706b4fc4SDimitry Andric     return "exit key";
6093706b4fc4SDimitry Andric   case KEY_FIND:
6094706b4fc4SDimitry Andric     return "find key";
6095706b4fc4SDimitry Andric   case KEY_HELP:
6096706b4fc4SDimitry Andric     return "help key";
6097706b4fc4SDimitry Andric   case KEY_MARK:
6098706b4fc4SDimitry Andric     return "mark key";
6099706b4fc4SDimitry Andric   case KEY_MESSAGE:
6100706b4fc4SDimitry Andric     return "message key";
6101706b4fc4SDimitry Andric   case KEY_MOVE:
6102706b4fc4SDimitry Andric     return "move key";
6103706b4fc4SDimitry Andric   case KEY_NEXT:
6104706b4fc4SDimitry Andric     return "next key";
6105706b4fc4SDimitry Andric   case KEY_OPEN:
6106706b4fc4SDimitry Andric     return "open key";
6107706b4fc4SDimitry Andric   case KEY_OPTIONS:
6108706b4fc4SDimitry Andric     return "options key";
6109706b4fc4SDimitry Andric   case KEY_PREVIOUS:
6110706b4fc4SDimitry Andric     return "previous key";
6111706b4fc4SDimitry Andric   case KEY_REDO:
6112706b4fc4SDimitry Andric     return "redo key";
6113706b4fc4SDimitry Andric   case KEY_REFERENCE:
6114706b4fc4SDimitry Andric     return "reference key";
6115706b4fc4SDimitry Andric   case KEY_REFRESH:
6116706b4fc4SDimitry Andric     return "refresh key";
6117706b4fc4SDimitry Andric   case KEY_REPLACE:
6118706b4fc4SDimitry Andric     return "replace key";
6119706b4fc4SDimitry Andric   case KEY_RESTART:
6120706b4fc4SDimitry Andric     return "restart key";
6121706b4fc4SDimitry Andric   case KEY_RESUME:
6122706b4fc4SDimitry Andric     return "resume key";
6123706b4fc4SDimitry Andric   case KEY_SAVE:
6124706b4fc4SDimitry Andric     return "save key";
6125706b4fc4SDimitry Andric   case KEY_SBEG:
6126706b4fc4SDimitry Andric     return "shifted begin key";
6127706b4fc4SDimitry Andric   case KEY_SCANCEL:
6128706b4fc4SDimitry Andric     return "shifted cancel key";
6129706b4fc4SDimitry Andric   case KEY_SCOMMAND:
6130706b4fc4SDimitry Andric     return "shifted command key";
6131706b4fc4SDimitry Andric   case KEY_SCOPY:
6132706b4fc4SDimitry Andric     return "shifted copy key";
6133706b4fc4SDimitry Andric   case KEY_SCREATE:
6134706b4fc4SDimitry Andric     return "shifted create key";
6135706b4fc4SDimitry Andric   case KEY_SDC:
6136706b4fc4SDimitry Andric     return "shifted delete-character key";
6137706b4fc4SDimitry Andric   case KEY_SDL:
6138706b4fc4SDimitry Andric     return "shifted delete-line key";
6139706b4fc4SDimitry Andric   case KEY_SELECT:
6140706b4fc4SDimitry Andric     return "select key";
6141706b4fc4SDimitry Andric   case KEY_SEND:
6142706b4fc4SDimitry Andric     return "shifted end key";
6143706b4fc4SDimitry Andric   case KEY_SEOL:
6144706b4fc4SDimitry Andric     return "shifted clear-to-end-of-line key";
6145706b4fc4SDimitry Andric   case KEY_SEXIT:
6146706b4fc4SDimitry Andric     return "shifted exit key";
6147706b4fc4SDimitry Andric   case KEY_SFIND:
6148706b4fc4SDimitry Andric     return "shifted find key";
6149706b4fc4SDimitry Andric   case KEY_SHELP:
6150706b4fc4SDimitry Andric     return "shifted help key";
6151706b4fc4SDimitry Andric   case KEY_SHOME:
6152706b4fc4SDimitry Andric     return "shifted home key";
6153706b4fc4SDimitry Andric   case KEY_SIC:
6154706b4fc4SDimitry Andric     return "shifted insert-character key";
6155706b4fc4SDimitry Andric   case KEY_SLEFT:
6156706b4fc4SDimitry Andric     return "shifted left-arrow key";
6157706b4fc4SDimitry Andric   case KEY_SMESSAGE:
6158706b4fc4SDimitry Andric     return "shifted message key";
6159706b4fc4SDimitry Andric   case KEY_SMOVE:
6160706b4fc4SDimitry Andric     return "shifted move key";
6161706b4fc4SDimitry Andric   case KEY_SNEXT:
6162706b4fc4SDimitry Andric     return "shifted next key";
6163706b4fc4SDimitry Andric   case KEY_SOPTIONS:
6164706b4fc4SDimitry Andric     return "shifted options key";
6165706b4fc4SDimitry Andric   case KEY_SPREVIOUS:
6166706b4fc4SDimitry Andric     return "shifted previous key";
6167706b4fc4SDimitry Andric   case KEY_SPRINT:
6168706b4fc4SDimitry Andric     return "shifted print key";
6169706b4fc4SDimitry Andric   case KEY_SREDO:
6170706b4fc4SDimitry Andric     return "shifted redo key";
6171706b4fc4SDimitry Andric   case KEY_SREPLACE:
6172706b4fc4SDimitry Andric     return "shifted replace key";
6173706b4fc4SDimitry Andric   case KEY_SRIGHT:
6174706b4fc4SDimitry Andric     return "shifted right-arrow key";
6175706b4fc4SDimitry Andric   case KEY_SRSUME:
6176706b4fc4SDimitry Andric     return "shifted resume key";
6177706b4fc4SDimitry Andric   case KEY_SSAVE:
6178706b4fc4SDimitry Andric     return "shifted save key";
6179706b4fc4SDimitry Andric   case KEY_SSUSPEND:
6180706b4fc4SDimitry Andric     return "shifted suspend key";
6181706b4fc4SDimitry Andric   case KEY_SUNDO:
6182706b4fc4SDimitry Andric     return "shifted undo key";
6183706b4fc4SDimitry Andric   case KEY_SUSPEND:
6184706b4fc4SDimitry Andric     return "suspend key";
6185706b4fc4SDimitry Andric   case KEY_UNDO:
6186706b4fc4SDimitry Andric     return "undo key";
6187706b4fc4SDimitry Andric   case KEY_MOUSE:
6188706b4fc4SDimitry Andric     return "Mouse event has occurred";
6189706b4fc4SDimitry Andric   case KEY_RESIZE:
6190706b4fc4SDimitry Andric     return "Terminal resize event";
6191706b4fc4SDimitry Andric #ifdef KEY_EVENT
6192706b4fc4SDimitry Andric   case KEY_EVENT:
6193706b4fc4SDimitry Andric     return "We were interrupted by an event";
6194706b4fc4SDimitry Andric #endif
6195706b4fc4SDimitry Andric   case KEY_RETURN:
6196706b4fc4SDimitry Andric     return "return";
6197706b4fc4SDimitry Andric   case ' ':
6198706b4fc4SDimitry Andric     return "space";
6199706b4fc4SDimitry Andric   case '\t':
6200706b4fc4SDimitry Andric     return "tab";
6201706b4fc4SDimitry Andric   case KEY_ESCAPE:
6202706b4fc4SDimitry Andric     return "escape";
6203706b4fc4SDimitry Andric   default:
6204cfca06d7SDimitry Andric     if (llvm::isPrint(ch))
6205706b4fc4SDimitry Andric       snprintf(g_desc, sizeof(g_desc), "%c", ch);
6206706b4fc4SDimitry Andric     else
6207706b4fc4SDimitry Andric       snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
6208706b4fc4SDimitry Andric     return g_desc;
6209706b4fc4SDimitry Andric   }
6210706b4fc4SDimitry Andric   return nullptr;
6211706b4fc4SDimitry Andric }
6212706b4fc4SDimitry Andric 
HelpDialogDelegate(const char * text,KeyHelp * key_help_array)6213706b4fc4SDimitry Andric HelpDialogDelegate::HelpDialogDelegate(const char *text,
6214706b4fc4SDimitry Andric                                        KeyHelp *key_help_array)
6215145449b1SDimitry Andric     : m_text() {
6216706b4fc4SDimitry Andric   if (text && text[0]) {
6217706b4fc4SDimitry Andric     m_text.SplitIntoLines(text);
6218706b4fc4SDimitry Andric     m_text.AppendString("");
6219706b4fc4SDimitry Andric   }
6220706b4fc4SDimitry Andric   if (key_help_array) {
6221706b4fc4SDimitry Andric     for (KeyHelp *key = key_help_array; key->ch; ++key) {
6222706b4fc4SDimitry Andric       StreamString key_description;
6223706b4fc4SDimitry Andric       key_description.Printf("%10s - %s", CursesKeyToCString(key->ch),
6224706b4fc4SDimitry Andric                              key->description);
6225706b4fc4SDimitry Andric       m_text.AppendString(key_description.GetString());
6226706b4fc4SDimitry Andric     }
6227706b4fc4SDimitry Andric   }
6228706b4fc4SDimitry Andric }
6229706b4fc4SDimitry Andric 
6230706b4fc4SDimitry Andric HelpDialogDelegate::~HelpDialogDelegate() = default;
6231706b4fc4SDimitry Andric 
WindowDelegateDraw(Window & window,bool force)6232706b4fc4SDimitry Andric bool HelpDialogDelegate::WindowDelegateDraw(Window &window, bool force) {
6233706b4fc4SDimitry Andric   window.Erase();
6234706b4fc4SDimitry Andric   const int window_height = window.GetHeight();
6235706b4fc4SDimitry Andric   int x = 2;
6236706b4fc4SDimitry Andric   int y = 1;
6237706b4fc4SDimitry Andric   const int min_y = y;
6238706b4fc4SDimitry Andric   const int max_y = window_height - 1 - y;
6239706b4fc4SDimitry Andric   const size_t num_visible_lines = max_y - min_y + 1;
6240706b4fc4SDimitry Andric   const size_t num_lines = m_text.GetSize();
6241706b4fc4SDimitry Andric   const char *bottom_message;
6242706b4fc4SDimitry Andric   if (num_lines <= num_visible_lines)
6243706b4fc4SDimitry Andric     bottom_message = "Press any key to exit";
6244706b4fc4SDimitry Andric   else
6245706b4fc4SDimitry Andric     bottom_message = "Use arrows to scroll, any other key to exit";
6246706b4fc4SDimitry Andric   window.DrawTitleBox(window.GetName(), bottom_message);
6247706b4fc4SDimitry Andric   while (y <= max_y) {
6248706b4fc4SDimitry Andric     window.MoveCursor(x, y);
6249706b4fc4SDimitry Andric     window.PutCStringTruncated(
6250b60736ecSDimitry Andric         1, m_text.GetStringAtIndex(m_first_visible_line + y - min_y));
6251706b4fc4SDimitry Andric     ++y;
6252706b4fc4SDimitry Andric   }
6253706b4fc4SDimitry Andric   return true;
6254706b4fc4SDimitry Andric }
6255706b4fc4SDimitry Andric 
WindowDelegateHandleChar(Window & window,int key)6256706b4fc4SDimitry Andric HandleCharResult HelpDialogDelegate::WindowDelegateHandleChar(Window &window,
6257706b4fc4SDimitry Andric                                                               int key) {
6258706b4fc4SDimitry Andric   bool done = false;
6259706b4fc4SDimitry Andric   const size_t num_lines = m_text.GetSize();
6260706b4fc4SDimitry Andric   const size_t num_visible_lines = window.GetHeight() - 2;
6261706b4fc4SDimitry Andric 
6262706b4fc4SDimitry Andric   if (num_lines <= num_visible_lines) {
6263706b4fc4SDimitry Andric     done = true;
6264706b4fc4SDimitry Andric     // If we have all lines visible and don't need scrolling, then any key
6265706b4fc4SDimitry Andric     // press will cause us to exit
6266706b4fc4SDimitry Andric   } else {
6267706b4fc4SDimitry Andric     switch (key) {
6268706b4fc4SDimitry Andric     case KEY_UP:
6269706b4fc4SDimitry Andric       if (m_first_visible_line > 0)
6270706b4fc4SDimitry Andric         --m_first_visible_line;
6271706b4fc4SDimitry Andric       break;
6272706b4fc4SDimitry Andric 
6273706b4fc4SDimitry Andric     case KEY_DOWN:
6274706b4fc4SDimitry Andric       if (m_first_visible_line + num_visible_lines < num_lines)
6275706b4fc4SDimitry Andric         ++m_first_visible_line;
6276706b4fc4SDimitry Andric       break;
6277706b4fc4SDimitry Andric 
6278706b4fc4SDimitry Andric     case KEY_PPAGE:
6279706b4fc4SDimitry Andric     case ',':
6280706b4fc4SDimitry Andric       if (m_first_visible_line > 0) {
6281706b4fc4SDimitry Andric         if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
6282706b4fc4SDimitry Andric           m_first_visible_line -= num_visible_lines;
6283706b4fc4SDimitry Andric         else
6284706b4fc4SDimitry Andric           m_first_visible_line = 0;
6285706b4fc4SDimitry Andric       }
6286706b4fc4SDimitry Andric       break;
6287706b4fc4SDimitry Andric 
6288706b4fc4SDimitry Andric     case KEY_NPAGE:
6289706b4fc4SDimitry Andric     case '.':
6290706b4fc4SDimitry Andric       if (m_first_visible_line + num_visible_lines < num_lines) {
6291706b4fc4SDimitry Andric         m_first_visible_line += num_visible_lines;
6292706b4fc4SDimitry Andric         if (static_cast<size_t>(m_first_visible_line) > num_lines)
6293706b4fc4SDimitry Andric           m_first_visible_line = num_lines - num_visible_lines;
6294706b4fc4SDimitry Andric       }
6295706b4fc4SDimitry Andric       break;
6296706b4fc4SDimitry Andric 
6297706b4fc4SDimitry Andric     default:
6298706b4fc4SDimitry Andric       done = true;
6299706b4fc4SDimitry Andric       break;
6300706b4fc4SDimitry Andric     }
6301706b4fc4SDimitry Andric   }
6302706b4fc4SDimitry Andric   if (done)
6303706b4fc4SDimitry Andric     window.GetParent()->RemoveSubWindow(&window);
6304706b4fc4SDimitry Andric   return eKeyHandled;
6305706b4fc4SDimitry Andric }
6306706b4fc4SDimitry Andric 
6307706b4fc4SDimitry Andric class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
6308706b4fc4SDimitry Andric public:
6309706b4fc4SDimitry Andric   enum {
6310706b4fc4SDimitry Andric     eMenuID_LLDB = 1,
6311706b4fc4SDimitry Andric     eMenuID_LLDBAbout,
6312706b4fc4SDimitry Andric     eMenuID_LLDBExit,
6313706b4fc4SDimitry Andric 
6314706b4fc4SDimitry Andric     eMenuID_Target,
6315706b4fc4SDimitry Andric     eMenuID_TargetCreate,
6316706b4fc4SDimitry Andric     eMenuID_TargetDelete,
6317706b4fc4SDimitry Andric 
6318706b4fc4SDimitry Andric     eMenuID_Process,
6319706b4fc4SDimitry Andric     eMenuID_ProcessAttach,
6320b60736ecSDimitry Andric     eMenuID_ProcessDetachResume,
6321b60736ecSDimitry Andric     eMenuID_ProcessDetachSuspended,
6322706b4fc4SDimitry Andric     eMenuID_ProcessLaunch,
6323706b4fc4SDimitry Andric     eMenuID_ProcessContinue,
6324706b4fc4SDimitry Andric     eMenuID_ProcessHalt,
6325706b4fc4SDimitry Andric     eMenuID_ProcessKill,
6326706b4fc4SDimitry Andric 
6327706b4fc4SDimitry Andric     eMenuID_Thread,
6328706b4fc4SDimitry Andric     eMenuID_ThreadStepIn,
6329706b4fc4SDimitry Andric     eMenuID_ThreadStepOver,
6330706b4fc4SDimitry Andric     eMenuID_ThreadStepOut,
6331706b4fc4SDimitry Andric 
6332706b4fc4SDimitry Andric     eMenuID_View,
6333706b4fc4SDimitry Andric     eMenuID_ViewBacktrace,
6334706b4fc4SDimitry Andric     eMenuID_ViewRegisters,
6335706b4fc4SDimitry Andric     eMenuID_ViewSource,
6336706b4fc4SDimitry Andric     eMenuID_ViewVariables,
6337c0981da4SDimitry Andric     eMenuID_ViewBreakpoints,
6338706b4fc4SDimitry Andric 
6339706b4fc4SDimitry Andric     eMenuID_Help,
6340706b4fc4SDimitry Andric     eMenuID_HelpGUIHelp
6341706b4fc4SDimitry Andric   };
6342706b4fc4SDimitry Andric 
ApplicationDelegate(Application & app,Debugger & debugger)6343706b4fc4SDimitry Andric   ApplicationDelegate(Application &app, Debugger &debugger)
6344706b4fc4SDimitry Andric       : WindowDelegate(), MenuDelegate(), m_app(app), m_debugger(debugger) {}
6345706b4fc4SDimitry Andric 
6346706b4fc4SDimitry Andric   ~ApplicationDelegate() override = default;
6347706b4fc4SDimitry Andric 
WindowDelegateDraw(Window & window,bool force)6348706b4fc4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
6349706b4fc4SDimitry Andric     return false; // Drawing not handled, let standard window drawing happen
6350706b4fc4SDimitry Andric   }
6351706b4fc4SDimitry Andric 
WindowDelegateHandleChar(Window & window,int key)6352706b4fc4SDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
6353706b4fc4SDimitry Andric     switch (key) {
6354706b4fc4SDimitry Andric     case '\t':
6355706b4fc4SDimitry Andric       window.SelectNextWindowAsActive();
6356706b4fc4SDimitry Andric       return eKeyHandled;
6357706b4fc4SDimitry Andric 
6358344a3780SDimitry Andric     case KEY_SHIFT_TAB:
6359b60736ecSDimitry Andric       window.SelectPreviousWindowAsActive();
6360b60736ecSDimitry Andric       return eKeyHandled;
6361b60736ecSDimitry Andric 
6362706b4fc4SDimitry Andric     case 'h':
6363706b4fc4SDimitry Andric       window.CreateHelpSubwindow();
6364706b4fc4SDimitry Andric       return eKeyHandled;
6365706b4fc4SDimitry Andric 
6366706b4fc4SDimitry Andric     case KEY_ESCAPE:
6367706b4fc4SDimitry Andric       return eQuitApplication;
6368706b4fc4SDimitry Andric 
6369706b4fc4SDimitry Andric     default:
6370706b4fc4SDimitry Andric       break;
6371706b4fc4SDimitry Andric     }
6372706b4fc4SDimitry Andric     return eKeyNotHandled;
6373706b4fc4SDimitry Andric   }
6374706b4fc4SDimitry Andric 
WindowDelegateGetHelpText()6375706b4fc4SDimitry Andric   const char *WindowDelegateGetHelpText() override {
6376706b4fc4SDimitry Andric     return "Welcome to the LLDB curses GUI.\n\n"
6377706b4fc4SDimitry Andric            "Press the TAB key to change the selected view.\n"
6378706b4fc4SDimitry Andric            "Each view has its own keyboard shortcuts, press 'h' to open a "
6379706b4fc4SDimitry Andric            "dialog to display them.\n\n"
6380706b4fc4SDimitry Andric            "Common key bindings for all views:";
6381706b4fc4SDimitry Andric   }
6382706b4fc4SDimitry Andric 
WindowDelegateGetKeyHelp()6383706b4fc4SDimitry Andric   KeyHelp *WindowDelegateGetKeyHelp() override {
6384706b4fc4SDimitry Andric     static curses::KeyHelp g_source_view_key_help[] = {
6385706b4fc4SDimitry Andric         {'\t', "Select next view"},
6386b60736ecSDimitry Andric         {KEY_BTAB, "Select previous view"},
6387706b4fc4SDimitry Andric         {'h', "Show help dialog with view specific key bindings"},
6388706b4fc4SDimitry Andric         {',', "Page up"},
6389706b4fc4SDimitry Andric         {'.', "Page down"},
6390706b4fc4SDimitry Andric         {KEY_UP, "Select previous"},
6391706b4fc4SDimitry Andric         {KEY_DOWN, "Select next"},
6392706b4fc4SDimitry Andric         {KEY_LEFT, "Unexpand or select parent"},
6393706b4fc4SDimitry Andric         {KEY_RIGHT, "Expand"},
6394706b4fc4SDimitry Andric         {KEY_PPAGE, "Page up"},
6395706b4fc4SDimitry Andric         {KEY_NPAGE, "Page down"},
6396706b4fc4SDimitry Andric         {'\0', nullptr}};
6397706b4fc4SDimitry Andric     return g_source_view_key_help;
6398706b4fc4SDimitry Andric   }
6399706b4fc4SDimitry Andric 
MenuDelegateAction(Menu & menu)6400706b4fc4SDimitry Andric   MenuActionResult MenuDelegateAction(Menu &menu) override {
6401706b4fc4SDimitry Andric     switch (menu.GetIdentifier()) {
6402c0981da4SDimitry Andric     case eMenuID_TargetCreate: {
6403c0981da4SDimitry Andric       WindowSP main_window_sp = m_app.GetMainWindow();
6404c0981da4SDimitry Andric       FormDelegateSP form_delegate_sp =
6405c0981da4SDimitry Andric           FormDelegateSP(new TargetCreateFormDelegate(m_debugger));
6406c0981da4SDimitry Andric       Rect bounds = main_window_sp->GetCenteredRect(80, 19);
6407c0981da4SDimitry Andric       WindowSP form_window_sp = main_window_sp->CreateSubWindow(
6408c0981da4SDimitry Andric           form_delegate_sp->GetName().c_str(), bounds, true);
6409c0981da4SDimitry Andric       WindowDelegateSP window_delegate_sp =
6410c0981da4SDimitry Andric           WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
6411c0981da4SDimitry Andric       form_window_sp->SetDelegate(window_delegate_sp);
6412c0981da4SDimitry Andric       return MenuActionResult::Handled;
6413c0981da4SDimitry Andric     }
6414706b4fc4SDimitry Andric     case eMenuID_ThreadStepIn: {
6415706b4fc4SDimitry Andric       ExecutionContext exe_ctx =
6416706b4fc4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6417706b4fc4SDimitry Andric       if (exe_ctx.HasThreadScope()) {
6418706b4fc4SDimitry Andric         Process *process = exe_ctx.GetProcessPtr();
6419706b4fc4SDimitry Andric         if (process && process->IsAlive() &&
6420706b4fc4SDimitry Andric             StateIsStoppedState(process->GetState(), true))
6421706b4fc4SDimitry Andric           exe_ctx.GetThreadRef().StepIn(true);
6422706b4fc4SDimitry Andric       }
6423706b4fc4SDimitry Andric     }
6424706b4fc4SDimitry Andric       return MenuActionResult::Handled;
6425706b4fc4SDimitry Andric 
6426706b4fc4SDimitry Andric     case eMenuID_ThreadStepOut: {
6427706b4fc4SDimitry Andric       ExecutionContext exe_ctx =
6428706b4fc4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6429706b4fc4SDimitry Andric       if (exe_ctx.HasThreadScope()) {
6430706b4fc4SDimitry Andric         Process *process = exe_ctx.GetProcessPtr();
6431706b4fc4SDimitry Andric         if (process && process->IsAlive() &&
6432145449b1SDimitry Andric             StateIsStoppedState(process->GetState(), true)) {
6433145449b1SDimitry Andric           Thread *thread = exe_ctx.GetThreadPtr();
64347fa27ce4SDimitry Andric           uint32_t frame_idx =
64357fa27ce4SDimitry Andric               thread->GetSelectedFrameIndex(SelectMostRelevantFrame);
6436145449b1SDimitry Andric           exe_ctx.GetThreadRef().StepOut(frame_idx);
6437145449b1SDimitry Andric         }
6438706b4fc4SDimitry Andric       }
6439706b4fc4SDimitry Andric     }
6440706b4fc4SDimitry Andric       return MenuActionResult::Handled;
6441706b4fc4SDimitry Andric 
6442706b4fc4SDimitry Andric     case eMenuID_ThreadStepOver: {
6443706b4fc4SDimitry Andric       ExecutionContext exe_ctx =
6444706b4fc4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6445706b4fc4SDimitry Andric       if (exe_ctx.HasThreadScope()) {
6446706b4fc4SDimitry Andric         Process *process = exe_ctx.GetProcessPtr();
6447706b4fc4SDimitry Andric         if (process && process->IsAlive() &&
6448706b4fc4SDimitry Andric             StateIsStoppedState(process->GetState(), true))
6449706b4fc4SDimitry Andric           exe_ctx.GetThreadRef().StepOver(true);
6450706b4fc4SDimitry Andric       }
6451706b4fc4SDimitry Andric     }
6452706b4fc4SDimitry Andric       return MenuActionResult::Handled;
6453706b4fc4SDimitry Andric 
6454344a3780SDimitry Andric     case eMenuID_ProcessAttach: {
6455344a3780SDimitry Andric       WindowSP main_window_sp = m_app.GetMainWindow();
6456344a3780SDimitry Andric       FormDelegateSP form_delegate_sp = FormDelegateSP(
6457344a3780SDimitry Andric           new ProcessAttachFormDelegate(m_debugger, main_window_sp));
6458344a3780SDimitry Andric       Rect bounds = main_window_sp->GetCenteredRect(80, 22);
6459344a3780SDimitry Andric       WindowSP form_window_sp = main_window_sp->CreateSubWindow(
6460344a3780SDimitry Andric           form_delegate_sp->GetName().c_str(), bounds, true);
6461344a3780SDimitry Andric       WindowDelegateSP window_delegate_sp =
6462344a3780SDimitry Andric           WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
6463344a3780SDimitry Andric       form_window_sp->SetDelegate(window_delegate_sp);
6464344a3780SDimitry Andric       return MenuActionResult::Handled;
6465344a3780SDimitry Andric     }
6466c0981da4SDimitry Andric     case eMenuID_ProcessLaunch: {
6467c0981da4SDimitry Andric       WindowSP main_window_sp = m_app.GetMainWindow();
6468c0981da4SDimitry Andric       FormDelegateSP form_delegate_sp = FormDelegateSP(
6469c0981da4SDimitry Andric           new ProcessLaunchFormDelegate(m_debugger, main_window_sp));
6470c0981da4SDimitry Andric       Rect bounds = main_window_sp->GetCenteredRect(80, 22);
6471c0981da4SDimitry Andric       WindowSP form_window_sp = main_window_sp->CreateSubWindow(
6472c0981da4SDimitry Andric           form_delegate_sp->GetName().c_str(), bounds, true);
6473c0981da4SDimitry Andric       WindowDelegateSP window_delegate_sp =
6474c0981da4SDimitry Andric           WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
6475c0981da4SDimitry Andric       form_window_sp->SetDelegate(window_delegate_sp);
6476c0981da4SDimitry Andric       return MenuActionResult::Handled;
6477c0981da4SDimitry Andric     }
6478344a3780SDimitry Andric 
6479706b4fc4SDimitry Andric     case eMenuID_ProcessContinue: {
6480706b4fc4SDimitry Andric       ExecutionContext exe_ctx =
6481706b4fc4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6482706b4fc4SDimitry Andric       if (exe_ctx.HasProcessScope()) {
6483706b4fc4SDimitry Andric         Process *process = exe_ctx.GetProcessPtr();
6484706b4fc4SDimitry Andric         if (process && process->IsAlive() &&
6485706b4fc4SDimitry Andric             StateIsStoppedState(process->GetState(), true))
6486706b4fc4SDimitry Andric           process->Resume();
6487706b4fc4SDimitry Andric       }
6488706b4fc4SDimitry Andric     }
6489706b4fc4SDimitry Andric       return MenuActionResult::Handled;
6490706b4fc4SDimitry Andric 
6491706b4fc4SDimitry Andric     case eMenuID_ProcessKill: {
6492706b4fc4SDimitry Andric       ExecutionContext exe_ctx =
6493706b4fc4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6494706b4fc4SDimitry Andric       if (exe_ctx.HasProcessScope()) {
6495706b4fc4SDimitry Andric         Process *process = exe_ctx.GetProcessPtr();
6496706b4fc4SDimitry Andric         if (process && process->IsAlive())
6497706b4fc4SDimitry Andric           process->Destroy(false);
6498706b4fc4SDimitry Andric       }
6499706b4fc4SDimitry Andric     }
6500706b4fc4SDimitry Andric       return MenuActionResult::Handled;
6501706b4fc4SDimitry Andric 
6502706b4fc4SDimitry Andric     case eMenuID_ProcessHalt: {
6503706b4fc4SDimitry Andric       ExecutionContext exe_ctx =
6504706b4fc4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6505706b4fc4SDimitry Andric       if (exe_ctx.HasProcessScope()) {
6506706b4fc4SDimitry Andric         Process *process = exe_ctx.GetProcessPtr();
6507706b4fc4SDimitry Andric         if (process && process->IsAlive())
6508706b4fc4SDimitry Andric           process->Halt();
6509706b4fc4SDimitry Andric       }
6510706b4fc4SDimitry Andric     }
6511706b4fc4SDimitry Andric       return MenuActionResult::Handled;
6512706b4fc4SDimitry Andric 
6513b60736ecSDimitry Andric     case eMenuID_ProcessDetachResume:
6514b60736ecSDimitry Andric     case eMenuID_ProcessDetachSuspended: {
6515706b4fc4SDimitry Andric       ExecutionContext exe_ctx =
6516706b4fc4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6517706b4fc4SDimitry Andric       if (exe_ctx.HasProcessScope()) {
6518706b4fc4SDimitry Andric         Process *process = exe_ctx.GetProcessPtr();
6519706b4fc4SDimitry Andric         if (process && process->IsAlive())
6520b60736ecSDimitry Andric           process->Detach(menu.GetIdentifier() ==
6521b60736ecSDimitry Andric                           eMenuID_ProcessDetachSuspended);
6522706b4fc4SDimitry Andric       }
6523706b4fc4SDimitry Andric     }
6524706b4fc4SDimitry Andric       return MenuActionResult::Handled;
6525706b4fc4SDimitry Andric 
6526706b4fc4SDimitry Andric     case eMenuID_Process: {
6527706b4fc4SDimitry Andric       // Populate the menu with all of the threads if the process is stopped
6528706b4fc4SDimitry Andric       // when the Process menu gets selected and is about to display its
6529706b4fc4SDimitry Andric       // submenu.
6530706b4fc4SDimitry Andric       Menus &submenus = menu.GetSubmenus();
6531706b4fc4SDimitry Andric       ExecutionContext exe_ctx =
6532706b4fc4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6533706b4fc4SDimitry Andric       Process *process = exe_ctx.GetProcessPtr();
6534706b4fc4SDimitry Andric       if (process && process->IsAlive() &&
6535706b4fc4SDimitry Andric           StateIsStoppedState(process->GetState(), true)) {
6536706b4fc4SDimitry Andric         if (submenus.size() == 7)
6537706b4fc4SDimitry Andric           menu.AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
6538706b4fc4SDimitry Andric         else if (submenus.size() > 8)
6539706b4fc4SDimitry Andric           submenus.erase(submenus.begin() + 8, submenus.end());
6540706b4fc4SDimitry Andric 
6541706b4fc4SDimitry Andric         ThreadList &threads = process->GetThreadList();
6542706b4fc4SDimitry Andric         std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
6543706b4fc4SDimitry Andric         size_t num_threads = threads.GetSize();
6544706b4fc4SDimitry Andric         for (size_t i = 0; i < num_threads; ++i) {
6545706b4fc4SDimitry Andric           ThreadSP thread_sp = threads.GetThreadAtIndex(i);
6546706b4fc4SDimitry Andric           char menu_char = '\0';
6547706b4fc4SDimitry Andric           if (i < 9)
6548706b4fc4SDimitry Andric             menu_char = '1' + i;
6549706b4fc4SDimitry Andric           StreamString thread_menu_title;
6550706b4fc4SDimitry Andric           thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
6551706b4fc4SDimitry Andric           const char *thread_name = thread_sp->GetName();
6552706b4fc4SDimitry Andric           if (thread_name && thread_name[0])
6553706b4fc4SDimitry Andric             thread_menu_title.Printf(" %s", thread_name);
6554706b4fc4SDimitry Andric           else {
6555706b4fc4SDimitry Andric             const char *queue_name = thread_sp->GetQueueName();
6556706b4fc4SDimitry Andric             if (queue_name && queue_name[0])
6557706b4fc4SDimitry Andric               thread_menu_title.Printf(" %s", queue_name);
6558706b4fc4SDimitry Andric           }
6559706b4fc4SDimitry Andric           menu.AddSubmenu(
6560706b4fc4SDimitry Andric               MenuSP(new Menu(thread_menu_title.GetString().str().c_str(),
6561706b4fc4SDimitry Andric                               nullptr, menu_char, thread_sp->GetID())));
6562706b4fc4SDimitry Andric         }
6563706b4fc4SDimitry Andric       } else if (submenus.size() > 7) {
6564706b4fc4SDimitry Andric         // Remove the separator and any other thread submenu items that were
6565706b4fc4SDimitry Andric         // previously added
6566706b4fc4SDimitry Andric         submenus.erase(submenus.begin() + 7, submenus.end());
6567706b4fc4SDimitry Andric       }
6568c0981da4SDimitry Andric       // Since we are adding and removing items we need to recalculate the
6569c0981da4SDimitry Andric       // name lengths
6570706b4fc4SDimitry Andric       menu.RecalculateNameLengths();
6571706b4fc4SDimitry Andric     }
6572706b4fc4SDimitry Andric       return MenuActionResult::Handled;
6573706b4fc4SDimitry Andric 
6574706b4fc4SDimitry Andric     case eMenuID_ViewVariables: {
6575706b4fc4SDimitry Andric       WindowSP main_window_sp = m_app.GetMainWindow();
6576706b4fc4SDimitry Andric       WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
6577706b4fc4SDimitry Andric       WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
6578706b4fc4SDimitry Andric       WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
6579706b4fc4SDimitry Andric       const Rect source_bounds = source_window_sp->GetBounds();
6580706b4fc4SDimitry Andric 
6581706b4fc4SDimitry Andric       if (variables_window_sp) {
6582706b4fc4SDimitry Andric         const Rect variables_bounds = variables_window_sp->GetBounds();
6583706b4fc4SDimitry Andric 
6584706b4fc4SDimitry Andric         main_window_sp->RemoveSubWindow(variables_window_sp.get());
6585706b4fc4SDimitry Andric 
6586706b4fc4SDimitry Andric         if (registers_window_sp) {
6587706b4fc4SDimitry Andric           // We have a registers window, so give all the area back to the
6588706b4fc4SDimitry Andric           // registers window
6589706b4fc4SDimitry Andric           Rect registers_bounds = variables_bounds;
6590706b4fc4SDimitry Andric           registers_bounds.size.width = source_bounds.size.width;
6591706b4fc4SDimitry Andric           registers_window_sp->SetBounds(registers_bounds);
6592706b4fc4SDimitry Andric         } else {
6593706b4fc4SDimitry Andric           // We have no registers window showing so give the bottom area back
6594706b4fc4SDimitry Andric           // to the source view
6595706b4fc4SDimitry Andric           source_window_sp->Resize(source_bounds.size.width,
6596706b4fc4SDimitry Andric                                    source_bounds.size.height +
6597706b4fc4SDimitry Andric                                        variables_bounds.size.height);
6598706b4fc4SDimitry Andric         }
6599706b4fc4SDimitry Andric       } else {
6600706b4fc4SDimitry Andric         Rect new_variables_rect;
6601706b4fc4SDimitry Andric         if (registers_window_sp) {
6602706b4fc4SDimitry Andric           // We have a registers window so split the area of the registers
6603706b4fc4SDimitry Andric           // window into two columns where the left hand side will be the
6604706b4fc4SDimitry Andric           // variables and the right hand side will be the registers
6605706b4fc4SDimitry Andric           const Rect variables_bounds = registers_window_sp->GetBounds();
6606706b4fc4SDimitry Andric           Rect new_registers_rect;
6607706b4fc4SDimitry Andric           variables_bounds.VerticalSplitPercentage(0.50, new_variables_rect,
6608706b4fc4SDimitry Andric                                                    new_registers_rect);
6609706b4fc4SDimitry Andric           registers_window_sp->SetBounds(new_registers_rect);
6610706b4fc4SDimitry Andric         } else {
6611b60736ecSDimitry Andric           // No registers window, grab the bottom part of the source window
6612706b4fc4SDimitry Andric           Rect new_source_rect;
6613706b4fc4SDimitry Andric           source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
6614706b4fc4SDimitry Andric                                                   new_variables_rect);
6615706b4fc4SDimitry Andric           source_window_sp->SetBounds(new_source_rect);
6616706b4fc4SDimitry Andric         }
6617706b4fc4SDimitry Andric         WindowSP new_window_sp = main_window_sp->CreateSubWindow(
6618706b4fc4SDimitry Andric             "Variables", new_variables_rect, false);
6619706b4fc4SDimitry Andric         new_window_sp->SetDelegate(
6620706b4fc4SDimitry Andric             WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
6621706b4fc4SDimitry Andric       }
6622706b4fc4SDimitry Andric       touchwin(stdscr);
6623706b4fc4SDimitry Andric     }
6624706b4fc4SDimitry Andric       return MenuActionResult::Handled;
6625706b4fc4SDimitry Andric 
6626706b4fc4SDimitry Andric     case eMenuID_ViewRegisters: {
6627706b4fc4SDimitry Andric       WindowSP main_window_sp = m_app.GetMainWindow();
6628706b4fc4SDimitry Andric       WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
6629706b4fc4SDimitry Andric       WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
6630706b4fc4SDimitry Andric       WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
6631706b4fc4SDimitry Andric       const Rect source_bounds = source_window_sp->GetBounds();
6632706b4fc4SDimitry Andric 
6633706b4fc4SDimitry Andric       if (registers_window_sp) {
6634706b4fc4SDimitry Andric         if (variables_window_sp) {
6635706b4fc4SDimitry Andric           const Rect variables_bounds = variables_window_sp->GetBounds();
6636706b4fc4SDimitry Andric 
6637706b4fc4SDimitry Andric           // We have a variables window, so give all the area back to the
6638706b4fc4SDimitry Andric           // variables window
6639706b4fc4SDimitry Andric           variables_window_sp->Resize(variables_bounds.size.width +
6640706b4fc4SDimitry Andric                                           registers_window_sp->GetWidth(),
6641706b4fc4SDimitry Andric                                       variables_bounds.size.height);
6642706b4fc4SDimitry Andric         } else {
6643706b4fc4SDimitry Andric           // We have no variables window showing so give the bottom area back
6644706b4fc4SDimitry Andric           // to the source view
6645706b4fc4SDimitry Andric           source_window_sp->Resize(source_bounds.size.width,
6646706b4fc4SDimitry Andric                                    source_bounds.size.height +
6647706b4fc4SDimitry Andric                                        registers_window_sp->GetHeight());
6648706b4fc4SDimitry Andric         }
6649706b4fc4SDimitry Andric         main_window_sp->RemoveSubWindow(registers_window_sp.get());
6650706b4fc4SDimitry Andric       } else {
6651706b4fc4SDimitry Andric         Rect new_regs_rect;
6652706b4fc4SDimitry Andric         if (variables_window_sp) {
6653706b4fc4SDimitry Andric           // We have a variables window, split it into two columns where the
6654706b4fc4SDimitry Andric           // left hand side will be the variables and the right hand side will
6655706b4fc4SDimitry Andric           // be the registers
6656706b4fc4SDimitry Andric           const Rect variables_bounds = variables_window_sp->GetBounds();
6657706b4fc4SDimitry Andric           Rect new_vars_rect;
6658706b4fc4SDimitry Andric           variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
6659706b4fc4SDimitry Andric                                                    new_regs_rect);
6660706b4fc4SDimitry Andric           variables_window_sp->SetBounds(new_vars_rect);
6661706b4fc4SDimitry Andric         } else {
6662b60736ecSDimitry Andric           // No variables window, grab the bottom part of the source window
6663706b4fc4SDimitry Andric           Rect new_source_rect;
6664706b4fc4SDimitry Andric           source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
6665706b4fc4SDimitry Andric                                                   new_regs_rect);
6666706b4fc4SDimitry Andric           source_window_sp->SetBounds(new_source_rect);
6667706b4fc4SDimitry Andric         }
6668706b4fc4SDimitry Andric         WindowSP new_window_sp =
6669706b4fc4SDimitry Andric             main_window_sp->CreateSubWindow("Registers", new_regs_rect, false);
6670706b4fc4SDimitry Andric         new_window_sp->SetDelegate(
6671706b4fc4SDimitry Andric             WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
6672706b4fc4SDimitry Andric       }
6673706b4fc4SDimitry Andric       touchwin(stdscr);
6674706b4fc4SDimitry Andric     }
6675706b4fc4SDimitry Andric       return MenuActionResult::Handled;
6676706b4fc4SDimitry Andric 
6677c0981da4SDimitry Andric     case eMenuID_ViewBreakpoints: {
6678c0981da4SDimitry Andric       WindowSP main_window_sp = m_app.GetMainWindow();
6679c0981da4SDimitry Andric       WindowSP threads_window_sp = main_window_sp->FindSubWindow("Threads");
6680c0981da4SDimitry Andric       WindowSP breakpoints_window_sp =
6681c0981da4SDimitry Andric           main_window_sp->FindSubWindow("Breakpoints");
6682c0981da4SDimitry Andric       const Rect threads_bounds = threads_window_sp->GetBounds();
6683c0981da4SDimitry Andric 
6684c0981da4SDimitry Andric       // If a breakpoints window already exists, remove it and give the area
6685c0981da4SDimitry Andric       // it used to occupy to the threads window. If it doesn't exist, split
6686c0981da4SDimitry Andric       // the threads window horizontally into two windows where the top window
6687c0981da4SDimitry Andric       // is the threads window and the bottom window is a newly added
6688c0981da4SDimitry Andric       // breakpoints window.
6689c0981da4SDimitry Andric       if (breakpoints_window_sp) {
6690c0981da4SDimitry Andric         threads_window_sp->Resize(threads_bounds.size.width,
6691c0981da4SDimitry Andric                                   threads_bounds.size.height +
6692c0981da4SDimitry Andric                                       breakpoints_window_sp->GetHeight());
6693c0981da4SDimitry Andric         main_window_sp->RemoveSubWindow(breakpoints_window_sp.get());
6694c0981da4SDimitry Andric       } else {
6695c0981da4SDimitry Andric         Rect new_threads_bounds, breakpoints_bounds;
6696c0981da4SDimitry Andric         threads_bounds.HorizontalSplitPercentage(0.70, new_threads_bounds,
6697c0981da4SDimitry Andric                                                  breakpoints_bounds);
6698c0981da4SDimitry Andric         threads_window_sp->SetBounds(new_threads_bounds);
6699c0981da4SDimitry Andric         breakpoints_window_sp = main_window_sp->CreateSubWindow(
6700c0981da4SDimitry Andric             "Breakpoints", breakpoints_bounds, false);
6701c0981da4SDimitry Andric         TreeDelegateSP breakpoints_delegate_sp(
6702c0981da4SDimitry Andric             new BreakpointsTreeDelegate(m_debugger));
6703c0981da4SDimitry Andric         breakpoints_window_sp->SetDelegate(WindowDelegateSP(
6704c0981da4SDimitry Andric             new TreeWindowDelegate(m_debugger, breakpoints_delegate_sp)));
6705c0981da4SDimitry Andric       }
6706c0981da4SDimitry Andric       touchwin(stdscr);
6707c0981da4SDimitry Andric       return MenuActionResult::Handled;
6708c0981da4SDimitry Andric     }
6709c0981da4SDimitry Andric 
6710706b4fc4SDimitry Andric     case eMenuID_HelpGUIHelp:
6711706b4fc4SDimitry Andric       m_app.GetMainWindow()->CreateHelpSubwindow();
6712706b4fc4SDimitry Andric       return MenuActionResult::Handled;
6713706b4fc4SDimitry Andric 
6714706b4fc4SDimitry Andric     default:
6715706b4fc4SDimitry Andric       break;
6716706b4fc4SDimitry Andric     }
6717706b4fc4SDimitry Andric 
6718706b4fc4SDimitry Andric     return MenuActionResult::NotHandled;
6719706b4fc4SDimitry Andric   }
6720706b4fc4SDimitry Andric 
6721706b4fc4SDimitry Andric protected:
6722706b4fc4SDimitry Andric   Application &m_app;
6723706b4fc4SDimitry Andric   Debugger &m_debugger;
6724706b4fc4SDimitry Andric };
6725706b4fc4SDimitry Andric 
6726706b4fc4SDimitry Andric class StatusBarWindowDelegate : public WindowDelegate {
6727706b4fc4SDimitry Andric public:
StatusBarWindowDelegate(Debugger & debugger)6728706b4fc4SDimitry Andric   StatusBarWindowDelegate(Debugger &debugger) : m_debugger(debugger) {
6729706b4fc4SDimitry Andric     FormatEntity::Parse("Thread: ${thread.id%tid}", m_format);
6730706b4fc4SDimitry Andric   }
6731706b4fc4SDimitry Andric 
6732706b4fc4SDimitry Andric   ~StatusBarWindowDelegate() override = default;
6733706b4fc4SDimitry Andric 
WindowDelegateDraw(Window & window,bool force)6734706b4fc4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
6735706b4fc4SDimitry Andric     ExecutionContext exe_ctx =
6736706b4fc4SDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext();
6737706b4fc4SDimitry Andric     Process *process = exe_ctx.GetProcessPtr();
6738706b4fc4SDimitry Andric     Thread *thread = exe_ctx.GetThreadPtr();
6739706b4fc4SDimitry Andric     StackFrame *frame = exe_ctx.GetFramePtr();
6740706b4fc4SDimitry Andric     window.Erase();
6741b60736ecSDimitry Andric     window.SetBackground(BlackOnWhite);
6742706b4fc4SDimitry Andric     window.MoveCursor(0, 0);
6743706b4fc4SDimitry Andric     if (process) {
6744706b4fc4SDimitry Andric       const StateType state = process->GetState();
6745706b4fc4SDimitry Andric       window.Printf("Process: %5" PRIu64 " %10s", process->GetID(),
6746706b4fc4SDimitry Andric                     StateAsCString(state));
6747706b4fc4SDimitry Andric 
6748706b4fc4SDimitry Andric       if (StateIsStoppedState(state, true)) {
6749706b4fc4SDimitry Andric         StreamString strm;
6750706b4fc4SDimitry Andric         if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx,
6751706b4fc4SDimitry Andric                                            nullptr, nullptr, false, false)) {
6752706b4fc4SDimitry Andric           window.MoveCursor(40, 0);
6753b60736ecSDimitry Andric           window.PutCStringTruncated(1, strm.GetString().str().c_str());
6754706b4fc4SDimitry Andric         }
6755706b4fc4SDimitry Andric 
6756706b4fc4SDimitry Andric         window.MoveCursor(60, 0);
6757706b4fc4SDimitry Andric         if (frame)
6758706b4fc4SDimitry Andric           window.Printf("Frame: %3u  PC = 0x%16.16" PRIx64,
6759706b4fc4SDimitry Andric                         frame->GetFrameIndex(),
6760706b4fc4SDimitry Andric                         frame->GetFrameCodeAddress().GetOpcodeLoadAddress(
6761706b4fc4SDimitry Andric                             exe_ctx.GetTargetPtr()));
6762706b4fc4SDimitry Andric       } else if (state == eStateExited) {
6763706b4fc4SDimitry Andric         const char *exit_desc = process->GetExitDescription();
6764706b4fc4SDimitry Andric         const int exit_status = process->GetExitStatus();
6765706b4fc4SDimitry Andric         if (exit_desc && exit_desc[0])
6766706b4fc4SDimitry Andric           window.Printf(" with status = %i (%s)", exit_status, exit_desc);
6767706b4fc4SDimitry Andric         else
6768706b4fc4SDimitry Andric           window.Printf(" with status = %i", exit_status);
6769706b4fc4SDimitry Andric       }
6770706b4fc4SDimitry Andric     }
6771706b4fc4SDimitry Andric     return true;
6772706b4fc4SDimitry Andric   }
6773706b4fc4SDimitry Andric 
6774706b4fc4SDimitry Andric protected:
6775706b4fc4SDimitry Andric   Debugger &m_debugger;
6776706b4fc4SDimitry Andric   FormatEntity::Entry m_format;
6777706b4fc4SDimitry Andric };
6778706b4fc4SDimitry Andric 
6779706b4fc4SDimitry Andric class SourceFileWindowDelegate : public WindowDelegate {
6780706b4fc4SDimitry Andric public:
SourceFileWindowDelegate(Debugger & debugger)6781706b4fc4SDimitry Andric   SourceFileWindowDelegate(Debugger &debugger)
6782706b4fc4SDimitry Andric       : WindowDelegate(), m_debugger(debugger), m_sc(), m_file_sp(),
6783145449b1SDimitry Andric         m_disassembly_sp(), m_disassembly_range(), m_title() {}
6784706b4fc4SDimitry Andric 
6785706b4fc4SDimitry Andric   ~SourceFileWindowDelegate() override = default;
6786706b4fc4SDimitry Andric 
Update(const SymbolContext & sc)6787706b4fc4SDimitry Andric   void Update(const SymbolContext &sc) { m_sc = sc; }
6788706b4fc4SDimitry Andric 
NumVisibleLines() const6789706b4fc4SDimitry Andric   uint32_t NumVisibleLines() const { return m_max_y - m_min_y; }
6790706b4fc4SDimitry Andric 
WindowDelegateGetHelpText()6791706b4fc4SDimitry Andric   const char *WindowDelegateGetHelpText() override {
6792706b4fc4SDimitry Andric     return "Source/Disassembly window keyboard shortcuts:";
6793706b4fc4SDimitry Andric   }
6794706b4fc4SDimitry Andric 
WindowDelegateGetKeyHelp()6795706b4fc4SDimitry Andric   KeyHelp *WindowDelegateGetKeyHelp() override {
6796706b4fc4SDimitry Andric     static curses::KeyHelp g_source_view_key_help[] = {
6797706b4fc4SDimitry Andric         {KEY_RETURN, "Run to selected line with one shot breakpoint"},
6798706b4fc4SDimitry Andric         {KEY_UP, "Select previous source line"},
6799706b4fc4SDimitry Andric         {KEY_DOWN, "Select next source line"},
6800b60736ecSDimitry Andric         {KEY_LEFT, "Scroll to the left"},
6801b60736ecSDimitry Andric         {KEY_RIGHT, "Scroll to the right"},
6802706b4fc4SDimitry Andric         {KEY_PPAGE, "Page up"},
6803706b4fc4SDimitry Andric         {KEY_NPAGE, "Page down"},
6804706b4fc4SDimitry Andric         {'b', "Set breakpoint on selected source/disassembly line"},
6805706b4fc4SDimitry Andric         {'c', "Continue process"},
6806706b4fc4SDimitry Andric         {'D', "Detach with process suspended"},
6807706b4fc4SDimitry Andric         {'h', "Show help dialog"},
6808706b4fc4SDimitry Andric         {'n', "Step over (source line)"},
6809706b4fc4SDimitry Andric         {'N', "Step over (single instruction)"},
6810b60736ecSDimitry Andric         {'f', "Step out (finish)"},
6811706b4fc4SDimitry Andric         {'s', "Step in (source line)"},
6812706b4fc4SDimitry Andric         {'S', "Step in (single instruction)"},
6813b60736ecSDimitry Andric         {'u', "Frame up"},
6814b60736ecSDimitry Andric         {'d', "Frame down"},
6815706b4fc4SDimitry Andric         {',', "Page up"},
6816706b4fc4SDimitry Andric         {'.', "Page down"},
6817706b4fc4SDimitry Andric         {'\0', nullptr}};
6818706b4fc4SDimitry Andric     return g_source_view_key_help;
6819706b4fc4SDimitry Andric   }
6820706b4fc4SDimitry Andric 
WindowDelegateDraw(Window & window,bool force)6821706b4fc4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
6822706b4fc4SDimitry Andric     ExecutionContext exe_ctx =
6823706b4fc4SDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext();
6824706b4fc4SDimitry Andric     Process *process = exe_ctx.GetProcessPtr();
6825706b4fc4SDimitry Andric     Thread *thread = nullptr;
6826706b4fc4SDimitry Andric 
6827706b4fc4SDimitry Andric     bool update_location = false;
6828706b4fc4SDimitry Andric     if (process) {
6829706b4fc4SDimitry Andric       StateType state = process->GetState();
6830706b4fc4SDimitry Andric       if (StateIsStoppedState(state, true)) {
6831706b4fc4SDimitry Andric         // We are stopped, so it is ok to
6832706b4fc4SDimitry Andric         update_location = true;
6833706b4fc4SDimitry Andric       }
6834706b4fc4SDimitry Andric     }
6835706b4fc4SDimitry Andric 
6836706b4fc4SDimitry Andric     m_min_x = 1;
6837706b4fc4SDimitry Andric     m_min_y = 2;
6838706b4fc4SDimitry Andric     m_max_x = window.GetMaxX() - 1;
6839706b4fc4SDimitry Andric     m_max_y = window.GetMaxY() - 1;
6840706b4fc4SDimitry Andric 
6841706b4fc4SDimitry Andric     const uint32_t num_visible_lines = NumVisibleLines();
6842706b4fc4SDimitry Andric     StackFrameSP frame_sp;
6843706b4fc4SDimitry Andric     bool set_selected_line_to_pc = false;
6844706b4fc4SDimitry Andric 
6845706b4fc4SDimitry Andric     if (update_location) {
6846e3b55780SDimitry Andric       const bool process_alive = process->IsAlive();
6847706b4fc4SDimitry Andric       bool thread_changed = false;
6848706b4fc4SDimitry Andric       if (process_alive) {
6849706b4fc4SDimitry Andric         thread = exe_ctx.GetThreadPtr();
6850706b4fc4SDimitry Andric         if (thread) {
68517fa27ce4SDimitry Andric           frame_sp = thread->GetSelectedFrame(SelectMostRelevantFrame);
6852706b4fc4SDimitry Andric           auto tid = thread->GetID();
6853706b4fc4SDimitry Andric           thread_changed = tid != m_tid;
6854706b4fc4SDimitry Andric           m_tid = tid;
6855706b4fc4SDimitry Andric         } else {
6856706b4fc4SDimitry Andric           if (m_tid != LLDB_INVALID_THREAD_ID) {
6857706b4fc4SDimitry Andric             thread_changed = true;
6858706b4fc4SDimitry Andric             m_tid = LLDB_INVALID_THREAD_ID;
6859706b4fc4SDimitry Andric           }
6860706b4fc4SDimitry Andric         }
6861706b4fc4SDimitry Andric       }
6862706b4fc4SDimitry Andric       const uint32_t stop_id = process ? process->GetStopID() : 0;
6863706b4fc4SDimitry Andric       const bool stop_id_changed = stop_id != m_stop_id;
6864706b4fc4SDimitry Andric       bool frame_changed = false;
6865706b4fc4SDimitry Andric       m_stop_id = stop_id;
6866706b4fc4SDimitry Andric       m_title.Clear();
6867706b4fc4SDimitry Andric       if (frame_sp) {
6868706b4fc4SDimitry Andric         m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
6869706b4fc4SDimitry Andric         if (m_sc.module_sp) {
6870706b4fc4SDimitry Andric           m_title.Printf(
6871706b4fc4SDimitry Andric               "%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
6872706b4fc4SDimitry Andric           ConstString func_name = m_sc.GetFunctionName();
6873706b4fc4SDimitry Andric           if (func_name)
6874706b4fc4SDimitry Andric             m_title.Printf("`%s", func_name.GetCString());
6875706b4fc4SDimitry Andric         }
6876706b4fc4SDimitry Andric         const uint32_t frame_idx = frame_sp->GetFrameIndex();
6877706b4fc4SDimitry Andric         frame_changed = frame_idx != m_frame_idx;
6878706b4fc4SDimitry Andric         m_frame_idx = frame_idx;
6879706b4fc4SDimitry Andric       } else {
6880706b4fc4SDimitry Andric         m_sc.Clear(true);
6881706b4fc4SDimitry Andric         frame_changed = m_frame_idx != UINT32_MAX;
6882706b4fc4SDimitry Andric         m_frame_idx = UINT32_MAX;
6883706b4fc4SDimitry Andric       }
6884706b4fc4SDimitry Andric 
6885706b4fc4SDimitry Andric       const bool context_changed =
6886706b4fc4SDimitry Andric           thread_changed || frame_changed || stop_id_changed;
6887706b4fc4SDimitry Andric 
6888706b4fc4SDimitry Andric       if (process_alive) {
6889706b4fc4SDimitry Andric         if (m_sc.line_entry.IsValid()) {
6890706b4fc4SDimitry Andric           m_pc_line = m_sc.line_entry.line;
6891706b4fc4SDimitry Andric           if (m_pc_line != UINT32_MAX)
6892706b4fc4SDimitry Andric             --m_pc_line; // Convert to zero based line number...
6893706b4fc4SDimitry Andric           // Update the selected line if the stop ID changed...
6894706b4fc4SDimitry Andric           if (context_changed)
6895706b4fc4SDimitry Andric             m_selected_line = m_pc_line;
6896706b4fc4SDimitry Andric 
6897ac9a064cSDimitry Andric           if (m_file_sp &&
6898ac9a064cSDimitry Andric               m_file_sp->GetFileSpec() == m_sc.line_entry.GetFile()) {
6899c0981da4SDimitry Andric             // Same file, nothing to do, we should either have the lines or
6900c0981da4SDimitry Andric             // not (source file missing)
6901706b4fc4SDimitry Andric             if (m_selected_line >= static_cast<size_t>(m_first_visible_line)) {
6902706b4fc4SDimitry Andric               if (m_selected_line >= m_first_visible_line + num_visible_lines)
6903706b4fc4SDimitry Andric                 m_first_visible_line = m_selected_line - 10;
6904706b4fc4SDimitry Andric             } else {
6905706b4fc4SDimitry Andric               if (m_selected_line > 10)
6906706b4fc4SDimitry Andric                 m_first_visible_line = m_selected_line - 10;
6907706b4fc4SDimitry Andric               else
6908706b4fc4SDimitry Andric                 m_first_visible_line = 0;
6909706b4fc4SDimitry Andric             }
6910706b4fc4SDimitry Andric           } else {
6911706b4fc4SDimitry Andric             // File changed, set selected line to the line with the PC
6912706b4fc4SDimitry Andric             m_selected_line = m_pc_line;
6913ac9a064cSDimitry Andric             m_file_sp = m_debugger.GetSourceManager().GetFile(
6914ac9a064cSDimitry Andric                 m_sc.line_entry.GetFile());
6915706b4fc4SDimitry Andric             if (m_file_sp) {
6916706b4fc4SDimitry Andric               const size_t num_lines = m_file_sp->GetNumLines();
6917706b4fc4SDimitry Andric               m_line_width = 1;
6918706b4fc4SDimitry Andric               for (size_t n = num_lines; n >= 10; n = n / 10)
6919706b4fc4SDimitry Andric                 ++m_line_width;
6920706b4fc4SDimitry Andric 
6921706b4fc4SDimitry Andric               if (num_lines < num_visible_lines ||
6922706b4fc4SDimitry Andric                   m_selected_line < num_visible_lines)
6923706b4fc4SDimitry Andric                 m_first_visible_line = 0;
6924706b4fc4SDimitry Andric               else
6925706b4fc4SDimitry Andric                 m_first_visible_line = m_selected_line - 10;
6926706b4fc4SDimitry Andric             }
6927706b4fc4SDimitry Andric           }
6928706b4fc4SDimitry Andric         } else {
6929706b4fc4SDimitry Andric           m_file_sp.reset();
6930706b4fc4SDimitry Andric         }
6931706b4fc4SDimitry Andric 
6932706b4fc4SDimitry Andric         if (!m_file_sp || m_file_sp->GetNumLines() == 0) {
6933706b4fc4SDimitry Andric           // Show disassembly
6934706b4fc4SDimitry Andric           bool prefer_file_cache = false;
6935706b4fc4SDimitry Andric           if (m_sc.function) {
6936706b4fc4SDimitry Andric             if (m_disassembly_scope != m_sc.function) {
6937706b4fc4SDimitry Andric               m_disassembly_scope = m_sc.function;
6938706b4fc4SDimitry Andric               m_disassembly_sp = m_sc.function->GetInstructions(
6939344a3780SDimitry Andric                   exe_ctx, nullptr, !prefer_file_cache);
6940706b4fc4SDimitry Andric               if (m_disassembly_sp) {
6941706b4fc4SDimitry Andric                 set_selected_line_to_pc = true;
6942706b4fc4SDimitry Andric                 m_disassembly_range = m_sc.function->GetAddressRange();
6943706b4fc4SDimitry Andric               } else {
6944706b4fc4SDimitry Andric                 m_disassembly_range.Clear();
6945706b4fc4SDimitry Andric               }
6946706b4fc4SDimitry Andric             } else {
6947706b4fc4SDimitry Andric               set_selected_line_to_pc = context_changed;
6948706b4fc4SDimitry Andric             }
6949706b4fc4SDimitry Andric           } else if (m_sc.symbol) {
6950706b4fc4SDimitry Andric             if (m_disassembly_scope != m_sc.symbol) {
6951706b4fc4SDimitry Andric               m_disassembly_scope = m_sc.symbol;
6952706b4fc4SDimitry Andric               m_disassembly_sp = m_sc.symbol->GetInstructions(
6953706b4fc4SDimitry Andric                   exe_ctx, nullptr, prefer_file_cache);
6954706b4fc4SDimitry Andric               if (m_disassembly_sp) {
6955706b4fc4SDimitry Andric                 set_selected_line_to_pc = true;
6956706b4fc4SDimitry Andric                 m_disassembly_range.GetBaseAddress() =
6957706b4fc4SDimitry Andric                     m_sc.symbol->GetAddress();
6958706b4fc4SDimitry Andric                 m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
6959706b4fc4SDimitry Andric               } else {
6960706b4fc4SDimitry Andric                 m_disassembly_range.Clear();
6961706b4fc4SDimitry Andric               }
6962706b4fc4SDimitry Andric             } else {
6963706b4fc4SDimitry Andric               set_selected_line_to_pc = context_changed;
6964706b4fc4SDimitry Andric             }
6965706b4fc4SDimitry Andric           }
6966706b4fc4SDimitry Andric         }
6967706b4fc4SDimitry Andric       } else {
6968706b4fc4SDimitry Andric         m_pc_line = UINT32_MAX;
6969706b4fc4SDimitry Andric       }
6970706b4fc4SDimitry Andric     }
6971706b4fc4SDimitry Andric 
6972706b4fc4SDimitry Andric     const int window_width = window.GetWidth();
6973706b4fc4SDimitry Andric     window.Erase();
6974706b4fc4SDimitry Andric     window.DrawTitleBox("Sources");
6975706b4fc4SDimitry Andric     if (!m_title.GetString().empty()) {
6976706b4fc4SDimitry Andric       window.AttributeOn(A_REVERSE);
6977706b4fc4SDimitry Andric       window.MoveCursor(1, 1);
6978706b4fc4SDimitry Andric       window.PutChar(' ');
6979b60736ecSDimitry Andric       window.PutCStringTruncated(1, m_title.GetString().str().c_str());
6980706b4fc4SDimitry Andric       int x = window.GetCursorX();
6981706b4fc4SDimitry Andric       if (x < window_width - 1) {
6982706b4fc4SDimitry Andric         window.Printf("%*s", window_width - x - 1, "");
6983706b4fc4SDimitry Andric       }
6984706b4fc4SDimitry Andric       window.AttributeOff(A_REVERSE);
6985706b4fc4SDimitry Andric     }
6986706b4fc4SDimitry Andric 
6987706b4fc4SDimitry Andric     Target *target = exe_ctx.GetTargetPtr();
6988706b4fc4SDimitry Andric     const size_t num_source_lines = GetNumSourceLines();
6989706b4fc4SDimitry Andric     if (num_source_lines > 0) {
6990706b4fc4SDimitry Andric       // Display source
6991706b4fc4SDimitry Andric       BreakpointLines bp_lines;
6992706b4fc4SDimitry Andric       if (target) {
6993706b4fc4SDimitry Andric         BreakpointList &bp_list = target->GetBreakpointList();
6994706b4fc4SDimitry Andric         const size_t num_bps = bp_list.GetSize();
6995706b4fc4SDimitry Andric         for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
6996706b4fc4SDimitry Andric           BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
6997706b4fc4SDimitry Andric           const size_t num_bps_locs = bp_sp->GetNumLocations();
6998706b4fc4SDimitry Andric           for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
6999706b4fc4SDimitry Andric             BreakpointLocationSP bp_loc_sp =
7000706b4fc4SDimitry Andric                 bp_sp->GetLocationAtIndex(bp_loc_idx);
7001706b4fc4SDimitry Andric             LineEntry bp_loc_line_entry;
7002706b4fc4SDimitry Andric             if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
7003706b4fc4SDimitry Andric                     bp_loc_line_entry)) {
7004ac9a064cSDimitry Andric               if (m_file_sp->GetFileSpec() == bp_loc_line_entry.GetFile()) {
7005706b4fc4SDimitry Andric                 bp_lines.insert(bp_loc_line_entry.line);
7006706b4fc4SDimitry Andric               }
7007706b4fc4SDimitry Andric             }
7008706b4fc4SDimitry Andric           }
7009706b4fc4SDimitry Andric         }
7010706b4fc4SDimitry Andric       }
7011706b4fc4SDimitry Andric 
7012706b4fc4SDimitry Andric       for (size_t i = 0; i < num_visible_lines; ++i) {
7013706b4fc4SDimitry Andric         const uint32_t curr_line = m_first_visible_line + i;
7014706b4fc4SDimitry Andric         if (curr_line < num_source_lines) {
7015706b4fc4SDimitry Andric           const int line_y = m_min_y + i;
7016706b4fc4SDimitry Andric           window.MoveCursor(1, line_y);
7017706b4fc4SDimitry Andric           const bool is_pc_line = curr_line == m_pc_line;
7018706b4fc4SDimitry Andric           const bool line_is_selected = m_selected_line == curr_line;
7019145449b1SDimitry Andric           // Highlight the line as the PC line first (done by passing
7020145449b1SDimitry Andric           // argument to OutputColoredStringTruncated()), then if the selected
7021145449b1SDimitry Andric           // line isn't the same as the PC line, highlight it differently.
7022706b4fc4SDimitry Andric           attr_t highlight_attr = 0;
7023706b4fc4SDimitry Andric           attr_t bp_attr = 0;
7024145449b1SDimitry Andric           if (line_is_selected && !is_pc_line)
7025145449b1SDimitry Andric             highlight_attr = A_REVERSE;
7026706b4fc4SDimitry Andric 
7027706b4fc4SDimitry Andric           if (bp_lines.find(curr_line + 1) != bp_lines.end())
7028b60736ecSDimitry Andric             bp_attr = COLOR_PAIR(BlackOnWhite);
7029706b4fc4SDimitry Andric 
7030706b4fc4SDimitry Andric           if (bp_attr)
7031706b4fc4SDimitry Andric             window.AttributeOn(bp_attr);
7032706b4fc4SDimitry Andric 
7033706b4fc4SDimitry Andric           window.Printf(" %*u ", m_line_width, curr_line + 1);
7034706b4fc4SDimitry Andric 
7035706b4fc4SDimitry Andric           if (bp_attr)
7036706b4fc4SDimitry Andric             window.AttributeOff(bp_attr);
7037706b4fc4SDimitry Andric 
7038706b4fc4SDimitry Andric           window.PutChar(ACS_VLINE);
7039706b4fc4SDimitry Andric           // Mark the line with the PC with a diamond
7040706b4fc4SDimitry Andric           if (is_pc_line)
7041706b4fc4SDimitry Andric             window.PutChar(ACS_DIAMOND);
7042706b4fc4SDimitry Andric           else
7043706b4fc4SDimitry Andric             window.PutChar(' ');
7044706b4fc4SDimitry Andric 
7045706b4fc4SDimitry Andric           if (highlight_attr)
7046706b4fc4SDimitry Andric             window.AttributeOn(highlight_attr);
7047b60736ecSDimitry Andric 
7048b60736ecSDimitry Andric           StreamString lineStream;
7049145449b1SDimitry Andric 
7050e3b55780SDimitry Andric           std::optional<size_t> column;
7051145449b1SDimitry Andric           if (is_pc_line && m_sc.line_entry.IsValid() && m_sc.line_entry.column)
7052145449b1SDimitry Andric             column = m_sc.line_entry.column - 1;
7053145449b1SDimitry Andric           m_file_sp->DisplaySourceLines(curr_line + 1, column, 0, 0,
7054145449b1SDimitry Andric                                         &lineStream);
7055b60736ecSDimitry Andric           StringRef line = lineStream.GetString();
7056312c0ed1SDimitry Andric           if (line.ends_with("\n"))
7057b60736ecSDimitry Andric             line = line.drop_back();
7058b60736ecSDimitry Andric           bool wasWritten = window.OutputColoredStringTruncated(
7059145449b1SDimitry Andric               1, line, m_first_visible_column, is_pc_line);
7060145449b1SDimitry Andric           if (!wasWritten && (line_is_selected || is_pc_line)) {
7061145449b1SDimitry Andric             // Draw an empty space to show the selected/PC line if empty,
7062b60736ecSDimitry Andric             // or draw '<' if nothing is visible because of scrolling too much
7063b60736ecSDimitry Andric             // to the right.
7064b60736ecSDimitry Andric             window.PutCStringTruncated(
7065b60736ecSDimitry Andric                 1, line.empty() && m_first_visible_column == 0 ? " " : "<");
7066b60736ecSDimitry Andric           }
7067706b4fc4SDimitry Andric 
7068706b4fc4SDimitry Andric           if (is_pc_line && frame_sp &&
7069706b4fc4SDimitry Andric               frame_sp->GetConcreteFrameIndex() == 0) {
7070706b4fc4SDimitry Andric             StopInfoSP stop_info_sp;
7071706b4fc4SDimitry Andric             if (thread)
7072706b4fc4SDimitry Andric               stop_info_sp = thread->GetStopInfo();
7073706b4fc4SDimitry Andric             if (stop_info_sp) {
7074706b4fc4SDimitry Andric               const char *stop_description = stop_info_sp->GetDescription();
7075706b4fc4SDimitry Andric               if (stop_description && stop_description[0]) {
7076706b4fc4SDimitry Andric                 size_t stop_description_len = strlen(stop_description);
7077706b4fc4SDimitry Andric                 int desc_x = window_width - stop_description_len - 16;
7078b60736ecSDimitry Andric                 if (desc_x - window.GetCursorX() > 0)
7079706b4fc4SDimitry Andric                   window.Printf("%*s", desc_x - window.GetCursorX(), "");
7080b60736ecSDimitry Andric                 window.MoveCursor(window_width - stop_description_len - 16,
7081b60736ecSDimitry Andric                                   line_y);
7082b60736ecSDimitry Andric                 const attr_t stop_reason_attr = COLOR_PAIR(WhiteOnBlue);
7083b60736ecSDimitry Andric                 window.AttributeOn(stop_reason_attr);
7084b60736ecSDimitry Andric                 window.PrintfTruncated(1, " <<< Thread %u: %s ",
7085b60736ecSDimitry Andric                                        thread->GetIndexID(), stop_description);
7086b60736ecSDimitry Andric                 window.AttributeOff(stop_reason_attr);
7087706b4fc4SDimitry Andric               }
7088706b4fc4SDimitry Andric             } else {
7089706b4fc4SDimitry Andric               window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
7090706b4fc4SDimitry Andric             }
7091706b4fc4SDimitry Andric           }
7092706b4fc4SDimitry Andric           if (highlight_attr)
7093706b4fc4SDimitry Andric             window.AttributeOff(highlight_attr);
7094706b4fc4SDimitry Andric         } else {
7095706b4fc4SDimitry Andric           break;
7096706b4fc4SDimitry Andric         }
7097706b4fc4SDimitry Andric       }
7098706b4fc4SDimitry Andric     } else {
7099706b4fc4SDimitry Andric       size_t num_disassembly_lines = GetNumDisassemblyLines();
7100706b4fc4SDimitry Andric       if (num_disassembly_lines > 0) {
7101706b4fc4SDimitry Andric         // Display disassembly
7102706b4fc4SDimitry Andric         BreakpointAddrs bp_file_addrs;
7103706b4fc4SDimitry Andric         Target *target = exe_ctx.GetTargetPtr();
7104706b4fc4SDimitry Andric         if (target) {
7105706b4fc4SDimitry Andric           BreakpointList &bp_list = target->GetBreakpointList();
7106706b4fc4SDimitry Andric           const size_t num_bps = bp_list.GetSize();
7107706b4fc4SDimitry Andric           for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
7108706b4fc4SDimitry Andric             BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
7109706b4fc4SDimitry Andric             const size_t num_bps_locs = bp_sp->GetNumLocations();
7110706b4fc4SDimitry Andric             for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs;
7111706b4fc4SDimitry Andric                  ++bp_loc_idx) {
7112706b4fc4SDimitry Andric               BreakpointLocationSP bp_loc_sp =
7113706b4fc4SDimitry Andric                   bp_sp->GetLocationAtIndex(bp_loc_idx);
7114706b4fc4SDimitry Andric               LineEntry bp_loc_line_entry;
7115706b4fc4SDimitry Andric               const lldb::addr_t file_addr =
7116706b4fc4SDimitry Andric                   bp_loc_sp->GetAddress().GetFileAddress();
7117706b4fc4SDimitry Andric               if (file_addr != LLDB_INVALID_ADDRESS) {
7118706b4fc4SDimitry Andric                 if (m_disassembly_range.ContainsFileAddress(file_addr))
7119706b4fc4SDimitry Andric                   bp_file_addrs.insert(file_addr);
7120706b4fc4SDimitry Andric               }
7121706b4fc4SDimitry Andric             }
7122706b4fc4SDimitry Andric           }
7123706b4fc4SDimitry Andric         }
7124706b4fc4SDimitry Andric 
7125706b4fc4SDimitry Andric         const attr_t selected_highlight_attr = A_REVERSE;
7126b60736ecSDimitry Andric         const attr_t pc_highlight_attr = COLOR_PAIR(WhiteOnBlue);
7127706b4fc4SDimitry Andric 
7128706b4fc4SDimitry Andric         StreamString strm;
7129706b4fc4SDimitry Andric 
7130706b4fc4SDimitry Andric         InstructionList &insts = m_disassembly_sp->GetInstructionList();
7131706b4fc4SDimitry Andric         Address pc_address;
7132706b4fc4SDimitry Andric 
7133706b4fc4SDimitry Andric         if (frame_sp)
7134706b4fc4SDimitry Andric           pc_address = frame_sp->GetFrameCodeAddress();
7135706b4fc4SDimitry Andric         const uint32_t pc_idx =
7136706b4fc4SDimitry Andric             pc_address.IsValid()
7137706b4fc4SDimitry Andric                 ? insts.GetIndexOfInstructionAtAddress(pc_address)
7138706b4fc4SDimitry Andric                 : UINT32_MAX;
7139706b4fc4SDimitry Andric         if (set_selected_line_to_pc) {
7140706b4fc4SDimitry Andric           m_selected_line = pc_idx;
7141706b4fc4SDimitry Andric         }
7142706b4fc4SDimitry Andric 
7143706b4fc4SDimitry Andric         const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
7144706b4fc4SDimitry Andric         if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
7145706b4fc4SDimitry Andric           m_first_visible_line = 0;
7146706b4fc4SDimitry Andric 
7147706b4fc4SDimitry Andric         if (pc_idx < num_disassembly_lines) {
7148706b4fc4SDimitry Andric           if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
7149706b4fc4SDimitry Andric               pc_idx >= m_first_visible_line + num_visible_lines)
7150706b4fc4SDimitry Andric             m_first_visible_line = pc_idx - non_visible_pc_offset;
7151706b4fc4SDimitry Andric         }
7152706b4fc4SDimitry Andric 
7153706b4fc4SDimitry Andric         for (size_t i = 0; i < num_visible_lines; ++i) {
7154706b4fc4SDimitry Andric           const uint32_t inst_idx = m_first_visible_line + i;
7155706b4fc4SDimitry Andric           Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
7156706b4fc4SDimitry Andric           if (!inst)
7157706b4fc4SDimitry Andric             break;
7158706b4fc4SDimitry Andric 
7159706b4fc4SDimitry Andric           const int line_y = m_min_y + i;
7160706b4fc4SDimitry Andric           window.MoveCursor(1, line_y);
7161706b4fc4SDimitry Andric           const bool is_pc_line = frame_sp && inst_idx == pc_idx;
7162706b4fc4SDimitry Andric           const bool line_is_selected = m_selected_line == inst_idx;
7163c0981da4SDimitry Andric           // Highlight the line as the PC line first, then if the selected
7164c0981da4SDimitry Andric           // line isn't the same as the PC line, highlight it differently
7165706b4fc4SDimitry Andric           attr_t highlight_attr = 0;
7166706b4fc4SDimitry Andric           attr_t bp_attr = 0;
7167706b4fc4SDimitry Andric           if (is_pc_line)
7168706b4fc4SDimitry Andric             highlight_attr = pc_highlight_attr;
7169706b4fc4SDimitry Andric           else if (line_is_selected)
7170706b4fc4SDimitry Andric             highlight_attr = selected_highlight_attr;
7171706b4fc4SDimitry Andric 
7172706b4fc4SDimitry Andric           if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) !=
7173706b4fc4SDimitry Andric               bp_file_addrs.end())
7174b60736ecSDimitry Andric             bp_attr = COLOR_PAIR(BlackOnWhite);
7175706b4fc4SDimitry Andric 
7176706b4fc4SDimitry Andric           if (bp_attr)
7177706b4fc4SDimitry Andric             window.AttributeOn(bp_attr);
7178706b4fc4SDimitry Andric 
7179706b4fc4SDimitry Andric           window.Printf(" 0x%16.16llx ",
7180706b4fc4SDimitry Andric                         static_cast<unsigned long long>(
7181706b4fc4SDimitry Andric                             inst->GetAddress().GetLoadAddress(target)));
7182706b4fc4SDimitry Andric 
7183706b4fc4SDimitry Andric           if (bp_attr)
7184706b4fc4SDimitry Andric             window.AttributeOff(bp_attr);
7185706b4fc4SDimitry Andric 
7186706b4fc4SDimitry Andric           window.PutChar(ACS_VLINE);
7187706b4fc4SDimitry Andric           // Mark the line with the PC with a diamond
7188706b4fc4SDimitry Andric           if (is_pc_line)
7189706b4fc4SDimitry Andric             window.PutChar(ACS_DIAMOND);
7190706b4fc4SDimitry Andric           else
7191706b4fc4SDimitry Andric             window.PutChar(' ');
7192706b4fc4SDimitry Andric 
7193706b4fc4SDimitry Andric           if (highlight_attr)
7194706b4fc4SDimitry Andric             window.AttributeOn(highlight_attr);
7195706b4fc4SDimitry Andric 
7196706b4fc4SDimitry Andric           const char *mnemonic = inst->GetMnemonic(&exe_ctx);
7197706b4fc4SDimitry Andric           const char *operands = inst->GetOperands(&exe_ctx);
7198706b4fc4SDimitry Andric           const char *comment = inst->GetComment(&exe_ctx);
7199706b4fc4SDimitry Andric 
7200706b4fc4SDimitry Andric           if (mnemonic != nullptr && mnemonic[0] == '\0')
7201706b4fc4SDimitry Andric             mnemonic = nullptr;
7202706b4fc4SDimitry Andric           if (operands != nullptr && operands[0] == '\0')
7203706b4fc4SDimitry Andric             operands = nullptr;
7204706b4fc4SDimitry Andric           if (comment != nullptr && comment[0] == '\0')
7205706b4fc4SDimitry Andric             comment = nullptr;
7206706b4fc4SDimitry Andric 
7207706b4fc4SDimitry Andric           strm.Clear();
7208706b4fc4SDimitry Andric 
7209706b4fc4SDimitry Andric           if (mnemonic != nullptr && operands != nullptr && comment != nullptr)
7210706b4fc4SDimitry Andric             strm.Printf("%-8s %-25s ; %s", mnemonic, operands, comment);
7211706b4fc4SDimitry Andric           else if (mnemonic != nullptr && operands != nullptr)
7212706b4fc4SDimitry Andric             strm.Printf("%-8s %s", mnemonic, operands);
7213706b4fc4SDimitry Andric           else if (mnemonic != nullptr)
7214706b4fc4SDimitry Andric             strm.Printf("%s", mnemonic);
7215706b4fc4SDimitry Andric 
7216706b4fc4SDimitry Andric           int right_pad = 1;
7217b60736ecSDimitry Andric           window.PutCStringTruncated(
7218b60736ecSDimitry Andric               right_pad,
7219b60736ecSDimitry Andric               strm.GetString().substr(m_first_visible_column).data());
7220706b4fc4SDimitry Andric 
7221706b4fc4SDimitry Andric           if (is_pc_line && frame_sp &&
7222706b4fc4SDimitry Andric               frame_sp->GetConcreteFrameIndex() == 0) {
7223706b4fc4SDimitry Andric             StopInfoSP stop_info_sp;
7224706b4fc4SDimitry Andric             if (thread)
7225706b4fc4SDimitry Andric               stop_info_sp = thread->GetStopInfo();
7226706b4fc4SDimitry Andric             if (stop_info_sp) {
7227706b4fc4SDimitry Andric               const char *stop_description = stop_info_sp->GetDescription();
7228706b4fc4SDimitry Andric               if (stop_description && stop_description[0]) {
7229706b4fc4SDimitry Andric                 size_t stop_description_len = strlen(stop_description);
7230706b4fc4SDimitry Andric                 int desc_x = window_width - stop_description_len - 16;
7231b60736ecSDimitry Andric                 if (desc_x - window.GetCursorX() > 0)
7232706b4fc4SDimitry Andric                   window.Printf("%*s", desc_x - window.GetCursorX(), "");
7233b60736ecSDimitry Andric                 window.MoveCursor(window_width - stop_description_len - 15,
7234b60736ecSDimitry Andric                                   line_y);
7235e3b55780SDimitry Andric                 if (thread)
7236b60736ecSDimitry Andric                   window.PrintfTruncated(1, "<<< Thread %u: %s ",
7237e3b55780SDimitry Andric                                          thread->GetIndexID(),
7238e3b55780SDimitry Andric                                          stop_description);
7239706b4fc4SDimitry Andric               }
7240706b4fc4SDimitry Andric             } else {
7241706b4fc4SDimitry Andric               window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
7242706b4fc4SDimitry Andric             }
7243706b4fc4SDimitry Andric           }
7244706b4fc4SDimitry Andric           if (highlight_attr)
7245706b4fc4SDimitry Andric             window.AttributeOff(highlight_attr);
7246706b4fc4SDimitry Andric         }
7247706b4fc4SDimitry Andric       }
7248706b4fc4SDimitry Andric     }
7249706b4fc4SDimitry Andric     return true; // Drawing handled
7250706b4fc4SDimitry Andric   }
7251706b4fc4SDimitry Andric 
GetNumLines()7252706b4fc4SDimitry Andric   size_t GetNumLines() {
7253706b4fc4SDimitry Andric     size_t num_lines = GetNumSourceLines();
7254706b4fc4SDimitry Andric     if (num_lines == 0)
7255706b4fc4SDimitry Andric       num_lines = GetNumDisassemblyLines();
7256706b4fc4SDimitry Andric     return num_lines;
7257706b4fc4SDimitry Andric   }
7258706b4fc4SDimitry Andric 
GetNumSourceLines() const7259706b4fc4SDimitry Andric   size_t GetNumSourceLines() const {
7260706b4fc4SDimitry Andric     if (m_file_sp)
7261706b4fc4SDimitry Andric       return m_file_sp->GetNumLines();
7262706b4fc4SDimitry Andric     return 0;
7263706b4fc4SDimitry Andric   }
7264706b4fc4SDimitry Andric 
GetNumDisassemblyLines() const7265706b4fc4SDimitry Andric   size_t GetNumDisassemblyLines() const {
7266706b4fc4SDimitry Andric     if (m_disassembly_sp)
7267706b4fc4SDimitry Andric       return m_disassembly_sp->GetInstructionList().GetSize();
7268706b4fc4SDimitry Andric     return 0;
7269706b4fc4SDimitry Andric   }
7270706b4fc4SDimitry Andric 
WindowDelegateHandleChar(Window & window,int c)7271706b4fc4SDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
7272706b4fc4SDimitry Andric     const uint32_t num_visible_lines = NumVisibleLines();
7273706b4fc4SDimitry Andric     const size_t num_lines = GetNumLines();
7274706b4fc4SDimitry Andric 
7275706b4fc4SDimitry Andric     switch (c) {
7276706b4fc4SDimitry Andric     case ',':
7277706b4fc4SDimitry Andric     case KEY_PPAGE:
7278706b4fc4SDimitry Andric       // Page up key
7279706b4fc4SDimitry Andric       if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
7280706b4fc4SDimitry Andric         m_first_visible_line -= num_visible_lines;
7281706b4fc4SDimitry Andric       else
7282706b4fc4SDimitry Andric         m_first_visible_line = 0;
7283706b4fc4SDimitry Andric       m_selected_line = m_first_visible_line;
7284706b4fc4SDimitry Andric       return eKeyHandled;
7285706b4fc4SDimitry Andric 
7286706b4fc4SDimitry Andric     case '.':
7287706b4fc4SDimitry Andric     case KEY_NPAGE:
7288706b4fc4SDimitry Andric       // Page down key
7289706b4fc4SDimitry Andric       {
7290706b4fc4SDimitry Andric         if (m_first_visible_line + num_visible_lines < num_lines)
7291706b4fc4SDimitry Andric           m_first_visible_line += num_visible_lines;
7292706b4fc4SDimitry Andric         else if (num_lines < num_visible_lines)
7293706b4fc4SDimitry Andric           m_first_visible_line = 0;
7294706b4fc4SDimitry Andric         else
7295706b4fc4SDimitry Andric           m_first_visible_line = num_lines - num_visible_lines;
7296706b4fc4SDimitry Andric         m_selected_line = m_first_visible_line;
7297706b4fc4SDimitry Andric       }
7298706b4fc4SDimitry Andric       return eKeyHandled;
7299706b4fc4SDimitry Andric 
7300706b4fc4SDimitry Andric     case KEY_UP:
7301706b4fc4SDimitry Andric       if (m_selected_line > 0) {
7302706b4fc4SDimitry Andric         m_selected_line--;
7303706b4fc4SDimitry Andric         if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
7304706b4fc4SDimitry Andric           m_first_visible_line = m_selected_line;
7305706b4fc4SDimitry Andric       }
7306706b4fc4SDimitry Andric       return eKeyHandled;
7307706b4fc4SDimitry Andric 
7308706b4fc4SDimitry Andric     case KEY_DOWN:
7309706b4fc4SDimitry Andric       if (m_selected_line + 1 < num_lines) {
7310706b4fc4SDimitry Andric         m_selected_line++;
7311706b4fc4SDimitry Andric         if (m_first_visible_line + num_visible_lines < m_selected_line)
7312706b4fc4SDimitry Andric           m_first_visible_line++;
7313706b4fc4SDimitry Andric       }
7314706b4fc4SDimitry Andric       return eKeyHandled;
7315706b4fc4SDimitry Andric 
7316b60736ecSDimitry Andric     case KEY_LEFT:
7317b60736ecSDimitry Andric       if (m_first_visible_column > 0)
7318b60736ecSDimitry Andric         --m_first_visible_column;
7319b60736ecSDimitry Andric       return eKeyHandled;
7320b60736ecSDimitry Andric 
7321b60736ecSDimitry Andric     case KEY_RIGHT:
7322b60736ecSDimitry Andric       ++m_first_visible_column;
7323b60736ecSDimitry Andric       return eKeyHandled;
7324b60736ecSDimitry Andric 
7325706b4fc4SDimitry Andric     case '\r':
7326706b4fc4SDimitry Andric     case '\n':
7327706b4fc4SDimitry Andric     case KEY_ENTER:
7328706b4fc4SDimitry Andric       // Set a breakpoint and run to the line using a one shot breakpoint
7329706b4fc4SDimitry Andric       if (GetNumSourceLines() > 0) {
7330706b4fc4SDimitry Andric         ExecutionContext exe_ctx =
7331706b4fc4SDimitry Andric             m_debugger.GetCommandInterpreter().GetExecutionContext();
7332706b4fc4SDimitry Andric         if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) {
7333706b4fc4SDimitry Andric           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
7334706b4fc4SDimitry Andric               nullptr, // Don't limit the breakpoint to certain modules
7335706b4fc4SDimitry Andric               m_file_sp->GetFileSpec(), // Source file
7336706b4fc4SDimitry Andric               m_selected_line +
7337706b4fc4SDimitry Andric                   1, // Source line number (m_selected_line is zero based)
7338706b4fc4SDimitry Andric               0,     // Unspecified column.
7339706b4fc4SDimitry Andric               0,     // No offset
7340706b4fc4SDimitry Andric               eLazyBoolCalculate,  // Check inlines using global setting
7341706b4fc4SDimitry Andric               eLazyBoolCalculate,  // Skip prologue using global setting,
7342706b4fc4SDimitry Andric               false,               // internal
7343706b4fc4SDimitry Andric               false,               // request_hardware
7344706b4fc4SDimitry Andric               eLazyBoolCalculate); // move_to_nearest_code
7345706b4fc4SDimitry Andric           // Make breakpoint one shot
7346344a3780SDimitry Andric           bp_sp->GetOptions().SetOneShot(true);
7347706b4fc4SDimitry Andric           exe_ctx.GetProcessRef().Resume();
7348706b4fc4SDimitry Andric         }
7349706b4fc4SDimitry Andric       } else if (m_selected_line < GetNumDisassemblyLines()) {
7350706b4fc4SDimitry Andric         const Instruction *inst = m_disassembly_sp->GetInstructionList()
7351706b4fc4SDimitry Andric                                       .GetInstructionAtIndex(m_selected_line)
7352706b4fc4SDimitry Andric                                       .get();
7353706b4fc4SDimitry Andric         ExecutionContext exe_ctx =
7354706b4fc4SDimitry Andric             m_debugger.GetCommandInterpreter().GetExecutionContext();
7355706b4fc4SDimitry Andric         if (exe_ctx.HasTargetScope()) {
7356706b4fc4SDimitry Andric           Address addr = inst->GetAddress();
7357706b4fc4SDimitry Andric           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
7358706b4fc4SDimitry Andric               addr,   // lldb_private::Address
7359706b4fc4SDimitry Andric               false,  // internal
7360706b4fc4SDimitry Andric               false); // request_hardware
7361706b4fc4SDimitry Andric           // Make breakpoint one shot
7362344a3780SDimitry Andric           bp_sp->GetOptions().SetOneShot(true);
7363706b4fc4SDimitry Andric           exe_ctx.GetProcessRef().Resume();
7364706b4fc4SDimitry Andric         }
7365706b4fc4SDimitry Andric       }
7366706b4fc4SDimitry Andric       return eKeyHandled;
7367706b4fc4SDimitry Andric 
7368706b4fc4SDimitry Andric     case 'b': // 'b' == toggle breakpoint on currently selected line
7369b60736ecSDimitry Andric       ToggleBreakpointOnSelectedLine();
7370706b4fc4SDimitry Andric       return eKeyHandled;
7371706b4fc4SDimitry Andric 
7372706b4fc4SDimitry Andric     case 'D': // 'D' == detach and keep stopped
7373706b4fc4SDimitry Andric     {
7374706b4fc4SDimitry Andric       ExecutionContext exe_ctx =
7375706b4fc4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
7376706b4fc4SDimitry Andric       if (exe_ctx.HasProcessScope())
7377b60736ecSDimitry Andric         exe_ctx.GetProcessRef().Detach(true);
7378706b4fc4SDimitry Andric     }
7379706b4fc4SDimitry Andric       return eKeyHandled;
7380706b4fc4SDimitry Andric 
7381706b4fc4SDimitry Andric     case 'c':
7382706b4fc4SDimitry Andric       // 'c' == continue
7383706b4fc4SDimitry Andric       {
7384706b4fc4SDimitry Andric         ExecutionContext exe_ctx =
7385706b4fc4SDimitry Andric             m_debugger.GetCommandInterpreter().GetExecutionContext();
7386706b4fc4SDimitry Andric         if (exe_ctx.HasProcessScope())
7387706b4fc4SDimitry Andric           exe_ctx.GetProcessRef().Resume();
7388706b4fc4SDimitry Andric       }
7389706b4fc4SDimitry Andric       return eKeyHandled;
7390706b4fc4SDimitry Andric 
7391b60736ecSDimitry Andric     case 'f':
7392b60736ecSDimitry Andric       // 'f' == step out (finish)
7393706b4fc4SDimitry Andric       {
7394706b4fc4SDimitry Andric         ExecutionContext exe_ctx =
7395706b4fc4SDimitry Andric             m_debugger.GetCommandInterpreter().GetExecutionContext();
7396706b4fc4SDimitry Andric         if (exe_ctx.HasThreadScope() &&
7397706b4fc4SDimitry Andric             StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
7398145449b1SDimitry Andric           Thread *thread = exe_ctx.GetThreadPtr();
73997fa27ce4SDimitry Andric           uint32_t frame_idx =
74007fa27ce4SDimitry Andric               thread->GetSelectedFrameIndex(SelectMostRelevantFrame);
7401145449b1SDimitry Andric           exe_ctx.GetThreadRef().StepOut(frame_idx);
7402706b4fc4SDimitry Andric         }
7403706b4fc4SDimitry Andric       }
7404706b4fc4SDimitry Andric       return eKeyHandled;
7405706b4fc4SDimitry Andric 
7406706b4fc4SDimitry Andric     case 'n': // 'n' == step over
7407706b4fc4SDimitry Andric     case 'N': // 'N' == step over instruction
7408706b4fc4SDimitry Andric     {
7409706b4fc4SDimitry Andric       ExecutionContext exe_ctx =
7410706b4fc4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
7411706b4fc4SDimitry Andric       if (exe_ctx.HasThreadScope() &&
7412706b4fc4SDimitry Andric           StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
7413706b4fc4SDimitry Andric         bool source_step = (c == 'n');
7414706b4fc4SDimitry Andric         exe_ctx.GetThreadRef().StepOver(source_step);
7415706b4fc4SDimitry Andric       }
7416706b4fc4SDimitry Andric     }
7417706b4fc4SDimitry Andric       return eKeyHandled;
7418706b4fc4SDimitry Andric 
7419706b4fc4SDimitry Andric     case 's': // 's' == step into
7420706b4fc4SDimitry Andric     case 'S': // 'S' == step into instruction
7421706b4fc4SDimitry Andric     {
7422706b4fc4SDimitry Andric       ExecutionContext exe_ctx =
7423706b4fc4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
7424706b4fc4SDimitry Andric       if (exe_ctx.HasThreadScope() &&
7425706b4fc4SDimitry Andric           StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
7426706b4fc4SDimitry Andric         bool source_step = (c == 's');
7427706b4fc4SDimitry Andric         exe_ctx.GetThreadRef().StepIn(source_step);
7428706b4fc4SDimitry Andric       }
7429706b4fc4SDimitry Andric     }
7430706b4fc4SDimitry Andric       return eKeyHandled;
7431706b4fc4SDimitry Andric 
7432b60736ecSDimitry Andric     case 'u': // 'u' == frame up
7433b60736ecSDimitry Andric     case 'd': // 'd' == frame down
7434b60736ecSDimitry Andric     {
7435b60736ecSDimitry Andric       ExecutionContext exe_ctx =
7436b60736ecSDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
7437b60736ecSDimitry Andric       if (exe_ctx.HasThreadScope()) {
7438b60736ecSDimitry Andric         Thread *thread = exe_ctx.GetThreadPtr();
74397fa27ce4SDimitry Andric         uint32_t frame_idx =
74407fa27ce4SDimitry Andric             thread->GetSelectedFrameIndex(SelectMostRelevantFrame);
7441b60736ecSDimitry Andric         if (frame_idx == UINT32_MAX)
7442b60736ecSDimitry Andric           frame_idx = 0;
7443b60736ecSDimitry Andric         if (c == 'u' && frame_idx + 1 < thread->GetStackFrameCount())
7444b60736ecSDimitry Andric           ++frame_idx;
7445b60736ecSDimitry Andric         else if (c == 'd' && frame_idx > 0)
7446b60736ecSDimitry Andric           --frame_idx;
7447b60736ecSDimitry Andric         if (thread->SetSelectedFrameByIndex(frame_idx, true))
74487fa27ce4SDimitry Andric           exe_ctx.SetFrameSP(thread->GetSelectedFrame(SelectMostRelevantFrame));
7449b60736ecSDimitry Andric       }
7450b60736ecSDimitry Andric     }
7451b60736ecSDimitry Andric       return eKeyHandled;
7452b60736ecSDimitry Andric 
7453706b4fc4SDimitry Andric     case 'h':
7454706b4fc4SDimitry Andric       window.CreateHelpSubwindow();
7455706b4fc4SDimitry Andric       return eKeyHandled;
7456706b4fc4SDimitry Andric 
7457706b4fc4SDimitry Andric     default:
7458706b4fc4SDimitry Andric       break;
7459706b4fc4SDimitry Andric     }
7460706b4fc4SDimitry Andric     return eKeyNotHandled;
7461706b4fc4SDimitry Andric   }
7462706b4fc4SDimitry Andric 
ToggleBreakpointOnSelectedLine()7463b60736ecSDimitry Andric   void ToggleBreakpointOnSelectedLine() {
7464b60736ecSDimitry Andric     ExecutionContext exe_ctx =
7465b60736ecSDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext();
7466b60736ecSDimitry Andric     if (!exe_ctx.HasTargetScope())
7467b60736ecSDimitry Andric       return;
7468b60736ecSDimitry Andric     if (GetNumSourceLines() > 0) {
7469b60736ecSDimitry Andric       // Source file breakpoint.
7470b60736ecSDimitry Andric       BreakpointList &bp_list = exe_ctx.GetTargetRef().GetBreakpointList();
7471b60736ecSDimitry Andric       const size_t num_bps = bp_list.GetSize();
7472b60736ecSDimitry Andric       for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
7473b60736ecSDimitry Andric         BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
7474b60736ecSDimitry Andric         const size_t num_bps_locs = bp_sp->GetNumLocations();
7475b60736ecSDimitry Andric         for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
7476b60736ecSDimitry Andric           BreakpointLocationSP bp_loc_sp =
7477b60736ecSDimitry Andric               bp_sp->GetLocationAtIndex(bp_loc_idx);
7478b60736ecSDimitry Andric           LineEntry bp_loc_line_entry;
7479b60736ecSDimitry Andric           if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
7480b60736ecSDimitry Andric                   bp_loc_line_entry)) {
7481ac9a064cSDimitry Andric             if (m_file_sp->GetFileSpec() == bp_loc_line_entry.GetFile() &&
7482b60736ecSDimitry Andric                 m_selected_line + 1 == bp_loc_line_entry.line) {
7483b60736ecSDimitry Andric               bool removed =
7484b60736ecSDimitry Andric                   exe_ctx.GetTargetRef().RemoveBreakpointByID(bp_sp->GetID());
7485b60736ecSDimitry Andric               assert(removed);
7486b60736ecSDimitry Andric               UNUSED_IF_ASSERT_DISABLED(removed);
7487b60736ecSDimitry Andric               return; // Existing breakpoint removed.
7488b60736ecSDimitry Andric             }
7489b60736ecSDimitry Andric           }
7490b60736ecSDimitry Andric         }
7491b60736ecSDimitry Andric       }
7492b60736ecSDimitry Andric       // No breakpoint found on the location, add it.
7493b60736ecSDimitry Andric       BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
7494b60736ecSDimitry Andric           nullptr, // Don't limit the breakpoint to certain modules
7495b60736ecSDimitry Andric           m_file_sp->GetFileSpec(), // Source file
7496b60736ecSDimitry Andric           m_selected_line +
7497b60736ecSDimitry Andric               1, // Source line number (m_selected_line is zero based)
7498b60736ecSDimitry Andric           0,     // No column specified.
7499b60736ecSDimitry Andric           0,     // No offset
7500b60736ecSDimitry Andric           eLazyBoolCalculate,  // Check inlines using global setting
7501b60736ecSDimitry Andric           eLazyBoolCalculate,  // Skip prologue using global setting,
7502b60736ecSDimitry Andric           false,               // internal
7503b60736ecSDimitry Andric           false,               // request_hardware
7504b60736ecSDimitry Andric           eLazyBoolCalculate); // move_to_nearest_code
7505b60736ecSDimitry Andric     } else {
7506b60736ecSDimitry Andric       // Disassembly breakpoint.
7507b60736ecSDimitry Andric       assert(GetNumDisassemblyLines() > 0);
7508b60736ecSDimitry Andric       assert(m_selected_line < GetNumDisassemblyLines());
7509b60736ecSDimitry Andric       const Instruction *inst = m_disassembly_sp->GetInstructionList()
7510b60736ecSDimitry Andric                                     .GetInstructionAtIndex(m_selected_line)
7511b60736ecSDimitry Andric                                     .get();
7512b60736ecSDimitry Andric       Address addr = inst->GetAddress();
7513b60736ecSDimitry Andric       // Try to find it.
7514b60736ecSDimitry Andric       BreakpointList &bp_list = exe_ctx.GetTargetRef().GetBreakpointList();
7515b60736ecSDimitry Andric       const size_t num_bps = bp_list.GetSize();
7516b60736ecSDimitry Andric       for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
7517b60736ecSDimitry Andric         BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
7518b60736ecSDimitry Andric         const size_t num_bps_locs = bp_sp->GetNumLocations();
7519b60736ecSDimitry Andric         for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
7520b60736ecSDimitry Andric           BreakpointLocationSP bp_loc_sp =
7521b60736ecSDimitry Andric               bp_sp->GetLocationAtIndex(bp_loc_idx);
7522b60736ecSDimitry Andric           LineEntry bp_loc_line_entry;
7523b60736ecSDimitry Andric           const lldb::addr_t file_addr =
7524b60736ecSDimitry Andric               bp_loc_sp->GetAddress().GetFileAddress();
7525b60736ecSDimitry Andric           if (file_addr == addr.GetFileAddress()) {
7526b60736ecSDimitry Andric             bool removed =
7527b60736ecSDimitry Andric                 exe_ctx.GetTargetRef().RemoveBreakpointByID(bp_sp->GetID());
7528b60736ecSDimitry Andric             assert(removed);
7529b60736ecSDimitry Andric             UNUSED_IF_ASSERT_DISABLED(removed);
7530b60736ecSDimitry Andric             return; // Existing breakpoint removed.
7531b60736ecSDimitry Andric           }
7532b60736ecSDimitry Andric         }
7533b60736ecSDimitry Andric       }
7534b60736ecSDimitry Andric       // No breakpoint found on the address, add it.
7535b60736ecSDimitry Andric       BreakpointSP bp_sp =
7536b60736ecSDimitry Andric           exe_ctx.GetTargetRef().CreateBreakpoint(addr, // lldb_private::Address
7537b60736ecSDimitry Andric                                                   false,  // internal
7538b60736ecSDimitry Andric                                                   false); // request_hardware
7539b60736ecSDimitry Andric     }
7540b60736ecSDimitry Andric   }
7541b60736ecSDimitry Andric 
7542706b4fc4SDimitry Andric protected:
7543706b4fc4SDimitry Andric   typedef std::set<uint32_t> BreakpointLines;
7544706b4fc4SDimitry Andric   typedef std::set<lldb::addr_t> BreakpointAddrs;
7545706b4fc4SDimitry Andric 
7546706b4fc4SDimitry Andric   Debugger &m_debugger;
7547706b4fc4SDimitry Andric   SymbolContext m_sc;
7548706b4fc4SDimitry Andric   SourceManager::FileSP m_file_sp;
7549145449b1SDimitry Andric   SymbolContextScope *m_disassembly_scope = nullptr;
7550706b4fc4SDimitry Andric   lldb::DisassemblerSP m_disassembly_sp;
7551706b4fc4SDimitry Andric   AddressRange m_disassembly_range;
7552706b4fc4SDimitry Andric   StreamString m_title;
7553145449b1SDimitry Andric   lldb::user_id_t m_tid = LLDB_INVALID_THREAD_ID;
7554145449b1SDimitry Andric   int m_line_width = 4;
7555145449b1SDimitry Andric   uint32_t m_selected_line = 0; // The selected line
7556145449b1SDimitry Andric   uint32_t m_pc_line = 0;       // The line with the PC
7557145449b1SDimitry Andric   uint32_t m_stop_id = 0;
7558145449b1SDimitry Andric   uint32_t m_frame_idx = UINT32_MAX;
7559145449b1SDimitry Andric   int m_first_visible_line = 0;
7560145449b1SDimitry Andric   int m_first_visible_column = 0;
7561145449b1SDimitry Andric   int m_min_x = 0;
7562145449b1SDimitry Andric   int m_min_y = 0;
7563145449b1SDimitry Andric   int m_max_x = 0;
7564145449b1SDimitry Andric   int m_max_y = 0;
7565706b4fc4SDimitry Andric };
7566706b4fc4SDimitry Andric 
7567706b4fc4SDimitry Andric DisplayOptions ValueObjectListDelegate::g_options = {true};
7568706b4fc4SDimitry Andric 
IOHandlerCursesGUI(Debugger & debugger)7569706b4fc4SDimitry Andric IOHandlerCursesGUI::IOHandlerCursesGUI(Debugger &debugger)
7570706b4fc4SDimitry Andric     : IOHandler(debugger, IOHandler::Type::Curses) {}
7571706b4fc4SDimitry Andric 
Activate()7572706b4fc4SDimitry Andric void IOHandlerCursesGUI::Activate() {
7573706b4fc4SDimitry Andric   IOHandler::Activate();
7574706b4fc4SDimitry Andric   if (!m_app_ap) {
7575cfca06d7SDimitry Andric     m_app_ap = std::make_unique<Application>(GetInputFILE(), GetOutputFILE());
7576706b4fc4SDimitry Andric 
7577706b4fc4SDimitry Andric     // This is both a window and a menu delegate
7578706b4fc4SDimitry Andric     std::shared_ptr<ApplicationDelegate> app_delegate_sp(
7579706b4fc4SDimitry Andric         new ApplicationDelegate(*m_app_ap, m_debugger));
7580706b4fc4SDimitry Andric 
7581706b4fc4SDimitry Andric     MenuDelegateSP app_menu_delegate_sp =
7582706b4fc4SDimitry Andric         std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
7583706b4fc4SDimitry Andric     MenuSP lldb_menu_sp(
7584706b4fc4SDimitry Andric         new Menu("LLDB", "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
7585706b4fc4SDimitry Andric     MenuSP exit_menuitem_sp(
7586706b4fc4SDimitry Andric         new Menu("Exit", nullptr, 'x', ApplicationDelegate::eMenuID_LLDBExit));
7587706b4fc4SDimitry Andric     exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
7588706b4fc4SDimitry Andric     lldb_menu_sp->AddSubmenu(MenuSP(new Menu(
7589706b4fc4SDimitry Andric         "About LLDB", nullptr, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
7590706b4fc4SDimitry Andric     lldb_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
7591706b4fc4SDimitry Andric     lldb_menu_sp->AddSubmenu(exit_menuitem_sp);
7592706b4fc4SDimitry Andric 
7593706b4fc4SDimitry Andric     MenuSP target_menu_sp(new Menu("Target", "F2", KEY_F(2),
7594706b4fc4SDimitry Andric                                    ApplicationDelegate::eMenuID_Target));
7595706b4fc4SDimitry Andric     target_menu_sp->AddSubmenu(MenuSP(new Menu(
7596706b4fc4SDimitry Andric         "Create", nullptr, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
7597706b4fc4SDimitry Andric     target_menu_sp->AddSubmenu(MenuSP(new Menu(
7598706b4fc4SDimitry Andric         "Delete", nullptr, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
7599706b4fc4SDimitry Andric 
7600706b4fc4SDimitry Andric     MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3),
7601706b4fc4SDimitry Andric                                     ApplicationDelegate::eMenuID_Process));
7602706b4fc4SDimitry Andric     process_menu_sp->AddSubmenu(MenuSP(new Menu(
7603706b4fc4SDimitry Andric         "Attach", nullptr, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
7604b60736ecSDimitry Andric     process_menu_sp->AddSubmenu(
7605b60736ecSDimitry Andric         MenuSP(new Menu("Detach and resume", nullptr, 'd',
7606b60736ecSDimitry Andric                         ApplicationDelegate::eMenuID_ProcessDetachResume)));
7607b60736ecSDimitry Andric     process_menu_sp->AddSubmenu(
7608b60736ecSDimitry Andric         MenuSP(new Menu("Detach suspended", nullptr, 's',
7609b60736ecSDimitry Andric                         ApplicationDelegate::eMenuID_ProcessDetachSuspended)));
7610706b4fc4SDimitry Andric     process_menu_sp->AddSubmenu(MenuSP(new Menu(
7611706b4fc4SDimitry Andric         "Launch", nullptr, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
7612706b4fc4SDimitry Andric     process_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
7613706b4fc4SDimitry Andric     process_menu_sp->AddSubmenu(
7614706b4fc4SDimitry Andric         MenuSP(new Menu("Continue", nullptr, 'c',
7615706b4fc4SDimitry Andric                         ApplicationDelegate::eMenuID_ProcessContinue)));
7616706b4fc4SDimitry Andric     process_menu_sp->AddSubmenu(MenuSP(new Menu(
7617706b4fc4SDimitry Andric         "Halt", nullptr, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
7618706b4fc4SDimitry Andric     process_menu_sp->AddSubmenu(MenuSP(new Menu(
7619706b4fc4SDimitry Andric         "Kill", nullptr, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
7620706b4fc4SDimitry Andric 
7621706b4fc4SDimitry Andric     MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4),
7622706b4fc4SDimitry Andric                                    ApplicationDelegate::eMenuID_Thread));
7623706b4fc4SDimitry Andric     thread_menu_sp->AddSubmenu(MenuSP(new Menu(
7624706b4fc4SDimitry Andric         "Step In", nullptr, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
7625706b4fc4SDimitry Andric     thread_menu_sp->AddSubmenu(
7626706b4fc4SDimitry Andric         MenuSP(new Menu("Step Over", nullptr, 'v',
7627706b4fc4SDimitry Andric                         ApplicationDelegate::eMenuID_ThreadStepOver)));
7628706b4fc4SDimitry Andric     thread_menu_sp->AddSubmenu(MenuSP(new Menu(
7629706b4fc4SDimitry Andric         "Step Out", nullptr, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
7630706b4fc4SDimitry Andric 
7631706b4fc4SDimitry Andric     MenuSP view_menu_sp(
7632706b4fc4SDimitry Andric         new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
7633706b4fc4SDimitry Andric     view_menu_sp->AddSubmenu(
7634c0981da4SDimitry Andric         MenuSP(new Menu("Backtrace", nullptr, 't',
7635706b4fc4SDimitry Andric                         ApplicationDelegate::eMenuID_ViewBacktrace)));
7636706b4fc4SDimitry Andric     view_menu_sp->AddSubmenu(
7637706b4fc4SDimitry Andric         MenuSP(new Menu("Registers", nullptr, 'r',
7638706b4fc4SDimitry Andric                         ApplicationDelegate::eMenuID_ViewRegisters)));
7639706b4fc4SDimitry Andric     view_menu_sp->AddSubmenu(MenuSP(new Menu(
7640706b4fc4SDimitry Andric         "Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource)));
7641706b4fc4SDimitry Andric     view_menu_sp->AddSubmenu(
7642706b4fc4SDimitry Andric         MenuSP(new Menu("Variables", nullptr, 'v',
7643706b4fc4SDimitry Andric                         ApplicationDelegate::eMenuID_ViewVariables)));
7644c0981da4SDimitry Andric     view_menu_sp->AddSubmenu(
7645c0981da4SDimitry Andric         MenuSP(new Menu("Breakpoints", nullptr, 'b',
7646c0981da4SDimitry Andric                         ApplicationDelegate::eMenuID_ViewBreakpoints)));
7647706b4fc4SDimitry Andric 
7648706b4fc4SDimitry Andric     MenuSP help_menu_sp(
7649706b4fc4SDimitry Andric         new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
7650706b4fc4SDimitry Andric     help_menu_sp->AddSubmenu(MenuSP(new Menu(
7651706b4fc4SDimitry Andric         "GUI Help", nullptr, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
7652706b4fc4SDimitry Andric 
7653706b4fc4SDimitry Andric     m_app_ap->Initialize();
7654706b4fc4SDimitry Andric     WindowSP &main_window_sp = m_app_ap->GetMainWindow();
7655706b4fc4SDimitry Andric 
7656706b4fc4SDimitry Andric     MenuSP menubar_sp(new Menu(Menu::Type::Bar));
7657706b4fc4SDimitry Andric     menubar_sp->AddSubmenu(lldb_menu_sp);
7658706b4fc4SDimitry Andric     menubar_sp->AddSubmenu(target_menu_sp);
7659706b4fc4SDimitry Andric     menubar_sp->AddSubmenu(process_menu_sp);
7660706b4fc4SDimitry Andric     menubar_sp->AddSubmenu(thread_menu_sp);
7661706b4fc4SDimitry Andric     menubar_sp->AddSubmenu(view_menu_sp);
7662706b4fc4SDimitry Andric     menubar_sp->AddSubmenu(help_menu_sp);
7663706b4fc4SDimitry Andric     menubar_sp->SetDelegate(app_menu_delegate_sp);
7664706b4fc4SDimitry Andric 
7665706b4fc4SDimitry Andric     Rect content_bounds = main_window_sp->GetFrame();
7666706b4fc4SDimitry Andric     Rect menubar_bounds = content_bounds.MakeMenuBar();
7667706b4fc4SDimitry Andric     Rect status_bounds = content_bounds.MakeStatusBar();
7668706b4fc4SDimitry Andric     Rect source_bounds;
7669706b4fc4SDimitry Andric     Rect variables_bounds;
7670706b4fc4SDimitry Andric     Rect threads_bounds;
7671706b4fc4SDimitry Andric     Rect source_variables_bounds;
7672706b4fc4SDimitry Andric     content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
7673706b4fc4SDimitry Andric                                            threads_bounds);
7674706b4fc4SDimitry Andric     source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds,
7675706b4fc4SDimitry Andric                                                       variables_bounds);
7676706b4fc4SDimitry Andric 
7677706b4fc4SDimitry Andric     WindowSP menubar_window_sp =
7678706b4fc4SDimitry Andric         main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
7679706b4fc4SDimitry Andric     // Let the menubar get keys if the active window doesn't handle the keys
7680706b4fc4SDimitry Andric     // that are typed so it can respond to menubar key presses.
7681706b4fc4SDimitry Andric     menubar_window_sp->SetCanBeActive(
7682706b4fc4SDimitry Andric         false); // Don't let the menubar become the active window
7683706b4fc4SDimitry Andric     menubar_window_sp->SetDelegate(menubar_sp);
7684706b4fc4SDimitry Andric 
7685706b4fc4SDimitry Andric     WindowSP source_window_sp(
7686706b4fc4SDimitry Andric         main_window_sp->CreateSubWindow("Source", source_bounds, true));
7687706b4fc4SDimitry Andric     WindowSP variables_window_sp(
7688706b4fc4SDimitry Andric         main_window_sp->CreateSubWindow("Variables", variables_bounds, false));
7689706b4fc4SDimitry Andric     WindowSP threads_window_sp(
7690706b4fc4SDimitry Andric         main_window_sp->CreateSubWindow("Threads", threads_bounds, false));
7691706b4fc4SDimitry Andric     WindowSP status_window_sp(
7692706b4fc4SDimitry Andric         main_window_sp->CreateSubWindow("Status", status_bounds, false));
7693706b4fc4SDimitry Andric     status_window_sp->SetCanBeActive(
7694706b4fc4SDimitry Andric         false); // Don't let the status bar become the active window
7695706b4fc4SDimitry Andric     main_window_sp->SetDelegate(
7696706b4fc4SDimitry Andric         std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
7697706b4fc4SDimitry Andric     source_window_sp->SetDelegate(
7698706b4fc4SDimitry Andric         WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
7699706b4fc4SDimitry Andric     variables_window_sp->SetDelegate(
7700706b4fc4SDimitry Andric         WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
7701706b4fc4SDimitry Andric     TreeDelegateSP thread_delegate_sp(new ThreadsTreeDelegate(m_debugger));
7702706b4fc4SDimitry Andric     threads_window_sp->SetDelegate(WindowDelegateSP(
7703706b4fc4SDimitry Andric         new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
7704706b4fc4SDimitry Andric     status_window_sp->SetDelegate(
7705706b4fc4SDimitry Andric         WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
7706706b4fc4SDimitry Andric 
7707b60736ecSDimitry Andric     // All colors with black background.
7708b60736ecSDimitry Andric     init_pair(1, COLOR_BLACK, COLOR_BLACK);
7709b60736ecSDimitry Andric     init_pair(2, COLOR_RED, COLOR_BLACK);
7710b60736ecSDimitry Andric     init_pair(3, COLOR_GREEN, COLOR_BLACK);
7711b60736ecSDimitry Andric     init_pair(4, COLOR_YELLOW, COLOR_BLACK);
7712b60736ecSDimitry Andric     init_pair(5, COLOR_BLUE, COLOR_BLACK);
7713b60736ecSDimitry Andric     init_pair(6, COLOR_MAGENTA, COLOR_BLACK);
7714b60736ecSDimitry Andric     init_pair(7, COLOR_CYAN, COLOR_BLACK);
7715b60736ecSDimitry Andric     init_pair(8, COLOR_WHITE, COLOR_BLACK);
7716b60736ecSDimitry Andric     // All colors with blue background.
7717b60736ecSDimitry Andric     init_pair(9, COLOR_BLACK, COLOR_BLUE);
7718b60736ecSDimitry Andric     init_pair(10, COLOR_RED, COLOR_BLUE);
7719b60736ecSDimitry Andric     init_pair(11, COLOR_GREEN, COLOR_BLUE);
7720b60736ecSDimitry Andric     init_pair(12, COLOR_YELLOW, COLOR_BLUE);
7721b60736ecSDimitry Andric     init_pair(13, COLOR_BLUE, COLOR_BLUE);
7722b60736ecSDimitry Andric     init_pair(14, COLOR_MAGENTA, COLOR_BLUE);
7723b60736ecSDimitry Andric     init_pair(15, COLOR_CYAN, COLOR_BLUE);
7724b60736ecSDimitry Andric     init_pair(16, COLOR_WHITE, COLOR_BLUE);
7725b60736ecSDimitry Andric     // These must match the order in the color indexes enum.
7726b60736ecSDimitry Andric     init_pair(17, COLOR_BLACK, COLOR_WHITE);
7727b60736ecSDimitry Andric     init_pair(18, COLOR_MAGENTA, COLOR_WHITE);
7728b60736ecSDimitry Andric     static_assert(LastColorPairIndex == 18, "Color indexes do not match.");
7729344a3780SDimitry Andric 
7730344a3780SDimitry Andric     define_key("\033[Z", KEY_SHIFT_TAB);
7731c0981da4SDimitry Andric     define_key("\033\015", KEY_ALT_ENTER);
7732706b4fc4SDimitry Andric   }
7733706b4fc4SDimitry Andric }
7734706b4fc4SDimitry Andric 
Deactivate()7735706b4fc4SDimitry Andric void IOHandlerCursesGUI::Deactivate() { m_app_ap->Terminate(); }
7736706b4fc4SDimitry Andric 
Run()7737706b4fc4SDimitry Andric void IOHandlerCursesGUI::Run() {
7738706b4fc4SDimitry Andric   m_app_ap->Run(m_debugger);
7739706b4fc4SDimitry Andric   SetIsDone(true);
7740706b4fc4SDimitry Andric }
7741706b4fc4SDimitry Andric 
7742706b4fc4SDimitry Andric IOHandlerCursesGUI::~IOHandlerCursesGUI() = default;
7743706b4fc4SDimitry Andric 
Cancel()7744706b4fc4SDimitry Andric void IOHandlerCursesGUI::Cancel() {}
7745706b4fc4SDimitry Andric 
Interrupt()7746145449b1SDimitry Andric bool IOHandlerCursesGUI::Interrupt() {
7747145449b1SDimitry Andric   return m_debugger.GetCommandInterpreter().IOHandlerInterrupt(*this);
7748145449b1SDimitry Andric }
7749706b4fc4SDimitry Andric 
GotEOF()7750706b4fc4SDimitry Andric void IOHandlerCursesGUI::GotEOF() {}
7751706b4fc4SDimitry Andric 
TerminalSizeChanged()7752b60736ecSDimitry Andric void IOHandlerCursesGUI::TerminalSizeChanged() {
7753b60736ecSDimitry Andric   m_app_ap->TerminalSizeChanged();
7754b60736ecSDimitry Andric }
7755b60736ecSDimitry Andric 
7756706b4fc4SDimitry Andric #endif // LLDB_ENABLE_CURSES
7757