diff --git a/framework/src/domain/eventmodel.cpp b/framework/src/domain/eventmodel.cpp index cc18877c..ade12b0e 100644 --- a/framework/src/domain/eventmodel.cpp +++ b/framework/src/domain/eventmodel.cpp @@ -1,241 +1,258 @@ /* Copyright (c) 2018 Michael Bohlender Copyright (c) 2018 Christian Mollekopf Copyright (c) 2018 Rémi Nicole This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "eventmodel.h" #include #include #include #include #include #include #include #include #include #include using Event = Sink::ApplicationDomain::Event; using Calendar = Sink::ApplicationDomain::Calendar; EventModel::EventModel(QObject *parent) : QAbstractItemModel(parent), mCalendarCache{EntityCache::Ptr::create()}, mCalendar{new KCalCore::MemoryCalendar{QTimeZone::systemTimeZone()}} { mRefreshTimer.setSingleShot(true); QObject::connect(&mRefreshTimer, &QTimer::timeout, this, &EventModel::updateFromSource); } void EventModel::setStart(const QDate &start) { mStart = start; updateQuery(); } QDate EventModel::start() const { return mStart; } void EventModel::setLength(int length) { mLength = length; updateQuery(); } int EventModel::length() const { return mLength; } void EventModel::setCalendarFilter(const QSet &calendarFilter) { mCalendarFilter = calendarFilter; updateQuery(); } +void EventModel::setFilter(const QVariantMap &filter) +{ + mFilter = filter; + updateQuery(); +} + void EventModel::updateQuery() { if (mCalendarFilter.isEmpty() || !mLength || !mStart.isValid()) { if (rowCount()) { refreshView(); } return; } mEnd = mStart.addDays(mLength); Sink::Query query; query.setFlags(Sink::Query::LiveQuery); query.request(); query.request(); query.request(); query.request(); query.request(); query.request(); query.request(); query.filter(Sink::Query::Comparator(QVariantList{mStart, mEnd}, Sink::Query::Comparator::Overlap)); mSourceModel = Sink::Store::loadModel(query); QObject::connect(mSourceModel.data(), &QAbstractItemModel::dataChanged, this, &EventModel::refreshView); QObject::connect(mSourceModel.data(), &QAbstractItemModel::layoutChanged, this, &EventModel::refreshView); QObject::connect(mSourceModel.data(), &QAbstractItemModel::modelReset, this, &EventModel::refreshView); QObject::connect(mSourceModel.data(), &QAbstractItemModel::rowsInserted, this, &EventModel::refreshView); QObject::connect(mSourceModel.data(), &QAbstractItemModel::rowsMoved, this, &EventModel::refreshView); QObject::connect(mSourceModel.data(), &QAbstractItemModel::rowsRemoved, this, &EventModel::refreshView); refreshView(); } void EventModel::refreshView() { if (!mRefreshTimer.isActive()) { //Instant update, but then only refresh every 50ms max. updateFromSource(); mRefreshTimer.start(50); } } void EventModel::updateFromSource() { beginResetModel(); mEvents.clear(); for (int i = 0; i < mSourceModel->rowCount(); ++i) { auto event = mSourceModel->index(i, 0).data(Sink::Store::DomainObjectRole).value(); - if (!mCalendarFilter.contains(event->getCalendar())) { + const bool skip = [&] { + if (!mCalendarFilter.contains(event->getCalendar())) { + return true; + } + for (auto it = mFilter.constBegin(); it!= mFilter.constEnd(); it++) { + if (event->getProperty(it.key().toLatin1()) != it.value()) { + return true; + } + } + return false; + }(); + if (skip) { continue; } //Parse the event auto icalEvent = KCalCore::ICalFormat().readIncidence(event->getIcal()).dynamicCast(); if(!icalEvent) { SinkWarning() << "Invalid ICal to process, ignoring..."; continue; } if (icalEvent->recurs()) { const auto duration = icalEvent->hasDuration() ? icalEvent->duration().asSeconds() : 0; KCalCore::OccurrenceIterator occurrenceIterator{*mCalendar, icalEvent, QDateTime{mStart, {0, 0, 0}}, QDateTime{mEnd, {12, 59, 59}}}; while (occurrenceIterator.hasNext()) { occurrenceIterator.next(); const auto start = occurrenceIterator.occurrenceStartDate(); const auto end = start.addSecs(duration); if (start.date() < mEnd && end.date() >= mStart) { mEvents.append({start, end, occurrenceIterator.incidence(), getColor(event->getCalendar()), event->getAllDay()}); } } } else { if (icalEvent->dtStart().date() < mEnd && icalEvent->dtEnd().date() >= mStart) { mEvents.append({icalEvent->dtStart(), icalEvent->dtEnd(), icalEvent, getColor(event->getCalendar()), event->getAllDay()}); } } } endResetModel(); } QModelIndex EventModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) { return {}; } if (!parent.isValid()) { return createIndex(row, column); } return {}; } QModelIndex EventModel::parent(const QModelIndex &) const { return {}; } int EventModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) { return mEvents.size(); } return 0; } int EventModel::columnCount(const QModelIndex &) const { return 1; } QByteArray EventModel::getColor(const QByteArray &calendar) const { const auto color = mCalendarCache->getProperty(calendar, "color").toByteArray(); if (color.isEmpty()) { qWarning() << "Failed to get color for calendar " << calendar; } return color; } // QDateTime EventModel::getStartTimeOfDay(const QDateTime &dateTime, int day) const // { // if (bucketOf(dateTime.date()) < day) { // return QDateTime{mPeriodStart.addDays(day), QTime{0,0}}; // } // return dateTime; // } // QDateTime EventModel::getEndTimeOfDay(const QDateTime &dateTime, int day) const // { // if (bucketOf(dateTime.date()) > day) { // return QDateTime{mPeriodStart.addDays(day), QTime{23, 59, 59}}; // } // return dateTime; // } QVariant EventModel::data(const QModelIndex &idx, int role) const { if (!hasIndex(idx.row(), idx.column())) { return {}; } auto event = mEvents.at(idx.row()); auto icalEvent = event.incidence; switch (role) { case Summary: return icalEvent->summary(); case Description: return icalEvent->description(); case StartTime: return event.start; case EndTime: return event.end; case Color: return event.color; case AllDay: return event.allDay; default: SinkWarning() << "Unknown role for event:" << QMetaEnum::fromType().valueToKey(role); return {}; } } diff --git a/framework/src/domain/multidayeventmodel.cpp b/framework/src/domain/multidayeventmodel.cpp index 3470c259..c531887d 100644 --- a/framework/src/domain/multidayeventmodel.cpp +++ b/framework/src/domain/multidayeventmodel.cpp @@ -1,170 +1,176 @@ /* Copyright (c) 2018 Michael Bohlender Copyright (c) 2018 Christian Mollekopf Copyright (c) 2018 Rémi Nicole This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "multidayeventmodel.h" #include #include #include #include enum Roles { - Events = EventModel::LastRole + Events = EventModel::LastRole, + WeekStartDate }; MultiDayEventModel::MultiDayEventModel(QObject *parent) : QAbstractItemModel(parent) { } QModelIndex MultiDayEventModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) { return {}; } if (!parent.isValid()) { return createIndex(row, column); } return {}; } QModelIndex MultiDayEventModel::parent(const QModelIndex &) const { return {}; } int MultiDayEventModel::rowCount(const QModelIndex &parent) const { //Number of weeks if (!parent.isValid() && mSourceModel) { return qMax(mSourceModel->length() / 7, 1); } return 0; } int MultiDayEventModel::columnCount(const QModelIndex &) const { return 1; } QVariant MultiDayEventModel::data(const QModelIndex &idx, int role) const { if (!hasIndex(idx.row(), idx.column())) { return {}; } + if (!mSourceModel) { + return {}; + } + const auto rowStart = mSourceModel->start().addDays(idx.row() * 7); switch (role) { + case WeekStartDate: + return rowStart; case Events: { - if (!mSourceModel) { - return {}; - } - const auto rowStart = mSourceModel->start().addDays(idx.row() * 7 + 1); const auto rowEnd = rowStart.addDays(7); auto getStart = [&] (const QDate &start) { return qMax(rowStart.daysTo(start), 0ll); }; auto getDuration = [&] (const QDate &start, const QDate &end) { return qMax(start.daysTo(end), 1ll); }; QMultiMap sorted; - //Sort by duration + //TODO: + //All-day first, sorted by duration + //then sort by start time for (int row = 0; row < mSourceModel->rowCount(); row++) { const auto srcIdx = mSourceModel->index(row, 0, {}); const auto start = srcIdx.data(EventModel::StartTime).toDateTime().date(); const auto end = srcIdx.data(EventModel::EndTime).toDateTime().date(); //Filter if not within this row //FIXME: avoid iterating over all events for every week if (end < rowStart || start > rowEnd) { continue; } sorted.insert(getDuration(start, end), srcIdx); } auto result = QVariantList{}; auto currentLine = QVariantList{}; int lastStart = -1; int lastDuration = 0; for (const auto &srcIdx : sorted) { const auto start = getStart(srcIdx.data(EventModel::StartTime).toDateTime().date()); const auto duration = qMin(getDuration(srcIdx.data(EventModel::StartTime).toDateTime().date(), srcIdx.data(EventModel::EndTime).toDateTime().date()), mPeriodLength - start); const auto end = start + duration; currentLine.append(QVariantMap{ {"text", srcIdx.data(EventModel::Summary)}, {"description", srcIdx.data(EventModel::Description)}, {"starts", start}, {"duration", duration}, {"color", srcIdx.data(EventModel::Color)}, }); if (lastStart >= 0) { const auto lastEnd = lastStart + lastDuration; //Does intersect if (((start >= lastStart) && (start <= lastEnd)) || ((end >= lastStart) && (end <= lastStart)) || ((start <= lastStart) && (end >= lastEnd))) { result.append(QVariant::fromValue(currentLine)); // qDebug() << "Found intersection " << currentLine; currentLine = {}; } } lastStart = start; lastDuration = duration; } if (!currentLine.isEmpty()) { result.append(QVariant::fromValue(currentLine)); } // qDebug() << "Found events " << result; return result; } default: Q_ASSERT(false); return {}; } } void MultiDayEventModel::setModel(EventModel *model) { beginResetModel(); mSourceModel = model; auto resetModel = [this] { beginResetModel(); endResetModel(); }; QObject::connect(model, &QAbstractItemModel::dataChanged, this, resetModel); QObject::connect(model, &QAbstractItemModel::layoutChanged, this, resetModel); QObject::connect(model, &QAbstractItemModel::modelReset, this, resetModel); QObject::connect(model, &QAbstractItemModel::rowsInserted, this, resetModel); QObject::connect(model, &QAbstractItemModel::rowsMoved, this, resetModel); QObject::connect(model, &QAbstractItemModel::rowsRemoved, this, resetModel); endResetModel(); } QHash MultiDayEventModel::roleNames() const { return { - {Events, "events"} + {Events, "events"}, + {WeekStartDate, "weekStartDate"} }; } diff --git a/views/calendar/qml/MonthView.qml b/views/calendar/qml/MonthView.qml index bd501959..69d54bee 100644 --- a/views/calendar/qml/MonthView.qml +++ b/views/calendar/qml/MonthView.qml @@ -1,123 +1,90 @@ /* * Copyright (C) 2018 Michael Bohlender, * Copyright (C) 2018 Christian Mollekopf, * * 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, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.4 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.2 import org.kube.framework 1.0 as Kube +import "dateutils.js" as DateUtils FocusScope { id: root property int daysPerRow: 7 - property int daysToShow: daysPerRow * 5 + property int daysToShow: daysPerRow * 6 property var dayWidth: (root.width - Kube.Units.gridUnit - Kube.Units.largeSpacing * 2) / root.daysPerRow property var hourHeight: Kube.Units.gridUnit * 2 property date currentDate property date startDate: currentDate property var calendarFilter - /** - * Returns the week number for this date. dowOffset is the day of week the week - * "starts" on for your locale - it can be from 0 to 6. If dowOffset is 1 (Monday), - * the week returned is the ISO 8601 week number. - * @param int dowOffset - * @return int - */ - function getWeek(date, dowOffset) { - var newYear = new Date(date.getFullYear(),0,1); - var day = newYear.getDay() - dowOffset; //the day of week the year begins on - day = (day >= 0 ? day : day + 7); - var daynum = Math.floor((date.getTime() - newYear.getTime() - - (date.getTimezoneOffset()-newYear.getTimezoneOffset())*60000)/86400000) + 1; - var weeknum; - //if the year starts before the middle of a week - if(day < 4) { - weeknum = Math.floor((daynum+day-1)/7) + 1; - if(weeknum > 52) { - nYear = new Date(date.getFullYear() + 1,0,1); - nday = nYear.getDay() - dowOffset; - nday = nday >= 0 ? nday : nday + 7; - /*if the next year starts before the middle of - the week, it is week #1 of that year*/ - weeknum = nday < 4 ? 1 : 53; - } - } - else { - weeknum = Math.floor((daynum+day-1)/7); - } - return weeknum; - } - function roundToDay(date) { - return new Date(date.getFullYear(), date.getMonth(), date.getDate()) - } Item { anchors { fill: parent rightMargin: Kube.Units.largeSpacing } //FIXME weeknumber per row // Repeater { // model: root.daysToShow / root.daysPerRow // Item { // id: weekNumber // anchors { // left: parent.left // } // y: index * root.dayHeight // width: Kube.Units.gridUnit * 2 // height: Kube.Units.gridUnit * 2 // Label { // anchors.centerIn: parent - // text: getWeek(startDate, 1) + // text: DateUtils.getWeek(startDate, Qt.locale().firstDayOfWeek) // font.bold: true // } // } // } DayLabels { id: dayLabels anchors.top: parent.top anchors.right: parent.right startDate: root.startDate dayWidth: root.dayWidth daysToShow: root.daysPerRow showDate: false } MultiDayView { anchors { top: dayLabels.bottom right: parent.right bottom: parent.bottom } dayWidth: root.dayWidth daysToShow: root.daysToShow daysPerRow: root.daysPerRow currentDate: root.currentDate startDate: root.startDate calendarFilter: root.calendarFilter paintGrid: true } } } diff --git a/views/calendar/qml/MultiDayView.qml b/views/calendar/qml/MultiDayView.qml index 54d4d42b..08b3cd87 100644 --- a/views/calendar/qml/MultiDayView.qml +++ b/views/calendar/qml/MultiDayView.qml @@ -1,165 +1,173 @@ /* * Copyright (C) 2018 Michael Bohlender, * Copyright (C) 2018 Christian Mollekopf, * * 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, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.4 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.2 import org.kube.framework 1.0 as Kube +import "dateutils.js" as DateUtils Rectangle { id: root property int daysToShow property int daysPerRow: daysToShow property var dayWidth property date currentDate property date startDate property var calendarFilter property bool paintGrid: false property var filter //Internal property int numberOfLinesShown: 0 property int numberOfRows: (daysToShow / daysPerRow) property var dayHeight: height / numberOfRows width: root.dayWidth * root.daysPerRow color: Kube.Colors.viewBackgroundColor border.width: 1 border.color: Kube.Colors.buttonColor //+2 to compensate for borders implicitHeight: numberOfRows > 1 ? Kube.Units.gridUnit * 10 * numberOfRows: numberOfLinesShown * Kube.Units.gridUnit + 2 height: implicitHeight visible: numberOfRows > 1 || numberOfLinesShown //Dimm days in the past Rectangle { anchors { left: parent.left top: parent.top bottom: parent.bottom } //if more than 7 days in past, set to 7, otherwise actual number of days in the past - width: (new Date(root.startDate.getFullYear(), root.startDate.getMonth(), root.startDate.getDate() + 7) < roundToDay(root.currentDate) ? 7 : root.currentDate.getDate() - root.startDate.getDate()) * root.dayWidth + width: (new Date(root.startDate.getFullYear(), root.startDate.getMonth(), root.startDate.getDate() + 7) < DateUtils.roundToDay(root.currentDate) ? 7 : root.currentDate.getDate() - root.startDate.getDate()) * root.dayWidth color: Kube.Colors.buttonColor opacity: 0.2 //Avoid showing at all in the future (the width calculation will not work either) - visible: roundToDay(root.currentDate) >= roundToDay(root.startDate) && !root.paintGrid + visible: DateUtils.roundToDay(root.currentDate) >= DateUtils.roundToDay(root.startDate) && !root.paintGrid } Column { anchors { fill: parent margins: 1 } //Weeks Repeater { - id: daysRepeater model: Kube.MultiDayEventModel { model: Kube.EventModel { start: root.startDate length: root.daysToShow calendarFilter: root.calendarFilter filter: root.filter } // daysPerRow: root.daysPerRow //Hardcoded to 7 } + //One row => one week Item { height: root.dayHeight width: parent.width + property var startDate: weekStartDate + //Grid Row { height: parent.height visible: root.paintGrid Repeater { id: gridRepeater model: root.daysPerRow Rectangle { height: parent.height width: root.dayWidth color: "transparent" border.width: 1 border.color: Kube.Colors.lightgrey Label { anchors { top: parent.top left: parent.left topMargin: Kube.Units.smallSpacing leftMargin: Kube.Units.smallSpacing } - text: modelData + function addDaysToDate(date, days) { + var date = new Date(date); + date.setDate(date.getDate() + days); + return date; + } + text: addDaysToDate(startDate, modelData).getDate() font.bold: true } } } } Column { anchors { fill: parent //Offset for date topMargin: root.paintGrid ? Kube.Units.gridUnit + Kube.Units.smallSpacing : 0 } Repeater { id: linesRepeater model: events onCountChanged: { root.numberOfLinesShown = count } Item { id: line height: Kube.Units.gridUnit width: parent.width //Events Repeater { id: eventsRepeater model: modelData Rectangle { x: root.dayWidth * modelData.starts y: 0 width: root.dayWidth * modelData.duration height: parent.height color: modelData.color radius: 2 border.width: 1 border.color: Kube.Colors.viewBackgroundColor Kube.Label { anchors { fill: parent leftMargin: Kube.Units.smallSpacing rightMargin: Kube.Units.smallSpacing } color: Kube.Colors.highlightedTextColor text: modelData.text elide: Text.ElideRight } } } } } } } } } } diff --git a/views/calendar/qml/WeekView.qml b/views/calendar/qml/WeekView.qml index bfe83378..21115f17 100644 --- a/views/calendar/qml/WeekView.qml +++ b/views/calendar/qml/WeekView.qml @@ -1,310 +1,275 @@ /* * Copyright (C) 2018 Michael Bohlender, * Copyright (C) 2018 Christian Mollekopf, * * 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, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.4 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.2 import org.kube.framework 1.0 as Kube +import "dateutils.js" as DateUtils FocusScope { id: root property int daysToShow: 7 property var dayWidth: (root.width - Kube.Units.gridUnit - Kube.Units.largeSpacing * 2) / root.daysToShow property var hourHeight: Kube.Units.gridUnit * 2 property date currentDate property date startDate: currentDate property var calendarFilter - /** - * Returns the week number for this date. dowOffset is the day of week the week - * "starts" on for your locale - it can be from 0 to 6. If dowOffset is 1 (Monday), - * the week returned is the ISO 8601 week number. - * @param int dowOffset - * @return int - */ - function getWeek(date, dowOffset) { - var newYear = new Date(date.getFullYear(),0,1); - var day = newYear.getDay() - dowOffset; //the day of week the year begins on - day = (day >= 0 ? day : day + 7); - var daynum = Math.floor((date.getTime() - newYear.getTime() - - (date.getTimezoneOffset()-newYear.getTimezoneOffset())*60000)/86400000) + 1; - var weeknum; - //if the year starts before the middle of a week - if(day < 4) { - weeknum = Math.floor((daynum+day-1)/7) + 1; - if(weeknum > 52) { - nYear = new Date(date.getFullYear() + 1,0,1); - nday = nYear.getDay() - dowOffset; - nday = nday >= 0 ? nday : nday + 7; - /*if the next year starts before the middle of - the week, it is week #1 of that year*/ - weeknum = nday < 4 ? 1 : 53; - } - } - else { - weeknum = Math.floor((daynum+day-1)/7); - } - return weeknum; - } - - function roundToDay(date) { - return new Date(date.getFullYear(), date.getMonth(), date.getDate()) - } - Item { anchors { top: parent.top right: parent.right rightMargin: Kube.Units.largeSpacing } width: root.dayWidth * root.daysToShow + Kube.Units.gridUnit * 2 height: root.height Item { id: weekNumber anchors { top: parent.top left: parent.left } width: Kube.Units.gridUnit * 2 height: Kube.Units.gridUnit * 2 Label { anchors.centerIn: parent - text: getWeek(startDate, 1) + text: DateUtils.getWeek(startDate, Qt.locale().firstDayOfWeek) font.bold: true } } DayLabels { id: dayLabels anchors.top: parent.top anchors.right: parent.right startDate: root.startDate dayWidth: root.dayWidth daysToShow: root.daysToShow } MultiDayView { id: daylong anchors { top: dayLabels.bottom right: parent.right } dayWidth: root.dayWidth daysToShow: root.daysToShow currentDate: root.currentDate startDate: root.startDate calendarFilter: root.calendarFilter filter: {"allDay": true} } Flickable { id: mainWeekViewer anchors { top: daylong.bottom } Layout.fillWidth: true height: root.height - daylong.height - dayLabels.height - Kube.Units.largeSpacing width: root.dayWidth * root.daysToShow + Kube.Units.gridUnit * 2 contentHeight: root.hourHeight * 24 contentWidth: width clip: true boundsBehavior: Flickable.StopAtBounds ScrollBar.vertical: Kube.ScrollBar {} Kube.ScrollHelper { id: scrollHelper flickable: mainWeekViewer anchors.fill: parent } Row { height: root.hourHeight * 24 width: root.dayWidth * root.daysToShow + Kube.Units.gridUnit * 2 spacing: 0 //BEGIN time labels Column { anchors.bottom: parent.bottom Repeater { model: ["1:00","2:00","3:00","4:00","5:00","6:00","7:00","8:00","9:00","10:00","11:00","12:00", "13:00","14:00","15:00","16:00","17:00","18:00","19:00","20:00","21:00","22:00","23:00","0:00"] delegate: Item { height: root.hourHeight width: Kube.Units.gridUnit * 2 Kube.Label { anchors { right: parent.right rightMargin: Kube.Units.smallSpacing bottom: parent.bottom } text: model.modelData } } } } //END time labels Repeater { model: Kube.PeriodDayEventModel { model: Kube.EventModel { start: root.startDate length: root.daysToShow calendarFilter: root.calendarFilter } } delegate: Rectangle { id: dayDelegate width: root.dayWidth height: root.hourHeight * 24 clip: true color: Kube.Colors.viewBackgroundColor - property bool isInPast: roundToDay(root.currentDate) > roundToDay(date) - property bool isToday: roundToDay(root.currentDate).getTime() == roundToDay(date).getTime() + property bool isInPast: DateUtils.roundToDay(root.currentDate) > DateUtils.roundToDay(date) + property bool isToday: DateUtils.roundToDay(root.currentDate).getTime() == DateUtils.roundToDay(date).getTime() //Dimm days in the past Rectangle { anchors.fill: parent color: Kube.Colors.buttonColor opacity: 0.2 visible: isInPast } //Grid Column { anchors.fill: parent Repeater { model: 12 delegate: Rectangle { height: root.hourHeight * 2 width: parent.width color: "transparent" border.width: 1 border.color: Kube.Colors.lightgrey } } } Repeater { model: events delegate: Rectangle { id: eventDelegate states: [ State { name: "dnd" when: mouseArea.drag.active PropertyChanges {target: mouseArea; cursorShape: Qt.ClosedHandCursor} PropertyChanges {target: eventDelegate; x: x; y: y} PropertyChanges {target: eventDelegate; parent: root} PropertyChanges {target: eventDelegate; opacity: 0.7} PropertyChanges {target: eventDelegate; anchors.right: ""} PropertyChanges {target: eventDelegate; width: root.dayWidth - Kube.Units.smallSpacing * 2} } ] anchors { right: parent.right rightMargin: Kube.Units.smallSpacing } radius: 2 width: root.dayWidth - Kube.Units.smallSpacing * 2 - Kube.Units.gridUnit * model.modelData.indentation height: Math.max(root.hourHeight * 0.5, root.hourHeight * model.modelData.duration) y: root.hourHeight * model.modelData.starts x: Kube.Units.gridUnit * model.modelData.indentation color: model.modelData.color border.width: 1 border.color: Kube.Colors.viewBackgroundColor Kube.Label { anchors { fill: parent leftMargin: Kube.Units.smallSpacing rightMargin: Kube.Units.smallSpacing } text: model.modelData.text color: Kube.Colors.highlightedTextColor wrapMode: Text.Wrap elide: Text.ElideRight } Drag.active: mouseArea.drag.active Drag.hotSpot.x: mouseArea.mouseX Drag.hotSpot.y: mouseArea.mouseY Drag.source: eventDelegate MouseArea { id: mouseArea anchors.fill: parent hoverEnabled: true drag.target: parent onReleased: eventDelegate.Drag.drop() } } } Rectangle { id: currentTimeLine anchors { right: parent.right left: parent.left } y: root.hourHeight * root.currentDate.getHours() + root.hourHeight / 60 * root.currentDate.getMinutes() height: 2 color: Kube.Colors.plasmaBlue visible: isToday opacity: 0.8 } DropArea { anchors.fill: parent onDropped: { console.log("DROP") drop.accept(Qt.MoveAction) //drop.source.visible = false console.log((drop.source.y - mainWeekViewer.y + mainWeekViewer.contentY) / hourHeight) } } } } } } } } diff --git a/views/calendar/qml/dateutils.js b/views/calendar/qml/dateutils.js new file mode 100644 index 00000000..53c308b3 --- /dev/null +++ b/views/calendar/qml/dateutils.js @@ -0,0 +1,37 @@ + +/** +* Returns the week number for this date. dowOffset is the day of week the week +* "starts" on for your locale - it can be from 0 to 6. If dowOffset is 1 (Monday), +* the week returned is the ISO 8601 week number. +* @param int dowOffset +* @return int +*/ +function getWeek(date, dowOffset) { + var newYear = new Date(date.getFullYear(),0,1); + var day = newYear.getDay() - dowOffset; //the day of week the year begins on + day = (day >= 0 ? day : day + 7); + var daynum = Math.floor((date.getTime() - newYear.getTime() - + (date.getTimezoneOffset()-newYear.getTimezoneOffset())*60000)/86400000) + 1; + var weeknum; + //if the year starts before the middle of a week + if(day < 4) { + weeknum = Math.floor((daynum+day-1)/7) + 1; + if(weeknum > 52) { + nYear = new Date(date.getFullYear() + 1,0,1); + nday = nYear.getDay() - dowOffset; + nday = nday >= 0 ? nday : nday + 7; + /*if the next year starts before the middle of + the week, it is week #1 of that year*/ + weeknum = nday < 4 ? 1 : 53; + } + } + else { + weeknum = Math.floor((daynum+day-1)/7); + } + return weeknum; +} + +function roundToDay(date) { + return new Date(date.getFullYear(), date.getMonth(), date.getDate()) +} +