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