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.
 
 
 
 
 
 

503 lines
20 KiB

/*
* Copyright 2013 Sebastian Kügler <sebas@kde.org>
* Copyright 2015 Martin Klapetek <mklapetek@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.4
import QtQuick.Layouts 1.1
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.calendar 2.0 as PlasmaCalendar
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.plasma.extras 2.0 as PlasmaExtras
PlasmaComponents3.Page {
id: calendar
// The "sensible" values
property int _minimumWidth: rootLayout.childrenRect.width + (calendar.paddings * 2)
property int _minimumHeight: rootLayout.childrenRect.height + (calendar.paddings * 2) + headerArea.height
Layout.minimumWidth: _minimumWidth
Layout.minimumHeight: _minimumHeight
Layout.preferredWidth: _minimumWidth
Layout.preferredHeight: _minimumHeight
Layout.maximumWidth: _minimumWidth
Layout.maximumHeight: _minimumHeight
readonly property int paddings: units.smallSpacing
readonly property bool showAgenda: PlasmaCalendar.EventPluginsManager.enabledPlugins.length > 0
readonly property bool showClocks: plasmoid.configuration.selectedTimeZones.length > 1
property alias borderWidth: monthView.borderWidth
property alias monthView: monthView
property bool debug: false
property bool isExpanded: plasmoid.expanded
onIsExpandedChanged: {
// clear all the selections when the plasmoid is showing/hiding
monthView.resetToToday();
}
// Header containing date and pin button
header: PlasmaExtras.PlasmoidHeading {
id: headerArea
RowLayout {
width: parent.width
PlasmaExtras.Heading {
Layout.fillWidth: true
Layout.leftMargin: calendar.paddings // Match calendar title
level: 1
text: monthView.currentDate.toLocaleDateString(Qt.locale(), Locale.LongFormat)
}
PlasmaComponents3.ToolButton {
visible: plasmoid.action("configure").enabled
icon.name: "configure"
onClicked: plasmoid.action("configure").trigger()
PlasmaComponents3.ToolTip {
text: plasmoid.action("configure").text
}
}
// Allows the user to keep the calendar open for reference
PlasmaComponents3.ToolButton {
checkable: true
checked: plasmoid.configuration.pin
onToggled: plasmoid.configuration.pin = checked
icon.name: "window-pin"
PlasmaComponents3.ToolTip {
text: i18n("Keep Open")
}
}
}
}
// Top-level layout containing:
// - Left column with current date header, calendar, and agenda view
// - Right column with world clocks
RowLayout {
id: rootLayout
anchors {
top: parent.top
left: parent.left
margins: calendar.paddings
}
spacing: calendar.paddings
// Left column containing calendar
// ===============================
// TODO KF6: remove the `Item` wrapper, which this is only needed since
// PlasmaCalendar.MonthView internally has `anchors.fill:parent` set on
// it, erroneously expecting to never be in a Layout
Item {
Layout.fillWidth: true
Layout.minimumHeight: units.gridUnit * 22
Layout.minimumWidth: units.gridUnit * 22
PlasmaCalendar.MonthView {
id: monthView
borderOpacity: 0.25
today: root.tzDate
showWeekNumbers: plasmoid.configuration.showWeekNumbers
}
}
// Vertical separator line between columns
// =======================================
PlasmaCore.SvgItem {
visible: rightColumn.visible
Layout.preferredWidth: naturalSize.width
Layout.fillHeight: true
// Unify margins because the calendar includes its own
Layout.topMargin: calendar.paddings
Layout.rightMargin: calendar.paddings
elementId: "vertical-line"
svg: PlasmaCore.Svg {
imagePath: "widgets/line"
}
}
// Right column containing agenda view and time zones
// ==================================================
ColumnLayout {
id: rightColumn
visible: agenda.visible || worldClocks.visible
Layout.minimumWidth: units.gridUnit * 14
// Agenda view stuff
// -----------------
// Header text
PlasmaExtras.Heading {
visible: agenda.visible
Layout.fillWidth: true
level: 2
text: i18n("Events")
maximumLineCount: 1
elide: Text.ElideRight
}
// Agenda view itself
Item {
id: agenda
visible: calendar.showAgenda
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: units.gridUnit * 4
Layout.leftMargin: -units.smallSpacing
function formatDateWithoutYear(date) {
// Unfortunatelly Qt overrides ECMA's Date.toLocaleDateString(),
// which is able to return locale-specific date-and-month-only date
// formats, with its dumb version that only supports Qt::DateFormat
// enum subset. So to get a day-and-month-only date format string we
// must resort to this magic and hope there are no locales that use
// other separators...
var format = Qt.locale().dateFormat(Locale.ShortFormat).replace(/[./ ]*Y{2,4}[./ ]*/i, '');
return Qt.formatDate(date, format);
}
function dateEquals(date1, date2) {
var values1 = [
date1.getFullYear(),
date1.getMonth(),
date1.getDate()
];
var values2 = [
date2.getFullYear(),
date2.getMonth(),
date2.getDate()
];
return values1.every((value, index) => {
return (value === values2[index]);
}, false)
}
Connections {
target: monthView
onCurrentDateChanged: {
// Apparently this is needed because this is a simple QList being
// returned and if the list for the current day has 1 event and the
// user clicks some other date which also has 1 event, QML sees the
// sizes match and does not update the labels with the content.
// Resetting the model to null first clears it and then correct data
// are displayed.
holidaysList.model = null;
holidaysList.model = monthView.daysModel.eventsForDate(monthView.currentDate);
}
}
Connections {
target: monthView.daysModel
onAgendaUpdated: {
if (agenda.dateEquals(updatedDate, monthView.currentDate)) {
holidaysList.model = null;
holidaysList.model = monthView.daysModel.eventsForDate(monthView.currentDate);
}
}
}
Connections {
target: plasmoid.configuration
onEnabledCalendarPluginsChanged: {
PlasmaCalendar.EventPluginsManager.enabledPlugins = plasmoid.configuration.enabledCalendarPlugins;
}
}
Binding {
target: plasmoid
property: "hideOnWindowDeactivate"
value: !plasmoid.configuration.pin
}
TextMetrics {
id: dateLabelMetrics
// Date/time are arbitrary values with all parts being two-digit
readonly property string timeString: Qt.formatTime(new Date(2000, 12, 12, 12, 12, 12, 12))
readonly property string dateString: agenda.formatDateWithoutYear(new Date(2000, 12, 12, 12, 12, 12))
font: theme.defaultFont
text: timeString.length > dateString.length ? timeString : dateString
}
PlasmaExtras.ScrollArea {
id: holidaysView
anchors.fill: parent
ListView {
id: holidaysList
delegate: PlasmaExtras.ListItem {
id: eventItem
implicitHeight: eventGrid.height + PlasmaCore.Units.smallSpacing * 2
property bool hasTime: {
// Explicitly all-day event
if (modelData.isAllDay) {
return false;
}
// Multi-day event which does not start or end today (so
// is all-day from today's point of view)
if (modelData.startDateTime - monthView.currentDate < 0 &&
modelData.endDateTime - monthView.currentDate > 86400000) { // 24hrs in ms
return false;
}
// Non-explicit all-day event
var startIsMidnight = modelData.startDateTime.getHours() === 0
&& modelData.startDateTime.getMinutes() === 0;
var endIsMidnight = modelData.endDateTime.getHours() === 0
&& modelData.endDateTime.getMinutes() === 0;
var sameDay = modelData.startDateTime.getDate() === modelData.endDateTime.getDate()
&& modelData.startDateTime.getDay() === modelData.endDateTime.getDay()
if (startIsMidnight && endIsMidnight && sameDay) {
return false
}
return true;
}
PlasmaCore.ToolTipArea {
width: parent.width
height: eventGrid.height
active: eventTitle.truncated || eventDescription.truncated
mainText: active ? eventTitle.text : ""
subText: active ? eventDescription.text : ""
GridLayout {
id: eventGrid
columns: 3
rows: 2
rowSpacing: 0
columnSpacing: 2 * units.smallSpacing
width: parent.width
Rectangle {
id: eventColor
Layout.row: 0
Layout.column: 0
Layout.rowSpan: 2
Layout.fillHeight: true
color: modelData.eventColor
width: 5 * units.devicePixelRatio
visible: modelData.eventColor !== ""
}
PlasmaComponents3.Label {
id: startTimeLabel
readonly property bool startsToday: modelData.startDateTime - monthView.currentDate >= 0
readonly property bool startedYesterdayLessThan12HoursAgo: modelData.startDateTime - monthView.currentDate >= -43200000 //12hrs in ms
Layout.row: 0
Layout.column: 1
Layout.minimumWidth: dateLabelMetrics.width
text: startsToday || startedYesterdayLessThan12HoursAgo
? Qt.formatTime(modelData.startDateTime)
: agenda.formatDateWithoutYear(modelData.startDateTime)
horizontalAlignment: Qt.AlignRight
visible: eventItem.hasTime
}
PlasmaComponents3.Label {
id: endTimeLabel
readonly property bool endsToday: modelData.endDateTime - monthView.currentDate <= 86400000 // 24hrs in ms
readonly property bool endsTomorrowInLessThan12Hours: modelData.endDateTime - monthView.currentDate <= 86400000 + 43200000 // 36hrs in ms
Layout.row: 1
Layout.column: 1
Layout.minimumWidth: dateLabelMetrics.width
text: endsToday || endsTomorrowInLessThan12Hours
? Qt.formatTime(modelData.endDateTime)
: agenda.formatDateWithoutYear(modelData.endDateTime)
horizontalAlignment: Qt.AlignRight
opacity: 0.7
visible: eventItem.hasTime
}
PlasmaComponents3.Label {
id: eventTitle
readonly property bool wrap: eventDescription.text === ""
Layout.row: 0
Layout.rowSpan: wrap ? 2 : 1
Layout.column: 2
Layout.fillWidth: true
elide: Text.ElideRight
text: modelData.title
verticalAlignment: Text.AlignVCenter
maximumLineCount: 2
wrapMode: wrap ? Text.Wrap : Text.NoWrap
}
PlasmaComponents3.Label {
id: eventDescription
opacity: 0.7
Layout.row: 1
Layout.column: 2
Layout.fillWidth: true
elide: Text.ElideRight
text: modelData.description
verticalAlignment: Text.AlignVCenter
enabled: false
visible: text !== ""
}
}
}
}
}
}
PlasmaExtras.Heading {
anchors.fill: holidaysView
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
anchors.leftMargin: units.largeSpacing
anchors.rightMargin: units.largeSpacing
text: monthView.isToday(monthView.currentDate) ? i18n("No events for today")
: i18n("No events for this day");
level: 3
enabled: false
visible: holidaysList.count == 0
}
}
// Horizontal separator line between events and time zones
PlasmaCore.SvgItem {
visible: worldClocks.visible && agenda.visible
Layout.fillWidth: true
Layout.preferredHeight: naturalSize.height
elementId: "horizontal-line"
svg: PlasmaCore.Svg {
imagePath: "widgets/line"
}
}
// Clocks stuff
// ------------
// Header text
PlasmaExtras.Heading {
visible: worldClocks.visible
Layout.fillWidth: true
level: 2
text: i18n("Time Zones")
maximumLineCount: 1
elide: Text.ElideRight
}
// Clocks view itself
PlasmaExtras.ScrollArea {
id: worldClocks
visible: calendar.showClocks
Layout.fillWidth: true
Layout.fillHeight: !agenda.visible
Layout.leftMargin: -units.smallSpacing
ListView {
id: clocksList
width: parent.width
model: {
var timezones = [];
for (var i = 0; i < plasmoid.configuration.selectedTimeZones.length; i++) {
timezones.push(plasmoid.configuration.selectedTimeZones[i]);
}
return timezones;
}
delegate: PlasmaExtras.ListItem {
id: listItem
readonly property bool isCurrentTimeZone: modelData === plasmoid.configuration.lastSelectedTimezone
separatorVisible: false
width: clocksList.width
height: units.gridUnit + units.smallSpacing
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: plasmoid.configuration.lastSelectedTimezone = modelData
}
RowLayout {
anchors.fill: parent
PlasmaComponents3.Label {
text: root.nameForZone(modelData)
font.weight: listItem.isCurrentTimeZone ? Font.Bold : Font.Normal
maximumLineCount: 1
elide: Text.ElideRight
}
PlasmaComponents3.Label {
Layout.fillWidth: true
horizontalAlignment: Qt.AlignRight
text: root.timeForZone(modelData)
font.weight: listItem.isCurrentTimeZone ? Font.Bold : Font.Normal
elide: Text.ElideRight
maximumLineCount: 1
}
}
}
}
}
}
}
}