1 /************************************************************************
2  **
3  **  @file   def.cpp
4  **  @author Roman Telezhynskyi <dismine(at)gmail.com>
5  **  @date   11 6, 2015
6  **
7  **  @brief
8  **  @copyright
9  **  This source code is part of the Valentina project, a pattern making
10  **  program, whose allow create and modeling patterns of clothing.
11  **  Copyright (C) 2015 Valentina project
12  **  <https://gitlab.com/smart-pattern/valentina> All Rights Reserved.
13  **
14  **  Valentina is free software: you can redistribute it and/or modify
15  **  it under the terms of the GNU General Public License as published by
16  **  the Free Software Foundation, either version 3 of the License, or
17  **  (at your option) any later version.
18  **
19  **  Valentina is distributed in the hope that it will be useful,
20  **  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  **  GNU General Public License for more details.
23  **
24  **  You should have received a copy of the GNU General Public License
25  **  along with Valentina.  If not, see <http://www.gnu.org/licenses/>.
26  **
27  *************************************************************************/
28 
29 #include "def.h"
30 
31 #include <QApplication>
32 #include <QChar>
33 #include <QColor>
34 #include <QComboBox>
35 #include <QCursor>
36 #include <QDir>
37 #include <QDirIterator>
38 #include <QFileInfo>
39 #include <QGuiApplication>
40 #include <QImage>
41 #include <QLatin1Char>
42 #include <QList>
43 #include <QMap>
44 #include <QMessageLogger>
45 #include <QObject>
46 #include <QPixmap>
47 #include <QPrinterInfo>
48 #include <QProcess>
49 #include <QRgb>
50 #include <QStaticStringData>
51 #include <QStringData>
52 #include <QStringDataPtr>
53 #include <QtDebug>
54 #include <QPixmapCache>
55 #include <QGraphicsItem>
56 #include <QGlobalStatic>
57 #include <QDesktopServices>
58 #include <QUrl>
59 
60 #include "vabstractapplication.h"
61 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
62 #   include "vdatastreamenum.h"
63 #endif
64 #include "../ifc/exception/vexception.h"
65 
66 const qreal   defCurveApproximationScale = 0.5;
67 const qreal   minCurveApproximationScale = 0.2;
68 const qreal   maxCurveApproximationScale = 10.0;
69 
70 const int minLabelFontSize = 5;
71 const int maxLabelFontSize = 100;
72 
73 //functions
74 const QString degTorad_F = QStringLiteral("degTorad");
75 const QString radTodeg_F = QStringLiteral("radTodeg");
76 const QString sin_F   = QStringLiteral("sin");
77 const QString cos_F   = QStringLiteral("cos");
78 const QString tan_F   = QStringLiteral("tan");
79 const QString asin_F  = QStringLiteral("asin");
80 const QString acos_F  = QStringLiteral("acos");
81 const QString atan_F  = QStringLiteral("atan");
82 const QString sinh_F  = QStringLiteral("sinh");
83 const QString cosh_F  = QStringLiteral("cosh");
84 const QString tanh_F  = QStringLiteral("tanh");
85 const QString asinh_F = QStringLiteral("asinh");
86 const QString acosh_F = QStringLiteral("acosh");
87 const QString atanh_F = QStringLiteral("atanh");
88 const QString sinD_F   = QStringLiteral("sinD");
89 const QString cosD_F   = QStringLiteral("cosD");
90 const QString tanD_F   = QStringLiteral("tanD");
91 const QString asinD_F  = QStringLiteral("asinD");
92 const QString acosD_F  = QStringLiteral("acosD");
93 const QString atanD_F  = QStringLiteral("atanD");
94 const QString log2_F  = QStringLiteral("log2");
95 const QString log10_F = QStringLiteral("log10");
96 const QString log_F   = QStringLiteral("log");
97 const QString ln_F    = QStringLiteral("ln");
98 const QString exp_F   = QStringLiteral("exp");
99 const QString sqrt_F  = QStringLiteral("sqrt");
100 const QString sign_F  = QStringLiteral("sign");
101 const QString rint_F  = QStringLiteral("rint");
102 const QString r2cm_F  = QStringLiteral("r2cm");
103 const QString csrCm_F = QStringLiteral("csrCm");
104 const QString csrInch_F = QStringLiteral("csrInch");
105 const QString abs_F   = QStringLiteral("abs");
106 const QString min_F   = QStringLiteral("min");
107 const QString max_F   = QStringLiteral("max");
108 const QString sum_F   = QStringLiteral("sum");
109 const QString avg_F   = QStringLiteral("avg");
110 const QString fmod_F  = QStringLiteral("fmod");
111 const QString warning_F = QStringLiteral("warning");
112 
113 const QStringList builInFunctions
114 {
115     degTorad_F, radTodeg_F,
116     sin_F, cos_F, tan_F,
117     asin_F, acos_F, atan_F,
118     sinh_F, cosh_F, tanh_F,
119     asinh_F, acosh_F, atanh_F,
120     sinD_F, cosD_F, tanD_F,
121     asinD_F, acosD_F, atanD_F,
122     log2_F, log10_F, log_F,
123     ln_F,
124     exp_F,
125     sqrt_F,
126     sign_F,
127     rint_F, r2cm_F,
128     csrCm_F, csrInch_F,
129     abs_F,
130     min_F, max_F,
131     sum_F,
132     avg_F,
133     fmod_F,
134     warning_F
135 };
136 
137 const QString pl_size          = QStringLiteral("size");
138 const QString pl_height        = QStringLiteral("height");
139 const QString pl_hip           = QStringLiteral("hip");
140 const QString pl_waist         = QStringLiteral("waist");
141 const QString pl_sizeLabel     = QStringLiteral("sizeLabel");
142 const QString pl_heightLabel   = QStringLiteral("heightLabel");
143 const QString pl_hipLabel      = QStringLiteral("hipLabel");
144 const QString pl_waistLabel    = QStringLiteral("waistLabel");
145 const QString pl_date          = QStringLiteral("date");
146 const QString pl_time          = QStringLiteral("time");
147 const QString pl_birthDate     = QStringLiteral("birthDate");
148 const QString pl_patternName   = QStringLiteral("patternName");
149 const QString pl_patternNumber = QStringLiteral("patternNumber");
150 const QString pl_author        = QStringLiteral("author");
151 const QString pl_customer      = QStringLiteral("customer");
152 const QString pl_email         = QStringLiteral("email");
153 const QString pl_userMaterial  = QStringLiteral("userMaterial");
154 const QString pl_pExt          = QStringLiteral("pExt");
155 const QString pl_pUnits        = QStringLiteral("pUnits");
156 const QString pl_pFileName     = QStringLiteral("pFileName");
157 const QString pl_mFileName     = QStringLiteral("mFileName");
158 const QString pl_mExt          = QStringLiteral("mExt");
159 const QString pl_mUnits        = QStringLiteral("mUnits");
160 const QString pl_mSizeUnits    = QStringLiteral("mSizeUnits");
161 const QString pl_pLetter       = QStringLiteral("pLetter");
162 const QString pl_pAnnotation   = QStringLiteral("pAnnotation");
163 const QString pl_pOrientation  = QStringLiteral("pOrientation");
164 const QString pl_pRotation     = QStringLiteral("pRotation");
165 const QString pl_pTilt         = QStringLiteral("pTilt");
166 const QString pl_pFoldPosition = QStringLiteral("pFoldPosition");
167 const QString pl_pName         = QStringLiteral("pName");
168 const QString pl_pQuantity     = QStringLiteral("pQuantity");
169 const QString pl_mFabric       = QStringLiteral("mFabric");
170 const QString pl_mLining       = QStringLiteral("mLining");
171 const QString pl_mInterfacing  = QStringLiteral("mInterfacing");
172 const QString pl_mInterlining  = QStringLiteral("mInterlining");
173 const QString pl_wCut          = QStringLiteral("wCut");
174 const QString pl_wOnFold       = QStringLiteral("wOnFold");
175 const QString pl_measurement   = QStringLiteral("measurement_");
176 const QString pl_finalMeasurement = QStringLiteral("finalMeasurement_");
177 
178 const QString cursorArrowOpenHand = QStringLiteral("://cursor/cursor-arrow-openhand.png");
179 const QString cursorArrowCloseHand = QStringLiteral("://cursor/cursor-arrow-closehand.png");
180 
181 const QString unitMM   = QStringLiteral("mm");
182 const QString unitCM   = QStringLiteral("cm");
183 const QString unitINCH = QStringLiteral("inch");
184 const QString unitPX   = QStringLiteral("px");
185 
186 const QString valentinaNamespace = QStringLiteral("valentina");
187 
188 //---------------------------------------------------------------------------------------------------------------------
QPixmapFromCache(const QString & pixmapPath)189 QPixmap QPixmapFromCache(const QString &pixmapPath)
190 {
191     QPixmap pixmap;
192 
193     if (not QPixmapCache::find(pixmapPath, &pixmap))
194     {
195         pixmap = QPixmap(pixmapPath);
196         QPixmapCache::insert(pixmapPath, pixmap);
197     }
198     return pixmap;
199 }
200 
201 //---------------------------------------------------------------------------------------------------------------------
SetItemOverrideCursor(QGraphicsItem * item,const QString & pixmapPath,int hotX,int hotY)202 void SetItemOverrideCursor(QGraphicsItem *item, const QString &pixmapPath, int hotX, int hotY)
203 {
204 #ifndef QT_NO_CURSOR
205     SCASSERT(item != nullptr)
206     item->setCursor(QCursor(QPixmapFromCache(pixmapPath), hotX, hotY));
207 #else
208     Q_UNUSED(item)
209     Q_UNUSED(pixmapPath)
210     Q_UNUSED(hotX)
211     Q_UNUSED(hotY)
212 #endif
213 }
214 
215 //---------------------------------------------------------------------------------------------------------------------
SupportedLocales()216 QStringList SupportedLocales()
217 {
218     return QStringList
219     {
220         "ru_RU",
221         "uk_UA",
222         "de_DE",
223         "cs_CZ",
224         "he_IL",
225         "fr_FR",
226         "it_IT",
227         "nl_NL",
228         "id_ID",
229         "es_ES",
230         "fi_FI",
231         "en_US",
232         "en_CA",
233         "en_IN",
234         "ro_RO",
235         "zh_CN",
236         "pt_BR",
237         "el_GR",
238         "pl_PL"
239     };
240 }
241 
242 //---------------------------------------------------------------------------------------------------------------------
243 /**
244  * @brief strippedName the function call around curFile to exclude the path to the file.
245  * @param fullFileName full path to the file.
246  * @return file name.
247  */
StrippedName(const QString & fullFileName)248 QString StrippedName(const QString &fullFileName)
249 {
250     return QFileInfo(fullFileName).fileName();
251 }
252 
253 //---------------------------------------------------------------------------------------------------------------------
RelativeMPath(const QString & patternPath,const QString & absoluteMPath)254 QString RelativeMPath(const QString &patternPath, const QString &absoluteMPath)
255 {
256     if (patternPath.isEmpty() || absoluteMPath.isEmpty())
257     {
258         return absoluteMPath;
259     }
260 
261     if (QFileInfo(absoluteMPath).isRelative())
262     {
263         return absoluteMPath;
264     }
265 
266     return QFileInfo(patternPath).absoluteDir().relativeFilePath(absoluteMPath);
267 }
268 
269 //---------------------------------------------------------------------------------------------------------------------
AbsoluteMPath(const QString & patternPath,const QString & relativeMPath)270 QString AbsoluteMPath(const QString &patternPath, const QString &relativeMPath)
271 {
272     if (patternPath.isEmpty() || relativeMPath.isEmpty())
273     {
274         return relativeMPath;
275     }
276 
277     if (QFileInfo(relativeMPath).isAbsolute())
278     {
279         return relativeMPath;
280     }
281 
282     return QFileInfo(QFileInfo(patternPath).absoluteDir(), relativeMPath).absoluteFilePath();
283 }
284 
285 //---------------------------------------------------------------------------------------------------------------------
GetMinPrinterFields(const QSharedPointer<QPrinter> & printer)286 QMarginsF GetMinPrinterFields(const QSharedPointer<QPrinter> &printer)
287 {
288     QPageLayout layout = printer->pageLayout();
289     layout.setUnits(QPageLayout::Millimeter);
290     const QMarginsF minMargins = layout.minimumMargins();
291 
292     QMarginsF min;
293     min.setLeft(UnitConvertor(minMargins.left(), Unit::Mm, Unit::Px));
294     min.setRight(UnitConvertor(minMargins.right(), Unit::Mm, Unit::Px));
295     min.setTop(UnitConvertor(minMargins.top(), Unit::Mm, Unit::Px));
296     min.setBottom(UnitConvertor(minMargins.bottom(), Unit::Mm, Unit::Px));
297     return min;
298 }
299 
300 //---------------------------------------------------------------------------------------------------------------------
GetPrinterFields(const QSharedPointer<QPrinter> & printer)301 auto GetPrinterFields(const QSharedPointer<QPrinter> &printer) -> QMarginsF
302 {
303     if (printer.isNull())
304     {
305         return {};
306     }
307 
308     // We can't use Unit::Px because our dpi in most cases is different
309     const QMarginsF m = printer->pageLayout().margins(QPageLayout::Millimeter);
310 
311     QMarginsF def;
312     def.setLeft(UnitConvertor(m.left(), Unit::Mm, Unit::Px));
313     def.setRight(UnitConvertor(m.right(), Unit::Mm, Unit::Px));
314     def.setTop(UnitConvertor(m.top(), Unit::Mm, Unit::Px));
315     def.setBottom(UnitConvertor(m.bottom(), Unit::Mm, Unit::Px));
316     return def;
317 }
318 
319 //---------------------------------------------------------------------------------------------------------------------
darkenPixmap(const QPixmap & pixmap)320 QPixmap darkenPixmap(const QPixmap &pixmap)
321 {
322     QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
323     const int imgh = img.height();
324     const int imgw = img.width();
325     for (int y = 0; y < imgh; ++y)
326     {
327         for (int x = 0; x < imgw; ++x)
328         {
329             int h, s, v;
330             QRgb pixel = img.pixel(x, y);
331             const int a = qAlpha(pixel);
332             QColor hsvColor(pixel);
333             hsvColor.getHsv(&h, &s, &v);
334             s = qMin(100, s * 2);
335             v = v / 2;
336             hsvColor.setHsv(h, s, v);
337             pixel = hsvColor.rgb();
338             img.setPixel(x, y, qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel), a));
339         }
340     }
341     return QPixmap::fromImage(img);
342 }
343 
344 //---------------------------------------------------------------------------------------------------------------------
ShowInGraphicalShell(const QString & filePath)345 void ShowInGraphicalShell(const QString &filePath)
346 {
347 #ifdef Q_OS_MAC
348     QStringList args{
349         "-e", "tell application \"Finder\"",
350         "-e", "activate",
351         "-e", "select POSIX file \""+filePath+"\"",
352         "-e", "end tell"
353     };
354     QProcess::startDetached(QStringLiteral("osascript"), args);
355 #elif defined(Q_OS_WIN)
356     QProcess::startDetached(QStringLiteral("explorer"), QStringList{"/select", QDir::toNativeSeparators(filePath)});
357 #else
358     // we cannot select a file here, because no file browser really supports it...
359     QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(filePath).path()));
360 #endif
361 }
362 
363 //---------------------------------------------------------------------------------------------------------------------
IsOptionSet(int argc,char * argv[],const char * option)364 bool IsOptionSet(int argc, char *argv[], const char *option)
365 {
366     for (int i = 1; i < argc; ++i)
367     {
368         if (!qstrcmp(argv[i], option))
369         {
370             return true;
371         }
372     }
373     return false;
374 }
375 
376 //---------------------------------------------------------------------------------------------------------------------
377 // See issue #624. https://bitbucket.org/dismine/valentina/issues/624
InitHighDpiScaling(int argc,char * argv[])378 void InitHighDpiScaling(int argc, char *argv[])
379 {
380     /* For more info see: http://doc.qt.io/qt-5/highdpi.html */
381     if (IsOptionSet(argc, argv, qPrintable(QLatin1String("--") + LONG_OPTION_NO_HDPI_SCALING)))
382     {
383 #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
384         QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
385 #else
386         qputenv("QT_DEVICE_PIXEL_RATIO", QByteArray("1"));
387 #endif
388     }
389     else
390     {
391 #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
392         QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); // DPI support
393 #else
394         qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", QByteArray("1"));
395 #endif
396     }
397 }
398 
399 const QString strOne   = QStringLiteral("one");
400 const QString strTwo   = QStringLiteral("two");
401 const QString strThree = QStringLiteral("three");
402 
403 Q_GLOBAL_STATIC_WITH_ARGS(const QString, strTMark, (QLatin1String("tMark")))
404 Q_GLOBAL_STATIC_WITH_ARGS(const QString, strVMark, (QLatin1String("vMark")))
405 Q_GLOBAL_STATIC_WITH_ARGS(const QString, strVMark2, (QLatin1String("vMark2")))
406 Q_GLOBAL_STATIC_WITH_ARGS(const QString, strUMark, (QLatin1String("uMark")))
407 Q_GLOBAL_STATIC_WITH_ARGS(const QString, strBoxMark, (QLatin1String("boxMark")))
408 
409 //---------------------------------------------------------------------------------------------------------------------
PassmarkLineTypeToString(PassmarkLineType type)410 QString PassmarkLineTypeToString(PassmarkLineType type)
411 {
412     switch(type)
413     {
414         case PassmarkLineType::OneLine:
415             return strOne;
416         case PassmarkLineType::TwoLines:
417             return strTwo;
418         case PassmarkLineType::ThreeLines:
419             return strThree;
420         case PassmarkLineType::TMark:
421             return *strTMark;
422         case PassmarkLineType::VMark:
423             return *strVMark;
424         case PassmarkLineType::VMark2:
425             return *strVMark2;
426         case PassmarkLineType::UMark:
427             return *strUMark;
428         case PassmarkLineType::BoxMark:
429             return *strBoxMark;
430         default:
431             break;
432     }
433 
434     return strOne;
435 }
436 
437 //---------------------------------------------------------------------------------------------------------------------
StringToPassmarkLineType(const QString & value)438 PassmarkLineType StringToPassmarkLineType(const QString &value)
439 {
440     const QStringList values{strOne, strTwo, strThree, *strTMark, *strVMark, *strVMark2, *strUMark, *strBoxMark};
441 
442     switch(values.indexOf(value))
443     {
444         case 0: // strOne
445             return PassmarkLineType::OneLine;
446         case 1: // strTwo
447             return PassmarkLineType::TwoLines;
448         case 2: // strThree
449             return PassmarkLineType::ThreeLines;
450         case 3: // strTMark
451             return PassmarkLineType::TMark;
452         case 4: // strVMark
453             return PassmarkLineType::VMark;
454         case 5: // strVMark2
455             return PassmarkLineType::VMark2;
456         case 6: // strUMark
457             return PassmarkLineType::UMark;
458         case 7: // strBoxMark
459             return PassmarkLineType::BoxMark;
460         default:
461             break;
462     }
463     return PassmarkLineType::OneLine;
464 }
465 
466 const QString strStraightforward        = QStringLiteral("straightforward");
467 const QString strBisector               = QStringLiteral("bisector");
468 const QString strIntersection           = QStringLiteral("intersection");
469 const QString strIntersectionOnlyLeft   = QStringLiteral("intersectionLeft");
470 const QString strIntersectionOnlyRight  = QStringLiteral("intersectionRight");
471 const QString strIntersection2          = QStringLiteral("intersection2");
472 const QString strIntersection2OnlyLeft  = QStringLiteral("intersection2Left");
473 const QString strIntersection2OnlyRight = QStringLiteral("intersection2Right");
474 const QString strTypeIncrement          = QStringLiteral("increment");
475 const QString strTypeSeparator          = QStringLiteral("separator");
476 
477 //---------------------------------------------------------------------------------------------------------------------
PassmarkAngleTypeToString(PassmarkAngleType type)478 QString PassmarkAngleTypeToString(PassmarkAngleType type)
479 {
480     switch(type)
481     {
482         case PassmarkAngleType::Straightforward:
483             return strStraightforward;
484         case PassmarkAngleType::Bisector:
485             return strBisector;
486         case PassmarkAngleType::Intersection:
487             return strIntersection;
488         case PassmarkAngleType::IntersectionOnlyLeft:
489             return strIntersectionOnlyLeft;
490         case PassmarkAngleType::IntersectionOnlyRight:
491             return strIntersectionOnlyRight;
492         case PassmarkAngleType::Intersection2:
493             return strIntersection2;
494         case PassmarkAngleType::Intersection2OnlyLeft:
495             return strIntersection2OnlyLeft;
496         case PassmarkAngleType::Intersection2OnlyRight:
497             return strIntersection2OnlyRight;
498         default:
499             break;
500     }
501 
502     return strStraightforward;
503 }
504 
505 //---------------------------------------------------------------------------------------------------------------------
StringToPassmarkAngleType(const QString & value)506 PassmarkAngleType StringToPassmarkAngleType(const QString &value)
507 {
508     const QStringList values = QStringList() << strStraightforward
509                                              << strBisector
510                                              << strIntersection
511                                              << strIntersectionOnlyLeft
512                                              << strIntersectionOnlyRight
513                                              << strIntersection2
514                                              << strIntersection2OnlyLeft
515                                              << strIntersection2OnlyRight;
516 
517     switch(values.indexOf(value))
518     {
519         case 0:
520             return PassmarkAngleType::Straightforward;
521         case 1:
522             return PassmarkAngleType::Bisector;
523         case 2:
524             return PassmarkAngleType::Intersection;
525         case 3:
526             return PassmarkAngleType::IntersectionOnlyLeft;
527         case 4:
528             return PassmarkAngleType::IntersectionOnlyRight;
529         case 5:
530             return PassmarkAngleType::Intersection2;
531         case 6:
532             return PassmarkAngleType::Intersection2OnlyLeft;
533         case 7:
534             return PassmarkAngleType::Intersection2OnlyRight;
535         default:
536             break;
537     }
538     return PassmarkAngleType::Straightforward;
539 }
540 
541 
542 //---------------------------------------------------------------------------------------------------------------------
StrToUnits(const QString & unit)543 Unit StrToUnits(const QString &unit)
544 {
545     const QStringList units = QStringList() << unitMM << unitCM << unitINCH << unitPX;
546     Unit result = Unit::Cm;
547     switch (units.indexOf(unit))
548     {
549         case 0:// mm
550             result = Unit::Mm;
551             break;
552         case 2:// inch
553             result = Unit::Inch;
554             break;
555         case 3:// px
556             result = Unit::Px;
557             break;
558         case 1:// cm
559         default:
560             result = Unit::Cm;
561             break;
562     }
563     return result;
564 }
565 
566 //---------------------------------------------------------------------------------------------------------------------
567 /**
568  * @brief UnitsToStr translate unit to string.
569  *
570  * This method used when need write unit in xml file and for showing unit in dialogs.
571  * @param unit curent unit
572  * @param translate true if need show translated name. Default value false.
573  * @return string reprezantation for unit.
574  */
UnitsToStr(const Unit & unit,const bool translate)575 QString UnitsToStr(const Unit &unit, const bool translate)
576 {
577     QString result;
578     switch (unit)
579     {
580         case Unit::Mm:
581             translate ? result = QObject::tr("mm") : result = unitMM;
582             break;
583         case Unit::Inch:
584             translate ? result = QObject::tr("inch") : result = unitINCH;
585             break;
586         case Unit::Px:
587             translate ? result = QObject::tr("px") : result = unitPX;
588             break;
589         case Unit::LAST_UNIT_DO_NOT_USE:
590             break;
591         case Unit::Cm:
592         default:
593             translate ? result = QObject::tr("cm") : result = unitCM;
594             break;
595     }
596     return result;
597 }
598 
599 //---------------------------------------------------------------------------------------------------------------------
InitLanguages(QComboBox * combobox)600 void InitLanguages(QComboBox *combobox)
601 {
602     SCASSERT(combobox != nullptr)
603     combobox->clear();
604 
605     QStringList fileNames;
606     QDirIterator it(VAbstractApplication::VApp()->translationsPath(), QStringList("valentina_*.qm"), QDir::Files,
607                     QDirIterator::Subdirectories);
608     while (it.hasNext())
609     {
610         it.next();
611         fileNames.append(it.fileName());
612     }
613 
614     bool englishUS = false;
615     const QString en_US = QStringLiteral("en_US");
616 
617     for (auto locale : fileNames)
618     {
619         // get locale extracted by filename           "valentina_de_De.qm"
620         locale.truncate(locale.lastIndexOf('.'));  // "valentina_de_De"
621         locale.remove(0, locale.indexOf('_') + 1); // "de_De"
622 
623         if (not englishUS)
624         {
625             englishUS = (en_US == locale);
626         }
627 
628         QLocale loc = QLocale(locale);
629         QString lang = loc.nativeLanguageName();
630         // Since Qt 5.12 country names have spaces
631         QIcon ico(QString("://flags/%1.png").arg(QLocale::countryToString(loc.country()).remove(' ')));
632 
633         combobox->addItem(ico, lang, locale);
634     }
635 
636     if (combobox->count() == 0 || not englishUS)
637     {
638         // English language is internal and doens't have own *.qm file.
639         // Since Qt 5.12 country names have spaces
640         QIcon ico(QString("://flags/%1.png").arg(QLocale::countryToString(QLocale::UnitedStates).remove(' ')));
641         QString lang = QLocale(en_US).nativeLanguageName();
642         combobox->addItem(ico, lang, en_US);
643     }
644 
645     // set default translators and language checked
646     qint32 index = combobox->findData(VAbstractApplication::VApp()->Settings()->GetLocale());
647     if (index != -1)
648     {
649         combobox->setCurrentIndex(index);
650     }
651 }
652 
653 const quint32 CustomSARecord::streamHeader = 0xEBFF7586; // CRC-32Q string "CustomSARecord"
654 const quint16 CustomSARecord::classVersion = 1;
655 
656 // Friend functions
657 //---------------------------------------------------------------------------------------------------------------------
operator <<(QDataStream & out,const CustomSARecord & record)658 QDataStream &operator<<(QDataStream &out, const CustomSARecord &record)
659 {
660     out << CustomSARecord::streamHeader << CustomSARecord::classVersion;
661 
662     // Added in classVersion = 1
663     out << record.startPoint;
664     out << record.path;
665     out << record.endPoint;
666     out << record.reverse;
667     out << record.includeType;
668 
669     // Added in classVersion = 2
670 
671     return out;
672 }
673 
674 //---------------------------------------------------------------------------------------------------------------------
operator >>(QDataStream & in,CustomSARecord & record)675 QDataStream &operator>>(QDataStream &in, CustomSARecord &record)
676 {
677     quint32 actualStreamHeader = 0;
678     in >> actualStreamHeader;
679 
680     if (actualStreamHeader != CustomSARecord::streamHeader)
681     {
682         QString message = QCoreApplication::tr("CustomSARecord prefix mismatch error: actualStreamHeader = 0x%1 "
683                                                "and streamHeader = 0x%2")
684                 .arg(actualStreamHeader, 8, 0x10, QChar('0'))
685                 .arg(CustomSARecord::streamHeader, 8, 0x10, QChar('0'));
686         throw VException(message);
687     }
688 
689     quint16 actualClassVersion = 0;
690     in >> actualClassVersion;
691 
692     if (actualClassVersion > CustomSARecord::classVersion)
693     {
694         QString message = QCoreApplication::tr("CustomSARecord compatibility error: actualClassVersion = %1 and "
695                                                "classVersion = %2")
696                 .arg(actualClassVersion).arg(CustomSARecord::classVersion);
697         throw VException(message);
698     }
699 
700     in >> record.startPoint;
701     in >> record.path;
702     in >> record.endPoint;
703     in >> record.reverse;
704     in >> record.includeType;
705 
706 //    if (actualClassVersion >= 2)
707 //    {
708 
709 //    }
710 
711     return in;
712 }
713 
714 //---------------------------------------------------------------------------------------------------------------------
IncrementTypeToString(IncrementType type)715 QString IncrementTypeToString(IncrementType type)
716 {
717     switch(type)
718     {
719         case IncrementType::Increment:
720             return strTypeIncrement;
721         case IncrementType::Separator:
722             return strTypeSeparator;
723         default:
724             break;
725     }
726 
727     return strTypeIncrement;
728 }
729 
730 //---------------------------------------------------------------------------------------------------------------------
StringToIncrementType(const QString & value)731 IncrementType StringToIncrementType(const QString &value)
732 {
733     const QStringList values { strTypeIncrement, strTypeSeparator };
734 
735     switch(values.indexOf(value))
736     {
737         case 0:
738             return IncrementType::Increment;
739         case 1:
740             return IncrementType::Separator;
741         default:
742             break;
743     }
744     return IncrementType::Increment;
745 }
746 
747 //---------------------------------------------------------------------------------------------------------------------
SplitFilePaths(const QString & path)748 QStringList SplitFilePaths(const QString &path)
749 {
750     QStringList result;
751     QString subPath = QDir::cleanPath(path);
752     QString lastFileName;
753 
754     do
755     {
756         QFileInfo fileInfo(subPath);
757         lastFileName = fileInfo.fileName();
758         if (not lastFileName.isEmpty())
759         {
760             result.prepend(lastFileName);
761             subPath = fileInfo.path();
762         }
763     }
764     while(not lastFileName.isEmpty());
765 
766     return result;
767 }
768