1 /****************************************************************************
2 **
3 ** This file is part of the LibreCAD project, a 2D CAD program
4 **
5 ** Copyright (C) 2018 A. Stebich (librecad@mail.lordofbikes.de)
6 ** Copyright (C) 2018 Simon Wells <simonrwells@gmail.com>
7 ** Copyright (C) 2015-2016 ravas (github.com/r-a-v-a-s)
8 ** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
9 ** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
10 **
11 ** This file may be distributed and/or modified under the terms of the
12 ** GNU General Public License version 2 as published by the Free Software
13 ** Foundation and appearing in the file gpl-2.0.txt included in the
14 ** packaging of this file.
15 **
16 ** This program is distributed in the hope that it will be useful,
17 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 ** GNU General Public License for more details.
20 **
21 ** You should have received a copy of the GNU General Public License
22 ** along with this program; if not, write to the Free Software
23 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
24 **
25 ** This copyright notice MUST APPEAR in all copies of the script!
26 **
27 **********************************************************************/
28 #include <clocale>
29 #include "main.h"
30 
31 #include <QDebug>
32 #include <QApplication>
33 #include <QSplashScreen>
34 #include <QSettings>
35 #include <QMessageBox>
36 #include <QFileInfo>
37 
38 #include "rs_fontlist.h"
39 #include "rs_patternlist.h"
40 #include "rs_settings.h"
41 #include "rs_system.h"
42 #include "qg_dlginitial.h"
43 
44 #include "lc_application.h"
45 #include "qc_applicationwindow.h"
46 #include "rs_debug.h"
47 
48 #include "console_dxf2pdf.h"
49 
50 
51 /**
52  * Main. Creates Application window.
53  */
main(int argc,char ** argv)54 int main(int argc, char** argv)
55 {
56     QT_REQUIRE_VERSION(argc, argv, "5.2.1");
57 
58     // Check first two arguments in order to decide if we want to run librecad
59     // as console dxf2pdf tool. On Linux we can create a link to librecad
60     // executable and  name it dxf2pdf. So, we can run either:
61     //
62     //     librecad dxf2pdf [options] ...
63     //
64     // or just:
65     //
66     //     dxf2pdf [options] ...
67     //
68     for (int i = 0; i < qMin(argc, 2); i++) {
69         QString arg(argv[i]);
70         if (i == 0) {
71             arg = QFileInfo(QFile::decodeName(argv[i])).baseName();
72         }
73         if (arg.compare("dxf2pdf") == 0) {
74             return console_dxf2pdf(argc, argv);
75         }
76     }
77 
78     RS_DEBUG->setLevel(RS_Debug::D_WARNING);
79 
80     LC_Application app(argc, argv);
81     QCoreApplication::setOrganizationName("LibreCAD");
82     QCoreApplication::setApplicationName("LibreCAD");
83     QCoreApplication::setApplicationVersion(XSTR(LC_VERSION));
84 
85 #if (QT_VERSION >= QT_VERSION_CHECK(5, 7, 0))
86     QGuiApplication::setDesktopFileName("librecad.desktop");
87 #endif
88 
89     QSettings settings;
90 
91     bool first_load = settings.value("Startup/FirstLoad", 1).toBool();
92 
93     const QString lpDebugSwitch0("-d"),lpDebugSwitch1("--debug") ;
94     const QString help0("-h"), help1("--help");
95     bool allowOptions=true;
96     QList<int> argClean;
97     for (int i=0; i<argc; i++)
98     {
99         QString argstr(argv[i]);
100         if(allowOptions&&QString::compare("--", argstr)==0)
101         {
102             allowOptions=false;
103             continue;
104         }
105         if (allowOptions && (help0.compare(argstr, Qt::CaseInsensitive)==0 ||
106                              help1.compare(argstr, Qt::CaseInsensitive)==0 ))
107         {
108             qDebug()<<"Usage: librecad [command] <options> <dxf file>";
109             qDebug()<<"";
110             qDebug()<<"Commands:";
111             qDebug()<<"";
112             qDebug()<<"  dxf2pdf\tRun librecad as console dxf2pdf tool. Use -h for help.";
113             qDebug()<<"";
114             qDebug()<<"Options:";
115             qDebug()<<"";
116             qDebug()<<"  -h, --help\tdisplay this message";
117             qDebug()<<"  -d, --debug <level>";
118             qDebug()<<"";
119             RS_DEBUG->print( RS_Debug::D_NOTHING, "possible debug levels:");
120             RS_DEBUG->print( RS_Debug::D_NOTHING, "    %d Nothing", RS_Debug::D_NOTHING);
121             RS_DEBUG->print( RS_Debug::D_NOTHING, "    %d Critical", RS_Debug::D_CRITICAL);
122             RS_DEBUG->print( RS_Debug::D_NOTHING, "    %d Error", RS_Debug::D_ERROR);
123             RS_DEBUG->print( RS_Debug::D_NOTHING, "    %d Warning", RS_Debug::D_WARNING);
124             RS_DEBUG->print( RS_Debug::D_NOTHING, "    %d Notice", RS_Debug::D_NOTICE);
125             RS_DEBUG->print( RS_Debug::D_NOTHING, "    %d Informational", RS_Debug::D_INFORMATIONAL);
126             RS_DEBUG->print( RS_Debug::D_NOTHING, "    %d Debugging", RS_Debug::D_DEBUGGING);
127             exit(0);
128         }
129         if ( allowOptions&& (argstr.startsWith(lpDebugSwitch0, Qt::CaseInsensitive) ||
130                              argstr.startsWith(lpDebugSwitch1, Qt::CaseInsensitive) ))
131         {
132             argClean<<i;
133 
134             // to control the level of debugging output use --debug with level 0-6, e.g. --debug3
135             // for a list of debug levels use --debug?
136             // if no level follows, the debugging level is set
137             argstr.remove(QRegExp("^"+lpDebugSwitch0));
138             argstr.remove(QRegExp("^"+lpDebugSwitch1));
139             char level;
140             if(argstr.size()==0)
141             {
142                 if(i+1<argc)
143                 {
144                     if(QRegExp("\\d*").exactMatch(argv[i+1]))
145                     {
146                         ++i;
147                         qDebug()<<"reading "<<argv[i]<<" as debugging level";
148                         level=argv[i][0];
149                         argClean<<i;
150                     }
151                     else
152                         level='3';
153                 }
154                 else
155                     level='3'; //default to D_WARNING
156             }
157             else
158                 level=argstr.toStdString()[0];
159 
160             switch(level)
161             {
162             case '?' :
163                 RS_DEBUG->print( RS_Debug::D_NOTHING, "possible debug levels:");
164                 RS_DEBUG->print( RS_Debug::D_NOTHING, "    %d Nothing", RS_Debug::D_NOTHING);
165                 RS_DEBUG->print( RS_Debug::D_NOTHING, "    %d Critical", RS_Debug::D_CRITICAL);
166                 RS_DEBUG->print( RS_Debug::D_NOTHING, "    %d Error", RS_Debug::D_ERROR);
167                 RS_DEBUG->print( RS_Debug::D_NOTHING, "    %d Warning", RS_Debug::D_WARNING);
168                 RS_DEBUG->print( RS_Debug::D_NOTHING, "    %d Notice", RS_Debug::D_NOTICE);
169                 RS_DEBUG->print( RS_Debug::D_NOTHING, "    %d Informational", RS_Debug::D_INFORMATIONAL);
170                 RS_DEBUG->print( RS_Debug::D_NOTHING, "    %d Debugging", RS_Debug::D_DEBUGGING);
171                 return 0;
172 
173             case '0' + RS_Debug::D_NOTHING :
174                 RS_DEBUG->setLevel( RS_Debug::D_NOTHING);
175                 break;
176 
177             case '0' + RS_Debug::D_CRITICAL :
178                 RS_DEBUG->setLevel( RS_Debug::D_CRITICAL);
179                 break;
180 
181             case '0' + RS_Debug::D_ERROR :
182                 RS_DEBUG->setLevel( RS_Debug::D_ERROR);
183                 break;
184 
185             case '0' + RS_Debug::D_WARNING :
186                 RS_DEBUG->setLevel( RS_Debug::D_WARNING);
187                 break;
188 
189             case '0' + RS_Debug::D_NOTICE :
190                 RS_DEBUG->setLevel( RS_Debug::D_NOTICE);
191                 break;
192 
193             case '0' + RS_Debug::D_INFORMATIONAL :
194                 RS_DEBUG->setLevel( RS_Debug::D_INFORMATIONAL);
195                 break;
196 
197             case '0' + RS_Debug::D_DEBUGGING :
198                 RS_DEBUG->setLevel( RS_Debug::D_DEBUGGING);
199                 break;
200 
201             default :
202                 RS_DEBUG->setLevel(RS_Debug::D_DEBUGGING);
203                 break;
204             }
205         }
206     }
207     RS_DEBUG->print("param 0: %s", argv[0]);
208 
209     QFileInfo prgInfo( QFile::decodeName(argv[0]) );
210     QString prgDir(prgInfo.absolutePath());
211     RS_SETTINGS->init(app.organizationName(), app.applicationName());
212     RS_SYSTEM->init(app.applicationName(), app.applicationVersion(), XSTR(QC_APPDIR), prgDir);
213 
214     // parse command line arguments that might not need a launched program:
215     QStringList fileList = handleArgs(argc, argv, argClean);
216 
217     QString unit = settings.value("Defaults/Unit", "Invalid").toString();
218 
219     // show initial config dialog:
220     if (first_load)
221     {
222         RS_DEBUG->print("main: show initial config dialog..");
223         QG_DlgInitial di(nullptr);
224         QPixmap pxm(":/main/intro_librecad.png");
225         di.setPixmap(pxm);
226         if (di.exec())
227         {
228             RS_SETTINGS->beginGroup("/Defaults");
229             unit = RS_SETTINGS->readEntry("/Unit", "None");
230             RS_SETTINGS->endGroup();
231         }
232         RS_DEBUG->print("main: show initial config dialog: OK");
233     }
234 
235     auto splash = new QSplashScreen;
236 
237     bool show_splash = settings.value("Startup/ShowSplash", 1).toBool();
238 
239     if (show_splash)
240     {
241         QPixmap pixmap(":/main/splash_librecad.png");
242         splash->setPixmap(pixmap);
243         splash->setAttribute(Qt::WA_DeleteOnClose);
244         splash->show();
245         splash->showMessage(QObject::tr("Loading.."),
246                             Qt::AlignRight|Qt::AlignBottom, Qt::black);
247         app.processEvents();
248         RS_DEBUG->print("main: splashscreen: OK");
249     }
250 
251     RS_DEBUG->print("main: init fontlist..");
252     RS_FONTLIST->init();
253     RS_DEBUG->print("main: init fontlist: OK");
254 
255     RS_DEBUG->print("main: init patternlist..");
256     RS_PATTERNLIST->init();
257     RS_DEBUG->print("main: init patternlist: OK");
258 
259     RS_DEBUG->print("main: loading translation..");
260 
261     settings.beginGroup("Appearance");
262     QString lang = settings.value("Language", "en").toString();
263     QString langCmd = settings.value("LanguageCmd", "en").toString();
264     settings.endGroup();
265 
266     RS_SYSTEM->loadTranslation(lang, langCmd);
267     RS_DEBUG->print("main: loading translation: OK");
268 
269     RS_DEBUG->print("main: creating main window..");
270     QC_ApplicationWindow appWin;
271 #ifdef Q_OS_MAC
272     app.installEventFilter(&appWin);
273 #endif
274     RS_DEBUG->print("main: setting caption");
275     appWin.setWindowTitle(app.applicationName());
276 
277     RS_DEBUG->print("main: show main window");
278 
279     settings.beginGroup("Geometry");
280     int windowWidth = settings.value("WindowWidth", 1024).toInt();
281     int windowHeight = settings.value("WindowHeight", 1024).toInt();
282     int windowX = settings.value("WindowX", 32).toInt();
283     int windowY = settings.value("WindowY", 32).toInt();
284     settings.endGroup();
285 
286     settings.beginGroup("Defaults");
287     if( !settings.contains("UseQtFileOpenDialog")) {
288 #ifdef Q_OS_LINUX
289         // on Linux don't use native file dialog
290         // because of case insensitive filters (issue #791)
291         settings.setValue("UseQtFileOpenDialog", QVariant(1));
292 #else
293         settings.setValue("UseQtFileOpenDialog", QVariant(0));
294 #endif
295     }
296     settings.endGroup();
297 
298     if (!first_load)
299         appWin.resize(windowWidth, windowHeight);
300 
301     appWin.move(windowX, windowY);
302 
303     bool maximize = settings.value("Startup/Maximize", 0).toBool();
304 
305     if (maximize || first_load)
306         appWin.showMaximized();
307     else
308         appWin.show();
309 
310     RS_DEBUG->print("main: set focus");
311     appWin.setFocus();
312     RS_DEBUG->print("main: creating main window: OK");
313 
314     if (show_splash)
315     {
316         RS_DEBUG->print("main: updating splash");
317         splash->raise();
318         splash->showMessage(QObject::tr("Loading..."),
319                 Qt::AlignRight|Qt::AlignBottom, Qt::black);
320         RS_DEBUG->print("main: processing events");
321         qApp->processEvents();
322         RS_DEBUG->print("main: updating splash: OK");
323     }
324 
325     // Set LC_NUMERIC so that entering numeric values uses . as the decimal separator
326     setlocale(LC_NUMERIC, "C");
327 
328     RS_DEBUG->print("main: loading files..");
329 #ifdef Q_OS_MAC
330     // get the file list from LC_Application
331     fileList << app.fileList();
332 #endif
333     bool files_loaded = false;
334     for (QStringList::Iterator it = fileList.begin(); it != fileList.end(); ++it )
335     {
336         if (show_splash)
337         {
338             splash->showMessage(QObject::tr("Loading File %1..")
339                     .arg(QDir::toNativeSeparators(*it)),
340             Qt::AlignRight|Qt::AlignBottom, Qt::black);
341             qApp->processEvents();
342         }
343         appWin.slotFileOpen(*it);
344         files_loaded = true;
345     }
346     RS_DEBUG->print("main: loading files: OK");
347 
348     if (!files_loaded)
349     {
350         appWin.slotFileNewNew();
351     }
352 
353     if (show_splash)
354         splash->finish(&appWin);
355     else
356         delete splash;
357 
358     if (first_load)
359         settings.setValue("Startup/FirstLoad", 0);
360 
361     RS_DEBUG->print("main: entering Qt event loop");
362 
363     int return_code = app.exec();
364 
365     RS_DEBUG->print("main: exited Qt event loop");
366 
367     return return_code;
368 }
369 
370 
371 /**
372  * Handles command line arguments that might not require a GUI.
373  *
374  * @return list of files to load on startup.
375  */
handleArgs(int argc,char ** argv,const QList<int> & argClean)376 QStringList handleArgs(int argc, char** argv, const QList<int>& argClean)
377 {
378     RS_DEBUG->print("main: handling args..");
379     QStringList ret;
380 
381     bool doexit = false;
382 
383     for (int i=1; i<argc; i++)
384     {
385         if(argClean.indexOf(i)>=0) continue;
386         if (!QString(argv[i]).startsWith("-"))
387         {
388             QString fname = QDir::toNativeSeparators(
389             QFileInfo(QFile::decodeName(argv[i])).absoluteFilePath());
390             ret.append(fname);
391         }
392         else if (QString(argv[i])=="--exit")
393         {
394             doexit = true;
395         }
396     }
397     if (doexit)
398     {
399         exit(0);
400     }
401     RS_DEBUG->print("main: handling args: OK");
402     return ret;
403 }
404 
405