Near-total rewrite to enhance UI/UX

master
Aaron Rainbolt 2 months ago
parent c184b42387
commit 056867affb

@ -26,6 +26,12 @@ set(PROJECT_SOURCES
src/backgroundscreen.h
src/backgroundscreen.cpp
src/resource.qrc
src/wifipassworddialog.h
src/wifipassworddialog.cpp
src/wifipassworddialog.ui
src/languagechangedialog.h
src/languagechangedialog.cpp
src/languagechangedialog.ui
)
set(TRANSLATION_RESOURCES "src/translations.qrc")
@ -51,7 +57,6 @@ install(TARGETS lubuntu-installer-prompt
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(PROGRAMS "scripts/change-system-language" DESTINATION libexec)
install(PROGRAMS "scripts/lubuntu-installer" DESTINATION libexec)
install(PROGRAMS "scripts/start-lubuntu-live-env" DESTINATION libexec)
install(FILES "lubuntu-live-environment.desktop" DESTINATION share/xsessions)

@ -2,9 +2,13 @@
This project is in beta. It presents a "Try or Install Lubuntu" screen. Eventually we want to extend this to support multiple flavors.
Releases are signed with Simon Quigley's GPG key: 5C7ABEA20F8630459CC8C8B5E27F2CF8458C2FA4
Art assets are copyrighted as follows:
All art assets are (C) 2010-2018 Rafael Laguna <rafaellaguna@gmail.com> (GPL-2.0+). Otherwise, the source is (C) 2022-2023 Lubuntu Developers <lubuntu-devel@lists.ubuntu.com>.
* img/arrow-down.svg: Copyright (C) 2014 Uri Herrera <uri_herrera@nitrux.in> and others. LGPL-3+ license. Taken from the Breeze icon theme.
* img/background.png: Copyright (C) 2024 Aaron Rainbolt <arraybolt3@ubuntu.com> and MediaMom Ministries. CC-BY-SA-4.0 license.
* img/lubuntu-logo.png, img/installer.png, img/emblem.png: Copyright (C) 2024 Lubuntu Developers <lubuntu-devel@lists.ubuntu.com>. Unknown license. Adapted from the official Lubuntu artwork.
All other files are licensed under the GNU General Public License version 3. Copyright (C) 2022-2024 Lubuntu Developers <lubuntu-devel@lists.ubuntu.com>.
## Architecture
@ -17,10 +21,10 @@ This section serves to explain how lubuntu-installer-prompt's various components
5. start-lubuntu-live-env starts /bin/lubuntu-installer-prompt but does not background it.
6. The installer prompt appears.
7. The user clicks "Install Lubuntu" or "Try Lubuntu".
8. If "Install Lubuntu" is clicked, the installer prompt executes /usr/libexec/lubuntu-installer.sh, which then executes Calamares. The installer prompt then removes the buttons from the screen.
8. If "Install Lubuntu" is clicked, the installer prompt executes `sudo -E calamares -D8`. The installer prompt then deactivates the buttons.
9. The installer window appears on the user's screen.
10. If Calamares closes, the installer prompt detects this and exits.
11. When the installer prompt exits, start-lubuntu-live-env kills Openbox, then runs startlxqt.
11. When the installer prompt exits, start-lubuntu-live-env kills Openbox and runs startlxqt.
12. If "Try Lubuntu" is clicked, steps 10 and 11 are executed immediately.
## Translations
@ -31,7 +35,7 @@ To add a new language to be translated:
* Open the `gen_ts.sh` script and add the locale code for the new language to the `langList` array.
* Run the script after doing this - a new template .ts file will be generated under `src/translations/`.
* Next, add the new template file to the `TS_FILES` list in `CMakeLists.txt` - it will be named `src/translations/lubuntu-installer-prompt-locale_CODE.ts`, where `locale_CODE` is the locale code of the added language.
* Next, add the new template file to the `TS_FILES` list in `CMakeLists.txt` - it will be named `src/translations/lubuntu-installer-prompt_locale_CODE.ts`, where `locale_CODE` is the locale code of the added language.
* Finally, add a line in the src/translations.qrc resource file to include the new translation file. The line should look like `<file alias="locale_CODE">lubuntu-installer-prompt_locale_CODE.qm</file>`, where `locale_CODE` is the locale code of the added language. This line should go inside the `<qresource>` tag.
For instance, if I were to add Chinese to the list of languages that could be translated into, I would do this:

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 32"
version="1.1"
id="svg5"
sodipodi:docname="arrow-down.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs9" />
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="23.21875"
inkscape:cx="16.086137"
inkscape:cy="16.086137"
inkscape:window-width="1920"
inkscape:window-height="1008"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg5" />
<style
type="text/css"
id="current-color-scheme">
.ColorScheme-Text {
color:#232629;
}
</style>
<path
id="path3"
style="fill:#ffffff;stroke-width:0.81806"
d="M 3.9687633,8.8275937 C 3.3903692,9.4059877 2.8119751,9.9843817 2.2335809,10.562776 6.8223872,15.151582 11.411193,19.740389 16,24.329195 20.588806,19.740389 25.177613,15.151582 29.766419,10.562776 28.802429,9.5987857 27.838438,8.6347957 26.874449,7.6708052 23.249632,11.295622 19.624816,14.920438 16,18.545254 12.375183,14.920438 8.7503672,11.295622 5.1255515,7.6708052 4.7399555,8.0564017 4.3543594,8.4419977 3.9687633,8.8275937 Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 14 KiB

@ -1,10 +1,10 @@
#!/bin/bash
LANGUAGE_CODE=$1
COUNTRY_CODE=$2
LANGUAGE_CODE="$1"
COUNTRY_CODE="$2"
LOCALE="${LANGUAGE_CODE}_${COUNTRY_CODE}.UTF-8"
LIBREOFFICE_CODE=$3
ONLY_LXQT=$4
LIBREOFFICE_CODE="$3"
ONLY_LXQT="$4"
# Special handling for language packs for Chinese
if [[ "$LANGUAGE_CODE" == "zh" ]]; then
@ -18,13 +18,14 @@ else
fi
if [ -z "$ONLY_LXQT" ]; then
apt-get -y install language-pack-gnome-$LANG_PACK_SUFFIX language-pack-kde-$LANG_PACK_SUFFIX
apt-get -y install language-pack-gnome-"$LANG_PACK_SUFFIX" language-pack-kde-"$LANG_PACK_SUFFIX"
# Install LibreOffice language pack only if LIBREOFFICE_CODE is provided
# and it's not English without a specific variant
if [[ "$LANGUAGE_CODE" != "en" ]] || [[ "$LANGUAGE_CODE" == "en" && ( "$COUNTRY_CODE" == "GB" || "$COUNTRY_CODE" == "ZA" ) ]]; then
apt-get -y install libreoffice-l10n-$LIBREOFFICE_CODE
apt-get -y install libreoffice-l10n-"$LIBREOFFICE_CODE"
fi
fi
update-locale LANGUAGE=$LOCALE LANG=$LOCALE LC_ALL=$LOCALE
locale-gen "$LOCALE"
update-locale LANGUAGE="$LOCALE" LANG="$LOCALE" LC_ALL="$LOCALE"
systemctl restart sddm

@ -1,3 +0,0 @@
#!/bin/bash
sudo -E calamares -D6

@ -6,22 +6,35 @@
BackgroundScreen::BackgroundScreen(QWidget *parent)
: QWidget(parent) {
}
BackgroundScreen::~BackgroundScreen()
{
}
void BackgroundScreen::activateBackground()
{
// Set the background image and scale it
QPixmap bg(":/background");
if (bg.isNull()) {
// the user will see the warning message that the InstallerPrompt object pops up, no need to bombard them with one message per screen
QImage image(":/background");
if (image.isNull()) {
return;
}
QScreen *screen = QGuiApplication::primaryScreen();
QRect screenGeometry = screen->geometry();
bg = bg.scaled(screenGeometry.size(), Qt::IgnoreAspectRatio);
qreal imgRatio = static_cast<qreal>(image.width()) / image.height();
qreal screenRatio = static_cast<qreal>(this->width()) / this->height();
QImage scaled;
if (imgRatio < screenRatio) {
scaled = image.scaledToWidth(this->width(), Qt::SmoothTransformation);
int yGap = (scaled.height() - this->height()) / 2;
scaled = scaled.copy(0, yGap, scaled.width(), this->height());
} else {
scaled = image.scaledToHeight(this->height(), Qt::SmoothTransformation);
int xGap = (scaled.width() - this->width()) / 2;
scaled = scaled.copy(xGap, 0, this->width(), scaled.height());
}
QPixmap bg = QPixmap::fromImage(scaled);
QPalette palette;
palette.setBrush(QPalette::Window, bg);
this->setPalette(palette);
}
BackgroundScreen::~BackgroundScreen()
{
}

@ -9,6 +9,7 @@ class BackgroundScreen : public QWidget {
public:
explicit BackgroundScreen(QWidget *parent = nullptr);
virtual ~BackgroundScreen();
void activateBackground();
};
#endif // BACKGROUNDSCREEN_H

@ -1,5 +1,6 @@
/*
* Copyright (C) 2022-2023 Lubuntu Developers <lubuntu-devel@lists.ubuntu.com>
* Copyright (C) 2024 Kubuntu Developers <kubuntu-devel@lists.ubuntu.com>
* Authored by: Simon Quigley <tsimonq2@lubuntu.me>
* Aaron Rainbolt <arraybolt3@lubuntu.me>
*
@ -13,8 +14,6 @@
* GNU General Public License for more details.
*/
#include <KBusyIndicatorWidget>
#include <KLed>
#include <NetworkManagerQt/ConnectionSettings>
#include <NetworkManagerQt/Manager>
#include <NetworkManagerQt/Device>
@ -27,10 +26,14 @@
#include <NetworkManagerQt/Settings>
#include <QProcess>
#include <QScreen>
#include <QTimer>
#include <QFile>
#include <QMessageBox>
#include <QUuid>
#include <QDBusPendingReply>
#include "installerprompt.h"
#include "wifipassworddialog.h"
#include "languagechangedialog.h"
#include "./ui_installerprompt.h"
InstallerPrompt::InstallerPrompt(QWidget *parent)
@ -38,367 +41,268 @@ InstallerPrompt::InstallerPrompt(QWidget *parent)
, ui(new Ui::InstallerPrompt) {
ui->setupUi(this);
// Hide the Incorrect Password and loading text
ui->incorrectPassword->setVisible(false);
ui->changingLanguageLabel->setVisible(false);
ui->changingLanguageLoader->setVisible(false);
updateConnectionInfo();
// Set the background image and scale it
QPixmap bg(":/background");
if (bg.isNull()) {
QMessageBox::warning(this, tr("Error"), tr("Background image cannot be loaded."));
return;
}
QScreen *screen = QGuiApplication::primaryScreen();
QRect screenGeometry = screen->geometry();
bg = bg.scaled(screenGeometry.size(), Qt::IgnoreAspectRatio);
QPalette palette;
palette.setBrush(QPalette::Window, bg);
this->setPalette(palette);
// Initialize process for external app launch
process = new QProcess(this);
// Set up signal-slot connections for buttons
connect(ui->tryLubuntu, &QAbstractButton::clicked, this, &InstallerPrompt::tryLubuntu);
connect(ui->installLubuntu, &QAbstractButton::clicked, this, &InstallerPrompt::installLubuntu);
connect(ui->connectWiFiButton, &QAbstractButton::clicked, this, &InstallerPrompt::onConnectWifiClicked);
connect(ui->confirmButton, &QAbstractButton::clicked, this, &InstallerPrompt::onLanguageConfirm);
// Set up the language combo box with available languages
initLanguageComboBox();
// Connect the appropriate language slots
connect(ui->languageComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onLanguageChanged(int)));
connect(ui->tryLubuntu, &QPushButton::clicked, this, &InstallerPrompt::onTryClicked);
connect(ui->installLubuntu, &QPushButton::clicked, this, &InstallerPrompt::onInstallClicked);
connect(ui->languageComboBox, SIGNAL(activated(int)), this, SLOT(onLanguageSelected(int)));
connect(ui->networkComboBox, SIGNAL(activated(int)), this, SLOT(onNetworkSelected(int)));
// Check initial network status and update UI
updateConnectionStatus();
QTimer *repeater = new QTimer();
connect(repeater, SIGNAL(timeout()), this, SLOT(updateConnectionInfo()));
repeater->start(15000);
}
// Find and store the Wi-Fi device
foreach (const NetworkManager::Device::Ptr &device, NetworkManager::networkInterfaces()) {
if (device->type() == NetworkManager::Device::Wifi) {
wifiDevice = device.objectCast<NetworkManager::WirelessDevice>();
connect(wifiDevice.data(), &NetworkManager::Device::stateChanged, this, &InstallerPrompt::handleWiFiConnectionChange);
break;
}
}
InstallerPrompt::~InstallerPrompt()
{
// Set up network manager signals for dynamic updates
auto nm = NetworkManager::notifier();
connect(nm, &NetworkManager::Notifier::deviceAdded, this, &InstallerPrompt::updateConnectionStatus);
connect(nm, &NetworkManager::Notifier::deviceRemoved, this, &InstallerPrompt::updateConnectionStatus);
connect(nm, &NetworkManager::Notifier::activeConnectionsChanged, this, &InstallerPrompt::updateConnectionStatus);
connect(nm, &NetworkManager::Notifier::wirelessEnabledChanged, this, &InstallerPrompt::updateConnectionStatus);
connect(nm, &NetworkManager::Notifier::activeConnectionAdded, this, &InstallerPrompt::updateConnectionStatus);
connect(nm, &NetworkManager::Notifier::connectivityChanged, this, &InstallerPrompt::updateConnectionStatus);
connect(nm, &NetworkManager::Notifier::primaryConnectionChanged, this, &InstallerPrompt::updateConnectionStatus);
}
void InstallerPrompt::updateConnectionStatus() {
auto status = NetworkManager::status();
bool online = false;
QString statusText;
switch (status) {
case NetworkManager::Status::Disconnected:
case NetworkManager::ConnectedLinkLocal:
case NetworkManager::Asleep:
statusText = tr("Not Connected");
ui->connectionLED->setColor(Qt::red);
ui->connectionLED->setState(KLed::Off);
break;
case NetworkManager::Status::Connected:
online = true;
statusText = tr("Connected");
ui->connectionLED->setColor(Qt::green);
ui->connectionLED->setState(KLed::On);
break;
case NetworkManager::Status::Connecting:
statusText = tr("Connecting...");
ui->connectionLED->setColor(Qt::yellow);
ui->connectionLED->setState(KLed::On);
break;
case NetworkManager::Status::Disconnecting:
statusText = tr("Disconnecting...");
ui->connectionLED->setColor(Qt::yellow);
ui->connectionLED->setState(KLed::On);
break;
default:
qDebug() << "Unknown status:" << status;
statusText = tr("Unknown Status");
ui->connectionLED->setColor(Qt::gray);
ui->connectionLED->setState(KLed::Off);
break;
void InstallerPrompt::activateBackground()
{
// Set the background image and scale it
QImage image(":/background");
if (image.isNull()) {
QMessageBox::warning(this, tr("Error"), tr("Background image cannot be loaded."));
return;
}
ui->connectionStatusLabel->setText(statusText);
const auto devices = NetworkManager::networkInterfaces();
bool wifiEnabled = false;
if (NetworkManager::isNetworkingEnabled()) {
for (const auto &device : devices) {
if (device->type() == NetworkManager::Device::Wifi && NetworkManager::isWirelessEnabled()) wifiEnabled = true;
}
qreal imgRatio = static_cast<qreal>(image.width()) / image.height();
qreal screenRatio = static_cast<qreal>(this->width()) / this->height();
QImage scaled;
if (imgRatio < screenRatio) {
scaled = image.scaledToWidth(this->width(), Qt::SmoothTransformation);
int yGap = (scaled.height() - this->height()) / 2;
scaled = scaled.copy(0, yGap, scaled.width(), this->height());
} else {
scaled = image.scaledToHeight(this->height(), Qt::SmoothTransformation);
int xGap = (scaled.width() - this->width()) / 2;
scaled = scaled.copy(xGap, 0, this->width(), scaled.height());
}
bool connectable = !online && wifiEnabled;
if (connectable) refreshNetworkList();
ui->connectWiFiButton->setVisible(connectable);
ui->WiFiLabel->setVisible(connectable);
ui->networkComboBox->setVisible(connectable);
ui->WiFiInfoLabel->setVisible(connectable);
ui->WiFiSpacer->changeSize(connectable ? 40 : 0, connectable ? 20 : 0, QSizePolicy::Fixed, QSizePolicy::Fixed);
QPixmap bg = QPixmap::fromImage(scaled);
QPalette palette;
palette.setBrush(QPalette::Window, bg);
this->setPalette(palette);
}
void InstallerPrompt::handleWiFiConnectionChange(NetworkManager::Device::State newstate, NetworkManager::Device::State oldstate, NetworkManager::Device::StateChangeReason reason)
void InstallerPrompt::onTryClicked()
{
QMutexLocker locker(&wifiChangeMutex);
if (reason == NetworkManager::Device::NoSecretsReason && !wifiWrongHandling) {
wifiWrongHandling = true;
qDebug() << wifiSSID;
foreach (const NetworkManager::Connection::Ptr &connection, NetworkManager::listConnections()) {
if (connection->settings()->connectionType() == NetworkManager::ConnectionSettings::Wireless) {
auto wirelessSetting = connection->settings()->setting(NetworkManager::Setting::Wireless).dynamicCast<NetworkManager::WirelessSetting>();
if (wirelessSetting && wirelessSetting->ssid() == wifiSSID) {
qDebug() << "Wiping connection with wrong password: " << wifiSSID;
// Show the Incorrect Password text
ui->incorrectPassword->setVisible(true);
QDBusPendingReply removeReply = connection->remove();
removeReply.waitForFinished();
}
}
}
wifiWrongHandling = false;
}
QApplication::quit();
}
NetworkManager::Connection::Ptr InstallerPrompt::findConnectionBySsid(const QString &ssid) {
foreach (const NetworkManager::Connection::Ptr &connection, NetworkManager::listConnections()) {
if (connection->settings()->connectionType() == NetworkManager::ConnectionSettings::Wireless) {
auto wirelessSetting = connection->settings()->setting(NetworkManager::Setting::Wireless).dynamicCast<NetworkManager::WirelessSetting>();
if (wirelessSetting && wirelessSetting->ssid() == ssid) {
return connection;
}
}
}
return NetworkManager::Connection::Ptr(); // Return null pointer if not found
}
void InstallerPrompt::onInstallClicked()
{
ui->tryLubuntu->setEnabled(false);
ui->installLubuntu->setEnabled(false);
ui->tryLubuntu->setToolTip("");
ui->installLubuntu->setToolTip("");
ui->languageComboBox->setEnabled(false);
QProcess *calamares = new QProcess(this);
calamares->setProgram("/usr/bin/sudo");
calamares->setArguments(QStringList() << "-E" << "calamares" << "-D8");
calamares->start();
QString InstallerPrompt::promptForWifiPassword(const QString &ssid, bool isWrongPassword) {
QDialog passwordDialog(this);
passwordDialog.setModal(true);
passwordDialog.setWindowTitle(tr("Wi-Fi Password Required"));
passwordDialog.setWindowIcon(QIcon::fromTheme("network-wireless"));
passwordDialog.setStyleSheet("QLabel { color: black; } ");
passwordDialog.setMinimumWidth(250);
passwordDialog.setMinimumHeight(120);
passwordDialog.setMaximumWidth(5000);
passwordDialog.setMaximumHeight(500);
QVBoxLayout layout;
QLabel passwordLabel(tr("Enter password for \"%1\":").arg(ssid), &passwordDialog);
QLineEdit passwordLineEdit(&passwordDialog);
QPushButton passwordButton(tr("Connect"), &passwordDialog);
passwordLineEdit.setEchoMode(QLineEdit::Password);
layout.addWidget(&passwordLabel);
layout.addWidget(&passwordLineEdit);
layout.addWidget(&passwordButton);
passwordDialog.setLayout(&layout);
// Connect with a lambda function for inline validation
connect(&passwordLineEdit, &QLineEdit::textChanged, this, [&passwordLineEdit](const QString &text) {
int minLength = 8;
int maxLength = 64;
bool isValid = text.length() >= minLength && text.length() <= maxLength;
passwordLineEdit.setStyleSheet(isValid ? "" : "border: 1px solid red;");
});
// If Calamares exits, it either crashed or the user cancelled the installation. Exit the installer prompt (and start LXQt).
connect(calamares, SIGNAL(finished(int)), this, SLOT(onTryClicked()));
}
connect(&passwordButton, &QPushButton::clicked, &passwordDialog, &QDialog::accept);
void InstallerPrompt::onLanguageSelected(int index)
{
QString selectedLanguage = ui->languageComboBox->itemText(index);
QString localeName = languageLocaleMap.value(selectedLanguage);
qDebug() << selectedLanguage << localeName;
if (passwordDialog.exec() == QDialog::Accepted) {
return passwordLineEdit.text();
}
// Split the locale name to get language and country code
QStringList localeParts = localeName.split('_');
QString languageCode = localeParts.value(0);
QString countryCode = localeParts.value(1);
return QString();
}
// If there is no internet connection and we don't ship the langpack, tell them
QStringList allowedLanguages = {"zh-hans", "hi", "es", "fr", "ar", "en"};
void InstallerPrompt::handleWifiConnection(const QString &ssid, bool recoverFromWrongPassword) {
ui->incorrectPassword->setVisible(false);
ui->networkComboBox->setEnabled(false);
wifiSSID = ssid;
qDebug() << "Attempting to find connection for SSID:" << ssid;
// Check for existing connection
NetworkManager::Connection::Ptr connection = findConnectionBySsid(ssid);
if (connection && !recoverFromWrongPassword) {
qDebug() << "Using existing connection for:" << ssid;
NetworkManager::activateConnection(connection->path(), wifiDevice->uni(), QString());
ui->networkComboBox->setEnabled(true);
return;
bool only_lxqt = false;
if (!allowedLanguages.contains(languageCode) && NetworkManager::status() != NetworkManager::Status::Connected) {
only_lxqt = true;
}
// Prompt for Wi-Fi password
QString password = promptForWifiPassword(ssid);
if (password.isEmpty()) {
ui->networkComboBox->setEnabled(true);
return;
// Some of the LibreOffice language packs need special-casing, do that here
QString libreOfficeLang;
if (localeParts[0] == "zh") {
libreOfficeLang = (countryCode == "CN" || countryCode == "SG" || countryCode == "MY") ? "zh-cn" : "zh-tw";
} else {
static const QMap<QString, QString> localeMap = {
{"en_GB", "en-gb"}, {"en_ZA", "en-za"}, {"pa_IN", "pa-in"}, {"pt_BR", "pt-br"}
};
libreOfficeLang = localeMap.value(localeParts.join('_'), languageCode);
}
// Create new Wi-Fi connection
NMVariantMapMap settings = createSettingsBySSID(ssid);
if (settings.isEmpty()) {
QMessageBox::warning(this, tr("Error"), tr("Failed to create Wi-Fi settings."));
ui->networkComboBox->setEnabled(true);
return;
}
// Construct the command to run the script with parameters
QProcess *process = new QProcess(this);
QStringList arguments;
// Update the wireless security settings
QVariantMap wirelessSecurity;
wirelessSecurity["key-mgmt"] = "wpa-psk";
wirelessSecurity["psk"] = password;
settings["802-11-wireless-security"] = wirelessSecurity;
// Add the new connection
QDBusPendingReply<QDBusObjectPath> reply = NetworkManager::addConnection(settings);
reply.waitForFinished();
if (reply.isError()) {
QMessageBox::warning(this, tr("Error"), tr("Failed to add Wi-Fi connection."));
ui->networkComboBox->setEnabled(true);
return;
}
process->setProgram("sudo");
arguments << "/usr/libexec/change-system-language" << languageCode << countryCode << libreOfficeLang;
if (only_lxqt) arguments << "1";
process->setArguments(arguments);
// Activate the new connection
QDBusObjectPath path = reply.value();
NetworkManager::activateConnection(path.path(), wifiDevice->uni(), QString());
ui->networkComboBox->setEnabled(true);
connect(process, &QProcess::errorOccurred, this, &InstallerPrompt::languageProcessError);
process->start();
LanguageChangeDialog lcd;
lcd.exec();
}
NMVariantMapMap InstallerPrompt::createSettingsBySSID(const QString &ssid) {
NMVariantMapMap convertedSettings;
void InstallerPrompt::languageProcessError(QProcess::ProcessError error) {
qDebug() << "Process failed with error:" << error;
}
if (!wifiDevice) {
qWarning() << "Wi-Fi device not found. Unable to set interface name.";
return convertedSettings;
}
void InstallerPrompt::onNetworkSelected(int index)
{
QString networkId = ui->networkComboBox->itemData(index).toString();
if (!networkId.isNull()) {
if (networkId.left(4) == "UNI_") { // this is an Ethernet device
QString deviceUni = networkId.right(networkId.count() - 4);
NetworkManager::WiredDevice wiredDevice(deviceUni);
QDBusPendingReply reply = wiredDevice.disconnectInterface();
reply.waitForFinished();
NetworkManager::Connection::List availableConnections = wiredDevice.availableConnections();
if (availableConnections.count() > 0) {
NetworkManager::activateConnection(availableConnections[0]->path(), wiredDevice.uni(), QString());
} else {
QMessageBox::warning(this, tr("Error"), tr("Selected Ethernet device has no network!"));
}
} else { // this is a Wifi connection
wifiSsid = networkId.right(networkId.length() - 4);
QDBusPendingReply reply = wifiDevice->disconnectInterface();
reply.waitForFinished();
NetworkManager::Connection::Ptr targetConnection;
foreach (const NetworkManager::Connection::Ptr &connection, NetworkManager::listConnections()) {
if (connection->settings()->connectionType() == NetworkManager::ConnectionSettings::Wireless) {
auto wirelessSetting = connection->settings()->setting(NetworkManager::Setting::Wireless).dynamicCast<NetworkManager::WirelessSetting>();
if (wirelessSetting && wirelessSetting->ssid() == wifiSsid) {
targetConnection = connection;
}
}
}
if (targetConnection) {
NetworkManager::activateConnection(targetConnection->path(), wifiDevice->uni(), QString());
} else {
WifiPasswordDialog wpd(wifiSsid);
wpd.exec();
QString password = wpd.getPassword();
if (password.isEmpty()) {
return;
}
NetworkManager::ConnectionSettings::Ptr newConnectionSettings(new NetworkManager::ConnectionSettings(NetworkManager::ConnectionSettings::Wireless));
newConnectionSettings->setId(ssid);
newConnectionSettings->setUuid(NetworkManager::ConnectionSettings::createNewUuid());
newConnectionSettings->setInterfaceName(wifiDevice->interfaceName());
// Configure wireless settings
QVariantMap wirelessSetting;
wirelessSetting.insert("ssid", ssid.toUtf8());
convertedSettings.insert("802-11-wireless", wirelessSetting);
// Convert other settings
const auto settingsMap = newConnectionSettings->toMap();
for (const auto &key : settingsMap.keys()) {
QVariant value = settingsMap.value(key);
convertedSettings.insert(key, value.toMap());
}
NMVariantMapMap wifiSettings;
if (!wifiDevice) {
qWarning() << "WiFi device not found. Unable to set interface name.";
return;
}
NetworkManager::ConnectionSettings::Ptr newConnectionSettings(new NetworkManager::ConnectionSettings(NetworkManager::ConnectionSettings::Wireless));
newConnectionSettings->setId(wifiSsid);
newConnectionSettings->setUuid(NetworkManager::ConnectionSettings::createNewUuid());
newConnectionSettings->setInterfaceName(wifiDevice->interfaceName());
QVariantMap wirelessSetting;
wirelessSetting.insert("ssid", wifiSsid.toUtf8());
wifiSettings.insert("802-11-wireless", wirelessSetting);
const auto settingsMap = newConnectionSettings->toMap();
for (const auto &key : settingsMap.keys()) {
QVariant value = settingsMap.value(key);
wifiSettings.insert(key, value.toMap());
}
return convertedSettings;
}
QVariantMap wirelessSecurity;
wirelessSecurity["key-mgmt"] = "wpa-psk";
wirelessSecurity["psk"] = password;
wifiSettings["802-11-wireless-security"] = wirelessSecurity;
void InstallerPrompt::onConnectWifiClicked() {
QString selectedSSID = ui->networkComboBox->currentText();
handleWifiConnection(selectedSSID);
}
QDBusPendingReply<QDBusObjectPath> reply = NetworkManager::addConnection(wifiSettings);
reply.waitForFinished();
if (reply.isError()) {
QMessageBox::warning(this, tr("Error"), tr("Failed to add WiFi connection."));
return;
}
void InstallerPrompt::showWifiOptions() {
bool foundWifiDevice = false;
for (const NetworkManager::Device::Ptr &device : NetworkManager::networkInterfaces()) {
if (device->type() == NetworkManager::Device::Wifi) {
foundWifiDevice = true;
auto wifiDevice = device.staticCast<NetworkManager::WirelessDevice>();
ui->networkComboBox->clear(); // Use the combo box from the UI file, clear existing items
for (const NetworkManager::WirelessNetwork::Ptr &network : wifiDevice->networks()) {
ui->networkComboBox->addItem(network->ssid()); // Add Wi-Fi networks to the combo box
QDBusObjectPath path = reply.value();
NetworkManager::activateConnection(path.path(), wifiDevice->uni(), QString());
}
break; // Handle the first Wi-Fi device
}
}
if (!foundWifiDevice) {
QMessageBox::information(this, tr("WiFi Not Available"), tr("No WiFi devices were found on this system."));
}
}
void InstallerPrompt::refreshNetworkList() {
NetworkManager::WirelessDevice::Ptr wirelessDevice;
// Iterate over network interfaces to find a wireless device
const auto devices = NetworkManager::networkInterfaces();
for (const auto &device : devices) {
if (device->type() == NetworkManager::Device::Wifi) {
wirelessDevice = device.staticCast<NetworkManager::WirelessDevice>();
break; // Break after finding the first wireless device
void InstallerPrompt::updateConnectionInfo()
{
ui->networkComboBox->clear();
int ethDevices = 0;
int connectedDevice = 0;
bool hitConnectedNetwork = false;
if (NetworkManager::isNetworkingEnabled()) {
for (const auto &device : NetworkManager::networkInterfaces()) {
if (device->type() == NetworkManager::Device::Ethernet) {
ethDevices++;
bool isDeviceConnected = device->state() == NetworkManager::Device::Activated;
if (isDeviceConnected) {
hitConnectedNetwork = true;
}
QString isConnected = isDeviceConnected ? tr("Connected") : tr("Disconnected");
if (!hitConnectedNetwork) {
connectedDevice++;
}
ui->networkComboBox->addItem(tr("Ethernet %1: %2").arg(ethDevices).arg(isConnected), QString("UNI_") + device->uni());
} else if (device->type() == NetworkManager::Device::Wifi && NetworkManager::isWirelessEnabled() && !hitWifiDevice) {
hitWifiDevice = true;
wifiDevice = device.staticCast<NetworkManager::WirelessDevice>();
connect(wifiDevice.data(), &NetworkManager::Device::stateChanged, this, &InstallerPrompt::handleWiFiConnectionChange);
}
}
if (hitWifiDevice) {
NetworkManager::ActiveConnection::Ptr activeConnection = wifiDevice->activeConnection();
NetworkManager::Connection::Ptr underlyingActiveConnection = activeConnection->connection();
for (const auto &network : wifiDevice->networks()) {
QString ssid = network->ssid();
bool isNetworkConnected = false;
if (activeConnection->state() == NetworkManager::ActiveConnection::Activated) {
NetworkManager::WirelessSetting::Ptr underlyingWirelessSetting = underlyingActiveConnection->settings()->setting(NetworkManager::Setting::Wireless).staticCast<NetworkManager::WirelessSetting>();
if (network->ssid() == QString(underlyingWirelessSetting->ssid())) {
isNetworkConnected = true;
hitConnectedNetwork = true;
}
}
QString isConnected = isNetworkConnected ? tr("Connected") : tr("Disconnected");
if (!hitConnectedNetwork) {
connectedDevice++;
}
ui->networkComboBox->addItem(tr("WiFi %1: %2").arg(ssid).arg(isConnected), QString("SSID") + ssid);
}
}
}
if (!wirelessDevice) {
// No wireless device found, handle appropriately
ui->networkComboBox->setVisible(false);
ui->connectWiFiButton->setVisible(false);
return;
}
QMap<QString, NetworkManager::WirelessNetwork::Ptr> tempWifiNetworkMap;
QStringList ssidList;
const auto networks = wirelessDevice->networks();
for (const auto &network : networks) {
QString ssid = network->ssid();
tempWifiNetworkMap[ssid] = network;
ssidList.append(ssid);
if (hitConnectedNetwork) {
ui->networkComboBox->setCurrentIndex(connectedDevice);
}
// Update the main map and combo box only after the new list is ready
ui->networkComboBox->clear();
ui->networkComboBox->addItems(ssidList);
// Adjust visibility
ui->networkComboBox->setVisible(!networks.isEmpty());
ui->connectWiFiButton->setVisible(!networks.isEmpty());
}
QString InstallerPrompt::getDisplayNameForLocale(const QLocale &locale) {
auto sanitize = [](QString s) -> QString {
s.replace("St.", "Saint", Qt::CaseInsensitive);
s.replace("&", "and", Qt::CaseInsensitive);
return s;
};
QLocale currentAppLocale = QLocale::system();
QString nativeName = locale.nativeLanguageName();
QString nativeCountryName = sanitize(locale.nativeCountryName());
QString englishLanguageName = currentAppLocale.languageToString(locale.language());
QString englishCountryName = sanitize(currentAppLocale.countryToString(locale.country()));
if (nativeName.isEmpty() || nativeCountryName.isEmpty()) {
return QString();
}
// Rename "American English" to "English"
if (locale.language() == QLocale::English) {
nativeName = "English";
englishLanguageName = "English";
}
void InstallerPrompt::handleWiFiConnectionChange(NetworkManager::Device::State newstate, NetworkManager::Device::State oldstate, NetworkManager::Device::StateChangeReason reason)
{
if (reason == NetworkManager::Device::NoSecretsReason && !wifiWrongHandling) {
wifiWrongHandling = true;
QString displayName = nativeName + " (" + nativeCountryName + ")";
if (nativeName.compare(englishLanguageName, Qt::CaseInsensitive) != 0 &&
nativeCountryName.compare(englishCountryName, Qt::CaseInsensitive) != 0) {
displayName += " - " + englishLanguageName + " (" + englishCountryName + ")";
foreach (const NetworkManager::Connection::Ptr &connection, NetworkManager::listConnections()) {
if (connection->settings()->connectionType() == NetworkManager::ConnectionSettings::Wireless) {
auto wirelessSetting = connection->settings()->setting(NetworkManager::Setting::Wireless).dynamicCast<NetworkManager::WirelessSetting>();
if (wirelessSetting && wirelessSetting->ssid() == wifiSsid) {
qDebug() << "Wiping connection with wrong password: " << wifiSsid;
QDBusPendingReply removeReply = connection->remove();
removeReply.waitForFinished();
QMessageBox::warning(this, tr("Error"), tr("WiFi password was incorrect."));
}
}
}
wifiWrongHandling = false;
}
return displayName;
}
void InstallerPrompt::initLanguageComboBox() {
@ -425,9 +329,34 @@ void InstallerPrompt::initLanguageComboBox() {
QStringList InstallerPrompt::getAvailableLanguages() {
QMap<QString, QString> language_map; // Default sorting by QString is case-sensitive
QStringList supported_languages;
QFile supported_locale_file("/usr/share/i18n/SUPPORTED");
char lineBuf[2048];
if (supported_locale_file.open(QFile::ReadOnly)) {
while (!supported_locale_file.atEnd()) {
supported_locale_file.readLine(lineBuf, 2048);
QString line(lineBuf);
if (!line.contains("UTF-8")) continue;
QStringList lineParts = line.split('.');
if (lineParts.count() == 1) {
lineParts = line.split(' ');
if (lineParts.count() == 1) {
QMessageBox::warning(this, tr("Error"), tr("Something has gone very wrong trying to parse the list of supported languages!"));
}
}
supported_languages.append(lineParts[0]);
}
supported_locale_file.close();
}
QList<QLocale> all_locales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);
for (const QLocale &locale : all_locales) {
QString check_locale_name = locale.name();
if (check_locale_name.contains(".UTF-8")) {
check_locale_name = check_locale_name.left(check_locale_name.length() - 6);
}
if (!supported_languages.contains(check_locale_name)) continue;
QString display_name = getDisplayNameForLocale(locale);
if (display_name.isEmpty()) continue;
@ -449,93 +378,34 @@ QStringList InstallerPrompt::getAvailableLanguages() {
return sorted_languages;
}
void InstallerPrompt::onLanguageChanged(int index) {
selectedLanguage = ui->languageComboBox->itemText(index);
}
void InstallerPrompt::onLanguageConfirm() {
ui->changingLanguageLabel->setVisible(true);
ui->changingLanguageLoader->setVisible(true);
ui->languageComboBox->setEnabled(false);
ui->tryLubuntu->setEnabled(false);
ui->installLubuntu->setEnabled(false);
localeName = languageLocaleMap.value(selectedLanguage);
qDebug() << selectedLanguage << localeName;
QString InstallerPrompt::getDisplayNameForLocale(const QLocale &locale) {
auto sanitize = [](QString s) -> QString {
s.replace("St.", "Saint", Qt::CaseInsensitive);
s.replace("&", "and", Qt::CaseInsensitive);
return s;
};
// Split the locale name to get language and country code
QStringList localeParts = localeName.split('_');
QString languageCode = localeParts.value(0);
QString countryCode = localeParts.value(1);
QLocale currentAppLocale = QLocale::system();
QString nativeName = locale.nativeLanguageName();
QString nativeCountryName = sanitize(locale.nativeCountryName());
QString englishLanguageName = currentAppLocale.languageToString(locale.language());
QString englishCountryName = sanitize(currentAppLocale.countryToString(locale.country()));
// If there is no internet connection and we don't ship the langpack, tell them
QStringList allowedLanguages = {"zh-hans", "hi", "es", "fr", "ar", "en"};
bool only_lxqt = false;
if (!allowedLanguages.contains(languageCode) && NetworkManager::status() != NetworkManager::Status::Connected) {
ui->changingLanguageLabel->setText(tr("Unable to download full language support, changing anyway..."));
only_lxqt = true;
} else {
ui->changingLanguageLabel->setText(tr("Changing language..."));
if (nativeName.isEmpty() || nativeCountryName.isEmpty()) {
return QString();
}
// Some of the LibreOffice language packs need special-casing, do that here
QString libreOfficeLang;
if (localeParts[0] == "zh") {
libreOfficeLang = (countryCode == "CN" || countryCode == "SG" || countryCode == "MY") ? "zh-cn" : "zh-tw";
} else {
static const QMap<QString, QString> localeMap = {
{"en_GB", "en-gb"}, {"en_ZA", "en-za"}, {"pa_IN", "pa-in"}, {"pt_BR", "pt-br"}
};
libreOfficeLang = localeMap.value(localeParts.join('_'), languageCode);
// Rename "American English" to "English"
if (locale.language() == QLocale::English) {
nativeName = "English";
englishLanguageName = "English";
}
// Construct the command to run the script with parameters
QProcess *process = new QProcess(this);
QStringList arguments;
process->setProgram("/usr/libexec/change-system-language");
arguments << languageCode << countryCode << libreOfficeLang;
if (only_lxqt) arguments << "1";
process->setArguments(arguments);
connect(process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &InstallerPrompt::languageProcessFinished);
connect(process, &QProcess::errorOccurred, this, &InstallerPrompt::languageProcessError);
process->start();
}
void InstallerPrompt::languageProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) {
qDebug() << "Process finished. Exit code:" << exitCode << "Exit status:" << exitStatus;
ui->changingLanguageLabel->setVisible(false);
ui->changingLanguageLoader->setVisible(false);
ui->languageComboBox->setEnabled(true);
ui->tryLubuntu->setEnabled(true);
ui->installLubuntu->setEnabled(true);
}
void InstallerPrompt::languageProcessError(QProcess::ProcessError error) {
qDebug() << "Process failed with error:" << error;
}
void InstallerPrompt::tryLubuntu()
{
QApplication::quit();
}
void InstallerPrompt::installLubuntu()
{
ui->tryLubuntu->setVisible(false);
ui->installLubuntu->setVisible(false);
ui->confirmButton->setVisible(false);
ui->languageComboBox->setEnabled(false);
QProcess *calamares = new QProcess(this);
QStringList args;
calamares->start("/usr/libexec/lubuntu-installer", args);
// If Calamares exits, it either crashed or the user cancelled the installation. Exit the installer prompt (and start LXQt).
connect(calamares, SIGNAL(finished(int)), this, SLOT(tryLubuntu()));
}
QString displayName = nativeName + " (" + nativeCountryName + ")";
if (nativeName.compare(englishLanguageName, Qt::CaseInsensitive) != 0 &&
nativeCountryName.compare(englishCountryName, Qt::CaseInsensitive) != 0) {
displayName += " - " + englishLanguageName + " (" + englishCountryName + ")";
}
InstallerPrompt::~InstallerPrompt()
{
delete ui;
return displayName;
}

@ -12,6 +12,7 @@
#include <QProcess>
#include <NetworkManagerQt/Device>
#include <NetworkManagerQt/WirelessDevice>
#include <NetworkManagerQt/WiredDevice>
#include <NetworkManagerQt/WirelessNetwork>
namespace Ui { class InstallerPrompt; }
@ -22,40 +23,28 @@ class InstallerPrompt : public QMainWindow {
public:
explicit InstallerPrompt(QWidget *parent = nullptr);
~InstallerPrompt() override;
void activateBackground();
private slots:
void refreshNetworkList();
void onLanguageChanged(int index);
void onLanguageConfirm();
void onConnectWifiClicked();
void updateConnectionStatus();
void onTryClicked();
void onInstallClicked();
void onLanguageSelected(int index);
void onNetworkSelected(int index);
void updateConnectionInfo();
void handleWiFiConnectionChange(NetworkManager::Device::State newstate, NetworkManager::Device::State oldstate, NetworkManager::Device::StateChangeReason reason);
void tryLubuntu();
void installLubuntu();
void languageProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
void languageProcessError(QProcess::ProcessError error);
private:
Ui::InstallerPrompt *ui;
QProcess *process;
NetworkManager::WirelessDevice::Ptr wifiDevice;
QString wifiSSID;
QMutex wifiChangeMutex;
NetworkManager::Connection::Ptr findConnectionBySsid(const QString &ssid);
bool hitWifiDevice = false;
QString wifiSsid;
bool wifiWrongHandling = false;
QLineEdit *passwordLineEdit;
QMap<QString, QString> languageLocaleMap;
QString selectedLanguage = "English (United States)";
QString localeName = "en_US";
QString getDisplayNameForLocale(const QLocale &locale);
void handleWifiConnection(const QString &ssid, bool recoverFromWrongPassword = false);
QString promptForWifiPassword(const QString &ssid, bool isWrongPassword = false);
void connectToWifi(const QString &ssid, const QString &password, bool recoverFromWrongPassword = false);
void initLanguageComboBox();
QStringList getAvailableLanguages();
void showWifiOptions();
NMVariantMapMap createSettingsBySSID(const QString &ssid);
QString getDisplayNameForLocale(const QLocale &locale);
};
#endif // INSTALLERPROMPT_H

File diff suppressed because it is too large Load Diff

@ -0,0 +1,14 @@
#include "languagechangedialog.h"
#include "ui_languagechangedialog.h"
LanguageChangeDialog::LanguageChangeDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::LanguageChangeDialog)
{
ui->setupUi(this);
}
LanguageChangeDialog::~LanguageChangeDialog()
{
delete ui;
}

@ -0,0 +1,22 @@
#ifndef LANGUAGECHANGEDIALOG_H
#define LANGUAGECHANGEDIALOG_H
#include <QDialog>
namespace Ui {
class LanguageChangeDialog;
}
class LanguageChangeDialog : public QDialog
{
Q_OBJECT
public:
explicit LanguageChangeDialog(QWidget *parent = nullptr);
~LanguageChangeDialog();
private:
Ui::LanguageChangeDialog *ui;
};
#endif // LANGUAGECHANGEDIALOG_H

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LanguageChangeDialog</class>
<widget class="QDialog" name="LanguageChangeDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>373</width>
<height>66</height>
</rect>
</property>
<property name="windowTitle">
<string>Lubuntu</string>
</property>
<property name="windowIcon">
<iconset theme="system-software-install">
<normaloff>.</normaloff>.</iconset>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Changing language...</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="maximum">
<number>0</number>
</property>
<property name="value">
<number>-1</number>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

@ -40,12 +40,14 @@ int main(int argc, char *argv[])
for (QScreen *screen : QApplication::screens()) {
if (screen == QApplication::primaryScreen()) {
w = new InstallerPrompt();
w->setGeometry(screen->geometry());
w->setGeometry(screen->geometry());
w->activateBackground();
w->showFullScreen();
continue;
}
BackgroundScreen *backscreen = new BackgroundScreen();
backscreen->setGeometry(screen->geometry());
backscreen->activateBackground();
backscreen->showFullScreen();
bss.append(backscreen);
}

@ -1,8 +1,9 @@
<RCC>
<qresource prefix="/">
<file alias="logo">../img/lubuntu-logo.png</file>
<file alias="background">../img/background.png</file>
<file alias="installer">../img/installer.png</file>
<file alias="symbol">../img/symbol.png</file>
<file alias="logo">../img/lubuntu-logo.png</file>
<file alias="arrow-down">../img/arrow-down.svg</file>
</qresource>
</RCC>

@ -9,137 +9,125 @@
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../installerprompt.ui" line="237"/>
<location filename="../installerprompt.ui" line="202"/>
<source>Select Your Language:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../installerprompt.ui" line="263"/>
<source> Confirm</source>
<location filename="../installerprompt.ui" line="281"/>
<source>Internet Connection:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../installerprompt.ui" line="280"/>
<source>You entered an incorrect password. Please try again.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../installerprompt.ui" line="320"/>
<source>Select a Wi-Fi Network:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../installerprompt.ui" line="380"/>
<source>(For advanced network configuration, select &quot;Try Lubuntu&quot;)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../installerprompt.ui" line="419"/>
<source> Connect</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../installerprompt.ui" line="433"/>
<location filename="../installerprompt.cpp" line="475"/>
<source>Changing language...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../installerprompt.ui" line="525"/>
<location filename="../installerprompt.ui" line="371"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Try Lubuntu without installing it on your system. Allows you to use programs, browse the Web, and even temporarily install apps without modifying your system. Changes made within the live environment will be lost when the computer shuts down.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../installerprompt.ui" line="539"/>
<location filename="../installerprompt.ui" line="389"/>
<source> Try Lubuntu</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../installerprompt.ui" line="589"/>
<location filename="../installerprompt.ui" line="438"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Install Lubuntu onto your system. You can install Lubuntu by itself, or alongside an existing operating system.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../installerprompt.ui" line="600"/>
<location filename="../installerprompt.ui" line="453"/>
<source> Install Lubuntu</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../installerprompt.ui" line="727"/>
<location filename="../installerprompt.cpp" line="114"/>
<source>Connected</source>
<location filename="../installerprompt.cpp" line="68"/>
<location filename="../installerprompt.cpp" line="175"/>
<location filename="../installerprompt.cpp" line="226"/>
<location filename="../installerprompt.cpp" line="300"/>
<location filename="../installerprompt.cpp" line="345"/>
<source>Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../installerprompt.cpp" line="49"/>
<location filename="../installerprompt.cpp" line="256"/>
<location filename="../installerprompt.cpp" line="271"/>
<source>Error</source>
<location filename="../installerprompt.cpp" line="68"/>
<source>Background image cannot be loaded.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../installerprompt.cpp" line="49"/>
<source>Background image cannot be loaded.</source>
<location filename="../installerprompt.cpp" line="175"/>
<source>Selected Ethernet device has no network!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../installerprompt.cpp" line="108"/>
<source>Not Connected</source>
<location filename="../installerprompt.cpp" line="226"/>
<source>Failed to add WiFi connection.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../installerprompt.cpp" line="119"/>
<source>Connecting...</source>
<location filename="../installerprompt.cpp" line="251"/>
<location filename="../installerprompt.cpp" line="275"/>
<source>Connected</source>
<translation type="unfinished"></translation>
</message>