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