Merge lp:~renatofilho/ubuntu-calendar-app/optimize into lp:ubuntu-calendar-app

Proposed by Renato Araujo Oliveira Filho
Status: Merged
Approved by: Bill Filler
Approved revision: 735
Merged at revision: 768
Proposed branch: lp:~renatofilho/ubuntu-calendar-app/optimize
Merge into: lp:ubuntu-calendar-app
Prerequisite: lp:~renatofilho/ubuntu-calendar-app/optimize-page-load
Diff against target: 6303 lines (+3052/-1463)
33 files modified
AgendaView.qml (+14/-13)
AllDayEventComponent.qml (+71/-36)
ContactChoicePopup.qml (+57/-15)
DayView.qml (+127/-87)
EventActions.qml (+2/-13)
EventBubble.qml (+61/-71)
EventLayoutHelper.js (+0/-132)
EventListModel.qml (+17/-5)
KeyboardRectangle.qml (+1/-7)
MonthComponent.qml (+178/-125)
MonthComponentDateDelegate.qml (+1/-140)
MonthComponentWithEventsDateDelegate.qml (+80/-0)
MonthView.qml (+46/-82)
MonthWithEventsComponent.qml (+80/-0)
NewEvent.qml (+228/-131)
NewEventBottomEdge.qml (+115/-0)
PageWithBottomEdge.qml (+44/-0)
PathViewBase.qml (+12/-4)
TimeLineBase.qml (+276/-190)
TimeLineBaseComponent.qml (+159/-75)
TimeLineHeader.qml (+6/-0)
WeekView.qml (+130/-60)
YearView.qml (+64/-48)
YearViewDelegate.qml (+39/-74)
calendar.qml (+150/-27)
calendar_canvas.js (+52/-0)
calendar_canvas_worker.js (+111/-0)
click/calendar-helper-apparmor.json (+1/-1)
click/calendar.apparmor (+1/-1)
click/manifest.json.in (+1/-1)
dateExt.js (+15/-2)
po/com.ubuntu.calendar.pot (+241/-123)
tests/unittests/tst_calendar_canvas.qml (+672/-0)
To merge this branch: bzr merge lp:~renatofilho/ubuntu-calendar-app/optimize
Reviewer Review Type Date Requested Status
Jenkins Bot continuous-integration Approve
Ubuntu Phone Apps Jenkins Bot continuous-integration Pending
Kunal Parmar Pending
Review via email: mp+283858@code.launchpad.net

This proposal supersedes a proposal from 2016-01-25.

Commit message

Optimize Application;
Rewrite event layout functions;
Fixed several bugs;

To post a comment you must log in.
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Kunal Parmar (pkunal-parmar) wrote : Posted in a previous version of this proposal

added inline comment

review: Needs Information
Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
729. By Renato Araujo Oliveira Filho

Use events word instead of 'ev.' for all day events.

730. By Renato Araujo Oliveira Filho

Update events filter if the collections changed.

Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
731. By Renato Araujo Oliveira Filho

Update apparmor policy version.

732. By Renato Araujo Oliveira Filho

Disable model update while sync is in progress to avoid several updates.

Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
733. By Renato Araujo Oliveira Filho

Keep event bubble color in sync with calendar event color.

734. By Renato Araujo Oliveira Filho

Fixed day view title.

Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
735. By Renato Araujo Oliveira Filho

