You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
626 lines
22 KiB
626 lines
22 KiB
/* |
|
* Copyright 2013 Heena Mahour <heena393@gmail.com> |
|
* Copyright 2013 Sebastian Kügler <sebas@kde.org> |
|
* Copyright 2013 Martin Klapetek <mklapetek@kde.org> |
|
* Copyright 2014 David Edmundson <davidedmundson@kde.org> |
|
* |
|
* This program is free software; you can redistribute it and/or |
|
* modify it under the terms of the GNU General Public License as |
|
* published by the Free Software Foundation; either version 2 of |
|
* the License, or (at your option) any later version. |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
*/ |
|
|
|
import QtQuick 2.2 |
|
import QtQuick.Layouts 1.1 |
|
import org.kde.plasma.core 2.0 as PlasmaCore |
|
import org.kde.plasma.components 2.0 as Components |
|
import org.kde.plasma.private.digitalclock 1.0 |
|
|
|
Item { |
|
id: main |
|
|
|
property string timeFormat |
|
property date currentTime |
|
|
|
property bool showSeconds: plasmoid.configuration.showSeconds |
|
property bool showLocalTimezone: plasmoid.configuration.showLocalTimezone |
|
property bool showDate: plasmoid.configuration.showDate |
|
property int dateFormat: { |
|
if (plasmoid.configuration.dateFormat === "longDate") { |
|
return Qt.SystemLocaleLongDate; |
|
} else if (plasmoid.configuration.dateFormat === "isoDate") { |
|
return Qt.ISODate; |
|
} |
|
|
|
return Qt.SystemLocaleShortDate; |
|
} |
|
|
|
property string lastSelectedTimezone: plasmoid.configuration.lastSelectedTimezone |
|
property bool displayTimezoneAsCode: plasmoid.configuration.displayTimezoneAsCode |
|
property int use24hFormat: plasmoid.configuration.use24hFormat |
|
|
|
property string lastDate: "" |
|
property int tzOffset |
|
|
|
// This is the index in the list of user selected timezones |
|
property int tzIndex: 0 |
|
|
|
// if the date/timezone cannot be fit with the smallest font to its designated space |
|
readonly property bool tooSmall: plasmoid.formFactor == PlasmaCore.Types.Horizontal && Math.round(2 * (main.height / 5)) <= theme.smallestFont.pixelSize |
|
|
|
onDateFormatChanged: { |
|
setupLabels(); |
|
updateToolTip(); |
|
} |
|
|
|
onDisplayTimezoneAsCodeChanged: { setupLabels(); } |
|
onStateChanged: { setupLabels(); } |
|
|
|
onLastSelectedTimezoneChanged: { timeFormatCorrection(Qt.locale().timeFormat(Locale.ShortFormat)) } |
|
onShowSecondsChanged: { timeFormatCorrection(Qt.locale().timeFormat(Locale.ShortFormat)) } |
|
onShowLocalTimezoneChanged: { timeFormatCorrection(Qt.locale().timeFormat(Locale.ShortFormat)) } |
|
onShowDateChanged: { timeFormatCorrection(Qt.locale().timeFormat(Locale.ShortFormat)) } |
|
onUse24hFormatChanged: { timeFormatCorrection(Qt.locale().timeFormat(Locale.ShortFormat)) } |
|
|
|
states: [ |
|
State { |
|
name: "horizontalPanel" |
|
when: plasmoid.formFactor == PlasmaCore.Types.Horizontal && !main.tooSmall |
|
|
|
PropertyChanges { |
|
target: main |
|
Layout.fillHeight: true |
|
Layout.fillWidth: false |
|
Layout.minimumWidth: Math.max(labelsFlow.width, dateLabel.paintedWidth) |
|
Layout.maximumWidth: Layout.minimumWidth |
|
|
|
} |
|
|
|
PropertyChanges { |
|
target: timeLabel |
|
|
|
height: sizehelper.height |
|
width: timeLabel.paintedWidth |
|
|
|
wrapMode: Text.NoWrap |
|
fontSizeMode: Text.VerticalFit |
|
} |
|
|
|
PropertyChanges { |
|
target: timezoneLabel |
|
|
|
height: main.showDate ? timeLabel.height : Math.round(2 * (timeLabel.height / 3)) |
|
width: main.showDate ? timezoneLabel.paintedWidth : timeLabel.width |
|
|
|
fontSizeMode: Text.VerticalFit |
|
minimumPointSize: 1 |
|
font.pointSize: 1024 |
|
elide: Text.ElideNone |
|
horizontalAlignment: Text.AlignHCenter |
|
} |
|
|
|
PropertyChanges { |
|
target: dateLabel |
|
|
|
height: Math.round(2 * (main.height / 5)) |
|
width: dateLabel.paintedWidth |
|
|
|
anchors.horizontalCenter: main.horizontalCenter |
|
} |
|
|
|
PropertyChanges { |
|
target: labelsFlow |
|
|
|
flow: main.showDate ? Flow.LeftToRight : Flow.TopToBottom |
|
} |
|
|
|
PropertyChanges { |
|
target: sizehelper |
|
|
|
height: main.showDate || timezoneLabel.visible ? Math.round(3 * (main.height / 5)) : main.height |
|
width: sizehelper.paintedWidth |
|
|
|
fontSizeMode: Text.VerticalFit |
|
} |
|
}, |
|
|
|
State { |
|
name: "horizontalPanelSmall" |
|
when: plasmoid.formFactor == PlasmaCore.Types.Horizontal && main.tooSmall |
|
|
|
PropertyChanges { |
|
target: main |
|
Layout.fillHeight: true |
|
Layout.fillWidth: false |
|
Layout.minimumWidth: labelsFlow.width |
|
Layout.maximumWidth: Layout.minimumWidth |
|
|
|
} |
|
|
|
PropertyChanges { |
|
target: timeLabel |
|
|
|
height: sizehelper.height |
|
width: timeLabel.paintedWidth |
|
|
|
wrapMode: Text.NoWrap |
|
fontSizeMode: Text.VerticalFit |
|
font.pointSize: theme.defaultFont.pointSize |
|
} |
|
|
|
PropertyChanges { |
|
target: timezoneLabel |
|
|
|
height: sizehelper.height |
|
width: timezoneLabel.paintedWidth |
|
|
|
fontSizeMode: Text.VerticalFit |
|
minimumPointSize: 1 |
|
font.pointSize: theme.defaultFont.pointSize |
|
elide: Text.ElideNone |
|
horizontalAlignment: Text.AlignHCenter |
|
} |
|
|
|
PropertyChanges { |
|
target: labelsFlow |
|
|
|
flow: Flow.LeftToRight |
|
} |
|
|
|
PropertyChanges { |
|
target: sizehelper |
|
|
|
height: main.height |
|
width: sizehelper.paintedWidth |
|
|
|
fontSizeMode: Text.VerticalFit |
|
} |
|
}, |
|
|
|
State { |
|
name: "verticalPanel" |
|
when: plasmoid.formFactor == PlasmaCore.Types.Vertical |
|
|
|
PropertyChanges { |
|
target: main |
|
Layout.fillHeight: false |
|
Layout.fillWidth: true |
|
Layout.maximumHeight: main.showDate ? labelsFlow.height + dateLabel.height : labelsFlow.height |
|
Layout.minimumHeight: Layout.maximumHeight |
|
} |
|
|
|
PropertyChanges { |
|
target: timeLabel |
|
|
|
height: sizehelper.paintedHeight |
|
width: main.width |
|
|
|
fontSizeMode: Text.VerticalFit |
|
wrapMode: Text.WordWrap |
|
} |
|
|
|
PropertyChanges { |
|
target: timezoneLabel |
|
|
|
height: Math.max(sizehelper.lineCount > 1 ? 2 * Math.round(timeLabel.height / 6) : 2 * Math.round(timeLabel.height / 3), theme.smallestFont.pixelSize) |
|
width: main.width |
|
|
|
fontSizeMode: Text.VerticalFit |
|
minimumPixelSize: theme.smallestFont.pixelSize |
|
elide: Text.ElideRight |
|
} |
|
|
|
PropertyChanges { |
|
target: dateLabel |
|
|
|
height: timezoneLabel.height |
|
width: timezoneLabel.width |
|
|
|
minimumPixelSize: theme.smallestFont.pixelSize |
|
elide: Text.ElideRight |
|
} |
|
|
|
PropertyChanges { |
|
target: sizehelper |
|
|
|
height: sizehelper.paintedHeight |
|
width: main.width |
|
|
|
fontSizeMode: Text.HorizontalFit |
|
horizontalAlignment: Text.AlignHCenter |
|
wrapMode: Text.WordWrap |
|
} |
|
}, |
|
|
|
State { |
|
name: "other" |
|
when: plasmoid.formFactor != PlasmaCore.Types.Vertical && plasmoid.formFactor != PlasmaCore.Types.Horizontal |
|
|
|
PropertyChanges { |
|
target: main |
|
Layout.fillHeight: false |
|
Layout.fillWidth: false |
|
Layout.minimumWidth: units.gridUnit * 3 |
|
Layout.maximumWidth: Math.max(labelsFlow.width, dateLabel.width) |
|
Layout.minimumHeight: units.gridUnit * 3 |
|
Layout.maximumHeight: main.showDate ? labelsFlow.height + dateLabel.height : labelsFlow.height |
|
} |
|
|
|
PropertyChanges { |
|
target: timeLabel |
|
|
|
height: sizehelper.height |
|
width: main.width |
|
|
|
wrapMode: Text.NoWrap |
|
fontSizeMode: Text.Fit |
|
} |
|
|
|
PropertyChanges { |
|
target: timezoneLabel |
|
|
|
height: dateLabel.visible ? Math.round(1 * (main.height / 5)) : Math.round(2 * (main.height / 5)) |
|
width: main.width |
|
|
|
fontSizeMode: Text.Fit |
|
minimumPixelSize: 0 |
|
elide: Text.ElideRight |
|
} |
|
|
|
PropertyChanges { |
|
target: dateLabel |
|
|
|
height: timezoneLabel.visible ? Math.round(1 * (main.height / 5)) : Math.round(2 * (main.height / 5)) |
|
width: main.width |
|
|
|
anchors.horizontalCenter: main.horizontalCenter |
|
fontSizeMode: Text.Fit |
|
} |
|
|
|
PropertyChanges { |
|
target: sizehelper |
|
|
|
height: main.showDate || timezoneLabel.visible ? Math.round(3 * (main.height / 5)) : main.height |
|
width: main.width |
|
|
|
fontSizeMode: Text.Fit |
|
} |
|
} |
|
|
|
] |
|
|
|
MouseArea { |
|
id: mouseArea |
|
|
|
property int wheelDelta: 0 |
|
|
|
anchors.fill: parent |
|
|
|
onClicked: plasmoid.expanded = !plasmoid.expanded |
|
onWheel: { |
|
if (!plasmoid.configuration.wheelChangesTimezone) { |
|
return; |
|
} |
|
|
|
var delta = wheel.angleDelta.y || wheel.angleDelta.x |
|
var newIndex = main.tzIndex; |
|
wheelDelta += delta; |
|
// magic number 120 for common "one click" |
|
// See: http://qt-project.org/doc/qt-5/qml-qtquick-wheelevent.html#angleDelta-prop |
|
while (wheelDelta >= 120) { |
|
wheelDelta -= 120; |
|
newIndex++; |
|
} |
|
while (wheelDelta <= -120) { |
|
wheelDelta += 120; |
|
newIndex--; |
|
} |
|
|
|
if (newIndex >= plasmoid.configuration.selectedTimeZones.length) { |
|
newIndex = 0; |
|
} else if (newIndex < 0) { |
|
newIndex = plasmoid.configuration.selectedTimeZones.length - 1; |
|
} |
|
|
|
if (newIndex != main.tzIndex) { |
|
plasmoid.configuration.lastSelectedTimezone = plasmoid.configuration.selectedTimeZones[newIndex]; |
|
main.tzIndex = newIndex; |
|
|
|
dataSource.dataChanged(); |
|
setupLabels(); |
|
} |
|
} |
|
} |
|
|
|
Flow { |
|
id: labelsFlow |
|
|
|
anchors.horizontalCenter: main.horizontalCenter |
|
|
|
flow: Flow.TopToBottom |
|
spacing: flow == Flow.LeftToRight && (timezoneLabel.visible || main.tooSmall) ? units.smallSpacing : 0 |
|
|
|
Components.Label { |
|
id: dateLabelLeft |
|
|
|
height: sizehelper.height |
|
visible: main.showDate && main.tooSmall |
|
|
|
font { |
|
weight: timeLabel.font.weight |
|
italic: timeLabel.font.italic |
|
pointSize: theme.defaultFont.pointSize |
|
} |
|
minimumPixelSize: 0 |
|
|
|
fontSizeMode: Text.VerticalFit |
|
|
|
horizontalAlignment: Text.AlignHCenter |
|
verticalAlignment: Text.AlignVCenter |
|
} |
|
|
|
Item { |
|
height: dateLabelLeft.height |
|
width: 1 |
|
visible: main.showDate && main.tooSmall |
|
|
|
Rectangle { |
|
id: delimiter |
|
|
|
height: dateLabelLeft.font.pixelSize |
|
width: 1 |
|
anchors.verticalCenter: parent.verticalCenter |
|
|
|
color: theme.textColor |
|
opacity: 0.4 |
|
} |
|
} |
|
|
|
Components.Label { |
|
id: timeLabel |
|
|
|
font { |
|
family: plasmoid.configuration.fontFamily || theme.defaultFont.family |
|
weight: plasmoid.configuration.boldText ? Font.Bold : Font.Normal |
|
italic: plasmoid.configuration.italicText |
|
pixelSize: 1024 |
|
pointSize: 1024 |
|
} |
|
|
|
text: { |
|
// get the time for the given timezone from the dataengine |
|
var now = dataSource.data[plasmoid.configuration.lastSelectedTimezone]["DateTime"]; |
|
// get current UTC time |
|
var msUTC = now.getTime() + (now.getTimezoneOffset() * 60000); |
|
// add the dataengine TZ offset to it |
|
var currentTime = new Date(msUTC + (dataSource.data[plasmoid.configuration.lastSelectedTimezone]["Offset"] * 1000)); |
|
|
|
main.currentTime = currentTime; |
|
return Qt.formatTime(currentTime, main.timeFormat); |
|
} |
|
|
|
verticalAlignment: Text.AlignVCenter |
|
horizontalAlignment: Text.AlignHCenter |
|
} |
|
|
|
Components.Label { |
|
id: timezoneLabel |
|
|
|
font.weight: timeLabel.font.weight |
|
font.italic: timeLabel.font.italic |
|
font.pixelSize: 1024 |
|
font.pointSize: 1024 |
|
minimumPixelSize: 0 |
|
|
|
visible: text.length > 0 |
|
horizontalAlignment: Text.AlignHCenter |
|
} |
|
} |
|
|
|
Components.Label { |
|
id: dateLabel |
|
|
|
anchors.top: labelsFlow.bottom |
|
visible: main.showDate && !main.tooSmall |
|
|
|
font.family: timeLabel.font.family |
|
font.weight: timeLabel.font.weight |
|
font.italic: timeLabel.font.italic |
|
font.pixelSize: 1024 |
|
font.pointSize: 1024 |
|
minimumPixelSize: 0 |
|
|
|
fontSizeMode: Text.VerticalFit |
|
|
|
horizontalAlignment: Text.AlignHCenter |
|
verticalAlignment: Text.AlignVCenter |
|
} |
|
|
|
Components.Label { |
|
id: sizehelper |
|
|
|
font.weight: timeLabel.font.weight |
|
font.italic: timeLabel.font.italic |
|
font.pixelSize: 1024 |
|
font.pointSize: 1024 |
|
|
|
verticalAlignment: Text.AlignVCenter |
|
|
|
visible: false |
|
} |
|
|
|
// Qt's QLocale does not offer any modular time creating like Klocale did |
|
// eg. no "gimme time with seconds" or "gimme time without seconds and with timezone". |
|
// QLocale supports only two formats - Long and Short. Long is unusable in many situations |
|
// and Short does not provide seconds. So if seconds are enabled, we need to add it here. |
|
// |
|
// What happens here is that it looks for the delimiter between "h" and "m", takes it |
|
// and appends it after "mm" and then appends "ss" for the seconds. Also it checks |
|
// if the format string already does not contain the seconds part. |
|
// |
|
// It can happen that Qt uses the 'C' locale (it's a fallback) and that locale |
|
// has always ":ss" part in ShortFormat, so we need to remove it. |
|
function timeFormatCorrection(timeFormatString) { |
|
var regexp = /(hh*)(.+)(mm)/i |
|
var match = regexp.exec(timeFormatString); |
|
|
|
var hours = match[1]; |
|
var delimiter = match[2]; |
|
var minutes = match[3] |
|
var seconds = "ss"; |
|
var amPm = "AP"; |
|
var uses24hFormatByDefault = timeFormatString.toLowerCase().indexOf("ap") == -1; |
|
|
|
// because QLocale is incredibly stupid and does not convert 12h/24h clock format |
|
// when uppercase H is used for hours, needs to be h or hh, so toLowerCase() |
|
var result = hours.toLowerCase() + delimiter + minutes; |
|
|
|
if (main.showSeconds && timeFormatString.indexOf('s') == -1) { |
|
result += delimiter + seconds; |
|
} |
|
|
|
// add "AM/PM" either if the setting is the default and locale uses it OR if the user unchecked "use 24h format" |
|
if ((main.use24hFormat == Qt.PartiallyChecked && !uses24hFormatByDefault) || main.use24hFormat == Qt.Unchecked) { |
|
result += " " + amPm; |
|
} |
|
|
|
main.timeFormat = result; |
|
setupLabels(); |
|
} |
|
|
|
function setupLabels() { |
|
var st = Qt.formatTime(new Date(2000, 0, 1, 22, 0, 0), main.timeFormat); |
|
var showTimezone = main.showLocalTimezone || (plasmoid.configuration.lastSelectedTimezone != "Local" |
|
&& dataSource.data["Local"]["Timezone City"] != dataSource.data[plasmoid.configuration.lastSelectedTimezone]["Timezone City"]); |
|
|
|
var timezoneString = ""; |
|
|
|
if (showTimezone) { |
|
timezoneString = plasmoid.configuration.displayTimezoneAsCode ? dataSource.data[plasmoid.configuration.lastSelectedTimezone]["Timezone Abbreviation"] |
|
: TimezonesI18n.i18nCity(dataSource.data[plasmoid.configuration.lastSelectedTimezone]["Timezone City"]); |
|
timezoneLabel.text = (main.showDate || main.tooSmall) && plasmoid.formFactor == PlasmaCore.Types.Horizontal ? "(" + timezoneString + ")" : timezoneString; |
|
} else { |
|
// this clears the label and that makes it hidden |
|
timezoneLabel.text = timezoneString; |
|
} |
|
|
|
|
|
if (main.showDate) { |
|
if (main.tooSmall) { |
|
dateLabelLeft.text = Qt.formatDate(main.currentTime, main.dateFormat); |
|
} else { |
|
dateLabel.text = Qt.formatDate(main.currentTime, main.dateFormat); |
|
} |
|
} else { |
|
// clear it so it doesn't take space in the layout |
|
dateLabel.text = ""; |
|
dateLabelLeft.text = ""; |
|
} |
|
|
|
if (sizehelper.text != st) { |
|
sizehelper.text = st; |
|
} |
|
} |
|
|
|
function updateToolTip() { |
|
var timezoneString = Qt.formatDate(root.tzDate, plasmoid.configuration.dateFormat === "isoDate" ? Qt.ISODate : root.dateFormatString); |
|
if (plasmoid.configuration.selectedTimeZones.length > 1) { |
|
timezoneString += "<br />"; |
|
for (var i = 0; i < plasmoid.configuration.selectedTimeZones.length; ++i) { |
|
timezoneString += "<br />" + timeForZone(i, false); |
|
} |
|
} |
|
plasmoid.toolTipSubText = timezoneString; |
|
} |
|
|
|
function timeForZone(zone, addlinebreaks) { |
|
var returnString = ""; |
|
|
|
// get the time for the given timezone from the dataengine |
|
var now = dataSource.data[plasmoid.configuration.selectedTimeZones[zone]]["DateTime"]; |
|
// get current UTC time |
|
var msUTC = now.getTime() + (now.getTimezoneOffset() * 60000); |
|
// add the dataengine TZ offset to it |
|
var dateTime = new Date(msUTC + (dataSource.data[plasmoid.configuration.selectedTimeZones[zone]]["Offset"] * 1000)); |
|
|
|
// add the timezone string to the clock |
|
if (plasmoid.configuration.selectedTimeZones[zone] != "Local" || main.showLocalTimezone) { |
|
var timezoneString = plasmoid.configuration.displayTimezoneAsCode ? dataSource.data[plasmoid.configuration.selectedTimeZones[zone]]["Timezone Abbreviation"] |
|
: TimezonesI18n.i18nCity(dataSource.data[plasmoid.configuration.selectedTimeZones[zone]]["Timezone City"]); |
|
|
|
if (addlinebreaks) { |
|
returnString += (showDate ? i18nc("This composes time and a timezone into one string that's displayed in the clock applet (the main clock in the panel). " |
|
+ "%1 is the current time and %2 is either the timezone city or timezone code, depending on user settings", |
|
"%1 (%2)", Qt.formatTime(dateTime, main.timeFormat), timezoneString) |
|
: i18nc("This composes time and a timezone into one string that's displayed in the clock applet (the main clock in the panel). " |
|
+ "From the previous case it's different that it puts the timezone name into separate new line without using ()s", |
|
"%1<br/>%2", Qt.formatTime(dateTime, main.timeFormat), timezoneString)); |
|
} else { |
|
// Without line breaks, and put the timezone first |
|
returnString += i18nc("This composes time and a timezone into one string that's displayed in the tooltip of the clock. " |
|
+ "%1 is the timezone city or timezone code depending on user settings and %2 is the current time", |
|
"%1 %2", timezoneString, Qt.formatTime(dateTime, main.timeFormat)); |
|
} |
|
} else { |
|
// return only the time |
|
returnString += Qt.formatTime(dateTime, main.timeFormat); |
|
} |
|
|
|
if (showDate) { |
|
returnString += (addlinebreaks ? "<br/>" + Qt.formatDate(dateTime, main.dateFormat) |
|
: " " + Qt.formatDate(dateTime, main.dateFormat)); |
|
} |
|
|
|
return returnString; |
|
} |
|
|
|
function dateTimeChanged() |
|
{ |
|
var doCorrections = false; |
|
|
|
if (main.showDate) { |
|
// If the date has changed, force size recalculation, because the day name |
|
// or the month name can now be longer/shorter, so we need to adjust applet size |
|
var currentDate = Qt.formatDateTime(dataSource.data["Local"]["DateTime"], "yyyy-mm-dd"); |
|
if (main.lastDate != currentDate) { |
|
doCorrections = true; |
|
main.lastDate = currentDate |
|
} |
|
} |
|
|
|
var currentTZOffset = dataSource.data["Local"]["Offset"] / 60; |
|
if (currentTZOffset != tzOffset) { |
|
doCorrections = true; |
|
tzOffset = currentTZOffset; |
|
Date.timeZoneUpdated(); // inform the QML JS engine about TZ change |
|
} |
|
|
|
if (doCorrections) { |
|
timeFormatCorrection(Qt.locale().timeFormat(Locale.ShortFormat)); |
|
} |
|
} |
|
|
|
Component.onCompleted: { |
|
for (var i = 0; i < plasmoid.configuration.selectedTimeZones.length; i++) { |
|
if (plasmoid.configuration.selectedTimeZones[i] == plasmoid.configuration.lastSelectedTimezone) { |
|
main.tzIndex = i; |
|
break; |
|
} |
|
} |
|
|
|
tzOffset = -(new Date().getTimezoneOffset()); |
|
dateTimeChanged(); |
|
timeFormatCorrection(Qt.locale().timeFormat(Locale.ShortFormat)); |
|
updateToolTip(); |
|
dataSource.onDataChanged.connect(dateTimeChanged); |
|
dataSource.onDataChanged.connect(updateToolTip); |
|
} |
|
}
|
|
|