1 | /* |
2 | * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com) |
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 <https://www.gnu.org/licenses/>. |
16 | */ |
17 | #include "editorcolorschemewidget.h" |
18 | #include "ui_editorcolorschemewidget.h" |
19 | #include "../settings.h" |
20 | #include "../colorscheme.h" |
21 | #include "../mainwindow.h" |
22 | |
23 | #include <QAction> |
24 | #include <QMessageBox> |
25 | #include <QDebug> |
26 | #include <QInputDialog> |
27 | #include <QFileDialog> |
28 | |
29 | EditorColorSchemeWidget::EditorColorSchemeWidget(const QString& name, const QString& group, QWidget *parent) : |
30 | SettingsWidget(name,group,parent), |
31 | ui(new Ui::EditorColorSchemeWidget) |
32 | { |
33 | ui->setupUi(this); |
34 | mStatementColors = std::make_shared<QHash<StatementKind, std::shared_ptr<ColorSchemeItem> >>(); |
35 | |
36 | mDefaultSchemeComboFont = ui->cbScheme->font(); |
37 | mModifiedSchemeComboFont = mDefaultSchemeComboFont; |
38 | mModifiedSchemeComboFont.setBold(true); |
39 | int schemeCount=0; |
40 | for (QString schemeName: pColorManager->getSchemes()) { |
41 | PColorScheme scheme = pColorManager->get(schemeName); |
42 | if (!scheme) |
43 | return; |
44 | ui->cbScheme->addItem(schemeName); |
45 | if (scheme->customed()) |
46 | ui->cbScheme->setItemData(schemeCount,mModifiedSchemeComboFont,Qt::FontRole); |
47 | schemeCount++; |
48 | } |
49 | ui->treeItems->setModel(&mDefinesModel); |
50 | mDefinesModel.setHorizontalHeaderLabels(QStringList()); |
51 | for (QString defineName : pColorManager->getDefines()) { |
52 | addDefine(defineName, pColorManager->getDefine(defineName)); |
53 | } |
54 | ui->treeItems->expandAll(); |
55 | QModelIndex groupIndex = mDefinesModel.index(0,0); |
56 | QModelIndex index = mDefinesModel.index(0,0,groupIndex); |
57 | ui->treeItems->setCurrentIndex(index); |
58 | connect(ui->treeItems->selectionModel(), &QItemSelectionModel::selectionChanged, |
59 | this, &EditorColorSchemeWidget::onItemSelectionChanged); |
60 | connect(ui->cbScheme, QOverload<int>::of(&QComboBox::currentIndexChanged), |
61 | this, &EditorColorSchemeWidget::changeSchemeComboFont); |
62 | connect(ui->cbScheme, QOverload<int>::of(&QComboBox::currentIndexChanged), |
63 | this, &EditorColorSchemeWidget::onItemSelectionChanged); |
64 | connect(this, &SettingsWidget::settingsChanged,this, |
65 | &EditorColorSchemeWidget::onSettingChanged); |
66 | ui->editDemo->document()->setText( |
67 | "#include <iostream>\n" |
68 | "#include <conio.h>\n" |
69 | "\n" |
70 | "int x=10;\n" |
71 | "\n" |
72 | "int main(int argc, char **argv)\n" |
73 | "{\n" |
74 | " int numbers[20];\n" |
75 | " float average, total; //breakpoint\n" |
76 | " for (int i = 0; i <= 19; i++)\n" |
77 | " { // active breakpoint\n" |
78 | " numbers[i] = i+x;\n" |
79 | " Total += i; // error line\n" |
80 | " }\n" |
81 | " average = total / 20; // comment\n" |
82 | " cout << \"total: \" << total << \"\nAverage: \" << average;\n" |
83 | " getch();\n" |
84 | "}\n" |
85 | ); |
86 | ui->editDemo->setReadOnly(true); |
87 | ui->editDemo->setStatementColors(mStatementColors); |
88 | onItemSelectionChanged(); |
89 | } |
90 | |
91 | void EditorColorSchemeWidget::addDefine(const QString& name, PColorSchemeItemDefine define) |
92 | { |
93 | QList<QStandardItem*> items = mDefinesModel.findItems(define->group()); |
94 | QStandardItem* pGroupItem; |
95 | if (items.count() == 0 ) { |
96 | pGroupItem = new QStandardItem(define->group()); |
97 | pGroupItem->setData("" , NameRole); |
98 | mDefinesModel.appendRow(pGroupItem); |
99 | } else { |
100 | pGroupItem = items[0]; |
101 | } |
102 | QStandardItem* pWidgetItem = new QStandardItem(define->displayName()); |
103 | pWidgetItem->setData(name, NameRole); |
104 | pGroupItem->appendRow(pWidgetItem); |
105 | } |
106 | |
107 | PColorSchemeItem EditorColorSchemeWidget::getCurrentItem() |
108 | { |
109 | QItemSelectionModel * selectionModel = ui->treeItems->selectionModel(); |
110 | QString name =mDefinesModel.data(selectionModel->currentIndex(),NameRole).toString(); |
111 | if (name.isEmpty()) |
112 | return PColorSchemeItem(); |
113 | return pColorManager->getItem(ui->cbScheme->currentText(), name); |
114 | } |
115 | |
116 | PColorScheme EditorColorSchemeWidget::getCurrentScheme() |
117 | { |
118 | return pColorManager->get(ui->cbScheme->currentText()); |
119 | } |
120 | |
121 | void EditorColorSchemeWidget::connectModificationSlots() |
122 | { |
123 | connect(ui->cbBackground,&QCheckBox::stateChanged, |
124 | this, &EditorColorSchemeWidget::onBackgroundChanged); |
125 | connect(ui->colorBackground,&ColorEdit::colorChanged, |
126 | this, &EditorColorSchemeWidget::onBackgroundChanged); |
127 | connect(ui->cbForeground,&QCheckBox::stateChanged, |
128 | this, &EditorColorSchemeWidget::onForegroundChanged); |
129 | connect(ui->colorForeground,&ColorEdit::colorChanged, |
130 | this, &EditorColorSchemeWidget::onForegroundChanged); |
131 | connect(ui->cbBold,&QCheckBox::stateChanged, |
132 | this, &EditorColorSchemeWidget::onFontStyleChanged); |
133 | connect(ui->cbItalic,&QCheckBox::stateChanged, |
134 | this, &EditorColorSchemeWidget::onFontStyleChanged); |
135 | connect(ui->cbStrikeout,&QCheckBox::stateChanged, |
136 | this, &EditorColorSchemeWidget::onFontStyleChanged); |
137 | connect(ui->cbUnderlined,&QCheckBox::stateChanged, |
138 | this, &EditorColorSchemeWidget::onFontStyleChanged); |
139 | } |
140 | |
141 | void EditorColorSchemeWidget::disconnectModificationSlots() |
142 | { |
143 | disconnect(ui->cbBackground,&QCheckBox::stateChanged, |
144 | this, &EditorColorSchemeWidget::onBackgroundChanged); |
145 | disconnect(ui->colorBackground,&ColorEdit::colorChanged, |
146 | this, &EditorColorSchemeWidget::onBackgroundChanged); |
147 | disconnect(ui->cbForeground,&QCheckBox::stateChanged, |
148 | this, &EditorColorSchemeWidget::onForegroundChanged); |
149 | disconnect(ui->colorForeground,&ColorEdit::colorChanged, |
150 | this, &EditorColorSchemeWidget::onForegroundChanged); |
151 | disconnect(ui->cbBold,&QCheckBox::stateChanged, |
152 | this, &EditorColorSchemeWidget::onFontStyleChanged); |
153 | disconnect(ui->cbItalic,&QCheckBox::stateChanged, |
154 | this, &EditorColorSchemeWidget::onFontStyleChanged); |
155 | disconnect(ui->cbStrikeout,&QCheckBox::stateChanged, |
156 | this, &EditorColorSchemeWidget::onFontStyleChanged); |
157 | disconnect(ui->cbUnderlined,&QCheckBox::stateChanged, |
158 | this, &EditorColorSchemeWidget::onFontStyleChanged); |
159 | } |
160 | |
161 | void EditorColorSchemeWidget::setCurrentSchemeModified() |
162 | { |
163 | PColorScheme scheme = getCurrentScheme(); |
164 | if (scheme) { |
165 | scheme->setCustomed(true); |
166 | } |
167 | // if (mModifiedSchemes.contains(ui->cbScheme->currentText())) |
168 | // return; |
169 | mModifiedSchemes.insert(ui->cbScheme->currentText()); |
170 | ui->cbScheme->setItemData(ui->cbScheme->currentIndex(), |
171 | mModifiedSchemeComboFont,Qt::FontRole); |
172 | ui->cbScheme->setFont(mModifiedSchemeComboFont); |
173 | //ui->cbScheme->view()->setFont(mDefaultSchemeComboFont); |
174 | //we must reset the editor here, because this slot is processed after the onSettingChanged |
175 | onSettingChanged(); |
176 | } |
177 | |
178 | EditorColorSchemeWidget::~EditorColorSchemeWidget() |
179 | { |
180 | delete ui; |
181 | } |
182 | |
183 | static void setColorProp(ColorEdit* ce, QCheckBox* cb, const QColor& color) { |
184 | if (color.isValid()) { |
185 | cb->setChecked(true); |
186 | ce->setColor(color); |
187 | ce->setVisible(true); |
188 | } else { |
189 | cb->setChecked(false); |
190 | ce->setVisible(false); |
191 | } |
192 | } |
193 | |
194 | void EditorColorSchemeWidget::onItemSelectionChanged() |
195 | { |
196 | disconnectModificationSlots(); |
197 | QItemSelectionModel * selectionModel = ui->treeItems->selectionModel(); |
198 | QString name =mDefinesModel.data(selectionModel->currentIndex(),NameRole).toString(); |
199 | bool found = false; |
200 | if (!name.isEmpty()) { |
201 | PColorSchemeItemDefine define = pColorManager->getDefine(name); |
202 | if (define) { |
203 | found = true; |
204 | ui->cbBackground->setEnabled(define->hasBackground()); |
205 | ui->colorBackground->setEnabled(define->hasBackground()); |
206 | ui->cbForeground->setEnabled(define->hasForeground()); |
207 | ui->colorForeground->setEnabled(define->hasForeground()); |
208 | ui->grpFontStyles->setEnabled(define->hasFontStyle()); |
209 | PColorSchemeItem item = pColorManager->getItem(ui->cbScheme->currentText(), name); |
210 | if (!item) { |
211 | PColorScheme scheme = pColorManager->get(ui->cbScheme->currentText()); |
212 | if (scheme) { |
213 | scheme->addItem(name); |
214 | } |
215 | } |
216 | if (define->hasBackground() && item) { |
217 | setColorProp(ui->colorBackground, ui->cbBackground,item->background()); |
218 | } else { |
219 | setColorProp(ui->colorBackground, ui->cbBackground,QColor()); |
220 | } |
221 | if (define->hasForeground() && item) { |
222 | setColorProp(ui->colorForeground, ui->cbForeground,item->foreground()); |
223 | } else { |
224 | setColorProp(ui->colorForeground, ui->cbForeground,QColor()); |
225 | } |
226 | if (define->hasFontStyle() && item) { |
227 | ui->cbBold->setChecked(item->bold()); |
228 | ui->cbItalic->setChecked(item->italic()); |
229 | ui->cbUnderlined->setChecked(item->underlined()); |
230 | ui->cbStrikeout->setChecked(item->strikeout()); |
231 | } else { |
232 | ui->cbBold->setChecked(false); |
233 | ui->cbItalic->setChecked(false); |
234 | ui->cbUnderlined->setChecked(false); |
235 | ui->cbStrikeout->setChecked(false); |
236 | } |
237 | } |
238 | } |
239 | |
240 | ui->widgetSchemeItem->setEnabled(found); |
241 | connectModificationSlots(); |
242 | } |
243 | |
244 | void EditorColorSchemeWidget::onSettingChanged() |
245 | { |
246 | pColorManager->updateStatementColors(mStatementColors,ui->cbScheme->currentText()); |
247 | ui->editDemo->applyColorScheme(ui->cbScheme->currentText()); |
248 | } |
249 | |
250 | void EditorColorSchemeWidget::onForegroundChanged() |
251 | { |
252 | PColorSchemeItem item = getCurrentItem(); |
253 | if (!item) |
254 | return; |
255 | ui->colorForeground->setVisible(ui->cbForeground->isChecked()); |
256 | if (ui->cbForeground->isChecked()) { |
257 | item->setForeground(ui->colorForeground->color()); |
258 | } else { |
259 | ui->colorForeground->setColor(QColor()); |
260 | item->setForeground(QColor()); |
261 | } |
262 | setCurrentSchemeModified(); |
263 | } |
264 | |
265 | void EditorColorSchemeWidget::onBackgroundChanged() |
266 | { |
267 | PColorSchemeItem item = getCurrentItem(); |
268 | if (!item) |
269 | return; |
270 | ui->colorBackground->setVisible(ui->cbBackground->isChecked()); |
271 | if (ui->cbBackground->isChecked()) { |
272 | item->setBackground(ui->colorBackground->color()); |
273 | } else { |
274 | ui->colorBackground->setColor(QColor()); |
275 | item->setBackground(QColor()); |
276 | } |
277 | setCurrentSchemeModified(); |
278 | } |
279 | |
280 | void EditorColorSchemeWidget::onFontStyleChanged() |
281 | { |
282 | PColorSchemeItem item = getCurrentItem(); |
283 | if (!item) |
284 | return; |
285 | item->setBold(ui->cbBold->isChecked()); |
286 | item->setItalic(ui->cbItalic->isChecked()); |
287 | item->setStrikeout(ui->cbStrikeout->isChecked()); |
288 | item->setUnderlined(ui->cbUnderlined->isChecked()); |
289 | setCurrentSchemeModified(); |
290 | } |
291 | |
292 | void EditorColorSchemeWidget::changeSchemeComboFont() |
293 | { |
294 | QString name = ui->cbScheme->currentText(); |
295 | PColorScheme scheme = pColorManager->get(name); |
296 | if (scheme && scheme->customed()) { |
297 | ui->cbScheme->setFont(mModifiedSchemeComboFont); |
298 | } else { |
299 | ui->cbScheme->setFont(mDefaultSchemeComboFont); |
300 | } |
301 | //ui->cbScheme->view()->setFont(mDefaultSchemeComboFont); |
302 | } |
303 | |
304 | void EditorColorSchemeWidget::doLoad() |
305 | { |
306 | ui->cbScheme->setCurrentText(pSettings->editor().colorScheme()); |
307 | ui->chkRainborParenthesis->setChecked(pSettings->editor().rainbowParenthesis()); |
308 | } |
309 | |
310 | void EditorColorSchemeWidget::doSave() |
311 | { |
312 | try { |
313 | for (QString name:mModifiedSchemes) { |
314 | pColorManager->saveScheme(name); |
315 | } |
316 | pSettings->editor().setColorScheme(ui->cbScheme->currentText()); |
317 | pSettings->editor().setRainbowParenthesis(ui->chkRainborParenthesis->isChecked()); |
318 | pSettings->editor().save(); |
319 | pMainWindow->updateEditorColorSchemes(); |
320 | } catch (FileError e) { |
321 | QMessageBox::critical(this,tr("Error" ),e.reason()); |
322 | } |
323 | } |
324 | |
325 | void EditorColorSchemeWidget::on_actionCopy_Scheme_triggered() |
326 | { |
327 | QString newName = pColorManager->copy(ui->cbScheme->currentText()); |
328 | if (newName.isEmpty()) |
329 | return; |
330 | ui->cbScheme->addItem(newName); |
331 | ui->cbScheme->setCurrentText(newName); |
332 | } |
333 | |
334 | void EditorColorSchemeWidget::() |
335 | { |
336 | QMenu ; |
337 | |
338 | PColorScheme scheme = getCurrentScheme(); |
339 | if (scheme) { |
340 | if (scheme->customed()) { |
341 | menu.addAction(ui->actionReset_Scheme); |
342 | } |
343 | if (!scheme->bundled()) { |
344 | menu.addAction(ui->actionRename_Scheme); |
345 | menu.addAction(ui->actionDelete_Scheme); |
346 | } |
347 | QString name = ui->cbScheme->currentText(); |
348 | if (!pColorManager->exists(name+ " Copy" )) |
349 | menu.addAction(ui->actionCopy_Scheme); |
350 | menu.addAction(ui->actionExport_Scheme); |
351 | menu.addSeparator(); |
352 | } |
353 | menu.addAction(ui->actionImport_Scheme); |
354 | QPoint p; |
355 | p.setX(0); |
356 | p.setY(ui->btnSchemeMenu->height()+2); |
357 | menu.exec(ui->btnSchemeMenu->mapToGlobal(p)); |
358 | } |
359 | |
360 | void EditorColorSchemeWidget::on_actionImport_Scheme_triggered() |
361 | { |
362 | QString filename = QFileDialog::getOpenFileName(this, |
363 | tr("Open" ), QString(), tr("Color Scheme Files (*.scheme)" )); |
364 | if (filename.isEmpty()) |
365 | return; |
366 | QFileInfo fileInfo(filename); |
367 | QString name = fileInfo.fileName(); |
368 | QString suffix = EXT_COLOR_SCHEME; |
369 | if (!name.toLower().endsWith(suffix)) |
370 | return; |
371 | name.remove(name.length()-suffix.length(),suffix.length()); |
372 | name.replace('_',' '); |
373 | if (!pColorManager->isValidName(name)) { |
374 | QMessageBox::critical(this,tr("Error" ),tr("'%1' is not a valid name for color scheme file." )); |
375 | return; |
376 | } |
377 | try { |
378 | PColorScheme scheme = ColorScheme::load(filename); |
379 | pColorManager->add(name, scheme); |
380 | ui->cbScheme->addItem(name); |
381 | ui->cbScheme->setCurrentText(name); |
382 | } catch (FileError e) { |
383 | QMessageBox::critical(this,tr("Error" ),e.reason()); |
384 | return; |
385 | } |
386 | } |
387 | |
388 | void EditorColorSchemeWidget::on_actionRename_Scheme_triggered() |
389 | { |
390 | QString name = ui->cbScheme->currentText(); |
391 | bool isOk; |
392 | QString newName = QInputDialog::getText(this,tr("New scheme name" ),tr("New scheme name" ), |
393 | QLineEdit::Normal,name,&isOk); |
394 | if (isOk) { |
395 | if (!pColorManager->isValidName(newName)) { |
396 | QMessageBox::critical(this,tr("Error" ),tr("'%1' is not a valid scheme name!" ).arg(newName)); |
397 | return; |
398 | } |
399 | try { |
400 | pColorManager->rename(name,newName); |
401 | ui->cbScheme->setItemText( |
402 | ui->cbScheme->currentIndex(), |
403 | newName |
404 | ); |
405 | if (mModifiedSchemes.contains(name)) |
406 | mModifiedSchemes.remove(name); |
407 | mModifiedSchemes.insert(newName); |
408 | } catch(FileError e) { |
409 | QMessageBox::critical(this,tr("Error" ),e.reason()); |
410 | } |
411 | } |
412 | } |
413 | |
414 | void EditorColorSchemeWidget::on_actionReset_Scheme_triggered() |
415 | { |
416 | try { |
417 | if (pColorManager->restoreToDefault(ui->cbScheme->currentText())) { |
418 | ui->cbScheme->setItemData( |
419 | ui->cbScheme->currentIndex(), |
420 | QVariant(), |
421 | Qt::FontRole); |
422 | ui->cbScheme->setFont(mDefaultSchemeComboFont); |
423 | //ui->cbScheme->view()->setFont(mDefaultSchemeComboFont); |
424 | } |
425 | } catch (FileError e) { |
426 | QMessageBox::critical(this,tr("Error" ),e.reason()); |
427 | } |
428 | |
429 | } |
430 | |
431 | void EditorColorSchemeWidget::on_actionExport_Scheme_triggered() |
432 | { |
433 | QString filename = QFileDialog::getSaveFileName(this, |
434 | tr("Save" ), QString(), tr("Color Scheme Files (*.scheme)" )); |
435 | if (filename.isEmpty()) |
436 | return; |
437 | try { |
438 | PColorScheme scheme = getCurrentScheme(); |
439 | scheme->save(filename); |
440 | } catch (FileError e) { |
441 | QMessageBox::critical(this,tr("Error" ),e.reason()); |
442 | return; |
443 | } |
444 | } |
445 | |
446 | void EditorColorSchemeWidget::on_actionDelete_Scheme_triggered() |
447 | { |
448 | |
449 | QString name = ui->cbScheme->currentText(); |
450 | if (QMessageBox::warning(this,tr("Confirm Delete Scheme" ), |
451 | tr("Scheme '%1' will be deleted!<br />Do you really want to continue?" ) |
452 | .arg(name), |
453 | QMessageBox::Yes, QMessageBox::No)!=QMessageBox::Yes) |
454 | return; |
455 | try { |
456 | if (pColorManager->remove(name)) { |
457 | if (mModifiedSchemes.contains(name)) |
458 | mModifiedSchemes.remove(name); |
459 | ui->cbScheme->removeItem(ui->cbScheme->currentIndex()); |
460 | if (name == pSettings->editor().colorScheme()) |
461 | doSave(); |
462 | } |
463 | } catch (FileError e) { |
464 | QMessageBox::critical(this,tr("Error" ),e.reason()); |
465 | } |
466 | } |
467 | |
468 | |
469 | |