1 #include "lc_global.h"
2 #include "lc_qpreferencesdialog.h"
3 #include "ui_lc_qpreferencesdialog.h"
4 #include "lc_qutils.h"
5 #include "lc_qcategorydialog.h"
6 #include "lc_library.h"
7 #include "lc_application.h"
8 #include "lc_qutils.h"
9 #include "lc_glextensions.h"
10 #include "pieceinf.h"
11 #include "lc_edgecolordialog.h"
12
13 static const char* gLanguageLocales[] =
14 {
15 "", "cs_CZ", "de_DE", "en_US", "fr_FR", "pt_PT", "es_ES"
16 };
17
lcQPreferencesDialog(QWidget * Parent,lcPreferencesDialogOptions * Options)18 lcQPreferencesDialog::lcQPreferencesDialog(QWidget* Parent, lcPreferencesDialogOptions* Options)
19 : QDialog(Parent), mOptions(Options), ui(new Ui::lcQPreferencesDialog)
20 {
21 ui->setupUi(this);
22
23 #if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
24 ui->povrayLabel->hide();
25 ui->povrayExecutable->hide();
26 ui->povrayExecutableBrowse->hide();
27 delete ui->povrayLabel;
28 delete ui->povrayLayout;
29 #endif
30
31 connect(ui->BackgroundSolidColorButton, &QToolButton::clicked, this, &lcQPreferencesDialog::ColorButtonClicked);
32 connect(ui->BackgroundGradient1ColorButton, &QToolButton::clicked, this, &lcQPreferencesDialog::ColorButtonClicked);
33 connect(ui->BackgroundGradient2ColorButton, &QToolButton::clicked, this, &lcQPreferencesDialog::ColorButtonClicked);
34 connect(ui->ActiveViewColorButton, &QToolButton::clicked, this, &lcQPreferencesDialog::ColorButtonClicked);
35 connect(ui->InactiveViewColorButton, &QToolButton::clicked, this, &lcQPreferencesDialog::ColorButtonClicked);
36 connect(ui->AxesColorButton, &QToolButton::clicked, this, &lcQPreferencesDialog::ColorButtonClicked);
37 connect(ui->TextColorButton, &QToolButton::clicked, this, &lcQPreferencesDialog::ColorButtonClicked);
38 connect(ui->MarqueeBorderColorButton, &QToolButton::clicked, this, &lcQPreferencesDialog::ColorButtonClicked);
39 connect(ui->MarqueeFillColorButton, &QToolButton::clicked, this, &lcQPreferencesDialog::ColorButtonClicked);
40 connect(ui->OverlayColorButton, &QToolButton::clicked, this, &lcQPreferencesDialog::ColorButtonClicked);
41 connect(ui->FadeStepsColor, &QToolButton::clicked, this, &lcQPreferencesDialog::ColorButtonClicked);
42 connect(ui->HighlightNewPartsColor, &QToolButton::clicked, this, &lcQPreferencesDialog::ColorButtonClicked);
43 connect(ui->gridStudColor, &QToolButton::clicked, this, &lcQPreferencesDialog::ColorButtonClicked);
44 connect(ui->gridLineColor, &QToolButton::clicked, this, &lcQPreferencesDialog::ColorButtonClicked);
45 connect(ui->ViewSphereColorButton, &QToolButton::clicked, this, &lcQPreferencesDialog::ColorButtonClicked);
46 connect(ui->ViewSphereTextColorButton, &QToolButton::clicked, this, &lcQPreferencesDialog::ColorButtonClicked);
47 connect(ui->ViewSphereHighlightColorButton, &QToolButton::clicked, this, &lcQPreferencesDialog::ColorButtonClicked);
48 connect(ui->categoriesTree, SIGNAL(itemSelectionChanged()), this, SLOT(updateParts()));
49 ui->shortcutEdit->installEventFilter(this);
50 connect(ui->commandList, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this, SLOT(commandChanged(QTreeWidgetItem*)));
51 connect(ui->mouseTree, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this, SLOT(MouseTreeItemChanged(QTreeWidgetItem*)));
52 connect(ui->HighContrastButton, SIGNAL(clicked()), this, SLOT(AutomateEdgeColor()));
53 connect(ui->AutomateEdgeColorButton, SIGNAL(clicked()), this, SLOT(AutomateEdgeColor()));
54
55 ui->partsLibrary->setText(mOptions->LibraryPath);
56 ui->ColorConfigEdit->setText(mOptions->ColorConfigPath);
57 ui->MinifigSettingsEdit->setText(mOptions->MinifigSettingsPath);
58 ui->povrayExecutable->setText(mOptions->POVRayPath);
59 ui->lgeoPath->setText(mOptions->LGEOPath);
60 ui->authorName->setText(mOptions->DefaultAuthor);
61 ui->mouseSensitivity->setValue(mOptions->Preferences.mMouseSensitivity);
62 const bool ColorThemeBlocked = ui->ColorTheme->blockSignals(true);
63 ui->ColorTheme->setCurrentIndex(static_cast<int>(mOptions->Preferences.mColorTheme));
64 ui->ColorTheme->blockSignals(ColorThemeBlocked);
65 for (unsigned int LanguageIdx = 0; LanguageIdx < LC_ARRAY_COUNT(gLanguageLocales); LanguageIdx++)
66 {
67 if (mOptions->Language == gLanguageLocales[LanguageIdx])
68 {
69 ui->Language->setCurrentIndex(LanguageIdx);
70 break;
71 }
72 }
73 ui->checkForUpdates->setCurrentIndex(mOptions->CheckForUpdates);
74 ui->fixedDirectionKeys->setChecked(mOptions->Preferences.mFixedAxes);
75 ui->autoLoadMostRecent->setChecked(mOptions->Preferences.mAutoLoadMostRecent);
76 ui->RestoreTabLayout->setChecked(mOptions->Preferences.mRestoreTabLayout);
77 ui->AutomateEdgeColor->setChecked(mOptions->Preferences.mAutomateEdgeColor);
78
79 ui->antiAliasing->setChecked(mOptions->AASamples != 1);
80 if (mOptions->AASamples == 8)
81 ui->antiAliasingSamples->setCurrentIndex(2);
82 else if (mOptions->AASamples == 4)
83 ui->antiAliasingSamples->setCurrentIndex(1);
84 else
85 ui->antiAliasingSamples->setCurrentIndex(0);
86 ui->edgeLines->setChecked(mOptions->Preferences.mDrawEdgeLines);
87 ui->ConditionalLinesCheckBox->setChecked(mOptions->Preferences.mDrawConditionalLines);
88
89 if (!gSupportsShaderObjects)
90 {
91 ui->ConditionalLinesCheckBox->setChecked(false);
92 ui->ConditionalLinesCheckBox->setEnabled(false);
93 }
94
95 #ifndef LC_OPENGLES
96 if (QSurfaceFormat::defaultFormat().samples() > 1)
97 {
98 glGetFloatv(GL_SMOOTH_LINE_WIDTH_RANGE, mLineWidthRange);
99 glGetFloatv(GL_SMOOTH_LINE_WIDTH_GRANULARITY, &mLineWidthGranularity);
100 }
101 else
102 #endif
103 {
104 glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, mLineWidthRange);
105 mLineWidthGranularity = 1.0f;
106 }
107
108 ui->LineWidthSlider->setRange(0, (mLineWidthRange[1] - mLineWidthRange[0]) / mLineWidthGranularity);
109 ui->LineWidthSlider->setValue((mOptions->Preferences.mLineWidth - mLineWidthRange[0]) / mLineWidthGranularity);
110
111 ui->MeshLOD->setChecked(mOptions->Preferences.mAllowLOD);
112
113 ui->MeshLODSlider->setRange(0, 1500.0f / mMeshLODMultiplier);
114 ui->MeshLODSlider->setValue(mOptions->Preferences.mMeshLODDistance / mMeshLODMultiplier);
115
116 ui->FadeSteps->setChecked(mOptions->Preferences.mFadeSteps);
117 ui->HighlightNewParts->setChecked(mOptions->Preferences.mHighlightNewParts);
118 ui->gridStuds->setChecked(mOptions->Preferences.mDrawGridStuds);
119 ui->gridLines->setChecked(mOptions->Preferences.mDrawGridLines);
120 ui->gridLineSpacing->setText(QString::number(mOptions->Preferences.mGridLineSpacing));
121 ui->GridOriginCheckBox->setChecked(mOptions->Preferences.mDrawGridOrigin);
122 ui->AxisIconCheckBox->setChecked(mOptions->Preferences.mDrawAxes);
123
124 ui->AxisIconLocationCombo->setCurrentIndex((int)mOptions->Preferences.mAxisIconLocation);
125
126 if (!mOptions->Preferences.mBackgroundGradient)
127 ui->BackgroundSolidRadio->setChecked(true);
128 else
129 ui->BackgroundGradientRadio->setChecked(true);
130
131 ui->ViewSphereLocationCombo->setCurrentIndex((int)mOptions->Preferences.mViewSphereLocation);
132
133 if (mOptions->Preferences.mViewSphereEnabled)
134 {
135 switch (mOptions->Preferences.mViewSphereSize)
136 {
137 case 200:
138 ui->ViewSphereSizeCombo->setCurrentIndex(3);
139 break;
140 case 100:
141 ui->ViewSphereSizeCombo->setCurrentIndex(2);
142 break;
143 case 50:
144 ui->ViewSphereSizeCombo->setCurrentIndex(1);
145 break;
146 default:
147 ui->ViewSphereSizeCombo->setCurrentIndex(0);
148 break;
149 }
150 }
151 else
152 ui->ViewSphereSizeCombo->setCurrentIndex(0);
153
154 ui->PreviewAxisIconCheckBox->setChecked(mOptions->Preferences.mDrawPreviewAxis);
155
156 ui->PreviewViewSphereLocationCombo->setCurrentIndex((int)mOptions->Preferences.mPreviewViewSphereLocation);
157
158 if (mOptions->Preferences.mPreviewViewSphereEnabled)
159 {
160 switch (mOptions->Preferences.mPreviewViewSphereSize)
161 {
162 case 100:
163 ui->PreviewViewSphereSizeCombo->setCurrentIndex(3);
164 break;
165 case 75:
166 ui->PreviewViewSphereSizeCombo->setCurrentIndex(2);
167 break;
168 case 50:
169 ui->PreviewViewSphereSizeCombo->setCurrentIndex(1);
170 break;
171 default:
172 ui->PreviewViewSphereSizeCombo->setCurrentIndex(0);
173 break;
174 }
175 }
176 else
177 ui->PreviewViewSphereSizeCombo->setCurrentIndex(0);
178
179 if (!lcGetPiecesLibrary()->SupportsStudStyle())
180 ui->studStyleCombo->setEnabled(false);
181
182 ui->studStyleCombo->setCurrentIndex(static_cast<int>(mOptions->StudStyle));
183
184 if (!gSupportsShaderObjects)
185 ui->ShadingMode->removeItem(static_cast<int>(lcShadingMode::DefaultLights));
186 ui->ShadingMode->setCurrentIndex(static_cast<int>(mOptions->Preferences.mShadingMode));
187
188 auto SetButtonPixmap = [](quint32 Color, QToolButton* Button)
189 {
190 QPixmap Pixmap(12, 12);
191
192 Pixmap.fill(QColor(LC_RGBA_RED(Color), LC_RGBA_GREEN(Color), LC_RGBA_BLUE(Color)));
193 Button->setIcon(Pixmap);
194 };
195
196 SetButtonPixmap(mOptions->Preferences.mBackgroundSolidColor, ui->BackgroundSolidColorButton);
197 SetButtonPixmap(mOptions->Preferences.mBackgroundGradientColorTop, ui->BackgroundGradient1ColorButton);
198 SetButtonPixmap(mOptions->Preferences.mBackgroundGradientColorBottom, ui->BackgroundGradient2ColorButton);
199 SetButtonPixmap(mOptions->Preferences.mAxesColor, ui->AxesColorButton);
200 SetButtonPixmap(mOptions->Preferences.mTextColor, ui->TextColorButton);
201 SetButtonPixmap(mOptions->Preferences.mMarqueeBorderColor, ui->MarqueeBorderColorButton);
202 SetButtonPixmap(mOptions->Preferences.mMarqueeFillColor, ui->MarqueeFillColorButton);
203 SetButtonPixmap(mOptions->Preferences.mOverlayColor, ui->OverlayColorButton);
204 SetButtonPixmap(mOptions->Preferences.mActiveViewColor, ui->ActiveViewColorButton);
205 SetButtonPixmap(mOptions->Preferences.mInactiveViewColor, ui->InactiveViewColorButton);
206 SetButtonPixmap(mOptions->Preferences.mFadeStepsColor, ui->FadeStepsColor);
207 SetButtonPixmap(mOptions->Preferences.mHighlightNewPartsColor, ui->HighlightNewPartsColor);
208 SetButtonPixmap(mOptions->Preferences.mGridStudColor, ui->gridStudColor);
209 SetButtonPixmap(mOptions->Preferences.mGridLineColor, ui->gridLineColor);
210 SetButtonPixmap(mOptions->Preferences.mViewSphereColor, ui->ViewSphereColorButton);
211 SetButtonPixmap(mOptions->Preferences.mViewSphereTextColor, ui->ViewSphereTextColorButton);
212 SetButtonPixmap(mOptions->Preferences.mViewSphereHighlightColor, ui->ViewSphereHighlightColorButton);
213
214 on_studStyleCombo_currentIndexChanged(ui->studStyleCombo->currentIndex());
215 on_antiAliasing_toggled();
216 on_AutomateEdgeColor_toggled();
217 on_edgeLines_toggled();
218 on_LineWidthSlider_valueChanged();
219 on_MeshLODSlider_valueChanged();
220 on_FadeSteps_toggled();
221 on_HighlightNewParts_toggled();
222 on_gridStuds_toggled();
223 on_gridLines_toggled();
224 on_ViewSphereSizeCombo_currentIndexChanged(ui->ViewSphereSizeCombo->currentIndex());
225 on_PreviewViewSphereSizeCombo_currentIndexChanged(ui->PreviewViewSphereSizeCombo->currentIndex());
226
227 updateCategories();
228 ui->categoriesTree->setCurrentItem(ui->categoriesTree->topLevelItem(0));
229
230 updateCommandList();
231 new lcQTreeWidgetColumnStretcher(ui->commandList, 0);
232 commandChanged(nullptr);
233
234 UpdateMouseTree();
235 ui->mouseTree->header()->setSectionResizeMode(0, QHeaderView::Stretch);
236 ui->mouseTree->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
237 ui->mouseTree->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
238 MouseTreeItemChanged(nullptr);
239 }
240
~lcQPreferencesDialog()241 lcQPreferencesDialog::~lcQPreferencesDialog()
242 {
243 delete ui;
244 }
245
accept()246 void lcQPreferencesDialog::accept()
247 {
248 int gridLineSpacing = ui->gridLineSpacing->text().toInt();
249 if (gridLineSpacing < 1)
250 {
251 QMessageBox::information(this, "LeoCAD", tr("Grid spacing must be greater than 0."));
252 return;
253 }
254
255 mOptions->LibraryPath = ui->partsLibrary->text();
256 mOptions->MinifigSettingsPath = ui->MinifigSettingsEdit->text();
257 mOptions->ColorConfigPath = ui->ColorConfigEdit->text();
258 mOptions->POVRayPath = ui->povrayExecutable->text();
259 mOptions->LGEOPath = ui->lgeoPath->text();
260 mOptions->DefaultAuthor = ui->authorName->text();
261 mOptions->Preferences.mMouseSensitivity = ui->mouseSensitivity->value();
262 mOptions->Preferences.mColorTheme = static_cast<lcColorTheme>(ui->ColorTheme->currentIndex());
263 mOptions->Preferences.mAutomateEdgeColor = ui->AutomateEdgeColor->isChecked();
264
265 int Language = ui->Language->currentIndex();
266 if (Language < 0 || Language > static_cast<int>(LC_ARRAY_COUNT(gLanguageLocales)))
267 Language = 0;
268 mOptions->Language = gLanguageLocales[Language];
269
270 mOptions->CheckForUpdates = ui->checkForUpdates->currentIndex();
271 mOptions->Preferences.mFixedAxes = ui->fixedDirectionKeys->isChecked();
272 mOptions->Preferences.mAutoLoadMostRecent = ui->autoLoadMostRecent->isChecked();
273 mOptions->Preferences.mRestoreTabLayout = ui->RestoreTabLayout->isChecked();
274
275 if (!ui->antiAliasing->isChecked())
276 mOptions->AASamples = 1;
277 else if (ui->antiAliasingSamples->currentIndex() == 2)
278 mOptions->AASamples = 8;
279 else if (ui->antiAliasingSamples->currentIndex() == 1)
280 mOptions->AASamples = 4;
281 else
282 mOptions->AASamples = 2;
283
284 mOptions->Preferences.mDrawEdgeLines = ui->edgeLines->isChecked();
285 mOptions->Preferences.mDrawConditionalLines = ui->ConditionalLinesCheckBox->isChecked();
286 mOptions->Preferences.mLineWidth = mLineWidthRange[0] + static_cast<float>(ui->LineWidthSlider->value()) * mLineWidthGranularity;
287 mOptions->Preferences.mAllowLOD = ui->MeshLOD->isChecked();
288 mOptions->Preferences.mMeshLODDistance = ui->MeshLODSlider->value() * mMeshLODMultiplier;
289 mOptions->Preferences.mFadeSteps = ui->FadeSteps->isChecked();
290 mOptions->Preferences.mHighlightNewParts = ui->HighlightNewParts->isChecked();
291
292 mOptions->Preferences.mDrawGridStuds = ui->gridStuds->isChecked();
293 mOptions->Preferences.mDrawGridLines = ui->gridLines->isChecked();
294 mOptions->Preferences.mGridLineSpacing = gridLineSpacing;
295 mOptions->Preferences.mDrawGridOrigin = ui->GridOriginCheckBox->isChecked();
296
297 mOptions->Preferences.mBackgroundGradient = ui->BackgroundGradientRadio->isChecked();
298 mOptions->Preferences.mDrawAxes = ui->AxisIconCheckBox->isChecked();
299 mOptions->Preferences.mAxisIconLocation = (lcAxisIconLocation)ui->AxisIconLocationCombo->currentIndex();
300 mOptions->Preferences.mViewSphereEnabled = ui->ViewSphereSizeCombo->currentIndex() > 0;
301 mOptions->Preferences.mViewSphereLocation = (lcViewSphereLocation)ui->ViewSphereLocationCombo->currentIndex();
302
303 switch (ui->ViewSphereSizeCombo->currentIndex())
304 {
305 case 3:
306 mOptions->Preferences.mViewSphereSize = 200;
307 break;
308 case 2:
309 mOptions->Preferences.mViewSphereSize = 100;
310 break;
311 case 1:
312 mOptions->Preferences.mViewSphereSize = 50;
313 break;
314 }
315
316 mOptions->Preferences.mShadingMode = (lcShadingMode)ui->ShadingMode->currentIndex();
317
318 mOptions->StudStyle = static_cast<lcStudStyle>(ui->studStyleCombo->currentIndex());
319
320 mOptions->Preferences.mDrawPreviewAxis = ui->PreviewAxisIconCheckBox->isChecked();
321 mOptions->Preferences.mPreviewViewSphereEnabled = ui->PreviewViewSphereSizeCombo->currentIndex() > 0;
322 mOptions->Preferences.mPreviewViewSphereLocation = (lcViewSphereLocation)ui->PreviewViewSphereLocationCombo->currentIndex();
323
324 switch (ui->PreviewViewSphereSizeCombo->currentIndex())
325 {
326 case 3:
327 mOptions->Preferences.mPreviewViewSphereSize = 100;
328 break;
329 case 2:
330 mOptions->Preferences.mPreviewViewSphereSize = 75;
331 break;
332 case 1:
333 mOptions->Preferences.mPreviewViewSphereSize = 50;
334 break;
335 }
336
337 QDialog::accept();
338 }
339
on_partsLibraryBrowse_clicked()340 void lcQPreferencesDialog::on_partsLibraryBrowse_clicked()
341 {
342 QString result = QFileDialog::getExistingDirectory(this, tr("Select Parts Library Folder"), ui->partsLibrary->text());
343
344 if (!result.isEmpty())
345 ui->partsLibrary->setText(QDir::toNativeSeparators(result));
346 }
347
on_partsArchiveBrowse_clicked()348 void lcQPreferencesDialog::on_partsArchiveBrowse_clicked()
349 {
350 QString result = QFileDialog::getOpenFileName(this, tr("Select Parts Library Archive"), ui->partsLibrary->text(), tr("Supported Archives (*.zip *.bin);;All Files (*.*)"));
351
352 if (!result.isEmpty())
353 ui->partsLibrary->setText(QDir::toNativeSeparators(result));
354 }
355
on_ColorConfigBrowseButton_clicked()356 void lcQPreferencesDialog::on_ColorConfigBrowseButton_clicked()
357 {
358 QString Result = QFileDialog::getOpenFileName(this, tr("Select Color Configuration File"), ui->ColorConfigEdit->text(), tr("Settings Files (*.ldr);;All Files (*.*)"));
359
360 if (!Result.isEmpty())
361 ui->ColorConfigEdit->setText(QDir::toNativeSeparators(Result));
362 }
363
on_MinifigSettingsBrowseButton_clicked()364 void lcQPreferencesDialog::on_MinifigSettingsBrowseButton_clicked()
365 {
366 QString Result = QFileDialog::getOpenFileName(this, tr("Select Minifig Settings File"), ui->MinifigSettingsEdit->text(), tr("Settings Files (*.ini);;All Files (*.*)"));
367
368 if (!Result.isEmpty())
369 ui->MinifigSettingsEdit->setText(QDir::toNativeSeparators(Result));
370 }
371
on_povrayExecutableBrowse_clicked()372 void lcQPreferencesDialog::on_povrayExecutableBrowse_clicked()
373 {
374 #ifdef Q_OS_WIN
375 QString filter(tr("Executable Files (*.exe);;All Files (*.*)"));
376 #else
377 QString filter(tr("All Files (*.*)"));
378 #endif
379
380 QString result = QFileDialog::getOpenFileName(this, tr("Select POV-Ray Executable"), ui->povrayExecutable->text(), filter);
381
382 if (!result.isEmpty())
383 ui->povrayExecutable->setText(QDir::toNativeSeparators(result));
384 }
385
on_lgeoPathBrowse_clicked()386 void lcQPreferencesDialog::on_lgeoPathBrowse_clicked()
387 {
388 QString result = QFileDialog::getExistingDirectory(this, tr("Open LGEO Folder"), ui->lgeoPath->text());
389
390 if (!result.isEmpty())
391 ui->lgeoPath->setText(QDir::toNativeSeparators(result));
392 }
393
on_ColorTheme_currentIndexChanged(int Index)394 void lcQPreferencesDialog::on_ColorTheme_currentIndexChanged(int Index)
395 {
396 Q_UNUSED(Index);
397
398 if (QMessageBox::question(this, tr("Reset Colors"), tr("Would you like to also reset the interface colors to match the color theme?")) == QMessageBox::Yes)
399 mOptions->Preferences.SetInterfaceColors(static_cast<lcColorTheme>(ui->ColorTheme->currentIndex()));
400 }
401
ColorButtonClicked()402 void lcQPreferencesDialog::ColorButtonClicked()
403 {
404 QObject* Button = sender();
405 QString Title;
406 quint32* Color = nullptr;
407 QColorDialog::ColorDialogOptions DialogOptions;
408
409 if (Button == ui->BackgroundSolidColorButton)
410 {
411 Color = &mOptions->Preferences.mBackgroundSolidColor;
412 Title = tr("Select Background Color");
413 }
414 else if (Button == ui->BackgroundGradient1ColorButton)
415 {
416 Color = &mOptions->Preferences.mBackgroundGradientColorTop;
417 Title = tr("Select Gradient Top Color");
418 }
419 else if (Button == ui->BackgroundGradient2ColorButton)
420 {
421 Color = &mOptions->Preferences.mBackgroundGradientColorBottom;
422 Title = tr("Select Gradient Bottom Color");
423 }
424 else if (Button == ui->AxesColorButton)
425 {
426 Color = &mOptions->Preferences.mAxesColor;
427 Title = tr("Select Axes Color");
428 }
429 else if (Button == ui->TextColorButton)
430 {
431 Color = &mOptions->Preferences.mTextColor;
432 Title = tr("Select Text Color");
433 }
434 else if (Button == ui->MarqueeBorderColorButton)
435 {
436 Color = &mOptions->Preferences.mMarqueeBorderColor;
437 Title = tr("Select Marquee Border Color");
438 }
439 else if (Button == ui->MarqueeFillColorButton)
440 {
441 Color = &mOptions->Preferences.mMarqueeFillColor;
442 Title = tr("Select Marquee Fill Color");
443 DialogOptions = QColorDialog::ShowAlphaChannel;
444 }
445 else if (Button == ui->OverlayColorButton)
446 {
447 Color = &mOptions->Preferences.mOverlayColor;
448 Title = tr("Select Overlay Color");
449 }
450 else if (Button == ui->ActiveViewColorButton)
451 {
452 Color = &mOptions->Preferences.mActiveViewColor;
453 Title = tr("Select Active View Color");
454 }
455 else if (Button == ui->InactiveViewColorButton)
456 {
457 Color = &mOptions->Preferences.mInactiveViewColor;
458 Title = tr("Select Inactive View Color");
459 }
460 else if (Button == ui->FadeStepsColor)
461 {
462 Color = &mOptions->Preferences.mFadeStepsColor;
463 Title = tr("Select Fade Color");
464 DialogOptions = QColorDialog::ShowAlphaChannel;
465 }
466 else if (Button == ui->HighlightNewPartsColor)
467 {
468 Color = &mOptions->Preferences.mHighlightNewPartsColor;
469 Title = tr("Select Highlight Color");
470 DialogOptions = QColorDialog::ShowAlphaChannel;
471 }
472 else if (Button == ui->gridStudColor)
473 {
474 Color = &mOptions->Preferences.mGridStudColor;
475 Title = tr("Select Grid Stud Color");
476 DialogOptions = QColorDialog::ShowAlphaChannel;
477 }
478 else if (Button == ui->gridLineColor)
479 {
480 Color = &mOptions->Preferences.mGridLineColor;
481 Title = tr("Select Grid Line Color");
482 }
483 else if (Button == ui->ViewSphereColorButton)
484 {
485 Color = &mOptions->Preferences.mViewSphereColor;
486 Title = tr("Select View Sphere Color");
487 }
488 else if (Button == ui->ViewSphereTextColorButton)
489 {
490 Color = &mOptions->Preferences.mViewSphereTextColor;
491 Title = tr("Select View Sphere Text Color");
492 }
493 else if (Button == ui->ViewSphereHighlightColorButton)
494 {
495 Color = &mOptions->Preferences.mViewSphereHighlightColor;
496 Title = tr("Select View Sphere Highlight Color");
497 }
498 else
499 return;
500
501 QColor oldColor = QColor(LC_RGBA_RED(*Color), LC_RGBA_GREEN(*Color), LC_RGBA_BLUE(*Color), LC_RGBA_ALPHA(*Color));
502 QColor newColor = QColorDialog::getColor(oldColor, this, Title, DialogOptions);
503
504 if (newColor == oldColor || !newColor.isValid())
505 return;
506
507 *Color = LC_RGBA(newColor.red(), newColor.green(), newColor.blue(), newColor.alpha());
508
509 QPixmap pix(12, 12);
510
511 newColor.setAlpha(255);
512 pix.fill(newColor);
513 ((QToolButton*)Button)->setIcon(pix);
514 }
515
on_antiAliasing_toggled()516 void lcQPreferencesDialog::on_antiAliasing_toggled()
517 {
518 ui->antiAliasingSamples->setEnabled(ui->antiAliasing->isChecked());
519 }
520
on_edgeLines_toggled()521 void lcQPreferencesDialog::on_edgeLines_toggled()
522 {
523 const bool Enable = ui->edgeLines->isChecked() || ui->ConditionalLinesCheckBox->isChecked();
524
525 ui->LineWidthSlider->setEnabled(Enable);
526 ui->LineWidthLabel->setEnabled(Enable);
527 }
528
on_ConditionalLinesCheckBox_toggled()529 void lcQPreferencesDialog::on_ConditionalLinesCheckBox_toggled()
530 {
531 const bool Enable = ui->edgeLines->isChecked() || ui->ConditionalLinesCheckBox->isChecked();
532
533 ui->LineWidthSlider->setEnabled(Enable);
534 ui->LineWidthLabel->setEnabled(Enable);
535 }
536
on_LineWidthSlider_valueChanged()537 void lcQPreferencesDialog::on_LineWidthSlider_valueChanged()
538 {
539 float Value = mLineWidthRange[0] + static_cast<float>(ui->LineWidthSlider->value()) * mLineWidthGranularity;
540 ui->LineWidthLabel->setText(QString::number(Value));
541 }
542
on_MeshLODSlider_valueChanged()543 void lcQPreferencesDialog::on_MeshLODSlider_valueChanged()
544 {
545 float Value = ui->MeshLODSlider->value() * mMeshLODMultiplier;
546 ui->MeshLODLabel->setText(QString::number(static_cast<int>(Value)));
547 }
548
on_FadeSteps_toggled()549 void lcQPreferencesDialog::on_FadeSteps_toggled()
550 {
551 ui->FadeStepsColor->setEnabled(ui->FadeSteps->isChecked());
552 }
553
on_HighlightNewParts_toggled()554 void lcQPreferencesDialog::on_HighlightNewParts_toggled()
555 {
556 ui->HighlightNewPartsColor->setEnabled(ui->HighlightNewParts->isChecked());
557 }
558
on_gridStuds_toggled()559 void lcQPreferencesDialog::on_gridStuds_toggled()
560 {
561 ui->gridStudColor->setEnabled(ui->gridStuds->isChecked());
562 }
563
on_gridLines_toggled()564 void lcQPreferencesDialog::on_gridLines_toggled()
565 {
566 ui->gridLineColor->setEnabled(ui->gridLines->isChecked());
567 ui->gridLineSpacing->setEnabled(ui->gridLines->isChecked());
568 }
569
on_PreviewViewSphereSizeCombo_currentIndexChanged(int Index)570 void lcQPreferencesDialog::on_PreviewViewSphereSizeCombo_currentIndexChanged(int Index)
571 {
572 ui->PreviewViewSphereLocationCombo->setEnabled(Index != 0);
573 }
574
on_ViewSphereSizeCombo_currentIndexChanged(int Index)575 void lcQPreferencesDialog::on_ViewSphereSizeCombo_currentIndexChanged(int Index)
576 {
577 ui->ViewSphereLocationCombo->setEnabled(Index != 0);
578 }
579
on_AutomateEdgeColor_toggled()580 void lcQPreferencesDialog::on_AutomateEdgeColor_toggled()
581 {
582 ui->AutomateEdgeColorButton->setEnabled(ui->AutomateEdgeColor->isChecked());
583 }
584
on_studStyleCombo_currentIndexChanged(int index)585 void lcQPreferencesDialog::on_studStyleCombo_currentIndexChanged(int index)
586 {
587 ui->HighContrastButton->setEnabled(lcIsHighContrast(static_cast<lcStudStyle>(index)));
588 }
589
AutomateEdgeColor()590 void lcQPreferencesDialog::AutomateEdgeColor()
591 {
592 lcAutomateEdgeColorDialog Dialog(this, sender() == ui->HighContrastButton);
593 if (Dialog.exec() == QDialog::Accepted)
594 {
595 mOptions->Preferences.mStudCylinderColor = Dialog.mStudCylinderColor;
596 mOptions->Preferences.mPartEdgeColor = Dialog.mPartEdgeColor;
597 mOptions->Preferences.mBlackEdgeColor = Dialog.mBlackEdgeColor;
598 mOptions->Preferences.mDarkEdgeColor = Dialog.mDarkEdgeColor;
599 mOptions->Preferences.mPartEdgeContrast = Dialog.mPartEdgeContrast;
600 mOptions->Preferences.mPartColorValueLDIndex = Dialog.mPartColorValueLDIndex;
601 }
602 }
603
updateCategories()604 void lcQPreferencesDialog::updateCategories()
605 {
606 QTreeWidgetItem* CategoryItem;
607 QTreeWidget* CategoriesTree = ui->categoriesTree;
608
609 CategoriesTree->clear();
610
611 for (int CategoryIndex = 0; CategoryIndex < static_cast<int>(mOptions->Categories.size()); CategoryIndex++)
612 {
613 CategoryItem = new QTreeWidgetItem(CategoriesTree, QStringList(mOptions->Categories[CategoryIndex].Name));
614 CategoryItem->setData(0, CategoryRole, QVariant(CategoryIndex));
615 }
616
617 CategoryItem = new QTreeWidgetItem(CategoriesTree, QStringList(tr("Unassigned")));
618 CategoryItem->setData(0, CategoryRole, QVariant(-1));
619 }
620
updateParts()621 void lcQPreferencesDialog::updateParts()
622 {
623 lcPiecesLibrary *Library = lcGetPiecesLibrary();
624 QTreeWidget *tree = ui->partsTree;
625
626 tree->clear();
627
628 QList<QTreeWidgetItem*> selectedItems = ui->categoriesTree->selectedItems();
629
630 if (selectedItems.empty())
631 return;
632
633 QTreeWidgetItem *categoryItem = selectedItems.first();
634 int categoryIndex = categoryItem->data(0, CategoryRole).toInt();
635
636 if (categoryIndex != -1)
637 {
638 lcArray<PieceInfo*> singleParts, groupedParts;
639
640 Library->GetCategoryEntries(mOptions->Categories[categoryIndex].Keywords.constData(), false, singleParts, groupedParts);
641
642 for (int partIndex = 0; partIndex < singleParts.GetSize(); partIndex++)
643 {
644 PieceInfo *info = singleParts[partIndex];
645
646 QStringList rowList(info->m_strDescription);
647 rowList.append(info->mFileName);
648
649 new QTreeWidgetItem(tree, rowList);
650 }
651 }
652 else
653 {
654 for (const auto& PartIt : Library->mPieces)
655 {
656 PieceInfo* Info = PartIt.second;
657
658 for (categoryIndex = 0; categoryIndex < static_cast<int>(mOptions->Categories.size()); categoryIndex++)
659 {
660 if (Library->PieceInCategory(Info, mOptions->Categories[categoryIndex].Keywords.constData()))
661 break;
662 }
663
664 if (categoryIndex == static_cast<int>(mOptions->Categories.size()))
665 {
666 QStringList rowList(Info->m_strDescription);
667 rowList.append(Info->mFileName);
668
669 new QTreeWidgetItem(tree, rowList);
670 }
671 }
672 }
673
674 tree->resizeColumnToContents(0);
675 tree->resizeColumnToContents(1);
676 }
677
on_newCategory_clicked()678 void lcQPreferencesDialog::on_newCategory_clicked()
679 {
680 lcLibraryCategory category;
681
682 lcQCategoryDialog dialog(this, &category);
683 if (dialog.exec() != QDialog::Accepted)
684 return;
685
686 mOptions->CategoriesModified = true;
687 mOptions->CategoriesDefault = false;
688 mOptions->Categories.emplace_back(std::move(category));
689
690 updateCategories();
691 ui->categoriesTree->setCurrentItem(ui->categoriesTree->topLevelItem(static_cast<int>(mOptions->Categories.size()) - 1));
692 }
693
on_editCategory_clicked()694 void lcQPreferencesDialog::on_editCategory_clicked()
695 {
696 QList<QTreeWidgetItem*> selectedItems = ui->categoriesTree->selectedItems();
697
698 if (selectedItems.empty())
699 return;
700
701 QTreeWidgetItem *categoryItem = selectedItems.first();
702 int categoryIndex = categoryItem->data(0, CategoryRole).toInt();
703
704 if (categoryIndex == -1)
705 return;
706
707 lcQCategoryDialog dialog(this, &mOptions->Categories[categoryIndex]);
708 if (dialog.exec() != QDialog::Accepted)
709 return;
710
711 mOptions->CategoriesModified = true;
712 mOptions->CategoriesDefault = false;
713
714 updateCategories();
715 ui->categoriesTree->setCurrentItem(ui->categoriesTree->topLevelItem(categoryIndex));
716 }
717
on_deleteCategory_clicked()718 void lcQPreferencesDialog::on_deleteCategory_clicked()
719 {
720 QList<QTreeWidgetItem*> selectedItems = ui->categoriesTree->selectedItems();
721
722 if (selectedItems.empty())
723 return;
724
725 QTreeWidgetItem *categoryItem = selectedItems.first();
726 int categoryIndex = categoryItem->data(0, CategoryRole).toInt();
727
728 if (categoryIndex == -1)
729 return;
730
731 QString question = tr("Are you sure you want to delete the category '%1'?").arg(mOptions->Categories[categoryIndex].Name);
732 if (QMessageBox::question(this, "LeoCAD", question, QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
733 return;
734
735 mOptions->CategoriesModified = true;
736 mOptions->CategoriesDefault = false;
737 mOptions->Categories.erase(mOptions->Categories.begin() + categoryIndex);
738
739 updateCategories();
740 }
741
on_importCategories_clicked()742 void lcQPreferencesDialog::on_importCategories_clicked()
743 {
744 QString FileName = QFileDialog::getOpenFileName(this, tr("Import Categories"), "", tr("Text Files (*.txt);;All Files (*.*)"));
745
746 if (FileName.isEmpty())
747 return;
748
749 std::vector<lcLibraryCategory> Categories;
750 if (!lcLoadCategories(FileName, Categories))
751 {
752 QMessageBox::warning(this, "LeoCAD", tr("Error loading categories file."));
753 return;
754 }
755
756 mOptions->Categories = Categories;
757 mOptions->CategoriesModified = true;
758 mOptions->CategoriesDefault = false;
759 }
760
on_exportCategories_clicked()761 void lcQPreferencesDialog::on_exportCategories_clicked()
762 {
763 QString FileName = QFileDialog::getSaveFileName(this, tr("Export Categories"), "", tr("Text Files (*.txt);;All Files (*.*)"));
764
765 if (FileName.isEmpty())
766 return;
767
768 if (!lcSaveCategories(FileName, mOptions->Categories))
769 {
770 QMessageBox::warning(this, "LeoCAD", tr("Error saving categories file."));
771 return;
772 }
773 }
774
on_resetCategories_clicked()775 void lcQPreferencesDialog::on_resetCategories_clicked()
776 {
777 if (QMessageBox::question(this, "LeoCAD", tr("Are you sure you want to load the default categories?"), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
778 return;
779
780 lcResetCategories(mOptions->Categories);
781
782 mOptions->CategoriesModified = true;
783 mOptions->CategoriesDefault = true;
784
785 updateCategories();
786 }
787
eventFilter(QObject * object,QEvent * event)788 bool lcQPreferencesDialog::eventFilter(QObject *object, QEvent *event)
789 {
790 Q_UNUSED(object);
791
792 if (event->type() == QEvent::KeyPress)
793 {
794 QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
795
796 int nextKey = keyEvent->key();
797 if (nextKey == Qt::Key_Control || nextKey == Qt::Key_Shift || nextKey == Qt::Key_Meta || nextKey == Qt::Key_Alt)
798 return true;
799
800 Qt::KeyboardModifiers state = keyEvent->modifiers();
801 QString text = QKeySequence(nextKey).toString();
802 if ((state & Qt::ShiftModifier) && (text.isEmpty() || !text.at(0).isPrint() || text.at(0).isLetter() || text.at(0).isSpace()))
803 nextKey |= Qt::SHIFT;
804 if (state & Qt::ControlModifier)
805 nextKey |= Qt::CTRL;
806 if (state & Qt::MetaModifier)
807 nextKey |= Qt::META;
808 if (state & Qt::AltModifier)
809 nextKey |= Qt::ALT;
810
811 QKeySequence ks(nextKey);
812 ui->shortcutEdit->setText(ks.toString(QKeySequence::NativeText));
813 keyEvent->accept();
814
815 return true;
816 }
817
818 if (event->type() == QEvent::Shortcut || event->type() == QEvent::KeyRelease || event->type() == QEvent::ShortcutOverride)
819 {
820 event->accept();
821 return true;
822 }
823
824 return QDialog::eventFilter(object, event);
825 }
826
updateCommandList()827 void lcQPreferencesDialog::updateCommandList()
828 {
829 ui->commandList->clear();
830 QMap<QString, QTreeWidgetItem*> sections;
831
832 for (unsigned int actionIdx = 0; actionIdx < LC_NUM_COMMANDS; actionIdx++)
833 {
834 if (!gCommands[actionIdx].ID[0])
835 continue;
836
837 const QString identifier = tr(gCommands[actionIdx].ID);
838
839 int pos = identifier.indexOf(QLatin1Char('.'));
840 int subPos = identifier.indexOf(QLatin1Char('.'), pos + 1);
841 if (subPos == -1)
842 subPos = pos;
843
844 const QString parentSection = identifier.left(pos);
845
846 if (subPos != pos)
847 {
848 if (!sections.contains(parentSection))
849 {
850 QTreeWidgetItem *categoryItem = new QTreeWidgetItem(ui->commandList, QStringList(parentSection));
851 QFont f = categoryItem->font(0);
852 f.setBold(true);
853 categoryItem->setFont(0, f);
854 sections.insert(parentSection, categoryItem);
855 ui->commandList->expandItem(categoryItem);
856 }
857 }
858
859 const QString section = identifier.left(subPos);
860 const QString subId = identifier.mid(subPos + 1);
861
862 if (!sections.contains(section))
863 {
864 QTreeWidgetItem *parent = sections[parentSection];
865 QTreeWidgetItem *categoryItem;
866 QString subSection;
867
868 if (pos != subPos)
869 subSection = identifier.mid(pos + 1, subPos - pos - 1);
870 else
871 subSection = section;
872
873 if (parent)
874 categoryItem = new QTreeWidgetItem(parent, QStringList(subSection));
875 else
876 categoryItem = new QTreeWidgetItem(ui->commandList, QStringList(subSection));
877
878 QFont f = categoryItem->font(0);
879 f.setBold(true);
880 categoryItem->setFont(0, f);
881 sections.insert(section, categoryItem);
882 ui->commandList->expandItem(categoryItem);
883 }
884
885 QTreeWidgetItem *item = new QTreeWidgetItem;
886 QKeySequence sequence(mOptions->KeyboardShortcuts.mShortcuts[actionIdx]);
887 item->setText(0, qApp->translate("Menu", gCommands[actionIdx].MenuName).remove('&').remove(QLatin1String("...")));
888 item->setText(1, sequence.toString(QKeySequence::NativeText));
889 item->setData(0, Qt::UserRole, QVariant::fromValue(actionIdx));
890
891 if (mOptions->KeyboardShortcuts.mShortcuts[actionIdx] != gCommands[actionIdx].DefaultShortcut)
892 setShortcutModified(item, true);
893
894 sections[section]->addChild(item);
895 }
896 }
897
setShortcutModified(QTreeWidgetItem * treeItem,bool modified)898 void lcQPreferencesDialog::setShortcutModified(QTreeWidgetItem *treeItem, bool modified)
899 {
900 QFont font = treeItem->font(0);
901 font.setItalic(modified);
902 treeItem->setFont(0, font);
903 font.setBold(modified);
904 treeItem->setFont(1, font);
905 }
906
commandChanged(QTreeWidgetItem * current)907 void lcQPreferencesDialog::commandChanged(QTreeWidgetItem *current)
908 {
909 if (!current || !current->data(0, Qt::UserRole).isValid())
910 {
911 ui->shortcutEdit->setText(QString());
912 ui->shortcutGroup->setEnabled(false);
913 return;
914 }
915
916 ui->shortcutGroup->setEnabled(true);
917
918 int shortcutIndex = qvariant_cast<int>(current->data(0, Qt::UserRole));
919 QKeySequence key(mOptions->KeyboardShortcuts.mShortcuts[shortcutIndex]);
920 ui->shortcutEdit->setText(key.toString(QKeySequence::NativeText));
921 }
922
on_KeyboardFilterEdit_textEdited(const QString & Text)923 void lcQPreferencesDialog::on_KeyboardFilterEdit_textEdited(const QString& Text)
924 {
925 if (Text.isEmpty())
926 {
927 std::function<void(QTreeWidgetItem*)> ShowItems = [&ShowItems](QTreeWidgetItem* ParentItem)
928 {
929 for (int ChildIdx = 0; ChildIdx < ParentItem->childCount(); ChildIdx++)
930 ShowItems(ParentItem->child(ChildIdx));
931
932 ParentItem->setHidden(false);
933 };
934
935 ShowItems(ui->commandList->invisibleRootItem());
936 }
937 else
938 {
939 std::function<bool(QTreeWidgetItem*,bool)> ShowItems = [&ShowItems, &Text](QTreeWidgetItem* ParentItem, bool ForceVisible)
940 {
941 ForceVisible |= (bool)ParentItem->text(0).contains(Text, Qt::CaseInsensitive) | (bool)ParentItem->text(1).contains(Text, Qt::CaseInsensitive);
942 bool Visible = ForceVisible;
943
944 for (int ChildIdx = 0; ChildIdx < ParentItem->childCount(); ChildIdx++)
945 Visible |= ShowItems(ParentItem->child(ChildIdx), ForceVisible);
946
947 ParentItem->setHidden(!Visible);
948
949 return Visible;
950 };
951
952 ShowItems(ui->commandList->invisibleRootItem(), false);
953 }
954 }
955
on_shortcutAssign_clicked()956 void lcQPreferencesDialog::on_shortcutAssign_clicked()
957 {
958 QTreeWidgetItem* CurrentItem = ui->commandList->currentItem();
959
960 if (!CurrentItem || !CurrentItem->data(0, Qt::UserRole).isValid())
961 return;
962
963 uint ShortcutIndex = CurrentItem->data(0, Qt::UserRole).toUInt();
964 QString (&Shortcuts)[LC_NUM_COMMANDS] = mOptions->KeyboardShortcuts.mShortcuts;
965
966 if (ShortcutIndex >= LC_ARRAY_COUNT(Shortcuts))
967 return;
968
969 QString NewShortcut = ui->shortcutEdit->text();
970
971 if (!NewShortcut.isEmpty())
972 {
973 for (uint ExistingIndex = 0; ExistingIndex < LC_ARRAY_COUNT(Shortcuts); ExistingIndex++)
974 {
975 if (NewShortcut == Shortcuts[ExistingIndex] && ExistingIndex != ShortcutIndex)
976 {
977 QString ActionText = qApp->translate("Menu", gCommands[ExistingIndex].MenuName).remove('&').remove(QLatin1String("..."));
978 QString QuestionText = tr("The shortcut '%1' is already assigned to '%2'. Do you want to replace it?").arg(NewShortcut, ActionText);
979
980 if (QMessageBox::question(this, tr("Override Shortcut"), QuestionText, QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
981 return;
982
983 mOptions->KeyboardShortcuts.mShortcuts[ExistingIndex].clear();
984
985 std::function<QTreeWidgetItem* (QTreeWidgetItem*)> FindItem = [&FindItem, ExistingIndex](QTreeWidgetItem* ParentItem) -> QTreeWidgetItem*
986 {
987 for (int ChildIdx = 0; ChildIdx < ParentItem->childCount(); ChildIdx++)
988 {
989 QTreeWidgetItem* ChildItem = ParentItem->child(ChildIdx);
990 uint ChildIndex = ChildItem->data(0, Qt::UserRole).toUInt();
991
992 if (ChildIndex == ExistingIndex)
993 return ChildItem;
994
995 QTreeWidgetItem* ExistingItem = FindItem(ChildItem);
996
997 if (ExistingItem)
998 return ExistingItem;
999 }
1000
1001 return nullptr;
1002 };
1003
1004 QTreeWidgetItem* ExistingItem = FindItem(ui->commandList->invisibleRootItem());
1005
1006 if (ExistingItem)
1007 {
1008 ExistingItem->setText(1, QString());
1009 setShortcutModified(ExistingItem, gCommands[ShortcutIndex].DefaultShortcut[0] != 0);
1010 }
1011 }
1012 }
1013 }
1014
1015 mOptions->KeyboardShortcuts.mShortcuts[ShortcutIndex] = NewShortcut;
1016 CurrentItem->setText(1, NewShortcut);
1017
1018 setShortcutModified(CurrentItem, mOptions->KeyboardShortcuts.mShortcuts[ShortcutIndex] != gCommands[ShortcutIndex].DefaultShortcut);
1019
1020 mOptions->KeyboardShortcutsModified = true;
1021 mOptions->KeyboardShortcutsDefault = false;
1022 }
1023
on_shortcutRemove_clicked()1024 void lcQPreferencesDialog::on_shortcutRemove_clicked()
1025 {
1026 ui->shortcutEdit->setText(QString());
1027
1028 on_shortcutAssign_clicked();
1029 }
1030
on_shortcutsImport_clicked()1031 void lcQPreferencesDialog::on_shortcutsImport_clicked()
1032 {
1033 QString FileName = QFileDialog::getOpenFileName(this, tr("Import shortcuts"), "", tr("Text Files (*.txt);;All Files (*.*)"));
1034
1035 if (FileName.isEmpty())
1036 return;
1037
1038 lcKeyboardShortcuts Shortcuts;
1039 if (!Shortcuts.Load(FileName))
1040 {
1041 QMessageBox::warning(this, "LeoCAD", tr("Error loading keyboard shortcuts file."));
1042 return;
1043 }
1044
1045 mOptions->KeyboardShortcuts = Shortcuts;
1046
1047 mOptions->KeyboardShortcutsModified = true;
1048 mOptions->KeyboardShortcutsDefault = false;
1049 }
1050
on_shortcutsExport_clicked()1051 void lcQPreferencesDialog::on_shortcutsExport_clicked()
1052 {
1053 QString FileName = QFileDialog::getSaveFileName(this, tr("Export shortcuts"), "", tr("Text Files (*.txt);;All Files (*.*)"));
1054
1055 if (FileName.isEmpty())
1056 return;
1057
1058 if (!mOptions->KeyboardShortcuts.Save(FileName))
1059 {
1060 QMessageBox::warning(this, "LeoCAD", tr("Error saving keyboard shortcuts file."));
1061 return;
1062 }
1063 }
1064
on_shortcutsReset_clicked()1065 void lcQPreferencesDialog::on_shortcutsReset_clicked()
1066 {
1067 if (QMessageBox::question(this, "LeoCAD", tr("Are you sure you want to load the default keyboard shortcuts?"), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
1068 return;
1069
1070 mOptions->KeyboardShortcuts.Reset();
1071 updateCommandList();
1072
1073 mOptions->KeyboardShortcutsModified = true;
1074 mOptions->KeyboardShortcutsDefault = true;
1075 }
1076
UpdateMouseTree()1077 void lcQPreferencesDialog::UpdateMouseTree()
1078 {
1079 ui->mouseTree->clear();
1080
1081 for (int ToolIdx = 0; ToolIdx < static_cast<int>(lcTool::Count); ToolIdx++)
1082 UpdateMouseTreeItem(ToolIdx);
1083 }
1084
UpdateMouseTreeItem(int ItemIndex)1085 void lcQPreferencesDialog::UpdateMouseTreeItem(int ItemIndex)
1086 {
1087 auto GetShortcutText = [](Qt::MouseButton Button, Qt::KeyboardModifiers Modifiers)
1088 {
1089 QString Shortcut = QKeySequence(Modifiers).toString(QKeySequence::NativeText);
1090
1091 switch (Button)
1092 {
1093 case Qt::LeftButton:
1094 Shortcut += tr("Left Button");
1095 break;
1096
1097 case Qt::MiddleButton:
1098 Shortcut += tr("Middle Button");
1099 break;
1100
1101 case Qt::RightButton:
1102 Shortcut += tr("Right Button");
1103 break;
1104
1105 default:
1106 Shortcut.clear();
1107 }
1108 return Shortcut;
1109 };
1110
1111 QString Shortcut1 = GetShortcutText(mOptions->MouseShortcuts.mShortcuts[ItemIndex].Button1, mOptions->MouseShortcuts.mShortcuts[ItemIndex].Modifiers1);
1112 QString Shortcut2 = GetShortcutText(mOptions->MouseShortcuts.mShortcuts[ItemIndex].Button2, mOptions->MouseShortcuts.mShortcuts[ItemIndex].Modifiers2);
1113
1114 QTreeWidgetItem* Item = ui->mouseTree->topLevelItem(ItemIndex);
1115
1116 if (Item)
1117 {
1118 Item->setText(1, Shortcut1);
1119 Item->setText(2, Shortcut2);
1120 }
1121 else
1122 new QTreeWidgetItem(ui->mouseTree, QStringList() << tr(gToolNames[ItemIndex]) << Shortcut1 << Shortcut2);
1123 }
1124
on_mouseAssign_clicked()1125 void lcQPreferencesDialog::on_mouseAssign_clicked()
1126 {
1127 QTreeWidgetItem* Current = ui->mouseTree->currentItem();
1128
1129 if (!Current)
1130 return;
1131
1132 int ButtonIndex = ui->mouseButton->currentIndex();
1133 Qt::MouseButton Button = Qt::NoButton;
1134 Qt::KeyboardModifiers Modifiers = Qt::NoModifier;
1135
1136 if (ButtonIndex)
1137 {
1138 switch (ButtonIndex)
1139 {
1140 case 1:
1141 Button = Qt::LeftButton;
1142 break;
1143
1144 case 2:
1145 Button = Qt::MiddleButton;
1146 break;
1147
1148 case 3:
1149 Button = Qt::RightButton;
1150 break;
1151 }
1152
1153 if (ui->mouseControl->isChecked())
1154 Modifiers |= Qt::ControlModifier;
1155
1156 if (ui->mouseShift->isChecked())
1157 Modifiers |= Qt::ShiftModifier;
1158
1159 if (ui->mouseAlt->isChecked())
1160 Modifiers |= Qt::AltModifier;
1161
1162 for (int ToolIdx = 0; ToolIdx < static_cast<int>(lcTool::Count); ToolIdx++)
1163 {
1164 if (ToolIdx == ButtonIndex)
1165 continue;
1166
1167 if (mOptions->MouseShortcuts.mShortcuts[ToolIdx].Button2 == Button && mOptions->MouseShortcuts.mShortcuts[ToolIdx].Modifiers2 == Modifiers)
1168 {
1169 if (QMessageBox::question(this, tr("Override Shortcut"), tr("This shortcut is already assigned to '%1', do you want to replace it?").arg(tr(gToolNames[ToolIdx])), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
1170 return;
1171
1172 mOptions->MouseShortcuts.mShortcuts[ToolIdx].Button2 = Qt::NoButton;
1173 mOptions->MouseShortcuts.mShortcuts[ToolIdx].Modifiers2 = Qt::NoModifier;
1174 }
1175
1176 if (mOptions->MouseShortcuts.mShortcuts[ToolIdx].Button1 == Button && mOptions->MouseShortcuts.mShortcuts[ToolIdx].Modifiers1 == Modifiers)
1177 {
1178 if (QMessageBox::question(this, tr("Override Shortcut"), tr("This shortcut is already assigned to '%1', do you want to replace it?").arg(tr(gToolNames[ToolIdx])), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
1179 return;
1180
1181 mOptions->MouseShortcuts.mShortcuts[ToolIdx].Button1 = mOptions->MouseShortcuts.mShortcuts[ToolIdx].Button2;
1182 mOptions->MouseShortcuts.mShortcuts[ToolIdx].Modifiers1 = mOptions->MouseShortcuts.mShortcuts[ToolIdx].Modifiers2;
1183 mOptions->MouseShortcuts.mShortcuts[ToolIdx].Button2 = Qt::NoButton;
1184 mOptions->MouseShortcuts.mShortcuts[ToolIdx].Modifiers2 = Qt::NoModifier;
1185
1186 UpdateMouseTreeItem(ToolIdx);
1187 }
1188 }
1189 }
1190
1191 int ItemIndex = ui->mouseTree->indexOfTopLevelItem(Current);
1192 mOptions->MouseShortcuts.mShortcuts[ItemIndex].Button2 = mOptions->MouseShortcuts.mShortcuts[ItemIndex].Button1;
1193 mOptions->MouseShortcuts.mShortcuts[ItemIndex].Modifiers2 = mOptions->MouseShortcuts.mShortcuts[ItemIndex].Modifiers1;
1194 mOptions->MouseShortcuts.mShortcuts[ItemIndex].Button1 = Button;
1195 mOptions->MouseShortcuts.mShortcuts[ItemIndex].Modifiers1 = Modifiers;
1196
1197 mOptions->MouseShortcutsModified = true;
1198 mOptions->MouseShortcutsDefault = false;
1199
1200 UpdateMouseTreeItem(ItemIndex);
1201 }
1202
on_mouseRemove_clicked()1203 void lcQPreferencesDialog::on_mouseRemove_clicked()
1204 {
1205 QTreeWidgetItem* Current = ui->mouseTree->currentItem();
1206
1207 if (!Current)
1208 return;
1209
1210 int ItemIndex = ui->mouseTree->indexOfTopLevelItem(Current);
1211 mOptions->MouseShortcuts.mShortcuts[ItemIndex].Button1 = mOptions->MouseShortcuts.mShortcuts[ItemIndex].Button2;
1212 mOptions->MouseShortcuts.mShortcuts[ItemIndex].Modifiers1 = mOptions->MouseShortcuts.mShortcuts[ItemIndex].Modifiers2;
1213 mOptions->MouseShortcuts.mShortcuts[ItemIndex].Button2 = Qt::NoButton;
1214 mOptions->MouseShortcuts.mShortcuts[ItemIndex].Modifiers2 = Qt::NoModifier;
1215
1216 mOptions->MouseShortcutsModified = true;
1217 mOptions->MouseShortcutsDefault = false;
1218
1219 UpdateMouseTreeItem(ItemIndex);
1220 MouseTreeItemChanged(Current);
1221 }
1222
on_MouseImportButton_clicked()1223 void lcQPreferencesDialog::on_MouseImportButton_clicked()
1224 {
1225 QString FileName = QFileDialog::getOpenFileName(this, tr("Import Shortcuts"), "", tr("Text Files (*.txt);;All Files (*.*)"));
1226
1227 if (FileName.isEmpty())
1228 return;
1229
1230 lcMouseShortcuts Shortcuts;
1231 if (!Shortcuts.Load(FileName))
1232 {
1233 QMessageBox::warning(this, "LeoCAD", tr("Error loading mouse shortcuts file."));
1234 return;
1235 }
1236
1237 mOptions->MouseShortcuts = Shortcuts;
1238 UpdateMouseTree();
1239
1240 mOptions->MouseShortcutsModified = true;
1241 mOptions->MouseShortcutsDefault = false;
1242 }
1243
on_MouseExportButton_clicked()1244 void lcQPreferencesDialog::on_MouseExportButton_clicked()
1245 {
1246 QString FileName = QFileDialog::getSaveFileName(this, tr("Export Shortcuts"), "", tr("Text Files (*.txt);;All Files (*.*)"));
1247
1248 if (FileName.isEmpty())
1249 return;
1250
1251 if (!mOptions->MouseShortcuts.Save(FileName))
1252 QMessageBox::warning(this, "LeoCAD", tr("Error saving mouse shortcuts file."));
1253 }
1254
on_mouseReset_clicked()1255 void lcQPreferencesDialog::on_mouseReset_clicked()
1256 {
1257 if (QMessageBox::question(this, "LeoCAD", tr("Are you sure you want to load the default mouse shortcuts?"), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
1258 return;
1259
1260 mOptions->MouseShortcuts.Reset();
1261 UpdateMouseTree();
1262
1263 mOptions->MouseShortcutsModified = true;
1264 mOptions->MouseShortcutsDefault = true;
1265 }
1266
MouseTreeItemChanged(QTreeWidgetItem * Current)1267 void lcQPreferencesDialog::MouseTreeItemChanged(QTreeWidgetItem* Current)
1268 {
1269 if (!Current)
1270 {
1271 ui->MouseShortcutGroup->setEnabled(false);
1272 return;
1273 }
1274
1275 ui->MouseShortcutGroup->setEnabled(true);
1276
1277 int ToolIndex = ui->mouseTree->indexOfTopLevelItem(Current);
1278
1279 Qt::MouseButton Button = mOptions->MouseShortcuts.mShortcuts[ToolIndex].Button1;
1280
1281 switch (Button)
1282 {
1283 case Qt::LeftButton:
1284 ui->mouseButton->setCurrentIndex(1);
1285 break;
1286
1287 case Qt::MiddleButton:
1288 ui->mouseButton->setCurrentIndex(2);
1289 break;
1290
1291 case Qt::RightButton:
1292 ui->mouseButton->setCurrentIndex(3);
1293 break;
1294
1295 default:
1296 ui->mouseButton->setCurrentIndex(0);
1297 break;
1298 }
1299
1300 Qt::KeyboardModifiers Modifiers = mOptions->MouseShortcuts.mShortcuts[ToolIndex].Modifiers1;
1301 ui->mouseControl->setChecked((Modifiers & Qt::ControlModifier) != 0);
1302 ui->mouseShift->setChecked((Modifiers & Qt::ShiftModifier) != 0);
1303 ui->mouseAlt->setChecked((Modifiers & Qt::AltModifier) != 0);
1304 }
1305