diff --git a/src/branding/default/banner.png b/src/branding/default/banner.png new file mode 100644 index 000000000..d1baeee85 Binary files /dev/null and b/src/branding/default/banner.png differ diff --git a/src/branding/default/branding.desc b/src/branding/default/branding.desc index b6694d1f4..53884e311 100644 --- a/src/branding/default/branding.desc +++ b/src/branding/default/branding.desc @@ -106,6 +106,11 @@ strings: # These images are loaded from the branding module directory. # +# productBanner is an optional image, which if present, will be shown +# on the welcome page of the application, above the welcome text. +# It is intended to have a width much greater than height. +# It is displayed at 64px height (also on HiDPI). +# Recommended size is 64px tall, and up to 460px wide. # productIcon is used as the window icon, and will (usually) be used # by the window manager to represent the application. This image # should be square, and may be displayed by the window manager @@ -121,8 +126,9 @@ strings: # # These filenames can also use substitutions from os-release (see above). images: - productLogo: "squid.png" + # productBanner: "banner.png" productIcon: "squid.png" + productLogo: "squid.png" productWelcome: "languages.png" # The slideshow is displayed during execution steps (e.g. when the diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index bf71fb0a2..6cbe980be 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -37,6 +37,7 @@ void LocaleTests::initTestCase() { // Otherwise plain get() is dubious in the TranslatableConfiguration tests + QLocale::setDefault( QLocale( QStringLiteral( "en_US" ) ) ); QVERIFY( ( QLocale().name() == "C" ) || ( QLocale().name() == "en_US" ) ); } diff --git a/src/libcalamares/utils/CalamaresUtilsSystem.cpp b/src/libcalamares/utils/CalamaresUtilsSystem.cpp index 651ac2c1e..327bece92 100644 --- a/src/libcalamares/utils/CalamaresUtilsSystem.cpp +++ b/src/libcalamares/utils/CalamaresUtilsSystem.cpp @@ -195,7 +195,7 @@ System::runCommand( System::RunLocation location, ? ( static_cast< int >( std::chrono::milliseconds( timeoutSec ).count() ) ) : -1 ) ) { - ( cWarning() << "Process" << args.first() << "timed out after" << timeoutSec.count() << "s. Output so far:\n" ).noquote().nospace() << process.readAllStandardOutput(); + cWarning() << "Process" << args.first() << "timed out after" << timeoutSec.count() << "s. Output so far:\n" << Logger::NoQuote{} << process.readAllStandardOutput(); return ProcessResult::Code::TimedOut; } @@ -203,7 +203,7 @@ System::runCommand( System::RunLocation location, if ( process.exitStatus() == QProcess::CrashExit ) { - ( cWarning() << "Process" << args.first() << "crashed. Output so far:\n" ).noquote().nospace() << output; + cWarning() << "Process" << args.first() << "crashed. Output so far:\n" << Logger::NoQuote{} << output; return ProcessResult::Code::Crashed; } @@ -212,7 +212,7 @@ System::runCommand( System::RunLocation location, bool showDebug = ( !Calamares::Settings::instance() ) || ( Calamares::Settings::instance()->debugMode() ); if ( ( r != 0 ) || showDebug ) { - ( cDebug() << "Target cmd:" << RedactedList( args ) << "output:\n" ).noquote().nospace() << output; + cDebug() << "Target cmd:" << RedactedList( args ) << "output:\n" << Logger::NoQuote{} << output; } return ProcessResult( r, output ); } diff --git a/src/libcalamares/utils/Logger.h b/src/libcalamares/utils/Logger.h index fe4b98fd4..24198e256 100644 --- a/src/libcalamares/utils/Logger.h +++ b/src/libcalamares/utils/Logger.h @@ -33,6 +33,9 @@ struct FuncSuppressor const char* m_s; }; +struct NoQuote {}; +struct Quote {}; + DLLEXPORT extern const FuncSuppressor Continuation; DLLEXPORT extern const FuncSuppressor SubEntry; @@ -74,6 +77,18 @@ operator<<( QDebug& s, const FuncSuppressor& f ) return s << f.m_s; } +inline QDebug& +operator<<( QDebug& s, const NoQuote& ) +{ + return s.noquote().nospace(); +} + +inline QDebug& +operator<<( QDebug& s, const Quote& ) +{ + return s.quote().space(); +} + /** * @brief The full path of the log file. */ diff --git a/src/libcalamaresui/Branding.cpp b/src/libcalamaresui/Branding.cpp index ff7e43fb8..67054a902 100644 --- a/src/libcalamaresui/Branding.cpp +++ b/src/libcalamaresui/Branding.cpp @@ -73,10 +73,11 @@ const QStringList Branding::s_stringEntryStrings = const QStringList Branding::s_imageEntryStrings = { - "productLogo", + "productBanner", "productIcon", - "productWelcome", - "productWallpaper" + "productLogo", + "productWallpaper", + "productWelcome" }; const QStringList Branding::s_styleEntryStrings = @@ -537,7 +538,7 @@ Branding::initSimpleSettings( const YAML::Node& doc ) [[noreturn]] void Branding::bail( const QString& message ) { - cError() << "FATAL in" << m_descriptorPath << "\n" + message; + cError() << "FATAL in" << m_descriptorPath << Logger::Continuation << Logger::NoQuote{} << message; ::exit( EXIT_FAILURE ); } diff --git a/src/libcalamaresui/Branding.h b/src/libcalamaresui/Branding.h index 023f1a511..847f28d89 100644 --- a/src/libcalamaresui/Branding.h +++ b/src/libcalamaresui/Branding.h @@ -67,10 +67,11 @@ public: enum ImageEntry : short { - ProductLogo, + ProductBanner, ProductIcon, - ProductWelcome, - ProductWallpaper + ProductLogo, + ProductWallpaper, + ProductWelcome }; Q_ENUM( ImageEntry ) diff --git a/src/modules/locale/Tests.cpp b/src/modules/locale/Tests.cpp index b07482ce7..af37a664b 100644 --- a/src/modules/locale/Tests.cpp +++ b/src/modules/locale/Tests.cpp @@ -25,12 +25,14 @@ #include +#include + QTEST_MAIN( LocaleTests ) -LocaleTests::LocaleTests() { } +LocaleTests::LocaleTests() {} -LocaleTests::~LocaleTests() { } +LocaleTests::~LocaleTests() {} void LocaleTests::initTestCase() @@ -147,6 +149,122 @@ LocaleTests::testTZImages() } } - QEXPECT_FAIL("", "TZ Images not yet all fixed", Continue); + QEXPECT_FAIL( "", "TZ Images not yet all fixed", Continue ); QCOMPARE( overlapcount, 0 ); } + +bool +operator<( const QPoint& l, const QPoint& r ) +{ + if ( l.x() < r.x() ) + { + return true; + } + if ( l.x() > r.x() ) + { + return false; + } + return l.y() < r.y(); +} + +void +listAll( const QPoint& p, const CalamaresUtils::Locale::CStringPairList& zones ) +{ + using namespace CalamaresUtils::Locale; + for ( const auto* pz : zones ) + { + const TZZone* zone = dynamic_cast< const TZZone* >( pz ); + if ( p == TimeZoneImageList::getLocationPosition( zone->longitude(), zone->latitude() ) ) + { + cError() << Logger::SubEntry << zone->zone(); + } + } +} + +void +LocaleTests::testTZLocations() +{ + using namespace CalamaresUtils::Locale; + const CStringPairList& regions = TZRegion::fromZoneTab(); + + int overlapcount = 0; + for ( const auto* pr : regions ) + { + const TZRegion* region = dynamic_cast< const TZRegion* >( pr ); + QVERIFY( region ); + + Logger::setupLogLevel( Logger::LOGDEBUG ); + cDebug() << "Region" << region->region() << "zones #" << region->zones().count(); + Logger::setupLogLevel( Logger::LOGERROR ); + + std::set< QPoint > occupied; + + const auto zones = region->zones(); + QVERIFY( zones.count() > 0 ); + for ( const auto* pz : zones ) + { + const TZZone* zone = dynamic_cast< const TZZone* >( pz ); + QVERIFY( zone ); + + auto pos = TimeZoneImageList::getLocationPosition( zone->longitude(), zone->latitude() ); + if ( occupied.find( pos ) != occupied.end() ) + { + cError() << "Zone" << zone->zone() << "occupies same spot as .."; + listAll( pos, zones ); + overlapcount++; + } + occupied.insert( pos ); + } + } + + QEXPECT_FAIL( "", "TZ Images contain pixel-overlaps", Continue ); + QCOMPARE( overlapcount, 0 ); +} + +const CalamaresUtils::Locale::TZZone* +findZone( const QString& name ) +{ + using namespace CalamaresUtils::Locale; + const CStringPairList& regions = TZRegion::fromZoneTab(); + + for ( const auto* pr : regions ) + { + const TZRegion* region = dynamic_cast< const TZRegion* >( pr ); + if ( !region ) + { + continue; + } + const auto zones = region->zones(); + for ( const auto* pz : zones ) + { + const TZZone* zone = dynamic_cast< const TZZone* >( pz ); + if ( !zone ) + { + continue; + } + + if ( zone->zone() == name ) + { + return zone; + } + } + } + return nullptr; +} + +void +LocaleTests::testSpecificLocations() +{ + const auto* gibraltar = findZone( "Gibraltar" ); + const auto* ceuta = findZone( "Ceuta" ); + QVERIFY( gibraltar ); + QVERIFY( ceuta ); + + auto gpos = TimeZoneImageList::getLocationPosition( gibraltar->longitude(), gibraltar->latitude() ); + auto cpos = TimeZoneImageList::getLocationPosition( ceuta->longitude(), ceuta->latitude() ); + QEXPECT_FAIL( "", "Gibraltar and Ceuta are really close", Continue ); + QVERIFY( gpos != cpos ); + QVERIFY( gibraltar->latitude() > ceuta->latitude() ); + QEXPECT_FAIL( "", "Gibraltar and Ceuta are really close", Continue ); + QVERIFY( gpos.y() < cpos.y() ); // Gibraltar is north of Ceuta +} diff --git a/src/modules/locale/Tests.h b/src/modules/locale/Tests.h index e0ca7ad0b..e01b1a25c 100644 --- a/src/modules/locale/Tests.h +++ b/src/modules/locale/Tests.h @@ -37,7 +37,9 @@ private Q_SLOTS: void testSplitLocaleConfiguration(); // Check the TZ images for consistency - void testTZImages(); + void testTZImages(); // No overlaps in images + void testTZLocations(); // No overlaps in locations + void testSpecificLocations(); }; #endif diff --git a/src/modules/partition/tests/ClearMountsJobTests.cpp b/src/modules/partition/tests/ClearMountsJobTests.cpp index 1f01c4638..17ff48945 100644 --- a/src/modules/partition/tests/ClearMountsJobTests.cpp +++ b/src/modules/partition/tests/ClearMountsJobTests.cpp @@ -42,7 +42,11 @@ getPartitionsForDevice_other(const QString& deviceName) process.start(); process.waitForFinished(); - const QString partitions = process.readAllStandardOutput(); + const QString partitions = process.readAllStandardOutput().trimmed(); + if ( partitions.isEmpty() ) + { + return QStringList(); + } const QStringList partitionsList = partitions.simplified().split( ' ' ); return partitionsList; diff --git a/src/modules/umount/main.py b/src/modules/umount/main.py index a337c481a..454222c5c 100644 --- a/src/modules/umount/main.py +++ b/src/modules/umount/main.py @@ -98,7 +98,9 @@ def run(): lst.sort(key=lambda x: x[1], reverse=True) for device, mount_point in lst: - subprocess.check_call(["umount", "-lv", mount_point]) + # On success, no output; if the command fails, its output is + # in the exception object. + subprocess.check_output(["umount", "-lv", mount_point], stderr=subprocess.STDOUT) os.rmdir(root_mount_point) diff --git a/src/modules/welcome/WelcomePage.cpp b/src/modules/welcome/WelcomePage.cpp index e4be00fe7..89fde33a0 100644 --- a/src/modules/welcome/WelcomePage.cpp +++ b/src/modules/welcome/WelcomePage.cpp @@ -51,7 +51,45 @@ WelcomePage::WelcomePage( Config* conf, QWidget* parent ) , m_languages( nullptr ) , m_conf( conf ) { + using Branding = Calamares::Branding; + const int defaultFontHeight = CalamaresUtils::defaultFontHeight(); + ui->setupUi( this ); + ui->aboutButton->setIcon( CalamaresUtils::defaultPixmap( + CalamaresUtils::Information, + CalamaresUtils::Original, + 2 * QSize( defaultFontHeight, defaultFontHeight ) ) ); + + // insert system-check widget below welcome text + const int welcome_text_idx = ui->verticalLayout->indexOf( ui->mainText ); + ui->verticalLayout->insertWidget( welcome_text_idx + 1, m_checkingWidget ); + + // insert optional logo banner image above welcome text + QString bannerPath = Branding::instance()->imagePath( Branding::ProductBanner ); + if ( !bannerPath.isEmpty() ) + { + // If the name is not empty, the file exists -- Branding checks that at startup + QPixmap bannerPixmap = QPixmap( bannerPath ); + if ( !bannerPixmap.isNull() ) + { + QLabel* bannerLabel = new QLabel; + bannerLabel->setPixmap( bannerPixmap ); + bannerLabel->setMinimumHeight( 64 ); + bannerLabel->setAlignment( Qt::AlignCenter ); + ui->aboveTextSpacer->changeSize( 20, defaultFontHeight ); // Shrink it down + ui->aboveTextSpacer->invalidate(); + ui->verticalLayout->insertSpacing( welcome_text_idx, defaultFontHeight ); + ui->verticalLayout->insertWidget( welcome_text_idx, bannerLabel ); + } + } + + initLanguages(); + + cDebug() << "Welcome string" << Calamares::Branding::instance()->welcomeStyleCalamares() + << *Calamares::Branding::VersionedName; + CALAMARES_RETRANSLATE_SLOT( &WelcomePage::retranslate ) + + connect( ui->aboutButton, &QPushButton::clicked, this, &WelcomePage::showAboutBox ); connect( Calamares::ModuleManager::instance(), &Calamares::ModuleManager::requirementsComplete, m_checkingWidget, @@ -60,28 +98,6 @@ WelcomePage::WelcomePage( Config* conf, QWidget* parent ) &Calamares::ModuleManager::requirementsProgress, m_checkingWidget, &CheckerContainer::requirementsProgress ); - ui->setupUi( this ); - - ui->verticalLayout->insertSpacing( 1, CalamaresUtils::defaultFontHeight() * 2 ); - initLanguages(); - - ui->mainText->setAlignment( Qt::AlignCenter ); - ui->mainText->setWordWrap( true ); - ui->mainText->setOpenExternalLinks( true ); - - cDebug() << "Welcome string" << Calamares::Branding::instance()->welcomeStyleCalamares() - << *Calamares::Branding::VersionedName; - - CALAMARES_RETRANSLATE_SLOT( &WelcomePage::retranslate ) - - ui->aboutButton->setIcon( CalamaresUtils::defaultPixmap( - CalamaresUtils::Information, - CalamaresUtils::Original, - 2 * QSize( CalamaresUtils::defaultFontHeight(), CalamaresUtils::defaultFontHeight() ) ) ); - connect( ui->aboutButton, &QPushButton::clicked, this, &WelcomePage::showAboutBox ); - - int welcome_text_idx = ui->verticalLayout->indexOf( ui->mainText ); - ui->verticalLayout->insertWidget( welcome_text_idx + 1, m_checkingWidget ); } void diff --git a/src/modules/welcome/WelcomePage.ui b/src/modules/welcome/WelcomePage.ui index 590029558..04b89f256 100644 --- a/src/modules/welcome/WelcomePage.ui +++ b/src/modules/welcome/WelcomePage.ui @@ -17,7 +17,7 @@ - + Qt::Vertical @@ -43,6 +43,12 @@ <Calamares welcome text> + + Qt::AlignCenter + + + true + @@ -78,15 +84,15 @@ - - Select application and system language - 2 0 + + Select application and system language +