VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/globals/UITranslator.cpp@ 103977

Last change on this file since 103977 was 103362, checked in by vboxsync, 10 months ago

FE/Qt: bugref:10450: Get rid of QRegExp which is a part of Qt5 legacy API; This is necessary to get rid of Qt5 support lib.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.3 KB
Line 
1/* $Id: UITranslator.cpp 103362 2024-02-14 16:50:56Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UITranslator class implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/* Qt includes: */
29#include <QApplication>
30#include <QDir>
31#include <QKeySequence>
32#include <QRegularExpression>
33#ifdef Q_OS_UNIX
34# include <QLibraryInfo>
35#endif
36
37/* GUI includes: */
38#include "UIConverter.h"
39#include "UIMessageCenter.h"
40#include "UITranslator.h"
41#ifdef VBOX_WS_MAC
42# include "VBoxUtils-darwin.h"
43#endif
44
45/* Other VBox includes: */
46#include <iprt/assert.h>
47#include <iprt/path.h>
48#ifdef Q_OS_UNIX
49# include <iprt/env.h>
50#endif
51
52/* External includes: */
53#include <math.h>
54
55
56/** Port config cache. */
57struct PortConfig
58{
59 const char *name;
60 const ulong IRQ;
61 const ulong IOBase;
62};
63
64/** Known port config COM ports. */
65static const PortConfig kComKnownPorts[] =
66{
67 { "COM1", 4, 0x3F8 },
68 { "COM2", 3, 0x2F8 },
69 { "COM3", 4, 0x3E8 },
70 { "COM4", 3, 0x2E8 },
71 /* Must not contain an element with IRQ=0 and IOBase=0 used to cause
72 * toCOMPortName() to return the "User-defined" string for these values. */
73};
74
75
76/* static */
77UITranslator *UITranslator::s_pTranslator = 0;
78bool UITranslator::s_fTranslationInProgress = false;
79QString UITranslator::s_strLoadedLanguageId = UITranslator::vboxBuiltInLanguageName();
80
81/* static */
82void UITranslator::loadLanguage(const QString &strLangId /* = QString() */)
83{
84 QString strEffectiveLangId = strLangId.isEmpty()
85 ? systemLanguageId()
86 : strLangId;
87 QString strLanguageFileName;
88 QString strSelectedLangId = vboxBuiltInLanguageName();
89
90 /* If C is selected we change it temporary to en. This makes sure any extra
91 * "en" translation file will be loaded. This is necessary for loading the
92 * plural forms of some of our translations. */
93 bool fResetToC = false;
94 if (strEffectiveLangId == "C")
95 {
96 strEffectiveLangId = "en";
97 fResetToC = true;
98 }
99
100 char szNlsPath[RTPATH_MAX];
101 int rc;
102
103 rc = RTPathAppPrivateNoArch(szNlsPath, sizeof(szNlsPath));
104 AssertRC(rc);
105
106 QString strNlsPath = QString(szNlsPath) + vboxLanguageSubDirectory();
107 QDir nlsDir(strNlsPath);
108
109 Assert(!strEffectiveLangId.isEmpty());
110 if (!strEffectiveLangId.isEmpty() && strEffectiveLangId != vboxBuiltInLanguageName())
111 {
112 const QRegularExpression re(vboxLanguageIdRegExp());
113 const QRegularExpressionMatch mt = re.match(strEffectiveLangId);
114 /* The language ID should match the regexp completely: */
115 AssertReturnVoid(mt.capturedStart() == 0);
116
117 QString strStrippedLangId = mt.captured(2);
118
119 if (nlsDir.exists(vboxLanguageFileBase() + strEffectiveLangId + vboxLanguageFileExtension()))
120 {
121 strLanguageFileName = nlsDir.absoluteFilePath(vboxLanguageFileBase() +
122 strEffectiveLangId +
123 vboxLanguageFileExtension());
124 strSelectedLangId = strEffectiveLangId;
125 }
126 else if (nlsDir.exists(vboxLanguageFileBase() + strStrippedLangId + vboxLanguageFileExtension()))
127 {
128 strLanguageFileName = nlsDir.absoluteFilePath(vboxLanguageFileBase() +
129 strStrippedLangId +
130 vboxLanguageFileExtension());
131 strSelectedLangId = strStrippedLangId;
132 }
133 else
134 {
135 /* Never complain when the default language is requested. In any
136 * case, if no explicit language file exists, we will simply
137 * fall-back to English (built-in). */
138 if (!strLangId.isNull() && strEffectiveLangId != "en")
139 msgCenter().cannotFindLanguage(strEffectiveLangId, strNlsPath);
140 /* strSelectedLangId remains built-in here: */
141 AssertReturnVoid(strSelectedLangId == vboxBuiltInLanguageName());
142 }
143 }
144
145 /* Lock listener: */
146 s_fTranslationInProgress = true;
147 /* A list of translators to install: */
148 QList<QTranslator*> translators;
149
150 /* Delete the old translator if there is one: */
151 if (s_pTranslator)
152 {
153 /* QTranslator destructor will call qApp->removeTranslator() for
154 * us. It will also delete all its child translations we attach to it
155 * below, so we don't have to care about them specially. */
156 delete s_pTranslator;
157 }
158
159 /* Load new language files: */
160 s_pTranslator = new UITranslator(qApp);
161 Assert(s_pTranslator);
162 bool fLoadOk = true;
163 if (s_pTranslator)
164 {
165 if (strSelectedLangId != vboxBuiltInLanguageName())
166 {
167 Assert(!strLanguageFileName.isNull());
168 fLoadOk = s_pTranslator->loadFile(strLanguageFileName);
169 }
170 /* We install the translator in any case: on failure, this will
171 * activate an empty translator that will give us English (built-in): */
172 translators << s_pTranslator;
173 }
174 else
175 fLoadOk = false;
176
177 if (fLoadOk)
178 s_strLoadedLanguageId = strSelectedLangId;
179 else
180 {
181 msgCenter().cannotLoadLanguage(strLanguageFileName);
182 s_strLoadedLanguageId = vboxBuiltInLanguageName();
183 }
184
185 /* Try to load the corresponding Qt translation: */
186 if (languageId() != vboxBuiltInLanguageName() && languageId() != "en")
187 {
188#ifdef Q_OS_UNIX
189 // We use system installations of Qt on Linux systems, so first, try
190 // to load the Qt translation from the system location.
191 strLanguageFileName = QLibraryInfo::path(QLibraryInfo::TranslationsPath) + "/qt_" +
192 languageId() + vboxLanguageFileExtension();
193 QTranslator *pQtSysTr = new QTranslator(s_pTranslator);
194 Assert(pQtSysTr);
195 if (pQtSysTr && pQtSysTr->load(strLanguageFileName))
196 translators << pQtSysTr;
197 // Note that the Qt translation supplied by Oracle is always loaded
198 // afterwards to make sure it will take precedence over the system
199 // translation (it may contain more decent variants of translation
200 // that better correspond to VirtualBox UI). We need to load both
201 // because a newer version of Qt may be installed on the user computer
202 // and the Oracle version may not fully support it. We don't do it on
203 // Win32 because we supply a Qt library there and therefore the
204 // Oracle translation is always the best one. */
205#endif
206 strLanguageFileName = nlsDir.absoluteFilePath(QString("qt_") +
207 languageId() +
208 vboxLanguageFileExtension());
209 QTranslator *pQtTr = new QTranslator(s_pTranslator);
210 Assert(pQtTr);
211 if (pQtTr && (fLoadOk = pQtTr->load(strLanguageFileName)))
212 translators << pQtTr;
213 /* The below message doesn't fit 100% (because it's an additional
214 * language and the main one won't be reset to built-in on failure)
215 * but the load failure is so rare here that it's not worth a separate
216 * message (but still, having something is better than having none) */
217 if (!fLoadOk && !strLangId.isNull())
218 msgCenter().cannotLoadLanguage(strLanguageFileName);
219 }
220 if (fResetToC)
221 s_strLoadedLanguageId = vboxBuiltInLanguageName();
222#ifdef VBOX_WS_MAC
223 // Qt doesn't translate the items in the Application menu initially.
224 // Manually trigger an update.
225 ::darwinRetranslateAppMenu();
226#endif
227
228 /* Iterate through all the translators: */
229 for (int i = 0; i < translators.size(); ++i)
230 {
231 /* Unlock listener before the last one translator: */
232 if (i == translators.size() - 1)
233 {
234 QCoreApplication::sendPostedEvents(0, QEvent::LanguageChange);
235 s_fTranslationInProgress = false;
236 }
237
238 /* Install current one: */
239 qApp->installTranslator(translators.at(i));
240 }
241
242 /* Unlock listener in case if it's still locked: */
243 s_fTranslationInProgress = false;
244}
245
246/* static */
247QString UITranslator::vboxLanguageSubDirectory()
248{
249 return "/nls";
250}
251
252/* static */
253QString UITranslator::vboxLanguageFileBase()
254{
255 return "VirtualBox_";
256}
257
258/* static */
259QString UITranslator::vboxLanguageFileExtension()
260{
261 return ".qm";
262}
263
264/* static */
265QString UITranslator::vboxLanguageIdRegExp()
266{
267 return "(([a-z]{2})(?:_([A-Z]{2}))?)|(C)";
268}
269
270/* static */
271QString UITranslator::vboxBuiltInLanguageName()
272{
273 return "C";
274}
275
276/* static */
277QString UITranslator::languageId()
278{
279 /* Note that it may not match with UIExtraDataManager::languageId() if the specified language cannot be loaded.
280 *
281 * If the built-in language is active, this method returns "C". "C" is treated as the built-in language for
282 * simplicity -- the C locale is used in unix environments as a fallback when the requested locale is invalid.
283 * This way we don't need to process both the "built_in" language and the "C" language (which is a valid
284 * environment setting) separately. */
285
286 return s_strLoadedLanguageId;
287}
288
289/* static */
290QString UITranslator::yearsToString(uint32_t cVal)
291{
292 return QApplication::translate("UITranslator", "%n year(s)", "", cVal);
293}
294
295/* static */
296QString UITranslator::monthsToString(uint32_t cVal)
297{
298 return QApplication::translate("UITranslator", "%n month(s)", "", cVal);
299}
300
301/* static */
302QString UITranslator::daysToString(uint32_t cVal)
303{
304 return QApplication::translate("UITranslator", "%n day(s)", "", cVal);
305}
306
307/* static */
308QString UITranslator::hoursToString(uint32_t cVal)
309{
310 return QApplication::translate("UITranslator", "%n hour(s)", "", cVal);
311}
312
313/* static */
314QString UITranslator::minutesToString(uint32_t cVal)
315{
316 return QApplication::translate("UITranslator", "%n minute(s)", "", cVal);
317}
318
319/* static */
320QString UITranslator::secondsToString(uint32_t cVal)
321{
322 return QApplication::translate("UITranslator", "%n second(s)", "", cVal);
323}
324
325/* static */
326QString UITranslator::yearsToStringAgo(uint32_t cVal)
327{
328 return QApplication::translate("UITranslator", "%n year(s) ago", "", cVal);
329}
330
331/* static */
332QString UITranslator::monthsToStringAgo(uint32_t cVal)
333{
334 return QApplication::translate("UITranslator", "%n month(s) ago", "", cVal);
335}
336
337/* static */
338QString UITranslator::daysToStringAgo(uint32_t cVal)
339{
340 return QApplication::translate("UITranslator", "%n day(s) ago", "", cVal);
341}
342
343/* static */
344QString UITranslator::hoursToStringAgo(uint32_t cVal)
345{
346 return QApplication::translate("UITranslator", "%n hour(s) ago", "", cVal);
347}
348
349/* static */
350QString UITranslator::minutesToStringAgo(uint32_t cVal)
351{
352 return QApplication::translate("UITranslator", "%n minute(s) ago", "", cVal);
353}
354
355/* static */
356QString UITranslator::secondsToStringAgo(uint32_t cVal)
357{
358 return QApplication::translate("UITranslator", "%n second(s) ago", "", cVal);
359}
360
361/* static */
362QString UITranslator::decimalSep()
363{
364 return QString(QLocale::system().decimalPoint());
365}
366
367/* static */
368QString UITranslator::sizeRegexp()
369{
370 /* This regexp will capture 5 groups of text:
371 * - cap(1): integer number in case when no decimal point is present
372 * (if empty, it means that decimal point is present)
373 * - cap(2): size suffix in case when no decimal point is present (may be empty)
374 * - cap(3): integer number in case when decimal point is present (may be empty)
375 * - cap(4): fraction number (hundredth) in case when decimal point is present
376 * - cap(5): size suffix in case when decimal point is present (note that
377 * B cannot appear there). */
378
379 const QString strRegexp =
380 QString("^(?:(?:(\\d+)(?:\\s?(%2|%3|%4|%5|%6|%7))?)|(?:(\\d*)%1(\\d{1,2})(?:\\s?(%3|%4|%5|%6|%7))))$")
381 .arg(decimalSep())
382 .arg(tr("B", "size suffix Bytes"))
383 .arg(tr("KB", "size suffix KBytes=1024 Bytes"))
384 .arg(tr("MB", "size suffix MBytes=1024 KBytes"))
385 .arg(tr("GB", "size suffix GBytes=1024 MBytes"))
386 .arg(tr("TB", "size suffix TBytes=1024 GBytes"))
387 .arg(tr("PB", "size suffix PBytes=1024 TBytes"));
388 return strRegexp;
389}
390
391/* static */
392quint64 UITranslator::parseSize(const QString &strText)
393{
394 /* Text should be in form of B|KB|MB|GB|TB|PB. */
395 const QRegularExpression re(sizeRegexp());
396 const QRegularExpressionMatch mt = re.match(strText);
397 if (mt.hasMatch())
398 {
399 QString strInteger = mt.captured(1);
400 QString strHundred;
401 QString strSuff = mt.captured(2);
402 if (strInteger.isEmpty())
403 {
404 strInteger = mt.captured(3);
405 strHundred = mt.captured(4);
406 strSuff = mt.captured(5);
407 }
408
409 quint64 uDenominator = 0;
410 if (strSuff.isEmpty() || strSuff == tr("B", "size suffix Bytes"))
411 uDenominator = 1;
412 else if (strSuff == tr("KB", "size suffix KBytes=1024 Bytes"))
413 uDenominator = _1K;
414 else if (strSuff == tr("MB", "size suffix MBytes=1024 KBytes"))
415 uDenominator = _1M;
416 else if (strSuff == tr("GB", "size suffix GBytes=1024 MBytes"))
417 uDenominator = _1G;
418 else if (strSuff == tr("TB", "size suffix TBytes=1024 GBytes"))
419 uDenominator = _1T;
420 else if (strSuff == tr("PB", "size suffix PBytes=1024 TBytes"))
421 uDenominator = _1P;
422
423 quint64 iInteger = strInteger.toULongLong();
424 if (uDenominator == 1)
425 return iInteger;
426
427 quint64 iHundred = strHundred.leftJustified(2, '0').toULongLong();
428 iHundred = iHundred * uDenominator / 100;
429 iInteger = iInteger * uDenominator + iHundred;
430 return iInteger;
431 }
432 else
433 return 0;
434}
435
436/* static */
437SizeSuffix UITranslator::parseSizeSuffix(const QString &strText)
438{
439 /* Text should be in form of B|KB|MB|GB|TB|PB. */
440 const QRegularExpression re(sizeRegexp());
441 const QRegularExpressionMatch mt = re.match(strText);
442 if (mt.hasMatch())
443 {
444 QString strInteger = mt.captured(1);
445 QString strSuff = mt.captured(2);
446 if (strInteger.isEmpty())
447 {
448 strInteger = mt.captured(3);
449 strSuff = mt.captured(5);
450 }
451
452 SizeSuffix enmSizeSuffix = SizeSuffix_Byte;
453
454 if (strSuff.isEmpty() || strSuff == tr("B", "size suffix Bytes"))
455 enmSizeSuffix = SizeSuffix_Byte;
456 else if (strSuff == tr("KB", "size suffix KBytes=1024 Bytes"))
457 enmSizeSuffix = SizeSuffix_KiloByte;
458 else if (strSuff == tr("MB", "size suffix MBytes=1024 KBytes"))
459 enmSizeSuffix = SizeSuffix_MegaByte;
460 else if (strSuff == tr("GB", "size suffix GBytes=1024 MBytes"))
461 enmSizeSuffix = SizeSuffix_GigaByte;
462 else if (strSuff == tr("TB", "size suffix TBytes=1024 GBytes"))
463 enmSizeSuffix = SizeSuffix_TeraByte;
464 else if (strSuff == tr("PB", "size suffix PBytes=1024 TBytes"))
465 enmSizeSuffix = SizeSuffix_PetaByte;
466 return enmSizeSuffix;
467 }
468 else
469 return SizeSuffix_Byte;
470}
471
472/* static */
473bool UITranslator::hasSizeSuffix(const QString &strText)
474{
475 /* Text should be in form of B|KB|MB|GB|TB|PB. */
476 const QRegularExpression re(sizeRegexp());
477 const QRegularExpressionMatch mt = re.match(strText);
478 if (mt.hasMatch())
479 {
480 QString strInteger = mt.captured(1);
481 QString strSuff = mt.captured(2);
482 if (strInteger.isEmpty())
483 {
484 strInteger = mt.captured(3);
485 strSuff = mt.captured(5);
486 }
487
488 if (strSuff.isEmpty())
489 return false;
490 if (strSuff == tr("B", "size suffix Bytes") ||
491 strSuff == tr("KB", "size suffix KBytes=1024 Bytes") ||
492 strSuff == tr("MB", "size suffix MBytes=1024 KBytes") ||
493 strSuff == tr("GB", "size suffix GBytes=1024 MBytes") ||
494 strSuff == tr("TB", "size suffix TBytes=1024 GBytes") ||
495 strSuff == tr("PB", "size suffix PBytes=1024 TBytes"))
496 return true;
497 return false;
498 }
499 else
500 return false;
501}
502
503/* static */
504QString UITranslator::formatSize(quint64 uSize, uint cDecimal /* = 2 */,
505 FormatSize enmMode /* = FormatSize_Round */)
506{
507 /* Text will be in form of B|KB|MB|GB|TB|PB.
508 *
509 * When enmMode is FormatSize_Round, the result is rounded to the
510 * closest number containing @a aDecimal decimal digits.
511 * When enmMode is FormatSize_RoundDown, the result is rounded to the
512 * largest number with @a aDecimal decimal digits that is not greater than
513 * the result. This guarantees that converting the resulting string back to
514 * the integer value in bytes will not produce a value greater that the
515 * initial size parameter.
516 * When enmMode is FormatSize_RoundUp, the result is rounded to the
517 * smallest number with @a aDecimal decimal digits that is not less than the
518 * result. This guarantees that converting the resulting string back to the
519 * integer value in bytes will not produce a value less that the initial
520 * size parameter. */
521
522 quint64 uDenominator = 0;
523 int iSuffix = 0;
524
525 if (uSize < _1K)
526 {
527 uDenominator = 1;
528 iSuffix = 0;
529 }
530 else if (uSize < _1M)
531 {
532 uDenominator = _1K;
533 iSuffix = 1;
534 }
535 else if (uSize < _1G)
536 {
537 uDenominator = _1M;
538 iSuffix = 2;
539 }
540 else if (uSize < _1T)
541 {
542 uDenominator = _1G;
543 iSuffix = 3;
544 }
545 else if (uSize < _1P)
546 {
547 uDenominator = _1T;
548 iSuffix = 4;
549 }
550 else
551 {
552 uDenominator = _1P;
553 iSuffix = 5;
554 }
555
556 quint64 uInteger = uSize / uDenominator;
557 quint64 uDecimal = uSize % uDenominator;
558 quint64 uMult = 1;
559 for (uint i = 0; i < cDecimal; ++i)
560 uMult *= 10;
561
562 QString strNumber;
563 if (uDenominator > 1)
564 {
565 if (uDecimal)
566 {
567 uDecimal *= uMult;
568 /* Not greater: */
569 if (enmMode == FormatSize_RoundDown)
570 uDecimal = uDecimal / uDenominator;
571 /* Not less: */
572 else if (enmMode == FormatSize_RoundUp)
573 uDecimal = (uDecimal + uDenominator - 1) / uDenominator;
574 /* Nearest: */
575 else
576 uDecimal = (uDecimal + uDenominator / 2) / uDenominator;
577 }
578 /* Check for the fractional part overflow due to rounding: */
579 if (uDecimal == uMult)
580 {
581 uDecimal = 0;
582 ++uInteger;
583 /* Check if we've got 1024 XB after rounding and scale down if so: */
584 if (uInteger == 1024 && iSuffix + 1 < (int)SizeSuffix_Max)
585 {
586 uInteger /= 1024;
587 ++iSuffix;
588 }
589 }
590 strNumber = QString::number(uInteger);
591 if (cDecimal)
592 strNumber += QString("%1%2").arg(decimalSep())
593 .arg(QString::number(uDecimal).rightJustified(cDecimal, '0'));
594 }
595 else
596 {
597 strNumber = QString::number(uInteger);
598 }
599
600 return QString("%1 %2").arg(strNumber).arg(gpConverter->toString(static_cast<SizeSuffix>(iSuffix)));
601}
602
603/* static */
604QString UITranslator::addMetricSuffixToNumber(quint64 uNumber)
605{
606 if (uNumber <= 0)
607 return QString();
608 /* See https://en.wikipedia.org/wiki/Metric_prefix for metric suffixes:*/
609 char suffixes[] = {'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'};
610 int zeroCount = (int)log10((long double)uNumber);
611 if (zeroCount < 3)
612 return QString::number(uNumber);
613 int h = 3 * (zeroCount / 3);
614 char result[128];
615 sprintf(result, "%.2f", uNumber / (float)pow((double)10, h));
616 return QString("%1%2").arg(result).arg(suffixes[h / 3 - 1]);
617}
618
619/* static */
620QStringList UITranslator::COMPortNames()
621{
622 QStringList list;
623 for (size_t i = 0; i < RT_ELEMENTS(kComKnownPorts); ++i)
624 list << kComKnownPorts[i].name;
625
626 return list;
627}
628
629/* static */
630QString UITranslator::toCOMPortName(ulong uIRQ, ulong uIOBase)
631{
632 for (size_t i = 0; i < RT_ELEMENTS(kComKnownPorts); ++i)
633 if (kComKnownPorts[i].IRQ == uIRQ &&
634 kComKnownPorts[i].IOBase == uIOBase)
635 return kComKnownPorts[i].name;
636
637 return tr("User-defined", "serial port");;
638}
639
640/* static */
641bool UITranslator::toCOMPortNumbers(const QString &strName, ulong &uIRQ, ulong &uIOBase)
642{
643 for (size_t i = 0; i < RT_ELEMENTS(kComKnownPorts); ++i)
644 if (strcmp(kComKnownPorts[i].name, strName.toUtf8().data()) == 0)
645 {
646 uIRQ = kComKnownPorts[i].IRQ;
647 uIOBase = kComKnownPorts[i].IOBase;
648 return true;
649 }
650
651 return false;
652}
653
654/* Regular expressions used by both highlight and emphasize. They use the
655 same prefix and suffix expression. Unfortunately, QRegularExpression isn't
656 thread safe, so we only store the string contstants here. */
657/** @todo qt6: Both these had bogus suffix sets '[:.-!);]', I've changed them to '[-:.!);]', hope that's correct. */
658static char const g_szRxSingleQuotes[] = "((?:^|\\s)[(]?)"
659 "'([^']*)'"
660 "(?=[-:.!);]?(?:\\s|$))";
661static const char g_szRxUuid[] = "((?:^|\\s)[(]?)"
662 "(\\{[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\\})"
663 "(?=[-:.!);]?(?:\\s|$))";
664
665/* static */
666QString UITranslator::highlight(QString strText, bool fToolTip /* = false */)
667{
668 /* We should reformat the input strText so that:
669 * - strings in single quotes will be put inside <nobr> and marked
670 * with blue color;
671 * - UUIDs be put inside <nobr> and marked
672 * with green color;
673 * - replaces new line chars with </p><p> constructs to form paragraphs
674 * (note that <p\> and </p> are not appended to the beginning and to the
675 * end of the string respectively, to allow the result be appended
676 * or prepended to the existing paragraph).
677 *
678 * If @a fToolTip is true, colouring is not applied, only the <nobr> tag
679 * is added. Also, new line chars are replaced with <br> instead of <p>. */
680
681 QString strFont;
682 QString uuidFont;
683 QString endFont;
684 if (!fToolTip)
685 {
686 strFont = "<font color=#0000CC>";
687 uuidFont = "<font color=#008000>";
688 endFont = "</font>";
689 }
690
691 /* Replace special entities, '&' -- first! */
692 strText.replace('&', "&amp;");
693 strText.replace('<', "&lt;");
694 strText.replace('>', "&gt;");
695 strText.replace('\"', "&quot;");
696
697 /* Mark strings in single quotes with color: */
698 strText.replace(QRegularExpression(g_szRxSingleQuotes), QString("\\1%1<nobr>'\\2'</nobr>%2").arg(strFont).arg(endFont));
699
700 /* Mark UUIDs with color: */
701 strText.replace(QRegularExpression(g_szRxUuid), QString("\\1%1<nobr>\\2</nobr>%2").arg(uuidFont).arg(endFont));
702
703 /* Split to paragraphs at \n chars: */
704 if (!fToolTip)
705 strText.replace('\n', "</p><p>");
706 else
707 strText.replace('\n', "<br>");
708
709 return strText;
710}
711
712/* static */
713QString UITranslator::emphasize(QString strText)
714{
715 /* We should reformat the input string @a strText so that:
716 * - strings in single quotes will be put inside \<nobr\> and marked
717 * with bold style;
718 * - UUIDs be put inside \<nobr\> and marked
719 * with italic style;
720 * - replaces new line chars with \</p\>\<p\> constructs to form paragraphs
721 * (note that \<p\> and \</p\> are not appended to the beginning and to the
722 * end of the string respectively, to allow the result be appended
723 * or prepended to the existing paragraph). */
724
725 QString strEmphStart("<b>");
726 QString strEmphEnd("</b>");
727 QString uuidEmphStart("<i>");
728 QString uuidEmphEnd("</i>");
729
730 /* Replace special entities, '&' -- first! */
731 strText.replace('&', "&amp;");
732 strText.replace('<', "&lt;");
733 strText.replace('>', "&gt;");
734 strText.replace('\"', "&quot;");
735
736 /* Mark strings in single quotes with bold style: */
737 strText.replace(QRegularExpression(g_szRxSingleQuotes), QString("\\1%1<nobr>'\\2'</nobr>%2").arg(strEmphStart).arg(strEmphEnd));
738
739 /* Mark UUIDs with italic style: */
740 strText.replace(QRegularExpression(g_szRxUuid), QString("\\1%1<nobr>\\2</nobr>%2").arg(uuidEmphStart).arg(uuidEmphEnd));
741
742 /* Split to paragraphs at \n chars: */
743 strText.replace('\n', "</p><p>");
744
745 return strText;
746}
747
748/* static */
749QString UITranslator::removeAccelMark(QString strText)
750{
751 /* In order to support accelerators used in non-alphabet languages
752 * (e.g. Japanese) that has a form of "(&<L>)" (where <L> is a latin letter),
753 * this method first searches for this pattern and, if found, removes it as a
754 * whole. If such a pattern is not found, then the '&' character is simply
755 * removed from the string. */
756
757 const QRegularExpression re("\\(&[a-zA-Z]\\)");
758 const QRegularExpressionMatch mt = re.match(strText);
759 int iPos = mt.capturedStart();
760 if (iPos >= 0)
761 strText.remove(iPos, mt.capturedLength());
762 else
763 {
764 iPos = strText.indexOf('&');
765 if (iPos >= 0)
766 strText.remove(iPos, 1);
767 }
768
769 return strText;
770}
771
772/* static */
773QString UITranslator::insertKeyToActionText(const QString &strText, const QString &strKey)
774{
775#ifdef VBOX_WS_MAC
776 QString strPattern("%1 (Host+%2)");
777#else
778 QString strPattern("%1 \tHost+%2");
779#endif
780 if ( strKey.isEmpty()
781 || strKey.compare("None", Qt::CaseInsensitive) == 0)
782 return strText;
783 else
784 return strPattern.arg(strText).arg(QKeySequence(strKey).toString(QKeySequence::NativeText));
785}
786
787/* static */
788bool UITranslator::isTranslationInProgress()
789{
790 return s_fTranslationInProgress;
791}
792
793/* static */
794QString UITranslator::byteStringToMegaByteString(const QString &strByteString)
795{
796 if (strByteString.isEmpty())
797 return QString();
798 bool fConversionSuccess = false;
799 qulonglong uByte = strByteString.toULongLong(&fConversionSuccess);
800 AssertReturn(fConversionSuccess, QString());
801 return QString::number(uByte / _1M);
802}
803
804/* static */
805QString UITranslator::megabyteStringToByteString(const QString &strMegaByteString)
806{
807 if (strMegaByteString.isEmpty())
808 return QString();
809 bool fConversionSuccess = false;
810 qulonglong uMegaByte = strMegaByteString.toULongLong(&fConversionSuccess);
811 AssertReturn(fConversionSuccess, QString());
812 return QString::number(uMegaByte * _1M);
813}
814
815UITranslator::UITranslator(QObject *pParent /* = 0 */)
816 : QTranslator(pParent)
817{
818}
819
820bool UITranslator::loadFile(const QString &strFileName)
821{
822 QFile file(strFileName);
823 if (!file.open(QIODevice::ReadOnly))
824 return false;
825 m_data = file.readAll();
826 return load((uchar*)m_data.data(), m_data.size());
827}
828
829/* static */
830QString UITranslator::languageName()
831{
832 /* Returns "English" if no translation is installed
833 * or if the translation file is invalid. */
834 return QApplication::translate("@@@", "English",
835 "Native language name");
836}
837
838/* static */
839QString UITranslator::languageCountry()
840{
841 /* Returns "--" if no translation is installed or if the translation file
842 * is invalid, or if the language is independent on the country. */
843 return QApplication::translate("@@@", "--",
844 "Native language country name "
845 "(empty if this language is for all countries)");
846}
847
848/* static */
849QString UITranslator::languageNameEnglish()
850{
851 /* Returns "English" if no translation is installed
852 * or if the translation file is invalid. */
853 return QApplication::translate("@@@", "English",
854 "Language name, in English");
855}
856
857/* static */
858QString UITranslator::languageCountryEnglish()
859{
860 /* Returns "--" if no translation is installed or if the translation file
861 * is invalid, or if the language is independent on the country. */
862 return QApplication::translate("@@@", "--",
863 "Language country name, in English "
864 "(empty if native country name is empty)");
865}
866
867/* static */
868QString UITranslator::languageTranslators()
869{
870 /* Returns "Oracle Corporation" if no translation is installed or if the translation file
871 * is invalid, or if the translation is supplied by Oracle Corporation. */
872 return QApplication::translate("@@@", "Oracle Corporation",
873 "Comma-separated list of translators");
874}
875
876/* static */
877QString UITranslator::systemLanguageId()
878{
879 /* This does exactly the same as QLocale::system().name() but corrects its wrong behavior on Linux systems
880 * (LC_NUMERIC for some strange reason takes precedence over any other locale setting in the QLocale::system()
881 * implementation). This implementation first looks at LC_ALL (as defined by SUS), then looks at LC_MESSAGES
882 * which is designed to define a language for program messages in case if it differs from the language for
883 * other locale categories. Then it looks for LANG and finally falls back to QLocale::system().name().
884 *
885 * The order of precedence is well defined here:
886 * http://opengroup.org/onlinepubs/007908799/xbd/envvar.html
887 *
888 * This method will return "C" when the requested locale is invalid or when the "C" locale is set explicitly. */
889
890#if defined(VBOX_WS_MAC)
891 // QLocale return the right id only if the user select the format
892 // of the language also. So we use our own implementation */
893 return ::darwinSystemLanguage();
894#elif defined(Q_OS_UNIX)
895 const char *pszValue = RTEnvGet("LC_ALL");
896 if (pszValue == 0)
897 pszValue = RTEnvGet("LC_MESSAGES");
898 if (pszValue == 0)
899 pszValue = RTEnvGet("LANG");
900 if (pszValue != 0)
901 return QLocale(pszValue).name();
902#endif
903 return QLocale::system().name();
904}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette