]> git.lyx.org Git - lyx.git/blob - src/frontends/qt/ButtonController.cpp
Inform user if panel or tab has invalid content (#10827)
[lyx.git] / src / frontends / qt / ButtonController.cpp
1 /**
2  * \file ButtonController.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Allan Rae
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "ButtonController.h"
14 #include "GuiApplication.h"
15 #include "PanelStack.h"
16
17 #include "qt_helpers.h"
18
19 #include "support/debug.h"
20
21 #include <QCheckBox>
22 #include <QPushButton>
23 #include <QLineEdit>
24 #include <QLabel>
25 #include <QList>
26 #include <QTabWidget>
27 #include <QValidator>
28
29
30 namespace lyx {
31 namespace frontend {
32
33 /////////////////////////////////////////////////////////////////////////
34 //
35 // CheckedLineEdit
36 //
37 /////////////////////////////////////////////////////////////////////////
38
39 class CheckedLineEdit
40 {
41 public:
42         CheckedLineEdit(QLineEdit * input, QWidget * label = nullptr,
43                         int tabindex = -1, QString const panel = QString());
44         /// check the widget and do visual marking
45         bool check() const;
46         /// reset all visual markings for tabs or panel sections
47         void setSectionsValid() const;
48
49 private:
50         // non-owned
51         QLineEdit * input_;
52         QWidget * target_;
53         int tab_index_;
54         QString panel_name_;
55 };
56
57
58 CheckedLineEdit::CheckedLineEdit(QLineEdit * input, QWidget * label,
59                                  int tabindex, QString const panel)
60         : input_(input), target_(label), tab_index_(tabindex), panel_name_(panel)
61 {}
62
63
64 bool CheckedLineEdit::check() const
65 {
66         if (!input_->isEnabled()) {
67                 // we do not check diabled widgets
68                 if (target_)
69                         setValid(target_, true);
70                 return true;
71         }
72
73         QValidator const * validator = input_->validator();
74         if (!validator)
75                 return true;
76
77         QString t = input_->text();
78         int p = 0;
79         bool const valid = validator->validate(t, p) == QValidator::Acceptable;
80
81         // Visual feedback.
82         setValid(input_, valid);
83         if (target_) {
84                 if (!valid && !panel_name_.isEmpty() && qobject_cast<PanelStack*>(target_) != nullptr) {
85                         qobject_cast<PanelStack*>(target_)->markPanelValid(panel_name_, false);
86                         // this is a panel, so stop here.
87                         return valid;
88                 }
89                 setValid(target_, valid);
90                 if (!valid && tab_index_ >= 0 && qobject_cast<QTabWidget*>(target_) != nullptr) {
91                         QIcon warn(getPixmap("images/", "emblem-shellescape", "svgz,png"));
92                         QTabBar * tb = qobject_cast<QTabWidget*>(target_)->tabBar();
93                         tb->setTabIcon(tab_index_, warn);
94                         tb->setTabToolTip(tab_index_, qt_("This tab contains invalid input. Please fix!"));
95                 }
96         }
97
98         return valid;
99 }
100
101
102 void CheckedLineEdit::setSectionsValid() const
103 {
104         if (target_ && tab_index_ >= 0 && qobject_cast<QTabWidget*>(target_) != nullptr) {
105                 QTabBar * tb = qobject_cast<QTabWidget*>(target_)->tabBar();
106                 tb->setTabIcon(tab_index_, QIcon());
107                 tb->setTabToolTip(tab_index_, QString());
108         }
109         else if (!panel_name_.isEmpty() && qobject_cast<PanelStack*>(target_) != nullptr)
110                 qobject_cast<PanelStack*>(target_)->markPanelValid(panel_name_, true);
111 }
112
113
114 /////////////////////////////////////////////////////////////////////////
115 //
116 // ButtonController::Private
117 //
118 /////////////////////////////////////////////////////////////////////////
119
120 class ButtonController::Private
121 {
122 public:
123         typedef QList<CheckedLineEdit> CheckedWidgetList;
124
125         Private() {}
126
127         /// \return true if all CheckedWidgets are in a valid state.
128         bool checkWidgets() const
129         {
130                 bool valid = true;
131                 for (const CheckedLineEdit & w : checked_widgets_) {
132                         w.setSectionsValid();
133                 }
134                 for (const CheckedLineEdit & w : checked_widgets_)
135                         valid &= w.check();
136                 return valid;
137         }
138
139 public:
140         CheckedWidgetList checked_widgets_;
141
142         QPushButton * okay_ = nullptr;
143         QPushButton * apply_ = nullptr;
144         QPushButton * cancel_ = nullptr;
145         QPushButton * restore_ = nullptr;
146         QCheckBox * auto_apply_ = nullptr;
147         QPushButton * default_ = nullptr;
148
149         typedef QList<QWidget *> Widgets;
150         Widgets read_only_;
151
152         ButtonPolicy policy_ {ButtonPolicy::IgnorantPolicy};
153 };
154
155
156 /////////////////////////////////////////////////////////////////////////
157 //
158 // ButtonController
159 //
160 /////////////////////////////////////////////////////////////////////////
161
162 ButtonController::ButtonController()
163         : d(new Private)
164 {}
165
166
167 ButtonController::~ButtonController()
168 {
169         delete d;
170 }
171
172
173 void ButtonController::setPolicy(ButtonPolicy::Policy policy)
174 {
175         d->policy_.setPolicy(policy);
176 }
177
178
179 void ButtonController::ok()
180 {
181         input(ButtonPolicy::SMI_OKAY);
182 }
183
184
185 void ButtonController::input(ButtonPolicy::SMInput in)
186 {
187         if (ButtonPolicy::SMI_NOOP == in)
188                 return;
189         d->policy_.input(in);
190         refresh();
191 }
192
193
194 void ButtonController::apply()
195 {
196         input(ButtonPolicy::SMI_APPLY);
197 }
198
199
200 void ButtonController::autoApply()
201 {
202         input(ButtonPolicy::SMI_AUTOAPPLY);
203 }
204
205
206 void ButtonController::cancel()
207 {
208         input(ButtonPolicy::SMI_CANCEL);
209 }
210
211
212 void ButtonController::restore()
213 {
214         input(ButtonPolicy::SMI_RESTORE);
215 }
216
217
218 void ButtonController::hide()
219 {
220         input(ButtonPolicy::SMI_HIDE);
221 }
222
223
224 void ButtonController::setValid(bool v)
225 {
226         input(v ? ButtonPolicy::SMI_VALID : ButtonPolicy::SMI_INVALID);
227 }
228
229
230 bool ButtonController::setReadOnly(bool ro)
231 {
232         LYXERR(Debug::GUI, "Setting controller ro: " << ro);
233
234         d->policy_.input(ro ?
235                 ButtonPolicy::SMI_READ_ONLY : ButtonPolicy::SMI_READ_WRITE);
236
237         refresh();
238         return ro;
239 }
240
241
242 void ButtonController::refresh() const
243 {
244         LYXERR(Debug::GUI, "Calling BC refresh()");
245
246         bool const all_valid = d->checkWidgets();
247
248         if (d->okay_) {
249                 bool const enabled =
250                         all_valid && policy().buttonStatus(ButtonPolicy::OKAY);
251                 d->okay_->setEnabled(enabled);
252         }
253         if (d->apply_) {
254                 bool const enabled =
255                         all_valid && policy().buttonStatus(ButtonPolicy::APPLY);
256                 d->apply_->setEnabled(enabled);
257         }
258         if (d->restore_) {
259                 bool const enabled =
260                         all_valid && policy().buttonStatus(ButtonPolicy::RESTORE);
261                 d->restore_->setEnabled(enabled);
262         }
263         if (d->cancel_) {
264                 bool const enabled = policy().buttonStatus(ButtonPolicy::CANCEL);
265                 if (enabled)
266                         d->cancel_->setText(qt_("Cancel"));
267                 else
268                         d->cancel_->setText(qt_("Close"));
269         }
270         if (d->auto_apply_) {
271                 bool const enabled = policy().buttonStatus(ButtonPolicy::AUTOAPPLY);
272                 d->auto_apply_->setEnabled(enabled);
273         }
274         if (d->default_)
275                 // Somewhere in the chain this can lose default status (#11417)
276                 d->default_->setDefault(true);
277 }
278
279
280 void ButtonController::addCheckedLineEdit(QLineEdit * input, QWidget * target, int tabindex)
281 {
282         d->checked_widgets_.append(CheckedLineEdit(input, target, tabindex));
283 }
284
285
286 void ButtonController::addCheckedLineEditPanel(QLineEdit * input, QWidget * target, QString const panel)
287 {
288         d->checked_widgets_.append(CheckedLineEdit(input, target, -1, panel));
289 }
290
291
292 void ButtonController::setOK(QPushButton * obj, bool const default_button)
293 {
294         d->okay_ = obj;
295         if (default_button)
296                 d->default_ = obj;
297 }
298
299
300 void ButtonController::setApply(QPushButton * obj, bool const default_button)
301 {
302         d->apply_ = obj;
303         if (default_button)
304                 d->default_ = obj;
305 }
306
307
308 void ButtonController::setAutoApply(QCheckBox * obj)
309 {
310         d->auto_apply_ = obj;
311 }
312
313
314 void ButtonController::setCancel(QPushButton * obj, bool const default_button)
315 {
316         d->cancel_ = obj;
317         if (default_button)
318                 d->default_ = obj;
319 }
320
321
322 void ButtonController::setRestore(QPushButton * obj, bool const default_button)
323 {
324         d->restore_ = obj;
325         if (default_button)
326                 d->default_ = obj;
327 }
328
329
330 void ButtonController::addReadOnly(QWidget * obj)
331 {
332         d->read_only_.push_back(obj);
333 }
334
335 ButtonPolicy const & ButtonController::policy() const
336 {
337         return d->policy_;
338 }
339
340
341 ButtonPolicy & ButtonController::policy()
342 {
343         return d->policy_;
344 }
345
346 } // namespace frontend
347 } // namespace lyx