1 #include "LDViewMainWindow.h"
2 #include <QtGlobal>
3 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
4 #include <QScreen>
5 #else
6 #include <QDesktopWidget>
7 #endif
8 #include "misc.h"
9 #include <QToolButton>
10 #include <TCFoundation/TCLocalStrings.h>
11 #include <QObject>
12 #include <QAction>
13 
14 TCStringArray *LDViewMainWindow::recentFiles = NULL;
15 
LDViewMainWindow(QApplication * a)16 LDViewMainWindow::LDViewMainWindow(QApplication *a)
17 	:QMainWindow(),Ui::LDView(),
18 #ifdef __APPLE__
19 	openRecentMenu(NULL),
20 #endif // __APPLE__
21 	fileSeparatorIndex(-1),
22 	toolbarMaxStep(new QLabel),
23 	toolbarCurrentStep(new QLabel),
24 	toolbarStepLabel(new QLabel("Step :")),
25 	toolbarViewAngle(NULL),
26 	toolbarWireframeMenu(new QMenu(this)),
27 	edgeMenu(new QMenu(this)),
28 	bfcMenu(new QMenu(this)),
29 	primitiveMenu(new QMenu(this))
30 {
31     setupUi(this);
32 	toolbar->insertWidget(toolbarFirstStep,toolbarStepLabel);
33 	toolbar->insertWidget(toolbarFirstStep,toolbarCurrentStep);
34 	toolbar->insertWidget(toolbarFirstStep,toolbarMaxStep);
35     if (modelViewer) modelViewer->setMainWindow(this);
36     connect( fileOpenAction, SIGNAL( triggered() ), this, SLOT( fileOpen() ) );
37     connect( fileSaveSettingsAction, SIGNAL( triggered() ), this, SLOT( fileSaveSettings() ) );
38     connect( fileExportAction, SIGNAL( triggered() ), this, SLOT( fileExport() ) );
39     connect( fileExportOptionAction, SIGNAL( triggered() ), this, SLOT( fileExportOption() ) );
40 	connect( file3DSExportOptionAction, SIGNAL( triggered() ), this, SLOT( file3DSExportOption() ) );
41     connect( fileJPEGOptionsAction, SIGNAL( triggered() ), this, SLOT( fileJPEGOptions() ) );
42     connect( fileSaveAction, SIGNAL( triggered() ), this, SLOT( fileSave() ) );
43     connect( filePrintAction, SIGNAL( triggered() ), this, SLOT( filePrint() ) );
44     connect( fileExitAction, SIGNAL( triggered() ), this, SLOT( fileExit() ) );
45     connect( helpContentsAction, SIGNAL( triggered() ), this, SLOT( helpContents() ) );
46     connect( helpAboutAction, SIGNAL( triggered() ), this, SLOT( helpAbout() ) );
47     connect( helpAboutQtAction, SIGNAL( triggered() ), this, SLOT( helpAboutQt() ) );
48     connect( editPreferencesAction, SIGNAL( triggered() ), this, SLOT( editPreferences() ) );
49     connect( viewToolBarAction, SIGNAL( toggled(bool) ), this, SLOT( viewToolBar(bool) ) );
50     connect( toolbarWireframeAction, SIGNAL( toggled(bool) ), this, SLOT( toolbarWireframe(bool) ) );
51     connect( toolbarEdgeAction, SIGNAL( toggled(bool) ), this, SLOT( toolbarEdge(bool) ) );
52     connect( toolbarPrimitiveSubstitutionAction, SIGNAL( toggled(bool) ), this, SLOT( toolbarPrimitiveSubstitution(bool) ) );
53     connect( toolbarLightingAction, SIGNAL( toggled(bool) ), this, SLOT( toolbarLighting(bool) ) );
54     connect( toolbarBFCAction, SIGNAL( toggled(bool) ), this, SLOT( toolbarBFC(bool) ) );
55     connect( toolbarAxesAction, SIGNAL( toggled(bool) ), this, SLOT( toolbarAxes(bool) ) );
56     connect( toolbarSeamsAction, SIGNAL( toggled(bool) ), this, SLOT( toolbarSeams(bool) ) );
57     connect( viewStatusBarAction, SIGNAL( toggled(bool) ), this, SLOT( viewStatusBar(bool) ) );
58     connect( viewLatitudeRotationAction, SIGNAL( toggled(bool) ), this, SLOT( latitudeRotation(bool) ) );
59     connect( viewKeepRightSideUpAction, SIGNAL( toggled(bool) ), this, SLOT( keepRightSideUp(bool) ) );
60     connect( viewFullScreenAction, SIGNAL( triggered() ), this, SLOT( viewFullScreen() ) );
61     connect( viewResetViewAction, SIGNAL( triggered() ), this, SLOT( viewResetView() ) );
62     connect( helpOpenGLDriverInfoAction, SIGNAL( triggered() ), this, SLOT( helpOpenGLDriverInfo() ) );
63     connect( fileLDrawDirAction, SIGNAL( triggered() ), this, SLOT( fileLDrawDir() ) );
64     connect( fileExtraDirAction, SIGNAL( triggered() ), this, SLOT( fileExtraDir() ) );
65     connect( fileCheckForUpdatesAction, SIGNAL( triggered() ), this, SLOT( fileCheckForUpdates() ) );
66     connect( fileReloadAction, SIGNAL( triggered() ), this, SLOT( fileReload() ) );
67     connect( viewShowErrorsAction, SIGNAL( triggered() ), this, SLOT( viewShowErrors() ) );
68     connect( pollActionGroup, SIGNAL( triggered(QAction*) ), this, SLOT( pollChanged(QAction*) ) );
69     connect( viewZoomToFitAction, SIGNAL( triggered() ), this, SLOT( viewZoomToFit() ) );
70 	connect( viewRightSideUpAction, SIGNAL( triggered() ), this, SLOT( viewRightSideUp() ) );
71 	connect( viewCameraLocationAction, SIGNAL( triggered() ), this, SLOT (viewCameraLocation() ) );
72 	connect( viewRotationCenterAction, SIGNAL( triggered() ), this, SLOT (viewRotationCenter() ) );
73     connect( viewModeActionGroup, SIGNAL( triggered(QAction*) ), this, SLOT( viewModeChanged(QAction*) ) );
74     connect( frontViewAngleAction, SIGNAL( triggered() ), this, SLOT( frontViewAngle() ) );
75     connect( backViewAngleAction, SIGNAL( triggered() ), this, SLOT( backViewAngle() ) );
76     connect( leftViewAngleAction, SIGNAL( triggered() ), this, SLOT( leftViewAngle() ) );
77     connect( rightViewAngleAction, SIGNAL( triggered() ), this, SLOT( rightViewAngle() ) );
78     connect( topViewAngleAction, SIGNAL( triggered() ), this, SLOT( topViewAngle() ) );
79     connect( bottomViewAngleAction, SIGNAL( triggered() ), this, SLOT( bottomViewAngle() ) );
80     connect( latLongViewAngleAction, SIGNAL( triggered() ), this, SLOT( latLongViewAngle() ) );
81     connect( isoViewAngleAction, SIGNAL( triggered() ), this, SLOT( isoViewAngle() ) );
82     connect( saveDefaultViewAngleAction, SIGNAL( triggered() ), this, SLOT( saveDefaultViewAngle() ) );
83     connect( fileCancelLoadAction, SIGNAL( triggered() ), this, SLOT( fileCancelLoad() ) );
84     connect( showViewInfoAction, SIGNAL( triggered() ), this, SLOT( showViewInfo() ) );
85     connect( showPovCameraAction, SIGNAL( triggered() ), this, SLOT( showPovCamera() ) );
86     connect( showPovAspectRatioAction, SIGNAL( toggled(bool) ), this, SLOT( showPovAspectRatio(bool) ) );
87     connect( toolsPartListAction, SIGNAL( triggered() ), this, SLOT( toolsPartList() ) );
88     connect( toolbarPrevStep, SIGNAL( triggered() ), this, SLOT( prevStep() ) );
89     connect( toolbarNextStep, SIGNAL( triggered() ), this, SLOT( nextStep() ) );
90     connect( toolbarFirstStep, SIGNAL( triggered() ), this, SLOT( firstStep() ) );
91     connect( toolbarLastStep, SIGNAL( triggered() ), this, SLOT( lastStep() ) );
92     connect( stepGoto, SIGNAL( triggered() ), this, SLOT( gotoStep() ) );
93     connect( toolsModelTreeAction, SIGNAL( triggered() ), this, SLOT( toolsModelTree() ) );
94     connect( toolsBoundingBoxAction, SIGNAL( triggered() ), this, SLOT( toolsBoundingBox() ) );
95     connect( toolsMpdModelSelectionAction, SIGNAL( triggered() ), this, SLOT( toolsMpdModelSelection() ) );
96 
97     connect(fileMenu, SIGNAL(aboutToShow()), this, SLOT(doFileMenuAboutToShow()));
98     connect(editMenu, SIGNAL(aboutToShow()), this, SLOT(doEditMenuAboutToShow()));
99     connect(viewMenu, SIGNAL(aboutToShow()), this, SLOT(doViewMenuAboutToShow()));
100     connect(toolsMenu,SIGNAL(aboutToShow()), this, SLOT(doToolsMenuAboutToShow()));
101     connect(helpMenu, SIGNAL(aboutToShow()), this, SLOT(doHelpMenuAboutToShow()));
102 
103 	int cnt,i;
104 	QAction *item;
105     for ( cnt = i = 0; ; i++)
106     {
107         item = fileMenu->actions()[i];
108         if (item->isSeparator())
109         {
110             if (++cnt == 2)
111                 break;
112         }
113     }
114     fileSeparatorIndex = i;
115 #ifdef __APPLE__
116 /*
117     fileMenu->removeItemAt(fileSeparatorIndex);
118     fileSeparatorIndex = -1;
119     openRecentMenu = new QMenu(this, "openRecentMenu");
120     fileMenu->insertItem("Open Recent", openRecentMenu, -1, 1);
121 */
122 #endif // __APPLE__
123     if (!recentFiles)
124     {
125         recentFiles = new TCStringArray(10);
126         populateRecentFiles();
127     }
128     populateRecentFileMenuItems();
129 	setupStandardSizes();
130 #ifdef __APPLE__
131         // Since Preferences is the only item in the edit menu, we need to
132         // delete the edit menu on the Mac, since the item is going to get
133         // magically moved to the LDView menu.  The problem is, if we delete
134         // the edit menu, the magic stops working, since it's apparently all
135         // done on the fly.  So, we're going to create a new fully-functional
136         // Preferences menu item at the top of the File menu, and THEN delete
137         // the edit menu.  This newly created menu item won't be visible to the
138         // user, but it will make the other one continue to function after the
139         // deletion of the edit menu.
140 //        fileMenu->insertItem("Preferences", this, SLOT(doPreferences()),
141 //            0, -1, 0);
142         // Remove the (empty without Preferences) edit menu.
143 //        menuBar->removeItem(menuBar->idAt(1));
144 #endif //__APPLE__
145 	toolbarViewAngle = new QToolButton(toolbar);
146 	toolbarViewAngle->setMenu(viewingAnglePopupMenu);
147 	toolbarViewAngle->setPopupMode(QToolButton::InstantPopup);
148 	toolbarViewAngle->setIcon(QPixmap( ":/images/Icons/toolbar_view.png"));
149 	toolbarViewAngle->setEnabled(false);
150 	toolbar->insertWidget(editPreferencesAction,toolbarViewAngle);
151 	connect( wireframeFogAction, SIGNAL( toggled(bool) ), this, SLOT( toolbarWireframeFog(bool) ) );
152 	connect( wireframeRemoveHiddenLinesAction, SIGNAL( toggled(bool) ), this, SLOT( toolbarWireframeRemoveHiddenLines(bool) ) );
153 	toolbarWireframeMenu->addAction(wireframeFogAction);
154 	toolbarWireframeMenu->addAction(wireframeRemoveHiddenLinesAction);
155 	QToolButton *toolbarWireframe =
156 		(QToolButton *)toolbar->widgetForAction(toolbarWireframeAction);
157 	if (toolbarWireframe != NULL)
158 	{
159 		toolbarWireframe->setMenu(toolbarWireframeMenu);
160 		toolbarWireframe->setPopupMode(QToolButton::MenuButtonPopup);
161 	}
162 	connect( textureStudAction, SIGNAL( toggled(bool) ), this, SLOT( textureStud(bool) ) );
163 	primitiveMenu->addAction(textureStudAction);
164 	QToolButton *toolbarPrimitives =
165 		(QToolButton *)toolbar->widgetForAction(toolbarPrimitiveSubstitutionAction);
166 	if (toolbarPrimitives)
167 	{
168 		toolbarPrimitives->setMenu(primitiveMenu);
169 		toolbarPrimitives->setPopupMode(QToolButton::MenuButtonPopup);
170 	}
171 	connect( edgeShowEdgeOnlyAction, SIGNAL( toggled(bool) ), this, SLOT( edgeShowEdgeOnly(bool) ) );
172 	connect( edgeConditionalLineAction, SIGNAL( toggled(bool) ), this, SLOT( edgeConditionalLine(bool) ) );
173 	connect( edgeHighQualityAction, SIGNAL( toggled(bool) ), this, SLOT( edgeHighQuality(bool) ) );
174 	connect( edgeAlwaysBlackAction, SIGNAL( toggled(bool) ), this, SLOT( edgeAlwaysBlack(bool) ) );
175 	edgeMenu->addAction(edgeShowEdgeOnlyAction);
176 	edgeMenu->addAction(edgeConditionalLineAction);
177 	edgeMenu->addAction(edgeHighQualityAction);
178 	edgeMenu->addAction(edgeAlwaysBlackAction);
179 	QToolButton *toolbarEdge =
180 		(QToolButton *)toolbar->widgetForAction(toolbarEdgeAction);
181 	if (toolbarEdge)
182 	{
183 		toolbarEdge->setMenu(edgeMenu);
184 		toolbarEdge->setPopupMode(QToolButton::MenuButtonPopup);
185 	}
186 	connect( bfcRedBackFacesAction, SIGNAL( toggled(bool) ), this, SLOT( bfcRedBackFaces(bool) ) );
187 	connect( bfcGreenFrontFacesAction, SIGNAL( toggled(bool) ), this, SLOT( bfcGreenFrontFaces(bool) ) );
188 	connect( bfcBlueNeutralFacesAction, SIGNAL( toggled(bool) ), this, SLOT( bfcBlueNeutralFaces(bool) ) );
189 	bfcMenu->addAction(bfcRedBackFacesAction);
190 	bfcMenu->addAction(bfcGreenFrontFacesAction);
191 	bfcMenu->addAction(bfcBlueNeutralFacesAction);
192 	QToolButton *toolbarBfc =
193 		(QToolButton *)toolbar->widgetForAction(toolbarBFCAction);
194 	if (toolbarBfc)
195 	{
196 		toolbarBfc->setMenu(bfcMenu);
197 		toolbarBfc->setPopupMode(QToolButton::MenuButtonPopup);
198 	}
199     modelViewer->setApplication(a);
200 }
201 
standardSizeSelected()202 void LDViewMainWindow::standardSizeSelected()
203 {
204 	QAction *action = qobject_cast<QAction *>(sender());
205     QString text;
206 #if QT_VERSION >= 0x60000
207 	QRegularExpression sep( "\\s+" );
208 #else
209     QRegExp sep( "\\s+" );
210 #endif
211     text = action->text();
212     if (! text.isNull())
213     {
214         int w,h;
215         bool ok;
216         w = text.section(sep,0,0).toInt(&ok);
217         h = text.section(sep,2,2).toInt(&ok);
218         resize(w + size().width() -
219                                modelViewer->getModelViewer()->getWidth(),
220                            h + size().height() -
221                                modelViewer->getModelViewer()->getHeight());
222     }
223 }
224 
setupStandardSizes()225 void LDViewMainWindow::setupStandardSizes()
226 {
227 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
228 	QSize workArea = QGuiApplication::primaryScreen()->availableGeometry().size();
229 #else
230     QSize workArea = QApplication::desktop()->availableGeometry(this).size();
231 #endif
232     QSize windowSize = frameSize();
233     LDrawModelViewer::getStandardSizes(workArea.width() - windowSize.width() +
234                                        modelViewer->getModelViewer()->getWidth(),
235                                        workArea.height() - windowSize.height() +
236                                        modelViewer->getModelViewer()->getHeight(),
237                                        standardSizes);
238     standardSizesPopupMenu->clear();
239     for (size_t i = 0; i < standardSizes.size(); i++)
240     {
241         QString qs;
242         ucstringtoqstring(qs, standardSizes[i].name);
243         QAction *act = standardSizesPopupMenu->addAction(qs);
244 		connect( act, SIGNAL( triggered() ), this, SLOT(standardSizeSelected()) );
245     }
246 }
247 
pollChanged(QAction * action)248 void LDViewMainWindow::pollChanged(QAction *action)
249 {
250 	LDVPollMode newMode = LDVPollNone;
251     if (action == promptPollingAction)
252     {
253         newMode = LDVPollPrompt;
254     }
255     else if (action == autoPollingAction)
256     {
257         newMode = LDVPollAuto;
258     }
259     else if (action == backgroundPollingAction)
260     {
261         newMode = LDVPollBackground;
262     }
263 	modelViewer->doPollChanged(newMode);
264 }
265 
setPollAction(LDVPollMode mode)266 void LDViewMainWindow::setPollAction(LDVPollMode mode)
267 {
268 	QAction *pollAction;
269     switch (mode)
270     {
271     case LDVPollPrompt:
272         pollAction = promptPollingAction;
273         break;
274     case LDVPollAuto:
275         pollAction = autoPollingAction;
276         break;
277     case LDVPollBackground:
278         pollAction = backgroundPollingAction;
279         break;
280     default:
281 		pollAction = noPollingAction;
282         break;
283     }
284     pollAction->setChecked(true);
285 
286 }
287 
setMenuItemsEnabled(QMenu * menu,bool enabled)288 void LDViewMainWindow::setMenuItemsEnabled(QMenu *menu, bool enabled)
289 {
290     int count = menu->actions().count();
291     int i;
292 
293     for (i = 0; i < count; i++)
294     {
295         menu->actions()[i]->setEnabled(enabled);
296     }
297 }
298 
doFileMenuAboutToShow(void)299 void LDViewMainWindow::doFileMenuAboutToShow(void)
300 {
301     if (modelViewer->isLoading())
302     {
303         setMenuItemsEnabled(fileMenu, false);
304         fileCancelLoadAction->setEnabled(true);
305     }
306     else
307     {
308         setMenuItemsEnabled(fileMenu, true);
309         fileCancelLoadAction->setEnabled(false);
310         if (!modelViewer->getModelViewer() || !modelViewer->getModelViewer()->getMainTREModel())
311         {
312             fileReloadAction->setEnabled(false);
313 			fileSaveAction->setEnabled(false);
314 			fileExportAction->setEnabled(false);
315 			filePrintAction->setEnabled(false);
316 			fileCancelLoadAction->setEnabled(false);
317         }
318     }
319 }
320 
doEditMenuAboutToShow(void)321 void LDViewMainWindow::doEditMenuAboutToShow(void)
322 {
323     if (modelViewer->isLoading())
324     {
325         setMenuItemsEnabled(editMenu, false);
326     }
327     else
328     {
329         setMenuItemsEnabled(editMenu, true);
330     }
331 }
332 
doViewMenuAboutToShow(void)333 void LDViewMainWindow::doViewMenuAboutToShow(void)
334 {
335     if (modelViewer->isLoading())
336     {
337         setMenuItemsEnabled(viewMenu, false);
338     }
339     else
340     {
341         if (!modelViewer->getModelViewer() || !modelViewer->getModelViewer()->getMainTREModel())
342         {
343             setMenuItemsEnabled(viewMenu, false);
344             viewToolBarAction->setEnabled(true);
345 			viewStatusBarAction->setEnabled(true);
346         }
347         else
348         {
349             setMenuItemsEnabled(viewMenu, true);
350 			viewLatitudeRotationAction->setEnabled( modelViewer->getViewMode() == LDInputHandler::VMExamine);
351 			viewKeepRightSideUpAction->setEnabled( modelViewer->getViewMode() == LDInputHandler::VMFlyThrough);
352         }
353     }
354 	//setupStandardSizes();
355 }
356 
doToolsMenuAboutToShow(void)357 void LDViewMainWindow::doToolsMenuAboutToShow(void)
358 {
359     if (modelViewer->isLoading())
360     {
361         setMenuItemsEnabled(toolsMenu, false);
362     }
363     else
364     {
365         if (!modelViewer->getModelViewer() || !modelViewer->getModelViewer()->getMainTREModel())
366         {
367             setMenuItemsEnabled(toolsMenu, false);
368         }
369         else
370             setMenuItemsEnabled(toolsMenu, true);
371     }
372 }
373 
doHelpMenuAboutToShow(void)374 void LDViewMainWindow::doHelpMenuAboutToShow(void)
375 {
376     setMenuItemsEnabled(helpMenu, !modelViewer->isLoading());
377 }
378 
truncateFilename(const char * filename)379 char *LDViewMainWindow::truncateFilename(const char *filename)
380 {
381     if (filename)
382     {
383         int len = strlen(filename);
384 
385         if (len > 40)
386         {
387             char *retValue = new char[44];
388 
389             strncpy(retValue, filename, 10);
390             strcpy(retValue + 10, "...");
391             strcat(retValue, filename + len - 30);
392             return retValue;
393         }
394         else
395         {
396             return copyString(filename);
397         }
398     }
399     else
400     {
401         return NULL;
402     }
403 }
404 
405 
clearRecentFileMenuItems(void)406 void LDViewMainWindow::clearRecentFileMenuItems(void)
407 {
408 #ifdef __APPLE__
409     if (openRecentMenu)
410     {
411         openRecentMenu->clear();
412     }
413 #else // __APPLE__
414     QAction *item;
415     int index = fileSeparatorIndex + 1;
416     int i;
417     int count = fileMenu->actions().count();
418 
419     for (i = index; i < count - 1; i++)
420     {
421         item = fileMenu->actions()[index];
422         fileMenu->removeAction(item);
423     }
424 #endif // __APPLE__
425 }
426 
populateRecentFiles(void)427 void LDViewMainWindow::populateRecentFiles(void)
428 {
429     int i;
430     long maxRecentFiles = Preferences::getMaxRecentFiles();
431 
432     recentFiles->removeAll();
433     for (i = 1; i <= maxRecentFiles; i++)
434     {
435         char *filename = Preferences::getRecentFile(i);
436 
437         if (filename)
438         {
439             recentFiles->addString(filename);
440             delete filename;
441         }
442         else
443         {
444             recentFiles->addString(NULL);
445         }
446     }
447 }
448 
populateRecentFileMenuItems(void)449 void LDViewMainWindow::populateRecentFileMenuItems(void)
450 {
451 #ifdef __APPLE__
452     if (!openRecentMenu)
453     {
454         return;
455     }
456 #endif // __APPLE__
457     clearRecentFileMenuItems();
458 
459     if (recentFiles->stringAtIndex(0))
460     {
461         int i;
462         long maxRecentFiles = Preferences::getMaxRecentFiles();
463 
464         for (i = 0; i < maxRecentFiles; i++)
465         {
466             char *filename = truncateFilename(recentFiles->stringAtIndex(i));
467 
468             if (filename)
469             {
470 #ifdef __APPLE__
471                 QMenu *menu = openRecentMenu;
472 #else // __APPLE__
473                 QMenu *menu = fileMenu;
474 #endif // __APPLE__
475 				QAction *act;
476 				act = new QAction(filename,this);
477 				act->setData(i);
478 				menu->insertAction(fileMenu->actions()[fileMenu->actions().count() - 1],
479 								   act);
480 				connect( act, SIGNAL( triggered() ), this, SLOT(doRecentFile()) );
481                 delete filename;
482             }
483         }
484 #ifndef __APPLE__
485         fileMenu->insertSeparator(fileMenu->actions()[fileMenu->actions().count() - 1]);
486 #endif // __APPLE__
487     }
488 }
489 
recordRecentFiles(void)490 void LDViewMainWindow::recordRecentFiles(void)
491 {
492     int i;
493     long maxRecentFiles = Preferences::getMaxRecentFiles();
494 
495     for (i = 1; i <= maxRecentFiles; i++)
496     {
497         char *filename = recentFiles->stringAtIndex(i - 1);
498 
499         Preferences::setRecentFile(i, filename);
500     }
501 }
502 
doRecentFile()503 void LDViewMainWindow::doRecentFile()
504 {
505 	QAction *action = qobject_cast<QAction *>(sender());
506 	modelViewer->doRecentFile(action->data().toInt());
507 }
508 
509