summaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
authorA. Wilcox <AWilcox@Wilcox-Tech.com>2019-11-14 12:04:30 -0600
committerA. Wilcox <AWilcox@Wilcox-Tech.com>2019-11-14 12:04:30 -0600
commite25f4a71ce293a7640f3a3def5094a3ad48a761f (patch)
tree90a107b427f3411dbc20171be6f11bf21b98671d /ui
parent84e180e488e80c6e3e4d743290d8ac0694ea4cf5 (diff)
downloadhorizon-e25f4a71ce293a7640f3a3def5094a3ad48a761f.tar.gz
horizon-e25f4a71ce293a7640f3a3def5094a3ad48a761f.tar.bz2
horizon-e25f4a71ce293a7640f3a3def5094a3ad48a761f.tar.xz
horizon-e25f4a71ce293a7640f3a3def5094a3ad48a761f.zip
Qt UI: Implement UI.SysMeta.DateTime/Timezone page
Diffstat (limited to 'ui')
-rw-r--r--ui/qt5/CMakeLists.txt4
-rw-r--r--ui/qt5/datetimepage.cc229
-rw-r--r--ui/qt5/datetimepage.hh73
-rw-r--r--ui/qt5/horizon.qrc1
-rw-r--r--ui/qt5/horizonwizard.cc4
-rw-r--r--ui/qt5/horizonwizard.hh1
-rw-r--r--ui/qt5/networkifacepage.cc2
-rw-r--r--ui/qt5/resources/datetime-help.txt17
8 files changed, 329 insertions, 2 deletions
diff --git a/ui/qt5/CMakeLists.txt b/ui/qt5/CMakeLists.txt
index 7bf3107..fc2069d 100644
--- a/ui/qt5/CMakeLists.txt
+++ b/ui/qt5/CMakeLists.txt
@@ -12,6 +12,7 @@ set(UI_SOURCES
networkingpage.cc
networkifacepage.cc
netsimplewifipage.cc
+ datetimepage.cc
horizon.qrc)
add_executable(horizon-qt5 ${UI_SOURCES})
@@ -22,6 +23,7 @@ install(TARGETS horizon-qt5 DESTINATION bin)
IF(INSTALL)
pkg_check_modules(XKBFile REQUIRED xkbfile)
pkg_check_modules(LIBX11 REQUIRED x11)
- target_link_libraries(horizon-qt5 ${LIBUDEV_LIBRARIES} ${LIBX11_LIBRARIES} ${XKBFile_LIBRARIES})
+ pkg_check_modules(LibCap REQUIRED libcap)
+ target_link_libraries(horizon-qt5 ${LIBUDEV_LIBRARIES} ${LibCap_LIBRARIES} ${LIBX11_LIBRARIES} ${XKBFile_LIBRARIES})
# add_executable(horizon-run-qt5 ${RUN_QT_SOURCES})
ENDIF(INSTALL)
diff --git a/ui/qt5/datetimepage.cc b/ui/qt5/datetimepage.cc
new file mode 100644
index 0000000..a333c15
--- /dev/null
+++ b/ui/qt5/datetimepage.cc
@@ -0,0 +1,229 @@
+/*
+ * datetimepage.cc - Implementation of the UI.SysMeta.DateTime page
+ * horizon-qt5, the Qt 5 user interface for
+ * Project Horizon
+ *
+ * Copyright (c) 2019 Adélie Linux and contributors. All rights reserved.
+ * This code is licensed under the AGPL 3.0 license, as noted in the
+ * LICENSE-code file in the root directory of this repository.
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+#include "datetimepage.hh"
+
+#include <QCheckBox>
+#include <QGroupBox>
+#include <QHBoxLayout>
+#include <QLineEdit>
+#include <QSortFilterProxyModel>
+#include <QTimeZone>
+#include <QVBoxLayout>
+
+#include <iostream>
+#ifdef HAS_INSTALL_ENV
+# include <sys/capability.h>
+# include <time.h>
+#endif /* HAS_INSTALL_ENV */
+
+
+TimeZone::TimeZone() : ianaName(), friendlyName(), offset(0) {}
+
+TimeZone::TimeZone(QByteArray iana) {
+ QTimeZone zone(iana);
+ QString offset, longName, comment;
+ this->ianaName = iana;
+
+ offset = zone.displayName(QTimeZone::GenericTime, QTimeZone::OffsetName);
+ this->friendlyName = "(" + offset + ") " + iana;
+ longName = zone.displayName(QTimeZone::GenericTime, QTimeZone::LongName);
+ if(longName != offset) {
+ this->friendlyName += " - " + longName;
+ }
+
+ comment = zone.comment();
+ if(comment.size() > 0 && comment != offset) {
+ this->friendlyName += " (" + comment + ")";
+ }
+
+ this->offset = zone.standardTimeOffset(QDateTime::currentDateTimeUtc());
+}
+
+
+TimeZoneModel::TimeZoneModel(QWidget *parent) : QAbstractListModel(parent) {
+ for(auto &iana : QTimeZone::availableTimeZoneIds()) {
+ TimeZone tzObj(iana);
+ zones.push_back(tzObj);
+ }
+
+ std::sort(zones.begin(), zones.end(), [](TimeZone tz1, TimeZone tz2) {
+ if(tz1.offset < tz2.offset) return true;
+ if(tz2.offset < tz1.offset) return false;
+ return tz1.friendlyName < tz2.friendlyName;
+ });
+}
+
+int TimeZoneModel::rowCount(const QModelIndex &) const {
+ return zones.size();
+}
+
+QVariant TimeZoneModel::data(const QModelIndex &index, int role) const {
+ if(!index.isValid()) {
+ return QVariant();
+ }
+
+ if(index.row() > zones.size()) {
+ return QVariant();
+ }
+
+ TimeZone zone = zones.at(index.row());
+
+ switch(role)
+ {
+ case Qt::DisplayRole:
+ return zone.friendlyName;
+ case Qt::ToolTipRole:
+ return QString(zone.ianaName);
+ default:
+ return QVariant();
+ }
+}
+
+QVariant TimeZoneModel::headerData(int, Qt::Orientation, int role) const {
+ if(role != Qt::DisplayRole) {
+ return QVariant();
+ }
+
+ return QString("Time Zone");
+}
+
+
+/*! Try to gain CAP_SYS_TIME capability.
+ * If we do, then enable the date/time edit boxes.
+ * This does nothing in the Runtime Environment.
+ */
+void DateTimePage::maybeRaiseCap() {
+#ifdef HAS_INSTALL_ENV
+ cap_t captain;
+ cap_value_t time_cap = CAP_SYS_TIME;
+
+ if(!CAP_IS_SUPPORTED(CAP_SYS_TIME))
+ return;
+
+ captain = cap_get_proc();
+ if(captain == nullptr)
+ return;
+
+ if(cap_set_flag(captain, CAP_EFFECTIVE, 1, &time_cap, CAP_SET) == -1)
+ return;
+
+ if(cap_set_proc(captain))
+ return;
+
+ cap_free(captain);
+
+ dateEdit->setEnabled(true);
+ timeEdit->setEnabled(true);
+#endif /* HAS_INSTALL_ENV */
+}
+
+DateTimePage::DateTimePage(QWidget *parent) : HorizonWizardPage(parent) {
+ setTitle(tr("Date and Time Settings"));
+
+ dateEdit = new QDateEdit(QDate::currentDate());
+ dateEdit->setDisplayFormat("dd MMMM yyyy");
+ dateEdit->setEnabled(false);
+ timeEdit = new QTimeEdit(QTime::currentTime());
+ timeEdit->setDisplayFormat("HH:mm:ss");
+ timeEdit->setEnabled(false);
+
+#ifdef HAS_INSTALL_ENV
+ /* explanations:
+ *
+ * isEnabled check:
+ * to prevent wasting time in the case where we don't have perms
+ *
+ * Qt::UTC:
+ * CLOCK_REALTIME is always UTC, so we need the UTC form of the
+ * current date/time.
+ */
+ connect(dateEdit, &QDateEdit::dateChanged, [=](const QDate &date) {
+ if(dateEdit->isEnabled() && date != QDate::currentDate()) {
+ QDateTime *newDT = new QDateTime(date, QTime::currentTime(),
+ Qt::UTC);
+ struct timespec ts = {newDT->toSecsSinceEpoch(), 0};
+ clock_settime(CLOCK_REALTIME, &ts);
+ }
+ });
+ connect(timeEdit, &QTimeEdit::timeChanged, [=](const QTime &time) {
+ if(timeEdit->isEnabled() && time != QTime::currentTime()) {
+ QDateTime *newDT = new QDateTime(QDate::currentDate(), time,
+ Qt::UTC);
+ struct timespec ts = {newDT->toSecsSinceEpoch(), 0};
+ clock_settime(CLOCK_REALTIME, &ts);
+ }
+ });
+#endif /* HAS_INSTALL_ENV */
+
+ updateTimer = new QTimer(this);
+ updateTimer->setInterval(1000);
+ connect(updateTimer, &QTimer::timeout, [=]{
+ dateEdit->setDate(QDate::currentDate());
+ timeEdit->setTime(QTime::currentTime());
+ });
+
+ QHBoxLayout *dateTimeLayout = new QHBoxLayout;
+ dateTimeLayout->addWidget(dateEdit);
+ dateTimeLayout->addWidget(timeEdit);
+ QGroupBox *dateTimeGroup = new QGroupBox(tr("Date and Time"));
+ dateTimeGroup->setLayout(dateTimeLayout);
+
+ QLineEdit *timeZoneSearch = new QLineEdit;
+ timeZoneSearch->addAction(QIcon::fromTheme("edit-find"),
+ QLineEdit::LeadingPosition);
+ timeZoneSearch->setPlaceholderText(tr("Search for a time zone"));
+ QSortFilterProxyModel *sortModel = new QSortFilterProxyModel(this);
+ sortModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+ sortModel->setSourceModel(&zoneModel);
+ connect(timeZoneSearch, &QLineEdit::textChanged, [=](const QString &text) {
+ sortModel->setFilterFixedString(text);
+ });
+ timeZoneList = new QListView;
+ timeZoneList->setModel(sortModel);
+ timeZoneList->setSelectionMode(QAbstractItemView::SingleSelection);
+ connect(timeZoneList->selectionModel(), &QItemSelectionModel::currentChanged,
+ [=]() {
+ emit timezoneChanged();
+ });
+
+ registerField("timezone*", this, "selectedTimeZone", SIGNAL(timezoneChanged()));
+
+ QVBoxLayout *timeZoneLayout = new QVBoxLayout;
+ timeZoneLayout->addWidget(timeZoneSearch);
+ timeZoneLayout->addWidget(timeZoneList);
+ QGroupBox *timeZoneGroup = new QGroupBox(tr("Time Zone"));
+ timeZoneGroup->setLayout(timeZoneLayout);
+
+ QVBoxLayout *mainLayout = new QVBoxLayout;
+ mainLayout->addStretch();
+ mainLayout->addWidget(dateTimeGroup);
+ mainLayout->addStretch();
+ mainLayout->addWidget(timeZoneGroup);
+ mainLayout->addStretch();
+ setLayout(mainLayout);
+
+ maybeRaiseCap();
+}
+
+QString DateTimePage::selectedTimeZone() {
+ QModelIndex curr = timeZoneList->selectionModel()->currentIndex();
+ return zoneModel.data(curr, Qt::ToolTipRole).toString();
+}
+
+void DateTimePage::initializePage() {
+ updateTimer->start();
+}
+
+void DateTimePage::cleanupPage() {
+ updateTimer->stop();
+}
diff --git a/ui/qt5/datetimepage.hh b/ui/qt5/datetimepage.hh
new file mode 100644
index 0000000..2c753dd
--- /dev/null
+++ b/ui/qt5/datetimepage.hh
@@ -0,0 +1,73 @@
+/*
+ * datetimepage.hh - Definition of the UI.SysMeta.DateTime page
+ * horizon-qt5, the Qt 5 user interface for
+ * Project Horizon
+ *
+ * Copyright (c) 2019 Adélie Linux and contributors. All rights reserved.
+ * This code is licensed under the AGPL 3.0 license, as noted in the
+ * LICENSE-code file in the root directory of this repository.
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+#ifndef DATETIMEPAGE_HH
+#define DATETIMEPAGE_HH
+
+#include "horizonwizardpage.hh"
+
+#include <QAbstractListModel>
+#include <QDateEdit>
+#include <QListView>
+#include <QTimeEdit>
+#include <QTimer>
+#include <QVector>
+
+/*! Represents a single time zone */
+struct TimeZone {
+ /*! The IANA name, i.e. America/Chicago */
+ QByteArray ianaName;
+ /*! The 'friendly' name, i.e. 'Central Time' */
+ QString friendlyName;
+ /*! The UTC offset in seconds */
+ int offset;
+
+ TimeZone();
+ TimeZone(QByteArray iana);
+};
+
+class TimeZoneModel : public QAbstractListModel {
+public:
+ TimeZoneModel(QWidget *parent = nullptr);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ QVariant headerData(int section, Qt::Orientation orientation,
+ int role = Qt::DisplayRole) const override;
+private:
+ QVector<TimeZone> zones;
+};
+
+class DateTimePage : public HorizonWizardPage {
+ Q_OBJECT
+ Q_PROPERTY(QString selectedTimeZone READ selectedTimeZone)
+public:
+ DateTimePage(QWidget *parent = nullptr);
+ void initializePage();
+ void cleanupPage();
+
+ TimeZoneModel zoneModel;
+ QString selectedTimeZone();
+
+signals:
+ void timezoneChanged();
+
+private:
+ QDateEdit *dateEdit;
+ QTimeEdit *timeEdit;
+ QTimer *updateTimer;
+ QListView *timeZoneList;
+
+ void maybeRaiseCap();
+};
+
+#endif /* !DATETIMEPAGE_HH */
diff --git a/ui/qt5/horizon.qrc b/ui/qt5/horizon.qrc
index 562418c..f715934 100644
--- a/ui/qt5/horizon.qrc
+++ b/ui/qt5/horizon.qrc
@@ -13,6 +13,7 @@
<file>resources/partition-help.txt</file>
<file>resources/network-start-help.txt</file>
<file>resources/network-iface-help.txt</file>
+ <file>resources/datetime-help.txt</file>
<file>resources/packages-simple-help.txt</file>
</qresource>
<qresource prefix="/">
diff --git a/ui/qt5/horizonwizard.cc b/ui/qt5/horizonwizard.cc
index 2b58e43..dda631f 100644
--- a/ui/qt5/horizonwizard.cc
+++ b/ui/qt5/horizonwizard.cc
@@ -28,6 +28,7 @@
#include "networkingpage.hh"
#include "networkifacepage.hh"
#include "netsimplewifipage.hh"
+#include "datetimepage.hh"
static std::map<int, std::string> help_id_map = {
{HorizonWizard::Page_Intro, "intro"},
@@ -42,6 +43,7 @@ static std::map<int, std::string> help_id_map = {
{HorizonWizard::Page_Network, "network-start"},
{HorizonWizard::Page_Network_Iface, "network-iface"},
{HorizonWizard::Page_Network_Wireless, "network-wifi"},
+ {HorizonWizard::Page_Network_CustomAP, "network-wifi-ap"},
{HorizonWizard::Page_Network_DHCP, "none"},
{HorizonWizard::Page_Network_Portal, "network-portal"},
{HorizonWizard::Page_Network_Manual, "network-manual"},
@@ -148,6 +150,8 @@ HorizonWizard::HorizonWizard(QWidget *parent) : QWizard(parent) {
setPage(Page_Input, new InputPage);
setPage(Page_Network, new NetworkingPage);
setPage(Page_Network_Iface, new NetworkIfacePage);
+ setPage(Page_Network_Wireless, new NetworkSimpleWirelessPage);
+ setPage(Page_DateTime, new DateTimePage);
QObject::connect(this, &QWizard::helpRequested, [=](void) {
if(help_id_map.find(currentId()) == help_id_map.end()) {
diff --git a/ui/qt5/horizonwizard.hh b/ui/qt5/horizonwizard.hh
index 3f3454b..d6f84cd 100644
--- a/ui/qt5/horizonwizard.hh
+++ b/ui/qt5/horizonwizard.hh
@@ -33,6 +33,7 @@ public:
Page_Network, /* network type selection (DHCP/static) */
Page_Network_Iface, /* network interface selection */
Page_Network_Wireless, /* wireless */
+ Page_Network_CustomAP, /* custom AP */
Page_Network_DHCP, /* interstitial for DHCP */
Page_Network_Portal, /* shown if captive portal is detected */
Page_Network_Manual, /* static addressing */
diff --git a/ui/qt5/networkifacepage.cc b/ui/qt5/networkifacepage.cc
index ce09a82..f850cfd 100644
--- a/ui/qt5/networkifacepage.cc
+++ b/ui/qt5/networkifacepage.cc
@@ -123,6 +123,6 @@ int NetworkIfacePage::nextId() const {
case HorizonWizard::Wireless:
return HorizonWizard::Page_Network_Wireless;
default:
- return HorizonWizard::Page_Network_DHCP;
+ return HorizonWizard::Page_DateTime;
}
}
diff --git a/ui/qt5/resources/datetime-help.txt b/ui/qt5/resources/datetime-help.txt
new file mode 100644
index 0000000..811b6cf
--- /dev/null
+++ b/ui/qt5/resources/datetime-help.txt
@@ -0,0 +1,17 @@
+<h2>Date and Time Settings</h2>
+
+<p>This page allows you to configure your computer's date and time. It also
+allows you to configure your computer's time zone.</p>
+
+<h3>How do I know which time zone to select?</h3>
+
+<p>If you're installing Adélie Linux on a desktop, workstation, or laptop
+computer, choose the time zone where you are located, or where you will use
+the computer the most. If you have another device with you that has Internet
+access, you may find your local time zone by using the interactive map at
+<a href="https://www.timeanddate.com/time/map/">
+https://www.timeanddate.com/time/map/</a>.</p>
+
+<p>If you're installing Adélie Linux on a server system, you should almost
+always use UTC. This ensures that Daylight Savings Time will not adversely
+affect running services.</p>