Fixed uri handler for eventId.

Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'AgendaView.qml'
2--- AgendaView.qml 2016-02-03 08:06:25 +0000
3+++ AgendaView.qml 2016-03-07 15:59:03 +0000
4@@ -23,16 +23,14 @@
5 import "dateExt.js" as DateExt
6 import "./3rd-party/lunar.js" as Lunar
7
8-Page{
9+PageWithBottomEdge {
10 id: root
11 objectName: "AgendaView"
12
13- property var currentDay: new Date()
14+ property var anchorDate: new Date()
15
16 signal dateSelected(var date);
17
18- Keys.forwardTo: [eventList]
19-
20 function goToBeginning() {
21 eventList.positionViewAtBeginning();
22 }
23@@ -45,13 +43,17 @@
24 return !!enabled_calendars.length;
25 }
26
27+
28+ Keys.forwardTo: [eventList]
29+ createEventAt: anchorDate
30+
31 Action {
32 id: calendarTodayAction
33 objectName:"todaybutton"
34 iconName: "calendar-today"
35 text: i18n.tr("Today")
36 onTriggered: {
37- currentDay = new Date()
38+ anchorDate = new Date()
39 goToBeginning()
40 }
41 }
42@@ -63,7 +65,6 @@
43 leadingActionBar.actions: tabs.tabsAction
44 trailingActionBar.actions: [
45 calendarTodayAction,
46- commonHeaderActions.newEventAction,
47 commonHeaderActions.showCalendarAction,
48 commonHeaderActions.reloadAction,
49 commonHeaderActions.syncCalendarAction,
50@@ -74,9 +75,9 @@
51
52 EventListModel {
53 id: eventListModel
54- startPeriod: currentDay.midnight();
55- endPeriod: currentDay.addDays(7).endOfDay()
56- filter: eventModel.filter
57+ startPeriod: anchorDate.midnight();
58+ endPeriod: anchorDate.addDays(7).endOfDay()
59+ filter: model.filter
60
61 sortOrders: [
62 SortOrder{
63@@ -106,7 +107,7 @@
64
65 return default_title;
66 }
67- visible: (eventListModel.count === 0) && !eventListModel.isLoading
68+ visible: (eventList.count === 0) && !eventListModel.isLoading
69 anchors.centerIn: parent
70 }
71
72@@ -119,12 +120,12 @@
73 color: UbuntuColors.orange
74
75 onClicked: {
76- pageStack.push(Qt.resolvedUrl("CalendarChoicePopup.qml"),{"model":eventModel});
77- pageStack.currentPage.collectionUpdated.connect(eventModel.delayedApplyFilter);
78+ pageStack.push(Qt.resolvedUrl("CalendarChoicePopup.qml"),{"model": model});
79+ pageStack.currentPage.collectionUpdated.connect(model.delayedApplyFilter);
80 }
81 }
82
83- ListView{
84+ ListView {
85 id: eventList
86 objectName: "eventList"
87 model: eventListModel
88
89=== modified file 'AllDayEventComponent.qml'
90--- AllDayEventComponent.qml 2016-02-03 08:06:25 +0000
91+++ AllDayEventComponent.qml 2016-03-07 15:59:03 +0000
92@@ -31,12 +31,14 @@
93 property var allDayEvents;
94 property var model;
95
96+ signal pressAndHold(var date)
97+
98 width: parent.width
99 height: units.gu(5)
100
101 function getAllDayEvents(startDate, endDate) {
102 var map = {};
103- var items = model.getItems(startDate,endDate);
104+ var items = model.itemsByTimePeriod(startDate,endDate);
105 for(var i = 0 ; i < items.length ; ++i) {
106 var event = items[(i)];
107 if( event && event.allDay ) {
108@@ -62,6 +64,10 @@
109 }
110
111 Repeater{
112+ id: repeater
113+
114+ readonly property bool compactView: (root.width / repeater.count) < units.gu(15)
115+
116 model: type == ViewType.ViewTypeWeek ? 7 : 1
117 delegate: Item {
118 id: allDayButton
119@@ -71,29 +77,86 @@
120 height: units.gu(5)
121 width: parent.width / (type == ViewType.ViewTypeWeek ? 7 : 1)
122
123+ Rectangle {
124+ id: temporaryEvent
125+
126+ anchors.fill: parent
127+ visible: mouseArea.mouseHold
128+ Label {
129+ anchors.fill: parent
130+ verticalAlignment: Text.AlignVCenter
131+ horizontalAlignment: Text.AlignHCenter
132+ text: i18n.tr("New event")
133+ }
134+ z: 100
135+ }
136+
137 MouseArea {
138+ id: mouseArea
139+
140+ property bool mouseHold: false
141+
142+ preventStealing: mouseHold
143 anchors.fill: parent
144 onClicked: {
145 if(!allDayButton.events || allDayButton.events.length === 0) {
146 return;
147 }
148
149- if(type == ViewType.ViewTypeWeek) {
150- PopupUtils.open(popoverComponent, root,{"events": allDayButton.events})
151+ if(repeater.compactView) {
152+ PopupUtils.open(popoverComponent, allDayButton,{"events": allDayButton.events})
153 } else {
154 if( allDayButton.events.length > 1 ) {
155- PopupUtils.open(popoverComponent, root,{"events": allDayButton.events})
156+ PopupUtils.open(popoverComponent, allDayButton,{"events": allDayButton.events})
157 } else {
158 pageStack.push(Qt.resolvedUrl("EventDetails.qml"),{"event":allDayButton.events[0],"model": root.model});
159 }
160 }
161 }
162+
163+ onReleased: {
164+ if (mouseHold && containsMouse) {
165+ root.pressAndHold(startDay.midnight().addDays(index))
166+ }
167+ mouseHold = false
168+ }
169+
170+
171+ onPressAndHold: {
172+ mouseHold = true
173+ Haptics.play()
174+ }
175 }
176
177- Loader {
178- id: eventLabelLoader
179- anchors.fill: parent
180- sourceComponent : !allDayButton.events || allDayButton.events.length === 0 ? undefined : eventComponent
181+ Label {
182+ id: eventLabel
183+ anchors {
184+ fill: parent
185+ margins: units.gu(0.5)
186+ }
187+ verticalAlignment: Text.AlignVCenter
188+ horizontalAlignment: Text.AlignHCenter
189+ elide: Text.ElideRight
190+ text: {
191+ if(!events || events.length === 0) {
192+ return "";
193+ }
194+
195+ if(repeater.compactView) {
196+ // TRANSLATORS: the first parameter refers to the number of all-day events
197+ // on a given day. "Ev." is short form for "Events".
198+ // Please keep the translation of "Ev." to 3 characters only, as the week view
199+ // where it's shown has limited space
200+ return i18n.tr("%1 event", "%1 events", events.length).arg(events.length)
201+ } else {
202+ if( events.length > 1) {
203+ // TRANSLATORS: the argument refers to the number of all day events
204+ return i18n.tr("%1 all day event", "%1 all day events", events.length).arg(events.length)
205+ } else {
206+ return events[0].displayLabel;
207+ }
208+ }
209+ }
210 }
211
212 Loader{
213@@ -110,39 +173,11 @@
214 sd = sd.addDays(index);
215 var key = Qt.formatDateTime(sd, "dd-MMM-yyyy");
216 events = allDayEvents[key];
217-
218- if(!events || events.length === 0) {
219- return;
220- }
221-
222- if(type == ViewType.ViewTypeWeek) {
223- // TRANSLATORS: the first parameter refers to the number of all-day events
224- // on a given day. "Ev." is short form for "Events".
225- // Please keep the translation of "Ev." to 3 characters only, as the week view
226- // where it's shown has limited space
227- eventLabelLoader.item.text = i18n.tr("%1 ev.").arg(events.length)
228- } else {
229- if( events.length > 1) {
230- // TRANSLATORS: the argument refers to the number of all day events
231- eventLabelLoader.item.text = i18n.tr("%1 all day event", "%1 all day events", events.length).arg(events.length)
232- } else {
233- eventLabelLoader.item.text = events[0].displayLabel;
234- }
235- }
236 }
237 }
238 }
239 }
240
241-
242- Component{
243- id: eventComponent
244- Label {
245- verticalAlignment: Text.AlignVCenter
246- horizontalAlignment: Text.AlignHCenter
247- }
248- }
249-
250 Component {
251 id: dividerComponent
252 SimpleDivider{
253
254=== modified file 'ContactChoicePopup.qml'
255--- ContactChoicePopup.qml 2016-01-29 14:35:14 +0000
256+++ ContactChoicePopup.qml 2016-03-07 15:59:03 +0000
257@@ -17,7 +17,7 @@
258 */
259 import QtQuick 2.4
260 import Ubuntu.Components 1.3
261-import Ubuntu.Components.Popups 1.0
262+import Ubuntu.Components.Popups 1.3
263 import Ubuntu.Components.ListItems 1.0
264 import Ubuntu.Components.Themes.Ambiance 1.0
265 import QtOrganizer 5.0
266@@ -29,7 +29,7 @@
267 id: root
268 objectName: "contactPopover"
269
270- signal contactSelected(var contact);
271+ signal contactSelected(var contact, string emailAddress);
272
273 Label {
274 id: noContact
275@@ -40,24 +40,27 @@
276
277 UnionFilter {
278 id: filter
279+
280+ property string searchString: ""
281+
282 filters: [
283 DetailFilter{
284 detail: ContactDetail.Name
285 field: Name.FirstName
286 matchFlags: Filter.MatchContains
287- value: searchBox.text
288+ value: filter.searchString
289 },
290 DetailFilter{
291 detail: ContactDetail.Name
292 field: Name.LastName
293 matchFlags: Filter.MatchContains
294- value: searchBox.text
295+ value: filter.searchString
296 },
297 DetailFilter{
298 detail: ContactDetail.DisplayLabel
299 field: DisplayLabel.Label
300 matchFlags: Filter.MatchContains
301- value: searchBox.text
302+ value: filter.searchString
303 }
304 ]
305 }
306@@ -69,6 +72,16 @@
307 autoUpdate: true
308 }
309
310+ Timer {
311+ id: idleSearch
312+
313+ interval: 500
314+ repeat: false
315+ onTriggered: {
316+ filter.searchString = searchBox.text
317+ }
318+ }
319+
320 Column {
321 anchors.top: parent.top
322 anchors.left: parent.left
323@@ -81,12 +94,16 @@
324 focus: true
325 width: parent.width
326 placeholderText: i18n.tr("Search contact")
327+ inputMethodHints: Qt.ImhNoPredictiveText
328 primaryItem: Icon {
329 height: parent.height*0.5
330 width: parent.height*0.5
331 anchors.verticalCenter: parent.verticalCenter
332 name:"find"
333- }
334+ }
335+ onTextChanged: {
336+ idleSearch.restart()
337+ }
338 }
339
340 ListView {
341@@ -96,17 +113,42 @@
342 model: contactModel
343 height: units.gu(15)
344 clip: true
345- delegate: Standard{
346- objectName: "contactPopoverList%1".arg(index)
347- property var item: contactModel.contacts[index]
348- height: units.gu(4)
349- text: item ? item.displayLabel.label : ""
350-
351- onClicked: {
352- root.contactSelected(item);
353- onClicked: PopupUtils.close(root)
354+ focus: false
355+ delegate: Column {
356+ width: contactList.width
357+ Repeater {
358+ anchors {
359+ left: parent.left
360+ right: parent.right
361+ }
362+ height: childrenRect.height
363+
364+ model: Math.max(1, contact.emails.length)
365+ delegate: ListItem {
366+ property string emailAddress: contact.emails.length > index ? contact.emails[index].emailAddress : ""
367+
368+ activeFocusOnPress: false
369+ opacity: emailAddress.length > 0 ? 1.0 : 0.3
370+ width: contactList.width
371+ objectName: "contactPopoverList%1".arg(index)
372+ ListItemLayout {
373+ title.text: contact.displayLabel.label
374+ subtitle.text: emailAddress
375+ }
376+ MouseArea {
377+ anchors.fill: parent
378+ onClicked: {
379+ if (emailAddress.length > 0) {
380+ root.contactSelected(contact, emailAddress);
381+ PopupUtils.close(root)
382+ }
383+ }
384+ }
385+ }
386 }
387 }
388 }
389 }
390+
391+ Component.onCompleted: searchBox.forceActiveFocus()
392 }
393
394=== modified file 'DayView.qml'
395--- DayView.qml 2016-02-03 08:06:25 +0000
396+++ DayView.qml 2016-03-07 15:59:03 +0000
397@@ -18,21 +18,52 @@
398
399 import QtQuick 2.4
400 import Ubuntu.Components 1.3
401+
402 import "dateExt.js" as DateExt
403 import "ViewType.js" as ViewType
404 import "./3rd-party/lunar.js" as Lunar
405
406-Page{
407+PageWithBottomEdge {
408 id: dayViewPage
409 objectName: "dayViewPage"
410
411- property var currentDay: new Date()
412- property bool isCurrentPage: false
413+
414+ property bool displayLunarCalendar: false
415+ property var anchorDate: new Date()
416+ readonly property var currentDate: dayViewPath.currentItem.startDay
417
418 signal dateSelected(var date);
419+ signal pressAndHoldAt(var date, bool allDay)
420+
421+ function delayScrollToDate(date, scrollTime) {
422+ idleScroll.scrollToTime = scrollTime != undefined ? scrollTime : true
423+ idleScroll.scrollToDate = new Date(date)
424+ idleScroll.restart()
425+ }
426
427 Keys.forwardTo: [dayViewPath]
428- flickable: null
429+
430+ createEventAt: currentDate
431+ onAnchorDateChanged: {
432+ dayViewPath.scrollToBegginer()
433+ }
434+
435+ onEventCreated: {
436+ var scrollDate = new Date(event.startDateTime)
437+ var needScroll = false
438+ if ((currentDate.getFullYear() !== scrollDate.getFullYear()) ||
439+ (currentDate.getMonth() !== scrollDate.getMonth()) ||
440+ (currentDate.getDate() !== scrollDate.getDate())) {
441+ anchorDate = new Date(scrollDate)
442+ needScroll = true
443+ } else if (!dayViewPath.currentItem.timeIsVisible(scrollDate)) {
444+ needScroll = true
445+ }
446+
447+ if (needScroll) {
448+ delayScrollToDate(scrollDate, !event.allDay)
449+ }
450+ }
451
452 Action {
453 id: calendarTodayAction
454@@ -40,17 +71,38 @@
455 iconName: "calendar-today"
456 text: i18n.tr("Today")
457 onTriggered: {
458- currentDay = new Date()
459+ anchorDate = new Date()
460+ delayScrollToDate(anchorDate)
461+ }
462+ }
463+
464+
465+ Timer {
466+ id: idleScroll
467+
468+ property var scrollToDate: null
469+ property bool scrollToTime: true
470+
471+ interval: 200
472+ repeat:false
473+ onTriggered: {
474+ if (scrollToDate && scrollToTime) {
475+ dayViewPath.currentItem.scrollToTime(scrollToDate)
476+ } else {
477+ dayViewPath.currentItem.scrollToBegin()
478+ }
479+ scrollToDate = null
480+ scrollToTime = true
481 }
482 }
483
484 header: PageHeader {
485 id: pageHeader
486
487+ flickable: null
488 leadingActionBar.actions: tabs.tabsAction
489 trailingActionBar.actions: [
490 calendarTodayAction,
491- commonHeaderActions.newEventAction,
492 commonHeaderActions.showCalendarAction,
493 commonHeaderActions.reloadAction,
494 commonHeaderActions.syncCalendarAction,
495@@ -58,105 +110,93 @@
496 ]
497
498 title: {
499- // TRANSLATORS: this is a time formatting string,
500- // see http://qt-project.org/doc/qt-5/qml-qtqml-date.html#details for valid expressions.
501- // It's used in the header of the month and week views
502- var monthName = currentDay.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy"))
503- return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1)
504+ if(dayViewPage.displayLunarCalendar){
505+ var lunarDate = Lunar.calendar.solar2lunar(currentDate.getFullYear(),
506+ currentDate.getMonth() + 1,
507+ currentDate.getDate())
508+ return ("%1 %2").arg(lunarDate .IMonthCn).arg(lunarDate.gzYear)
509+ } else {
510+ // TRANSLATORS: this is a time formatting string,
511+ // see http://qt-project.org/doc/qt-5/qml-qtqml-date.html#details for valid expressions.
512+ // It's used in the header of the month and week views
513+ var monthName = currentDate.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy"))
514+ return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1)
515+ }
516 }
517+ }
518
519+ onBottomEdgeCommitStarted: {
520+ var eventAt = new Date()
521+ if (dayViewPath.currentItem) {
522+ eventAt.setFullYear(currentDate.getFullYear())
523+ eventAt.setMonth(currentDate.getMonth())
524+ eventAt.setDate(currentDate.getDate())
525+ }
526+ createEventAt = eventAt
527 }
528
529 PathViewBase{
530 id: dayViewPath
531 objectName: "dayViewPath"
532
533- property var startDay: currentDay
534+ property var startDay: currentDate
535 //This is used to scroll all view together when currentItem scrolls
536- property var childContentY;
537+ property real childContentY;
538
539 anchors {
540 fill: parent
541 topMargin: header.height
542 }
543
544- onNextItemHighlighted: {
545- //next day
546- currentDay = currentDay.addDays(1);
547- }
548-
549- onPreviousItemHighlighted: {
550- //previous day
551- currentDay = currentDay.addDays(-1);
552- }
553-
554- delegate: Loader {
555+ delegate: TimeLineBaseComponent {
556+ id: timeLineView
557+ objectName: "DayComponent-"+index
558+
559 width: parent.width
560 height: parent.height
561- asynchronous: !dayViewPath.isCurrentItem
562- sourceComponent: delegateComponent
563-
564- Component {
565- id: delegateComponent
566-
567- TimeLineBaseComponent {
568- id: timeLineView
569- objectName: "DayComponent-"+index
570-
571- type: ViewType.ViewTypeDay
572- anchors.fill: parent
573-
574- isActive: parent.PathView.isCurrentItem
575- contentInteractive: parent.PathView.isCurrentItem
576- startDay: dayViewPath.startDay.addDays(dayViewPath.indexType(index))
577- keyboardEventProvider: dayViewPath
578-
579- Component.onCompleted: {
580- if(dayViewPage.isCurrentPage){
581- timeLineView.scrollToCurrentTime();
582- }
583- }
584-
585- Connections{
586- target: dayViewPage
587- onIsCurrentPageChanged:{
588- if(dayViewPage.isCurrentPage){
589- timeLineView.scrollToCurrentTime();
590- }
591- }
592- }
593-
594- //get contentY value from PathView, if its not current Item
595- Binding{
596- target: timeLineView
597- property: "contentY"
598- value: dayViewPath.childContentY;
599- when: !parent.PathView.isCurrentItem
600- }
601-
602- //set PathView's contentY property, if its current item
603- Binding{
604- target: dayViewPath
605- property: "childContentY"
606- value: contentY
607- when: parent.PathView.isCurrentItem
608- }
609- }
610+
611+ type: ViewType.ViewTypeDay
612+ isCurrentItem: PathView.isCurrentItem
613+ isActive: !dayViewPath.moving && !dayViewPath.flicking
614+ contentInteractive: PathView.isCurrentItem
615+ startDay: anchorDate.addDays(dayViewPath.loopCurrentIndex + dayViewPath.indexType(index))
616+ keyboardEventProvider: dayViewPath
617+ modelFilter: dayViewPage.model ? dayViewPage.model.filter : null
618+
619+ onPressAndHoldAt: {
620+ dayViewPage.pressAndHoldAt(date, allDay)
621+ }
622+
623+ Component.onCompleted: {
624+ if(dayViewPage.tabSelected){
625+ idleScroll.restart()
626+ }
627+ }
628+
629+ Connections{
630+ target: dayViewPage
631+ onTabSelectedChanged: {
632+ if(tabSelected){
633+ timeLineView.scrollToTime(new Date());
634+ }
635+ }
636+ }
637+
638+ //get contentY value from PathView, if its not current Item
639+ Binding{
640+ target: timeLineView
641+ property: "contentY"
642+ value: dayViewPath.childContentY;
643+ when: !timeLineView.isCurrentItem
644+ }
645+
646+ //set PathView's contentY property, if its current item
647+ Binding{
648+ target: dayViewPath
649+ property: "childContentY"
650+ value: timeLineView.contentY
651+ when: timeLineView.isCurrentItem
652 }
653 }
654 }
655-
656- Component.onCompleted: {
657- pageHeader.title = Qt.binding(function(){
658- if(mainView.displayLunarCalendar){
659- var lunarDate = Lunar.calendar.solar2lunar(currentDay.getFullYear(),
660- currentDay.getMonth() + 1,
661- currentDay.getDate())
662- return i18n.tr("%1 %2").arg(lunarDate .IMonthCn).arg(lunarDate.gzYear)
663- } else {
664- var monthName = currentDay.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy"))
665- return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1)
666- }
667- })
668- }
669 }
670
671=== modified file 'EventActions.qml'
672--- EventActions.qml 2016-02-01 19:49:33 +0000
673+++ EventActions.qml 2016-03-07 15:59:03 +0000
674@@ -23,10 +23,10 @@
675 Item {
676 id: actionPool
677
678- property alias newEventAction: _newEventAction
679 property alias showCalendarAction: _showCalendarAction
680 property alias syncCalendarAction: _syncCalendarAction
681 property alias settingsAction: _settingsAction
682+ readonly property bool syncInProgress: (syncMonitor.state === "syncing")
683
684 Action {
685 id: _syncCalendarAction
686@@ -36,7 +36,7 @@
687 // Currently ,there is no way we can increase width of action menu currently.
688 text: enabled ? i18n.tr("Sync") : i18n.tr("Syncing")
689 onTriggered: syncMonitor.sync(["calendar"])
690- enabled: (syncMonitor.state !== "syncing")
691+ enabled: !syncInProgress
692 visible: syncMonitor.enabledServices ? syncMonitor.serviceIsEnabled("calendar") : false
693 }
694
695@@ -44,17 +44,6 @@
696 id: syncMonitor
697 }
698
699- Action {
700- id: _newEventAction
701- objectName: "neweventbutton"
702- name: "neweventbutton"
703- iconName: "new-event"
704- text: i18n.tr("New Event")
705- onTriggered: {
706- pageStack.push(Qt.resolvedUrl("NewEvent.qml"),{"date":tabs.currentDay,"model":eventModel});
707- }
708- }
709-
710 Action{
711 id: _showCalendarAction
712 objectName: "calendarsbutton"
713
714=== modified file 'EventBubble.qml'
715--- EventBubble.qml 2016-01-29 14:35:14 +0000
716+++ EventBubble.qml 2016-03-07 15:59:03 +0000
717@@ -20,54 +20,39 @@
718 import Ubuntu.Components 1.3
719 import QtOrganizer 5.0
720
721+import "calendar_canvas.js" as CanlendarCanvas
722+
723 Item{
724 id: infoBubble
725
726+ property var anchorDate;
727 property var event;
728- property var model;
729-
730+ property var model: null
731+ property int depthInRow: 0
732+ property real sizeOfRow:0.0
733+ property real minuteHeight: 1.0
734 property int type: narrowType
735 property int wideType: 1;
736 property int narrowType: 2;
737-
738- property int depthInRow: 0;
739- property int sizeOfRow:0
740-
741 property bool isLiveEditing: false
742-
743 property Flickable flickable;
744+ property bool isEventBubble: true
745
746 readonly property int minimumHeight: type == wideType
747 ? detailsItems.timeLabelHeight + /*top-bottom margin*/ units.gu(2)
748 : units.gu(2)
749
750- z: depthInRow
751+ readonly property real startTimeInMinutes: event ? CanlendarCanvas.minutesSince(infoBubble.anchorDate, event.startDateTime) : 0.0
752+ readonly property real endTimeInMinutes: event ? CanlendarCanvas.minutesSince(infoBubble.anchorDate, event.endDateTime) : 0.0
753+ readonly property real durationInMinutes: endTimeInMinutes - startTimeInMinutes
754
755 signal clicked(var event);
756
757- Rectangle{
758- id: bg
759- anchors.fill: parent
760- border.color: isLiveEditing ? "red" : "white"
761- }
762-
763- function resize() {
764- var offset = parent.width/sizeOfRow;
765- x = (depthInRow) * offset;
766- width = parent.width - x;
767- }
768-
769- Connections{
770- target: parent
771- onWidthChanged:{
772- resize();
773- }
774- }
775-
776- onEventChanged: {
777- resize();
778- assingnBgColor();
779- setDetails();
780+ // keep color up-to-date
781+ Connections {
782+ target: model
783+ ignoreUnknownSignals: true
784+ onCollectionsChanged: assingnBgColor()
785 }
786
787 function assingnBgColor() {
788@@ -102,20 +87,6 @@
789 }
790 }
791
792- function layoutBubbleDetails() {
793- if( !flickable || flickable === undefined ) {
794- return;
795- }
796-
797- if( infoBubble.y < flickable.contentY && infoBubble.height > flickable.height) {
798- var y = (flickable.contentY - infoBubble.y) * 1.2;
799- if( ( y + detailsItems.height + units.gu(2)) > infoBubble.height) {
800- y = infoBubble.height - detailsItems.height - units.gu(2);
801- }
802- detailsItems.y = y;
803- }
804- }
805-
806 function setDetails() {
807 if(event === null || event === undefined) {
808 return;
809@@ -153,8 +124,38 @@
810 timeLabel.horizontalAlignment = Text.AlignHCenter
811 timeLabel.wrapMode = Text.WrapAtWordBoundaryOrAnywhere
812 }
813-
814- layoutBubbleDetails();
815+ }
816+
817+ function resize()
818+ {
819+ width = parent ? parent.width * sizeOfRow : 0
820+ x = depthInRow * width
821+ z = depthInRow
822+ height = Math.max(30, (durationInMinutes * parent.minuteHeight))
823+ }
824+
825+ onEventChanged: {
826+ assingnBgColor();
827+ setDetails();
828+ resize()
829+ }
830+
831+ Connections {
832+ target: parent
833+ onWidthChanged: resize()
834+ }
835+
836+ Binding {
837+ target: infoBubble
838+ property: "y"
839+ value: (startTimeInMinutes * parent.minuteHeight)
840+ when: !infoBubble.isLiveEditing
841+ }
842+
843+ Rectangle{
844+ id: bg
845+ anchors.fill: parent
846+ border.color: isLiveEditing ? "red" : "white"
847 }
848
849 Item {
850@@ -193,25 +194,6 @@
851 wrapMode: Text.WrapAtWordBoundaryOrAnywhere
852 }
853 }
854-
855- onHeightChanged: {
856- layoutBubbleDetails();
857- }
858-
859- Connections {
860- target: infoBubble
861- onFlickableChanged: {
862- if (flickable && infoBubble.height > flickable.height) {
863- flickable.onContentYChanged.connect(layoutBubbleDetails);
864- }
865- }
866-
867- onHeightChanged: {
868- if(flickable && infoBubble.height > flickable.height) {
869- flickable.onContentYChanged.connect(layoutBubbleDetails);
870- }
871- }
872- }
873 }
874
875 Drag.active: dragArea.drag.active
876@@ -219,11 +201,19 @@
877 MouseArea {
878 id: dragArea
879 anchors.fill: parent
880- drag.target: isLiveEditing ? infoBubble : null
881- drag.axis: Drag.YAxis
882- drag.minimumY: flickable ? flickable.y : 0
883- drag.maximumY: flickable ? flickable.contentHeight - infoBubble.height : infoBubble.height
884- onReleased: parent.Drag.drop()
885+ drag {
886+ target: isLiveEditing ? infoBubble : null
887+ axis: Drag.YAxis
888+ minimumY: flickable ? flickable.y : 0
889+ maximumY: flickable ? flickable.contentHeight - infoBubble.height : infoBubble.height
890+ }
891+ onReleased: {
892+ if (isLiveEditing) {
893+ isLiveEditing = false;
894+ infoBubble.z -= 1;
895+ }
896+ parent.Drag.drop()
897+ }
898 onClicked: {
899 if( isLiveEditing ) {
900 isLiveEditing = false;
901
902=== removed file 'EventLayoutHelper.js'
903--- EventLayoutHelper.js 2015-11-27 01:34:46 +0000
904+++ EventLayoutHelper.js 1970-01-01 00:00:00 +0000
905@@ -1,132 +0,0 @@
906-/*
907- * Copyright (C) 2013-2014 Canonical Ltd
908- *
909- * This file is part of Ubuntu Calendar App
910- *
911- * Ubuntu Calendar App is free software: you can redistribute it and/or modify
912- * it under the terms of the GNU General Public License version 3 as
913- * published by the Free Software Foundation.
914- *
915- * Ubuntu Calendar App is distributed in the hope that it will be useful,
916- * but WITHOUT ANY WARRANTY; without even the implied warranty of
917- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
918- * GNU General Public License for more details.
919- *
920- * You should have received a copy of the GNU General Public License
921- * along with this program. If not, see <http://www.gnu.org/licenses/>.
922- */
923-WorkerScript.onMessage = function(events) {
924-
925- //returns sorted array of schedules
926- //schedule is start time and duration
927- var allSchs = processEvents(events);
928-
929- while( allSchs.length > 0) {
930- var sch = allSchs.shift();
931-
932- //finds all schedules overlapping with current schedule and remove from original array
933- var schs = findOverlappingSchedules(sch, allSchs);
934-
935- //insert original schedule first, so array remain sorted
936- schs.unshift(sch);
937-
938- //assign position to schedules with respest to their duration and start time
939- var array = [];
940- var maxDepth = assignDepth(schs, array);
941- WorkerScript.sendMessage({ 'schedules': array,"maxDepth":maxDepth});
942- }
943-}
944-
945-function processEvents(events) {
946- var array = [];
947- for( var i = 0; i < events.length ; ++i) {
948- var event = events[i]
949- var sch = {};
950- sch["start"] = event.startDateTime.getTime()
951- sch["duration"] = event.endDateTime - event.startDateTime
952- sch["id"] = event.id;
953- sch["depth"] = 0;
954- sortedInsert(array,sch);
955- }
956- return array;
957-}
958-
959-//insert in to array using insertion sort
960-function sortedInsert(array, sch) {
961- for(var i = array.length-1; i >=0 ; --i) {
962- var temp = array[i];
963- if( sch.duration < array[i].duration) {
964- array.splice(i+1,0,sch);
965- return;
966- }
967- }
968- array.push(sch);
969-}
970-
971-//find all overlapping schedules respect to provided schedule
972-function findOverlappingSchedules( sch, schs) {
973- var array = [];
974- var i = 0;
975- while( i < schs.length && schs.length > 0) {
976- var otherSch = schs[i];
977- if( doesOverlap(sch, otherSch) ) {
978- schs.splice(i,1);
979- array.push(otherSch);
980- sch = mergeSchedules(sch,otherSch);
981- } else {
982- i++;
983- }
984- }
985- return array;
986-}
987-
988-//merges tow schedules and creates a schedule which is long engouth to contain both the of them
989-function mergeSchedules(sch1,sch2) {
990- var sch = {};
991- sch["start"] = Math.min(sch1.start,sch2.start);
992- sch["duration"] = Math.max(sch1.duration,sch2.duration);
993- sch["depth"] = 1;
994- sch["id"] = "DUMMY";
995- return sch;
996-}
997-
998-
999-//check if two schedules overlap each other
1000-//is start time of one schedule is between
1001-//start and end time of other schedule then it's considered as overlap
1002-function doesOverlap( sch1, sch2) {
1003- if( sch1.start >= sch2.start && sch1.start < sch2.start + sch2.duration ) {
1004- return true;
1005- }
1006-
1007- if( sch2.start >= sch1.start && sch2.start < sch1.start + sch1.duration ) {
1008- return true;
1009- }
1010-
1011- return false;
1012-}
1013-
1014-//assign depth(position) of schedule with respect to other
1015-function assignDepth(schs, array) {
1016- var maxDepth = 0;
1017- while( schs.length > 0 ) {
1018- var sch = schs.shift()
1019- array.push(sch);
1020- var depth = findDepth(sch, schs);
1021- maxDepth = Math.max(maxDepth,depth);
1022- }
1023- return maxDepth;
1024-}
1025-
1026-function findDepth( sch, schs) {
1027- var maxDepth = 0;
1028- for( var i = 0; i < schs.length ; ++i) {
1029- var otherSch = schs[i];
1030- if( doesOverlap(sch, otherSch) ) {
1031- otherSch.depth ++;
1032- maxDepth = Math.max(maxDepth,otherSch.depth);
1033- sch = mergeSchedules(sch,otherSch);
1034- }
1035- }
1036- return maxDepth;
1037-}
1038
1039=== modified file 'EventListModel.qml'
1040--- EventListModel.qml 2016-01-25 13:20:32 +0000
1041+++ EventListModel.qml 2016-03-07 15:59:03 +0000
1042@@ -80,7 +80,6 @@
1043 var collections = eventModel.collections;
1044 for(var i = 0 ; i < collections.length ; ++i) {
1045 var cal = collections[i];
1046- print(cal.name + " ---- " + cal.extendedMetaData("collection-readonly"));
1047 if( cal.extendedMetaData("collection-type") === "Calendar" &&
1048 cal.extendedMetaData("collection-readonly") === false ) {
1049 cals.push(cal);
1050@@ -90,16 +89,21 @@
1051 }
1052
1053 function getDefaultCollection() {
1054+ var defaultCol = defaultCollection();
1055+ if (defaultCol.extendedMetaData("collection-selected") === true) {
1056+ return defaultCol
1057+ }
1058+
1059 var cals = getCollections();
1060- for(var i = 0 ; i < cals.length ; ++i) {
1061+ for(var i = 0 ; i < cals.length ; ++i) {
1062 var cal = cals[i]
1063- var val = cal.extendedMetaData("X-CAL-DEFAULT-CALENDAR")
1064- if( val ) {
1065+ var val = cal.extendedMetaData("collection-selected")
1066+ if (val === true) {
1067 return cal;
1068 }
1069 }
1070
1071- return defaultCollection();
1072+ return cals[0]
1073 }
1074
1075 function setDefaultCollection( collectionId ) {
1076@@ -122,4 +126,12 @@
1077 startLoadingTimer();
1078 }
1079 }
1080+
1081+ // disable update while syncing to avoid tons of unecessary update
1082+ autoUpdate: !mainView.syncInProgress
1083+ onAutoUpdateChanged: {
1084+ if (autoUpdate) {
1085+ eventModel.update()
1086+ }
1087+ }
1088 }
1089
1090=== modified file 'KeyboardRectangle.qml'
1091--- KeyboardRectangle.qml 2016-01-25 13:20:32 +0000
1092+++ KeyboardRectangle.qml 2016-03-07 15:59:03 +0000
1093@@ -20,18 +20,12 @@
1094
1095 Item {
1096 id: keyboardRect
1097+
1098 anchors.left: parent.left
1099 anchors.right: parent.right
1100 anchors.bottom: parent.bottom
1101 height: Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height : 0
1102
1103- Behavior on height {
1104- NumberAnimation {
1105- duration: 300
1106- easing.type: Easing.InOutQuad
1107- }
1108- }
1109-
1110 states: [
1111 State {
1112 name: "hidden"
1113
1114=== modified file 'MonthComponent.qml'
1115--- MonthComponent.qml 2016-02-03 08:06:25 +0000
1116+++ MonthComponent.qml 2016-03-07 15:59:03 +0000
1117@@ -17,78 +17,45 @@
1118 */
1119 import QtQuick 2.4
1120 import Ubuntu.Components 1.3
1121+
1122 import "dateExt.js" as DateExt
1123 import "colorUtils.js" as Color
1124+import "./3rd-party/lunar.js" as Lunar
1125
1126 Item{
1127 id: root
1128 objectName: "MonthComponent"
1129
1130 property bool isCurrentItem;
1131-
1132- property bool showEvents: false
1133-
1134- property var currentMonth;
1135+ property int currentYear;
1136+ property int currentMonth;
1137+
1138 property var isYearView;
1139- property var selectedDay;
1140- property bool displayWeekNumber:false;
1141- property bool displayLunarCalendar: false;
1142-
1143- property string subLabelFontSize: "small"
1144+ property var highlightedDate;
1145+ property bool displayWeekNumber:false
1146+ property bool displayLunarCalendar: false
1147+
1148+ property alias dayLabelDelegate : dayLabelRepeater.delegate
1149+ property alias dateLabelDelegate : dateLabelRepeater.delegate
1150+ readonly property alias monthStartDate: intern.monthStart
1151+
1152 property string dayLabelFontSize: "medium"
1153 property string dateLabelFontSize: "large"
1154 property string monthLabelFontSize: "large"
1155 property string yearLabelFontSize: "large"
1156
1157- property alias dayLabelDelegate : dayLabelRepeater.delegate
1158- property alias dateLabelDelegate : dateLabelRepeater.delegate
1159-
1160 signal monthSelected(var date);
1161 signal dateSelected(var date);
1162 signal dateHighlighted(var date);
1163
1164- //creatng timer only if we need to show events in month
1165- Loader {
1166- id: timerLoader
1167- sourceComponent: showEvents ? timerComp : undefined
1168- }
1169-
1170- // Timer to delay creation of Model, There seems some problem fetching events if we create Model immediatly
1171- Component {
1172- id: timerComp
1173- Timer{
1174- interval: 200; running: true; repeat: false
1175- onTriggered: {
1176- modelLoader.sourceComponent = modelComponent
1177- }
1178- }
1179- }
1180-
1181- Loader{
1182- id: modelLoader
1183- }
1184-
1185- Component{
1186- id: modelComponent
1187- EventListModel {
1188- id: mainModel
1189- startPeriod: intern.monthStart.midnight();
1190- endPeriod: intern.monthStart.addDays((/*monthGrid.rows * cols */ 42 )-1).endOfDay()
1191- filter: eventModel.filter
1192- onModelChanged: {
1193- intern.eventStatus = Qt.binding(function() { return mainModel.containsItems(startPeriod, endPeriod, 86400/*24*60*60*/)});
1194- }
1195- }
1196+ function updateEvents(events) {
1197+ intern.eventStatus = events
1198 }
1199
1200 QtObject{
1201 id: intern
1202
1203- property var eventStatus;
1204-
1205- property int curMonthDate: currentMonth.getDate()
1206- property int curMonth: currentMonth.getMonth()
1207- property int curMonthYear: currentMonth.getFullYear()
1208+ property var eventStatus: new Array(42)
1209
1210 property var today: DateExt.today()
1211 property int todayDate: today.getDate()
1212@@ -96,36 +63,42 @@
1213 property int todayYear: today.getFullYear()
1214
1215 //date from month will start, this date might be from previous month
1216- property var monthStart: currentMonth.weekStart( Qt.locale().firstDayOfWeek )
1217+ property var currentDate: new Date(root.currentYear, root.currentMonth, 1, 0, 0, 0, 0)
1218+ property var monthStart: currentDate.weekStart( Qt.locale().firstDayOfWeek )
1219 property int monthStartDate: monthStart.getDate()
1220 property int monthStartMonth: monthStart.getMonth()
1221 property int monthStartYear: monthStart.getFullYear()
1222-
1223- property int daysInStartMonth: Date.daysInMonth(monthStartYear, monthStartMonth)
1224- property int daysInCurMonth: Date.daysInMonth(curMonthYear,curMonth)
1225+ readonly property int daysInStartMonth: Date.daysInMonth(monthStartYear, monthStartMonth)
1226
1227 //check if current month is start month
1228- property bool isCurMonthStartMonth: curMonthDate === monthStartDate
1229- && curMonth === monthStartMonth
1230- && curMonthYear === monthStartYear
1231+ property bool isCurMonthStartMonth: root.currentMonth === monthStartMonth &&
1232+ root.currentYear === monthStartYear
1233
1234 //check current month is same as today's month
1235- property bool isCurMonthTodayMonth: todayYear === curMonthYear && todayMonth == curMonth
1236+ property bool isCurMonthTodayMonth: todayYear === root.currentYear &&
1237+ todayMonth == root.currentMonth
1238 //offset from current month's first date to start date of current month
1239 property int offset: isCurMonthStartMonth ? -1 : (daysInStartMonth - monthStartDate)
1240
1241 property int dateFontSize: FontUtils.sizeToPixels(root.dateLabelFontSize)
1242 property int dayFontSize: FontUtils.sizeToPixels(root.dayLabelFontSize)
1243
1244- property int selectedIndex: -1
1245+ property int highlightedIndex: root.isCurrentItem &&
1246+ root.highlightedDate ?
1247+ intern.indexByDate(root.highlightedDate) : -1
1248+ property int todayIndex: root.isCurrentItem &&
1249+ isCurMonthTodayMonth ?
1250+ intern.indexByDate(intern.today) : -1
1251
1252- function findSelectedDayIndex(){
1253- if(!selectedDay) {
1254+ function indexByDate(date){
1255+ if (!date) {
1256 return -1;
1257 }
1258
1259- if( todayMonth === selectedDay.getMonth() && selectedDay.getFullYear() === todayYear){
1260- return selectedDay.getDate() +
1261+ if ((root.currentMonth === date.getMonth()) &&
1262+ (root.currentYear === date.getFullYear())) {
1263+
1264+ return date.getDate() +
1265 (Date.daysInMonth(monthStartYear, monthStartMonth) - monthStartDate);
1266 } else {
1267 return -1;
1268@@ -133,14 +106,40 @@
1269 }
1270 }
1271
1272- onSelectedDayChanged: {
1273- if( isCurrentItem ) {
1274- intern.selectedIndex = intern.findSelectedDayIndex();
1275+ UbuntuShape{
1276+ id: todayShape
1277+
1278+ visible: (monthGrid.todayItem != null)
1279+ color: (monthGrid.highlightedItem === monthGrid.todayItem) ? UbuntuColors.darkGrey : UbuntuColors.orange
1280+ width: parent ? Math.min(parent.height, parent.width) / 1.3 : 0
1281+ height: width
1282+ parent: monthGrid.todayItem
1283+ anchors.centerIn: parent
1284+ z: -1
1285+ Rectangle {
1286+ anchors.fill: parent
1287+ anchors.margins: units.gu(0.5)
1288+ color: UbuntuColors.orange
1289+ radius: 5
1290 }
1291 }
1292
1293- onCurrentMonthChanged: {
1294- intern.selectedIndex = -1;
1295+ UbuntuShape{
1296+ id: highlightedShape
1297+
1298+ visible: monthGrid.highlightedItem && (monthGrid.highlightedItem != monthGrid.todayItem)
1299+ color: UbuntuColors.darkGrey
1300+ width: parent ? Math.min(parent.height, parent.width) / 1.3 : 0
1301+ height: width
1302+ parent: monthGrid.highlightedItem
1303+ anchors.centerIn: parent
1304+ z: -1
1305+ Rectangle {
1306+ anchors.fill: parent
1307+ anchors.margins: units.gu(0.5)
1308+ color: UbuntuColors.lightGrey
1309+ radius: 5
1310+ }
1311 }
1312
1313 Column{
1314@@ -166,9 +165,9 @@
1315 ViewHeader{
1316 id: monthHeader
1317 anchors.fill: parent
1318- month: intern.curMonth
1319- year: intern.curMonthYear
1320- daysInMonth: intern.daysInCurMonth
1321+ month: root.currentMonth
1322+ year: root.currentYear
1323+ daysInMonth: intern.daysInStartMonth
1324
1325 monthLabelFontSize: root.monthLabelFontSize
1326 yearLabelFontSize: root.yearLabelFontSize
1327@@ -186,9 +185,11 @@
1328
1329 property int dayWidth: width / 7;
1330
1331- width: parent.width
1332- anchors.horizontalCenter: parent.horizontalCenter
1333- anchors.verticalCenter: parent.verticalCenter
1334+ anchors{
1335+ left: parent.left
1336+ right: parent.right
1337+ verticalCenter: parent.verticalCenter
1338+ }
1339
1340 Repeater{
1341 id: dayLabelRepeater
1342@@ -198,23 +199,75 @@
1343 }
1344 }
1345
1346- Grid{
1347+ Grid {
1348 id: monthGrid
1349 objectName: "monthGrid"
1350
1351- width: parent.width
1352- height: parent.height - monthGrid.y
1353-
1354 property int dayWidth: width / 7 /*cols*/;
1355 property int dayHeight: height / 6/*rows*/;
1356+ readonly property var todayItem: (intern.todayIndex != -1) &&
1357+ (monthGrid.children.length > intern.todayIndex) ?
1358+ dateLabelRepeater.itemAt(intern.todayIndex) : null
1359+ readonly property var highlightedItem: (intern.highlightedIndex != -1) &&
1360+ (monthGrid.children.length > intern.highlightedIndex) ?
1361+ dateLabelRepeater.itemAt(intern.highlightedIndex) : null
1362
1363- rows: 6
1364+ anchors {
1365+ left: parent.left
1366+ right: parent.right
1367+ }
1368+ height: parent.height - monthGrid.y
1369 columns: 7
1370
1371 Repeater{
1372 id: dateLabelRepeater
1373- model: 42 //monthGrid.rows * monthGrid.columns
1374- delegate: defaultDateLabelComponent
1375+ model: 42
1376+ delegate: isYearView ? monthWithoutEventsDelegate : monthWithEventsDelegate
1377+ }
1378+ }
1379+ }
1380+
1381+ MouseArea {
1382+ id: mouseArea
1383+
1384+ function getItemAt(x, y)
1385+ {
1386+ var clickPosition = mouseArea.mapToItem(monthGrid, x, y)
1387+ return monthGrid.childAt(clickPosition.x, clickPosition.y)
1388+ }
1389+
1390+ anchors {
1391+ fill: column
1392+ topMargin: monthGrid.y
1393+ }
1394+
1395+ onPressAndHold: {
1396+ var dayItem = getItemAt(mouse.x, mouse.y)
1397+
1398+ if( dayItem.isSelected ) {
1399+ var selectedDate = new Date();
1400+ selectedDate.setFullYear(intern.monthStartYear)
1401+ selectedDate.setMonth(intern.monthStartMonth + 1)
1402+ selectedDate.setDate(dayItem.date)
1403+ selectedDate.setMinutes(60, 0, 0)
1404+ pageStack.push(Qt.resolvedUrl("NewEvent.qml"), {"date":selectedDate, "model":eventModel});
1405+ }
1406+ }
1407+ onClicked: {
1408+ var dayItem = getItemAt(mouse.x, mouse.y)
1409+ var selectedDate = new Date(intern.monthStartYear,
1410+ intern.monthStartMonth + 1,
1411+ dayItem.date, 0, 0, 0, 0)
1412+ if (root.isYearView) {
1413+ //If yearView is clicked then open selected MonthView
1414+ root.monthSelected(selectedDate);
1415+ } else {
1416+ if (dayItem.isSelected) {
1417+ //If monthView is clicked then open selected DayView
1418+ root.dateSelected(selectedDate);
1419+ } else {
1420+ root.dateHighlighted(selectedDate)
1421+ }
1422 }
1423 }
1424 }
1425@@ -301,53 +354,9 @@
1426 }
1427
1428 Component{
1429- id: defaultDateLabelComponent
1430- MonthComponentDateDelegate{
1431- date: {
1432- //try to find date from index and month's first week's first date
1433- var temp = intern.daysInStartMonth - intern.offset + index
1434- //date exceeds days in startMonth,
1435- //this means previous month is over and we are now in current month
1436- //to get actual date we need to remove number of days in startMonth
1437- if( temp > intern.daysInStartMonth ) {
1438- temp = temp - intern.daysInStartMonth
1439- //date exceeds days in current month
1440- // this means date is from next month
1441- //to get actual date we need to remove number of days in current month
1442- if( temp > intern.daysInCurMonth ) {
1443- temp = temp - intern.daysInCurMonth
1444- }
1445- }
1446- return temp;
1447- }
1448-
1449- isCurrentMonth: {
1450- //remove offset from index
1451- //if index falls in 1 to no of days in current month
1452- //then date is inside current month
1453- var temp = index - intern.offset
1454- return (temp >= 1 && temp <= intern.daysInCurMonth)
1455- }
1456-
1457- isToday: intern.todayDate == date && intern.isCurMonthTodayMonth
1458-
1459- isSelected: showEvents && intern.selectedIndex == index
1460-
1461- width: parent.dayWidth
1462- height: parent.dayHeight
1463- fontSize: intern.dateFontSize
1464- showLunarCalendar: displayLunarCalendar
1465- showEvent: showEvents
1466- && intern.eventStatus !== undefined
1467- && intern.eventStatus[index] !== undefined
1468- && intern.eventStatus[index]
1469- }
1470- }
1471-
1472- Component{
1473 id: dafaultDayLabelComponent
1474
1475- Label{
1476+ Text {
1477 id: weekDay
1478 objectName: "weekDay" + index
1479 width: parent.dayWidth
1480@@ -359,4 +368,48 @@
1481 color: "black"
1482 }
1483 }
1484+ Component {
1485+ id: monthWithEventsDelegate
1486+
1487+ MonthComponentWithEventsDateDelegate {
1488+ property var delegateDate: intern.monthStart.addDays(index)
1489+
1490+ date: delegateDate.getDate()
1491+ isCurrentMonth: delegateDate.getMonth() === root.currentMonth
1492+ showEvent: intern.eventStatus[index] === true
1493+ lunarData: {
1494+ if (!root.displayLunarCalendar)
1495+ return null
1496+
1497+ var lunar = Lunar.calendar.solar2lunar(intern.monthStartYear,
1498+ intern.monthStartMonth + 1,
1499+ intern.monthStartDate + index)
1500+ if (lunar.isTerm) {
1501+ return {"lunarText": lunar.Term, "isTerm" :lunar.isTerm};
1502+ } else {
1503+ return {"lunarText": lunar.IDayCn, "isTerm" :lunar.isTerm};
1504+ }
1505+ }
1506+ isToday: intern.todayDate == date && intern.isCurMonthTodayMonth
1507+ isSelected: intern.highlightedIndex == index
1508+ width: monthGrid.dayWidth
1509+ height: monthGrid.dayHeight
1510+ }
1511+ }
1512+
1513+ Component {
1514+ id: monthWithoutEventsDelegate
1515+
1516+ MonthComponentDateDelegate {
1517+ property var delegateDate: intern.monthStart.addDays(index)
1518+
1519+ date: delegateDate.getDate()
1520+ isCurrentMonth: delegateDate.getMonth() === root.currentMonth
1521+
1522+ isToday: intern.todayDate == date && intern.isCurMonthTodayMonth
1523+ isSelected: intern.highlightedIndex == index
1524+ width: monthGrid.dayWidth
1525+ height: monthGrid.dayHeight
1526+ }
1527+ }
1528 }
1529
1530=== modified file 'MonthComponentDateDelegate.qml'
1531--- MonthComponentDateDelegate.qml 2016-02-03 08:06:25 +0000
1532+++ MonthComponentDateDelegate.qml 2016-03-07 15:59:03 +0000
1533@@ -1,6 +1,5 @@
1534 import QtQuick 2.4
1535 import Ubuntu.Components 1.3
1536-import "./3rd-party/lunar.js" as Lunar
1537
1538 Item{
1539 id: dateRootItem
1540@@ -8,30 +7,13 @@
1541 property int date;
1542 property bool isCurrentMonth;
1543 property bool isToday;
1544- property bool showEvent;
1545- property bool showLunarCalendar;
1546 property alias fontSize: dateLabel.font.pixelSize
1547-
1548 property bool isSelected: false
1549
1550- Loader {
1551- sourceComponent: (isToday && isCurrentMonth) || isSelected ? highLightComp : undefined
1552-
1553- onSourceComponentChanged: {
1554- width = Qt.binding( function() {
1555- var width = dateRootItem.height > dateRootItem.width ? dateRootItem.width :dateRootItem.height
1556- return ( width / 1.3 );
1557- });
1558- height = Qt.binding ( function() { return width} );
1559- anchors.centerIn = Qt.binding( function() { return dateLabel});
1560- }
1561- }
1562-
1563- Label {
1564+ Text {
1565 id: dateLabel
1566 anchors.centerIn: parent
1567 text: date
1568- fontSize: root.dateLabelFontSize
1569 color: {
1570 if( isCurrentMonth ) {
1571 if( isToday || isSelected ) {
1572@@ -48,125 +30,4 @@
1573 }
1574 }
1575 }
1576-
1577- Loader{
1578- sourceComponent: showLunarCalendar ? reservedFiled : undefined
1579- onSourceComponentChanged: {
1580- if (item != undefined) {
1581- item.reservedData = Qt.binding(function(){
1582- var lunarDate = Lunar.calendar.solar2lunar(intern.monthStartYear,
1583- intern.monthStartMonth + 1,
1584- intern.monthStartDate + index)
1585- if (lunarDate.isTerm) {
1586- return {"lunarText": lunarDate.Term, "isTerm" :lunarDate.isTerm};
1587- } else {
1588- return {"lunarText": lunarDate.IDayCn, "isTerm" :lunarDate.isTerm};
1589- }
1590- })
1591- }
1592-
1593- width = Qt.binding( function() { return units.gu(0.8)})
1594- height = Qt.binding( function() { return width })
1595- anchors.horizontalCenter = Qt.binding( function() { return parent.horizontalCenter })
1596- anchors.top = Qt.binding( function() { return parent.verticalCenter })
1597- anchors.topMargin = Qt.binding( function() {
1598- return (dateRootItem.height > dateRootItem.width ? dateRootItem.width :dateRootItem.height)/ 4.0 + units.gu(0.25)
1599- });
1600- }
1601- }
1602-
1603- //this component is reserved for extra information display
1604- Component {
1605- id: reservedFiled
1606- Label {
1607- id: reservedLabel
1608- property var reservedData
1609- onReservedDataChanged: {
1610- text = reservedData.lunarText
1611- if (reservedData.isTerm)
1612- color = "red";
1613- }
1614-
1615- horizontalAlignment: Text.AlignHCenter
1616- fontSize: root.subLabelFontSize
1617- color: "#5D5D5D"
1618- }
1619- }
1620-
1621- Loader{
1622- sourceComponent: showEvent ? eventIndicatorComp : undefined
1623- onSourceComponentChanged: {
1624- width = Qt.binding( function() { return units.gu(0.8)})
1625- height = Qt.binding( function() { return width })
1626- anchors.horizontalCenter = Qt.binding( function() { return parent.horizontalCenter })
1627- anchors.top = Qt.binding( function() { return parent.verticalCenter })
1628- anchors.topMargin = Qt.binding( function() {
1629- if (showLunarCalendar) {
1630- return (dateRootItem.height > dateRootItem.width ? dateRootItem.width :dateRootItem.height) / 2 + units.gu(1.5)
1631- } else {
1632- var w = (dateRootItem.height > dateRootItem.width ? dateRootItem.width :dateRootItem.height)/1.3
1633- return (w/2) + units.gu(0.1)
1634- }
1635- });
1636- }
1637- }
1638-
1639- Component{
1640- id: eventIndicatorComp
1641- Rectangle {
1642- anchors.fill: parent
1643- radius: height/2
1644- color: "black"
1645- }
1646- }
1647-
1648- Component{
1649- id: highLightComp
1650- UbuntuShape{
1651- color: {
1652- if( isToday && !isSelected ) {
1653- "#DD4814"
1654- } else {
1655- "gray"
1656- }
1657- }
1658-
1659- Rectangle{
1660- anchors.fill: parent
1661- anchors.margins: units.gu(0.5)
1662- color: isToday ? "#DD4814" : "darkgray"
1663- }
1664- }
1665- }
1666-
1667- MouseArea {
1668- anchors.fill: parent
1669- onPressAndHold: {
1670- if( isSelected ) {
1671- var selectedDate = new Date();
1672- selectedDate.setFullYear(intern.monthStartYear)
1673- selectedDate.setMonth(intern.monthStartMonth + 1)
1674- selectedDate.setDate(date)
1675- selectedDate.setMinutes(60, 0, 0)
1676- pageStack.push(Qt.resolvedUrl("NewEvent.qml"), {"date":selectedDate, "model":eventModel});
1677- }
1678- }
1679- onClicked: {
1680- var selectedDate = new Date(intern.monthStartYear,
1681- intern.monthStartMonth,
1682- intern.monthStartDate + index, 0, 0, 0, 0)
1683- if( isYearView ) {
1684- //If yearView is clicked then open selected MonthView
1685- root.monthSelected(selectedDate);
1686- } else {
1687- if( isSelected ) {
1688- //If monthView is clicked then open selected DayView
1689- root.dateSelected(selectedDate);
1690- } else {
1691- intern.selectedIndex = index
1692- root.dateHighlighted(selectedDate)
1693- }
1694- }
1695- }
1696- }
1697 }
1698
1699=== added file 'MonthComponentWithEventsDateDelegate.qml'
1700--- MonthComponentWithEventsDateDelegate.qml 1970-01-01 00:00:00 +0000
1701+++ MonthComponentWithEventsDateDelegate.qml 2016-03-07 15:59:03 +0000
1702@@ -0,0 +1,80 @@
1703+import QtQuick 2.4
1704+import Ubuntu.Components 1.3
1705+
1706+Item{
1707+ id: dateRootItem
1708+
1709+ property int date;
1710+ property bool isCurrentMonth;
1711+ property bool isToday;
1712+ property bool showEvent;
1713+ property alias fontSize: dateLabel.font.pixelSize
1714+ property bool isSelected: false
1715+ property alias lunarData: lunarLabel.lunarData
1716+
1717+ Text {
1718+ id: dateLabel
1719+ anchors.centerIn: parent
1720+ text: date
1721+ color: {
1722+ if( isCurrentMonth ) {
1723+ if( isToday || isSelected ) {
1724+ "white"
1725+ } else {
1726+ "#5D5D5D"
1727+ }
1728+ } else {
1729+ if(isSelected) {
1730+ "white"
1731+ } else {
1732+ "#AEA79F"
1733+ }
1734+ }
1735+ }
1736+ }
1737+
1738+ Label {
1739+ id: lunarLabel
1740+
1741+ property var lunarData: null
1742+
1743+ text: lunarData ? lunarData.lunarText : ""
1744+ color: {
1745+ if (lunarData && lunarData.isTerm) {
1746+ if (isCurrentMonth && isToday)
1747+ return "black"
1748+ else
1749+ return UbuntuColors.red
1750+ } else {
1751+ if (isSelected)
1752+ return "white"
1753+ else
1754+ return "#5D5D5D"
1755+ }
1756+ }
1757+ fontSize: "small"
1758+ visible: (lunarData != null)
1759+ horizontalAlignment: Text.AlignHCenter
1760+ width: parent.width
1761+ anchors {
1762+ top: dateLabel.bottom
1763+ topMargin: units.gu(0.5)
1764+ }
1765+ }
1766+
1767+ Rectangle {
1768+ id: eventIndicator
1769+
1770+ width: visible ? units.gu(0.8) : 0
1771+ height: width
1772+ anchors {
1773+ horizontalCenter: parent.horizontalCenter
1774+ //top: parent.verticalCenter
1775+ //topMargin: ((Math.min(parent.height, dateRootItem.width) / 1.3) / 2) + units.gu(0.1)
1776+ bottom: parent.bottom
1777+ }
1778+ radius: height/2
1779+ color:"black"
1780+ visible: showEvent
1781+ }
1782+}
1783
1784=== modified file 'MonthView.qml'
1785--- MonthView.qml 2016-02-03 08:06:25 +0000
1786+++ MonthView.qml 2016-03-07 15:59:03 +0000
1787@@ -17,21 +17,31 @@
1788 */
1789 import QtQuick 2.4
1790 import Ubuntu.Components 1.3
1791+
1792 import "dateExt.js" as DateExt
1793 import "colorUtils.js" as Color
1794 import "./3rd-party/lunar.js" as Lunar
1795
1796-Page {
1797+PageWithBottomEdge {
1798 id: monthViewPage
1799 objectName: "monthViewPage"
1800
1801- property var currentMonth: DateExt.today();
1802+ property var anchorDate: DateExt.today();
1803+ readonly property var firstDayOfAnchorDate: new Date(anchorDate.getFullYear(),
1804+ anchorDate.getMonth(),
1805+ 1,
1806+ 0, 0, 0)
1807+ readonly property var currentDate: monthViewPath.currentItem.indexDate
1808+
1809 property var selectedDay;
1810+ property var highlightedDate;
1811+ property bool displayLunarCalendar: false
1812
1813 signal dateSelected(var date);
1814- signal dateHighlighted(var date);
1815
1816 Keys.forwardTo: [monthViewPath]
1817+ createEventAt: highlightedDate ? highlightedDate : currentDate
1818+ onAnchorDateChanged: monthViewPath.scrollToBegginer()
1819
1820 Action {
1821 id: calendarTodayAction
1822@@ -39,7 +49,7 @@
1823 iconName: "calendar-today"
1824 text: i18n.tr("Today")
1825 onTriggered: {
1826- currentMonth = new Date().midnight()
1827+ anchorDate = new Date().midnight()
1828 }
1829 }
1830
1831@@ -49,20 +59,26 @@
1832 leadingActionBar.actions: tabs.tabsAction
1833 trailingActionBar.actions: [
1834 calendarTodayAction,
1835- commonHeaderActions.newEventAction,
1836 commonHeaderActions.showCalendarAction,
1837 commonHeaderActions.reloadAction,
1838 commonHeaderActions.syncCalendarAction,
1839 commonHeaderActions.settingsAction
1840 ]
1841 title: {
1842- // TRANSLATORS: this is a time formatting string,
1843- // see http://qt-project.org/doc/qt-5/qml-qtqml-date.html#details for valid expressions.
1844- // It's used in the header of the month and week views
1845- var monthName = currentMonth.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy"))
1846- return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1)
1847+ if (displayLunarCalendar) {
1848+ var year = currentDate.getFullYear()
1849+ var month = currentDate.getMonth()
1850+ var day = Math.floor(Date.daysInMonth(year, month) / 2.0)
1851+ var lunarDate = Lunar.calendar.solar2lunar(year, month + 1, day)
1852+ return i18n.tr("%1 %2").arg(lunarDate .IMonthCn).arg(lunarDate.gzYear)
1853+ } else {
1854+ // TRANSLATORS: this is a time formatting string,
1855+ // see http://qt-project.org/doc/qt-5/qml-qtqml-date.html#details for valid expressions.
1856+ // It's used in the header of the month and week views
1857+ var monthName = currentDate.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy"))
1858+ return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1)
1859+ }
1860 }
1861-
1862 flickable: null
1863 }
1864
1865@@ -70,85 +86,33 @@
1866 id: monthViewPath
1867 objectName: "monthViewPath"
1868
1869- property var startMonth: currentMonth;
1870-
1871 anchors {
1872 fill: parent
1873 topMargin: header.height
1874 }
1875
1876- onNextItemHighlighted: {
1877- nextMonth();
1878- }
1879-
1880- onPreviousItemHighlighted: {
1881- previousMonth();
1882- }
1883-
1884- function nextMonth() {
1885- currentMonth = addMonth(currentMonth, 1);
1886- }
1887-
1888- function previousMonth() {
1889- currentMonth = addMonth(currentMonth, -1);
1890- }
1891-
1892- function addMonth(date,month) {
1893- return new Date(date.getFullYear(), date.getMonth() + month, 1, 0, 0, 0);
1894- }
1895-
1896- delegate: Loader {
1897+ delegate: MonthWithEventsComponent {
1898+ property var indexDate: firstDayOfAnchorDate.addMonths(monthViewPath.loopCurrentIndex + monthViewPath.indexType(index))
1899+
1900+ currentMonth: indexDate.getMonth()
1901+ currentYear: indexDate.getFullYear()
1902+ displayLunarCalendar: monthViewPage.displayLunarCalendar
1903+
1904+ modelFilter: eventModel.filter
1905 width: parent.width - units.gu(4)
1906 height: parent.height
1907-
1908- sourceComponent: delegateComponent
1909- asynchronous: index !== monthViewPath.currentIndex
1910-
1911- Component {
1912- id: delegateComponent
1913-
1914- MonthComponent {
1915- isCurrentItem: index === monthViewPath.currentIndex
1916-
1917- showEvents: true
1918-
1919- displayWeekNumber: mainView.displayWeekNumber
1920- displayLunarCalendar: mainView.displayLunarCalendar
1921- anchors.fill: parent
1922-
1923- currentMonth: monthViewPath.addMonth(monthViewPath.startMonth,
1924- monthViewPath.indexType(index));
1925-
1926- selectedDay: monthViewPage.selectedDay
1927- isYearView: false
1928-
1929- onDateSelected: {
1930- monthViewPage.dateSelected(date);
1931- }
1932-
1933- onDateHighlighted: {
1934- monthViewPage.dateHighlighted(date);
1935- }
1936- }
1937+ isCurrentItem: PathView.isCurrentItem
1938+ isActive: !monthViewPath.moving && !monthViewPath.flicking
1939+ displayWeekNumber: mainView.displayWeekNumber
1940+ highlightedDate: monthViewPage.highlightedDate
1941+ isYearView: false
1942+
1943+ onDateSelected: {
1944+ monthViewPage.dateSelected(date);
1945+ }
1946+ onDateHighlighted: {
1947+ monthViewPage.highlightedDate = date
1948 }
1949 }
1950 }
1951-
1952- Component.onCompleted: {
1953- pageHeader.title = Qt.binding(function(){
1954- if(mainView.displayLunarCalendar){
1955- var year = currentMonth.getFullYear()
1956- var month = currentMonth.getMonth()
1957- var day = Math.floor(Date.daysInMonth(year, month) / 2.0)
1958- var lunarDate = Lunar.calendar.solar2lunar( year, month + 1, day)
1959- return i18n.tr("%1 %2").arg(lunarDate .IMonthCn).arg(lunarDate.gzYear)
1960- } else {
1961- // TRANSLATORS: this is a time formatting string,
1962- // see http://qt-project.org/doc/qt-5/qml-qtqml-date.html#details for valid expressions.
1963- // It's used in the header of the month and week views
1964- var monthName = currentMonth.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy"))
1965- return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1)
1966- }
1967- })
1968- }
1969 }
1970
1971=== added file 'MonthWithEventsComponent.qml'
1972--- MonthWithEventsComponent.qml 1970-01-01 00:00:00 +0000
1973+++ MonthWithEventsComponent.qml 2016-03-07 15:59:03 +0000
1974@@ -0,0 +1,80 @@
1975+/*
1976+ * Copyright (C) 2013-2014 Canonical Ltd
1977+ *
1978+ * This file is part of Ubuntu Calendar App
1979+ *
1980+ * Ubuntu Calendar App is free software: you can redistribute it and/or modify
1981+ * it under the terms of the GNU General Public License version 3 as
1982+ * published by the Free Software Foundation.
1983+ *
1984+ * Ubuntu Calendar App is distributed in the hope that it will be useful,
1985+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1986+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1987+ * GNU General Public License for more details.
1988+ *
1989+ * You should have received a copy of the GNU General Public License
1990+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1991+ */
1992+import QtQuick 2.4
1993+import Ubuntu.Components 1.3
1994+import QtOrganizer 5.0
1995+import "dateExt.js" as DateExt
1996+import "colorUtils.js" as Color
1997+
1998+MonthComponent {
1999+ id: root
2000+ objectName: "MonthComponent"
2001+
2002+ property bool isActive: false
2003+ property var modelFilter: invalidFilter
2004+
2005+ onIsActiveChanged: {
2006+ if (isActive && (mainModel.filter === invalidFilter)) {
2007+ idleRefresh.reset()
2008+ }
2009+ }
2010+
2011+ Timer {
2012+ id: idleRefresh
2013+
2014+ function reset()
2015+ {
2016+ mainModel.filter = invalidFilter
2017+ restart()
2018+ }
2019+
2020+ interval: root.isCurrentItem ? 1000 : 2000
2021+ repeat: false
2022+ onTriggered: {
2023+ mainModel.filter = Qt.binding(function() { return root.modelFilter } )
2024+ }
2025+ }
2026+
2027+ InvalidFilter {
2028+ id: invalidFilter
2029+ }
2030+
2031+ EventListModel {
2032+ id: mainModel
2033+
2034+ startPeriod: root.monthStartDate.midnight();
2035+ endPeriod: root.monthStartDate.addDays((/*monthGrid.rows * cols */ 42 )-1).endOfDay()
2036+ filter: invalidFilter
2037+ fetchHint: FetchHint {
2038+ detailTypesHint: [ Detail.EventTime,
2039+ Detail.JournalTime,
2040+ Detail.TodoTime
2041+ ]
2042+ }
2043+
2044+ onModelChanged: {
2045+ var eventStatus = mainModel.containsItems(startPeriod,
2046+ endPeriod,
2047+ 86400/*24*60*60*/);
2048+ root.updateEvents(eventStatus)
2049+ }
2050+
2051+ onStartPeriodChanged: idleRefresh.reset()
2052+ onEndPeriodChanged: idleRefresh.reset()
2053+ }
2054+}
2055
2056=== modified file 'NewEvent.qml'
2057--- NewEvent.qml 2016-02-02 22:54:03 +0000
2058+++ NewEvent.qml 2016-03-07 15:59:03 +0000
2059@@ -20,7 +20,7 @@
2060 import QtOrganizer 5.0
2061 import Ubuntu.Components 1.3
2062 import Ubuntu.Components.Popups 1.0
2063-import Ubuntu.Components.ListItems 1.0 as ListItem
2064+import Ubuntu.Components.ListItems 1.0 as ListItems
2065 import Ubuntu.Components.Themes.Ambiance 1.0
2066 import Ubuntu.Components.Pickers 1.0
2067 import QtOrganizer 5.0
2068@@ -31,10 +31,11 @@
2069 objectName: 'newEventPage'
2070
2071 property var date;
2072+ property alias allDay: allDayEventCheckbox.checked
2073
2074 property var event:null;
2075 property var rule :null;
2076- property var model;
2077+ property var model:null;
2078
2079 property var startDate;
2080 property var endDate;
2081@@ -42,56 +43,44 @@
2082 property alias scrollY: flickable.contentY
2083 property bool isEdit: false
2084
2085- flickable: null
2086-
2087 signal eventAdded(var event);
2088 signal eventDeleted(var event);
2089-
2090- onStartDateChanged: {
2091- startDateTimeInput.dateTime = startDate;
2092-
2093- // set time forward to one hour
2094- var time_forward = 3600000;
2095-
2096- if (isEdit && event !== null) {
2097- time_forward = event.endDateTime - event.startDateTime;
2098- }
2099- adjustEndDateToStartDate(time_forward);
2100- }
2101-
2102- onEndDateChanged: {
2103- endDateTimeInput.dateTime = endDate;
2104- }
2105-
2106- head.actions: [
2107- Action {
2108- text: i18n.tr("Delete");
2109- objectName: "delete"
2110- iconName: "delete"
2111- visible : isEdit
2112- onTriggered: {
2113- var dialog = PopupUtils.open(Qt.resolvedUrl("DeleteConfirmationDialog.qml"),root,{"event": event});
2114- dialog.deleteEvent.connect( function(eventId){
2115- model.removeItem(eventId);
2116- pageStack.pop();
2117- root.eventDeleted(eventId);
2118- });
2119- }
2120- },
2121- Action {
2122- iconName: "ok"
2123- objectName: "save"
2124- text: i18n.tr("Save")
2125- enabled: !!titleEdit.text.trim()
2126- onTriggered: saveToQtPim();
2127- }]
2128+ signal canceled()
2129+
2130 Component.onCompleted: {
2131- //If current date is setted by an argument we don't have to change it.
2132- if(typeof(date) === 'undefined'){
2133+ setDate(root.date)
2134+
2135+ if (event === undefined) {
2136+ return
2137+ } else if(event === null){
2138+ isEdit = false;
2139+ addEvent();
2140+ } else{
2141+ isEdit = true;
2142+ editEvent(event);
2143+ }
2144+ }
2145+
2146+ function cancel()
2147+ {
2148+ if (pageStack)
2149+ pageStack.pop();
2150+ root.canceled()
2151+ }
2152+
2153+ function updateEventDate(date, allDay) {
2154+ root.startDate = undefined
2155+ root.endDate = undefined
2156+ setDate(date)
2157+ root.allDay = allDay
2158+ }
2159+
2160+ function setDate(date) {
2161+ if ((typeof(date) === 'undefined') || (date === null)) {
2162 date = new Date();
2163 }
2164
2165- if( typeof(date) == 'undefined' || (date.getHours() == 0 && date.getMinutes() == 0) ) {
2166+ if(date.getHours() === 0 && date.getMinutes() === 0) {
2167 var newDate = new Date();
2168 date.setHours(newDate.getHours(), newDate.getMinutes());
2169 }
2170@@ -108,15 +97,6 @@
2171 endTimeInput.text = Qt.formatDateTime(endDate, Qt.locale().timeFormat(Locale.ShortFormat));
2172 }
2173
2174- if(event === null){
2175- isEdit = false;
2176- addEvent();
2177- }
2178-
2179- else{
2180- isEdit = true;
2181- editEvent(event);
2182- }
2183 }
2184
2185 function selectCalendar(collectionId) {
2186@@ -146,14 +126,14 @@
2187 }
2188
2189 startDate =new Date(e.startDateTime);
2190- endDate = new Date(e.endDateTime);
2191
2192 if(e.displayLabel) {
2193 titleEdit.text = e.displayLabel;
2194 }
2195- if(e.allDay){
2196- allDayEventCheckbox.checked =true;
2197- endDate = endDate.addDays(-1);
2198+
2199+ if (e.allDay) {
2200+ allDayEventCheckbox.checked = true
2201+ endDate = new Date(e.endDateTime).addDays(-1);
2202 }
2203
2204 if(e.location) {
2205@@ -170,8 +150,7 @@
2206 if( e.itemType === Type.Event ) {
2207 if(e.attendees){
2208 for( var j = 0 ; j < e.attendees.length ; ++j ) {
2209- contactList.array.push(e.attendees[j]);
2210- contactModel.append(e.attendees[j]);
2211+ contactModel.append({"contact": e.attendees[j]});
2212 }
2213 }
2214 }
2215@@ -202,11 +181,11 @@
2216 }
2217
2218 event.allDay = allDayEventCheckbox.checked;
2219- event.startDateTime = startDate;
2220-
2221 if (event.allDay){
2222- event.endDateTime = endDate.addDays(1);
2223+ event.startDateTime = new Date(startDate).midnight()
2224+ event.endDateTime = new Date(endDate).addDays(1).midnight()
2225 } else {
2226+ event.startDateTime = startDate;
2227 event.endDateTime = endDate;
2228 }
2229
2230@@ -215,13 +194,14 @@
2231 event.location = locationEdit.text
2232
2233 if( event.itemType === Type.Event ) {
2234- event.attendees = []; // if Edit remove all attendes & add them again if any
2235- var contacts = [];
2236- for(var i=0; i < contactList.array.length ; ++i) {
2237- var contact = contactList.array[i]
2238- contacts.push(contact);
2239+ var newContacts = []
2240+ for(var i=0; i < contactModel.count ; ++i) {
2241+ var contact = contactModel.get(i).contact
2242+ if (contact) {
2243+ newContacts.push(internal.attendeeFromData(contact.attendeeId, contact.name, contact.emailAddress));
2244+ }
2245 }
2246- event.attendees = contacts;
2247+ event.attendees = newContacts;
2248 }
2249
2250 //Set the Rule object to an event
2251@@ -253,9 +233,9 @@
2252 event.removeDetail(comment);
2253 }
2254
2255- model.saveItem(event);
2256- pageStack.pop();
2257-
2258+ model.saveItem(event)
2259+ if (pageStack)
2260+ pageStack.pop();
2261 root.eventAdded(event);
2262 }
2263 }
2264@@ -294,10 +274,15 @@
2265 function roundDate(date) {
2266 var tempDate = new Date(date)
2267 tempDate.setHours(date.getHours(), date.getMinutes(), 0, 0);
2268- if(tempDate.getMinutes() < 30)
2269+ var tempMinutes = tempDate.getMinutes()
2270+ if (tempMinutes === 0) {
2271+ return tempDate
2272+ } else if(tempMinutes < 30) {
2273 return tempDate.setMinutes(30)
2274- tempDate.setMinutes(0)
2275- return tempDate.setHours(tempDate.getHours() + 1)
2276+ } else {
2277+ tempDate.setMinutes(0)
2278+ return tempDate.setHours(tempDate.getHours() + 1)
2279+ }
2280 }
2281
2282 function adjustEndDateToStartDate(time_forward) {
2283@@ -322,10 +307,65 @@
2284 scrollAnimation.start()
2285 }
2286
2287- title: isEdit ? i18n.tr("Edit Event"):i18n.tr("New Event")
2288-
2289- Keys.onEscapePressed: {
2290- pageStack.pop();
2291+ Keys.onEscapePressed: root.cancel()
2292+ onStartDateChanged: {
2293+ if (!startDate)
2294+ return
2295+
2296+ startDateTimeInput.dateTime = startDate;
2297+
2298+ // set time forward to one hour
2299+ var time_forward = 3600000;
2300+
2301+ if (isEdit && event !== null) {
2302+ time_forward = event.endDateTime - event.startDateTime;
2303+ }
2304+ adjustEndDateToStartDate(time_forward);
2305+ }
2306+
2307+ onEndDateChanged: {
2308+ if (endDate)
2309+ endDateTimeInput.dateTime = endDate;
2310+ }
2311+
2312+ header: PageHeader {
2313+ id: pageHeader
2314+
2315+ flickable: null
2316+ title: isEdit ? i18n.tr("Edit Event"):i18n.tr("New Event")
2317+ leadingActionBar.actions: Action {
2318+ id: backAction
2319+
2320+ name: "cancel"
2321+ text: i18n.tr("Cancel")
2322+ iconName: isEdit ? "back" : "down"
2323+ onTriggered: root.cancel()
2324+ }
2325+
2326+ trailingActionBar.actions: [
2327+ Action {
2328+ text: i18n.tr("Delete");
2329+ objectName: "delete"
2330+ iconName: "delete"
2331+ visible : isEdit
2332+ onTriggered: {
2333+ var dialog = PopupUtils.open(Qt.resolvedUrl("DeleteConfirmationDialog.qml"),root,{"event": event});
2334+ dialog.deleteEvent.connect( function(eventId){
2335+ model.removeItem(eventId);
2336+ if (pageStack)
2337+ pageStack.pop();
2338+ root.eventDeleted(eventId);
2339+ });
2340+ }
2341+ },
2342+ Action {
2343+ iconName: "ok"
2344+ objectName: "save"
2345+ text: i18n.tr("Save")
2346+ enabled: !!titleEdit.text.trim()
2347+ onTriggered: saveToQtPim();
2348+ }
2349+ ]
2350 }
2351
2352 Component{
2353@@ -376,9 +416,16 @@
2354 flickable.returnToBounds()
2355 }
2356
2357- anchors.fill: parent
2358+ flickableDirection: Flickable.VerticalFlick
2359+ anchors{
2360+ left: parent.left
2361+ top: parent.top
2362+ topMargin: pageHeader.height
2363+ right: parent.right
2364+ bottom: keyboardRectangle.top
2365+ }
2366 contentWidth: width
2367- contentHeight: column.height + units.gu(10)
2368+ contentHeight: column.height
2369
2370 Column {
2371 id: column
2372@@ -411,7 +458,7 @@
2373 }
2374 }
2375
2376- ListItem.Standard {
2377+ ListItems.Standard {
2378 anchors {
2379 left: parent.left
2380 right: parent.right
2381@@ -426,13 +473,13 @@
2382 }
2383 }
2384
2385- ListItem.ThinDivider {}
2386+ ListItems.ThinDivider {}
2387
2388 Column {
2389 width: parent.width
2390 spacing: units.gu(1)
2391
2392- ListItem.Header{
2393+ ListItems.Header{
2394 text: i18n.tr("Event Details")
2395 }
2396
2397@@ -446,6 +493,7 @@
2398 margins: units.gu(2)
2399 }
2400
2401+ inputMethodHints: Qt.ImhNoPredictiveText
2402 placeholderText: i18n.tr("Event Name")
2403 onFocusChanged: {
2404 if(titleEdit.focus) {
2405@@ -482,6 +530,7 @@
2406 margins: units.gu(2)
2407 }
2408
2409+ inputMethodHints: Qt.ImhNoPredictiveText
2410 placeholderText: i18n.tr("Location")
2411
2412 onFocusChanged: {
2413@@ -496,7 +545,7 @@
2414 width: parent.width
2415 spacing: units.gu(1)
2416
2417- ListItem.Header {
2418+ ListItems.Header {
2419 text: i18n.tr("Calendar")
2420 }
2421
2422@@ -521,9 +570,11 @@
2423 width: height
2424 height: parent.height - units.gu(2)
2425 color: modelData.color
2426- anchors.right: parent.right
2427- anchors.rightMargin: units.gu(2)
2428- anchors.verticalCenter: parent.verticalCenter
2429+ anchors {
2430+ right: parent.right
2431+ rightMargin: units.gu(4)
2432+ verticalCenter: parent.verticalCenter
2433+ }
2434 }
2435 }
2436 onExpandedChanged: Qt.inputMethod.hide();
2437@@ -534,14 +585,17 @@
2438 width: parent.width
2439 spacing: units.gu(1)
2440
2441- ListItem.Header {
2442+ ListItems.Header {
2443 text: i18n.tr("Guests")
2444 }
2445
2446 Button{
2447+ id: addGuestButton
2448+ objectName: "addGuestButton"
2449+
2450+ property var contactsPopup: null
2451+
2452 text: i18n.tr("Add Guest")
2453- objectName: "addGuestButton"
2454-
2455 anchors {
2456 left: parent.left
2457 right: parent.right
2458@@ -549,14 +603,21 @@
2459 }
2460
2461 onClicked: {
2462- var popup = PopupUtils.open(Qt.resolvedUrl("ContactChoicePopup.qml"), contactList);
2463- popup.contactSelected.connect( function(contact) {
2464- var t = internal.contactToAttendee(contact);
2465- if( !internal.isContactAlreadyAdded(contact) ) {
2466- contactModel.append(t);
2467- contactList.array.push(t);
2468+ if (contactsPopup)
2469+ return
2470+
2471+ flickable.makeMeVisible(addGuestButton)
2472+ contactsPopup = PopupUtils.open(Qt.resolvedUrl("ContactChoicePopup.qml"), addGuestButton);
2473+ contactsPopup.contactSelected.connect( function(contact, emailAddress) {
2474+ if(!internal.isContactAlreadyAdded(contact, emailAddress) ) {
2475+ var t = internal.contactToAttendee(contact, emailAddress);
2476+ contactModel.append({"contact": t});
2477 }
2478+
2479 });
2480+ contactsPopup.Component.onDestruction.connect( function() {
2481+ addGuestButton.contactsPopup = null
2482+ })
2483 }
2484 }
2485
2486@@ -577,35 +638,40 @@
2487 width: parent.width
2488 clip: true
2489
2490- property var array: []
2491-
2492 ListModel{
2493 id: contactModel
2494 }
2495
2496 Repeater{
2497 model: contactModel
2498- delegate: ListItem.Standard {
2499+ delegate: ListItem {
2500 objectName: "eventGuest%1".arg(index)
2501- height: units.gu(4)
2502- text: name
2503- removable: true
2504- onItemRemoved: {
2505- contactList.array.splice(index, 1)
2506- contactModel.remove(index)
2507+
2508+ ListItemLayout {
2509+ title.text: contact.name
2510+ subtitle.text: contact.emailAddress
2511+ }
2512+
2513+ leadingActions: ListItemActions {
2514+ actions: Action {
2515+ iconName: "delete"
2516+ onTriggered: {
2517+ contactModel.remove(index)
2518+ }
2519+ }
2520 }
2521 }
2522 }
2523 }
2524 }
2525
2526- ListItem.ThinDivider {
2527- visible: event.itemType === Type.Event
2528+ ListItems.ThinDivider {
2529+ visible: (event != undefined) && (event.itemType === Type.Event)
2530 }
2531
2532 }
2533
2534- ListItem.Subtitled{
2535+ ListItems.Subtitled{
2536 id:thisHappens
2537 objectName :"thisHappens"
2538
2539@@ -615,17 +681,17 @@
2540
2541 showDivider: false
2542 progression: true
2543- visible: event.itemType === Type.Event
2544+ visible: (event != undefined) && (event.itemType === Type.Event)
2545 text: i18n.tr("Repeats")
2546- subText: event.itemType === Type.Event ? rule === null ? Defines.recurrenceLabel[0] : eventUtils.getRecurrenceString(rule) : ""
2547+ subText: (event != undefined) && (event.itemType === Type.Event) ? rule === null ? Defines.recurrenceLabel[0] : eventUtils.getRecurrenceString(rule) : ""
2548 onClicked: pageStack.push(Qt.resolvedUrl("EventRepetition.qml"),{"eventRoot": root,"isEdit":isEdit});
2549 }
2550
2551- ListItem.ThinDivider {
2552- visible: event.itemType === Type.Event
2553+ ListItems.ThinDivider {
2554+ visible: (event != undefined) && (event.itemType === Type.Event)
2555 }
2556
2557- ListItem.Subtitled{
2558+ ListItems.Subtitled{
2559 id:eventReminder
2560 objectName : "eventReminder"
2561
2562@@ -658,22 +724,42 @@
2563 "eventTitle": titleEdit.text})
2564 }
2565
2566- ListItem.ThinDivider {}
2567+ ListItems.ThinDivider {}
2568 }
2569 }
2570+
2571 // used to keep the field visible when the keyboard appear or dismiss
2572 KeyboardRectangle {
2573- id: keyboard
2574-
2575- onHeightChanged: {
2576- if (flickable.activeItem) {
2577- flickable.makeMeVisible(flickable.activeItem)
2578+ id: keyboardRectangle
2579+
2580+ anchors {
2581+ left: parent.left
2582+ right: parent.right
2583+ bottom: parent.bottom
2584+ }
2585+
2586+ Behavior on height {
2587+ SequentialAnimation {
2588+ PauseAnimation { duration: 200 }
2589+ ScriptAction {
2590+ script: {
2591+ if (addGuestButton.contactsPopup) {
2592+ // WORKAROUND: causes the popover to follow the buttom position when keyboard appears
2593+ flickable.makeMeVisible(addGuestButton)
2594+ addGuestButton.contactsPopup.caller = null
2595+ addGuestButton.contactsPopup.caller = addGuestButton
2596+ } else {
2597+ flickable.makeMeVisible(flickable.activeItem)
2598+ }
2599+ }
2600+ }
2601 }
2602 }
2603 }
2604
2605 QtObject {
2606 id: internal
2607+
2608 property var collectionId;
2609
2610 function clearFocus() {
2611@@ -685,22 +771,33 @@
2612 messageEdit.focus = false
2613 }
2614
2615- function isContactAlreadyAdded(contact) {
2616- for(var i=0; i < contactList.array.length ; ++i) {
2617- var attendee = contactList.array[i];
2618- if( attendee.attendeeId === contact.contactId) {
2619- return true;
2620+ function isContactAlreadyAdded(contact, emailAddress) {
2621+ for(var i=0; i < contactModel.count; i++) {
2622+ var attendee = contactModel.get(i).contact;
2623+ if (attendee && (emailAddress.length > 0)) {
2624+ if (attendee.emailAddress === emailAddress) {
2625+ return true;
2626+ }
2627+ } else {
2628+ if (attendee.attendeeId === contact.contactId) {
2629+ return true
2630+ }
2631 }
2632 }
2633 return false;
2634 }
2635
2636- function contactToAttendee(contact) {
2637- var attendee = Qt.createQmlObject("import QtOrganizer 5.0; EventAttendee{}", event, "NewEvent.qml");
2638- attendee.name = contact.displayLabel.label
2639- attendee.emailAddress = contact.email.emailAddress;
2640- attendee.attendeeId = contact.contactId;
2641+ function attendeeFromData(id, name, emailAddress)
2642+ {
2643+ var attendee = Qt.createQmlObject("import QtOrganizer 5.0; EventAttendee{}", internal, "NewEvent.qml");
2644+ attendee.name = name
2645+ attendee.emailAddress = emailAddress
2646+ attendee.attendeeId = id
2647 return attendee;
2648 }
2649+
2650+ function contactToAttendee(contact, emailAddress) {
2651+ return attendeeFromData(contact.contactId, contact.displayLabel.label, emailAddress)
2652+ }
2653 }
2654 }
2655
2656=== added file 'NewEventBottomEdge.qml'
2657--- NewEventBottomEdge.qml 1970-01-01 00:00:00 +0000
2658+++ NewEventBottomEdge.qml 2016-03-07 15:59:03 +0000
2659@@ -0,0 +1,115 @@
2660+/*
2661+ * Copyright (C) 2013-2016 Canonical Ltd
2662+ *
2663+ * This file is part of Ubuntu Calendar App
2664+ *
2665+ * Ubuntu Calendar App is free software: you can redistribute it and/or modify
2666+ * it under the terms of the GNU General Public License version 3 as
2667+ * published by the Free Software Foundation.
2668+ *
2669+ * Ubuntu Calendar App is distributed in the hope that it will be useful,
2670+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2671+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2672+ * GNU General Public License for more details.
2673+ *
2674+ * You should have received a copy of the GNU General Public License
2675+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2676+ */
2677+
2678+import QtQuick 2.4
2679+import Ubuntu.Components 1.3
2680+
2681+BottomEdge {
2682+ id: bottomEdge
2683+ objectName: "bottomEdge"
2684+
2685+ property var pageStack: null
2686+ property var eventModel: null
2687+ property var date: new Date()
2688+
2689+ // WORKAROUND: BottomEdge component loads the page async while draging it
2690+ // this cause a very bad visual.
2691+ // To avoid that we create it as soon as the component is ready and keep
2692+ // it invisible until the user start to drag it.
2693+ property var _realPage: null
2694+
2695+ signal opened()
2696+ signal eventCreated(var event)
2697+
2698+ function updateNewEventDate(date, allDay)
2699+ {
2700+ _realPage.updateEventDate(date, allDay)
2701+ }
2702+
2703+ hint {
2704+ visible: bottomEdge.enabled
2705+ enabled: visible
2706+ action: Action {
2707+ objectName: "neweventbutton"
2708+ name: "neweventbutton"
2709+
2710+ iconName: "new-event"
2711+ text: i18n.tr("New Event")
2712+ shortcut: "ctrl+n"
2713+ enabled: bottomEdge.enabled
2714+ onTriggered: bottomEdge.commit()
2715+ }
2716+ }
2717+
2718+ contentComponent: Item {
2719+ id: pageContent
2720+
2721+ implicitWidth: bottomEdge.width
2722+ implicitHeight: bottomEdge.height
2723+ children: bottomEdge._realPage
2724+ Component.onDestruction: {
2725+ if (bottomEdge._realPage) {
2726+ bottomEdge._realPage.destroy()
2727+ bottomEdge._realPage = null
2728+ _realPage = editorPageBottomEdge.createObject(null)
2729+ }
2730+ }
2731+ }
2732+
2733+ onCommitStarted: {
2734+ bottomEdge.opened()
2735+ updateNewEventDate(bottomEdge.date ? bottomEdge.date : new Date(), false)
2736+ }
2737+
2738+ Component.onCompleted: {
2739+ if (eventModel)
2740+ _realPage = editorPageBottomEdge.createObject(null)
2741+ }
2742+
2743+ onEventModelChanged: {
2744+ if (eventModel)
2745+ _realPage = editorPageBottomEdge.createObject(null)
2746+ }
2747+
2748+ Component {
2749+ id: editorPageBottomEdge
2750+ NewEvent {
2751+ id: newEventPage
2752+
2753+ implicitWidth: bottomEdge.width
2754+ implicitHeight: bottomEdge.height
2755+ model: bottomEdge.eventModel
2756+ date: bottomEdge.date
2757+ enabled: bottomEdge.status === BottomEdge.Committed
2758+ active: bottomEdge.status === BottomEdge.Committed
2759+ visible: (bottomEdge.status !== BottomEdge.Hidden)
2760+ onCanceled: bottomEdge.collapse()
2761+ onEventAdded: {
2762+ bottomEdge.collapse()
2763+ bottomEdge.eventCreated(event)
2764+ }
2765+ }
2766+ }
2767+
2768+ Component.onDestruction: {
2769+ if (bottomEdge._realPage) {
2770+ bottomEdge._realPage.destroy()
2771+ bottomEdge._realPage = null
2772+ }
2773+ }
2774+}
2775
2776=== added file 'PageWithBottomEdge.qml'
2777--- PageWithBottomEdge.qml 1970-01-01 00:00:00 +0000
2778+++ PageWithBottomEdge.qml 2016-03-07 15:59:03 +0000
2779@@ -0,0 +1,44 @@
2780+/*
2781+ * Copyright (C) 2013-2016 Canonical Ltd
2782+ *
2783+ * This file is part of Ubuntu Calendar App
2784+ *
2785+ * Ubuntu Calendar App is free software: you can redistribute it and/or modify
2786+ * it under the terms of the GNU General Public License version 3 as
2787+ * published by the Free Software Foundation.
2788+ *
2789+ * Ubuntu Calendar App is distributed in the hope that it will be useful,
2790+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2791+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2792+ * GNU General Public License for more details.
2793+ *
2794+ * You should have received a copy of the GNU General Public License
2795+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2796+ */
2797+import QtQuick 2.4
2798+import Ubuntu.Components 1.3
2799+
2800+Page {
2801+ id: root
2802+
2803+ property alias model: bottomEdge.eventModel
2804+ property alias createEventAt: bottomEdge.date
2805+ property bool bootomEdgeEnabled: bottomEdge.enabled
2806+
2807+ signal bottomEdgeCommitStarted()
2808+ signal eventCreated(var event)
2809+
2810+ function bottomEdgeCommit(date, allDay)
2811+ {
2812+ bottomEdge.commit()
2813+ bottomEdge.updateNewEventDate(date, allDay)
2814+ }
2815+
2816+ NewEventBottomEdge {
2817+ id: bottomEdge
2818+
2819+ pageStack: tabs
2820+ onOpened: root.bottomEdgeCommitStarted()
2821+ onEventCreated: root.eventCreated(event)
2822+ }
2823+}
2824
2825=== modified file 'PathViewBase.qml'
2826--- PathViewBase.qml 2016-01-25 13:20:32 +0000
2827+++ PathViewBase.qml 2016-03-07 15:59:03 +0000
2828@@ -20,8 +20,7 @@
2829 PathView {
2830 id: root
2831
2832- model: 3
2833- snapMode: PathView.SnapOneItem
2834+ readonly property alias loopCurrentIndex: intern.loopCurrentIndex
2835
2836 signal nextItemHighlighted();
2837 signal previousItemHighlighted();
2838@@ -29,6 +28,8 @@
2839 signal scrollUp();
2840 signal scrollDown();
2841
2842+ model: 3
2843+ snapMode: PathView.SnapOneItem
2844 preferredHighlightBegin: 0.5
2845 preferredHighlightEnd: 0.5
2846
2847@@ -68,6 +69,11 @@
2848 }
2849 }
2850
2851+ function scrollToBegginer()
2852+ {
2853+ intern.loopCurrentIndex = intern.previousIndex = currentIndex = 0
2854+ }
2855+
2856 Keys.onLeftPressed:{
2857 root.decrementCurrentIndex();
2858 }
2859@@ -98,15 +104,17 @@
2860 intern.previousIndex = currentIndex
2861
2862 if ( diff > 0 ) {
2863- root.nextItemHighlighted();
2864+ intern.loopCurrentIndex++
2865 }
2866 else {
2867- root.previousItemHighlighted();
2868+ intern.loopCurrentIndex--
2869 }
2870 }
2871
2872 QtObject{
2873 id: intern
2874+
2875+ property int loopCurrentIndex: 0
2876 property int previousIndex: root.currentIndex
2877 }
2878 }
2879
2880=== modified file 'TimeLineBase.qml'
2881--- TimeLineBase.qml 2016-02-03 08:01:25 +0000
2882+++ TimeLineBase.qml 2016-03-07 15:59:03 +0000
2883@@ -1,4 +1,4 @@
2884-/*
2885+ /*
2886 * Copyright (C) 2013-2014 Canonical Ltd
2887 *
2888 * This file is part of Ubuntu Calendar App
2889@@ -20,36 +20,36 @@
2890 import QtOrganizer 5.0
2891
2892 import "dateExt.js" as DateExt
2893+import "calendar_canvas.js" as CanlendarCanvas
2894
2895 Item {
2896 id: bubbleOverLay
2897
2898 property var delegate;
2899 property var day;
2900- property int hourHeight: units.gu(8)
2901- property var model;
2902-
2903- Component.onCompleted: {
2904- bubbleOverLay.createEvents();
2905+
2906+ property alias model: modelConnections.target
2907+ property var flickable: null
2908+ readonly property alias creatingEvent: overlayMouseArea.creatingEvent
2909+ readonly property real hourHeight: units.gu(8)
2910+ readonly property real minuteHeight: (hourHeight / 60)
2911+
2912+ signal pressAndHoldAt(var date)
2913+
2914+ function waitForModelChange()
2915+ {
2916+ intern.waitingForModelChange = true
2917 }
2918
2919- MouseArea {
2920- anchors.fill: parent
2921- objectName: "mouseArea"
2922-
2923- onPressAndHold: {
2924- var selectedDate = new Date(day);
2925- var hour = parseInt(mouseY / hourHeight);
2926- selectedDate.setHours(hour)
2927- createOrganizerEvent(selectedDate);
2928- }
2929-
2930- onPressed: {
2931- intern.now = new Date();
2932- if( intern.now.isSameDay( bubbleOverLay.day ) ) {
2933- bubbleOverLay.showSeparator();
2934- }
2935- }
2936+ function createOrganizerEvent( startDate ) {
2937+ var event = Qt.createQmlObject("import QtOrganizer 5.0; Event {}", Qt.application,"TimeLineBase.qml");
2938+ event.collectionId = (model.defaultCollection().collectionId);
2939+ var endDate = new Date( startDate.getTime() + 3600000 );
2940+ event.startDateTime = startDate;
2941+ event.endDateTime = endDate;
2942+ event.displayLabel = i18n.tr("New event");
2943+ event.setDetail(Qt.createQmlObject("import QtOrganizer 5.0; Comment{ comment: 'X-CAL-DEFAULT-EVENT'}", event,"TimeLineBase.qml"));
2944+ return event
2945 }
2946
2947 function getTimeFromYPos(y, day) {
2948@@ -64,32 +64,6 @@
2949 return date;
2950 }
2951
2952- function createOrganizerEvent( startDate ) {
2953- var event = Qt.createQmlObject("import QtOrganizer 5.0; Event {}", Qt.application,"TimeLineBase.qml");
2954- event.collectionId = (model.defaultCollection().collectionId);
2955- var endDate = new Date( startDate.getTime() + 3600000 );
2956- event.startDateTime = startDate;
2957- event.endDateTime = endDate;
2958- event.displayLabel = i18n.tr("Untitled");
2959- event.setDetail(Qt.createQmlObject("import QtOrganizer 5.0; Comment{ comment: 'X-CAL-DEFAULT-EVENT'}", event,"TimeLineBase.qml"));
2960- model.saveItem(event);
2961- }
2962-
2963- TimeSeparator {
2964- id: separator
2965- objectName: "separator"
2966- width: bubbleOverLay.width
2967- visible: false
2968- z:10
2969- }
2970-
2971- QtObject {
2972- id: intern
2973- property var now : new Date();
2974- property var eventMap;
2975- property var unUsedEvents: new Object();
2976- }
2977-
2978 function showEventDetails(event) {
2979 var comment = event.detail(Detail.Comment);
2980 if(comment && comment.comment === "X-CAL-DEFAULT-EVENT") {
2981@@ -101,167 +75,279 @@
2982
2983 WorkerScript {
2984 id: eventLayoutHelper
2985- source: "EventLayoutHelper.js"
2986
2987+ source: "calendar_canvas_worker.js"
2988 onMessage: {
2989- layoutEvents(messageObject.schedules,messageObject.maxDepth);
2990- }
2991- }
2992-
2993- function layoutEvents(array, depth) {
2994- for(var i=0; i < array.length ; ++i) {
2995- var schedule = array[i];
2996- var event = intern.eventMap[schedule.id];
2997- bubbleOverLay.createEvent(event , schedule.depth, depth +1);
2998- }
2999+ // check if anything changed during the process
3000+ if (intern.dirty) {
3001+ console.debug("Something has changed while work script was running, ignore message")
3002+ } else {
3003+ var events = messageObject.reply
3004+ var dirty = false
3005+ for (var i=0; i < events.length; i++) {
3006+ var e = intern.eventsById[events[i].eventId]
3007+ if (e.eventId != events[i].itemId) {
3008+ console.warn("Event does not match id:", i)
3009+ dirty = true
3010+ }
3011+ if (e.startDateTime.getTime() != events[i].eventStartTime) {
3012+ console.warn("Event does not match start time")
3013+ dirty = true
3014+ }
3015+ if (e.endDateTime.getTime() != events[i].eventEndTime) {
3016+ console.warn("Event does not match end time")
3017+ dirty = true
3018+ }
3019+
3020+ if (dirty) {
3021+ console.warn("Mark as dirty")
3022+ intern.dirty = true
3023+ break
3024+ }
3025+
3026+ createVisual(events[i])
3027+ }
3028+ }
3029+ intern.busy = false
3030+ intern.eventsById = {}
3031+ if (intern.dirty) {
3032+ bubbleOverLay.idleCreateEvents()
3033+ }
3034+ }
3035+ }
3036+
3037+ function createVisual(eventInfo)
3038+ {
3039+ var eventBubble;
3040+ if (intern.unUsedEvents.length === 0) {
3041+ var incubator = delegate.incubateObject(bubbleOverLay)
3042+ incubator.forceCompletion()
3043+ eventBubble = incubator.object
3044+ } else {
3045+ eventBubble = getUnusedEventBubble();
3046+ }
3047+
3048+ var eventWidth = (bubbleOverLay.width * eventInfo.width)
3049+
3050+ eventBubble.anchorDate = bubbleOverLay.day
3051+ eventBubble.minuteHeight = bubbleOverLay.minuteHeight
3052+ eventBubble.sizeOfRow = eventInfo.width
3053+ eventBubble.depthInRow = eventInfo.y
3054+ eventBubble.model = bubbleOverLay.model
3055+ eventBubble.event = intern.eventsById[eventInfo.eventId]
3056+ eventBubble.resize()
3057+ eventBubble.visible = true
3058+ eventBubble.clicked.connect( bubbleOverLay.showEventDetails );
3059+ }
3060+
3061+ function idleCreateEvents() {
3062+ createEventsTimer.restart()
3063 }
3064
3065 function createEvents() {
3066 if(!bubbleOverLay || bubbleOverLay == undefined || model === undefined || model === null) {
3067- return;
3068- }
3069-
3070+ console.debug("\tabort.")
3071+ return;
3072+ }
3073+
3074+ // check if there is any update in progress
3075+ if (intern.busy) {
3076+ console.debug("Work script still busy, postpone update")
3077+ // mark as dirsty to triggere a new update after the message arrives
3078+ intern.dirty = true
3079+ return;
3080+ }
3081+
3082+ intern.busy = true
3083+ intern.dirty = false
3084 destroyAllChildren();
3085-
3086- var eventMap = {};
3087- var allSchs = [];
3088-
3089- var startDate = new Date(day).midnight();
3090- var endDate = new Date(day).endOfDay();
3091- var items = model.getItems(startDate,endDate);
3092- for(var i = 0; i < items.length; ++i) {
3093- var event = items[i];
3094-
3095- if(event.allDay) {
3096- continue;
3097- }
3098-
3099- var schedule = {"startDateTime": event.startDateTime, "endDateTime": event.endDateTime,"id":event.itemId };
3100- allSchs.push(schedule);
3101- eventMap[event.itemId] = event;
3102- }
3103-
3104- intern.eventMap = eventMap;
3105- eventLayoutHelper.sendMessage(allSchs);
3106-
3107- if( intern.now.isSameDay( bubbleOverLay.day ) ) {
3108+ intern.eventsById = {}
3109+
3110+ var startDate = day.midnight()
3111+ var itemsOfTheDay = model.itemsByTimePeriod(startDate, startDate.endOfDay())
3112+ if (itemsOfTheDay.length === 0) {
3113 bubbleOverLay.showSeparator();
3114- }
3115+ intern.busy = false
3116+ return
3117+ }
3118+
3119+ for(var i=0; i < itemsOfTheDay.length; i++) {
3120+ var e = itemsOfTheDay[i]
3121+ intern.eventsById[e.itemId] = e
3122+ }
3123+
3124+ var eventInfo = CanlendarCanvas.parseDayEvents(startDate, itemsOfTheDay)
3125+ eventLayoutHelper.sendMessage({'events': eventInfo})
3126+ bubbleOverLay.showSeparator();
3127 }
3128
3129 function destroyAllChildren() {
3130- for( var i = children.length - 1; i >= 0; --i ) {
3131- if( children[i].objectName === "mouseArea" ||
3132- children[i].objectName === "weekdevider") {
3133+ separator.visible = false
3134+ for(var i=0; i < children.length; i++) {
3135+ var child = children[i]
3136+ if (!child.isEventBubble) {
3137 continue;
3138 }
3139- children[i].visible = false;
3140- if( children[i].objectName !== "separator") {
3141- children[i].clicked.disconnect( bubbleOverLay.showEventDetails );
3142- var key = children[i].objectName;
3143- if (intern.unUsedEvents[key] === "undefined") {
3144- intern.unUsedEvents[key] = children[i];
3145- }
3146+ if (intern.unUsedEvents.indexOf(child) === -1) {
3147+ child.event = null
3148+ child.visible = false;
3149+ child.clicked.disconnect(bubbleOverLay.showEventDetails);
3150+ intern.unUsedEvents.push(child)
3151 }
3152 }
3153 }
3154
3155- function isHashEmpty(hash) {
3156- for (var prop in hash) {
3157- if (prop)
3158- return false;
3159- }
3160- return true;
3161- }
3162-
3163- function getAKeyFromHash(hash) {
3164- for (var prop in hash) {
3165- return prop;
3166- }
3167- return "undefined";
3168- }
3169-
3170 function getUnusedEventBubble() {
3171- /* Recycle an item from unUsedEvents, and remove from hash */
3172- var key = getAKeyFromHash(intern.unUsedEvents);
3173- var unUsedBubble = intern.unUsedEvents[key];
3174- delete intern.unUsedEvents[key];
3175-
3176- return unUsedBubble;
3177- }
3178-
3179- function createEvent( event, depth, sizeOfRow ) {
3180- var eventBubble;
3181- if( isHashEmpty(intern.unUsedEvents) ) {
3182- var incubator = delegate.incubateObject(bubbleOverLay);
3183- if (incubator.status !== Component.Ready) {
3184- incubator.onStatusChanged = function(status) {
3185- if (status === Component.Ready) {
3186- incubator.object.objectName = children.length;
3187- assignBubbleProperties(incubator.object, event, depth, sizeOfRow);
3188- }
3189- }
3190- } else {
3191- incubator.object.objectName = children.length;
3192- assignBubbleProperties(incubator.object, event, depth, sizeOfRow);
3193- }
3194- } else {
3195- eventBubble = getUnusedEventBubble();
3196- assignBubbleProperties(eventBubble, event, depth, sizeOfRow);
3197- }
3198- }
3199-
3200- function assignBubbleProperties(eventBubble, event, depth, sizeOfRow) {
3201- var yPos = 0;
3202- var height = 0;
3203- var hour = 0;
3204- var durationMin = 0;
3205-
3206- // skip it in case of endDateTime == dd-MM-yyyy 12:00 AM
3207- if (event.endDateTime - day == 0)
3208- return;
3209-
3210- if (event.endDateTime.isSameDay(day) &&
3211- event.endDateTime.isSameDay(event.startDateTime)) {
3212- hour = event.startDateTime.getHours();
3213- yPos = (( event.startDateTime.getMinutes() * hourHeight) / 60) + hour * hourHeight
3214- durationMin = (event.endDateTime - event.startDateTime) / Date.msPerMin;
3215- }
3216- if (!event.startDateTime.isSameDay(day) &&
3217- event.endDateTime.isSameDay(day)) {
3218- hour = 0;
3219- yPos = 0;
3220- durationMin = event.endDateTime.getHours() * 60;
3221- durationMin += event.endDateTime.getMinutes();
3222- }
3223- if (event.startDateTime.isSameDay(day) &&
3224- !event.endDateTime.isSameDay(day)) {
3225- hour = event.startDateTime.getHours();
3226- yPos = (( event.startDateTime.getMinutes() * hourHeight) / 60) + hour * hourHeight
3227- durationMin = (24 - event.startDateTime.getHours()) * 60;
3228- }
3229- if (!event.startDateTime.isSameDay(day) &&
3230- !event.endDateTime.isSameDay(day)) {
3231- hour = 0;
3232- yPos = 0;
3233- durationMin = 24 * 60;
3234- }
3235-
3236- eventBubble.y = yPos;
3237- height = (durationMin * hourHeight )/ 60;
3238- eventBubble.height = (height > eventBubble.minimumHeight) ? height:eventBubble.minimumHeight ;
3239-
3240- eventBubble.model = bubbleOverLay.model
3241- eventBubble.depthInRow = depth;
3242- eventBubble.sizeOfRow = sizeOfRow;
3243- eventBubble.event = event
3244- eventBubble.visible = true;
3245- eventBubble.clicked.connect( bubbleOverLay.showEventDetails );
3246+ var unusedEvent = null
3247+ if (intern.unUsedEvents.length > 0) {
3248+ unusedEvent = intern.unUsedEvents[0]
3249+ intern.unUsedEvents.splice(0, 1);
3250+ }
3251+ return unusedEvent
3252 }
3253
3254 function showSeparator() {
3255- var y = ((intern.now.getMinutes() * hourHeight) / 60) + intern.now.getHours() * hourHeight;
3256- separator.y = y;
3257- separator.visible = true;
3258+ intern.now = new Date();
3259+ if (intern.now.isSameDay(bubbleOverLay.day) ) {
3260+ var y = ((intern.now.getMinutes() * hourHeight) / 60) + intern.now.getHours() * hourHeight;
3261+ separator.y = y;
3262+ separator.visible = true;
3263+ } else {
3264+ separator.visible = false;
3265+ }
3266+ }
3267+
3268+ onDayChanged: bubbleOverLay.idleCreateEvents();
3269+ Component.onCompleted: bubbleOverLay.idleCreateEvents();
3270+ enabled: !intern.busy && !intern.waitingForModelChange
3271+
3272+ EventBubble {
3273+ id: temporaryEvent
3274+
3275+ isEventBubble: false
3276+ Drag.active: overlayMouseArea.drag.active
3277+ isLiveEditing: overlayMouseArea.creatingEvent
3278+ visible: overlayMouseArea.creatingEvent
3279+ sizeOfRow: 1.0
3280+ z: 100
3281+ onVisibleChanged: {
3282+ if (visible)
3283+ y = event ? CanlendarCanvas.minutesSince(bubbleOverLay.day, event.startDateTime) * bubbleOverLay.minuteHeight : 0
3284+ }
3285+ }
3286+
3287+ Item {
3288+ anchors {
3289+ topMargin: flickable ? flickable.contentY : 0
3290+ bottomMargin: flickable ? bubbleOverLay.height - flickable.contentY - flickable.height : bubbleOverLay.height
3291+ fill: parent
3292+ }
3293+
3294+ ActivityIndicator {
3295+ visible: intern.busy || intern.waitingForModelChange
3296+ running: visible
3297+ anchors.centerIn: parent
3298+ }
3299+
3300+ z: 100
3301+ }
3302+
3303+ MouseArea {
3304+ id: overlayMouseArea
3305+
3306+ property bool creatingEvent: false
3307+
3308+ anchors.fill: parent
3309+ objectName: "mouseArea"
3310+ drag {
3311+ target: creatingEvent ? temporaryEvent : null
3312+ axis: Drag.YAxis
3313+ minimumY: 0
3314+ maximumY: height - temporaryEvent.height
3315+ }
3316+
3317+
3318+ Binding {
3319+ target: temporaryEvent
3320+ property: "visible"
3321+ value: overlayMouseArea.creatingEvent
3322+ }
3323+
3324+ onPressAndHold: {
3325+ var selectedDate = new Date(day);
3326+ var pointY = mouse.y - (hourHeight / 2);
3327+ selectedDate.setHours(Math.floor(pointY / hourHeight))
3328+ selectedDate.setMinutes(Math.min(pointY % hourHeight, 60))
3329+ var event = createOrganizerEvent(selectedDate)
3330+
3331+ Haptics.play()
3332+
3333+ temporaryEvent.anchorDate = bubbleOverLay.day
3334+ temporaryEvent.minuteHeight = bubbleOverLay.minuteHeight
3335+ temporaryEvent.depthInRow = 0
3336+ temporaryEvent.model = bubbleOverLay.model
3337+ temporaryEvent.event = event
3338+ temporaryEvent.resize()
3339+ creatingEvent = true
3340+ }
3341+
3342+ onReleased: {
3343+ if (creatingEvent) {
3344+ bubbleOverLay.pressAndHoldAt(temporaryEvent.event.startDateTime)
3345+ creatingEvent = false
3346+ }
3347+ }
3348+
3349+ onCanceled: {
3350+ if (creatingEvent) {
3351+ creatingEvent = false
3352+ }
3353+ }
3354+ }
3355+
3356+ TimeSeparator {
3357+ id: separator
3358+ objectName: "separator"
3359+ width: bubbleOverLay.width
3360+ visible: false
3361+ // make sure that the object is aways visible
3362+ z: 1000
3363+ }
3364+
3365+ Timer {
3366+ id: separtorUpdateTimer
3367+ interval: 300000 // every 5 minutes
3368+ running: true
3369+ repeat: true
3370+ onTriggered: showSeparator()
3371+ }
3372+
3373+ QtObject {
3374+ id: intern
3375+
3376+ property var now : new Date();
3377+ property var eventsById: ({})
3378+ property var unUsedEvents: []
3379+ property bool busy: false
3380+ property bool dirty: false
3381+ property bool waitingForModelChange: false
3382+ }
3383+
3384+ Timer {
3385+ id: createEventsTimer
3386+
3387+ interval: 300
3388+ running: false
3389+ repeat: false
3390+ onTriggered: createEvents()
3391+ }
3392+
3393+ Connections {
3394+ id: modelConnections
3395+ onModelChanged: {
3396+ intern.dirty = true
3397+ intern.waitingForModelChange = false
3398+ bubbleOverLay.idleCreateEvents()
3399+ }
3400 }
3401 }
3402
3403=== modified file 'TimeLineBaseComponent.qml'
3404--- TimeLineBaseComponent.qml 2016-01-29 14:35:14 +0000
3405+++ TimeLineBaseComponent.qml 2016-03-07 15:59:03 +0000
3406@@ -29,58 +29,106 @@
3407
3408 property var keyboardEventProvider;
3409
3410+ property bool isCurrentItem: false
3411+ property bool isActive: false
3412+
3413 property date startDay: DateExt.today();
3414 property int weekNumber: startDay.weekNumber(Qt.locale().firstDayOfWeek);
3415- property bool isActive: false
3416 property alias contentY: timeLineView.contentY
3417 property alias contentInteractive: timeLineView.interactive
3418+ property var modelFilter: invalidFilter
3419 property var selectedDay;
3420
3421+ readonly property real hourItemHeight: units.gu(8)
3422+ readonly property int currentHour: timeLineView.contentY > hourItemHeight ?
3423+ Math.round(timeLineView.contentY / hourItemHeight) : 1
3424+ readonly property int currentDayOfWeek: timeLineView.contentX > timeLineView.delegateWidth ?
3425+ Math.floor(timeLineView.contentX / timeLineView.delegateWidth) : 0
3426 property int type: ViewType.ViewTypeWeek
3427
3428 //visible hour
3429 property int scrollHour;
3430
3431- property EventListModel mainModel;
3432-
3433 signal dateSelected(var date);
3434 signal dateHighlighted(var date);
3435-
3436- function scrollToCurrentTime() {
3437- var currentTime = new Date();
3438- scrollHour = currentTime.getHours();
3439-
3440- timeLineView.contentY = scrollHour * units.gu(8);
3441- if(timeLineView.contentY >= timeLineView.contentHeight - timeLineView.height) {
3442- timeLineView.contentY = timeLineView.contentHeight - timeLineView.height
3443- }
3444- }
3445-
3446- function scrollTocurrentDate() {
3447- if ( type != ViewType.ViewTypeWeek ){
3448+ signal pressAndHoldAt(var date, bool allDay);
3449+
3450+ function timeIsVisible(date) {
3451+
3452+ var hour = date.getHours();
3453+ var currentTimeY = (hour * hourItemHeight)
3454+ return ((currentTimeY >= timeLineView.contentY) &&
3455+ (currentTimeY <= (timeLineView.contentY + timeLineView.height)));
3456+ }
3457+
3458+ function dateIsVisible(date) {
3459+ if (date.getFullYear() !== startDay.getFullYear()) {
3460+ return false;
3461+ }
3462+
3463+ if (type != ViewType.ViewTypeWeek) {
3464+ return ((date.getMonth() === startDay.getMonth) &&
3465+ (date.getDate() === startDay.getDate()))
3466+ }
3467+
3468+ var dateDayOfWeekX = date.getDay() * timeLineView.delegateWidth
3469+ return ((dateDayOfWeekX >= timeLineView.contentX) &&
3470+ (dateDayOfWeekX <= (timeLineView.contentX + timeLineView.width)))
3471+ }
3472+
3473+ function dateTimeIsVisible(date) {
3474+ return dateIsVisible(date) && timeIsVisible(date);
3475+ }
3476+
3477+ function scrollToTime(date) {
3478+ scrollHour = date.getHours();
3479+
3480+ var currentTimeY = (scrollHour * hourItemHeight)
3481+ var margin = (timeLineView.height / 2.0) - units.gu(5)
3482+ currentTimeY = currentTimeY - margin
3483+ timeLineView.contentY = Math.min(timeLineView.contentHeight - timeLineView.height, currentTimeY > 0 ? currentTimeY : 0)
3484+ timeLineView.returnToBounds()
3485+ }
3486+
3487+ function scrollToDate(date) {
3488+ if (type != ViewType.ViewTypeWeek) {
3489 return;
3490 }
3491
3492- var today = DateExt.today();
3493- var startOfWeek = today.weekStart(Qt.locale().firstDayOfWeek);
3494- var weekDay = today.getDay();
3495- var diff = weekDay - Qt.locale().firstDayOfWeek
3496- diff = diff < 0 ? 6 : diff
3497-
3498- if( startOfWeek.isSameDay(startDay) && diff > 2) {
3499- timeLineView.contentX = (diff * timeLineView.delegateWidth);
3500- if( timeLineView.contentX > (timeLineView.contentWidth - timeLineView.width) ) {
3501- timeLineView.contentX = timeLineView.contentWidth - timeLineView.width
3502- }
3503+ var todayWeekNumber = date.weekNumber(Qt.locale().firstDayOfWeek);
3504+
3505+ if (todayWeekNumber === root.weekNumber) {
3506+ var startOfWeek = date.weekStart(Qt.locale().firstDayOfWeek);
3507+ var weekDay = date.getDay();
3508+ var diff = weekDay - Qt.locale().firstDayOfWeek
3509+ diff = diff < 0 ? 0 : diff
3510+
3511+ var currentDayY = timeLineView.delegateWidth * diff
3512+ var margin = (timeLineView.width - timeLineView.delegateWidth) / 2
3513+ currentDayY = currentDayY - margin
3514+ timeLineView.contentX = Math.min(timeLineView.contentWidth - timeLineView.width, currentDayY > 0 ? currentDayY : 0)
3515 } else {
3516- //need to check swipe direction
3517- //and change startion position as per direction
3518- if(weekViewPath.swipeDirection() === -1) {
3519- timeLineView.contentX = timeLineView.contentWidth - timeLineView.width
3520- } else {
3521- timeLineView.contentX = 0;
3522- }
3523+ timeLineView.contentX = 0
3524 }
3525+
3526+ timeLineView.returnToBounds()
3527+ }
3528+
3529+ function scrollToDateAndTime(date) {
3530+ scrollToTime(date)
3531+ scrollToDate(date)
3532+ }
3533+
3534+ function scrollToEnd()
3535+ {
3536+ timeLineView.contentX = timeLineView.contentWidth - timeLineView.width
3537+ timeLineView.returnToBounds()
3538+ }
3539+
3540+ function scrollToBegin()
3541+ {
3542+ timeLineView.contentX = 0
3543+ timeLineView.returnToBounds()
3544 }
3545
3546 Connections{
3547@@ -110,29 +158,51 @@
3548 }
3549 }
3550
3551- Timer{
3552- interval: 200; running: true; repeat: false
3553- onTriggered: {
3554- mainModel = modelComponent.createObject();
3555- activityLoader.running = Qt.binding( function (){ return mainModel.isLoading;});
3556- }
3557- }
3558-
3559- Component {
3560- id: modelComponent
3561- EventListModel {
3562- id: mainModel
3563- startPeriod: startDay.midnight();
3564- endPeriod: type == ViewType.ViewTypeWeek ? startPeriod.addDays(7).endOfDay(): startPeriod.endOfDay()
3565- filter: eventModel.filter
3566- }
3567- }
3568+ onIsActiveChanged: {
3569+ if (isActive && (mainModel.filter === invalidFilter)) {
3570+ idleRefresh.reset()
3571+ }
3572+ }
3573+
3574+ Timer {
3575+ id: idleRefresh
3576+
3577+ function reset()
3578+ {
3579+ mainModel.filter = invalidFilter
3580+ restart()
3581+ }
3582+
3583+ interval: root.isCurrentItem ? 500 : 1000
3584+ repeat: false
3585+ onTriggered: {
3586+ mainModel.filter = Qt.binding(function() { return root.modelFilter} )
3587+ }
3588+ }
3589+
3590+ InvalidFilter {
3591+ id: invalidFilter
3592+ }
3593+
3594+ EventListModel {
3595+ id: mainModel
3596+
3597+ manager:"eds"
3598+ startPeriod: startDay.midnight();
3599+ endPeriod: type == ViewType.ViewTypeWeek ? startPeriod.addDays(7).endOfDay(): startPeriod.endOfDay()
3600+ filter: invalidFilter
3601+
3602+ onStartPeriodChanged: idleRefresh.reset()
3603+ onEndPeriodChanged: idleRefresh.reset()
3604+ }
3605
3606 ActivityIndicator {
3607 id: activityLoader
3608+ objectName : "activityIndicator"
3609+
3610 visible: running
3611- objectName : "activityIndicator"
3612 anchors.centerIn: parent
3613+ //running: mainModel.isLoading
3614 z:2
3615 }
3616
3617@@ -149,11 +219,21 @@
3618 selectedDay: root.selectedDay
3619
3620 onDateSelected: {
3621- root.dateSelected(date);
3622+ root.dateSelected(date.getFullYear(),
3623+ date.getMonth(),
3624+ date.getDate(),
3625+ root.currentHour, 0, 0)
3626 }
3627
3628 onDateHighlighted: {
3629- root.dateHighlighted(date);
3630+ root.dateHighlighted(date.getFullYear(),
3631+ date.getMonth(),
3632+ date.getDate(),
3633+ root.currentHour, 0, 0)
3634+ }
3635+
3636+ onAllDayPressAndHold: {
3637+ root.pressAndHoldAt(date, true)
3638 }
3639 }
3640
3641@@ -183,7 +263,7 @@
3642
3643 property int delegateWidth: {
3644 if( type == ViewType.ViewTypeWeek ) {
3645- width/3 - units.gu(1) /*partial visible area*/
3646+ width/3 - units.gu(1) // partial visible area
3647 } else {
3648 width
3649 }
3650@@ -198,11 +278,6 @@
3651 }
3652 }
3653
3654- onContentWidthChanged: {
3655- scrollToCurrentTime();
3656- scrollTocurrentDate();
3657- }
3658-
3659 clip: true
3660
3661 TimeLineBackground{}
3662@@ -214,7 +289,12 @@
3663 model: type == ViewType.ViewTypeWeek ? 7 : 1
3664
3665 delegate: TimeLineBase {
3666+ id: delegate
3667+
3668+ objectName: "TimeLineBase_" + root.objectName
3669+
3670 property int idx: index
3671+ flickable: timeLineView
3672 anchors.top: parent.top
3673 width: {
3674 if( type == ViewType.ViewTypeWeek ) {
3675@@ -228,12 +308,14 @@
3676 day: startDay.addDays(index)
3677 model: mainModel
3678
3679- Connections{
3680- target: mainModel
3681+ onPressAndHoldAt: {
3682+ root.pressAndHoldAt(date, false)
3683+ }
3684
3685- onModelChanged: {
3686- createEvents();
3687- }
3688+ Binding {
3689+ target: timeLineView
3690+ property: "interactive"
3691+ value: !delegate.creatingEvent
3692 }
3693
3694 DropArea {
3695@@ -251,12 +333,13 @@
3696 event.startDateTime = startDate;
3697 event.endDateTime = endDate;
3698
3699- return event;
3700+ return event;
3701 }
3702
3703 onDropped: {
3704 var event = dropArea.modifyEventForDrag(drop);
3705- model.saveItem(event);
3706+ delegate.waitForModelChange()
3707+ delegate.model.saveItem(event);
3708 }
3709
3710 onPositionChanged: {
3711@@ -299,13 +382,6 @@
3712 anchors.fill: parent
3713 }
3714 }
3715-
3716- Connections{
3717- target: mainModel
3718- onStartPeriodChanged:{
3719- destroyAllChildren();
3720- }
3721- }
3722 }
3723 }
3724 }
3725@@ -317,8 +393,16 @@
3726 id: comp
3727 EventBubble {
3728 type: root.type == ViewType.ViewTypeWeek ? narrowType : wideType
3729- flickable: root.isActive ? timeLineView : null
3730+ flickable: root.isCurrentItem ? timeLineView : null
3731 clip: true
3732+ opacity: parent.enabled ? 1.0 : 0.3
3733+
3734+ // send a signal to update application current date
3735+ onClicked: root.dateHighlighted(event.startDateTime)
3736+ onIsLiveEditingChanged: {
3737+ if (isLiveEditing)
3738+ root.dateHighlighted(event.startDateTime)
3739+ }
3740 }
3741 }
3742 }
3743
3744=== modified file 'TimeLineHeader.qml'
3745--- TimeLineHeader.qml 2016-01-29 14:35:14 +0000
3746+++ TimeLineHeader.qml 2016-03-07 15:59:03 +0000
3747@@ -33,6 +33,7 @@
3748
3749 signal dateSelected(var date);
3750 signal dateHighlighted(var date);
3751+ signal allDayPressAndHold(var date);
3752
3753 width: parent.width
3754 height: units.gu(10)
3755@@ -118,6 +119,9 @@
3756 width: parent.width
3757 height: units.gu(5)
3758
3759+ onPressAndHold: headerRoot.allDayPressAndHold(date)
3760+
3761+
3762 Connections{
3763 target: mainModel
3764 onModelChanged : {
3765@@ -184,6 +188,8 @@
3766 height: units.gu(5)
3767 model: mainModel
3768
3769+ onPressAndHold: headerRoot.allDayPressAndHold(date)
3770+
3771 Connections{
3772 target: mainModel
3773 onModelChanged : {
3774
3775=== modified file 'WeekView.qml'
3776--- WeekView.qml 2016-02-03 08:06:25 +0000
3777+++ WeekView.qml 2016-03-07 15:59:03 +0000
3778@@ -22,21 +22,31 @@
3779 import "ViewType.js" as ViewType
3780 import "./3rd-party/lunar.js" as Lunar
3781
3782-Page{
3783+PageWithBottomEdge {
3784 id: weekViewPage
3785 objectName: "weekViewPage"
3786
3787- property var dayStart: new Date();
3788- property var firstDay: dayStart.weekStart(Qt.locale().firstDayOfWeek);
3789+ property var anchorDate: new Date();
3790+ readonly property var anchorFirstDayOfWeek: anchorDate.weekStart(Qt.locale().firstDayOfWeek)
3791+ readonly property var currentDate: weekViewPath.currentItem.item.startDay
3792+ readonly property var currentFirstDayOfWeek: currentDate.weekStart(Qt.locale().firstDayOfWeek)
3793+
3794 property bool isCurrentPage: false
3795 property var selectedDay;
3796+ property var highlightedDay;
3797+ property bool displayLunarCalendar: false
3798
3799 signal dateSelected(var date);
3800- signal dateHighlighted(var date);
3801+ signal pressAndHoldAt(var date, bool allDay)
3802+
3803+ function delayScrollToDate(scrollDate, scrollTime) {
3804+ idleScroll.scrollToTime = scrollTime != undefined ? scrollTime : true
3805+ idleScroll.scrollToDate = new Date(scrollDate)
3806+ idleScroll.restart()
3807+ }
3808
3809 Keys.forwardTo: [weekViewPath]
3810-
3811- flickable: null
3812+ createEventAt: null
3813
3814 Action {
3815 id: calendarTodayAction
3816@@ -44,7 +54,60 @@
3817 iconName: "calendar-today"
3818 text: i18n.tr("Today")
3819 onTriggered: {
3820- dayStart = new Date()
3821+ var today = new Date()
3822+ delayScrollToDate(today)
3823+ anchorDate = today
3824+ }
3825+ }
3826+
3827+ onAnchorDateChanged: {
3828+ weekViewPath.scrollToBegginer()
3829+ }
3830+
3831+ onEventCreated: {
3832+ var scrollDate = new Date(event.startDateTime)
3833+ var currentWeekNumber = currentDate.weekNumber(Qt.locale().firstDayOfWeek)
3834+ var eventWeekNumber = scrollDate.weekNumber(Qt.locale().firstDayOfWeek)
3835+ var needScroll = false
3836+
3837+ if ((scrollDate.getFullYear() !== currentDate.getFullYear()) ||
3838+ (currentWeekNumber !== eventWeekNumber)) {
3839+ anchorDate = new Date(scrollDate)
3840+ needScroll = true
3841+ } else {
3842+ if (event.allDay) {
3843+ needScroll = !weekViewPath.currentItem.item.dateIsVisible(scrollDate)
3844+ } else {
3845+ needScroll = !weekViewPath.currentItem.item.timeIsVisible(scrollDate)
3846+ }
3847+ }
3848+
3849+ highlightedDay = scrollDate
3850+ if (needScroll) {
3851+ delayScrollToDate(scrollDate, !event.allDay)
3852+ }
3853+ }
3854+
3855+ Timer {
3856+ id: idleScroll
3857+
3858+ property var scrollToDate: null
3859+ property bool scrollToTime: true
3860+
3861+ interval: 200
3862+ repeat:false
3863+ onTriggered: {
3864+ if (scrollToDate) {
3865+ if (scrollToTime)
3866+ weekViewPath.currentItem.item.scrollToDateAndTime(scrollToDate);
3867+ else
3868+ weekViewPath.currentItem.item.scrollToDate(scrollToDate);
3869+ } else {
3870+ weekViewPath.currentItem.item.scrollToBegin()
3871+ }
3872+
3873+ scrollToDate = null
3874+ scrollToTime = true
3875 }
3876 }
3877
3878@@ -54,7 +117,6 @@
3879 leadingActionBar.actions: tabs.tabsAction
3880 trailingActionBar.actions: [
3881 calendarTodayAction,
3882- commonHeaderActions.newEventAction,
3883 commonHeaderActions.showCalendarAction,
3884 commonHeaderActions.reloadAction,
3885 commonHeaderActions.syncCalendarAction,
3886@@ -62,11 +124,27 @@
3887 ]
3888
3889 title: {
3890- // TRANSLATORS: this is a time formatting string,
3891- // see http://qt-project.org/doc/qt-5/qml-qtqml-date.html#details for valid expressions.
3892- // It's used in the header of the month and week views
3893- var monthName = dayStart.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy"))
3894- return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1)
3895+ if(weekViewPage.displayLunarCalendar){
3896+ var lunarDate = Lunar.calendar.solar2lunar(currentDate.getFullYear(),
3897+ currentDate.getMonth() + 1,
3898+ currentDate.getDate())
3899+ return i18n.tr("%1 %2").arg(lunarDate .IMonthCn).arg(lunarDate.gzYear)
3900+ } else {
3901+ // TRANSLATORS: this is a time formatting string,
3902+ // see http://qt-project.org/doc/qt-5/qml-qtqml-date.html#details for valid expressions.
3903+ // It's used in the header of the month and week views
3904+ var currentLastDayOfWeek = currentFirstDayOfWeek.addDays(7)
3905+ if (currentLastDayOfWeek.getMonth() !== currentFirstDayOfWeek.getMonth()) {
3906+ var firstMonthName = currentFirstDayOfWeek.toLocaleString(Qt.locale(),i18n.tr("MMM"))
3907+ var lastMonthName = currentLastDayOfWeek.toLocaleString(Qt.locale(),i18n.tr("MMM"))
3908+ return (firstMonthName[0].toUpperCase() + firstMonthName.substr(1, 2) + "/" +
3909+ lastMonthName[0].toUpperCase() + lastMonthName.substr(1, 2) + " " +
3910+ currentLastDayOfWeek.getFullYear())
3911+ } else {
3912+ var monthName = currentDate.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy"))
3913+ return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1)
3914+ }
3915+ }
3916 }
3917 flickable: null
3918 }
3919@@ -80,25 +158,13 @@
3920 topMargin: header.height
3921 }
3922
3923+ onCurrentIndexChanged: {
3924+ weekViewPage.highlightedDay = null
3925+ }
3926+
3927 //This is used to scroll all view together when currentItem scrolls
3928 property var childContentY;
3929
3930- onNextItemHighlighted: {
3931- nextWeek();
3932- }
3933-
3934- onPreviousItemHighlighted: {
3935- previousWeek();
3936- }
3937-
3938- function nextWeek() {
3939- dayStart = firstDay.addDays(7);
3940- }
3941-
3942- function previousWeek(){
3943- dayStart = firstDay.addDays(-7);
3944- }
3945-
3946 delegate: Loader {
3947 id: timelineLoader
3948 width: parent.width
3949@@ -112,39 +178,52 @@
3950 TimeLineBaseComponent {
3951 id: timeLineView
3952
3953+ startDay: anchorFirstDayOfWeek.addDays((weekViewPath.loopCurrentIndex + weekViewPath.indexType(index)) * 7)
3954+ anchors.fill: parent
3955 type: ViewType.ViewTypeWeek
3956- anchors.fill: parent
3957- isActive: parent.PathView.isCurrentItem
3958- startDay: firstDay.addDays( weekViewPath.indexType(index) * 7)
3959+ isCurrentItem: parent.PathView.isCurrentItem
3960+ isActive: !weekViewPath.moving && !weekViewPath.flicking
3961 keyboardEventProvider: weekViewPath
3962 selectedDay: weekViewPage.selectedDay
3963-
3964- onIsActiveChanged: {
3965- timeLineView.scrollTocurrentDate();
3966- }
3967+ modelFilter: weekViewPage.model ? weekViewPage.model.filter : null
3968
3969 onDateSelected: {
3970 weekViewPage.dateSelected(date);
3971 }
3972
3973 onDateHighlighted:{
3974- weekViewPage.dateHighlighted(date);
3975+ weekViewPage.highlightedDay = date
3976+ }
3977+
3978+ Component.onCompleted: {
3979+ var iType = weekViewPath.indexType(index)
3980+ if (iType === 0) {
3981+ idleScroll.restart()
3982+ } else if (iType < 0) {
3983+ scrollToEnd()
3984+ }
3985+ }
3986+
3987+ onPressAndHoldAt: {
3988+ weekViewPage.pressAndHoldAt(date, allDay)
3989 }
3990
3991 Connections{
3992 target: calendarTodayAction
3993 onTriggered:{
3994- if( isActive )
3995- timeLineView.scrollTocurrentDate();
3996- }
3997+ if (isActive)
3998+ timeLineView.scrollToDate(new Date());
3999+ }
4000 }
4001
4002- Connections{
4003- target: weekViewPage
4004- onIsCurrentPageChanged:{
4005- if(weekViewPage.isCurrentPage){
4006- timeLineView.scrollToCurrentTime();
4007- timeLineView.scrollTocurrentDate();
4008+ Connections {
4009+ target: weekViewPath
4010+ onLoopCurrentIndexChanged: {
4011+ var iType = weekViewPath.indexType(index)
4012+ if (iType < 0) {
4013+ scrollToEnd()
4014+ } else if (iType > 0) {
4015+ scrollToBegin()
4016 }
4017 }
4018 }
4019@@ -164,22 +243,13 @@
4020 value: contentY
4021 when: parent.PathView.isCurrentItem
4022 }
4023+ Binding {
4024+ target: weekViewPath
4025+ property: "interactive"
4026+ value: timeLineView.contentInteractive
4027+ }
4028 }
4029 }
4030 }
4031 }
4032-
4033- Component.onCompleted: {
4034- pageHeader.title = Qt.binding(function(){
4035- if(mainView.displayLunarCalendar){
4036- var lunarDate = Lunar.calendar.solar2lunar(dayStart.getFullYear(),
4037- dayStart.getMonth() + 1,
4038- dayStart.getDate())
4039- return i18n.tr("%1 %2").arg(lunarDate .IMonthCn).arg(lunarDate.gzYear)
4040- } else {
4041- var monthName = dayStart.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy"))
4042- return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1)
4043- }
4044- })
4045- }
4046 }
4047
4048=== modified file 'YearView.qml'
4049--- YearView.qml 2016-02-03 08:06:25 +0000
4050+++ YearView.qml 2016-03-07 15:59:03 +0000
4051@@ -18,22 +18,36 @@
4052
4053 import QtQuick 2.4
4054 import Ubuntu.Components 1.3
4055+
4056 import "dateExt.js" as DateExt
4057 import "./3rd-party/lunar.js" as Lunar
4058
4059-Page {
4060+PageWithBottomEdge {
4061 id: yearViewPage
4062 objectName: "yearViewPage"
4063
4064- property int currentYear: DateExt.today().getFullYear();
4065+ property int anchorYear: new Date().getFullYear()
4066+ property bool displayLunarCalendar: false
4067+ readonly property int currentYear: yearPathView.currentItem.item ? yearPathView.currentItem.item.year : anchorYear
4068+
4069 signal monthSelected(var date);
4070
4071+ function refreshCurrentYear(year)
4072+ {
4073+ if (currentYear !== year) {
4074+ anchorYear = year;
4075+ yearPathView.scrollToBegginer()
4076+ }
4077+ var yearViewDelegate = yearPathView.currentItem;
4078+ if (yearViewDelegate && yearViewDelegate.item) {
4079+ yearViewDelegate.item.refresh();
4080+ }
4081+ }
4082+
4083+ createEventAt: null
4084 Keys.forwardTo: [yearPathView]
4085-
4086- function refreshCurrentYear(year) {
4087- currentYear = year;
4088- var yearViewDelegate = yearPathView.currentItem.item;
4089- yearViewDelegate.refresh();
4090+ onAnchorYearChanged: {
4091+ yearPathView.scrollToBegginer()
4092 }
4093
4094 Action {
4095@@ -42,26 +56,50 @@
4096 iconName: "calendar-today"
4097 text: i18n.tr("Today")
4098 onTriggered: {
4099- currentYear = new Date().getFullYear()
4100+ var todayYear = new Date().getFullYear()
4101+ yearViewPage.refreshCurrentYear(todayYear)
4102 }
4103 }
4104
4105 header: PageHeader {
4106 id: pageHeader
4107-
4108 leadingActionBar.actions: tabs.tabsAction
4109 trailingActionBar.actions: [
4110 calendarTodayAction,
4111- commonHeaderActions.newEventAction,
4112 commonHeaderActions.showCalendarAction,
4113 commonHeaderActions.reloadAction,
4114 commonHeaderActions.syncCalendarAction,
4115 commonHeaderActions.settingsAction
4116 ]
4117- title: i18n.tr("Year %1").arg(currentYear)
4118+ title: {
4119+ if (displayLunarCalendar) {
4120+ var lunarDate = Lunar.calendar.solar2lunar(currentYear, 6, 0)
4121+ return lunarDate.gzYear +" "+ lunarDate.Animal
4122+ } else {
4123+ return i18n.tr("Year %1").arg(currentYear)
4124+ }
4125+ }
4126 flickable: null
4127 }
4128
4129+ ActivityIndicator {
4130+ property var startDate: new Date()
4131+
4132+ visible: running
4133+ running: (yearPathView.currentItem.status !== Loader.Ready)
4134+ anchors.centerIn: parent
4135+ z:2
4136+
4137+ onRunningChanged: {
4138+ if (!running) {
4139+ var current = new Date()
4140+ console.debug("Elapsed:" + (current.getTime() - startDate.getTime()))
4141+ }
4142+ }
4143+ }
4144+
4145+ flickable: null
4146+
4147 PathViewBase {
4148 id: yearPathView
4149 objectName: "yearPathView"
4150@@ -71,46 +109,24 @@
4151 topMargin: header.height
4152 }
4153
4154- onNextItemHighlighted: {
4155- currentYear = currentYear + 1;
4156- }
4157-
4158- onPreviousItemHighlighted: {
4159- currentYear = currentYear - 1;
4160- }
4161-
4162 delegate: Loader {
4163- width: parent.width
4164- height: parent.height
4165- anchors.top: parent.top
4166-
4167- asynchronous: index !== yearPathView.currentIndex
4168- sourceComponent: delegateComponent
4169-
4170- Component{
4171- id: delegateComponent
4172-
4173- YearViewDelegate{
4174- focus: index == yearPathView.currentIndex
4175-
4176- scrollMonth: 0;
4177- isCurrentItem: index == yearPathView.currentIndex
4178- year: (currentYear + yearPathView.indexType(index))
4179-
4180- anchors.fill: parent
4181+ id: delegateLoader
4182+
4183+ asynchronous: true
4184+ width: PathView.view.width
4185+ height: PathView.view.height
4186+
4187+ sourceComponent: YearViewDelegate {
4188+ visible: delegateLoader.status === Loader.Ready
4189+ anchors.fill: parent
4190+ scrollMonth: 0;
4191+ isCurrentItem: (index === yearPathView.currentIndex)
4192+ focus: isCurrentItem
4193+ year: (yearViewPage.anchorYear + yearPathView.loopCurrentIndex + yearPathView.indexType(index))
4194+ onMonthSelected: {
4195+ yearViewPage.monthSelected(date)
4196 }
4197 }
4198 }
4199 }
4200-
4201- Component.onCompleted: {
4202- pageHeader.title = Qt.binding(function(){
4203- if(mainView.displayLunarCalendar){
4204- var lunarDate = Lunar.calendar.solar2lunar(currentYear, 6, 0)
4205- return lunarDate.gzYear +" "+ lunarDate.Animal
4206- } else {
4207- return i18n.tr("Year %1").arg(currentYear)
4208- }
4209- })
4210- }
4211 }
4212
4213=== modified file 'YearViewDelegate.qml'
4214--- YearViewDelegate.qml 2016-02-03 08:06:25 +0000
4215+++ YearViewDelegate.qml 2016-03-07 15:59:03 +0000
4216@@ -1,26 +1,37 @@
4217 import QtQuick 2.4
4218 import Ubuntu.Components 1.3
4219
4220-GridView{
4221+ GridView{
4222 id: yearView
4223- clip: true
4224
4225 property int scrollMonth;
4226 property bool isCurrentItem;
4227 property int year;
4228-
4229+ readonly property var currentDate: new Date()
4230+ readonly property int currentYear: currentDate.getFullYear()
4231+ readonly property int currentMonth: currentDate.getMonth()
4232 readonly property int minCellWidth: units.gu(30)
4233+
4234+ signal monthSelected(var date);
4235+
4236+ function refresh() {
4237+ scrollMonth = 0;
4238+ if(year == currentYear) {
4239+ scrollMonth = currentMonth
4240+ }
4241+ yearView.positionViewAtIndex(scrollMonth, GridView.Beginning);
4242+ }
4243+
4244+ // Does not increase cash buffer if user is scolling
4245+ cacheBuffer: parent.PathView.view.flicking || parent.PathView.view.dragging || !isCurrentItem ? 0 : 6 * cellHeight
4246+
4247 cellWidth: Math.floor(Math.min.apply(Math, [3, 4].map(function(n)
4248 { return ((width / n >= minCellWidth) ? width / n : width / 2) })))
4249-
4250 cellHeight: cellWidth * 1.4
4251
4252+ clip: true
4253 model: 12 /* months in a year */
4254
4255- onYearChanged: {
4256- refresh();
4257- }
4258-
4259 //scroll in case content height changed
4260 onHeightChanged: {
4261 yearView.positionViewAtIndex(scrollMonth, GridView.Beginning);
4262@@ -30,71 +41,25 @@
4263 yearView.positionViewAtIndex(scrollMonth, GridView.Beginning);
4264 }
4265
4266- function refresh() {
4267- scrollMonth = 0;
4268- var today = new Date();
4269- if(year == today.getFullYear()) {
4270- scrollMonth = today.getMonth();
4271- }
4272- yearView.positionViewAtIndex(scrollMonth, GridView.Beginning);
4273- }
4274-
4275- Connections{
4276- target: yearPathView
4277- onScrollUp: {
4278- scrollMonth -= 2;
4279- if(scrollMonth < 0) {
4280- scrollMonth = 0;
4281- }
4282- yearView.positionViewAtIndex(scrollMonth, GridView.Beginning);
4283- }
4284-
4285- onScrollDown: {
4286- scrollMonth += 2;
4287- var visibleMonths = yearView.height / cellHeight;
4288- if( scrollMonth >= (11 - visibleMonths)) {
4289- scrollMonth = (11 - visibleMonths);
4290- }
4291- yearView.positionViewAtIndex(scrollMonth, GridView.Beginning);
4292- }
4293- }
4294-
4295- delegate: Loader {
4296- width: yearView.cellWidth
4297- height: yearView.cellHeight
4298-
4299- sourceComponent: delegateComponent
4300- asynchronous: !yearView.focus
4301-
4302- Component {
4303- id: delegateComponent
4304-
4305- Item {
4306- anchors.fill: parent
4307- anchors.margins: units.gu(0.5)
4308-
4309- MonthComponent {
4310- id: monthComponent
4311- objectName: "monthComponent" + index
4312- showEvents: false
4313- currentMonth: new Date(yearView.year, index, 1, 0, 0, 0, 0)
4314- displayWeekNumber: mainView.displayWeekNumber;
4315- displayLunarCalendar: false; //we disable lunar calendar display in yeaer view due to space
4316- isCurrentItem: yearView.focus
4317-
4318- isYearView: true
4319- anchors.fill: parent
4320-
4321- dayLabelFontSize:"x-small"
4322- dateLabelFontSize: "medium"
4323- monthLabelFontSize: "medium"
4324- yearLabelFontSize: "medium"
4325-
4326- onMonthSelected: {
4327- yearViewPage.monthSelected(date);
4328- }
4329- }
4330- }
4331- }
4332+ delegate: MonthComponent {
4333+ id: monthComponent
4334+ objectName: "monthComponent" + index
4335+
4336+ width: yearView.cellWidth - units.gu(1)
4337+ height: yearView.cellHeight - units.gu(1)
4338+ y: units.gu(0.5)
4339+ x: units.gu(0.5)
4340+
4341+ currentYear: yearView.year
4342+ currentMonth: index
4343+ isCurrentItem: yearView.focus
4344+ isYearView: true
4345+ dayLabelFontSize:"x-small"
4346+ dateLabelFontSize: "medium"
4347+ monthLabelFontSize: "medium"
4348+ yearLabelFontSize: "medium"
4349+ onMonthSelected: {
4350+ yearView.monthSelected(date);
4351+ }
4352 }
4353 }
4354
4355=== modified file 'calendar.qml'
4356--- calendar.qml 2016-02-26 12:57:00 +0000
4357+++ calendar.qml 2016-03-07 15:59:03 +0000
4358@@ -20,6 +20,7 @@
4359 import Ubuntu.Components.Popups 1.0
4360 import QtOrganizer 5.0
4361 import Qt.labs.settings 1.0
4362+
4363 import "dateExt.js" as DateExt
4364
4365 MainView {
4366@@ -27,6 +28,7 @@
4367
4368 property bool displayWeekNumber: false;
4369 property bool displayLunarCalendar: false;
4370+ readonly property bool syncInProgress: commonHeaderActions.syncInProgress
4371
4372 // Work-around until this branch lands:
4373 // https://code.launchpad.net/~tpeeters/ubuntu-ui-toolkit/optIn-tabsDrawer/+merge/212496
4374@@ -92,7 +94,7 @@
4375 focus: true
4376 Keys.forwardTo: [pageStack.currentPage]
4377 backgroundColor: "#ffffff"
4378- anchorToKeyboard: true
4379+ anchorToKeyboard: false
4380
4381 Connections {
4382 target: UriHandler
4383@@ -103,8 +105,9 @@
4384 if(commands[0].toLowerCase() === "eventid") {
4385 // calendar://eventid=??
4386 if( eventModel ) {
4387+ // qtorganizer:eds::<event-id>
4388 var eventId = commands[1];
4389- var prefix = "qtorganizer:eds::system-calendar/";
4390+ var prefix = "qtorganizer:eds::";
4391 if (eventId.indexOf(prefix) < 0)
4392 eventId = prefix + eventId;
4393
4394@@ -174,17 +177,37 @@
4395 EventListModel{
4396 id: eventModel
4397
4398- autoUpdate: true
4399+ property bool isReady: false
4400+
4401 startPeriod: tabs.currentDay
4402 endPeriod: tabs.currentDay
4403
4404 filter: invalidFilter
4405
4406- function delayedApplyFilter() {
4407- applyFilterTimer.restart();
4408+ onCollectionsChanged: {
4409+ if (!isReady)
4410+ return
4411+
4412+ var collectionIds = enabledColections()
4413+ var oldCollections = collectionFilter.ids
4414+ var needsUpdate = false
4415+ if (collectionIds.length != oldCollections.length) {
4416+ needsUpdate = true
4417+ } else {
4418+ for(var i=oldCollections.length - 1; i >=0 ; i--) {
4419+ if (collectionIds.indexOf(oldCollections[i]) === -1) {
4420+ needsUpdate = true
4421+ break;
4422+ }
4423+ }
4424+ }
4425+
4426+ if (needsUpdate)
4427+ collectionFilter.ids = collectionIds;
4428 }
4429
4430- function applyFilterFinal() {
4431+ function enabledColections()
4432+ {
4433 var collectionIds = [];
4434 var collections = eventModel.getCollections();
4435 for(var i=0; i < collections.length ; ++i) {
4436@@ -193,8 +216,18 @@
4437 collectionIds.push(collection.collectionId);
4438 }
4439 }
4440+ return collectionIds
4441+ }
4442+
4443+ function delayedApplyFilter() {
4444+ applyFilterTimer.restart();
4445+ }
4446+
4447+ function applyFilterFinal() {
4448+ var collectionIds = enabledColections()
4449 collectionFilter.ids = collectionIds;
4450- filter = mainFilter
4451+ filter = Qt.binding(function() { return mainFilter; })
4452+ isReady = true
4453 }
4454
4455 function showEventFromId(eventId) {
4456@@ -379,7 +412,7 @@
4457 }
4458 } // End of else if (starttime)
4459 else if (eventId !== "") {
4460- var prefix = "qtorganizer:eds::system-calendar/";
4461+ var prefix = "qtorganizer:eds::";
4462 if (eventId.indexOf(prefix) < 0)
4463 eventId = prefix + eventId;
4464
4465@@ -395,6 +428,11 @@
4466 tabs.selectedTabIndex = settings.defaultViewIndex;
4467 }
4468 tabs.isReady = true
4469+ // WORKAROUND: Due the missing feature on SDK, they can not detect if
4470+ // there is a mouse attached to device or not. And this will cause the
4471+ // bootom edge component to not work correct on desktop.
4472+ // We will consider that a mouse is always attached until it get implement on SDK.
4473+ QuickUtils.mouseAttached = true
4474 } // End of Component.onCompleted:
4475
4476 Keys.onTabPressed: {
4477@@ -530,6 +568,16 @@
4478 id: yearViewComp
4479
4480 YearView {
4481+ readonly property bool tabSelected: tabs.selectedTabIndex === yearTab.index
4482+
4483+ model: eventModel.isReady ? eventModel : null
4484+ bootomEdgeEnabled: tabSelected
4485+ displayLunarCalendar: mainView.displayLunarCalendar
4486+
4487+ onCurrentYearChanged: {
4488+ tabs.currentDay = new Date(currentYear, 1, 1)
4489+ }
4490+
4491 onMonthSelected: {
4492 var now = DateExt.today();
4493 if ((date.getMonth() === now.getMonth()) &&
4494@@ -540,9 +588,10 @@
4495 }
4496 tabs.selectedTabIndex = monthTab.index;
4497 }
4498- onActiveChanged: {
4499- if (active) {
4500- refreshCurrentYear(DateExt.today().getFullYear())
4501+
4502+ onTabSelectedChanged: {
4503+ if (tabSelected) {
4504+ refreshCurrentYear(tabs.currentDay.getFullYear())
4505 }
4506 }
4507 }
4508@@ -552,13 +601,35 @@
4509 id: monthViewComp
4510
4511 MonthView {
4512+ readonly property bool tabSelected: tabs.selectedTabIndex === monthTab.index
4513+
4514+ model: eventModel.isReady ? eventModel : null
4515+ bootomEdgeEnabled: tabSelected
4516+ displayLunarCalendar: mainView.displayLunarCalendar
4517+
4518+ onCurrentDateChanged: {
4519+ tabs.currentDay = currentDate
4520+ }
4521+
4522+ onHighlightedDateChanged: {
4523+ if (highlightedDate)
4524+ tabs.currentDay = highlightedDate
4525+ else
4526+ tabs.currentDay = currentDate
4527+ }
4528+
4529 onDateSelected: {
4530- tabs.currentDay = date;
4531+ tabs.currentDay = date
4532 tabs.selectedTabIndex = dayTab.index
4533 }
4534- onActiveChanged: {
4535- if (active)
4536- currentMonth = tabs.currentDay.midnight()
4537+
4538+ onTabSelectedChanged: {
4539+ if (tabSelected) {
4540+ anchorDate = new Date(tabs.currentDay.getFullYear(),
4541+ tabs.currentDay.getMonth(),
4542+ 1,
4543+ 0, 0, 0)
4544+ }
4545 }
4546 }
4547 }
4548@@ -567,16 +638,45 @@
4549 id: weekViewComp
4550
4551 WeekView {
4552- onDayStartChanged: {
4553- tabs.currentDay = dayStart
4554- }
4555+ readonly property bool tabSelected: tabs.selectedTab === weekTab
4556+
4557+ model: eventModel.isReady ? eventModel : null
4558+ bootomEdgeEnabled: tabSelected
4559+ displayLunarCalendar: mainView.displayLunarCalendar
4560+
4561+ onHighlightedDayChanged: {
4562+ if (highlightedDay)
4563+ tabs.currentDay = highlightedDay
4564+ else
4565+ tabs.currentDay = currentFirstDayOfWeek
4566+ }
4567+
4568+ onCurrentFirstDayOfWeekChanged: {
4569+ tabs.currentDay = currentFirstDayOfWeek
4570+ }
4571+
4572 onDateSelected: {
4573 tabs.currentDay = date;
4574 tabs.selectedTabIndex = dayTab.index
4575 }
4576- onActiveChanged: {
4577- if (active)
4578- dayStart = tabs.currentDay
4579+
4580+ onPressAndHoldAt: {
4581+ bottomEdgeCommit(date, allDay)
4582+ }
4583+
4584+ onTabSelectedChanged: {
4585+ if (tabSelected) {
4586+ // 'tabs.currntDay' can change after set 'anchorDate' to avoid that
4587+ // create a copy of the current value
4588+ var tabDate = new Date(tabs.currentDay)
4589+ if (!anchorDate ||
4590+ (tabs.currentDay.getFullYear() != anchorDate.getFullYear()) ||
4591+ (tabs.currentDay.getMonth() != anchorDate.getMonth()) ||
4592+ (tabs.currentDay.getDate() != anchorDate.getDate())) {
4593+ anchorDate = new Date(tabDate)
4594+ }
4595+ delayScrollToDate(tabDate)
4596+ }
4597 }
4598 }
4599 }
4600@@ -585,17 +685,37 @@
4601 id: dayViewComp
4602
4603 DayView {
4604- onCurrentDayChanged: {
4605- tabs.currentDay = currentDay;
4606- }
4607+ readonly property bool tabSelected: tabs.selectedTabIndex === dayTab.index
4608+
4609+ model: eventModel.isReady ? eventModel : null
4610+ bootomEdgeEnabled: tabSelected
4611+ displayLunarCalendar: mainView.displayLunarCalendar
4612
4613 onDateSelected: {
4614 tabs.currentDay = date
4615 }
4616
4617- onActiveChanged: {
4618- if (active)
4619- currentDay = tabs.currentDay;
4620+ onPressAndHoldAt: {
4621+ bottomEdgeCommit(date, allDay)
4622+ }
4623+
4624+ onCurrentDateChanged: {
4625+ tabs.currentDay = currentDate
4626+ }
4627+
4628+ onTabSelectedChanged: {
4629+ if (tabSelected) {
4630+ // 'tabs.currntDay' can change after set 'anchorDate' to avoid that
4631+ // create a copy of the current value
4632+ var tabDate = new Date(tabs.currentDay)
4633+ if (!anchorDate ||
4634+ (tabs.currentDay.getFullYear() != anchorDate.getFullYear()) ||
4635+ (tabs.currentDay.getMonth() != anchorDate.getMonth()) ||
4636+ (tabs.currentDay.getDate() != anchorDate.getDate())) {
4637+ anchorDate = new Date(tabDate)
4638+ }
4639+ delayScrollToDate(tabDate)
4640+ }
4641 }
4642 }
4643 }
4644@@ -604,6 +724,9 @@
4645 id: agendaViewComp
4646
4647 AgendaView {
4648+ model: eventModel.isReady ? eventModel : null
4649+ bootomEdgeEnabled: tabs.selectedTabIndex === agendaTab.index
4650+
4651 onDateSelected: {
4652 tabs.currentDay = date;
4653 tabs.selectedTabIndex = dayTab.index
4654
4655=== added file 'calendar_canvas.js'
4656--- calendar_canvas.js 1970-01-01 00:00:00 +0000
4657+++ calendar_canvas.js 2016-03-07 15:59:03 +0000
4658@@ -0,0 +1,52 @@
4659+.pragma library
4660+.import "dateExt.js" as DateExt
4661+
4662+function minutesSince(since, until)
4663+{
4664+ var sinceTime = new Date(since)
4665+ sinceTime.setSeconds(0)
4666+ var untilTime = new Date(until)
4667+ untilTime.setSeconds(0)
4668+
4669+ var sinceTimeInSecs = sinceTime.getTime()
4670+ var untilTimeInSecs = untilTime.getTime()
4671+
4672+ // limit to since day minutes
4673+ untilTimeInSecs = Math.min(sinceTime.endOfDay().getTime(), untilTimeInSecs)
4674+
4675+ // calculate the time in minutes of this event
4676+ var totalSecs = untilTimeInSecs - sinceTimeInSecs
4677+ if (totalSecs > 0)
4678+ return (totalSecs / 60000)
4679+
4680+ return 0
4681+}
4682+
4683+function parseDayEvents(date, itemsOfTheDay)
4684+{
4685+ var eventsInfo = []
4686+ for(var c=0; c < itemsOfTheDay.length; c++) {
4687+ var event = itemsOfTheDay[c]
4688+ if (event.allDay)
4689+ continue
4690+
4691+ var eventStartTimeInMinutes = minutesSince(date, event.startDateTime)
4692+ var eventEndTimeInMinutes = minutesSince(date, event.endDateTime)
4693+
4694+ // avoid to draw events too small
4695+ if ((eventEndTimeInMinutes - eventStartTimeInMinutes) < 20)
4696+ eventEndTimeInMinutes = eventStartTimeInMinutes + 20
4697+
4698+ eventsInfo.push({'eventId': event.itemId,
4699+ 'eventStartTime': event.startDateTime.getTime(),
4700+ 'eventEndTime': event.endDateTime.getTime(),
4701+ 'startTime': eventStartTimeInMinutes,
4702+ 'endTime': eventEndTimeInMinutes,
4703+ 'endTimeInSecs': event.endDateTime.getTime(),
4704+ 'y': -1,
4705+ 'intersectionCount': 0,
4706+ 'width': 1.0})
4707+ }
4708+
4709+ return eventsInfo
4710+}
4711
4712=== added file 'calendar_canvas_worker.js'
4713--- calendar_canvas_worker.js 1970-01-01 00:00:00 +0000
4714+++ calendar_canvas_worker.js 2016-03-07 15:59:03 +0000
4715@@ -0,0 +1,111 @@
4716+WorkerScript.onMessage = function(message) {
4717+ var processedEvents = dayEventsMap(message.events)
4718+ WorkerScript.sendMessage({'reply': processedEvents})
4719+}
4720+
4721+function sortByStartAndSize(eventA, eventB)
4722+{
4723+ var sort = sortEventsByStart(eventA, eventB)
4724+ if (sort === 0)
4725+ sort = sortEventsBySize(eventA, eventB)
4726+ return sort
4727+}
4728+
4729+function sortEventsByStart(eventA, eventB)
4730+{
4731+ if (eventA.startTime < eventB.startTime)
4732+ return -1
4733+ else if (eventA.startTime > eventB.startTime)
4734+ return 1
4735+ else
4736+ return 0
4737+}
4738+
4739+function sortEventsBySize(eventA, eventB)
4740+{
4741+ var eventAEndTime = eventA.endTimeInSecs
4742+ var eventBEndTime = eventB.endTimeInSecs
4743+ if (eventAEndTime > eventBEndTime)
4744+ return -1
4745+ else if (eventAEndTime < eventBEndTime)
4746+ return 1
4747+ return 0
4748+}
4749+
4750+function yArrayCount(yArray, time)
4751+{
4752+ var maxY = yArray.length - 1
4753+ for(var i=yArray.length - 1; i >= 0; i--) {
4754+ if (!yArray[i] || (yArray[i].endTime <= time))
4755+ maxY = i
4756+ }
4757+}
4758+
4759+/*
4760+ * Find item 'y' position on the intersection list
4761+ */
4762+function findOptimalY(intersections)
4763+{
4764+ if (intersections.length === 0)
4765+ return
4766+
4767+ var eventWidth = 1.0 / intersections.length
4768+ var yArray = new Array(intersections.length)
4769+ var maxY = 0
4770+
4771+ for (var i = 0; i < intersections.length; i++) {
4772+ var intersection = intersections[i]
4773+ for (var y=0; y < yArray.length; y++) {
4774+ if (!yArray[y] || (yArray[y].endTime <= intersection.startTime)) {
4775+ if (y > maxY)
4776+ maxY = y
4777+ intersection.y = y
4778+ intersection.intersectionCount = intersections.length
4779+ yArray[y] = intersection
4780+ break
4781+ }
4782+ }
4783+ }
4784+
4785+ for (var i = 0; i < intersections.length; i++) {
4786+ intersections[i].width = (1.0 / (maxY + 1))
4787+ }
4788+
4789+}
4790+
4791+function dayEventsMap(eventsInfo)
4792+{
4793+ eventsInfo.sort(sortByStartAndSize)
4794+
4795+ var events = eventsInfo.slice()
4796+ var lines = []
4797+
4798+ while (events.length > 0) {
4799+ var aux = {"startTime": 0, "ednTime": 0}
4800+ var eventA = events[0]
4801+ events.splice(0, 1)
4802+ var line = [eventA]
4803+
4804+ aux.starTime = eventA.startTime
4805+ aux.endTime = eventA.endTime
4806+
4807+ var newList = []
4808+ for(var i = 0; i < events.length; i++) {
4809+ var eventB = events[i]
4810+ if ((aux.startTime < eventB.endTime) &&
4811+ (eventB.startTime < aux.endTime)) {
4812+ if (aux.endTime < eventB.endTime)
4813+ aux.endTime = eventB.endTime
4814+ line.push(eventB)
4815+ } else {
4816+ newList.push(eventB)
4817+ }
4818+ }
4819+
4820+ findOptimalY(line)
4821+ lines.push(line)
4822+ events = newList
4823+ }
4824+
4825+ return eventsInfo
4826+}
4827
4828=== modified file 'click/calendar-helper-apparmor.json'
4829--- click/calendar-helper-apparmor.json 2015-12-16 10:51:01 +0000
4830+++ click/calendar-helper-apparmor.json 2016-03-07 15:59:03 +0000
4831@@ -3,5 +3,5 @@
4832 "policy_groups": [
4833 "push-notification-client"
4834 ],
4835- "policy_version": 1.2
4836+ "policy_version": 1.3
4837 }
4838
4839=== modified file 'click/calendar.apparmor'
4840--- click/calendar.apparmor 2015-12-16 10:51:01 +0000
4841+++ click/calendar.apparmor 2016-03-07 15:59:03 +0000
4842@@ -6,5 +6,5 @@
4843 "accounts",
4844 "push-notification-client"
4845 ],
4846- "policy_version": 1.2
4847+ "policy_version": 1.3
4848 }
4849
4850=== modified file 'click/manifest.json.in'
4851--- click/manifest.json.in 2015-12-16 10:51:01 +0000
4852+++ click/manifest.json.in 2016-03-07 15:59:03 +0000
4853@@ -1,7 +1,7 @@
4854 {
4855 "architecture": "all",
4856 "description": "A calendar for Ubuntu which syncs with online accounts",
4857- "framework": "ubuntu-sdk-14.10-qml",
4858+ "framework": "ubuntu-sdk-15.04.4",
4859 "hooks": {
4860 "calendar": {
4861 "account-application": "@PROJECT_NAME@_@APP_NAME@.application",
4862
4863=== modified file 'dateExt.js'
4864--- dateExt.js 2015-11-27 01:34:46 +0000
4865+++ dateExt.js 2016-03-07 15:59:03 +0000
4866@@ -60,11 +60,23 @@
4867 }
4868
4869 Date.prototype.addDays = function(days) {
4870+ if (days === 0)
4871+ return this
4872+
4873 var date = new Date(this)
4874 date.setDate(date.getDate() + days);
4875 return date
4876 }
4877
4878+Date.prototype.addMinutes = function(minutes) {
4879+ if (minutes === 0)
4880+ return this
4881+
4882+ var date = new Date(this)
4883+ date.setMinutes(date.getMinutes() + minutes);
4884+ return date
4885+}
4886+
4887 Date.prototype.addMonths = function(months) {
4888 var date = new Date(this)
4889 date.setMonth(date.getMonth() + months)
4890@@ -87,8 +99,9 @@
4891 }
4892
4893 Date.prototype.weekNumber = function(weekStartDay) {
4894- var date = this.weekStart(weekStartDay).addDays(3) // Thursday midnight
4895- var onejan = new Date(this.getFullYear(), 0, 3);
4896+ var date = new Date(this)
4897+ date = date.weekStart(weekStartDay).addDays(3) // Thursday midnight
4898+ var onejan = new Date(date.getFullYear(), 0, 3);
4899 return Math.ceil((((date - onejan) / 86400000) + onejan.getDay()+1)/7);
4900 }
4901
4902
4903=== modified file 'po/com.ubuntu.calendar.pot'
4904--- po/com.ubuntu.calendar.pot 2016-03-01 11:07:01 +0000
4905+++ po/com.ubuntu.calendar.pot 2016-03-07 15:59:03 +0000
4906@@ -8,7 +8,7 @@
4907 msgstr ""
4908 "Project-Id-Version: \n"
4909 "Report-Msgid-Bugs-To: \n"
4910-"POT-Creation-Date: 2016-03-01 16:35+0530\n"
4911+"POT-Creation-Date: 2016-03-02 13:04-0300\n"
4912 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
4913 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
4914 "Language-Team: LANGUAGE <LL@li.org>\n"
4915@@ -18,221 +18,277 @@
4916 "Content-Transfer-Encoding: 8bit\n"
4917 "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
4918
4919-#: ../AgendaView.qml:52 ../DayView.qml:41 ../MonthView.qml:40
4920-#: ../WeekView.qml:45 ../YearView.qml:43
4921+#: ../AgendaView.qml:53 ../DayView.qml:68 ../MonthView.qml:48
4922+#: ../WeekView.qml:52 ../YearView.qml:55 ../build/install/AgendaView.qml:53
4923+#: ../build/install/DayView.qml:43 ../build/install/MonthView.qml:47
4924+#: ../build/install/WeekView.qml:45 ../build/install/YearView.qml:49
4925+#: ../build/pkg/AgendaView.qml:53 ../build/pkg/DayView.qml:68
4926+#: ../build/pkg/MonthView.qml:48 ../build/pkg/WeekView.qml:52
4927+#: ../build/pkg/YearView.qml:55
4928 msgid "Today"
4929 msgstr ""
4930
4931-#: ../AgendaView.qml:62 ../calendar.qml:288 ../calendar.qml:509
4932+#: ../AgendaView.qml:63 ../build/install/AgendaView.qml:63
4933+#: ../build/install/calendar.qml:287 ../build/install/calendar.qml:503
4934+#: ../build/pkg/AgendaView.qml:63 ../build/pkg/calendar.qml:288
4935+#: ../build/pkg/calendar.qml:504 ../calendar.qml:288 ../calendar.qml:504
4936 msgid "Agenda"
4937 msgstr ""
4938
4939-#: ../AgendaView.qml:101
4940+#: ../AgendaView.qml:101 ../build/install/AgendaView.qml:101
4941+#: ../build/pkg/AgendaView.qml:101
4942 msgid "No upcoming events"
4943 msgstr ""
4944
4945-#: ../AgendaView.qml:104
4946+#: ../AgendaView.qml:104 ../build/install/AgendaView.qml:104
4947+#: ../build/pkg/AgendaView.qml:104
4948 msgid "You have no calendars enabled"
4949 msgstr ""
4950
4951-#: ../AgendaView.qml:114
4952+#: ../AgendaView.qml:114 ../build/install/AgendaView.qml:114
4953+#: ../build/pkg/AgendaView.qml:114
4954 msgid "Enable calendars"
4955 msgstr ""
4956
4957 #. TRANSLATORS: the first argument (%1) refers to a start time for an event,
4958 #. while the second one (%2) refers to the end time
4959-#: ../AgendaView.qml:177 ../EventBubble.qml:133
4960+#: ../AgendaView.qml:177 ../EventBubble.qml:97
4961+#: ../build/install/AgendaView.qml:177 ../build/install/EventBubble.qml:134
4962+#: ../build/pkg/AgendaView.qml:177 ../build/pkg/EventBubble.qml:97
4963 #, qt-format
4964 msgid "%1 - %2"
4965 msgstr ""
4966
4967-#: ../AgendaView.qml:183
4968-#, qt-format
4969-msgid "%1 %2 %3 %4 %5"
4970+#: ../AllDayEventComponent.qml:86 ../TimeLineBase.qml:50
4971+#: ../build/install/AllDayEventComponent.qml:108
4972+#: ../build/install/TimeLineBase.qml:131
4973+#: ../build/pkg/AllDayEventComponent.qml:86 ../build/pkg/TimeLineBase.qml:50
4974+msgid "New event"
4975 msgstr ""
4976
4977 #. TRANSLATORS: the first parameter refers to the number of all-day events
4978 #. on a given day. "Ev." is short form for "Events".
4979 #. Please keep the translation of "Ev." to 3 characters only, as the week view
4980 #. where it's shown has limited space
4981-#: ../AllDayEventComponent.qml:123
4982+#: ../AllDayEventComponent.qml:158
4983+#: ../build/install/AllDayEventComponent.qml:156
4984+#: ../build/pkg/AllDayEventComponent.qml:158
4985 #, qt-format
4986 msgid "%1 ev."
4987 msgstr ""
4988
4989 #. TRANSLATORS: the argument refers to the number of all day events
4990-#: ../AllDayEventComponent.qml:127
4991+#: ../AllDayEventComponent.qml:162
4992+#: ../build/install/AllDayEventComponent.qml:160
4993+#: ../build/pkg/AllDayEventComponent.qml:162
4994 #, qt-format
4995 msgid "%1 all day event"
4996 msgid_plural "%1 all day events"
4997 msgstr[0] ""
4998 msgstr[1] ""
4999
5000-#: ../CalendarChoicePopup.qml:34 ../EventActions.qml:63
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to status/vote changes: