ubuntu-touch-coreapps-reviewers team mailing list archive
-
ubuntu-touch-coreapps-reviewers team
-
Mailing list archive
-
Message #08532
[Merge] lp:~renatofilho/ubuntu-calendar-app/optimize into lp:ubuntu-calendar-app
Renato Araujo Oliveira Filho has proposed merging lp:~renatofilho/ubuntu-calendar-app/optimize into lp:ubuntu-calendar-app with lp:~renatofilho/ubuntu-calendar-app/optimize-page-load as a prerequisite.
Commit message:
Optimize Application;
Rewrite event layout functions;
Fixed several bugs;
Requested reviews:
Jenkins Bot (ubuntu-core-apps-jenkins-bot): continuous-integration
Kunal Parmar (pkunal-parmar)
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot): continuous-integration
For more details, see:
https://code.launchpad.net/~renatofilho/ubuntu-calendar-app/optimize/+merge/283858
--
Your team Ubuntu Calendar Developers is subscribed to branch lp:ubuntu-calendar-app.
=== modified file 'AgendaView.qml'
--- AgendaView.qml 2016-02-03 08:06:25 +0000
+++ AgendaView.qml 2016-03-03 21:42:32 +0000
@@ -23,16 +23,14 @@
import "dateExt.js" as DateExt
import "./3rd-party/lunar.js" as Lunar
-Page{
+PageWithBottomEdge {
id: root
objectName: "AgendaView"
- property var currentDay: new Date()
+ property var anchorDate: new Date()
signal dateSelected(var date);
- Keys.forwardTo: [eventList]
-
function goToBeginning() {
eventList.positionViewAtBeginning();
}
@@ -45,13 +43,17 @@
return !!enabled_calendars.length;
}
+
+ Keys.forwardTo: [eventList]
+ createEventAt: anchorDate
+
Action {
id: calendarTodayAction
objectName:"todaybutton"
iconName: "calendar-today"
text: i18n.tr("Today")
onTriggered: {
- currentDay = new Date()
+ anchorDate = new Date()
goToBeginning()
}
}
@@ -63,7 +65,6 @@
leadingActionBar.actions: tabs.tabsAction
trailingActionBar.actions: [
calendarTodayAction,
- commonHeaderActions.newEventAction,
commonHeaderActions.showCalendarAction,
commonHeaderActions.reloadAction,
commonHeaderActions.syncCalendarAction,
@@ -74,9 +75,9 @@
EventListModel {
id: eventListModel
- startPeriod: currentDay.midnight();
- endPeriod: currentDay.addDays(7).endOfDay()
- filter: eventModel.filter
+ startPeriod: anchorDate.midnight();
+ endPeriod: anchorDate.addDays(7).endOfDay()
+ filter: model.filter
sortOrders: [
SortOrder{
@@ -106,7 +107,7 @@
return default_title;
}
- visible: (eventListModel.count === 0) && !eventListModel.isLoading
+ visible: (eventList.count === 0) && !eventListModel.isLoading
anchors.centerIn: parent
}
@@ -119,12 +120,12 @@
color: UbuntuColors.orange
onClicked: {
- pageStack.push(Qt.resolvedUrl("CalendarChoicePopup.qml"),{"model":eventModel});
- pageStack.currentPage.collectionUpdated.connect(eventModel.delayedApplyFilter);
+ pageStack.push(Qt.resolvedUrl("CalendarChoicePopup.qml"),{"model": model});
+ pageStack.currentPage.collectionUpdated.connect(model.delayedApplyFilter);
}
}
- ListView{
+ ListView {
id: eventList
objectName: "eventList"
model: eventListModel
=== modified file 'AllDayEventComponent.qml'
--- AllDayEventComponent.qml 2016-02-03 08:06:25 +0000
+++ AllDayEventComponent.qml 2016-03-03 21:42:32 +0000
@@ -31,12 +31,14 @@
property var allDayEvents;
property var model;
+ signal pressAndHold(var date)
+
width: parent.width
height: units.gu(5)
function getAllDayEvents(startDate, endDate) {
var map = {};
- var items = model.getItems(startDate,endDate);
+ var items = model.itemsByTimePeriod(startDate,endDate);
for(var i = 0 ; i < items.length ; ++i) {
var event = items[(i)];
if( event && event.allDay ) {
@@ -62,6 +64,10 @@
}
Repeater{
+ id: repeater
+
+ readonly property bool compactView: (root.width / repeater.count) < units.gu(15)
+
model: type == ViewType.ViewTypeWeek ? 7 : 1
delegate: Item {
id: allDayButton
@@ -71,29 +77,86 @@
height: units.gu(5)
width: parent.width / (type == ViewType.ViewTypeWeek ? 7 : 1)
+ Rectangle {
+ id: temporaryEvent
+
+ anchors.fill: parent
+ visible: mouseArea.mouseHold
+ Label {
+ anchors.fill: parent
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ text: i18n.tr("New event")
+ }
+ z: 100
+ }
+
MouseArea {
+ id: mouseArea
+
+ property bool mouseHold: false
+
+ preventStealing: mouseHold
anchors.fill: parent
onClicked: {
if(!allDayButton.events || allDayButton.events.length === 0) {
return;
}
- if(type == ViewType.ViewTypeWeek) {
- PopupUtils.open(popoverComponent, root,{"events": allDayButton.events})
+ if(repeater.compactView) {
+ PopupUtils.open(popoverComponent, allDayButton,{"events": allDayButton.events})
} else {
if( allDayButton.events.length > 1 ) {
- PopupUtils.open(popoverComponent, root,{"events": allDayButton.events})
+ PopupUtils.open(popoverComponent, allDayButton,{"events": allDayButton.events})
} else {
pageStack.push(Qt.resolvedUrl("EventDetails.qml"),{"event":allDayButton.events[0],"model": root.model});
}
}
}
+
+ onReleased: {
+ if (mouseHold && containsMouse) {
+ root.pressAndHold(startDay.midnight().addDays(index))
+ }
+ mouseHold = false
+ }
+
+
+ onPressAndHold: {
+ mouseHold = true
+ Haptics.play()
+ }
}
- Loader {
- id: eventLabelLoader
- anchors.fill: parent
- sourceComponent : !allDayButton.events || allDayButton.events.length === 0 ? undefined : eventComponent
+ Label {
+ id: eventLabel
+ anchors {
+ fill: parent
+ margins: units.gu(0.5)
+ }
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ elide: Text.ElideRight
+ text: {
+ if(!events || events.length === 0) {
+ return "";
+ }
+
+ if(repeater.compactView) {
+ // TRANSLATORS: the first parameter refers to the number of all-day events
+ // on a given day. "Ev." is short form for "Events".
+ // Please keep the translation of "Ev." to 3 characters only, as the week view
+ // where it's shown has limited space
+ return i18n.tr("%1 ev.").arg(events.length)
+ } else {
+ if( events.length > 1) {
+ // TRANSLATORS: the argument refers to the number of all day events
+ return i18n.tr("%1 all day event", "%1 all day events", events.length).arg(events.length)
+ } else {
+ return events[0].displayLabel;
+ }
+ }
+ }
}
Loader{
@@ -110,39 +173,11 @@
sd = sd.addDays(index);
var key = Qt.formatDateTime(sd, "dd-MMM-yyyy");
events = allDayEvents[key];
-
- if(!events || events.length === 0) {
- return;
- }
-
- if(type == ViewType.ViewTypeWeek) {
- // TRANSLATORS: the first parameter refers to the number of all-day events
- // on a given day. "Ev." is short form for "Events".
- // Please keep the translation of "Ev." to 3 characters only, as the week view
- // where it's shown has limited space
- eventLabelLoader.item.text = i18n.tr("%1 ev.").arg(events.length)
- } else {
- if( events.length > 1) {
- // TRANSLATORS: the argument refers to the number of all day events
- eventLabelLoader.item.text = i18n.tr("%1 all day event", "%1 all day events", events.length).arg(events.length)
- } else {
- eventLabelLoader.item.text = events[0].displayLabel;
- }
- }
}
}
}
}
-
- Component{
- id: eventComponent
- Label {
- verticalAlignment: Text.AlignVCenter
- horizontalAlignment: Text.AlignHCenter
- }
- }
-
Component {
id: dividerComponent
SimpleDivider{
=== modified file 'ContactChoicePopup.qml'
--- ContactChoicePopup.qml 2016-01-29 14:35:14 +0000
+++ ContactChoicePopup.qml 2016-03-03 21:42:32 +0000
@@ -17,7 +17,7 @@
*/
import QtQuick 2.4
import Ubuntu.Components 1.3
-import Ubuntu.Components.Popups 1.0
+import Ubuntu.Components.Popups 1.3
import Ubuntu.Components.ListItems 1.0
import Ubuntu.Components.Themes.Ambiance 1.0
import QtOrganizer 5.0
@@ -29,7 +29,7 @@
id: root
objectName: "contactPopover"
- signal contactSelected(var contact);
+ signal contactSelected(var contact, string emailAddress);
Label {
id: noContact
@@ -40,24 +40,27 @@
UnionFilter {
id: filter
+
+ property string searchString: ""
+
filters: [
DetailFilter{
detail: ContactDetail.Name
field: Name.FirstName
matchFlags: Filter.MatchContains
- value: searchBox.text
+ value: filter.searchString
},
DetailFilter{
detail: ContactDetail.Name
field: Name.LastName
matchFlags: Filter.MatchContains
- value: searchBox.text
+ value: filter.searchString
},
DetailFilter{
detail: ContactDetail.DisplayLabel
field: DisplayLabel.Label
matchFlags: Filter.MatchContains
- value: searchBox.text
+ value: filter.searchString
}
]
}
@@ -69,6 +72,16 @@
autoUpdate: true
}
+ Timer {
+ id: idleSearch
+
+ interval: 500
+ repeat: false
+ onTriggered: {
+ filter.searchString = searchBox.text
+ }
+ }
+
Column {
anchors.top: parent.top
anchors.left: parent.left
@@ -81,12 +94,16 @@
focus: true
width: parent.width
placeholderText: i18n.tr("Search contact")
+ inputMethodHints: Qt.ImhNoPredictiveText
primaryItem: Icon {
height: parent.height*0.5
width: parent.height*0.5
anchors.verticalCenter: parent.verticalCenter
name:"find"
- }
+ }
+ onTextChanged: {
+ idleSearch.restart()
+ }
}
ListView {
@@ -96,17 +113,42 @@
model: contactModel
height: units.gu(15)
clip: true
- delegate: Standard{
- objectName: "contactPopoverList%1".arg(index)
- property var item: contactModel.contacts[index]
- height: units.gu(4)
- text: item ? item.displayLabel.label : ""
-
- onClicked: {
- root.contactSelected(item);
- onClicked: PopupUtils.close(root)
+ focus: false
+ delegate: Column {
+ width: contactList.width
+ Repeater {
+ anchors {
+ left: parent.left
+ right: parent.right
+ }
+ height: childrenRect.height
+
+ model: Math.max(1, contact.emails.length)
+ delegate: ListItem {
+ property string emailAddress: contact.emails.length > index ? contact.emails[index].emailAddress : ""
+
+ activeFocusOnPress: false
+ opacity: emailAddress.length > 0 ? 1.0 : 0.3
+ width: contactList.width
+ objectName: "contactPopoverList%1".arg(index)
+ ListItemLayout {
+ title.text: contact.displayLabel.label
+ subtitle.text: emailAddress
+ }
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ if (emailAddress.length > 0) {
+ root.contactSelected(contact, emailAddress);
+ PopupUtils.close(root)
+ }
+ }
+ }
+ }
}
}
}
}
+
+ Component.onCompleted: searchBox.forceActiveFocus()
}
=== modified file 'DayView.qml'
--- DayView.qml 2016-02-03 08:06:25 +0000
+++ DayView.qml 2016-03-03 21:42:32 +0000
@@ -18,21 +18,51 @@
import QtQuick 2.4
import Ubuntu.Components 1.3
+
import "dateExt.js" as DateExt
import "ViewType.js" as ViewType
import "./3rd-party/lunar.js" as Lunar
-Page{
+PageWithBottomEdge {
id: dayViewPage
objectName: "dayViewPage"
- property var currentDay: new Date()
- property bool isCurrentPage: false
+
+ property var anchorDate: new Date()
+ readonly property var currentDate: dayViewPath.currentItem.startDay
signal dateSelected(var date);
+ signal pressAndHoldAt(var date, bool allDay)
+
+ function delayScrollToDate(date, scrollTime) {
+ idleScroll.scrollToTime = scrollTime != undefined ? scrollTime : true
+ idleScroll.scrollToDate = new Date(date)
+ idleScroll.restart()
+ }
Keys.forwardTo: [dayViewPath]
- flickable: null
+
+ createEventAt: currentDate
+ onAnchorDateChanged: {
+ dayViewPath.scrollToBegginer()
+ }
+
+ onEventCreated: {
+ var scrollDate = new Date(event.startDateTime)
+ var needScroll = false
+ if ((currentDate.getFullYear() !== scrollDate.getFullYear()) ||
+ (currentDate.getMonth() !== scrollDate.getMonth()) ||
+ (currentDate.getDate() !== scrollDate.getDate())) {
+ anchorDate = new Date(scrollDate)
+ needScroll = true
+ } else if (!dayViewPath.currentItem.timeIsVisible(scrollDate)) {
+ needScroll = true
+ }
+
+ if (needScroll) {
+ delayScrollToDate(scrollDate, !event.allDay)
+ }
+ }
Action {
id: calendarTodayAction
@@ -40,17 +70,38 @@
iconName: "calendar-today"
text: i18n.tr("Today")
onTriggered: {
- currentDay = new Date()
+ anchorDate = new Date()
+ delayScrollToDate(anchorDate)
+ }
+ }
+
+
+ Timer {
+ id: idleScroll
+
+ property var scrollToDate: null
+ property bool scrollToTime: true
+
+ interval: 200
+ repeat:false
+ onTriggered: {
+ if (scrollToDate && scrollToTime) {
+ dayViewPath.currentItem.scrollToTime(scrollToDate)
+ } else {
+ dayViewPath.currentItem.scrollToBegin()
+ }
+ scrollToDate = null
+ scrollToTime = true
}
}
header: PageHeader {
id: pageHeader
+ flickable: null
leadingActionBar.actions: tabs.tabsAction
trailingActionBar.actions: [
calendarTodayAction,
- commonHeaderActions.newEventAction,
commonHeaderActions.showCalendarAction,
commonHeaderActions.reloadAction,
commonHeaderActions.syncCalendarAction,
@@ -61,87 +112,82 @@
// TRANSLATORS: this is a time formatting string,
// see http://qt-project.org/doc/qt-5/qml-qtqml-date.html#details for valid expressions.
// It's used in the header of the month and week views
- var monthName = currentDay.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy"))
+ var monthName = currentDate.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy"))
return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1)
}
+ }
+ onBottomEdgeCommitStarted: {
+ var eventAt = new Date()
+ if (dayViewPath.currentItem) {
+ eventAt.setFullYear(currentDate.getFullYear())
+ eventAt.setMonth(currentDate.getMonth())
+ eventAt.setDate(currentDate.getDate())
+ }
+ createEventAt = eventAt
}
PathViewBase{
id: dayViewPath
objectName: "dayViewPath"
- property var startDay: currentDay
+ property var startDay: currentDate
//This is used to scroll all view together when currentItem scrolls
- property var childContentY;
+ property real childContentY;
anchors {
fill: parent
topMargin: header.height
}
- onNextItemHighlighted: {
- //next day
- currentDay = currentDay.addDays(1);
- }
-
- onPreviousItemHighlighted: {
- //previous day
- currentDay = currentDay.addDays(-1);
- }
-
- delegate: Loader {
+ delegate: TimeLineBaseComponent {
+ id: timeLineView
+ objectName: "DayComponent-"+index
+
width: parent.width
height: parent.height
- asynchronous: !dayViewPath.isCurrentItem
- sourceComponent: delegateComponent
-
- Component {
- id: delegateComponent
-
- TimeLineBaseComponent {
- id: timeLineView
- objectName: "DayComponent-"+index
-
- type: ViewType.ViewTypeDay
- anchors.fill: parent
-
- isActive: parent.PathView.isCurrentItem
- contentInteractive: parent.PathView.isCurrentItem
- startDay: dayViewPath.startDay.addDays(dayViewPath.indexType(index))
- keyboardEventProvider: dayViewPath
-
- Component.onCompleted: {
- if(dayViewPage.isCurrentPage){
- timeLineView.scrollToCurrentTime();
- }
- }
-
- Connections{
- target: dayViewPage
- onIsCurrentPageChanged:{
- if(dayViewPage.isCurrentPage){
- timeLineView.scrollToCurrentTime();
- }
- }
- }
-
- //get contentY value from PathView, if its not current Item
- Binding{
- target: timeLineView
- property: "contentY"
- value: dayViewPath.childContentY;
- when: !parent.PathView.isCurrentItem
- }
-
- //set PathView's contentY property, if its current item
- Binding{
- target: dayViewPath
- property: "childContentY"
- value: contentY
- when: parent.PathView.isCurrentItem
- }
- }
+
+ type: ViewType.ViewTypeDay
+ isCurrentItem: PathView.isCurrentItem
+ isActive: !dayViewPath.moving && !dayViewPath.flicking
+ contentInteractive: PathView.isCurrentItem
+ startDay: anchorDate.addDays(dayViewPath.loopCurrentIndex + dayViewPath.indexType(index))
+ keyboardEventProvider: dayViewPath
+ modelFilter: dayViewPage.model ? dayViewPage.model.filter : null
+
+ onPressAndHoldAt: {
+ dayViewPage.pressAndHoldAt(date, allDay)
+ }
+
+ Component.onCompleted: {
+ if(dayViewPage.tabSelected){
+ idleScroll.restart()
+ }
+ }
+
+ Connections{
+ target: dayViewPage
+ onTabSelectedChanged: {
+ if(tabSelected){
+ timeLineView.scrollToTime(new Date());
+ }
+ }
+ }
+
+ //get contentY value from PathView, if its not current Item
+ Binding{
+ target: timeLineView
+ property: "contentY"
+ value: dayViewPath.childContentY;
+ when: !timeLineView.isCurrentItem
+ }
+
+ //set PathView's contentY property, if its current item
+ Binding{
+ target: dayViewPath
+ property: "childContentY"
+ value: timeLineView.contentY
+ when: timeLineView.isCurrentItem
}
}
}
=== modified file 'EventActions.qml'
--- EventActions.qml 2016-02-01 19:49:33 +0000
+++ EventActions.qml 2016-03-03 21:42:32 +0000
@@ -23,7 +23,6 @@
Item {
id: actionPool
- property alias newEventAction: _newEventAction
property alias showCalendarAction: _showCalendarAction
property alias syncCalendarAction: _syncCalendarAction
property alias settingsAction: _settingsAction
@@ -44,17 +43,6 @@
id: syncMonitor
}
- Action {
- id: _newEventAction
- objectName: "neweventbutton"
- name: "neweventbutton"
- iconName: "new-event"
- text: i18n.tr("New Event")
- onTriggered: {
- pageStack.push(Qt.resolvedUrl("NewEvent.qml"),{"date":tabs.currentDay,"model":eventModel});
- }
- }
-
Action{
id: _showCalendarAction
objectName: "calendarsbutton"
=== modified file 'EventBubble.qml'
--- EventBubble.qml 2016-01-29 14:35:14 +0000
+++ EventBubble.qml 2016-03-03 21:42:32 +0000
@@ -20,56 +20,34 @@
import Ubuntu.Components 1.3
import QtOrganizer 5.0
+import "calendar_canvas.js" as CanlendarCanvas
+
Item{
id: infoBubble
+ property var anchorDate;
property var event;
property var model;
-
+ property int depthInRow: 0
+ property real sizeOfRow:0.0
+ property real minuteHeight: 1.0
property int type: narrowType
property int wideType: 1;
property int narrowType: 2;
-
- property int depthInRow: 0;
- property int sizeOfRow:0
-
property bool isLiveEditing: false
-
property Flickable flickable;
+ property bool isEventBubble: true
readonly property int minimumHeight: type == wideType
? detailsItems.timeLabelHeight + /*top-bottom margin*/ units.gu(2)
: units.gu(2)
- z: depthInRow
+ readonly property real startTimeInMinutes: event ? CanlendarCanvas.minutesSince(infoBubble.anchorDate, event.startDateTime) : 0.0
+ readonly property real endTimeInMinutes: event ? CanlendarCanvas.minutesSince(infoBubble.anchorDate, event.endDateTime) : 0.0
+ readonly property real durationInMinutes: endTimeInMinutes - startTimeInMinutes
signal clicked(var event);
- Rectangle{
- id: bg
- anchors.fill: parent
- border.color: isLiveEditing ? "red" : "white"
- }
-
- function resize() {
- var offset = parent.width/sizeOfRow;
- x = (depthInRow) * offset;
- width = parent.width - x;
- }
-
- Connections{
- target: parent
- onWidthChanged:{
- resize();
- }
- }
-
- onEventChanged: {
- resize();
- assingnBgColor();
- setDetails();
- }
-
function assingnBgColor() {
if (model && event ) {
var collection = model.collection( event.collectionId );
@@ -102,20 +80,6 @@
}
}
- function layoutBubbleDetails() {
- if( !flickable || flickable === undefined ) {
- return;
- }
-
- if( infoBubble.y < flickable.contentY && infoBubble.height > flickable.height) {
- var y = (flickable.contentY - infoBubble.y) * 1.2;
- if( ( y + detailsItems.height + units.gu(2)) > infoBubble.height) {
- y = infoBubble.height - detailsItems.height - units.gu(2);
- }
- detailsItems.y = y;
- }
- }
-
function setDetails() {
if(event === null || event === undefined) {
return;
@@ -153,8 +117,38 @@
timeLabel.horizontalAlignment = Text.AlignHCenter
timeLabel.wrapMode = Text.WrapAtWordBoundaryOrAnywhere
}
-
- layoutBubbleDetails();
+ }
+
+ function resize()
+ {
+ width = parent ? parent.width * sizeOfRow : 0
+ x = depthInRow * width
+ z = depthInRow
+ height = Math.max(30, (durationInMinutes * parent.minuteHeight))
+ }
+
+ onEventChanged: {
+ assingnBgColor();
+ setDetails();
+ resize()
+ }
+
+ Connections {
+ target: parent
+ onWidthChanged: resize()
+ }
+
+ Binding {
+ target: infoBubble
+ property: "y"
+ value: (startTimeInMinutes * parent.minuteHeight)
+ when: !infoBubble.isLiveEditing
+ }
+
+ Rectangle{
+ id: bg
+ anchors.fill: parent
+ border.color: isLiveEditing ? "red" : "white"
}
Item {
@@ -193,25 +187,6 @@
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
}
}
-
- onHeightChanged: {
- layoutBubbleDetails();
- }
-
- Connections {
- target: infoBubble
- onFlickableChanged: {
- if (flickable && infoBubble.height > flickable.height) {
- flickable.onContentYChanged.connect(layoutBubbleDetails);
- }
- }
-
- onHeightChanged: {
- if(flickable && infoBubble.height > flickable.height) {
- flickable.onContentYChanged.connect(layoutBubbleDetails);
- }
- }
- }
}
Drag.active: dragArea.drag.active
@@ -219,11 +194,19 @@
MouseArea {
id: dragArea
anchors.fill: parent
- drag.target: isLiveEditing ? infoBubble : null
- drag.axis: Drag.YAxis
- drag.minimumY: flickable ? flickable.y : 0
- drag.maximumY: flickable ? flickable.contentHeight - infoBubble.height : infoBubble.height
- onReleased: parent.Drag.drop()
+ drag {
+ target: isLiveEditing ? infoBubble : null
+ axis: Drag.YAxis
+ minimumY: flickable ? flickable.y : 0
+ maximumY: flickable ? flickable.contentHeight - infoBubble.height : infoBubble.height
+ }
+ onReleased: {
+ if (isLiveEditing) {
+ isLiveEditing = false;
+ infoBubble.z -= 1;
+ }
+ parent.Drag.drop()
+ }
onClicked: {
if( isLiveEditing ) {
isLiveEditing = false;
=== removed file 'EventLayoutHelper.js'
--- EventLayoutHelper.js 2015-11-27 01:34:46 +0000
+++ EventLayoutHelper.js 1970-01-01 00:00:00 +0000
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2013-2014 Canonical Ltd
- *
- * This file is part of Ubuntu Calendar App
- *
- * Ubuntu Calendar App is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3 as
- * published by the Free Software Foundation.
- *
- * Ubuntu Calendar App 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/>.
- */
-WorkerScript.onMessage = function(events) {
-
- //returns sorted array of schedules
- //schedule is start time and duration
- var allSchs = processEvents(events);
-
- while( allSchs.length > 0) {
- var sch = allSchs.shift();
-
- //finds all schedules overlapping with current schedule and remove from original array
- var schs = findOverlappingSchedules(sch, allSchs);
-
- //insert original schedule first, so array remain sorted
- schs.unshift(sch);
-
- //assign position to schedules with respest to their duration and start time
- var array = [];
- var maxDepth = assignDepth(schs, array);
- WorkerScript.sendMessage({ 'schedules': array,"maxDepth":maxDepth});
- }
-}
-
-function processEvents(events) {
- var array = [];
- for( var i = 0; i < events.length ; ++i) {
- var event = events[i]
- var sch = {};
- sch["start"] = event.startDateTime.getTime()
- sch["duration"] = event.endDateTime - event.startDateTime
- sch["id"] = event.id;
- sch["depth"] = 0;
- sortedInsert(array,sch);
- }
- return array;
-}
-
-//insert in to array using insertion sort
-function sortedInsert(array, sch) {
- for(var i = array.length-1; i >=0 ; --i) {
- var temp = array[i];
- if( sch.duration < array[i].duration) {
- array.splice(i+1,0,sch);
- return;
- }
- }
- array.push(sch);
-}
-
-//find all overlapping schedules respect to provided schedule
-function findOverlappingSchedules( sch, schs) {
- var array = [];
- var i = 0;
- while( i < schs.length && schs.length > 0) {
- var otherSch = schs[i];
- if( doesOverlap(sch, otherSch) ) {
- schs.splice(i,1);
- array.push(otherSch);
- sch = mergeSchedules(sch,otherSch);
- } else {
- i++;
- }
- }
- return array;
-}
-
-//merges tow schedules and creates a schedule which is long engouth to contain both the of them
-function mergeSchedules(sch1,sch2) {
- var sch = {};
- sch["start"] = Math.min(sch1.start,sch2.start);
- sch["duration"] = Math.max(sch1.duration,sch2.duration);
- sch["depth"] = 1;
- sch["id"] = "DUMMY";
- return sch;
-}
-
-
-//check if two schedules overlap each other
-//is start time of one schedule is between
-//start and end time of other schedule then it's considered as overlap
-function doesOverlap( sch1, sch2) {
- if( sch1.start >= sch2.start && sch1.start < sch2.start + sch2.duration ) {
- return true;
- }
-
- if( sch2.start >= sch1.start && sch2.start < sch1.start + sch1.duration ) {
- return true;
- }
-
- return false;
-}
-
-//assign depth(position) of schedule with respect to other
-function assignDepth(schs, array) {
- var maxDepth = 0;
- while( schs.length > 0 ) {
- var sch = schs.shift()
- array.push(sch);
- var depth = findDepth(sch, schs);
- maxDepth = Math.max(maxDepth,depth);
- }
- return maxDepth;
-}
-
-function findDepth( sch, schs) {
- var maxDepth = 0;
- for( var i = 0; i < schs.length ; ++i) {
- var otherSch = schs[i];
- if( doesOverlap(sch, otherSch) ) {
- otherSch.depth ++;
- maxDepth = Math.max(maxDepth,otherSch.depth);
- sch = mergeSchedules(sch,otherSch);
- }
- }
- return maxDepth;
-}
=== modified file 'EventListModel.qml'
--- EventListModel.qml 2016-01-25 13:20:32 +0000
+++ EventListModel.qml 2016-03-03 21:42:32 +0000
@@ -80,7 +80,6 @@
var collections = eventModel.collections;
for(var i = 0 ; i < collections.length ; ++i) {
var cal = collections[i];
- print(cal.name + " ---- " + cal.extendedMetaData("collection-readonly"));
if( cal.extendedMetaData("collection-type") === "Calendar" &&
cal.extendedMetaData("collection-readonly") === false ) {
cals.push(cal);
@@ -90,16 +89,21 @@
}
function getDefaultCollection() {
+ var defaultCol = defaultCollection();
+ if (defaultCol.extendedMetaData("collection-selected") === true) {
+ return defaultCol
+ }
+
var cals = getCollections();
- for(var i = 0 ; i < cals.length ; ++i) {
+ for(var i = 0 ; i < cals.length ; ++i) {
var cal = cals[i]
- var val = cal.extendedMetaData("X-CAL-DEFAULT-CALENDAR")
- if( val ) {
+ var val = cal.extendedMetaData("collection-selected")
+ if (val === true) {
return cal;
}
}
- return defaultCollection();
+ return cals[0]
}
function setDefaultCollection( collectionId ) {
=== modified file 'KeyboardRectangle.qml'
--- KeyboardRectangle.qml 2016-01-25 13:20:32 +0000
+++ KeyboardRectangle.qml 2016-03-03 21:42:32 +0000
@@ -20,18 +20,12 @@
Item {
id: keyboardRect
+
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height : 0
- Behavior on height {
- NumberAnimation {
- duration: 300
- easing.type: Easing.InOutQuad
- }
- }
-
states: [
State {
name: "hidden"
=== modified file 'MonthComponent.qml'
--- MonthComponent.qml 2016-02-03 08:06:25 +0000
+++ MonthComponent.qml 2016-03-03 21:42:32 +0000
@@ -17,78 +17,45 @@
*/
import QtQuick 2.4
import Ubuntu.Components 1.3
+
import "dateExt.js" as DateExt
import "colorUtils.js" as Color
+import "./3rd-party/lunar.js" as Lunar
Item{
id: root
objectName: "MonthComponent"
property bool isCurrentItem;
-
- property bool showEvents: false
-
- property var currentMonth;
+ property int currentYear;
+ property int currentMonth;
+
property var isYearView;
- property var selectedDay;
- property bool displayWeekNumber:false;
- property bool displayLunarCalendar: false;
-
- property string subLabelFontSize: "small"
+ property var highlightedDate;
+ property bool displayWeekNumber:false
+ property bool displayLunarCalendar: false
+
+ property alias dayLabelDelegate : dayLabelRepeater.delegate
+ property alias dateLabelDelegate : dateLabelRepeater.delegate
+ readonly property alias monthStartDate: intern.monthStart
+
property string dayLabelFontSize: "medium"
property string dateLabelFontSize: "large"
property string monthLabelFontSize: "large"
property string yearLabelFontSize: "large"
- property alias dayLabelDelegate : dayLabelRepeater.delegate
- property alias dateLabelDelegate : dateLabelRepeater.delegate
-
signal monthSelected(var date);
signal dateSelected(var date);
signal dateHighlighted(var date);
- //creatng timer only if we need to show events in month
- Loader {
- id: timerLoader
- sourceComponent: showEvents ? timerComp : undefined
- }
-
- // Timer to delay creation of Model, There seems some problem fetching events if we create Model immediatly
- Component {
- id: timerComp
- Timer{
- interval: 200; running: true; repeat: false
- onTriggered: {
- modelLoader.sourceComponent = modelComponent
- }
- }
- }
-
- Loader{
- id: modelLoader
- }
-
- Component{
- id: modelComponent
- EventListModel {
- id: mainModel
- startPeriod: intern.monthStart.midnight();
- endPeriod: intern.monthStart.addDays((/*monthGrid.rows * cols */ 42 )-1).endOfDay()
- filter: eventModel.filter
- onModelChanged: {
- intern.eventStatus = Qt.binding(function() { return mainModel.containsItems(startPeriod, endPeriod, 86400/*24*60*60*/)});
- }
- }
+ function updateEvents(events) {
+ intern.eventStatus = events
}
QtObject{
id: intern
- property var eventStatus;
-
- property int curMonthDate: currentMonth.getDate()
- property int curMonth: currentMonth.getMonth()
- property int curMonthYear: currentMonth.getFullYear()
+ property var eventStatus: new Array(42)
property var today: DateExt.today()
property int todayDate: today.getDate()
@@ -96,36 +63,42 @@
property int todayYear: today.getFullYear()
//date from month will start, this date might be from previous month
- property var monthStart: currentMonth.weekStart( Qt.locale().firstDayOfWeek )
+ property var currentDate: new Date(root.currentYear, root.currentMonth, 1, 0, 0, 0, 0)
+ property var monthStart: currentDate.weekStart( Qt.locale().firstDayOfWeek )
property int monthStartDate: monthStart.getDate()
property int monthStartMonth: monthStart.getMonth()
property int monthStartYear: monthStart.getFullYear()
-
- property int daysInStartMonth: Date.daysInMonth(monthStartYear, monthStartMonth)
- property int daysInCurMonth: Date.daysInMonth(curMonthYear,curMonth)
+ readonly property int daysInStartMonth: Date.daysInMonth(monthStartYear, monthStartMonth)
//check if current month is start month
- property bool isCurMonthStartMonth: curMonthDate === monthStartDate
- && curMonth === monthStartMonth
- && curMonthYear === monthStartYear
+ property bool isCurMonthStartMonth: root.currentMonth === monthStartMonth &&
+ root.currentYear === monthStartYear
//check current month is same as today's month
- property bool isCurMonthTodayMonth: todayYear === curMonthYear && todayMonth == curMonth
+ property bool isCurMonthTodayMonth: todayYear === root.currentYear &&
+ todayMonth == root.currentMonth
//offset from current month's first date to start date of current month
property int offset: isCurMonthStartMonth ? -1 : (daysInStartMonth - monthStartDate)
property int dateFontSize: FontUtils.sizeToPixels(root.dateLabelFontSize)
property int dayFontSize: FontUtils.sizeToPixels(root.dayLabelFontSize)
- property int selectedIndex: -1
+ property int highlightedIndex: root.isCurrentItem &&
+ root.highlightedDate ?
+ intern.indexByDate(root.highlightedDate) : -1
+ property int todayIndex: root.isCurrentItem &&
+ isCurMonthTodayMonth ?
+ intern.indexByDate(intern.today) : -1
- function findSelectedDayIndex(){
- if(!selectedDay) {
+ function indexByDate(date){
+ if (!date) {
return -1;
}
- if( todayMonth === selectedDay.getMonth() && selectedDay.getFullYear() === todayYear){
- return selectedDay.getDate() +
+ if ((root.currentMonth === date.getMonth()) &&
+ (root.currentYear === date.getFullYear())) {
+
+ return date.getDate() +
(Date.daysInMonth(monthStartYear, monthStartMonth) - monthStartDate);
} else {
return -1;
@@ -133,14 +106,40 @@
}
}
- onSelectedDayChanged: {
- if( isCurrentItem ) {
- intern.selectedIndex = intern.findSelectedDayIndex();
+ UbuntuShape{
+ id: todayShape
+
+ visible: (monthGrid.todayItem != null)
+ color: (monthGrid.highlightedItem === monthGrid.todayItem) ? UbuntuColors.darkGrey : UbuntuColors.orange
+ width: parent ? Math.min(parent.height, parent.width) / 1.3 : 0
+ height: width
+ parent: monthGrid.todayItem
+ anchors.centerIn: parent
+ z: -1
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: units.gu(0.5)
+ color: UbuntuColors.orange
+ radius: 5
}
}
- onCurrentMonthChanged: {
- intern.selectedIndex = -1;
+ UbuntuShape{
+ id: highlightedShape
+
+ visible: monthGrid.highlightedItem && (monthGrid.highlightedItem != monthGrid.todayItem)
+ color: UbuntuColors.darkGrey
+ width: parent ? Math.min(parent.height, parent.width) / 1.3 : 0
+ height: width
+ parent: monthGrid.highlightedItem
+ anchors.centerIn: parent
+ z: -1
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: units.gu(0.5)
+ color: UbuntuColors.lightGrey
+ radius: 5
+ }
}
Column{
@@ -166,9 +165,9 @@
ViewHeader{
id: monthHeader
anchors.fill: parent
- month: intern.curMonth
- year: intern.curMonthYear
- daysInMonth: intern.daysInCurMonth
+ month: root.currentMonth
+ year: root.currentYear
+ daysInMonth: intern.daysInStartMonth
monthLabelFontSize: root.monthLabelFontSize
yearLabelFontSize: root.yearLabelFontSize
@@ -186,9 +185,11 @@
property int dayWidth: width / 7;
- width: parent.width
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.verticalCenter: parent.verticalCenter
+ anchors{
+ left: parent.left
+ right: parent.right
+ verticalCenter: parent.verticalCenter
+ }
Repeater{
id: dayLabelRepeater
@@ -198,23 +199,75 @@
}
}
- Grid{
+ Grid {
id: monthGrid
objectName: "monthGrid"
- width: parent.width
- height: parent.height - monthGrid.y
-
property int dayWidth: width / 7 /*cols*/;
property int dayHeight: height / 6/*rows*/;
+ readonly property var todayItem: (intern.todayIndex != -1) &&
+ (monthGrid.children.length > intern.todayIndex) ?
+ dateLabelRepeater.itemAt(intern.todayIndex) : null
+ readonly property var highlightedItem: (intern.highlightedIndex != -1) &&
+ (monthGrid.children.length > intern.highlightedIndex) ?
+ dateLabelRepeater.itemAt(intern.highlightedIndex) : null
- rows: 6
+ anchors {
+ left: parent.left
+ right: parent.right
+ }
+ height: parent.height - monthGrid.y
columns: 7
Repeater{
id: dateLabelRepeater
- model: 42 //monthGrid.rows * monthGrid.columns
- delegate: defaultDateLabelComponent
+ model: 42
+ delegate: isYearView ? monthWithoutEventsDelegate : monthWithEventsDelegate
+ }
+ }
+ }
+
+ MouseArea {
+ id: mouseArea
+
+ function getItemAt(x, y)
+ {
+ var clickPosition = mouseArea.mapToItem(monthGrid, x, y)
+ return monthGrid.childAt(clickPosition.x, clickPosition.y)
+ }
+
+ anchors {
+ fill: column
+ topMargin: monthGrid.y
+ }
+
+ onPressAndHold: {
+ var dayItem = getItemAt(mouse.x, mouse.y)
+
+ if( dayItem.isSelected ) {
+ var selectedDate = new Date();
+ selectedDate.setFullYear(intern.monthStartYear)
+ selectedDate.setMonth(intern.monthStartMonth + 1)
+ selectedDate.setDate(dayItem.date)
+ selectedDate.setMinutes(60, 0, 0)
+ pageStack.push(Qt.resolvedUrl("NewEvent.qml"), {"date":selectedDate, "model":eventModel});
+ }
+ }
+ onClicked: {
+ var dayItem = getItemAt(mouse.x, mouse.y)
+ var selectedDate = new Date(intern.monthStartYear,
+ intern.monthStartMonth + 1,
+ dayItem.date, 0, 0, 0, 0)
+ if (root.isYearView) {
+ //If yearView is clicked then open selected MonthView
+ root.monthSelected(selectedDate);
+ } else {
+ if (dayItem.isSelected) {
+ //If monthView is clicked then open selected DayView
+ root.dateSelected(selectedDate);
+ } else {
+ root.dateHighlighted(selectedDate)
+ }
}
}
}
@@ -301,53 +354,9 @@
}
Component{
- id: defaultDateLabelComponent
- MonthComponentDateDelegate{
- date: {
- //try to find date from index and month's first week's first date
- var temp = intern.daysInStartMonth - intern.offset + index
- //date exceeds days in startMonth,
- //this means previous month is over and we are now in current month
- //to get actual date we need to remove number of days in startMonth
- if( temp > intern.daysInStartMonth ) {
- temp = temp - intern.daysInStartMonth
- //date exceeds days in current month
- // this means date is from next month
- //to get actual date we need to remove number of days in current month
- if( temp > intern.daysInCurMonth ) {
- temp = temp - intern.daysInCurMonth
- }
- }
- return temp;
- }
-
- isCurrentMonth: {
- //remove offset from index
- //if index falls in 1 to no of days in current month
- //then date is inside current month
- var temp = index - intern.offset
- return (temp >= 1 && temp <= intern.daysInCurMonth)
- }
-
- isToday: intern.todayDate == date && intern.isCurMonthTodayMonth
-
- isSelected: showEvents && intern.selectedIndex == index
-
- width: parent.dayWidth
- height: parent.dayHeight
- fontSize: intern.dateFontSize
- showLunarCalendar: displayLunarCalendar
- showEvent: showEvents
- && intern.eventStatus !== undefined
- && intern.eventStatus[index] !== undefined
- && intern.eventStatus[index]
- }
- }
-
- Component{
id: dafaultDayLabelComponent
- Label{
+ Text {
id: weekDay
objectName: "weekDay" + index
width: parent.dayWidth
@@ -359,4 +368,48 @@
color: "black"
}
}
+ Component {
+ id: monthWithEventsDelegate
+
+ MonthComponentWithEventsDateDelegate {
+ property var delegateDate: intern.monthStart.addDays(index)
+
+ date: delegateDate.getDate()
+ isCurrentMonth: delegateDate.getMonth() === root.currentMonth
+ showEvent: intern.eventStatus[index] === true
+ lunarData: {
+ if (!root.displayLunarCalendar)
+ return null
+
+ var lunar = Lunar.calendar.solar2lunar(intern.monthStartYear,
+ intern.monthStartMonth + 1,
+ intern.monthStartDate + index)
+ if (lunar.isTerm) {
+ return {"lunarText": lunar.Term, "isTerm" :lunar.isTerm};
+ } else {
+ return {"lunarText": lunar.IDayCn, "isTerm" :lunar.isTerm};
+ }
+ }
+ isToday: intern.todayDate == date && intern.isCurMonthTodayMonth
+ isSelected: intern.highlightedIndex == index
+ width: monthGrid.dayWidth
+ height: monthGrid.dayHeight
+ }
+ }
+
+ Component {
+ id: monthWithoutEventsDelegate
+
+ MonthComponentDateDelegate {
+ property var delegateDate: intern.monthStart.addDays(index)
+
+ date: delegateDate.getDate()
+ isCurrentMonth: delegateDate.getMonth() === root.currentMonth
+
+ isToday: intern.todayDate == date && intern.isCurMonthTodayMonth
+ isSelected: intern.highlightedIndex == index
+ width: monthGrid.dayWidth
+ height: monthGrid.dayHeight
+ }
+ }
}
=== modified file 'MonthComponentDateDelegate.qml'
--- MonthComponentDateDelegate.qml 2016-02-03 08:06:25 +0000
+++ MonthComponentDateDelegate.qml 2016-03-03 21:42:32 +0000
@@ -1,6 +1,5 @@
import QtQuick 2.4
import Ubuntu.Components 1.3
-import "./3rd-party/lunar.js" as Lunar
Item{
id: dateRootItem
@@ -8,30 +7,13 @@
property int date;
property bool isCurrentMonth;
property bool isToday;
- property bool showEvent;
- property bool showLunarCalendar;
property alias fontSize: dateLabel.font.pixelSize
-
property bool isSelected: false
- Loader {
- sourceComponent: (isToday && isCurrentMonth) || isSelected ? highLightComp : undefined
-
- onSourceComponentChanged: {
- width = Qt.binding( function() {
- var width = dateRootItem.height > dateRootItem.width ? dateRootItem.width :dateRootItem.height
- return ( width / 1.3 );
- });
- height = Qt.binding ( function() { return width} );
- anchors.centerIn = Qt.binding( function() { return dateLabel});
- }
- }
-
- Label {
+ Text {
id: dateLabel
anchors.centerIn: parent
text: date
- fontSize: root.dateLabelFontSize
color: {
if( isCurrentMonth ) {
if( isToday || isSelected ) {
@@ -48,125 +30,4 @@
}
}
}
-
- Loader{
- sourceComponent: showLunarCalendar ? reservedFiled : undefined
- onSourceComponentChanged: {
- if (item != undefined) {
- item.reservedData = Qt.binding(function(){
- var lunarDate = Lunar.calendar.solar2lunar(intern.monthStartYear,
- intern.monthStartMonth + 1,
- intern.monthStartDate + index)
- if (lunarDate.isTerm) {
- return {"lunarText": lunarDate.Term, "isTerm" :lunarDate.isTerm};
- } else {
- return {"lunarText": lunarDate.IDayCn, "isTerm" :lunarDate.isTerm};
- }
- })
- }
-
- width = Qt.binding( function() { return units.gu(0.8)})
- height = Qt.binding( function() { return width })
- anchors.horizontalCenter = Qt.binding( function() { return parent.horizontalCenter })
- anchors.top = Qt.binding( function() { return parent.verticalCenter })
- anchors.topMargin = Qt.binding( function() {
- return (dateRootItem.height > dateRootItem.width ? dateRootItem.width :dateRootItem.height)/ 4.0 + units.gu(0.25)
- });
- }
- }
-
- //this component is reserved for extra information display
- Component {
- id: reservedFiled
- Label {
- id: reservedLabel
- property var reservedData
- onReservedDataChanged: {
- text = reservedData.lunarText
- if (reservedData.isTerm)
- color = "red";
- }
-
- horizontalAlignment: Text.AlignHCenter
- fontSize: root.subLabelFontSize
- color: "#5D5D5D"
- }
- }
-
- Loader{
- sourceComponent: showEvent ? eventIndicatorComp : undefined
- onSourceComponentChanged: {
- width = Qt.binding( function() { return units.gu(0.8)})
- height = Qt.binding( function() { return width })
- anchors.horizontalCenter = Qt.binding( function() { return parent.horizontalCenter })
- anchors.top = Qt.binding( function() { return parent.verticalCenter })
- anchors.topMargin = Qt.binding( function() {
- if (showLunarCalendar) {
- return (dateRootItem.height > dateRootItem.width ? dateRootItem.width :dateRootItem.height) / 2 + units.gu(1.5)
- } else {
- var w = (dateRootItem.height > dateRootItem.width ? dateRootItem.width :dateRootItem.height)/1.3
- return (w/2) + units.gu(0.1)
- }
- });
- }
- }
-
- Component{
- id: eventIndicatorComp
- Rectangle {
- anchors.fill: parent
- radius: height/2
- color: "black"
- }
- }
-
- Component{
- id: highLightComp
- UbuntuShape{
- color: {
- if( isToday && !isSelected ) {
- "#DD4814"
- } else {
- "gray"
- }
- }
-
- Rectangle{
- anchors.fill: parent
- anchors.margins: units.gu(0.5)
- color: isToday ? "#DD4814" : "darkgray"
- }
- }
- }
-
- MouseArea {
- anchors.fill: parent
- onPressAndHold: {
- if( isSelected ) {
- var selectedDate = new Date();
- selectedDate.setFullYear(intern.monthStartYear)
- selectedDate.setMonth(intern.monthStartMonth + 1)
- selectedDate.setDate(date)
- selectedDate.setMinutes(60, 0, 0)
- pageStack.push(Qt.resolvedUrl("NewEvent.qml"), {"date":selectedDate, "model":eventModel});
- }
- }
- onClicked: {
- var selectedDate = new Date(intern.monthStartYear,
- intern.monthStartMonth,
- intern.monthStartDate + index, 0, 0, 0, 0)
- if( isYearView ) {
- //If yearView is clicked then open selected MonthView
- root.monthSelected(selectedDate);
- } else {
- if( isSelected ) {
- //If monthView is clicked then open selected DayView
- root.dateSelected(selectedDate);
- } else {
- intern.selectedIndex = index
- root.dateHighlighted(selectedDate)
- }
- }
- }
- }
}
=== added file 'MonthComponentWithEventsDateDelegate.qml'
--- MonthComponentWithEventsDateDelegate.qml 1970-01-01 00:00:00 +0000
+++ MonthComponentWithEventsDateDelegate.qml 2016-03-03 21:42:32 +0000
@@ -0,0 +1,80 @@
+import QtQuick 2.4
+import Ubuntu.Components 1.3
+
+Item{
+ id: dateRootItem
+
+ property int date;
+ property bool isCurrentMonth;
+ property bool isToday;
+ property bool showEvent;
+ property alias fontSize: dateLabel.font.pixelSize
+ property bool isSelected: false
+ property alias lunarData: lunarLabel.lunarData
+
+ Text {
+ id: dateLabel
+ anchors.centerIn: parent
+ text: date
+ color: {
+ if( isCurrentMonth ) {
+ if( isToday || isSelected ) {
+ "white"
+ } else {
+ "#5D5D5D"
+ }
+ } else {
+ if(isSelected) {
+ "white"
+ } else {
+ "#AEA79F"
+ }
+ }
+ }
+ }
+
+ Label {
+ id: lunarLabel
+
+ property var lunarData: null
+
+ text: lunarData ? lunarData.lunarText : ""
+ color: {
+ if (lunarData && lunarData.isTerm) {
+ if (isCurrentMonth && isToday)
+ return "black"
+ else
+ return UbuntuColors.red
+ } else {
+ if (isSelected)
+ return "white"
+ else
+ return "#5D5D5D"
+ }
+ }
+ fontSize: "small"
+ visible: (lunarData != null)
+ horizontalAlignment: Text.AlignHCenter
+ width: parent.width
+ anchors {
+ top: dateLabel.bottom
+ topMargin: units.gu(0.5)
+ }
+ }
+
+ Rectangle {
+ id: eventIndicator
+
+ width: visible ? units.gu(0.8) : 0
+ height: width
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ //top: parent.verticalCenter
+ //topMargin: ((Math.min(parent.height, dateRootItem.width) / 1.3) / 2) + units.gu(0.1)
+ bottom: parent.bottom
+ }
+ radius: height/2
+ color:"black"
+ visible: showEvent
+ }
+}
=== modified file 'MonthView.qml'
--- MonthView.qml 2016-02-03 08:06:25 +0000
+++ MonthView.qml 2016-03-03 21:42:32 +0000
@@ -17,21 +17,31 @@
*/
import QtQuick 2.4
import Ubuntu.Components 1.3
+
import "dateExt.js" as DateExt
import "colorUtils.js" as Color
import "./3rd-party/lunar.js" as Lunar
-Page {
+PageWithBottomEdge {
id: monthViewPage
objectName: "monthViewPage"
- property var currentMonth: DateExt.today();
+ property var anchorDate: DateExt.today();
+ readonly property var firstDayOfAnchorDate: new Date(anchorDate.getFullYear(),
+ anchorDate.getMonth(),
+ 1,
+ 0, 0, 0)
+ readonly property var currentDate: monthViewPath.currentItem.indexDate
+
property var selectedDay;
+ property var highlightedDate;
+ property bool displayLunarCalendar: false
signal dateSelected(var date);
- signal dateHighlighted(var date);
Keys.forwardTo: [monthViewPath]
+ createEventAt: highlightedDate ? highlightedDate : currentDate
+ onAnchorDateChanged: monthViewPath.scrollToBegginer()
Action {
id: calendarTodayAction
@@ -39,7 +49,7 @@
iconName: "calendar-today"
text: i18n.tr("Today")
onTriggered: {
- currentMonth = new Date().midnight()
+ anchorDate = new Date().midnight()
}
}
@@ -49,20 +59,26 @@
leadingActionBar.actions: tabs.tabsAction
trailingActionBar.actions: [
calendarTodayAction,
- commonHeaderActions.newEventAction,
commonHeaderActions.showCalendarAction,
commonHeaderActions.reloadAction,
commonHeaderActions.syncCalendarAction,
commonHeaderActions.settingsAction
]
title: {
- // TRANSLATORS: this is a time formatting string,
- // see http://qt-project.org/doc/qt-5/qml-qtqml-date.html#details for valid expressions.
- // It's used in the header of the month and week views
- var monthName = currentMonth.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy"))
- return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1)
+ if (displayLunarCalendar) {
+ var year = currentDate.getFullYear()
+ var month = currentDate.getMonth()
+ var day = Math.floor(Date.daysInMonth(year, month) / 2.0)
+ var lunarDate = Lunar.calendar.solar2lunar(year, month + 1, day)
+ return i18n.tr("%1 %2").arg(lunarDate .IMonthCn).arg(lunarDate.gzYear)
+ } else {
+ // TRANSLATORS: this is a time formatting string,
+ // see http://qt-project.org/doc/qt-5/qml-qtqml-date.html#details for valid expressions.
+ // It's used in the header of the month and week views
+ var monthName = currentDate.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy"))
+ return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1)
+ }
}
-
flickable: null
}
@@ -70,85 +86,33 @@
id: monthViewPath
objectName: "monthViewPath"
- property var startMonth: currentMonth;
-
anchors {
fill: parent
topMargin: header.height
}
- onNextItemHighlighted: {
- nextMonth();
- }
-
- onPreviousItemHighlighted: {
- previousMonth();
- }
-
- function nextMonth() {
- currentMonth = addMonth(currentMonth, 1);
- }
-
- function previousMonth() {
- currentMonth = addMonth(currentMonth, -1);
- }
-
- function addMonth(date,month) {
- return new Date(date.getFullYear(), date.getMonth() + month, 1, 0, 0, 0);
- }
-
- delegate: Loader {
+ delegate: MonthWithEventsComponent {
+ property var indexDate: firstDayOfAnchorDate.addMonths(monthViewPath.loopCurrentIndex + monthViewPath.indexType(index))
+
+ currentMonth: indexDate.getMonth()
+ currentYear: indexDate.getFullYear()
+ displayLunarCalendar: monthViewPage.displayLunarCalendar
+
+ modelFilter: eventModel.filter
width: parent.width - units.gu(4)
height: parent.height
-
- sourceComponent: delegateComponent
- asynchronous: index !== monthViewPath.currentIndex
-
- Component {
- id: delegateComponent
-
- MonthComponent {
- isCurrentItem: index === monthViewPath.currentIndex
-
- showEvents: true
-
- displayWeekNumber: mainView.displayWeekNumber
- displayLunarCalendar: mainView.displayLunarCalendar
- anchors.fill: parent
-
- currentMonth: monthViewPath.addMonth(monthViewPath.startMonth,
- monthViewPath.indexType(index));
-
- selectedDay: monthViewPage.selectedDay
- isYearView: false
-
- onDateSelected: {
- monthViewPage.dateSelected(date);
- }
-
- onDateHighlighted: {
- monthViewPage.dateHighlighted(date);
- }
- }
+ isCurrentItem: PathView.isCurrentItem
+ isActive: !monthViewPath.moving && !monthViewPath.flicking
+ displayWeekNumber: mainView.displayWeekNumber
+ highlightedDate: monthViewPage.highlightedDate
+ isYearView: false
+
+ onDateSelected: {
+ monthViewPage.dateSelected(date);
+ }
+ onDateHighlighted: {
+ monthViewPage.highlightedDate = date
}
}
}
-
- Component.onCompleted: {
- pageHeader.title = Qt.binding(function(){
- if(mainView.displayLunarCalendar){
- var year = currentMonth.getFullYear()
- var month = currentMonth.getMonth()
- var day = Math.floor(Date.daysInMonth(year, month) / 2.0)
- var lunarDate = Lunar.calendar.solar2lunar( year, month + 1, day)
- return i18n.tr("%1 %2").arg(lunarDate .IMonthCn).arg(lunarDate.gzYear)
- } else {
- // TRANSLATORS: this is a time formatting string,
- // see http://qt-project.org/doc/qt-5/qml-qtqml-date.html#details for valid expressions.
- // It's used in the header of the month and week views
- var monthName = currentMonth.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy"))
- return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1)
- }
- })
- }
}
=== added file 'MonthWithEventsComponent.qml'
--- MonthWithEventsComponent.qml 1970-01-01 00:00:00 +0000
+++ MonthWithEventsComponent.qml 2016-03-03 21:42:32 +0000
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2013-2014 Canonical Ltd
+ *
+ * This file is part of Ubuntu Calendar App
+ *
+ * Ubuntu Calendar App is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Ubuntu Calendar App 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 Ubuntu.Components 1.3
+import QtOrganizer 5.0
+import "dateExt.js" as DateExt
+import "colorUtils.js" as Color
+
+MonthComponent {
+ id: root
+ objectName: "MonthComponent"
+
+ property bool isActive: false
+ property var modelFilter: invalidFilter
+
+ onIsActiveChanged: {
+ if (isActive && (mainModel.filter === invalidFilter)) {
+ idleRefresh.reset()
+ }
+ }
+
+ Timer {
+ id: idleRefresh
+
+ function reset()
+ {
+ mainModel.filter = invalidFilter
+ restart()
+ }
+
+ interval: root.isCurrentItem ? 1000 : 2000
+ repeat: false
+ onTriggered: {
+ mainModel.filter = Qt.binding(function() { return root.modelFilter } )
+ }
+ }
+
+ InvalidFilter {
+ id: invalidFilter
+ }
+
+ EventListModel {
+ id: mainModel
+
+ startPeriod: root.monthStartDate.midnight();
+ endPeriod: root.monthStartDate.addDays((/*monthGrid.rows * cols */ 42 )-1).endOfDay()
+ filter: invalidFilter
+ fetchHint: FetchHint {
+ detailTypesHint: [ Detail.EventTime,
+ Detail.JournalTime,
+ Detail.TodoTime
+ ]
+ }
+
+ onModelChanged: {
+ var eventStatus = mainModel.containsItems(startPeriod,
+ endPeriod,
+ 86400/*24*60*60*/);
+ root.updateEvents(eventStatus)
+ }
+
+ onStartPeriodChanged: idleRefresh.reset()
+ onEndPeriodChanged: idleRefresh.reset()
+ }
+}
=== modified file 'NewEvent.qml'
--- NewEvent.qml 2016-02-02 22:54:03 +0000
+++ NewEvent.qml 2016-03-03 21:42:32 +0000
@@ -20,7 +20,7 @@
import QtOrganizer 5.0
import Ubuntu.Components 1.3
import Ubuntu.Components.Popups 1.0
-import Ubuntu.Components.ListItems 1.0 as ListItem
+import Ubuntu.Components.ListItems 1.0 as ListItems
import Ubuntu.Components.Themes.Ambiance 1.0
import Ubuntu.Components.Pickers 1.0
import QtOrganizer 5.0
@@ -31,10 +31,11 @@
objectName: 'newEventPage'
property var date;
+ property alias allDay: allDayEventCheckbox.checked
property var event:null;
property var rule :null;
- property var model;
+ property var model:null;
property var startDate;
property var endDate;
@@ -42,56 +43,44 @@
property alias scrollY: flickable.contentY
property bool isEdit: false
- flickable: null
-
signal eventAdded(var event);
signal eventDeleted(var event);
-
- onStartDateChanged: {
- startDateTimeInput.dateTime = startDate;
-
- // set time forward to one hour
- var time_forward = 3600000;
-
- if (isEdit && event !== null) {
- time_forward = event.endDateTime - event.startDateTime;
- }
- adjustEndDateToStartDate(time_forward);
- }
-
- onEndDateChanged: {
- endDateTimeInput.dateTime = endDate;
- }
-
- head.actions: [
- Action {
- text: i18n.tr("Delete");
- objectName: "delete"
- iconName: "delete"
- visible : isEdit
- onTriggered: {
- var dialog = PopupUtils.open(Qt.resolvedUrl("DeleteConfirmationDialog.qml"),root,{"event": event});
- dialog.deleteEvent.connect( function(eventId){
- model.removeItem(eventId);
- pageStack.pop();
- root.eventDeleted(eventId);
- });
- }
- },
- Action {
- iconName: "ok"
- objectName: "save"
- text: i18n.tr("Save")
- enabled: !!titleEdit.text.trim()
- onTriggered: saveToQtPim();
- }]
+ signal canceled()
+
Component.onCompleted: {
- //If current date is setted by an argument we don't have to change it.
- if(typeof(date) === 'undefined'){
+ setDate(root.date)
+
+ if (event === undefined) {
+ return
+ } else if(event === null){
+ isEdit = false;
+ addEvent();
+ } else{
+ isEdit = true;
+ editEvent(event);
+ }
+ }
+
+ function cancel()
+ {
+ if (pageStack)
+ pageStack.pop();
+ root.canceled()
+ }
+
+ function updateEventDate(date, allDay) {
+ root.startDate = undefined
+ root.endDate = undefined
+ setDate(date)
+ root.allDay = allDay
+ }
+
+ function setDate(date) {
+ if ((typeof(date) === 'undefined') || (date === null)) {
date = new Date();
}
- if( typeof(date) == 'undefined' || (date.getHours() == 0 && date.getMinutes() == 0) ) {
+ if(date.getHours() === 0 && date.getMinutes() === 0) {
var newDate = new Date();
date.setHours(newDate.getHours(), newDate.getMinutes());
}
@@ -108,15 +97,6 @@
endTimeInput.text = Qt.formatDateTime(endDate, Qt.locale().timeFormat(Locale.ShortFormat));
}
- if(event === null){
- isEdit = false;
- addEvent();
- }
-
- else{
- isEdit = true;
- editEvent(event);
- }
}
function selectCalendar(collectionId) {
@@ -146,14 +126,14 @@
}
startDate =new Date(e.startDateTime);
- endDate = new Date(e.endDateTime);
if(e.displayLabel) {
titleEdit.text = e.displayLabel;
}
- if(e.allDay){
- allDayEventCheckbox.checked =true;
- endDate = endDate.addDays(-1);
+
+ if (e.allDay) {
+ allDayEventCheckbox.checked = true
+ endDate = new Date(e.endDateTime).addDays(-1);
}
if(e.location) {
@@ -170,8 +150,7 @@
if( e.itemType === Type.Event ) {
if(e.attendees){
for( var j = 0 ; j < e.attendees.length ; ++j ) {
- contactList.array.push(e.attendees[j]);
- contactModel.append(e.attendees[j]);
+ contactModel.append({"contact": e.attendees[j]});
}
}
}
@@ -202,11 +181,11 @@
}
event.allDay = allDayEventCheckbox.checked;
- event.startDateTime = startDate;
-
if (event.allDay){
- event.endDateTime = endDate.addDays(1);
+ event.startDateTime = new Date(startDate).midnight()
+ event.endDateTime = new Date(endDate).addDays(1).midnight()
} else {
+ event.startDateTime = startDate;
event.endDateTime = endDate;
}
@@ -215,13 +194,14 @@
event.location = locationEdit.text
if( event.itemType === Type.Event ) {
- event.attendees = []; // if Edit remove all attendes & add them again if any
- var contacts = [];
- for(var i=0; i < contactList.array.length ; ++i) {
- var contact = contactList.array[i]
- contacts.push(contact);
+ var newContacts = []
+ for(var i=0; i < contactModel.count ; ++i) {
+ var contact = contactModel.get(i).contact
+ if (contact) {
+ newContacts.push(internal.attendeeFromData(contact.attendeeId, contact.name, contact.emailAddress));
+ }
}
- event.attendees = contacts;
+ event.attendees = newContacts;
}
//Set the Rule object to an event
@@ -253,9 +233,9 @@
event.removeDetail(comment);
}
- model.saveItem(event);
- pageStack.pop();
-
+ model.saveItem(event)
+ if (pageStack)
+ pageStack.pop();
root.eventAdded(event);
}
}
@@ -294,10 +274,15 @@
function roundDate(date) {
var tempDate = new Date(date)
tempDate.setHours(date.getHours(), date.getMinutes(), 0, 0);
- if(tempDate.getMinutes() < 30)
+ var tempMinutes = tempDate.getMinutes()
+ if (tempMinutes === 0) {
+ return tempDate
+ } else if(tempMinutes < 30) {
return tempDate.setMinutes(30)
- tempDate.setMinutes(0)
- return tempDate.setHours(tempDate.getHours() + 1)
+ } else {
+ tempDate.setMinutes(0)
+ return tempDate.setHours(tempDate.getHours() + 1)
+ }
}
function adjustEndDateToStartDate(time_forward) {
@@ -322,10 +307,65 @@
scrollAnimation.start()
}
- title: isEdit ? i18n.tr("Edit Event"):i18n.tr("New Event")
-
- Keys.onEscapePressed: {
- pageStack.pop();
+ Keys.onEscapePressed: root.cancel()
+ onStartDateChanged: {
+ if (!startDate)
+ return
+
+ startDateTimeInput.dateTime = startDate;
+
+ // set time forward to one hour
+ var time_forward = 3600000;
+
+ if (isEdit && event !== null) {
+ time_forward = event.endDateTime - event.startDateTime;
+ }
+ adjustEndDateToStartDate(time_forward);
+ }
+
+ onEndDateChanged: {
+ if (endDate)
+ endDateTimeInput.dateTime = endDate;
+ }
+
+ header: PageHeader {
+ id: pageHeader
+
+ flickable: null
+ title: isEdit ? i18n.tr("Edit Event"):i18n.tr("New Event")
+ leadingActionBar.actions: Action {
+ id: backAction
+
+ name: "cancel"
+ text: i18n.tr("Cancel")
+ iconName: isEdit ? "back" : "down"
+ onTriggered: root.cancel()
+ }
+
+ trailingActionBar.actions: [
+ Action {
+ text: i18n.tr("Delete");
+ objectName: "delete"
+ iconName: "delete"
+ visible : isEdit
+ onTriggered: {
+ var dialog = PopupUtils.open(Qt.resolvedUrl("DeleteConfirmationDialog.qml"),root,{"event": event});
+ dialog.deleteEvent.connect( function(eventId){
+ model.removeItem(eventId);
+ if (pageStack)
+ pageStack.pop();
+ root.eventDeleted(eventId);
+ });
+ }
+ },
+ Action {
+ iconName: "ok"
+ objectName: "save"
+ text: i18n.tr("Save")
+ enabled: !!titleEdit.text.trim()
+ onTriggered: saveToQtPim();
+ }
+ ]
}
Component{
@@ -376,9 +416,16 @@
flickable.returnToBounds()
}
- anchors.fill: parent
+ flickableDirection: Flickable.VerticalFlick
+ anchors{
+ left: parent.left
+ top: parent.top
+ topMargin: pageHeader.height
+ right: parent.right
+ bottom: keyboardRectangle.top
+ }
contentWidth: width
- contentHeight: column.height + units.gu(10)
+ contentHeight: column.height
Column {
id: column
@@ -411,7 +458,7 @@
}
}
- ListItem.Standard {
+ ListItems.Standard {
anchors {
left: parent.left
right: parent.right
@@ -426,13 +473,13 @@
}
}
- ListItem.ThinDivider {}
+ ListItems.ThinDivider {}
Column {
width: parent.width
spacing: units.gu(1)
- ListItem.Header{
+ ListItems.Header{
text: i18n.tr("Event Details")
}
@@ -446,6 +493,7 @@
margins: units.gu(2)
}
+ inputMethodHints: Qt.ImhNoPredictiveText
placeholderText: i18n.tr("Event Name")
onFocusChanged: {
if(titleEdit.focus) {
@@ -482,6 +530,7 @@
margins: units.gu(2)
}
+ inputMethodHints: Qt.ImhNoPredictiveText
placeholderText: i18n.tr("Location")
onFocusChanged: {
@@ -496,7 +545,7 @@
width: parent.width
spacing: units.gu(1)
- ListItem.Header {
+ ListItems.Header {
text: i18n.tr("Calendar")
}
@@ -521,9 +570,11 @@
width: height
height: parent.height - units.gu(2)
color: modelData.color
- anchors.right: parent.right
- anchors.rightMargin: units.gu(2)
- anchors.verticalCenter: parent.verticalCenter
+ anchors {
+ right: parent.right
+ rightMargin: units.gu(4)
+ verticalCenter: parent.verticalCenter
+ }
}
}
onExpandedChanged: Qt.inputMethod.hide();
@@ -534,14 +585,17 @@
width: parent.width
spacing: units.gu(1)
- ListItem.Header {
+ ListItems.Header {
text: i18n.tr("Guests")
}
Button{
+ id: addGuestButton
+ objectName: "addGuestButton"
+
+ property var contactsPopup: null
+
text: i18n.tr("Add Guest")
- objectName: "addGuestButton"
-
anchors {
left: parent.left
right: parent.right
@@ -549,14 +603,21 @@
}
onClicked: {
- var popup = PopupUtils.open(Qt.resolvedUrl("ContactChoicePopup.qml"), contactList);
- popup.contactSelected.connect( function(contact) {
- var t = internal.contactToAttendee(contact);
- if( !internal.isContactAlreadyAdded(contact) ) {
- contactModel.append(t);
- contactList.array.push(t);
+ if (contactsPopup)
+ return
+
+ flickable.makeMeVisible(addGuestButton)
+ contactsPopup = PopupUtils.open(Qt.resolvedUrl("ContactChoicePopup.qml"), addGuestButton);
+ contactsPopup.contactSelected.connect( function(contact, emailAddress) {
+ if(!internal.isContactAlreadyAdded(contact, emailAddress) ) {
+ var t = internal.contactToAttendee(contact, emailAddress);
+ contactModel.append({"contact": t});
}
+
});
+ contactsPopup.Component.onDestruction.connect( function() {
+ addGuestButton.contactsPopup = null
+ })
}
}
@@ -577,35 +638,40 @@
width: parent.width
clip: true
- property var array: []
-
ListModel{
id: contactModel
}
Repeater{
model: contactModel
- delegate: ListItem.Standard {
+ delegate: ListItem {
objectName: "eventGuest%1".arg(index)
- height: units.gu(4)
- text: name
- removable: true
- onItemRemoved: {
- contactList.array.splice(index, 1)
- contactModel.remove(index)
+
+ ListItemLayout {
+ title.text: contact.name
+ subtitle.text: contact.emailAddress
+ }
+
+ leadingActions: ListItemActions {
+ actions: Action {
+ iconName: "delete"
+ onTriggered: {
+ contactModel.remove(index)
+ }
+ }
}
}
}
}
}
- ListItem.ThinDivider {
- visible: event.itemType === Type.Event
+ ListItems.ThinDivider {
+ visible: (event != undefined) && (event.itemType === Type.Event)
}
}
- ListItem.Subtitled{
+ ListItems.Subtitled{
id:thisHappens
objectName :"thisHappens"
@@ -615,17 +681,17 @@
showDivider: false
progression: true
- visible: event.itemType === Type.Event
+ visible: (event != undefined) && (event.itemType === Type.Event)
text: i18n.tr("Repeats")
- subText: event.itemType === Type.Event ? rule === null ? Defines.recurrenceLabel[0] : eventUtils.getRecurrenceString(rule) : ""
+ subText: (event != undefined) && (event.itemType === Type.Event) ? rule === null ? Defines.recurrenceLabel[0] : eventUtils.getRecurrenceString(rule) : ""
onClicked: pageStack.push(Qt.resolvedUrl("EventRepetition.qml"),{"eventRoot": root,"isEdit":isEdit});
}
- ListItem.ThinDivider {
- visible: event.itemType === Type.Event
+ ListItems.ThinDivider {
+ visible: (event != undefined) && (event.itemType === Type.Event)
}
- ListItem.Subtitled{
+ ListItems.Subtitled{
id:eventReminder
objectName : "eventReminder"
@@ -658,22 +724,42 @@
"eventTitle": titleEdit.text})
}
- ListItem.ThinDivider {}
+ ListItems.ThinDivider {}
}
}
+
// used to keep the field visible when the keyboard appear or dismiss
KeyboardRectangle {
- id: keyboard
-
- onHeightChanged: {
- if (flickable.activeItem) {
- flickable.makeMeVisible(flickable.activeItem)
+ id: keyboardRectangle
+
+ anchors {
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+ }
+
+ Behavior on height {
+ SequentialAnimation {
+ PauseAnimation { duration: 200 }
+ ScriptAction {
+ script: {
+ if (addGuestButton.contactsPopup) {
+ // WORKAROUND: causes the popover to follow the buttom position when keyboard appears
+ flickable.makeMeVisible(addGuestButton)
+ addGuestButton.contactsPopup.caller = null
+ addGuestButton.contactsPopup.caller = addGuestButton
+ } else {
+ flickable.makeMeVisible(flickable.activeItem)
+ }
+ }
+ }
}
}
}
QtObject {
id: internal
+
property var collectionId;
function clearFocus() {
@@ -685,22 +771,33 @@
messageEdit.focus = false
}
- function isContactAlreadyAdded(contact) {
- for(var i=0; i < contactList.array.length ; ++i) {
- var attendee = contactList.array[i];
- if( attendee.attendeeId === contact.contactId) {
- return true;
+ function isContactAlreadyAdded(contact, emailAddress) {
+ for(var i=0; i < contactModel.count; i++) {
+ var attendee = contactModel.get(i).contact;
+ if (attendee && (emailAddress.length > 0)) {
+ if (attendee.emailAddress === emailAddress) {
+ return true;
+ }
+ } else {
+ if (attendee.attendeeId === contact.contactId) {
+ return true
+ }
}
}
return false;
}
- function contactToAttendee(contact) {
- var attendee = Qt.createQmlObject("import QtOrganizer 5.0; EventAttendee{}", event, "NewEvent.qml");
- attendee.name = contact.displayLabel.label
- attendee.emailAddress = contact.email.emailAddress;
- attendee.attendeeId = contact.contactId;
+ function attendeeFromData(id, name, emailAddress)
+ {
+ var attendee = Qt.createQmlObject("import QtOrganizer 5.0; EventAttendee{}", internal, "NewEvent.qml");
+ attendee.name = name
+ attendee.emailAddress = emailAddress
+ attendee.attendeeId = id
return attendee;
}
+
+ function contactToAttendee(contact, emailAddress) {
+ return attendeeFromData(contact.contactId, contact.displayLabel.label, emailAddress)
+ }
}
}
=== added file 'NewEventBottomEdge.qml'
--- NewEventBottomEdge.qml 1970-01-01 00:00:00 +0000
+++ NewEventBottomEdge.qml 2016-03-03 21:42:32 +0000
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2013-2016 Canonical Ltd
+ *
+ * This file is part of Ubuntu Calendar App
+ *
+ * Ubuntu Calendar App is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Ubuntu Calendar App 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 Ubuntu.Components 1.3
+
+BottomEdge {
+ id: bottomEdge
+ objectName: "bottomEdge"
+
+ property var pageStack: null
+ property var eventModel: null
+ property var date: new Date()
+
+ // WORKAROUND: BottomEdge component loads the page async while draging it
+ // this cause a very bad visual.
+ // To avoid that we create it as soon as the component is ready and keep
+ // it invisible until the user start to drag it.
+ property var _realPage: null
+
+ signal opened()
+ signal eventCreated(var event)
+
+ function updateNewEventDate(date, allDay)
+ {
+ _realPage.updateEventDate(date, allDay)
+ }
+
+ hint {
+ visible: bottomEdge.enabled
+ enabled: visible
+ action: Action {
+ objectName: "neweventbutton"
+ name: "neweventbutton"
+
+ iconName: "new-event"
+ text: i18n.tr("New Event")
+ shortcut: "ctrl+n"
+ enabled: bottomEdge.enabled
+ onTriggered: bottomEdge.commit()
+ }
+ }
+
+ contentComponent: Item {
+ id: pageContent
+
+ implicitWidth: bottomEdge.width
+ implicitHeight: bottomEdge.height
+ children: bottomEdge._realPage
+ Component.onDestruction: {
+ if (bottomEdge._realPage) {
+ bottomEdge._realPage.destroy()
+ bottomEdge._realPage = null
+ _realPage = editorPageBottomEdge.createObject(null)
+ }
+ }
+ }
+
+ onCommitStarted: {
+ bottomEdge.opened()
+ updateNewEventDate(bottomEdge.date ? bottomEdge.date : new Date(), false)
+ }
+
+ Component.onCompleted: {
+ if (eventModel)
+ _realPage = editorPageBottomEdge.createObject(null)
+ }
+
+ onEventModelChanged: {
+ if (eventModel)
+ _realPage = editorPageBottomEdge.createObject(null)
+ }
+
+ Component {
+ id: editorPageBottomEdge
+ NewEvent {
+ id: newEventPage
+
+ implicitWidth: bottomEdge.width
+ implicitHeight: bottomEdge.height
+ model: bottomEdge.eventModel
+ date: bottomEdge.date
+ enabled: bottomEdge.status === BottomEdge.Committed
+ active: bottomEdge.status === BottomEdge.Committed
+ visible: (bottomEdge.status !== BottomEdge.Hidden)
+ onCanceled: bottomEdge.collapse()
+ onEventAdded: {
+ bottomEdge.collapse()
+ bottomEdge.eventCreated(event)
+ }
+ }
+ }
+
+ Component.onDestruction: {
+ if (bottomEdge._realPage) {
+ bottomEdge._realPage.destroy()
+ bottomEdge._realPage = null
+ }
+ }
+}
=== added file 'PageWithBottomEdge.qml'
--- PageWithBottomEdge.qml 1970-01-01 00:00:00 +0000
+++ PageWithBottomEdge.qml 2016-03-03 21:42:32 +0000
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2013-2016 Canonical Ltd
+ *
+ * This file is part of Ubuntu Calendar App
+ *
+ * Ubuntu Calendar App is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Ubuntu Calendar App 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 Ubuntu.Components 1.3
+
+Page {
+ id: root
+
+ property alias model: bottomEdge.eventModel
+ property alias createEventAt: bottomEdge.date
+ property bool bootomEdgeEnabled: bottomEdge.enabled
+
+ signal bottomEdgeCommitStarted()
+ signal eventCreated(var event)
+
+ function bottomEdgeCommit(date, allDay)
+ {
+ bottomEdge.commit()
+ bottomEdge.updateNewEventDate(date, allDay)
+ }
+
+ NewEventBottomEdge {
+ id: bottomEdge
+
+ pageStack: tabs
+ onOpened: root.bottomEdgeCommitStarted()
+ onEventCreated: root.eventCreated(event)
+ }
+}
=== modified file 'PathViewBase.qml'
--- PathViewBase.qml 2016-01-25 13:20:32 +0000
+++ PathViewBase.qml 2016-03-03 21:42:32 +0000
@@ -20,8 +20,7 @@
PathView {
id: root
- model: 3
- snapMode: PathView.SnapOneItem
+ readonly property alias loopCurrentIndex: intern.loopCurrentIndex
signal nextItemHighlighted();
signal previousItemHighlighted();
@@ -29,6 +28,8 @@
signal scrollUp();
signal scrollDown();
+ model: 3
+ snapMode: PathView.SnapOneItem
preferredHighlightBegin: 0.5
preferredHighlightEnd: 0.5
@@ -68,6 +69,11 @@
}
}
+ function scrollToBegginer()
+ {
+ intern.loopCurrentIndex = intern.previousIndex = currentIndex = 0
+ }
+
Keys.onLeftPressed:{
root.decrementCurrentIndex();
}
@@ -98,15 +104,17 @@
intern.previousIndex = currentIndex
if ( diff > 0 ) {
- root.nextItemHighlighted();
+ intern.loopCurrentIndex++
}
else {
- root.previousItemHighlighted();
+ intern.loopCurrentIndex--
}
}
QtObject{
id: intern
+
+ property int loopCurrentIndex: 0
property int previousIndex: root.currentIndex
}
}
=== modified file 'TimeLineBase.qml'
--- TimeLineBase.qml 2016-02-03 08:01:25 +0000
+++ TimeLineBase.qml 2016-03-03 21:42:32 +0000
@@ -1,4 +1,4 @@
-/*
+ /*
* Copyright (C) 2013-2014 Canonical Ltd
*
* This file is part of Ubuntu Calendar App
@@ -20,36 +20,36 @@
import QtOrganizer 5.0
import "dateExt.js" as DateExt
+import "calendar_canvas.js" as CanlendarCanvas
Item {
id: bubbleOverLay
property var delegate;
property var day;
- property int hourHeight: units.gu(8)
- property var model;
-
- Component.onCompleted: {
- bubbleOverLay.createEvents();
+
+ property alias model: modelConnections.target
+ property var flickable: null
+ readonly property alias creatingEvent: overlayMouseArea.creatingEvent
+ readonly property real hourHeight: units.gu(8)
+ readonly property real minuteHeight: (hourHeight / 60)
+
+ signal pressAndHoldAt(var date)
+
+ function waitForModelChange()
+ {
+ intern.waitingForModelChange = true
}
- MouseArea {
- anchors.fill: parent
- objectName: "mouseArea"
-
- onPressAndHold: {
- var selectedDate = new Date(day);
- var hour = parseInt(mouseY / hourHeight);
- selectedDate.setHours(hour)
- createOrganizerEvent(selectedDate);
- }
-
- onPressed: {
- intern.now = new Date();
- if( intern.now.isSameDay( bubbleOverLay.day ) ) {
- bubbleOverLay.showSeparator();
- }
- }
+ function createOrganizerEvent( startDate ) {
+ var event = Qt.createQmlObject("import QtOrganizer 5.0; Event {}", Qt.application,"TimeLineBase.qml");
+ event.collectionId = (model.defaultCollection().collectionId);
+ var endDate = new Date( startDate.getTime() + 3600000 );
+ event.startDateTime = startDate;
+ event.endDateTime = endDate;
+ event.displayLabel = i18n.tr("New event");
+ event.setDetail(Qt.createQmlObject("import QtOrganizer 5.0; Comment{ comment: 'X-CAL-DEFAULT-EVENT'}", event,"TimeLineBase.qml"));
+ return event
}
function getTimeFromYPos(y, day) {
@@ -64,32 +64,6 @@
return date;
}
- function createOrganizerEvent( startDate ) {
- var event = Qt.createQmlObject("import QtOrganizer 5.0; Event {}", Qt.application,"TimeLineBase.qml");
- event.collectionId = (model.defaultCollection().collectionId);
- var endDate = new Date( startDate.getTime() + 3600000 );
- event.startDateTime = startDate;
- event.endDateTime = endDate;
- event.displayLabel = i18n.tr("Untitled");
- event.setDetail(Qt.createQmlObject("import QtOrganizer 5.0; Comment{ comment: 'X-CAL-DEFAULT-EVENT'}", event,"TimeLineBase.qml"));
- model.saveItem(event);
- }
-
- TimeSeparator {
- id: separator
- objectName: "separator"
- width: bubbleOverLay.width
- visible: false
- z:10
- }
-
- QtObject {
- id: intern
- property var now : new Date();
- property var eventMap;
- property var unUsedEvents: new Object();
- }
-
function showEventDetails(event) {
var comment = event.detail(Detail.Comment);
if(comment && comment.comment === "X-CAL-DEFAULT-EVENT") {
@@ -101,167 +75,279 @@
WorkerScript {
id: eventLayoutHelper
- source: "EventLayoutHelper.js"
+ source: "calendar_canvas_worker.js"
onMessage: {
- layoutEvents(messageObject.schedules,messageObject.maxDepth);
- }
- }
-
- function layoutEvents(array, depth) {
- for(var i=0; i < array.length ; ++i) {
- var schedule = array[i];
- var event = intern.eventMap[schedule.id];
- bubbleOverLay.createEvent(event , schedule.depth, depth +1);
- }
+ // check if anything changed during the process
+ if (intern.dirty) {
+ console.debug("Something has changed while work script was running, ignore message")
+ } else {
+ var events = messageObject.reply
+ var dirty = false
+ for (var i=0; i < events.length; i++) {
+ var e = intern.eventsById[events[i].eventId]
+ if (e.eventId != events[i].itemId) {
+ console.warn("Event does not match id:", i)
+ dirty = true
+ }
+ if (e.startDateTime.getTime() != events[i].eventStartTime) {
+ console.warn("Event does not match start time")
+ dirty = true
+ }
+ if (e.endDateTime.getTime() != events[i].eventEndTime) {
+ console.warn("Event does not match end time")
+ dirty = true
+ }
+
+ if (dirty) {
+ console.warn("Mark as dirty")
+ intern.dirty = true
+ break
+ }
+
+ createVisual(events[i])
+ }
+ }
+ intern.busy = false
+ intern.eventsById = {}
+ if (intern.dirty) {
+ bubbleOverLay.idleCreateEvents()
+ }
+ }
+ }
+
+ function createVisual(eventInfo)
+ {
+ var eventBubble;
+ if (intern.unUsedEvents.length === 0) {
+ var incubator = delegate.incubateObject(bubbleOverLay)
+ incubator.forceCompletion()
+ eventBubble = incubator.object
+ } else {
+ eventBubble = getUnusedEventBubble();
+ }
+
+ var eventWidth = (bubbleOverLay.width * eventInfo.width)
+
+ eventBubble.anchorDate = bubbleOverLay.day
+ eventBubble.minuteHeight = bubbleOverLay.minuteHeight
+ eventBubble.sizeOfRow = eventInfo.width
+ eventBubble.depthInRow = eventInfo.y
+ eventBubble.model = bubbleOverLay.model
+ eventBubble.event = intern.eventsById[eventInfo.eventId]
+ eventBubble.resize()
+ eventBubble.visible = true
+ eventBubble.clicked.connect( bubbleOverLay.showEventDetails );
+ }
+
+ function idleCreateEvents() {
+ createEventsTimer.restart()
}
function createEvents() {
if(!bubbleOverLay || bubbleOverLay == undefined || model === undefined || model === null) {
- return;
- }
-
+ console.debug("\tabort.")
+ return;
+ }
+
+ // check if there is any update in progress
+ if (intern.busy) {
+ console.debug("Work script still busy, postpone update")
+ // mark as dirsty to triggere a new update after the message arrives
+ intern.dirty = true
+ return;
+ }
+
+ intern.busy = true
+ intern.dirty = false
destroyAllChildren();
-
- var eventMap = {};
- var allSchs = [];
-
- var startDate = new Date(day).midnight();
- var endDate = new Date(day).endOfDay();
- var items = model.getItems(startDate,endDate);
- for(var i = 0; i < items.length; ++i) {
- var event = items[i];
-
- if(event.allDay) {
- continue;
- }
-
- var schedule = {"startDateTime": event.startDateTime, "endDateTime": event.endDateTime,"id":event.itemId };
- allSchs.push(schedule);
- eventMap[event.itemId] = event;
- }
-
- intern.eventMap = eventMap;
- eventLayoutHelper.sendMessage(allSchs);
-
- if( intern.now.isSameDay( bubbleOverLay.day ) ) {
+ intern.eventsById = {}
+
+ var startDate = day.midnight()
+ var itemsOfTheDay = model.itemsByTimePeriod(startDate, startDate.endOfDay())
+ if (itemsOfTheDay.length === 0) {
bubbleOverLay.showSeparator();
- }
+ intern.busy = false
+ return
+ }
+
+ for(var i=0; i < itemsOfTheDay.length; i++) {
+ var e = itemsOfTheDay[i]
+ intern.eventsById[e.itemId] = e
+ }
+
+ var eventInfo = CanlendarCanvas.parseDayEvents(startDate, itemsOfTheDay)
+ eventLayoutHelper.sendMessage({'events': eventInfo})
+ bubbleOverLay.showSeparator();
}
function destroyAllChildren() {
- for( var i = children.length - 1; i >= 0; --i ) {
- if( children[i].objectName === "mouseArea" ||
- children[i].objectName === "weekdevider") {
+ separator.visible = false
+ for(var i=0; i < children.length; i++) {
+ var child = children[i]
+ if (!child.isEventBubble) {
continue;
}
- children[i].visible = false;
- if( children[i].objectName !== "separator") {
- children[i].clicked.disconnect( bubbleOverLay.showEventDetails );
- var key = children[i].objectName;
- if (intern.unUsedEvents[key] === "undefined") {
- intern.unUsedEvents[key] = children[i];
- }
+ if (intern.unUsedEvents.indexOf(child) === -1) {
+ child.event = null
+ child.visible = false;
+ child.clicked.disconnect(bubbleOverLay.showEventDetails);
+ intern.unUsedEvents.push(child)
}
}
}
- function isHashEmpty(hash) {
- for (var prop in hash) {
- if (prop)
- return false;
- }
- return true;
- }
-
- function getAKeyFromHash(hash) {
- for (var prop in hash) {
- return prop;
- }
- return "undefined";
- }
-
function getUnusedEventBubble() {
- /* Recycle an item from unUsedEvents, and remove from hash */
- var key = getAKeyFromHash(intern.unUsedEvents);
- var unUsedBubble = intern.unUsedEvents[key];
- delete intern.unUsedEvents[key];
-
- return unUsedBubble;
- }
-
- function createEvent( event, depth, sizeOfRow ) {
- var eventBubble;
- if( isHashEmpty(intern.unUsedEvents) ) {
- var incubator = delegate.incubateObject(bubbleOverLay);
- if (incubator.status !== Component.Ready) {
- incubator.onStatusChanged = function(status) {
- if (status === Component.Ready) {
- incubator.object.objectName = children.length;
- assignBubbleProperties(incubator.object, event, depth, sizeOfRow);
- }
- }
- } else {
- incubator.object.objectName = children.length;
- assignBubbleProperties(incubator.object, event, depth, sizeOfRow);
- }
- } else {
- eventBubble = getUnusedEventBubble();
- assignBubbleProperties(eventBubble, event, depth, sizeOfRow);
- }
- }
-
- function assignBubbleProperties(eventBubble, event, depth, sizeOfRow) {
- var yPos = 0;
- var height = 0;
- var hour = 0;
- var durationMin = 0;
-
- // skip it in case of endDateTime == dd-MM-yyyy 12:00 AM
- if (event.endDateTime - day == 0)
- return;
-
- if (event.endDateTime.isSameDay(day) &&
- event.endDateTime.isSameDay(event.startDateTime)) {
- hour = event.startDateTime.getHours();
- yPos = (( event.startDateTime.getMinutes() * hourHeight) / 60) + hour * hourHeight
- durationMin = (event.endDateTime - event.startDateTime) / Date.msPerMin;
- }
- if (!event.startDateTime.isSameDay(day) &&
- event.endDateTime.isSameDay(day)) {
- hour = 0;
- yPos = 0;
- durationMin = event.endDateTime.getHours() * 60;
- durationMin += event.endDateTime.getMinutes();
- }
- if (event.startDateTime.isSameDay(day) &&
- !event.endDateTime.isSameDay(day)) {
- hour = event.startDateTime.getHours();
- yPos = (( event.startDateTime.getMinutes() * hourHeight) / 60) + hour * hourHeight
- durationMin = (24 - event.startDateTime.getHours()) * 60;
- }
- if (!event.startDateTime.isSameDay(day) &&
- !event.endDateTime.isSameDay(day)) {
- hour = 0;
- yPos = 0;
- durationMin = 24 * 60;
- }
-
- eventBubble.y = yPos;
- height = (durationMin * hourHeight )/ 60;
- eventBubble.height = (height > eventBubble.minimumHeight) ? height:eventBubble.minimumHeight ;
-
- eventBubble.model = bubbleOverLay.model
- eventBubble.depthInRow = depth;
- eventBubble.sizeOfRow = sizeOfRow;
- eventBubble.event = event
- eventBubble.visible = true;
- eventBubble.clicked.connect( bubbleOverLay.showEventDetails );
+ var unusedEvent = null
+ if (intern.unUsedEvents.length > 0) {
+ unusedEvent = intern.unUsedEvents[0]
+ intern.unUsedEvents.splice(0, 1);
+ }
+ return unusedEvent
}
function showSeparator() {
- var y = ((intern.now.getMinutes() * hourHeight) / 60) + intern.now.getHours() * hourHeight;
- separator.y = y;
- separator.visible = true;
+ intern.now = new Date();
+ if (intern.now.isSameDay(bubbleOverLay.day) ) {
+ var y = ((intern.now.getMinutes() * hourHeight) / 60) + intern.now.getHours() * hourHeight;
+ separator.y = y;
+ separator.visible = true;
+ } else {
+ separator.visible = false;
+ }
+ }
+
+ onDayChanged: bubbleOverLay.idleCreateEvents();
+ Component.onCompleted: bubbleOverLay.idleCreateEvents();
+ enabled: !intern.busy && !intern.waitingForModelChange
+
+ EventBubble {
+ id: temporaryEvent
+
+ isEventBubble: false
+ Drag.active: overlayMouseArea.drag.active
+ isLiveEditing: overlayMouseArea.creatingEvent
+ visible: overlayMouseArea.creatingEvent
+ sizeOfRow: 1.0
+ z: 100
+ onVisibleChanged: {
+ if (visible)
+ y = event ? CanlendarCanvas.minutesSince(bubbleOverLay.day, event.startDateTime) * bubbleOverLay.minuteHeight : 0
+ }
+ }
+
+ Item {
+ anchors {
+ topMargin: flickable ? flickable.contentY : 0
+ bottomMargin: flickable ? bubbleOverLay.height - flickable.contentY - flickable.height : bubbleOverLay.height
+ fill: parent
+ }
+
+ ActivityIndicator {
+ visible: intern.busy || intern.waitingForModelChange
+ running: visible
+ anchors.centerIn: parent
+ }
+
+ z: 100
+ }
+
+ MouseArea {
+ id: overlayMouseArea
+
+ property bool creatingEvent: false
+
+ anchors.fill: parent
+ objectName: "mouseArea"
+ drag {
+ target: creatingEvent ? temporaryEvent : null
+ axis: Drag.YAxis
+ minimumY: 0
+ maximumY: height - temporaryEvent.height
+ }
+
+
+ Binding {
+ target: temporaryEvent
+ property: "visible"
+ value: overlayMouseArea.creatingEvent
+ }
+
+ onPressAndHold: {
+ var selectedDate = new Date(day);
+ var pointY = mouse.y - (hourHeight / 2);
+ selectedDate.setHours(Math.floor(pointY / hourHeight))
+ selectedDate.setMinutes(Math.min(pointY % hourHeight, 60))
+ var event = createOrganizerEvent(selectedDate)
+
+ Haptics.play()
+
+ temporaryEvent.anchorDate = bubbleOverLay.day
+ temporaryEvent.minuteHeight = bubbleOverLay.minuteHeight
+ temporaryEvent.depthInRow = 0
+ temporaryEvent.model = bubbleOverLay.model
+ temporaryEvent.event = event
+ temporaryEvent.resize()
+ creatingEvent = true
+ }
+
+ onReleased: {
+ if (creatingEvent) {
+ bubbleOverLay.pressAndHoldAt(temporaryEvent.event.startDateTime)
+ creatingEvent = false
+ }
+ }
+
+ onCanceled: {
+ if (creatingEvent) {
+ creatingEvent = false
+ }
+ }
+ }
+
+ TimeSeparator {
+ id: separator
+ objectName: "separator"
+ width: bubbleOverLay.width
+ visible: false
+ // make sure that the object is aways visible
+ z: 1000
+ }
+
+ Timer {
+ id: separtorUpdateTimer
+ interval: 300000 // every 5 minutes
+ running: true
+ repeat: true
+ onTriggered: showSeparator()
+ }
+
+ QtObject {
+ id: intern
+
+ property var now : new Date();
+ property var eventsById: ({})
+ property var unUsedEvents: []
+ property bool busy: false
+ property bool dirty: false
+ property bool waitingForModelChange: false
+ }
+
+ Timer {
+ id: createEventsTimer
+
+ interval: 300
+ running: false
+ repeat: false
+ onTriggered: createEvents()
+ }
+
+ Connections {
+ id: modelConnections
+ onModelChanged: {
+ intern.dirty = true
+ intern.waitingForModelChange = false
+ bubbleOverLay.idleCreateEvents()
+ }
}
}
=== modified file 'TimeLineBaseComponent.qml'
--- TimeLineBaseComponent.qml 2016-01-29 14:35:14 +0000
+++ TimeLineBaseComponent.qml 2016-03-03 21:42:32 +0000
@@ -29,58 +29,106 @@
property var keyboardEventProvider;
+ property bool isCurrentItem: false
+ property bool isActive: false
+
property date startDay: DateExt.today();
property int weekNumber: startDay.weekNumber(Qt.locale().firstDayOfWeek);
- property bool isActive: false
property alias contentY: timeLineView.contentY
property alias contentInteractive: timeLineView.interactive
+ property var modelFilter: invalidFilter
property var selectedDay;
+ readonly property real hourItemHeight: units.gu(8)
+ readonly property int currentHour: timeLineView.contentY > hourItemHeight ?
+ Math.round(timeLineView.contentY / hourItemHeight) : 1
+ readonly property int currentDayOfWeek: timeLineView.contentX > timeLineView.delegateWidth ?
+ Math.floor(timeLineView.contentX / timeLineView.delegateWidth) : 0
property int type: ViewType.ViewTypeWeek
//visible hour
property int scrollHour;
- property EventListModel mainModel;
-
signal dateSelected(var date);
signal dateHighlighted(var date);
-
- function scrollToCurrentTime() {
- var currentTime = new Date();
- scrollHour = currentTime.getHours();
-
- timeLineView.contentY = scrollHour * units.gu(8);
- if(timeLineView.contentY >= timeLineView.contentHeight - timeLineView.height) {
- timeLineView.contentY = timeLineView.contentHeight - timeLineView.height
- }
- }
-
- function scrollTocurrentDate() {
- if ( type != ViewType.ViewTypeWeek ){
+ signal pressAndHoldAt(var date, bool allDay);
+
+ function timeIsVisible(date) {
+
+ var hour = date.getHours();
+ var currentTimeY = (hour * hourItemHeight)
+ return ((currentTimeY >= timeLineView.contentY) &&
+ (currentTimeY <= (timeLineView.contentY + timeLineView.height)));
+ }
+
+ function dateIsVisible(date) {
+ if (date.getFullYear() !== startDay.getFullYear()) {
+ return false;
+ }
+
+ if (type != ViewType.ViewTypeWeek) {
+ return ((date.getMonth() === startDay.getMonth) &&
+ (date.getDate() === startDay.getDate()))
+ }
+
+ var dateDayOfWeekX = date.getDay() * timeLineView.delegateWidth
+ return ((dateDayOfWeekX >= timeLineView.contentX) &&
+ (dateDayOfWeekX <= (timeLineView.contentX + timeLineView.width)))
+ }
+
+ function dateTimeIsVisible(date) {
+ return dateIsVisible(date) && timeIsVisible(date);
+ }
+
+ function scrollToTime(date) {
+ scrollHour = date.getHours();
+
+ var currentTimeY = (scrollHour * hourItemHeight)
+ var margin = (timeLineView.height / 2.0) - units.gu(5)
+ currentTimeY = currentTimeY - margin
+ timeLineView.contentY = Math.min(timeLineView.contentHeight - timeLineView.height, currentTimeY > 0 ? currentTimeY : 0)
+ timeLineView.returnToBounds()
+ }
+
+ function scrollToDate(date) {
+ if (type != ViewType.ViewTypeWeek) {
return;
}
- var today = DateExt.today();
- var startOfWeek = today.weekStart(Qt.locale().firstDayOfWeek);
- var weekDay = today.getDay();
- var diff = weekDay - Qt.locale().firstDayOfWeek
- diff = diff < 0 ? 6 : diff
-
- if( startOfWeek.isSameDay(startDay) && diff > 2) {
- timeLineView.contentX = (diff * timeLineView.delegateWidth);
- if( timeLineView.contentX > (timeLineView.contentWidth - timeLineView.width) ) {
- timeLineView.contentX = timeLineView.contentWidth - timeLineView.width
- }
+ var todayWeekNumber = date.weekNumber(Qt.locale().firstDayOfWeek);
+
+ if (todayWeekNumber === root.weekNumber) {
+ var startOfWeek = date.weekStart(Qt.locale().firstDayOfWeek);
+ var weekDay = date.getDay();
+ var diff = weekDay - Qt.locale().firstDayOfWeek
+ diff = diff < 0 ? 0 : diff
+
+ var currentDayY = timeLineView.delegateWidth * diff
+ var margin = (timeLineView.width - timeLineView.delegateWidth) / 2
+ currentDayY = currentDayY - margin
+ timeLineView.contentX = Math.min(timeLineView.contentWidth - timeLineView.width, currentDayY > 0 ? currentDayY : 0)
} else {
- //need to check swipe direction
- //and change startion position as per direction
- if(weekViewPath.swipeDirection() === -1) {
- timeLineView.contentX = timeLineView.contentWidth - timeLineView.width
- } else {
- timeLineView.contentX = 0;
- }
+ timeLineView.contentX = 0
}
+
+ timeLineView.returnToBounds()
+ }
+
+ function scrollToDateAndTime(date) {
+ scrollToTime(date)
+ scrollToDate(date)
+ }
+
+ function scrollToEnd()
+ {
+ timeLineView.contentX = timeLineView.contentWidth - timeLineView.width
+ timeLineView.returnToBounds()
+ }
+
+ function scrollToBegin()
+ {
+ timeLineView.contentX = 0
+ timeLineView.returnToBounds()
}
Connections{
@@ -110,29 +158,52 @@
}
}
- Timer{
- interval: 200; running: true; repeat: false
- onTriggered: {
- mainModel = modelComponent.createObject();
- activityLoader.running = Qt.binding( function (){ return mainModel.isLoading;});
- }
- }
-
- Component {
- id: modelComponent
- EventListModel {
- id: mainModel
- startPeriod: startDay.midnight();
- endPeriod: type == ViewType.ViewTypeWeek ? startPeriod.addDays(7).endOfDay(): startPeriod.endOfDay()
- filter: eventModel.filter
- }
- }
+ onIsActiveChanged: {
+ if (isActive && (mainModel.filter === invalidFilter)) {
+ idleRefresh.reset()
+ }
+ }
+
+ Timer {
+ id: idleRefresh
+
+ function reset()
+ {
+ mainModel.filter = invalidFilter
+ restart()
+ }
+
+ interval: root.isCurrentItem ? 500 : 1000
+ repeat: false
+ onTriggered: {
+ mainModel.filter = Qt.binding(function() { return root.modelFilter} )
+ }
+ }
+
+ InvalidFilter {
+ id: invalidFilter
+ }
+
+ EventListModel {
+ id: mainModel
+
+ manager:"eds"
+ startPeriod: startDay.midnight();
+ endPeriod: type == ViewType.ViewTypeWeek ? startPeriod.addDays(7).endOfDay(): startPeriod.endOfDay()
+ filter: invalidFilter
+ autoUpdate: true
+
+ onStartPeriodChanged: idleRefresh.reset()
+ onEndPeriodChanged: idleRefresh.reset()
+ }
ActivityIndicator {
id: activityLoader
+ objectName : "activityIndicator"
+
visible: running
- objectName : "activityIndicator"
anchors.centerIn: parent
+ //running: mainModel.isLoading
z:2
}
@@ -149,11 +220,21 @@
selectedDay: root.selectedDay
onDateSelected: {
- root.dateSelected(date);
+ root.dateSelected(date.getFullYear(),
+ date.getMonth(),
+ date.getDate(),
+ root.currentHour, 0, 0)
}
onDateHighlighted: {
- root.dateHighlighted(date);
+ root.dateHighlighted(date.getFullYear(),
+ date.getMonth(),
+ date.getDate(),
+ root.currentHour, 0, 0)
+ }
+
+ onAllDayPressAndHold: {
+ root.pressAndHoldAt(date, true)
}
}
@@ -183,7 +264,7 @@
property int delegateWidth: {
if( type == ViewType.ViewTypeWeek ) {
- width/3 - units.gu(1) /*partial visible area*/
+ width/3 - units.gu(1) // partial visible area
} else {
width
}
@@ -198,11 +279,6 @@
}
}
- onContentWidthChanged: {
- scrollToCurrentTime();
- scrollTocurrentDate();
- }
-
clip: true
TimeLineBackground{}
@@ -214,7 +290,12 @@
model: type == ViewType.ViewTypeWeek ? 7 : 1
delegate: TimeLineBase {
+ id: delegate
+
+ objectName: "TimeLineBase_" + root.objectName
+
property int idx: index
+ flickable: timeLineView
anchors.top: parent.top
width: {
if( type == ViewType.ViewTypeWeek ) {
@@ -228,12 +309,14 @@
day: startDay.addDays(index)
model: mainModel
- Connections{
- target: mainModel
+ onPressAndHoldAt: {
+ root.pressAndHoldAt(date, false)
+ }
- onModelChanged: {
- createEvents();
- }
+ Binding {
+ target: timeLineView
+ property: "interactive"
+ value: !delegate.creatingEvent
}
DropArea {
@@ -251,12 +334,13 @@
event.startDateTime = startDate;
event.endDateTime = endDate;
- return event;
+ return event;
}
onDropped: {
var event = dropArea.modifyEventForDrag(drop);
- model.saveItem(event);
+ delegate.waitForModelChange()
+ delegate.model.saveItem(event);
}
onPositionChanged: {
@@ -299,13 +383,6 @@
anchors.fill: parent
}
}
-
- Connections{
- target: mainModel
- onStartPeriodChanged:{
- destroyAllChildren();
- }
- }
}
}
}
@@ -317,8 +394,16 @@
id: comp
EventBubble {
type: root.type == ViewType.ViewTypeWeek ? narrowType : wideType
- flickable: root.isActive ? timeLineView : null
+ flickable: root.isCurrentItem ? timeLineView : null
clip: true
+ opacity: parent.enabled ? 1.0 : 0.3
+
+ // send a signal to update application current date
+ onClicked: root.dateHighlighted(event.startDateTime)
+ onIsLiveEditingChanged: {
+ if (isLiveEditing)
+ root.dateHighlighted(event.startDateTime)
+ }
}
}
}
=== modified file 'TimeLineHeader.qml'
--- TimeLineHeader.qml 2016-01-29 14:35:14 +0000
+++ TimeLineHeader.qml 2016-03-03 21:42:32 +0000
@@ -33,6 +33,7 @@
signal dateSelected(var date);
signal dateHighlighted(var date);
+ signal allDayPressAndHold(var date);
width: parent.width
height: units.gu(10)
@@ -118,6 +119,9 @@
width: parent.width
height: units.gu(5)
+ onPressAndHold: headerRoot.allDayPressAndHold(date)
+
+
Connections{
target: mainModel
onModelChanged : {
@@ -184,6 +188,8 @@
height: units.gu(5)
model: mainModel
+ onPressAndHold: headerRoot.allDayPressAndHold(date)
+
Connections{
target: mainModel
onModelChanged : {
=== modified file 'WeekView.qml'
--- WeekView.qml 2016-02-03 08:06:25 +0000
+++ WeekView.qml 2016-03-03 21:42:32 +0000
@@ -22,21 +22,30 @@
import "ViewType.js" as ViewType
import "./3rd-party/lunar.js" as Lunar
-Page{
+PageWithBottomEdge {
id: weekViewPage
objectName: "weekViewPage"
- property var dayStart: new Date();
- property var firstDay: dayStart.weekStart(Qt.locale().firstDayOfWeek);
+ property var anchorDate: new Date();
+ readonly property var anchorFirstDayOfWeek: anchorDate.weekStart(Qt.locale().firstDayOfWeek)
+ readonly property var currentDate: weekViewPath.currentItem.item.startDay
+ readonly property var currentFirstDayOfWeek: currentDate.weekStart(Qt.locale().firstDayOfWeek)
+
property bool isCurrentPage: false
property var selectedDay;
+ property var highlightedDay;
signal dateSelected(var date);
- signal dateHighlighted(var date);
+ signal pressAndHoldAt(var date, bool allDay)
+
+ function delayScrollToDate(scrollDate, scrollTime) {
+ idleScroll.scrollToTime = scrollTime != undefined ? scrollTime : true
+ idleScroll.scrollToDate = new Date(scrollDate)
+ idleScroll.restart()
+ }
Keys.forwardTo: [weekViewPath]
-
- flickable: null
+ createEventAt: null
Action {
id: calendarTodayAction
@@ -44,7 +53,60 @@
iconName: "calendar-today"
text: i18n.tr("Today")
onTriggered: {
- dayStart = new Date()
+ var today = new Date()
+ delayScrollToDate(today)
+ anchorDate = today
+ }
+ }
+
+ onAnchorDateChanged: {
+ weekViewPath.scrollToBegginer()
+ }
+
+ onEventCreated: {
+ var scrollDate = new Date(event.startDateTime)
+ var currentWeekNumber = currentDate.weekNumber(Qt.locale().firstDayOfWeek)
+ var eventWeekNumber = scrollDate.weekNumber(Qt.locale().firstDayOfWeek)
+ var needScroll = false
+
+ if ((scrollDate.getFullYear() !== currentDate.getFullYear()) ||
+ (currentWeekNumber !== eventWeekNumber)) {
+ anchorDate = new Date(scrollDate)
+ needScroll = true
+ } else {
+ if (event.allDay) {
+ needScroll = !weekViewPath.currentItem.item.dateIsVisible(scrollDate)
+ } else {
+ needScroll = !weekViewPath.currentItem.item.timeIsVisible(scrollDate)
+ }
+ }
+
+ highlightedDay = scrollDate
+ if (needScroll) {
+ delayScrollToDate(scrollDate, !event.allDay)
+ }
+ }
+
+ Timer {
+ id: idleScroll
+
+ property var scrollToDate: null
+ property bool scrollToTime: true
+
+ interval: 200
+ repeat:false
+ onTriggered: {
+ if (scrollToDate) {
+ if (scrollToTime)
+ weekViewPath.currentItem.item.scrollToDateAndTime(scrollToDate);
+ else
+ weekViewPath.currentItem.item.scrollToDate(scrollToDate);
+ } else {
+ weekViewPath.currentItem.item.scrollToBegin()
+ }
+
+ scrollToDate = null
+ scrollToTime = true
}
}
@@ -54,7 +116,6 @@
leadingActionBar.actions: tabs.tabsAction
trailingActionBar.actions: [
calendarTodayAction,
- commonHeaderActions.newEventAction,
commonHeaderActions.showCalendarAction,
commonHeaderActions.reloadAction,
commonHeaderActions.syncCalendarAction,
@@ -65,8 +126,17 @@
// TRANSLATORS: this is a time formatting string,
// see http://qt-project.org/doc/qt-5/qml-qtqml-date.html#details for valid expressions.
// It's used in the header of the month and week views
- var monthName = dayStart.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy"))
- return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1)
+ var currentLastDayOfWeek = currentFirstDayOfWeek.addDays(7)
+ if (currentLastDayOfWeek.getMonth() !== currentFirstDayOfWeek.getMonth()) {
+ var firstMonthName = currentFirstDayOfWeek.toLocaleString(Qt.locale(),i18n.tr("MMM"))
+ var lastMonthName = currentLastDayOfWeek.toLocaleString(Qt.locale(),i18n.tr("MMM"))
+ return (firstMonthName[0].toUpperCase() + firstMonthName.substr(1, 2) + "/" +
+ lastMonthName[0].toUpperCase() + lastMonthName.substr(1, 2) + " " +
+ currentLastDayOfWeek.getFullYear())
+ } else {
+ var monthName = currentDate.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy"))
+ return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1)
+ }
}
flickable: null
}
@@ -80,25 +150,13 @@
topMargin: header.height
}
+ onCurrentIndexChanged: {
+ weekViewPage.highlightedDay = null
+ }
+
//This is used to scroll all view together when currentItem scrolls
property var childContentY;
- onNextItemHighlighted: {
- nextWeek();
- }
-
- onPreviousItemHighlighted: {
- previousWeek();
- }
-
- function nextWeek() {
- dayStart = firstDay.addDays(7);
- }
-
- function previousWeek(){
- dayStart = firstDay.addDays(-7);
- }
-
delegate: Loader {
id: timelineLoader
width: parent.width
@@ -112,39 +170,52 @@
TimeLineBaseComponent {
id: timeLineView
+ startDay: anchorFirstDayOfWeek.addDays((weekViewPath.loopCurrentIndex + weekViewPath.indexType(index)) * 7)
+ anchors.fill: parent
type: ViewType.ViewTypeWeek
- anchors.fill: parent
- isActive: parent.PathView.isCurrentItem
- startDay: firstDay.addDays( weekViewPath.indexType(index) * 7)
+ isCurrentItem: parent.PathView.isCurrentItem
+ isActive: !weekViewPath.moving && !weekViewPath.flicking
keyboardEventProvider: weekViewPath
selectedDay: weekViewPage.selectedDay
-
- onIsActiveChanged: {
- timeLineView.scrollTocurrentDate();
- }
+ modelFilter: weekViewPage.model ? weekViewPage.model.filter : null
onDateSelected: {
weekViewPage.dateSelected(date);
}
onDateHighlighted:{
- weekViewPage.dateHighlighted(date);
+ weekViewPage.highlightedDay = date
+ }
+
+ Component.onCompleted: {
+ var iType = weekViewPath.indexType(index)
+ if (iType === 0) {
+ idleScroll.restart()
+ } else if (iType < 0) {
+ scrollToEnd()
+ }
+ }
+
+ onPressAndHoldAt: {
+ weekViewPage.pressAndHoldAt(date, allDay)
}
Connections{
target: calendarTodayAction
onTriggered:{
- if( isActive )
- timeLineView.scrollTocurrentDate();
- }
+ if (isActive)
+ timeLineView.scrollToDate(new Date());
+ }
}
- Connections{
- target: weekViewPage
- onIsCurrentPageChanged:{
- if(weekViewPage.isCurrentPage){
- timeLineView.scrollToCurrentTime();
- timeLineView.scrollTocurrentDate();
+ Connections {
+ target: weekViewPath
+ onLoopCurrentIndexChanged: {
+ var iType = weekViewPath.indexType(index)
+ if (iType < 0) {
+ scrollToEnd()
+ } else if (iType > 0) {
+ scrollToBegin()
}
}
}
@@ -164,6 +235,11 @@
value: contentY
when: parent.PathView.isCurrentItem
}
+ Binding {
+ target: weekViewPath
+ property: "interactive"
+ value: timeLineView.contentInteractive
+ }
}
}
}
=== modified file 'YearView.qml'
--- YearView.qml 2016-02-03 08:06:25 +0000
+++ YearView.qml 2016-03-03 21:42:32 +0000
@@ -18,22 +18,36 @@
import QtQuick 2.4
import Ubuntu.Components 1.3
+
import "dateExt.js" as DateExt
import "./3rd-party/lunar.js" as Lunar
-Page {
+PageWithBottomEdge {
id: yearViewPage
objectName: "yearViewPage"
- property int currentYear: DateExt.today().getFullYear();
+ property int anchorYear: new Date().getFullYear()
+ property bool displayLunarCalendar: false
+ readonly property int currentYear: yearPathView.currentItem.item ? yearPathView.currentItem.item.year : anchorYear
+
signal monthSelected(var date);
+ function refreshCurrentYear(year)
+ {
+ if (currentYear !== year) {
+ anchorYear = year;
+ yearPathView.scrollToBegginer()
+ }
+ var yearViewDelegate = yearPathView.currentItem;
+ if (yearViewDelegate && yearViewDelegate.item) {
+ yearViewDelegate.item.refresh();
+ }
+ }
+
+ createEventAt: null
Keys.forwardTo: [yearPathView]
-
- function refreshCurrentYear(year) {
- currentYear = year;
- var yearViewDelegate = yearPathView.currentItem.item;
- yearViewDelegate.refresh();
+ onAnchorYearChanged: {
+ yearPathView.scrollToBegginer()
}
Action {
@@ -42,26 +56,50 @@
iconName: "calendar-today"
text: i18n.tr("Today")
onTriggered: {
- currentYear = new Date().getFullYear()
+ var todayYear = new Date().getFullYear()
+ yearViewPage.refreshCurrentYear(todayYear)
}
}
header: PageHeader {
id: pageHeader
-
leadingActionBar.actions: tabs.tabsAction
trailingActionBar.actions: [
calendarTodayAction,
- commonHeaderActions.newEventAction,
commonHeaderActions.showCalendarAction,
commonHeaderActions.reloadAction,
commonHeaderActions.syncCalendarAction,
commonHeaderActions.settingsAction
]
- title: i18n.tr("Year %1").arg(currentYear)
+ title: {
+ if (displayLunarCalendar) {
+ var lunarDate = Lunar.calendar.solar2lunar(currentYear, 6, 0)
+ return lunarDate.gzYear +" "+ lunarDate.Animal
+ } else {
+ return i18n.tr("Year %1").arg(currentYear)
+ }
+ }
flickable: null
}
+ ActivityIndicator {
+ property var startDate: new Date()
+
+ visible: running
+ running: (yearPathView.currentItem.status !== Loader.Ready)
+ anchors.centerIn: parent
+ z:2
+
+ onRunningChanged: {
+ if (!running) {
+ var current = new Date()
+ console.debug("Elapsed:" + (current.getTime() - startDate.getTime()))
+ }
+ }
+ }
+
+ flickable: null
+
PathViewBase {
id: yearPathView
objectName: "yearPathView"
@@ -71,46 +109,24 @@
topMargin: header.height
}
- onNextItemHighlighted: {
- currentYear = currentYear + 1;
- }
-
- onPreviousItemHighlighted: {
- currentYear = currentYear - 1;
- }
-
delegate: Loader {
- width: parent.width
- height: parent.height
- anchors.top: parent.top
-
- asynchronous: index !== yearPathView.currentIndex
- sourceComponent: delegateComponent
-
- Component{
- id: delegateComponent
-
- YearViewDelegate{
- focus: index == yearPathView.currentIndex
-
- scrollMonth: 0;
- isCurrentItem: index == yearPathView.currentIndex
- year: (currentYear + yearPathView.indexType(index))
-
- anchors.fill: parent
+ id: delegateLoader
+
+ asynchronous: true
+ width: PathView.view.width
+ height: PathView.view.height
+
+ sourceComponent: YearViewDelegate {
+ visible: delegateLoader.status === Loader.Ready
+ anchors.fill: parent
+ scrollMonth: 0;
+ isCurrentItem: (index === yearPathView.currentIndex)
+ focus: isCurrentItem
+ year: (yearViewPage.anchorYear + yearPathView.loopCurrentIndex + yearPathView.indexType(index))
+ onMonthSelected: {
+ yearViewPage.monthSelected(date)
}
}
}
}
-
- Component.onCompleted: {
- pageHeader.title = Qt.binding(function(){
- if(mainView.displayLunarCalendar){
- var lunarDate = Lunar.calendar.solar2lunar(currentYear, 6, 0)
- return lunarDate.gzYear +" "+ lunarDate.Animal
- } else {
- return i18n.tr("Year %1").arg(currentYear)
- }
- })
- }
}
=== modified file 'YearViewDelegate.qml'
--- YearViewDelegate.qml 2016-02-03 08:06:25 +0000
+++ YearViewDelegate.qml 2016-03-03 21:42:32 +0000
@@ -1,26 +1,37 @@
import QtQuick 2.4
import Ubuntu.Components 1.3
-GridView{
+ GridView{
id: yearView
- clip: true
property int scrollMonth;
property bool isCurrentItem;
property int year;
-
+ readonly property var currentDate: new Date()
+ readonly property int currentYear: currentDate.getFullYear()
+ readonly property int currentMonth: currentDate.getMonth()
readonly property int minCellWidth: units.gu(30)
+
+ signal monthSelected(var date);
+
+ function refresh() {
+ scrollMonth = 0;
+ if(year == currentYear) {
+ scrollMonth = currentMonth
+ }
+ yearView.positionViewAtIndex(scrollMonth, GridView.Beginning);
+ }
+
+ // Does not increase cash buffer if user is scolling
+ cacheBuffer: parent.PathView.view.flicking || parent.PathView.view.dragging || !isCurrentItem ? 0 : 6 * cellHeight
+
cellWidth: Math.floor(Math.min.apply(Math, [3, 4].map(function(n)
{ return ((width / n >= minCellWidth) ? width / n : width / 2) })))
-
cellHeight: cellWidth * 1.4
+ clip: true
model: 12 /* months in a year */
- onYearChanged: {
- refresh();
- }
-
//scroll in case content height changed
onHeightChanged: {
yearView.positionViewAtIndex(scrollMonth, GridView.Beginning);
@@ -30,71 +41,25 @@
yearView.positionViewAtIndex(scrollMonth, GridView.Beginning);
}
- function refresh() {
- scrollMonth = 0;
- var today = new Date();
- if(year == today.getFullYear()) {
- scrollMonth = today.getMonth();
- }
- yearView.positionViewAtIndex(scrollMonth, GridView.Beginning);
- }
-
- Connections{
- target: yearPathView
- onScrollUp: {
- scrollMonth -= 2;
- if(scrollMonth < 0) {
- scrollMonth = 0;
- }
- yearView.positionViewAtIndex(scrollMonth, GridView.Beginning);
- }
-
- onScrollDown: {
- scrollMonth += 2;
- var visibleMonths = yearView.height / cellHeight;
- if( scrollMonth >= (11 - visibleMonths)) {
- scrollMonth = (11 - visibleMonths);
- }
- yearView.positionViewAtIndex(scrollMonth, GridView.Beginning);
- }
- }
-
- delegate: Loader {
- width: yearView.cellWidth
- height: yearView.cellHeight
-
- sourceComponent: delegateComponent
- asynchronous: !yearView.focus
-
- Component {
- id: delegateComponent
-
- Item {
- anchors.fill: parent
- anchors.margins: units.gu(0.5)
-
- MonthComponent {
- id: monthComponent
- objectName: "monthComponent" + index
- showEvents: false
- currentMonth: new Date(yearView.year, index, 1, 0, 0, 0, 0)
- displayWeekNumber: mainView.displayWeekNumber;
- displayLunarCalendar: false; //we disable lunar calendar display in yeaer view due to space
- isCurrentItem: yearView.focus
-
- isYearView: true
- anchors.fill: parent
-
- dayLabelFontSize:"x-small"
- dateLabelFontSize: "medium"
- monthLabelFontSize: "medium"
- yearLabelFontSize: "medium"
-
- onMonthSelected: {
- yearViewPage.monthSelected(date);
- }
- }
- }
- }
+ delegate: MonthComponent {
+ id: monthComponent
+ objectName: "monthComponent" + index
+
+ width: yearView.cellWidth - units.gu(1)
+ height: yearView.cellHeight - units.gu(1)
+ y: units.gu(0.5)
+ x: units.gu(0.5)
+
+ currentYear: yearView.year
+ currentMonth: index
+ isCurrentItem: yearView.focus
+ isYearView: true
+ dayLabelFontSize:"x-small"
+ dateLabelFontSize: "medium"
+ monthLabelFontSize: "medium"
+ yearLabelFontSize: "medium"
+ onMonthSelected: {
+ yearView.monthSelected(date);
+ }
}
}
=== modified file 'calendar.qml'
--- calendar.qml 2016-02-26 12:57:00 +0000
+++ calendar.qml 2016-03-03 21:42:32 +0000
@@ -92,7 +92,7 @@
focus: true
Keys.forwardTo: [pageStack.currentPage]
backgroundColor: "#ffffff"
- anchorToKeyboard: true
+ anchorToKeyboard: false
Connections {
target: UriHandler
@@ -174,6 +174,8 @@
EventListModel{
id: eventModel
+ property bool isReady: false
+
autoUpdate: true
startPeriod: tabs.currentDay
endPeriod: tabs.currentDay
@@ -193,8 +195,10 @@
collectionIds.push(collection.collectionId);
}
}
+ console.debug("Selected collections:" + collectionIds)
collectionFilter.ids = collectionIds;
- filter = mainFilter
+ filter = Qt.binding(function() { return mainFilter; })
+ isReady = true
}
function showEventFromId(eventId) {
@@ -395,6 +399,11 @@
tabs.selectedTabIndex = settings.defaultViewIndex;
}
tabs.isReady = true
+ // WORKAROUND: Due the missing feature on SDK, they can not detect if
+ // there is a mouse attached to device or not. And this will cause the
+ // bootom edge component to not work correct on desktop.
+ // We will consider that a mouse is always attached until it get implement on SDK.
+ QuickUtils.mouseAttached = true
} // End of Component.onCompleted:
Keys.onTabPressed: {
@@ -530,6 +539,15 @@
id: yearViewComp
YearView {
+ readonly property bool tabSelected: tabs.selectedTabIndex === yearTab.index
+
+ model: eventModel.isReady ? eventModel : null
+ bootomEdgeEnabled: tabSelected
+
+ onCurrentYearChanged: {
+ tabs.currentDay = new Date(currentYear, 1, 1)
+ }
+
onMonthSelected: {
var now = DateExt.today();
if ((date.getMonth() === now.getMonth()) &&
@@ -540,9 +558,10 @@
}
tabs.selectedTabIndex = monthTab.index;
}
- onActiveChanged: {
- if (active) {
- refreshCurrentYear(DateExt.today().getFullYear())
+
+ onTabSelectedChanged: {
+ if (tabSelected) {
+ refreshCurrentYear(tabs.currentDay.getFullYear())
}
}
}
@@ -552,13 +571,35 @@
id: monthViewComp
MonthView {
+ readonly property bool tabSelected: tabs.selectedTabIndex === monthTab.index
+
+ model: eventModel.isReady ? eventModel : null
+ bootomEdgeEnabled: tabSelected
+ displayLunarCalendar: mainView.displayLunarCalendar
+
+ onCurrentDateChanged: {
+ tabs.currentDay = currentDate
+ }
+
+ onHighlightedDateChanged: {
+ if (highlightedDate)
+ tabs.currentDay = highlightedDate
+ else
+ tabs.currentDay = currentDate
+ }
+
onDateSelected: {
- tabs.currentDay = date;
+ tabs.currentDay = date
tabs.selectedTabIndex = dayTab.index
}
- onActiveChanged: {
- if (active)
- currentMonth = tabs.currentDay.midnight()
+
+ onTabSelectedChanged: {
+ if (tabSelected) {
+ anchorDate = new Date(tabs.currentDay.getFullYear(),
+ tabs.currentDay.getMonth(),
+ 1,
+ 0, 0, 0)
+ }
}
}
}
@@ -567,16 +608,44 @@
id: weekViewComp
WeekView {
- onDayStartChanged: {
- tabs.currentDay = dayStart
- }
+ readonly property bool tabSelected: tabs.selectedTab === weekTab
+
+ model: eventModel.isReady ? eventModel : null
+ bootomEdgeEnabled: tabSelected
+
+ onHighlightedDayChanged: {
+ if (highlightedDay)
+ tabs.currentDay = highlightedDay
+ else
+ tabs.currentDay = currentFirstDayOfWeek
+ }
+
+ onCurrentFirstDayOfWeekChanged: {
+ tabs.currentDay = currentFirstDayOfWeek
+ }
+
onDateSelected: {
tabs.currentDay = date;
tabs.selectedTabIndex = dayTab.index
}
- onActiveChanged: {
- if (active)
- dayStart = tabs.currentDay
+
+ onPressAndHoldAt: {
+ bottomEdgeCommit(date, allDay)
+ }
+
+ onTabSelectedChanged: {
+ if (tabSelected) {
+ // 'tabs.currntDay' can change after set 'anchorDate' to avoid that
+ // create a copy of the current value
+ var tabDate = new Date(tabs.currentDay)
+ if (!anchorDate ||
+ (tabs.currentDay.getFullYear() != anchorDate.getFullYear()) ||
+ (tabs.currentDay.getMonth() != anchorDate.getMonth()) ||
+ (tabs.currentDay.getDate() != anchorDate.getDate())) {
+ anchorDate = new Date(tabDate)
+ }
+ delayScrollToDate(tabDate)
+ }
}
}
}
@@ -585,17 +654,36 @@
id: dayViewComp
DayView {
- onCurrentDayChanged: {
- tabs.currentDay = currentDay;
- }
+ readonly property bool tabSelected: tabs.selectedTabIndex === dayTab.index
+
+ model: eventModel.isReady ? eventModel : null
+ bootomEdgeEnabled: tabSelected
onDateSelected: {
tabs.currentDay = date
}
- onActiveChanged: {
- if (active)
- currentDay = tabs.currentDay;
+ onPressAndHoldAt: {
+ bottomEdgeCommit(date, allDay)
+ }
+
+ onCurrentDateChanged: {
+ tabs.currentDay = currentDate
+ }
+
+ onTabSelectedChanged: {
+ if (tabSelected) {
+ // 'tabs.currntDay' can change after set 'anchorDate' to avoid that
+ // create a copy of the current value
+ var tabDate = new Date(tabs.currentDay)
+ if (!anchorDate ||
+ (tabs.currentDay.getFullYear() != anchorDate.getFullYear()) ||
+ (tabs.currentDay.getMonth() != anchorDate.getMonth()) ||
+ (tabs.currentDay.getDate() != anchorDate.getDate())) {
+ anchorDate = new Date(tabDate)
+ }
+ delayScrollToDate(tabDate)
+ }
}
}
}
@@ -604,6 +692,9 @@
id: agendaViewComp
AgendaView {
+ model: eventModel.isReady ? eventModel : null
+ bootomEdgeEnabled: tabs.selectedTabIndex === agendaTab.index
+
onDateSelected: {
tabs.currentDay = date;
tabs.selectedTabIndex = dayTab.index
=== added file 'calendar_canvas.js'
--- calendar_canvas.js 1970-01-01 00:00:00 +0000
+++ calendar_canvas.js 2016-03-03 21:42:32 +0000
@@ -0,0 +1,52 @@
+.pragma library
+.import "dateExt.js" as DateExt
+
+function minutesSince(since, until)
+{
+ var sinceTime = new Date(since)
+ sinceTime.setSeconds(0)
+ var untilTime = new Date(until)
+ untilTime.setSeconds(0)
+
+ var sinceTimeInSecs = sinceTime.getTime()
+ var untilTimeInSecs = untilTime.getTime()
+
+ // limit to since day minutes
+ untilTimeInSecs = Math.min(sinceTime.endOfDay().getTime(), untilTimeInSecs)
+
+ // calculate the time in minutes of this event
+ var totalSecs = untilTimeInSecs - sinceTimeInSecs
+ if (totalSecs > 0)
+ return (totalSecs / 60000)
+
+ return 0
+}
+
+function parseDayEvents(date, itemsOfTheDay)
+{
+ var eventsInfo = []
+ for(var c=0; c < itemsOfTheDay.length; c++) {
+ var event = itemsOfTheDay[c]
+ if (event.allDay)
+ continue
+
+ var eventStartTimeInMinutes = minutesSince(date, event.startDateTime)
+ var eventEndTimeInMinutes = minutesSince(date, event.endDateTime)
+
+ // avoid to draw events too small
+ if ((eventEndTimeInMinutes - eventStartTimeInMinutes) < 20)
+ eventEndTimeInMinutes = eventStartTimeInMinutes + 20
+
+ eventsInfo.push({'eventId': event.itemId,
+ 'eventStartTime': event.startDateTime.getTime(),
+ 'eventEndTime': event.endDateTime.getTime(),
+ 'startTime': eventStartTimeInMinutes,
+ 'endTime': eventEndTimeInMinutes,
+ 'endTimeInSecs': event.endDateTime.getTime(),
+ 'y': -1,
+ 'intersectionCount': 0,
+ 'width': 1.0})
+ }
+
+ return eventsInfo
+}
=== added file 'calendar_canvas_worker.js'
--- calendar_canvas_worker.js 1970-01-01 00:00:00 +0000
+++ calendar_canvas_worker.js 2016-03-03 21:42:32 +0000
@@ -0,0 +1,111 @@
+WorkerScript.onMessage = function(message) {
+ var processedEvents = dayEventsMap(message.events)
+ WorkerScript.sendMessage({'reply': processedEvents})
+}
+
+function sortByStartAndSize(eventA, eventB)
+{
+ var sort = sortEventsByStart(eventA, eventB)
+ if (sort === 0)
+ sort = sortEventsBySize(eventA, eventB)
+ return sort
+}
+
+function sortEventsByStart(eventA, eventB)
+{
+ if (eventA.startTime < eventB.startTime)
+ return -1
+ else if (eventA.startTime > eventB.startTime)
+ return 1
+ else
+ return 0
+}
+
+function sortEventsBySize(eventA, eventB)
+{
+ var eventAEndTime = eventA.endTimeInSecs
+ var eventBEndTime = eventB.endTimeInSecs
+ if (eventAEndTime > eventBEndTime)
+ return -1
+ else if (eventAEndTime < eventBEndTime)
+ return 1
+ return 0
+}
+
+function yArrayCount(yArray, time)
+{
+ var maxY = yArray.length - 1
+ for(var i=yArray.length - 1; i >= 0; i--) {
+ if (!yArray[i] || (yArray[i].endTime <= time))
+ maxY = i
+ }
+}
+
+/*
+ * Find item 'y' position on the intersection list
+ */
+function findOptimalY(intersections)
+{
+ if (intersections.length === 0)
+ return
+
+ var eventWidth = 1.0 / intersections.length
+ var yArray = new Array(intersections.length)
+ var maxY = 0
+
+ for (var i = 0; i < intersections.length; i++) {
+ var intersection = intersections[i]
+ for (var y=0; y < yArray.length; y++) {
+ if (!yArray[y] || (yArray[y].endTime <= intersection.startTime)) {
+ if (y > maxY)
+ maxY = y
+ intersection.y = y
+ intersection.intersectionCount = intersections.length
+ yArray[y] = intersection
+ break
+ }
+ }
+ }
+
+ for (var i = 0; i < intersections.length; i++) {
+ intersections[i].width = (1.0 / (maxY + 1))
+ }
+
+}
+
+function dayEventsMap(eventsInfo)
+{
+ eventsInfo.sort(sortByStartAndSize)
+
+ var events = eventsInfo.slice()
+ var lines = []
+
+ while (events.length > 0) {
+ var aux = {"startTime": 0, "ednTime": 0}
+ var eventA = events[0]
+ events.splice(0, 1)
+ var line = [eventA]
+
+ aux.starTime = eventA.startTime
+ aux.endTime = eventA.endTime
+
+ var newList = []
+ for(var i = 0; i < events.length; i++) {
+ var eventB = events[i]
+ if ((aux.startTime < eventB.endTime) &&
+ (eventB.startTime < aux.endTime)) {
+ if (aux.endTime < eventB.endTime)
+ aux.endTime = eventB.endTime
+ line.push(eventB)
+ } else {
+ newList.push(eventB)
+ }
+ }
+
+ findOptimalY(line)
+ lines.push(line)
+ events = newList
+ }
+
+ return eventsInfo
+}
=== modified file 'click/calendar.apparmor'
--- click/calendar.apparmor 2015-12-16 10:51:01 +0000
+++ click/calendar.apparmor 2016-03-03 21:42:32 +0000
@@ -6,5 +6,5 @@
"accounts",
"push-notification-client"
],
- "policy_version": 1.2
+ "policy_version": 1.3
}
=== modified file 'click/manifest.json.in'
--- click/manifest.json.in 2015-12-16 10:51:01 +0000
+++ click/manifest.json.in 2016-03-03 21:42:32 +0000
@@ -1,7 +1,7 @@
{
"architecture": "all",
"description": "A calendar for Ubuntu which syncs with online accounts",
- "framework": "ubuntu-sdk-14.10-qml",
+ "framework": "ubuntu-sdk-15.04.4",
"hooks": {
"calendar": {
"account-application": "@PROJECT_NAME@_@APP_NAME@.application",
=== modified file 'dateExt.js'
--- dateExt.js 2015-11-27 01:34:46 +0000
+++ dateExt.js 2016-03-03 21:42:32 +0000
@@ -60,11 +60,23 @@
}
Date.prototype.addDays = function(days) {
+ if (days === 0)
+ return this
+
var date = new Date(this)
date.setDate(date.getDate() + days);
return date
}
+Date.prototype.addMinutes = function(minutes) {
+ if (minutes === 0)
+ return this
+
+ var date = new Date(this)
+ date.setMinutes(date.getMinutes() + minutes);
+ return date
+}
+
Date.prototype.addMonths = function(months) {
var date = new Date(this)
date.setMonth(date.getMonth() + months)
@@ -87,8 +99,9 @@
}
Date.prototype.weekNumber = function(weekStartDay) {
- var date = this.weekStart(weekStartDay).addDays(3) // Thursday midnight
- var onejan = new Date(this.getFullYear(), 0, 3);
+ var date = new Date(this)
+ date = date.weekStart(weekStartDay).addDays(3) // Thursday midnight
+ var onejan = new Date(date.getFullYear(), 0, 3);
return Math.ceil((((date - onejan) / 86400000) + onejan.getDay()+1)/7);
}
=== modified file 'po/com.ubuntu.calendar.pot'
--- po/com.ubuntu.calendar.pot 2016-03-01 11:07:01 +0000
+++ po/com.ubuntu.calendar.pot 2016-03-03 21:42:32 +0000
@@ -8,7 +8,7 @@
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-03-01 16:35+0530\n"
+"POT-Creation-Date: 2016-03-02 13:04-0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@xxxxxx>\n"
@@ -18,221 +18,277 @@
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
-#: ../AgendaView.qml:52 ../DayView.qml:41 ../MonthView.qml:40
-#: ../WeekView.qml:45 ../YearView.qml:43
+#: ../AgendaView.qml:53 ../DayView.qml:68 ../MonthView.qml:48
+#: ../WeekView.qml:52 ../YearView.qml:55 ../build/install/AgendaView.qml:53
+#: ../build/install/DayView.qml:43 ../build/install/MonthView.qml:47
+#: ../build/install/WeekView.qml:45 ../build/install/YearView.qml:49
+#: ../build/pkg/AgendaView.qml:53 ../build/pkg/DayView.qml:68
+#: ../build/pkg/MonthView.qml:48 ../build/pkg/WeekView.qml:52
+#: ../build/pkg/YearView.qml:55
msgid "Today"
msgstr ""
-#: ../AgendaView.qml:62 ../calendar.qml:288 ../calendar.qml:509
+#: ../AgendaView.qml:63 ../build/install/AgendaView.qml:63
+#: ../build/install/calendar.qml:287 ../build/install/calendar.qml:503
+#: ../build/pkg/AgendaView.qml:63 ../build/pkg/calendar.qml:288
+#: ../build/pkg/calendar.qml:504 ../calendar.qml:288 ../calendar.qml:504
msgid "Agenda"
msgstr ""
-#: ../AgendaView.qml:101
+#: ../AgendaView.qml:101 ../build/install/AgendaView.qml:101
+#: ../build/pkg/AgendaView.qml:101
msgid "No upcoming events"
msgstr ""
-#: ../AgendaView.qml:104
+#: ../AgendaView.qml:104 ../build/install/AgendaView.qml:104
+#: ../build/pkg/AgendaView.qml:104
msgid "You have no calendars enabled"
msgstr ""
-#: ../AgendaView.qml:114
+#: ../AgendaView.qml:114 ../build/install/AgendaView.qml:114
+#: ../build/pkg/AgendaView.qml:114
msgid "Enable calendars"
msgstr ""
#. TRANSLATORS: the first argument (%1) refers to a start time for an event,
#. while the second one (%2) refers to the end time
-#: ../AgendaView.qml:177 ../EventBubble.qml:133
+#: ../AgendaView.qml:177 ../EventBubble.qml:97
+#: ../build/install/AgendaView.qml:177 ../build/install/EventBubble.qml:134
+#: ../build/pkg/AgendaView.qml:177 ../build/pkg/EventBubble.qml:97
#, qt-format
msgid "%1 - %2"
msgstr ""
-#: ../AgendaView.qml:183
-#, qt-format
-msgid "%1 %2 %3 %4 %5"
+#: ../AllDayEventComponent.qml:86 ../TimeLineBase.qml:50
+#: ../build/install/AllDayEventComponent.qml:108
+#: ../build/install/TimeLineBase.qml:131
+#: ../build/pkg/AllDayEventComponent.qml:86 ../build/pkg/TimeLineBase.qml:50
+msgid "New event"
msgstr ""
#. TRANSLATORS: the first parameter refers to the number of all-day events
#. on a given day. "Ev." is short form for "Events".
#. Please keep the translation of "Ev." to 3 characters only, as the week view
#. where it's shown has limited space
-#: ../AllDayEventComponent.qml:123
+#: ../AllDayEventComponent.qml:158
+#: ../build/install/AllDayEventComponent.qml:156
+#: ../build/pkg/AllDayEventComponent.qml:158
#, qt-format
msgid "%1 ev."
msgstr ""
#. TRANSLATORS: the argument refers to the number of all day events
-#: ../AllDayEventComponent.qml:127
+#: ../AllDayEventComponent.qml:162
+#: ../build/install/AllDayEventComponent.qml:160
+#: ../build/pkg/AllDayEventComponent.qml:162
#, qt-format
msgid "%1 all day event"
msgid_plural "%1 all day events"
msgstr[0] ""
msgstr[1] ""
-#: ../CalendarChoicePopup.qml:34 ../EventActions.qml:63
+#: ../CalendarChoicePopup.qml:33 ../EventActions.qml:51
+#: ../build/install/CalendarChoicePopup.qml:33
+#: ../build/install/EventActions.qml:51
+#: ../build/pkg/CalendarChoicePopup.qml:33 ../build/pkg/EventActions.qml:51
msgid "Calendars"
msgstr ""
-#: ../CalendarChoicePopup.qml:36 ../Settings.qml:31
+#: ../CalendarChoicePopup.qml:37 ../Settings.qml:32
+#: ../build/install/CalendarChoicePopup.qml:37
+#: ../build/install/Settings.qml:32 ../build/pkg/CalendarChoicePopup.qml:37
+#: ../build/pkg/Settings.qml:32
msgid "Back"
msgstr ""
#. TRANSLATORS: Please translate this string to 15 characters only.
#. Currently ,there is no way we can increase width of action menu currently.
-#: ../CalendarChoicePopup.qml:48 ../EventActions.qml:37
+#: ../CalendarChoicePopup.qml:51 ../EventActions.qml:36
+#: ../build/install/CalendarChoicePopup.qml:51
+#: ../build/install/EventActions.qml:36
+#: ../build/pkg/CalendarChoicePopup.qml:51 ../build/pkg/EventActions.qml:36
msgid "Sync"
msgstr ""
-#: ../CalendarChoicePopup.qml:48 ../EventActions.qml:37
+#: ../CalendarChoicePopup.qml:51 ../EventActions.qml:36
+#: ../build/install/CalendarChoicePopup.qml:51
+#: ../build/install/EventActions.qml:36
+#: ../build/pkg/CalendarChoicePopup.qml:51 ../build/pkg/EventActions.qml:36
msgid "Syncing"
msgstr ""
-#: ../CalendarChoicePopup.qml:72
+#: ../CalendarChoicePopup.qml:70 ../build/install/CalendarChoicePopup.qml:70
+#: ../build/pkg/CalendarChoicePopup.qml:70
msgid "Add online Calendar"
msgstr ""
-#: ../ColorPickerDialog.qml:25
+#: ../ColorPickerDialog.qml:25 ../build/install/ColorPickerDialog.qml:25
+#: ../build/pkg/ColorPickerDialog.qml:25
msgid "Select Color"
msgstr ""
#: ../ColorPickerDialog.qml:55 ../DeleteConfirmationDialog.qml:60
-#: ../EditEventConfirmationDialog.qml:53
+#: ../EditEventConfirmationDialog.qml:53 ../NewEvent.qml:340
+#: ../build/install/ColorPickerDialog.qml:55
+#: ../build/install/DeleteConfirmationDialog.qml:60
+#: ../build/install/EditEventConfirmationDialog.qml:53
+#: ../build/install/NewEvent.qml:344 ../build/pkg/ColorPickerDialog.qml:55
+#: ../build/pkg/DeleteConfirmationDialog.qml:60
+#: ../build/pkg/EditEventConfirmationDialog.qml:53
+#: ../build/pkg/NewEvent.qml:340
msgid "Cancel"
msgstr ""
-#: ../ContactChoicePopup.qml:37
+#: ../ContactChoicePopup.qml:37 ../build/install/ContactChoicePopup.qml:37
+#: ../build/pkg/ContactChoicePopup.qml:37
msgid "No contact"
msgstr ""
-#: ../ContactChoicePopup.qml:83
+#: ../ContactChoicePopup.qml:96 ../build/install/ContactChoicePopup.qml:96
+#: ../build/pkg/ContactChoicePage.qml:50
+#: ../build/pkg/ContactChoicePopup.qml:96
msgid "Search contact"
msgstr ""
#. TRANSLATORS: this is a time formatting string,
#. see http://qt-project.org/doc/qt-5/qml-qtqml-date.html#details for valid expressions.
#. It's used in the header of the month and week views
-#: ../DayView.qml:64 ../DayView.qml:157 ../MonthView.qml:62
-#: ../MonthView.qml:149 ../WeekView.qml:68 ../WeekView.qml:180
+#: ../DayView.qml:110 ../MonthView.qml:69 ../WeekView.qml:116
+#: ../build/install/DayView.qml:67 ../build/install/MonthView.qml:69
+#: ../build/install/WeekView.qml:68 ../build/pkg/DayView.qml:110
+#: ../build/pkg/MonthView.qml:69 ../build/pkg/WeekView.qml:116
msgid "MMMM yyyy"
msgstr ""
-#: ../DayView.qml:155 ../MonthView.qml:144 ../WeekView.qml:178
-#, qt-format
-msgid "%1 %2"
-msgstr ""
-
#: ../DeleteConfirmationDialog.qml:31
+#: ../build/install/DeleteConfirmationDialog.qml:31
+#: ../build/pkg/DeleteConfirmationDialog.qml:31
msgid "Delete Recurring Event"
msgstr ""
#: ../DeleteConfirmationDialog.qml:32
+#: ../build/install/DeleteConfirmationDialog.qml:32
+#: ../build/pkg/DeleteConfirmationDialog.qml:32
msgid "Delete Event"
msgstr ""
#. TRANSLATORS: argument (%1) refers to an event name.
#: ../DeleteConfirmationDialog.qml:36
+#: ../build/install/DeleteConfirmationDialog.qml:36
+#: ../build/pkg/DeleteConfirmationDialog.qml:36
#, qt-format
msgid "Delete only this event \"%1\", or all events in the series?"
msgstr ""
#: ../DeleteConfirmationDialog.qml:37
+#: ../build/install/DeleteConfirmationDialog.qml:37
+#: ../build/pkg/DeleteConfirmationDialog.qml:37
#, qt-format
msgid "Are you sure you want to delete the event \"%1\"?"
msgstr ""
#: ../DeleteConfirmationDialog.qml:40
+#: ../build/install/DeleteConfirmationDialog.qml:40
+#: ../build/pkg/DeleteConfirmationDialog.qml:40
msgid "Delete series"
msgstr ""
#: ../DeleteConfirmationDialog.qml:51
+#: ../build/install/DeleteConfirmationDialog.qml:51
+#: ../build/pkg/DeleteConfirmationDialog.qml:51
msgid "Delete this"
msgstr ""
-#: ../DeleteConfirmationDialog.qml:51 ../NewEvent.qml:68
+#: ../DeleteConfirmationDialog.qml:51 ../NewEvent.qml:347
+#: ../build/install/DeleteConfirmationDialog.qml:51
+#: ../build/install/NewEvent.qml:352
+#: ../build/pkg/DeleteConfirmationDialog.qml:51 ../build/pkg/NewEvent.qml:347
msgid "Delete"
msgstr ""
-#: ../EditEventConfirmationDialog.qml:29 ../NewEvent.qml:325
+#: ../EditEventConfirmationDialog.qml:29 ../NewEvent.qml:335
+#: ../build/install/EditEventConfirmationDialog.qml:29
+#: ../build/install/NewEvent.qml:339
+#: ../build/pkg/EditEventConfirmationDialog.qml:29
+#: ../build/pkg/NewEvent.qml:335
msgid "Edit Event"
msgstr ""
#. TRANSLATORS: argument (%1) refers to an event name.
#: ../EditEventConfirmationDialog.qml:32
+#: ../build/install/EditEventConfirmationDialog.qml:32
+#: ../build/pkg/EditEventConfirmationDialog.qml:32
#, qt-format
msgid "Edit only this event \"%1\", or all events in the series?"
msgstr ""
#: ../EditEventConfirmationDialog.qml:35
+#: ../build/install/EditEventConfirmationDialog.qml:35
+#: ../build/pkg/EditEventConfirmationDialog.qml:35
msgid "Edit series"
msgstr ""
#: ../EditEventConfirmationDialog.qml:44
+#: ../build/install/EditEventConfirmationDialog.qml:44
+#: ../build/pkg/EditEventConfirmationDialog.qml:44
msgid "Edit this"
msgstr ""
-#: ../EventActions.qml:52 ../NewEvent.qml:325
-msgid "New Event"
-msgstr ""
-
-#: ../EventActions.qml:75 ../Settings.qml:29
+#: ../EventActions.qml:63 ../Settings.qml:30
+#: ../build/install/EventActions.qml:63 ../build/install/Settings.qml:30
+#: ../build/pkg/EventActions.qml:63 ../build/pkg/Settings.qml:30
msgid "Settings"
msgstr ""
#. TRANSLATORS: the first argument (%1) refers to a time for an event,
#. while the second one (%2) refers to title of event
-#: ../EventBubble.qml:144 ../EventBubble.qml:149
+#: ../EventBubble.qml:108 ../EventBubble.qml:113
+#: ../build/install/EventBubble.qml:145 ../build/install/EventBubble.qml:150
+#: ../build/pkg/EventBubble.qml:108 ../build/pkg/EventBubble.qml:113
#, qt-format
msgid "%1 <b>%2</b>"
msgstr ""
-#: ../EventDetails.qml:44 ../NewEvent.qml:436
+#: ../EventDetails.qml:43 ../NewEvent.qml:483
+#: ../build/install/EventDetails.qml:43 ../build/install/NewEvent.qml:489
+#: ../build/pkg/EventDetails.qml:43 ../build/pkg/NewEvent.qml:483
msgid "Event Details"
msgstr ""
#. TRANSLATORS: the first parameter refers to the name of event calendar.
-#: ../EventDetails.qml:69
+#: ../EventDetails.qml:68 ../build/install/EventDetails.qml:68
+#: ../build/pkg/EventDetails.qml:68
#, qt-format
msgid "%1 Calendar"
msgstr ""
-#: ../EventDetails.qml:143
-#, qt-format
-msgid "%1 %2 %3 - %4 %5 %6 (All Day)"
-msgstr ""
-
-#: ../EventDetails.qml:147
+#: ../EventDetails.qml:129 ../build/install/EventDetails.qml:129
+#: ../build/pkg/EventDetails.qml:129
#, qt-format
msgid "%1 - %2 (All Day)"
msgstr ""
-#: ../EventDetails.qml:153
-#, qt-format
-msgid "%1 %2 %3 (All Day)"
-msgstr ""
-
-#: ../EventDetails.qml:156
+#: ../EventDetails.qml:133 ../build/install/EventDetails.qml:133
+#: ../build/pkg/EventDetails.qml:133
#, qt-format
msgid "%1 (All Day)"
msgstr ""
-#: ../EventDetails.qml:162
-#, qt-format
-msgid "%1 %2 %3, %4 - %5 %6 %7, %8"
-msgstr ""
-
-#: ../EventDetails.qml:171
-#, qt-format
-msgid "%1 %2 %3, %4 - %5"
-msgstr ""
-
-#: ../EventDetails.qml:238
+#: ../EventDetails.qml:203 ../build/install/EventDetails.qml:203
+#: ../build/pkg/EventDetails.qml:203
msgid "Edit"
msgstr ""
-#: ../EventDetails.qml:389 ../NewEvent.qml:538
+#: ../EventDetails.qml:354 ../NewEvent.qml:589
+#: ../build/install/EventDetails.qml:354 ../build/install/NewEvent.qml:595
+#: ../build/pkg/EventDetails.qml:354 ../build/pkg/NewEvent.qml:589
msgid "Guests"
msgstr ""
-#: ../EventDetails.qml:432 ../EventReminder.qml:35 ../NewEvent.qml:635
+#: ../EventDetails.qml:397 ../EventReminder.qml:35 ../NewEvent.qml:701
+#: ../build/install/EventDetails.qml:397 ../build/install/EventReminder.qml:35
+#: ../build/install/NewEvent.qml:711 ../build/pkg/EventDetails.qml:397
+#: ../build/pkg/EventReminder.qml:35 ../build/pkg/NewEvent.qml:701
msgid "Reminder"
msgstr ""
@@ -241,31 +297,40 @@
#. and as the header of the list item that shows the repetition
#. summary in the page that displays the event details
#: ../EventRepetition.qml:40 ../EventRepetition.qml:155
+#: ../build/install/EventRepetition.qml:40
+#: ../build/install/EventRepetition.qml:155
+#: ../build/pkg/EventRepetition.qml:40 ../build/pkg/EventRepetition.qml:155
msgid "Repeat"
msgstr ""
-#: ../EventRepetition.qml:174
+#: ../EventRepetition.qml:174 ../build/install/EventRepetition.qml:174
+#: ../build/pkg/EventRepetition.qml:174
msgid "Repeats On:"
msgstr ""
-#: ../EventRepetition.qml:219
+#: ../EventRepetition.qml:219 ../build/install/EventRepetition.qml:219
+#: ../build/pkg/EventRepetition.qml:219
msgid "Recurring event ends"
msgstr ""
#. TRANSLATORS: this refers to how often a recurrent event repeats
#. and it is shown as the header of the option selector to choose
#. its repetition
-#: ../EventRepetition.qml:242 ../NewEvent.qml:619
+#: ../EventRepetition.qml:242 ../NewEvent.qml:685
+#: ../build/install/EventRepetition.qml:242 ../build/install/NewEvent.qml:695
+#: ../build/pkg/EventRepetition.qml:242 ../build/pkg/NewEvent.qml:685
msgid "Repeats"
msgstr ""
-#: ../EventRepetition.qml:267
+#: ../EventRepetition.qml:267 ../build/install/EventRepetition.qml:267
+#: ../build/pkg/EventRepetition.qml:267
msgid "Date"
msgstr ""
#. TRANSLATORS: the argument refers to multiple recurrence of event with count .
#. E.g. "Daily; 5 times."
-#: ../EventUtils.qml:75
+#: ../EventUtils.qml:75 ../build/install/EventUtils.qml:75
+#: ../build/pkg/EventUtils.qml:75
#, qt-format
msgid "%1; %2 time"
msgid_plural "%1; %2 times"
@@ -274,217 +339,270 @@
#. TRANSLATORS: the argument refers to recurrence until user selected date.
#. E.g. "Daily; until 12/12/2014."
-#: ../EventUtils.qml:79
+#: ../EventUtils.qml:79 ../build/install/EventUtils.qml:79
+#: ../build/pkg/EventUtils.qml:79
#, qt-format
msgid "%1; until %2"
msgstr ""
#. TRANSLATORS: the argument refers to several different days of the week.
#. E.g. "Weekly on Mondays, Tuesdays"
-#: ../EventUtils.qml:102
+#: ../EventUtils.qml:102 ../build/install/EventUtils.qml:102
+#: ../build/pkg/EventUtils.qml:102
#, qt-format
msgid "Weekly on %1"
msgstr ""
-#: ../HeaderDateComponent.qml:90
-#, qt-format
-msgid "%1 %2 %3"
-msgstr ""
-
-#: ../LimitLabelModel.qml:25
+#: ../LimitLabelModel.qml:25 ../build/install/LimitLabelModel.qml:25
+#: ../build/pkg/LimitLabelModel.qml:25
msgid "Never"
msgstr ""
-#: ../LimitLabelModel.qml:26
+#: ../LimitLabelModel.qml:26 ../build/install/LimitLabelModel.qml:26
+#: ../build/pkg/LimitLabelModel.qml:26
msgid "After X Occurrence"
msgstr ""
-#: ../LimitLabelModel.qml:27
+#: ../LimitLabelModel.qml:27 ../build/install/LimitLabelModel.qml:27
+#: ../build/pkg/LimitLabelModel.qml:27
msgid "After Date"
msgstr ""
-#: ../MonthComponent.qml:262
+#: ../MonthComponent.qml:312 ../build/install/MonthComponent.qml:312
+#: ../build/pkg/MonthComponent.qml:312
msgid "Wk"
msgstr ""
-#: ../NewEvent.qml:84
+#: ../NewEvent.qml:171 ../build/install/NewEvent.qml:173
+#: ../build/pkg/NewEvent.qml:171
+msgid "End time can't be before start time"
+msgstr ""
+
+#: ../NewEvent.qml:335 ../NewEventBottomEdge.qml:52
+#: ../build/install/NewEvent.qml:339
+#: ../build/install/NewEventBottomEdge.qml:49 ../build/pkg/NewEvent.qml:335
+#: ../build/pkg/NewEventBottomEdge.qml:52
+msgid "New Event"
+msgstr ""
+
+#: ../NewEvent.qml:364 ../build/install/NewEvent.qml:370
+#: ../build/pkg/NewEvent.qml:364
msgid "Save"
msgstr ""
-#: ../NewEvent.qml:192
-msgid "End time can't be before start time"
-msgstr ""
-
-#: ../NewEvent.qml:335
+#: ../NewEvent.qml:375 ../build/install/NewEvent.qml:381
+#: ../build/pkg/NewEvent.qml:375
msgid "Error"
msgstr ""
-#: ../NewEvent.qml:337
+#: ../NewEvent.qml:377 ../build/install/NewEvent.qml:383
+#: ../build/pkg/NewEvent.qml:377
msgid "OK"
msgstr ""
-#: ../NewEvent.qml:390
+#: ../NewEvent.qml:437 ../build/install/NewEvent.qml:443
+#: ../build/pkg/NewEvent.qml:437
msgid "From"
msgstr ""
-#: ../NewEvent.qml:403
+#: ../NewEvent.qml:450 ../build/install/NewEvent.qml:456
+#: ../build/pkg/NewEvent.qml:450
msgid "To"
msgstr ""
-#: ../NewEvent.qml:420
+#: ../NewEvent.qml:467 ../build/install/NewEvent.qml:473
+#: ../build/pkg/NewEvent.qml:467
msgid "All day event"
msgstr ""
-#: ../NewEvent.qml:449
+#: ../NewEvent.qml:497 ../build/install/NewEvent.qml:503
+#: ../build/pkg/NewEvent.qml:497
msgid "Event Name"
msgstr ""
-#: ../NewEvent.qml:467
+#: ../NewEvent.qml:515 ../build/install/NewEvent.qml:521
+#: ../build/pkg/NewEvent.qml:515
msgid "Description"
msgstr ""
-#: ../NewEvent.qml:485
+#: ../NewEvent.qml:534 ../build/install/NewEvent.qml:540
+#: ../build/pkg/NewEvent.qml:534
msgid "Location"
msgstr ""
-#: ../NewEvent.qml:500 com.ubuntu.calendar_calendar.desktop.in.in.h:1
+#: ../NewEvent.qml:549 ../build/install/NewEvent.qml:555
+#: ../build/pkg/NewEvent.qml:549
+#: com.ubuntu.calendar_calendar.desktop.in.in.h:1
msgid "Calendar"
msgstr ""
-#: ../NewEvent.qml:542
+#: ../NewEvent.qml:598 ../build/install/NewEvent.qml:604
+#: ../build/pkg/NewEvent.qml:598
msgid "Add Guest"
msgstr ""
#: ../RecurrenceLabelDefines.qml:23
+#: ../build/install/RecurrenceLabelDefines.qml:23
+#: ../build/pkg/RecurrenceLabelDefines.qml:23
msgid "Once"
msgstr ""
#: ../RecurrenceLabelDefines.qml:24
+#: ../build/install/RecurrenceLabelDefines.qml:24
+#: ../build/pkg/RecurrenceLabelDefines.qml:24
msgid "Daily"
msgstr ""
#: ../RecurrenceLabelDefines.qml:25
+#: ../build/install/RecurrenceLabelDefines.qml:25
+#: ../build/pkg/RecurrenceLabelDefines.qml:25
msgid "On Weekdays"
msgstr ""
#. TRANSLATORS: The arguments refer to days of the week. E.g. "On Monday, Tuesday, Thursday"
#: ../RecurrenceLabelDefines.qml:27
+#: ../build/install/RecurrenceLabelDefines.qml:27
+#: ../build/pkg/RecurrenceLabelDefines.qml:27
#, qt-format
msgid "On %1, %2 ,%3"
msgstr ""
#. TRANSLATORS: The arguments refer to days of the week. E.g. "On Monday and Thursday"
#: ../RecurrenceLabelDefines.qml:29
+#: ../build/install/RecurrenceLabelDefines.qml:29
+#: ../build/pkg/RecurrenceLabelDefines.qml:29
#, qt-format
msgid "On %1 and %2"
msgstr ""
#: ../RecurrenceLabelDefines.qml:30
+#: ../build/install/RecurrenceLabelDefines.qml:30
+#: ../build/pkg/RecurrenceLabelDefines.qml:30
msgid "Weekly"
msgstr ""
#: ../RecurrenceLabelDefines.qml:31
+#: ../build/install/RecurrenceLabelDefines.qml:31
+#: ../build/pkg/RecurrenceLabelDefines.qml:31
msgid "Monthly"
msgstr ""
#: ../RecurrenceLabelDefines.qml:32
+#: ../build/install/RecurrenceLabelDefines.qml:32
+#: ../build/pkg/RecurrenceLabelDefines.qml:32
msgid "Yearly"
msgstr ""
-#: ../RemindersModel.qml:26
+#: ../RemindersModel.qml:26 ../build/install/RemindersModel.qml:26
+#: ../build/pkg/RemindersModel.qml:26
msgid "No Reminder"
msgstr ""
#. TRANSLATORS: this refers to when a reminder should be shown as a notification
#. in the indicators. "On Event" means that it will be shown right at the time
#. the event starts, not any time before
-#: ../RemindersModel.qml:30
+#: ../RemindersModel.qml:30 ../build/install/RemindersModel.qml:30
+#: ../build/pkg/RemindersModel.qml:30
msgid "On Event"
msgstr ""
-#: ../RemindersModel.qml:31
+#: ../RemindersModel.qml:31 ../build/install/RemindersModel.qml:31
+#: ../build/pkg/RemindersModel.qml:31
msgid "5 minutes"
msgstr ""
-#: ../RemindersModel.qml:32
+#: ../RemindersModel.qml:32 ../build/install/RemindersModel.qml:32
+#: ../build/pkg/RemindersModel.qml:32
msgid "15 minutes"
msgstr ""
-#: ../RemindersModel.qml:33
+#: ../RemindersModel.qml:33 ../build/install/RemindersModel.qml:33
+#: ../build/pkg/RemindersModel.qml:33
msgid "30 minutes"
msgstr ""
-#: ../RemindersModel.qml:34
+#: ../RemindersModel.qml:34 ../build/install/RemindersModel.qml:34
+#: ../build/pkg/RemindersModel.qml:34
msgid "1 hour"
msgstr ""
-#: ../RemindersModel.qml:35
+#: ../RemindersModel.qml:35 ../build/install/RemindersModel.qml:35
+#: ../build/pkg/RemindersModel.qml:35
msgid "2 hours"
msgstr ""
-#: ../RemindersModel.qml:36
+#: ../RemindersModel.qml:36 ../build/install/RemindersModel.qml:36
+#: ../build/pkg/RemindersModel.qml:36
msgid "1 day"
msgstr ""
-#: ../RemindersModel.qml:37
+#: ../RemindersModel.qml:37 ../build/install/RemindersModel.qml:37
+#: ../build/pkg/RemindersModel.qml:37
msgid "2 days"
msgstr ""
-#: ../RemindersModel.qml:38
+#: ../RemindersModel.qml:38 ../build/install/RemindersModel.qml:38
+#: ../build/pkg/RemindersModel.qml:38
msgid "1 week"
msgstr ""
-#: ../RemindersModel.qml:39
+#: ../RemindersModel.qml:39 ../build/install/RemindersModel.qml:39
+#: ../build/pkg/RemindersModel.qml:39
msgid "2 weeks"
msgstr ""
-#: ../Settings.qml:55
+#: ../Settings.qml:60 ../build/install/Settings.qml:60
+#: ../build/pkg/Settings.qml:60
msgid "Show week numbers"
msgstr ""
-#: ../Settings.qml:71
-msgid "Show lunar calendar"
-msgstr ""
-
-#: ../TimeLineBase.qml:73
-msgid "Untitled"
-msgstr ""
-
#. TRANSLATORS: W refers to Week, followed by the actual week number (%1)
-#: ../TimeLineHeader.qml:53
+#: ../TimeLineHeader.qml:54 ../build/install/TimeLineHeader.qml:54
+#: ../build/pkg/TimeLineHeader.qml:54
#, qt-format
msgid "W%1"
msgstr ""
-#: ../TimeLineHeader.qml:65
+#: ../TimeLineHeader.qml:66 ../build/install/TimeLineHeader.qml:66
+#: ../build/pkg/TimeLineHeader.qml:66
msgid "All Day"
msgstr ""
-#: ../YearView.qml:61 ../YearView.qml:112
+#: ../YearView.qml:72 ../build/install/YearView.qml:66
+#: ../build/pkg/YearView.qml:72
#, qt-format
msgid "Year %1"
msgstr ""
-#: ../calendar.qml:46
+#: ../build/install/calendar.qml:45 ../build/pkg/calendar.qml:45
+#: ../calendar.qml:45
msgid ""
"Calendar app accept four arguments: --starttime, --endtime, --newevent and --"
"eventid. They will be managed by system. See the source for a full comment "
"about them"
msgstr ""
-#: ../calendar.qml:256 ../calendar.qml:425
+#: ../build/install/calendar.qml:255 ../build/install/calendar.qml:419
+#: ../build/pkg/calendar.qml:256 ../build/pkg/calendar.qml:420
+#: ../calendar.qml:256 ../calendar.qml:420
msgid "Year"
msgstr ""
-#: ../calendar.qml:264 ../calendar.qml:446
+#: ../build/install/calendar.qml:263 ../build/install/calendar.qml:440
+#: ../build/pkg/calendar.qml:264 ../build/pkg/calendar.qml:441
+#: ../calendar.qml:264 ../calendar.qml:441
msgid "Month"
msgstr ""
-#: ../calendar.qml:272 ../calendar.qml:467
+#: ../build/install/calendar.qml:271 ../build/install/calendar.qml:461
+#: ../build/pkg/calendar.qml:272 ../build/pkg/calendar.qml:462
+#: ../calendar.qml:272 ../calendar.qml:462
msgid "Week"
msgstr ""
-#: ../calendar.qml:280 ../calendar.qml:488
+#: ../build/install/calendar.qml:279 ../build/install/calendar.qml:482
+#: ../build/pkg/calendar.qml:280 ../build/pkg/calendar.qml:483
+#: ../calendar.qml:280 ../calendar.qml:483
msgid "Day"
msgstr ""
=== added file 'tests/unittests/tst_calendar_canvas.qml'
--- tests/unittests/tst_calendar_canvas.qml 1970-01-01 00:00:00 +0000
+++ tests/unittests/tst_calendar_canvas.qml 2016-03-03 21:42:32 +0000
@@ -0,0 +1,672 @@
+import QtQuick 2.0
+import QtTest 1.0
+import QtOrganizer 5.0
+
+import "../../calendar_canvas.js" as CanvasJs
+
+TestCase{
+ id: root
+ name: "Date tests"
+
+ Component {
+ id: modelComp
+
+ OrganizerModel {
+ id: eventModel
+
+ manager: "memory"
+ startPeriod: new Date(2016, 7, 2, 0, 0, 0, 0)
+ endPeriod: new Date(2016, 13, 2, 0, 0, 0, 0)
+ autoUpdate: true
+ }
+ }
+
+ Component {
+ id: eventComp
+
+ Event {
+ }
+ }
+
+ Component {
+ id: spyComp
+
+ SignalSpy {
+ id: spy
+ signalName: "onModelChanged"
+ }
+ }
+
+ WorkerScript {
+ id: worker
+
+ property var reply: null
+ property var eventsById: []
+ property bool done: false
+
+ function start(model, startDate)
+ {
+ worker.done = false
+ worker.reply = null
+ worker.eventsById = []
+
+ var itemsOfTheDay = model.itemsByTimePeriod(startDate.midnight(), startDate.endOfDay())
+ for(var i=0; i < itemsOfTheDay.length; i++) {
+ var e = itemsOfTheDay[i]
+ worker.eventsById[e.itemId] = e
+ }
+
+ console.debug("Items of Day:"+ itemsOfTheDay.length)
+
+ var events = CanvasJs.parseDayEvents(startDate.midnight(), itemsOfTheDay)
+ console.debug("Events:" + events.length)
+ worker.sendMessage({'events': events})
+ }
+
+ source: Qt.resolvedUrl("../../calendar_canvas_worker.js")
+ onMessage: {
+ var reply = messageObject.reply
+ console.debug("Reply:" + reply.length)
+ for(var i=0; i < reply.length; i++) {
+ var info = reply[i]
+ console.debug("I:" + i + " id:" + info.eventId + " e:" + worker.eventsById[info.eventId])
+ info['event'] = worker.eventsById[info.eventId]
+ }
+ reply.sort(sortEventsByDateAndY)
+ worker.reply =reply
+ worker.done = true
+ }
+ }
+
+ function create_event_from_data(model, data)
+ {
+ return eventComp.createObject(model,
+ {'allDay': false,
+ 'displayLabel': data.label,
+ 'startDateTime': data.startDate,
+ 'endDateTime': data.endDate})
+ }
+
+ function create_events(data)
+ {
+ var model = modelComp.createObject(root, {});
+ var spy = spyComp.createObject(root, {'target': model})
+
+ for(var i=0; i < data.length; i++) {
+ var ev = create_event_from_data(model, data[i])
+ model.saveItem(ev)
+ tryCompare(spy, 'count', i+1)
+ }
+ compare(model.itemCount, data.length)
+ return model
+ }
+
+ function debug_map(map)
+ {
+ for(var k in map) {
+ var info = map[k]
+ console.debug("\t" + (info.event ? info.event.displayLabel : "null") + " y:" + info.y)
+ }
+ }
+
+ function sortEventsByDateAndY(eventA, eventB)
+ {
+ if (eventA.startTime < eventB.startTime)
+ return -1
+ else if (eventA.startTime > eventB.startTime)
+ return 1
+ else {
+ if (eventA.y < eventB.y)
+ return -1
+ else if (eventA.y > eventB.y)
+ return 1
+ }
+ return 0
+ }
+
+ // 10:00 ----
+ // |XX|
+ // 11:00 ----
+
+ // 12:00
+
+ // 13:00 ----
+ // ----
+ // 14:00
+ function test_two_events_in_distinct_time()
+ {
+ var data = [{startDate: new Date(2016, 7, 2, 13, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 13, 30, 0, 0),
+ label: "Event at 13:00 until 13:30"},
+ {startDate: new Date(2016, 7, 2, 10, 10, 0, 0),
+ endDate: new Date(2016, 7, 2, 11, 00, 0, 0),
+ label: "Event at 10:10 until 11:00"}]
+ var model = create_events(data)
+
+ var startDate = new Date(2016, 7, 2, 11, 11, 11, 11)
+ worker.start(model, startDate)
+ tryCompare(worker, 'done', true)
+
+ var eventMap = worker.reply
+ //"Event at 10:10 until 11:00"
+ var eventsAtTenTen = eventMap[0]
+ compare(eventsAtTenTen.event.displayLabel, "Event at 10:10 until 11:00")
+ compare(eventsAtTenTen.y, 0)
+
+ //"Event at 13:00 until 13:30"
+ var eventsAtOnePm = eventMap[1]
+ compare(eventsAtOnePm.event.displayLabel, "Event at 13:00 until 13:30")
+ compare(eventsAtTenTen.y, 0)
+ }
+
+ // 13:00 ---- ----
+ // |XX| |XX|
+ // 14:00 ---- ----
+ //
+ // 15:00
+ function test_two_events_at_the_same_time()
+ {
+ var data = [{startDate: new Date(2016, 7, 2, 13, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 13, 30, 0, 1),
+ label: "Event at 13:00 until 13:30"},
+ {startDate: new Date(2016, 7, 2, 13, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 13, 30, 0, 0),
+ label: "Event at 13:00 until 13:30 (1)"}]
+ var model = create_events(data)
+
+ var startDate = new Date(2016, 7, 2, 11, 11, 11, 11)
+ worker.start(model, startDate)
+ tryCompare(worker, 'done', true)
+ var eventMap = worker.reply
+
+ //"Event at 13:00 until 13:30"
+ var eventsAtOnePm = eventMap[0]
+ compare(eventsAtOnePm.event.displayLabel, "Event at 13:00 until 13:30")
+ compare(eventsAtOnePm.y, 0)
+
+ eventsAtOnePm = eventMap[1]
+ compare(eventsAtOnePm.event.displayLabel, "Event at 13:00 until 13:30 (1)")
+ compare(eventsAtOnePm.y, 1)
+ }
+
+ // 13:00 ----
+ // |XX|
+ // 14:00 |XX| ----
+ // |XX| |XX|
+ // 15:00 |XX| ----
+ // ----
+ // 16:00
+ function test_intersec_two_events()
+ {
+ var data = [{startDate: new Date(2016, 7, 2, 13, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 15, 30, 0, 0),
+ label: "Event at 13:00 until 15:30"},
+ {startDate: new Date(2016, 7, 2, 14, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 15, 00, 0, 0),
+ label: "Event at 14:00 until 15:00"}]
+ var model = create_events(data)
+
+ var startDate = new Date(2016, 7, 2, 11, 11, 11, 11)
+ worker.start(model, startDate)
+ tryCompare(worker, 'done', true)
+ var eventMap = worker.reply
+
+ //"Event at 13:00 until 15:30"
+ var eventsAtOnePm = eventMap[0]
+ compare(eventsAtOnePm.event.displayLabel, "Event at 13:00 until 15:30")
+ compare(eventsAtOnePm.y, 0)
+
+ //Event at 14:00 until 15:00
+ var eventsAtTwoPm = eventMap[1]
+ compare(eventsAtTwoPm.event.displayLabel, "Event at 14:00 until 15:00")
+ compare(eventsAtTwoPm.y, 1)
+ }
+
+ // 13:00 ----
+ // |XX| ----
+ // 14:00 |XX| |XX|
+ // |XX| |XX|
+ // 15:00 |XX| |XX| ----
+ // ---- ---- |XX|
+ // 16:00 |XX|
+ // |XX|
+ // 17:00 |XX|
+ // |XX|
+ // 18:00 |XX|
+ // |XX|
+ // 19:00 ----
+ function test_intersec_three_events()
+ {
+ var data = [{startDate: new Date(2016, 7, 2, 13, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 15, 30, 0, 0),
+ label: "Event at 13:00 until 15:30"},
+ {startDate: new Date(2016, 7, 2, 13, 30, 0, 0),
+ endDate: new Date(2016, 7, 2, 15, 30, 0, 0),
+ label: "Event at 13:30 until 15:30"},
+ {startDate: new Date(2016, 7, 2, 15, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 19, 00, 0, 0),
+ label: "Event at 15:00 until 19:00"}]
+ var model = create_events(data)
+
+ var startDate = new Date(2016, 7, 2, 11, 11, 11, 11)
+ worker.start(model, startDate)
+ tryCompare(worker, 'done', true)
+ var eventMap = worker.reply
+
+ //"Event at 13:00 until 15:30"
+ var eventsAtOnePm = eventMap[0]
+ compare(eventsAtOnePm.event.displayLabel, "Event at 13:00 until 15:30")
+ compare(eventsAtOnePm.y, 0)
+
+ //"Event at 13:30 until 15:30"
+ var eventsAtHalfPastOnePm = eventMap[1]
+ compare(eventsAtHalfPastOnePm.event.displayLabel, "Event at 13:30 until 15:30")
+ compare(eventsAtHalfPastOnePm.y, 1)
+
+ //"Event at 15:00 until 19:00"
+ var eventsAtThreePm = eventMap[2]
+ compare(eventsAtThreePm.event.displayLabel, "Event at 15:00 until 19:00")
+ compare(eventsAtThreePm.y, 2)
+ }
+
+ // 13:00 ----
+ // |XX|
+ // 14:00 |XX| ----
+ // |XX| |XX|
+ // 15:00 |XX| |XX|
+ // ---- |XX|
+ // 16:00 |XX|
+ // ---- |XX|
+ // 17:00 |XX| |XX|
+ // ---- |XX|
+ // 18:00 |XX|
+ // |XX|
+ // 19:00 |XX|
+ // ----
+ // 20:00
+ function test_intersec_three_events_2()
+ {
+ var data = [{startDate: new Date(2016, 7, 2, 13, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 15, 30, 0, 0),
+ label: "Event at 13:00 until 15:30"},
+ {startDate: new Date(2016, 7, 2, 14, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 19, 30, 0, 0),
+ label: "Event at 14:00 until 19:30"},
+ {startDate: new Date(2016, 7, 2, 16, 30, 0, 0),
+ endDate: new Date(2016, 7, 2, 17, 30, 0, 0),
+ label: "Event at 16:30 until 17:30"}]
+ var model = create_events(data)
+
+ var startDate = new Date(2016, 7, 2, 11, 11, 11, 11)
+ worker.start(model, startDate)
+ tryCompare(worker, 'done', true)
+ var eventMap = worker.reply
+
+ //"Event at 13:00 until 15:30"
+ var eventsAtOnePm = eventMap[0]
+ compare(eventsAtOnePm.event.displayLabel, "Event at 13:00 until 15:30")
+ compare(eventsAtOnePm.y, 0)
+
+ //"Event at 14:00 until 19:30"
+ var eventsAtTwoPm = eventMap[1]
+ compare(eventsAtTwoPm.event.displayLabel, "Event at 14:00 until 19:30")
+ compare(eventsAtTwoPm.y, 1)
+
+
+ //"Event at 16:30 until 17:30"
+ var eventsHalfPastFor = eventMap[2]
+ compare(eventsHalfPastFor.event.displayLabel, "Event at 16:30 until 17:30")
+ compare(eventsHalfPastFor.y, 0)
+
+ }
+
+ // 13:00 ---- ---- ----
+ // |XX| |XX| |XX|
+ // 14:00 |XX| |XX| ----
+ // |XX| |XX|
+ // 15:00 |XX| |XX| ---- ---- ----
+ // |XX| |XX| |XX| |XX| |XX|
+ // 16:00 |XX| |XX| ---- ---- ----
+ // |XX| |XX|
+ // 17:00 |XX| |XX|
+ // |XX| ----
+ // 18:00 |XX|
+ function test_intersec_events_after()
+ {
+ var data = [{startDate: new Date(2016, 7, 2, 13, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 17, 30, 0, 0),
+ label: "Event at 13:00 until 17:30"},
+ {startDate: new Date(2016, 7, 2, 13, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 18, 30, 0, 0),
+ label: "Event at 13:00 until 18:30"},
+ {startDate: new Date(2016, 7, 2, 13, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 14, 0, 0, 0),
+ label: "Event at 13:00 until 14:00"},
+ {startDate: new Date(2016, 7, 2, 15, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 16, 0, 0, 3),
+ label: "Event at 15:00 until 16:00"},
+ {startDate: new Date(2016, 7, 2, 15, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 16, 0, 0, 2),
+ label: "Event at 15:00 until 16:00 (1)"},
+ {startDate: new Date(2016, 7, 2, 15, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 16, 0, 0, 1),
+ label: "Event at 15:00 until 16:00 (2)"}
+ ]
+ var model = create_events(data)
+
+ var startDate = new Date(2016, 7, 2, 11, 11, 11, 11)
+ worker.start(model, startDate)
+ tryCompare(worker, 'done', true)
+ var eventMap = worker.reply
+
+ //Events @13:00
+ var eventsAtOnePm = eventMap[0]
+ compare(eventsAtOnePm.event.displayLabel, "Event at 13:00 until 18:30")
+ compare(eventsAtOnePm.y, 0)
+
+ eventsAtOnePm = eventMap[1]
+ compare(eventsAtOnePm.event.displayLabel, "Event at 13:00 until 17:30")
+ compare(eventsAtOnePm.y, 1)
+
+ eventsAtOnePm = eventMap[2]
+ compare(eventsAtOnePm.event.displayLabel, "Event at 13:00 until 14:00")
+ compare(eventsAtOnePm.y, 2)
+
+
+ //"Events @15:00
+ var eventsAtThreePm = eventMap[3]
+ compare(eventsAtThreePm.event.displayLabel, "Event at 15:00 until 16:00")
+ compare(eventsAtThreePm.y, 2)
+
+ eventsAtThreePm = eventMap[4]
+ compare(eventsAtThreePm.event.displayLabel, "Event at 15:00 until 16:00 (1)")
+ compare(eventsAtThreePm.y, 3)
+
+ eventsAtThreePm = eventMap[5]
+ compare(eventsAtThreePm.event.displayLabel, "Event at 15:00 until 16:00 (2)")
+ compare(eventsAtThreePm.y, 4)
+ }
+
+ // 12:00 ---- ----
+ // |XX| |XX| ----
+ // 14:00 |XX| |XX| |XX|
+ // |XX| |XX| ----
+ // 15:00 |XX| |XX|
+ // ---- ----
+ function test_intersec_three_events_3()
+ {
+ var data = [{startDate: new Date(2016, 7, 2, 12, 30, 0, 0),
+ endDate: new Date(2016, 7, 2, 14, 30, 0, 0),
+ label: "Event at 12:30 until 14:30"},
+ {startDate: new Date(2016, 7, 2, 12, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 15, 30, 0, 1),
+ label: "Event at 12:00 until 15:30"},
+ {startDate: new Date(2016, 7, 2, 12, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 15, 30, 0, 0),
+ label: "Event at 12:00 until 15:30 (1)"}]
+ var model = create_events(data)
+
+ var startDate = new Date(2016, 7, 2, 11, 11, 11, 11)
+ worker.start(model, startDate)
+ tryCompare(worker, 'done', true)
+ var eventMap = worker.reply
+
+ //"Event at 12:00 until 15:30"
+ var eventsAtNoon = eventMap[0]
+ compare(eventsAtNoon.event.displayLabel, "Event at 12:00 until 15:30")
+ compare(eventsAtNoon.y, 0)
+
+ eventsAtNoon = eventMap[1]
+ compare(eventsAtNoon.event.displayLabel, "Event at 12:00 until 15:30 (1)")
+ compare(eventsAtNoon.y, 1)
+
+ //"Event at 12:30 until 14:30"
+ var eventsAtHalfPastNoon = eventMap[2]
+ compare(eventsAtHalfPastNoon.event.displayLabel, "Event at 12:30 until 14:30")
+ compare(eventsAtHalfPastNoon.y, 2)
+ }
+
+ function test_intersec_three_events_4()
+ {
+ var data = [{startDate: new Date(2016, 7, 2, 12, 15, 0, 0),
+ endDate: new Date(2016, 7, 2, 12, 30, 0, 0),
+ label: "Event at 12:15 until 12:30"},
+ {startDate: new Date(2016, 7, 2, 12, 00, 0, 0),
+ endDate: new Date(2016, 7, 2, 12, 30, 0, 1),
+ label: "Event at 12:00 until 12:30"},
+ {startDate: new Date(2016, 7, 2, 12, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 12, 30, 0, 0),
+ label: "Event at 12:00 until 12:30 (1)"}]
+ var model = create_events(data)
+
+ var startDate = new Date(2016, 7, 2, 11, 11, 11, 11)
+ worker.start(model, startDate)
+ tryCompare(worker, 'done', true)
+ var eventMap = worker.reply
+
+ //"Event at 12:00 until 12:30"
+ var eventsAtNoon = eventMap[0]
+ compare(eventsAtNoon.event.displayLabel, "Event at 12:00 until 12:30")
+ compare(eventsAtNoon.y, 0)
+
+ eventsAtNoon = eventMap[1]
+ compare(eventsAtNoon.event.displayLabel, "Event at 12:00 until 12:30 (1)")
+ compare(eventsAtNoon.y, 1)
+
+ //"Event at 12:15 until 12:20"
+ var eventsAtHalfPastNoon = eventMap[2]
+ compare(eventsAtHalfPastNoon.event.displayLabel, "Event at 12:15 until 12:30")
+ compare(eventsAtHalfPastNoon.y, 2)
+ }
+
+ function test_intersec_next_day_events()
+ {
+ var data = [{startDate: new Date(2016, 7, 2, 23, 00, 0, 0),
+ endDate: new Date(2016, 7, 2, 23, 30, 0, 0),
+ label: "Event at 23:00 until 23:30"},
+ {startDate: new Date(2016, 7, 2, 23, 10, 0, 0),
+ endDate: new Date(2016, 7, 3, 00, 30, 0, 0),
+ label: "Event at 23:10 until 00:30"}]
+ var model = create_events(data)
+ var startDate = new Date(2016, 7, 2, 11, 11, 11, 11)
+ worker.start(model, startDate)
+ tryCompare(worker, 'done', true)
+ var eventMap = worker.reply
+
+ //"Event at 23:00 until 23:30"
+ var eventsAtElevenPm = eventMap[0]
+ compare(eventsAtElevenPm.event.displayLabel, "Event at 23:00 until 23:30")
+ compare(eventsAtElevenPm.y, 0)
+
+ //"Event at 23:30 until 00:30"
+ var eventsAtHalfPastElevenPm = eventMap[1]
+ compare(eventsAtHalfPastElevenPm.event.displayLabel, "Event at 23:10 until 00:30")
+ compare(eventsAtHalfPastElevenPm.y, 1)
+ }
+
+ // 10:00 ----
+ // |XX|
+ // 11:00 |XX| ----
+ // |XX| |XX|
+ // 12:00 |XX| |XX|
+ // |XX| |XX|
+ // 13:00 ---- |XX|
+ // |XX| |XX|
+ // 14:00 |XX| ----
+ // |XX|
+ // 15:00 |XX| ----
+ // |XX| |XX|
+ // 16:00 |XX| |XX|
+ // ---- |XX|
+ // 17:00 ----
+ function test_intersec_five_events()
+ {
+ var data = [{startDate: new Date(2016, 7, 2, 10, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 13, 0, 0, 0),
+ label: "Event at 10:00 until 13:00"},
+ {startDate: new Date(2016, 7, 2, 11, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 14, 00, 0, 0),
+ label: "Event at 11:00 until 14:00"},
+ {startDate: new Date(2016, 7, 2, 13, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 16, 30, 0, 0),
+ label: "Event at 13:00 until 16:30"},
+ {startDate: new Date(2016, 7, 2, 15, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 17, 00, 0, 0),
+ label: "Event at 15:00 until 17:00"}
+ ]
+ var model = create_events(data)
+
+ var startDate = new Date(2016, 7, 2, 11, 11, 11, 11)
+ worker.start(model, startDate)
+ tryCompare(worker, 'done', true)
+ var eventMap = worker.reply
+
+ // "Event at 10:00 until 13:00"
+ var eventsAtTen = eventMap[0]
+ compare(eventsAtTen.event.displayLabel, "Event at 10:00 until 13:00")
+ compare(eventsAtTen.y, 0)
+
+ //"Event at 11:00 until 14:00"
+ var eventsAtEleven = eventMap[1]
+ compare(eventsAtEleven.event.displayLabel, "Event at 11:00 until 14:00")
+ compare(eventsAtEleven.y, 1)
+
+ //"Event at 13:00 until 16:30"
+ var eventAtOnePm = eventMap[2]
+ compare(eventAtOnePm.event.displayLabel, "Event at 13:00 until 16:30")
+ compare(eventAtOnePm.y, 0)
+
+ //"Event at 15:00 until 17:00"
+ var eventAtThreePm = eventMap[3]
+ compare(eventAtThreePm.event.displayLabel, "Event at 15:00 until 17:00")
+ compare(eventAtThreePm.y, 1)
+ }
+
+ // 10:00 ----
+ // |XX|
+ // 11:00 |XX| ----
+ // |XX| |XX|
+ // 12:00 |XX| |XX|
+ // |XX| |XX|
+ // 13:00 |XX| |XX|
+ // |XX| |XX|
+ // 14:00 |XX| |XX|
+ // ---- |XX|
+ // 15:00 |XX| |XX|
+ // |XX| |XX|
+ // 16:00 |XX| |XX| ----
+ // ---- |XX| |XX|
+ // 17:00 ---- |XX|
+ // ----
+ // 18:00
+ function test_intersec_between_10_18()
+ {
+ var data = [{startDate: new Date(2016, 7, 2, 10, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 14, 30, 0, 0),
+ label: "Event at 10:00 until 14:30"},
+ {startDate: new Date(2016, 7, 2, 11, 0, 0, 0),
+ endDate: new Date(2016, 7, 2, 17, 00, 0, 0),
+ label: "Event at 11:00 until 17:00"},
+ {startDate: new Date(2016, 7, 2, 14, 30, 0, 0),
+ endDate: new Date(2016, 7, 2, 16, 30, 0, 0),
+ label: "Event at 14:30 until 16:30"},
+ {startDate: new Date(2016, 7, 2, 16, 00, 0, 0),
+ endDate: new Date(2016, 7, 2, 17, 30, 0, 0),
+ label: "Event at 16:00 until 17:30"}
+
+ ]
+ var model = create_events(data)
+
+ var startDate = new Date(2016, 7, 2, 11, 11, 11, 11)
+ worker.start(model, startDate)
+ tryCompare(worker, 'done', true)
+ var eventMap = worker.reply
+
+ // "Event at 10:00 until 14:30"
+ var eventsA = eventMap[0]
+ compare(eventsA.event.displayLabel, "Event at 10:00 until 14:30")
+ compare(eventsA.y, 0)
+
+ //"Event at 11:00 until 17:00"
+ var eventsB = eventMap[1]
+ compare(eventsB.event.displayLabel, "Event at 11:00 until 17:00")
+ compare(eventsB.y, 1)
+
+ //"Event at 14:30 until 16:30"
+ var eventsC = eventMap[2]
+ compare(eventsC.event.displayLabel, "Event at 14:30 until 16:30")
+ compare(eventsC.y, 0)
+
+ //"Event at 16:00 until 17:30"
+ var eventsD = eventMap[3]
+ compare(eventsD.event.displayLabel, "Event at 16:00 until 17:30")
+ compare(eventsD.y, 2)
+ }
+
+ // 14:00 ----
+ // |XX| ---- ----
+ // 15:00 ---- ---- |XX|
+ // ---- ---- ----
+ // 16:00 ---- ----
+ //
+ function test_intersec_between_14_15_until_16_30()
+ {
+ var data = [{startDate: new Date(2016, 7, 2, 14, 15, 0, 0),
+ endDate: new Date(2016, 7, 2, 15, 15, 0, 0),
+ label: "Event at 14:15 until 15:15"},
+ {startDate: new Date(2016, 7, 2, 14, 30, 0, 0),
+ endDate: new Date(2016, 7, 2, 15, 00, 0, 0),
+ label: "Event at 14:30 until 15:00"},
+ {startDate: new Date(2016, 7, 2, 14, 45, 0, 0),
+ endDate: new Date(2016, 7, 2, 15, 45, 0, 0),
+ label: "Event at 14:45 until 15:45"},
+ {startDate: new Date(2016, 7, 2, 15, 30, 0, 0),
+ endDate: new Date(2016, 7, 2, 16, 30, 0, 1),
+ label: "Event at 15:30 until 16:30"},
+ {startDate: new Date(2016, 7, 2, 15, 30, 0, 0),
+ endDate: new Date(2016, 7, 2, 16, 30, 0, 0),
+ label: "Event at 15:30 until 16:30 (1)"}
+ ]
+ var model = create_events(data)
+
+ var startDate = new Date(2016, 7, 2, 11, 11, 11, 11)
+ worker.start(model, startDate)
+ tryCompare(worker, 'done', true)
+ var eventMap = worker.reply
+
+ // "Event at 14:15 until 15:15"
+ var eventsA = eventMap[0]
+ compare(eventsA.event.displayLabel, "Event at 14:15 until 15:15")
+ compare(eventsA.y, 0)
+ compare(eventsA.intersectionCount, 5)
+ fuzzyCompare(eventsA.width, 0.2, 0.1)
+
+ //"Event at 14:30 until 15:00"
+ var eventsB = eventMap[1]
+ compare(eventsB.event.displayLabel, "Event at 14:30 until 15:00")
+ compare(eventsB.y, 1)
+ compare(eventsB.intersectionCount, 5)
+ fuzzyCompare(eventsB.width, 0.2, 0.1)
+
+ //"Event at 14:45 until 15:45"
+ var eventsC = eventMap[2]
+ compare(eventsC.event.displayLabel, "Event at 14:45 until 15:45")
+ compare(eventsC.y, 2)
+ compare(eventsC.intersectionCount, 5)
+ fuzzyCompare(eventsC.width, 0.2, 0.1)
+
+ //"Event at 15:30 until 16:30"
+ var eventsD = eventMap[3]
+ compare(eventsD.event.displayLabel, "Event at 15:30 until 16:30")
+ compare(eventsD.y, 0)
+ compare(eventsD.intersectionCount, 5)
+ fuzzyCompare(eventsD.width, 0.2, 0.1)
+
+ //"Event at 15:30 until 16:30 (1)"
+ var eventsE = eventMap[4]
+ compare(eventsE.event.displayLabel, "Event at 15:30 until 16:30 (1)")
+ compare(eventsE.y, 1)
+ compare(eventsE.intersectionCount, 5)
+ fuzzyCompare(eventsE.width, 0.2, 0.1)
+ }
+}
References