1 /**********************************************************************************************
2     Copyright (C) 2017 Oliver Eichler <oliver.eichler@gmx.de>
3 
4     This program is free software: you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation, either version 3 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 
17 **********************************************************************************************/
18 
19 #include "helpers/CSettings.h"
20 #include "widgets/CTemplateWidget.h"
21 
22 #include <QtGui>
23 #include <QtUiTools>
24 #include <QtWidgets>
25 
CTemplateWidget(QWidget * parent)26 CTemplateWidget::CTemplateWidget(QWidget* parent)
27     : QDialog(parent)
28 {
29     setupUi(this);
30     connect(comboTemplates, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &CTemplateWidget::slotTemplateActivated);
31     connect(pushPreview, &QPushButton::pressed, this, &CTemplateWidget::slotPreview);
32     connect(toolPathTemplates, &QToolButton::pressed, this, &CTemplateWidget::slotSetPath);
33     listTemplates();
34 }
35 
36 
listTemplates()37 void CTemplateWidget::listTemplates()
38 {
39     comboTemplates->clear();
40     comboTemplates->addItem(tr("choose one..."));
41     comboTemplates->addItem(tr("Cycling Tour Summary (built-in)"), "://templates/Cycling_Tour_Summary.ui");
42     comboTemplates->addItem(tr("Hiking Tour Summary (built-in)"), "://templates/Hiking_Tour_Summary.ui");
43     comboTemplates->addItem(tr("Hiking Tour Summary - Advanced (built-in)"), "://templates/Hiking_Tour_Summary_(Advanced).ui");
44     comboTemplates->addItem(tr("Running Summary (built-in)"), "://templates/Running_Summary.ui");
45 
46     SETTINGS;
47     const QString& path = cfg.value("TextEditWidget/templatePath", "").toString();
48 
49     if(!path.isEmpty())
50     {
51         QDir dir(path);
52         const QStringList& files = dir.entryList(QStringList("*.ui"), QDir::Files);
53         for(const QString& file : files)
54         {
55             QString name = QFileInfo(file).completeBaseName().replace("_", " ");
56             comboTemplates->addItem(name, dir.absoluteFilePath(file));
57         }
58     }
59 
60     const QString& data = cfg.value("TextEditWidget/template", "").toString();
61     const int idx = comboTemplates->findData(data);
62     if(idx != -1)
63     {
64         comboTemplates->setCurrentIndex(idx);
65     }
66 }
67 
text()68 QString CTemplateWidget::text()
69 {
70     if(widget.isNull())
71     {
72         return "";
73     }
74     QString str;
75 
76     QList<QGroupBox*> groups = widget->findChildren<QGroupBox*>(QRegExp("group.*"), Qt::FindDirectChildrenOnly);
77     qSort(groups.begin(), groups.end(), [](const QGroupBox* g1, const QGroupBox* g2){return g1->objectName() < g2->objectName(); });
78 
79     for(const QGroupBox* group : qAsConst(groups))
80     {
81         str += QString("<p><b>%1</b>: ").arg(group->title());
82         str += resolveGroup(group);
83         str += "</p>";
84     }
85 
86     return str;
87 }
88 
resolveGroup(const QGroupBox * group)89 QString CTemplateWidget::resolveGroup(const QGroupBox* group)
90 {
91     QString str;
92     QList<QWidget*> widgets = group->findChildren<QWidget*>(QRegExp(".*"), Qt::FindDirectChildrenOnly);
93     qSort(widgets.begin(), widgets.end(), [](const QWidget* w1, const QWidget* w2){return w1->property("order") < w2->property("order"); });
94 
95     for(const QWidget* w : qAsConst(widgets))
96     {
97         const QString pre(str.isEmpty() ? "" : ", ");
98 
99         {
100             const QCheckBox* obj = dynamic_cast<const QCheckBox*>(w);
101             if(obj != nullptr)
102             {
103                 if(obj->isChecked())
104                 {
105                     str += pre + obj->text().replace("&", "");
106                 }
107                 continue;
108             }
109         }
110 
111         {
112             const QRadioButton* obj = dynamic_cast<const QRadioButton*>(w);
113             if(obj != nullptr)
114             {
115                 if(obj->isChecked())
116                 {
117                     str += pre + obj->text().replace("&", "");
118                 }
119                 continue;
120             }
121         }
122 
123         {
124             const QComboBox* obj = dynamic_cast<const QComboBox*>(w);
125             if(obj != nullptr)
126             {
127                 if(!obj->currentText().isEmpty())
128                 {
129                     str += pre + obj->currentText();
130                 }
131                 continue;
132             }
133         }
134 
135         {
136             const QLineEdit* obj = dynamic_cast<const QLineEdit*>(w);
137             if(obj != nullptr)
138             {
139                 if(!obj->text().simplified().isEmpty())
140                 {
141                     str += pre + obj->text();
142                 }
143                 continue;
144             }
145         }
146 
147         {
148             const QTextEdit* obj = dynamic_cast<const QTextEdit*>(w);
149             if(obj != nullptr)
150             {
151                 if(!obj->toPlainText().simplified().isEmpty())
152                 {
153                     str += pre + obj->toHtml();
154                 }
155                 continue;
156             }
157         }
158     }
159 
160     if(str.isEmpty())
161     {
162         str += tr("-");
163     }
164 
165     return str;
166 }
167 
slotSetPath()168 void CTemplateWidget::slotSetPath()
169 {
170     SETTINGS;
171     QString path = cfg.value("TextEditWidget/templatePath", QDir::homePath()).toString();
172     path = QFileDialog::getExistingDirectory(this, tr("Template path..."), path);
173     if(path.isEmpty())
174     {
175         return;
176     }
177     cfg.setValue("TextEditWidget/templatePath", path);
178     listTemplates();
179 }
180 
slotTemplateActivated(int idx)181 void CTemplateWidget::slotTemplateActivated(int idx)
182 {
183     delete widget;
184     if(idx < 1)
185     {
186         pushPreview->setEnabled(false);
187         buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
188         return;
189     }
190 
191     bool success = false;
192     const QString& filename = comboTemplates->itemData(idx).toString();
193     QFile file(filename);
194     if(!file.open(QFile::ReadOnly))
195     {
196         widget = new QLabel(tr("Failed to read template file %1.").arg(filename));
197     }
198     else
199     {
200         QUiLoader loader;
201         widget = loader.load(&file, this);
202         file.close();
203 
204         if(widget.isNull())
205         {
206             widget = new QLabel(loader.errorString());
207         }
208         else if(nextInFocusChain() != nullptr)
209         {
210             // convert focus chain into a sortable property.
211             quint32 cnt = 0;
212             QWidget* first = nextInFocusChain();
213             QWidget* next = first;
214             do
215             {
216                 if(  (dynamic_cast<QCheckBox*>(next) != nullptr)
217                      || (dynamic_cast<QRadioButton*>(next) != nullptr)
218                      || (dynamic_cast<QComboBox*>(next) != nullptr)
219                      || (dynamic_cast<QLineEdit*>(next) != nullptr)
220                      || (dynamic_cast<QTextEdit*>(next) != nullptr)
221                      )
222                 {
223                     next->setProperty("order", cnt++);
224                 }
225 
226                 next = next->nextInFocusChain();
227             }
228             while(next && next != first);
229 
230             success = true;
231         }
232     }
233 
234     layoutWidget->insertWidget(0, widget);
235     pushPreview->setEnabled(success);
236     buttonBox->button(QDialogButtonBox::Ok)->setEnabled(success);
237 
238     SETTINGS;
239     cfg.setValue("TextEditWidget/template", filename);
240 
241     adjustSize();
242 }
243 
slotPreview()244 void CTemplateWidget::slotPreview()
245 {
246     QTextBrowser* preview = new QTextBrowser();
247 
248     preview->setAttribute(Qt::WA_DeleteOnClose, true);
249     preview->setWindowModality(Qt::ApplicationModal);
250     preview->setReadOnly(true);
251     preview->setHtml(text());
252     preview->setWindowTitle(tr("Preview..."));
253 
254     preview->setMinimumWidth(600);
255     preview->move(QApplication::desktop()->screen()->rect().center() - preview->rect().center());
256     preview->show();
257     preview->raise();
258 
259     QAction* action = new QAction(preview);
260     action->setShortcut(Qt::Key_Escape);
261 
262     preview->addAction(action);
263     connect(action, &QAction::triggered, preview, &QTextBrowser::close);
264 }
265