1 // Copyright 2017 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4
5 #include "DolphinQt/Debugger/NewBreakpointDialog.h"
6
7 #include <QCheckBox>
8 #include <QDialogButtonBox>
9 #include <QGridLayout>
10 #include <QGroupBox>
11 #include <QHBoxLayout>
12 #include <QLabel>
13 #include <QLineEdit>
14 #include <QRadioButton>
15 #include <QVBoxLayout>
16
17 #include "DolphinQt/Debugger/BreakpointWidget.h"
18 #include "DolphinQt/QtUtils/ModalMessageBox.h"
19
NewBreakpointDialog(BreakpointWidget * parent)20 NewBreakpointDialog::NewBreakpointDialog(BreakpointWidget* parent)
21 : QDialog(parent), m_parent(parent)
22 {
23 setWindowTitle(tr("New Breakpoint"));
24 CreateWidgets();
25 ConnectWidgets();
26
27 OnBPTypeChanged();
28 OnAddressTypeChanged();
29 }
30
CreateWidgets()31 void NewBreakpointDialog::CreateWidgets()
32 {
33 m_buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
34
35 // Instruction BP
36 m_instruction_bp = new QRadioButton(tr("Instruction Breakpoint"));
37 m_instruction_bp->setChecked(true);
38 m_instruction_box = new QGroupBox;
39 m_instruction_address = new QLineEdit;
40
41 auto* instruction_layout = new QHBoxLayout;
42 m_instruction_box->setLayout(instruction_layout);
43 instruction_layout->addWidget(new QLabel(tr("Address:")));
44 instruction_layout->addWidget(m_instruction_address);
45
46 // Memory BP
47 m_memory_bp = new QRadioButton(tr("Memory Breakpoint"));
48 m_memory_box = new QGroupBox;
49 m_memory_use_address = new QRadioButton(tr("Address"));
50 m_memory_use_address->setChecked(true);
51 // i18n: A range of memory addresses
52 m_memory_use_range = new QRadioButton(tr("Range"));
53 m_memory_address_from = new QLineEdit;
54 m_memory_address_to = new QLineEdit;
55 m_memory_address_from_label = new QLabel; // Set by OnAddressTypeChanged
56 m_memory_address_to_label = new QLabel(tr("To:"));
57 // i18n: This is a selectable condition when adding a breakpoint
58 m_memory_on_read = new QRadioButton(tr("Read"));
59 // i18n: This is a selectable condition when adding a breakpoint
60 m_memory_on_write = new QRadioButton(tr("Write"));
61 // i18n: This is a selectable condition when adding a breakpoint
62 m_memory_on_read_and_write = new QRadioButton(tr("Read or Write"));
63 m_memory_on_write->setChecked(true);
64 // i18n: This is a selectable action when adding a breakpoint
65 m_do_log = new QRadioButton(tr("Write to Log"));
66 // i18n: This is a selectable action when adding a breakpoint
67 m_do_break = new QRadioButton(tr("Break"));
68 // i18n: This is a selectable action when adding a breakpoint
69 m_do_log_and_break = new QRadioButton(tr("Write to Log and Break"));
70 m_do_log_and_break->setChecked(true);
71
72 auto* memory_layout = new QGridLayout;
73 m_memory_box->setLayout(memory_layout);
74 memory_layout->addWidget(m_memory_use_address, 0, 0);
75 memory_layout->addWidget(m_memory_use_range, 0, 3);
76 memory_layout->addWidget(m_memory_address_from_label, 1, 0);
77 memory_layout->addWidget(m_memory_address_from, 1, 1);
78 memory_layout->addWidget(m_memory_address_to_label, 1, 2);
79 memory_layout->addWidget(m_memory_address_to, 1, 3);
80 QGroupBox* condition_box = new QGroupBox(tr("Condition"));
81 auto* condition_layout = new QHBoxLayout;
82 condition_box->setLayout(condition_layout);
83
84 memory_layout->addWidget(condition_box, 2, 0, 1, -1);
85 condition_layout->addWidget(m_memory_on_read);
86 condition_layout->addWidget(m_memory_on_write);
87 condition_layout->addWidget(m_memory_on_read_and_write);
88
89 QGroupBox* action_box = new QGroupBox(tr("Action"));
90 auto* action_layout = new QHBoxLayout;
91 action_box->setLayout(action_layout);
92 action_layout->addWidget(m_do_log);
93 action_layout->addWidget(m_do_break);
94 action_layout->addWidget(m_do_log_and_break);
95
96 auto* layout = new QVBoxLayout;
97
98 layout->addWidget(m_instruction_bp);
99 layout->addWidget(m_instruction_box);
100 layout->addWidget(m_memory_bp);
101 layout->addWidget(m_memory_box);
102 layout->addWidget(action_box);
103 layout->addWidget(m_buttons);
104
105 setLayout(layout);
106 }
107
ConnectWidgets()108 void NewBreakpointDialog::ConnectWidgets()
109 {
110 connect(m_buttons, &QDialogButtonBox::accepted, this, &NewBreakpointDialog::accept);
111 connect(m_buttons, &QDialogButtonBox::rejected, this, &NewBreakpointDialog::reject);
112
113 connect(m_instruction_bp, &QRadioButton::toggled, this, &NewBreakpointDialog::OnBPTypeChanged);
114 connect(m_memory_bp, &QRadioButton::toggled, this, &NewBreakpointDialog::OnBPTypeChanged);
115
116 connect(m_memory_use_address, &QRadioButton::toggled, this,
117 &NewBreakpointDialog::OnAddressTypeChanged);
118 connect(m_memory_use_range, &QRadioButton::toggled, this,
119 &NewBreakpointDialog::OnAddressTypeChanged);
120 }
121
OnBPTypeChanged()122 void NewBreakpointDialog::OnBPTypeChanged()
123 {
124 m_instruction_box->setEnabled(m_instruction_bp->isChecked());
125 m_memory_box->setEnabled(m_memory_bp->isChecked());
126 }
127
OnAddressTypeChanged()128 void NewBreakpointDialog::OnAddressTypeChanged()
129 {
130 bool ranged = m_memory_use_range->isChecked();
131
132 m_memory_address_to->setHidden(!ranged);
133 m_memory_address_to_label->setHidden(!ranged);
134
135 m_memory_address_from_label->setText(ranged ? tr("From:") : tr("Address:"));
136 }
137
accept()138 void NewBreakpointDialog::accept()
139 {
140 auto invalid_input = [this](QString field) {
141 ModalMessageBox::critical(this, tr("Error"),
142 tr("Invalid input for the field \"%1\"").arg(field));
143 };
144
145 bool instruction = m_instruction_bp->isChecked();
146 bool ranged = m_memory_use_range->isChecked();
147
148 // Triggers
149 bool on_read = m_memory_on_read->isChecked() || m_memory_on_read_and_write->isChecked();
150 bool on_write = m_memory_on_write->isChecked() || m_memory_on_read_and_write->isChecked();
151
152 // Actions
153 bool do_log = m_do_log->isChecked() || m_do_log_and_break->isChecked();
154 bool do_break = m_do_break->isChecked() || m_do_log_and_break->isChecked();
155
156 bool good;
157
158 if (instruction)
159 {
160 u32 address = m_instruction_address->text().toUInt(&good, 16);
161
162 if (!good)
163 {
164 invalid_input(tr("Address"));
165 return;
166 }
167
168 m_parent->AddBP(address, false, do_break, do_log);
169 }
170 else
171 {
172 u32 from = m_memory_address_from->text().toUInt(&good, 16);
173
174 if (!good)
175 {
176 invalid_input(ranged ? tr("From") : tr("Address"));
177 return;
178 }
179
180 if (ranged)
181 {
182 u32 to = m_memory_address_to->text().toUInt(&good, 16);
183 if (!good)
184 {
185 invalid_input(tr("To"));
186 return;
187 }
188
189 m_parent->AddRangedMBP(from, to, on_read, on_write, do_log, do_break);
190 }
191 else
192 {
193 m_parent->AddAddressMBP(from, on_read, on_write, do_log, do_break);
194 }
195 }
196
197 QDialog::accept();
198 }
199