1 /*
2  * Cantata
3  *
4  * Copyright (c) 2011-2020 Craig Drummond <craig.p.drummond@gmail.com>
5  *
6  * ----
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; see the file COPYING.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 
24 #include "dialog.h"
25 #include "configuration.h"
26 #include "utils.h"
27 #include "monoicon.h"
28 #ifdef Q_OS_MAC
29 #include "osxstyle.h"
30 #endif
31 #include "icon.h"
32 #include "acceleratormanager.h"
33 #include <QDialogButtonBox>
34 #include <QPushButton>
35 #include <QBoxLayout>
36 #include <QSettings>
37 #include <QStyle>
38 
Dialog(QWidget * parent,const QString & name,const QSize & defSize)39 Dialog::Dialog(QWidget *parent, const QString &name, const QSize &defSize)
40     : QDialog(parent)
41     , defButton(0)
42     , buttonTypes(0)
43     , mw(nullptr)
44     , buttonBox(nullptr)
45     , shown(false)
46 {
47     if (!name.isEmpty()) {
48         setObjectName(name);
49         Configuration cfg(name);
50         cfgSize=cfg.get("size", QSize());
51         if (!cfgSize.isEmpty()) {
52             QDialog::resize(cfgSize);
53         } else if (!defSize.isEmpty()) {
54             QDialog::resize(defSize);
55         }
56     }
57     #ifdef Q_OS_MAC
58     setWindowIcon(QIcon());
59     #endif
60 }
61 
~Dialog()62 Dialog::~Dialog()
63 {
64     if (!objectName().isEmpty() && size()!=cfgSize) {
65         Configuration cfg(objectName());
66         cfg.set("size", size());
67     }
68     #ifdef Q_OS_MAC
69     OSXStyle::self()->removeWindow(this);
70     #endif
71 }
72 
resize(const QSize & sz)73 void Dialog::resize(const QSize &sz)
74 {
75     if (cfgSize.isEmpty()) {
76         QDialog::resize(sz);
77         cfgSize=sz;
78     }
79 }
80 
81 #ifdef Q_OS_MAC
ButtonProxyStyle()82 Dialog::ButtonProxyStyle::ButtonProxyStyle()
83     : QProxyStyle()
84 {
85     setBaseStyle(qApp->style());
86 }
87 
styleHint(StyleHint stylehint,const QStyleOption * opt,const QWidget * widget,QStyleHintReturn * returnData) const88 int Dialog::ButtonProxyStyle::styleHint(StyleHint stylehint, const QStyleOption *opt, const QWidget *widget, QStyleHintReturn *returnData) const
89 {
90     if (QStyle::SH_DialogButtonLayout==stylehint) {
91         return QDialogButtonBox::GnomeLayout;
92     } else {
93         return QProxyStyle::styleHint(stylehint, opt, widget, returnData);
94     }
95 }
96 
buttonProxyStyle()97 Dialog::ButtonProxyStyle * Dialog::buttonProxyStyle()
98 {
99     static ButtonProxyStyle *style=0;
100     if (!style) {
101         style=new ButtonProxyStyle();
102     }
103     return style;
104 }
105 #endif
106 
monoIcon(const GuiItem & i)107 static QIcon monoIcon(const GuiItem &i)
108 {
109     static QColor col(QColor::Invalid);
110 
111     if (!i.red && !col.isValid()) {
112         col=Utils::monoIconColor();
113     }
114     return MonoIcon::icon((FontAwesome::icon)i.monoIcon, i.red ? MonoIcon::constRed : col,
115                            i.red ? MonoIcon::constRed : QColor(QColor::Invalid));
116 }
117 
118 namespace StdGuiItem {
ok()119 GuiItem ok() { return GuiItem(QObject::tr("&OK"), FontAwesome::check); }
cancel()120 GuiItem cancel() { return GuiItem(QObject::tr("&Cancel"), FontAwesome::ban); }
yes()121 GuiItem yes() { return GuiItem(QObject::tr("&Yes"), FontAwesome::check); }
no()122 GuiItem no() { return GuiItem(QObject::tr("&No"), FontAwesome::times, true); }
discard()123 GuiItem discard() { return GuiItem(QObject::tr("&Discard"), FontAwesome::trash, true); }
save()124 GuiItem save() { return GuiItem(QObject::tr("&Save"), FontAwesome::save); }
apply()125 GuiItem apply() { return GuiItem(QObject::tr("&Apply"), FontAwesome::check); }
close()126 GuiItem close() { return GuiItem(QObject::tr("&Close"), FontAwesome::close, true); }
help()127 GuiItem help() { return GuiItem(QObject::tr("&Help"), FontAwesome::lifering); }
overwrite()128 GuiItem overwrite() { return GuiItem(QObject::tr("&Overwrite")); }
reset()129 GuiItem reset() { return GuiItem(QObject::tr("&Reset"), FontAwesome::undo); }
cont()130 GuiItem cont() { return GuiItem(QObject::tr("&Continue"), FontAwesome::arrowright); }
del()131 GuiItem del() { return GuiItem(QObject::tr("&Delete"), FontAwesome::trash, true); }
stop()132 GuiItem stop() { return GuiItem(QObject::tr("&Stop"), FontAwesome::times); }
remove()133 GuiItem remove() { return   GuiItem(QObject::tr("&Remove"), FontAwesome::remove); }
back(bool useRtl)134 GuiItem back(bool useRtl) { return GuiItem(QObject::tr("&Previous"), useRtl && QApplication::isRightToLeft() ? FontAwesome::chevronright : FontAwesome::chevronleft); }
forward(bool useRtl)135 GuiItem forward(bool useRtl) { return GuiItem(QObject::tr("&Next"), useRtl && QApplication::isRightToLeft() ? FontAwesome::chevronleft : FontAwesome::chevronright); }
136 
standardNames()137 QSet<QString> standardNames()
138 {
139     static QSet<QString> names;
140     if (names.isEmpty()) {
141         QStringList strings = QStringList() << ok().text << cancel().text << yes().text << no().text << discard().text << save().text << apply().text
142                                             << close().text << help().text << overwrite().text << reset().text << cont().text << del().text
143                                             << stop().text << remove().text << back().text << forward().text;
144 
145         for (QString s: strings) {
146             names.insert(s.remove("&"));
147         }
148     }
149     return names;
150 }
151 
152 }
153 
mapType(int btn)154 static QDialogButtonBox::StandardButton mapType(int btn) {
155     switch (btn) {
156     case Dialog::Help:   return QDialogButtonBox::Help;
157     case Dialog::Ok:     return QDialogButtonBox::Ok;
158     case Dialog::Apply:  return QDialogButtonBox::Apply;
159     case Dialog::Cancel: return QDialogButtonBox::Cancel;
160     case Dialog::Close:  return QDialogButtonBox::Close;
161     case Dialog::No:     return QDialogButtonBox::No;
162     case Dialog::Yes:    return QDialogButtonBox::Yes;
163     case Dialog::Reset:  return QDialogButtonBox::Reset;
164     default:             return QDialogButtonBox::NoButton;
165     }
166 }
167 
setButtons(ButtonCodes buttons)168 void Dialog::setButtons(ButtonCodes buttons)
169 {
170     if (buttonBox && buttons==buttonTypes) {
171         return;
172     }
173 
174     QFlags<QDialogButtonBox::StandardButton> btns;
175     if (buttons&Help) {
176         btns|=QDialogButtonBox::Help;
177     }
178     if (buttons&Ok) {
179         btns|=QDialogButtonBox::Ok;
180     }
181     if (buttons&Apply) {
182         btns|=QDialogButtonBox::Apply;
183     }
184     if (buttons&Cancel) {
185         btns|=QDialogButtonBox::Cancel;
186     }
187     if (buttons&Close) {
188         btns|=QDialogButtonBox::Close;
189     }
190     if (buttons&No) {
191         btns|=QDialogButtonBox::No;
192     }
193     if (buttons&Yes) {
194         btns|=QDialogButtonBox::Yes;
195     }
196     if (buttons&Reset) {
197         btns|=QDialogButtonBox::Reset;
198     }
199 
200     buttonTypes=(int)btns;
201     bool needToCreate=true;
202     if (buttonBox) {
203         needToCreate=false;
204         buttonBox->clear();
205         buttonBox->setStandardButtons(btns);
206         userButtons.clear();
207     } else {
208         buttonBox = new QDialogButtonBox(btns, Qt::Horizontal, this);
209         #ifdef Q_OS_MAC
210         buttonBox->setStyle(buttonProxyStyle());
211         #endif
212     }
213 
214     if (buttons&Help) {
215         setButtonGuiItem(QDialogButtonBox::Help, StdGuiItem::help());
216     }
217     if (buttons&Ok) {
218         setButtonGuiItem(QDialogButtonBox::Ok, StdGuiItem::ok());
219     }
220     if (buttons&Apply) {
221         setButtonGuiItem(QDialogButtonBox::Apply, StdGuiItem::apply());
222     }
223     if (buttons&Cancel) {
224         setButtonGuiItem(QDialogButtonBox::Cancel, StdGuiItem::cancel());
225     }
226     if (buttons&Close) {
227         setButtonGuiItem(QDialogButtonBox::Close, StdGuiItem::close());
228     }
229     if (buttons&No) {
230         setButtonGuiItem(QDialogButtonBox::No, StdGuiItem::no());
231     }
232     if (buttons&Yes) {
233         setButtonGuiItem(QDialogButtonBox::Yes, StdGuiItem::yes());
234     }
235     if (buttons&Reset) {
236         setButtonGuiItem(QDialogButtonBox::Reset, StdGuiItem::reset());
237     }
238 
239     if (buttons&User3) {
240         QPushButton *button=new QPushButton(buttonBox);
241         userButtons.insert(User3, button);
242         buttonBox->addButton(button, QDialogButtonBox::ActionRole);
243     }
244     if (buttons&User2) {
245         QPushButton *button=new QPushButton(buttonBox);
246         userButtons.insert(User2, button);
247         buttonBox->addButton(button, QDialogButtonBox::ActionRole);
248     }
249     if (buttons&User1) {
250         QPushButton *button=new QPushButton(buttonBox);
251         userButtons.insert(User1, button);
252         buttonBox->addButton(button, QDialogButtonBox::ActionRole);
253     }
254 
255     if (needToCreate && mw && buttonBox) {
256         create();
257     }
258 }
259 
setDefaultButton(ButtonCode button)260 void Dialog::setDefaultButton(ButtonCode button)
261 {
262     QAbstractButton *b=getButton(button);
263     if (b) {
264         qobject_cast<QPushButton *>(b)->setDefault(true);
265     }
266     defButton=button;
267 }
268 
setButtonText(ButtonCode button,const QString & text)269 void Dialog::setButtonText(ButtonCode button, const QString &text)
270 {
271     QAbstractButton *b=getButton(button);
272     if (b) {
273         b->setText(text);
274     }
275 }
276 
setButtonGuiItem(ButtonCode button,const GuiItem & item)277 void Dialog::setButtonGuiItem(ButtonCode button, const GuiItem &item)
278 {
279     QAbstractButton *b=getButton(button);
280     if (b) {
281         b->setText(item.text);
282         if (style()->styleHint(QStyle::SH_DialogButtonBox_ButtonsHaveIcons)) {
283             if (!item.icon.isEmpty()) {
284                 b->setIcon(Icon::get(item.icon));
285             } else if (item.monoIcon>0) {
286                 b->setIcon(monoIcon(item));
287             } else {
288                 b->setIcon(QIcon());
289             }
290         }
291     }
292 }
293 
setButtonGuiItem(QDialogButtonBox::StandardButton button,const GuiItem & item)294 void Dialog::setButtonGuiItem(QDialogButtonBox::StandardButton button, const GuiItem &item)
295 {
296     QAbstractButton *b=buttonBox->button(button);
297     if (b) {
298         b->setText(item.text);
299         if (style()->styleHint(QStyle::SH_DialogButtonBox_ButtonsHaveIcons)) {
300             if (!item.icon.isEmpty()) {
301                 b->setIcon(Icon::get(item.icon));
302             } else if (item.monoIcon>0) {
303                 b->setIcon(monoIcon(item));
304             } else {
305                 b->setIcon(QIcon());
306             }
307         }
308     }
309 }
310 
setButtonMenu(ButtonCode button,QMenu * menu,ButtonPopupMode popupmode)311 void Dialog::setButtonMenu(ButtonCode button, QMenu *menu, ButtonPopupMode popupmode)
312 {
313     Q_UNUSED(popupmode)
314     QAbstractButton *b=getButton(button);
315     if (b) {
316         qobject_cast<QPushButton *>(b)->setMenu(menu);
317     }
318 }
319 
enableButton(ButtonCode button,bool enable)320 void Dialog::enableButton(ButtonCode button, bool enable)
321 {
322     QAbstractButton *b=getButton(button);
323     if (b) {
324         b->setEnabled(enable);
325     }
326 }
327 
isButtonEnabled(ButtonCode button)328 bool Dialog::isButtonEnabled(ButtonCode button)
329 {
330     QAbstractButton *b=getButton(button);
331     return b ? b->isEnabled() : false;
332 }
333 
setMainWidget(QWidget * widget)334 void Dialog::setMainWidget(QWidget *widget)
335 {
336     if (mw) {
337         return;
338     }
339     mw=widget;
340     if (mw && buttonBox) {
341         create();
342     }
343 }
344 
slotButtonClicked(int button)345 void Dialog::slotButtonClicked(int button)
346 {
347     switch (button) {
348     case Ok: accept(); break;
349     case Cancel: reject(); break;
350     case Close: reject(); break;
351     default: break;
352     }
353 }
354 
buttonPressed(QAbstractButton * button)355 void Dialog::buttonPressed(QAbstractButton *button)
356 {
357     if (buttonTypes&QDialogButtonBox::Help && button==buttonBox->button(QDialogButtonBox::Help)) {
358         slotButtonClicked(Help);
359     } else if (buttonTypes&QDialogButtonBox::Ok && button==buttonBox->button(QDialogButtonBox::Ok)) {
360         slotButtonClicked(Ok);
361     } else if (buttonTypes&QDialogButtonBox::Apply && button==buttonBox->button(QDialogButtonBox::Apply)) {
362         slotButtonClicked(Apply);
363     } else if (buttonTypes&QDialogButtonBox::Cancel && button==buttonBox->button(QDialogButtonBox::Cancel)) {
364         slotButtonClicked(Cancel);
365     } else if (buttonTypes&QDialogButtonBox::Close && button==buttonBox->button(QDialogButtonBox::Close)) {
366         slotButtonClicked(Close);
367     } else if (buttonTypes&QDialogButtonBox::No && button==buttonBox->button(QDialogButtonBox::No)) {
368         slotButtonClicked(No);
369     } else if (buttonTypes&QDialogButtonBox::Yes && button==buttonBox->button(QDialogButtonBox::Yes)) {
370         slotButtonClicked(Yes);
371     } else if (buttonTypes&QDialogButtonBox::Reset && button==buttonBox->button(QDialogButtonBox::Reset)) {
372         slotButtonClicked(Reset);
373     } else if (userButtons.contains(User1) && userButtons[User1]==button) {
374         slotButtonClicked(User1);
375     } else if (userButtons.contains(User2) && userButtons[User2]==button) {
376         slotButtonClicked(User2);
377     } else if (userButtons.contains(User3) && userButtons[User3]==button) {
378         slotButtonClicked(User3);
379     }
380 }
381 
create()382 void Dialog::create()
383 {
384     QBoxLayout *layout=new QBoxLayout(QBoxLayout::TopToBottom, this);
385     layout->addWidget(mw);
386     layout->addWidget(buttonBox);
387     connect(buttonBox, SIGNAL(clicked(QAbstractButton *)), this, SLOT(buttonPressed(QAbstractButton *)));
388 }
389 
getButton(ButtonCode button)390 QAbstractButton *Dialog::getButton(ButtonCode button)
391 {
392     QDialogButtonBox::StandardButton mapped=mapType(button);
393     QAbstractButton *b=QDialogButtonBox::NoButton==mapped ? nullptr : buttonBox->button(mapped);
394     if (!b && userButtons.contains(button)) {
395         b=userButtons[button];
396     }
397     return b;
398 }
399 
showEvent(QShowEvent * e)400 void Dialog::showEvent(QShowEvent *e)
401 {
402     if (!shown) {
403         shown=true;
404         AcceleratorManager::manage(this);
405         if (defButton) {
406             setDefaultButton((ButtonCode)defButton);
407         }
408         if (buttonBox && mw) {
409             QSize mwSize=mw->minimumSize();
410             if (mwSize.width()<16 || mwSize.height()<16) {
411                 mwSize=mw->minimumSizeHint();
412             }
413             if (mwSize.width()>15 && mwSize.height()>15) {
414                 setMinimumHeight(qMax(minimumHeight(), buttonBox->height()+layout()->spacing()+mwSize.height()+(2*layout()->margin())));
415                 setMinimumWidth(qMax(minimumWidth(), mwSize.width()+(2*layout()->margin())));
416             }
417         }
418     }
419     #ifdef Q_OS_MAC
420     if (!isModal()) {
421         OSXStyle::self()->addWindow(this);
422     }
423     #endif
424     QDialog::showEvent(e);
425 }
426 
427 #ifdef Q_OS_MAC
hideEvent(QHideEvent * e)428 void Dialog::hideEvent(QHideEvent *e)
429 {
430     OSXStyle::self()->removeWindow(this);
431     QDialog::hideEvent(e);
432 }
433 
closeEvent(QCloseEvent * e)434 void Dialog::closeEvent(QCloseEvent *e)
435 {
436     OSXStyle::self()->removeWindow(this);
437     QDialog::closeEvent(e);
438 }
439 #endif
440 
441 #include "moc_dialog.cpp"
442