[gnome-shell/datetime] Calendar updates



commit a1440bdec721227994dbf89f9bea996e74e40ae0
Author: David Zeuthen <davidz redhat com>
Date:   Tue Jan 25 09:55:41 2011 -0500

    Calendar updates
    
     - Reshuffle items to make it look more like the mockup
     - Increase vertical spacing between event section ("Today") headings
       and the event items (see mockup)
     - Don't show the year in month-switcher and event list unless it's
       a different year
     - Include the day in the date heading
     - Sort events in event list according to start time
     - Respect 12h/24h setting in event list
     - Support "All Day" events in CalendarTask abstraction
     - Show "Nothing Scheduled" if there are no events
     - Add a "Open Calendar" button
     - Refactor some of the code
    
    Signed-off-by: David Zeuthen <davidz redhat com>

 data/theme/gnome-shell.css |   28 ++-----
 js/ui/calendar.js          |  169 ++++++++++++++++++++++++++++++--------------
 js/ui/dateMenu.js          |   49 ++++++++++---
 3 files changed, 162 insertions(+), 84 deletions(-)
---
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index 4696262..5572a20 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -677,7 +677,7 @@ StTooltip StLabel {
 .calendar-vertical-separator {
     -stipple-width: 1px;
     -stipple-color: #505050;
-    width: 1em;
+    width: 1.5em;
 }
 
 #calendarPopup {
@@ -691,15 +691,8 @@ StTooltip StLabel {
     padding: 10px;
 }
 
-#calendarHBox {
-    padding: 10px 20px;
-}
-
-#calendarVBox1 {
-    spacing: 20px;
-}
-
 .calendar {
+    padding: .4em 1.75em;
     spacing-rows: 0px;
     spacing-columns: 0px;
 }
@@ -749,22 +742,24 @@ StTooltip StLabel {
 }
 
 .datemenu-date-label {
+    padding: .4em 1.75em;
     font-size: 16px;
     color: #ffffff;
 }
 
 .calendar-day-base {
     font-size: 10px;
-    padding: 5px 3px;
     text-align: center;
+    width: 24px;
+    height: 24px;
 }
 
 .calendar-day-base:hover {
-    background: #999999;
+    background: #777777;
 }
 
 .calendar-day-base:active {
-    background: #666666;
+    background: #555555;
 }
 
 .calendar-day-heading {
@@ -820,6 +815,7 @@ StTooltip StLabel {
 .events-day-header {
     font-size: 14px;
     color: rgba(153, 153, 153, 1.0);
+    padding: 0.3em;
 }
 
 .events-day-dayname {
@@ -852,14 +848,6 @@ StTooltip StLabel {
     width: 200px;
 }
 
-.events-no-events {
-    font-weight: bold;
-    padding-left: 40px;
-    padding-right: 40px;
-    font-size: 14px;
-    color: rgba(153, 153, 153, 1.0);
-}
-
 .open-calendar {
     padding-bottom: 12px;
     padding-left: 12px;
diff --git a/js/ui/calendar.js b/js/ui/calendar.js
index aea20de..2e89479 100644
--- a/js/ui/calendar.js
+++ b/js/ui/calendar.js
@@ -15,12 +15,19 @@ const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
 const WEEKDATE_HEADER_WIDTH_DIGITS = 3;
 const SHOW_WEEKDATE_KEY = 'show-weekdate';
 
+// in org.gnome.desktop.interface
+const CLOCK_FORMAT_KEY        = 'clock-format';
+
 function _sameDay(dateA, dateB) {
     return (dateA.getDate() == dateB.getDate() &&
             dateA.getMonth() == dateB.getMonth() &&
             dateA.getYear() == dateB.getYear());
 }
 
+function _sameYear(dateA, dateB) {
+    return (dateA.getYear() == dateB.getYear());
+}
+
 /* TODO: maybe needs config - right now we assume that Saturday and
  * Sunday are non-work days (not true in e.g. Israel, it's Sunday and
  * Monday there)
@@ -29,6 +36,43 @@ function _isWorkDay(date) {
     return date.getDay() != 0 && date.getDay() != 6;
 }
 
+function _getBeginningOfDay(date) {
+    let ret = new Date(date.getTime());
+    ret.setHours(0);
+    ret.setMinutes(0);
+    ret.setMilliseconds(0);
+    return ret;
+}
+
+function _getEndOfDay(date) {
+    let ret = new Date(date.getTime());
+    ret.setHours(23);
+    ret.setMinutes(59);
+    ret.setMilliseconds(999);
+    return ret;
+}
+
+function _formatTaskTime(task, clockFormat) {
+    let ret;
+    if (task.allDay) {
+        /* Translators: Shown in calendar event list for all day events */
+        ret = _("All Day");
+    } else {
+        switch (clockFormat) {
+        case '24h':
+            ret = task.date.toLocaleFormat('%H:%M');
+            break;
+
+        default:
+            /* explicit fall-through */
+        case '12h':
+            ret = task.date.toLocaleFormat('%l:%M %p');
+            break;
+        }
+    }
+    return ret;
+}
+
 function _getCalendarWeekForDate(date) {
     // Based on the algorithms found here:
     // http://en.wikipedia.org/wiki/Talk:ISO_week_date
@@ -106,14 +150,15 @@ function _getEventDayAbbreviation(dayNumber) {
 
 // Abstraction for an appointment/task in a calendar
 
-function CalendarTask(date, summary) {
-    this._init(date, summary);
+function CalendarTask(date, summary, allDay) {
+    this._init(date, summary, allDay);
 }
 
 CalendarTask.prototype = {
-    _init: function(date, summary) {
+    _init: function(date, summary, allDay) {
         this.date = date;
         this.summary = summary;
+        this.allDay = allDay;
     }
 };
 
@@ -153,52 +198,56 @@ FakeEventSource.prototype = {
 
         // Generate fake events
         //
-        let now = new Date();
+        let midnightToday = _getBeginningOfDay(new Date());
         let summary = '';
-        now.setHours(0);
-        now.setMinutes(0);
-        now.setSeconds(0);
 
         // '10-oclock pow-wow' is an event occuring IN THE PAST every four days at 10am
-        for(let n = 0; n < 10; n++) {
-            let t = new Date(now.getTime() - n * 4 * 86400 * 1000);
+        for (let n = 0; n < 10; n++) {
+            let t = new Date(midnightToday.getTime() - n * 4 * 86400 * 1000);
             t.setHours(10);
             summary = '10-oclock pow-wow (n=' + n + ')';
-            this._fakeTasks.push(new CalendarTask(t, summary));
+            this._fakeTasks.push(new CalendarTask(t, summary, false));
         }
 
         // '11-oclock thing' is an event occuring every three days at 11am
-        for(let n = 0; n < 10; n++) {
-            let t = new Date(now.getTime() + n * 3 * 86400 * 1000);
+        for (let n = 0; n < 10; n++) {
+            let t = new Date(midnightToday.getTime() + n * 3 * 86400 * 1000);
             t.setHours(11);
             summary = '11-oclock thing (n=' + n + ')';
-            this._fakeTasks.push(new CalendarTask(t, summary));
+            this._fakeTasks.push(new CalendarTask(t, summary, false));
         }
 
         // 'Weekly Meeting' is an event occuring every seven days at 1:45pm (two days displaced)
-        for(let n = 0; n < 5; n++) {
-            let t = new Date(now.getTime() + (n * 7 + 2) * 86400 * 1000);
+        for (let n = 0; n < 5; n++) {
+            let t = new Date(midnightToday.getTime() + (n * 7 + 2) * 86400 * 1000);
             t.setHours(13);
             t.setMinutes(45);
             summary = 'Weekly Meeting (n=' + n + ')';
-            this._fakeTasks.push(new CalendarTask(t, summary));
+            this._fakeTasks.push(new CalendarTask(t, summary, false));
+        }
+
+        // 'Fun All Day' is an all-day event occuring every fortnight (three days displayed)
+        for (let n = 0; n < 10; n++) {
+            let t = new Date(midnightToday.getTime() + (n * 14 + 3) * 86400 * 1000);
+            summary = 'Fun All Day (n=' + n + ')';
+            this._fakeTasks.push(new CalendarTask(t, summary, true));
         }
 
         // 'Get Married' is an event that actually reflects reality (Dec 4, 2010) :-)
-        this._fakeTasks.push(new CalendarTask(new Date(2010, 11, 4, 16, 0), 'Get Married'));
+        this._fakeTasks.push(new CalendarTask(new Date(2010, 11, 4, 16, 0), 'Get Married', false));
 
         // ditto for 'NE Patriots vs NY Jets'
-        this._fakeTasks.push(new CalendarTask(new Date(2010, 11, 6, 20, 30), 'NE Patriots vs NY Jets'));
+        this._fakeTasks.push(new CalendarTask(new Date(2010, 11, 6, 20, 30), 'NE Patriots vs NY Jets', false));
 
         // An event for tomorrow @6:30pm that is added/removed every five
         // seconds (to check that the ::changed signal works)
-        let transientEventDate = new Date(now.getTime() + 86400*1000);
+        let transientEventDate = new Date(midnightToday.getTime() + 86400 * 1000);
         transientEventDate.setHours(18);
         transientEventDate.setMinutes(30);
         transientEventDate.setSeconds(0);
         Mainloop.timeout_add(5000, Lang.bind(this, this._updateTransientEvent));
         this._includeTransientEvent = false;
-        this._transientEvent = new CalendarTask(transientEventDate, 'A Transient Event');
+        this._transientEvent = new CalendarTask(transientEventDate, 'A Transient Event', false);
         this._transientEventCounter = 1;
     },
 
@@ -223,16 +272,15 @@ FakeEventSource.prototype = {
         }
         if (this._includeTransientEvent && this._transientEvent.date >= begin && this._transientEvent.date <= end)
             result.push(this._transientEvent);
+        result.sort(function(task1, task2) {
+            return task1.date.getTime() - task2.date.getTime();
+        });
         return result;
     },
 
     hasTasks: function(day) {
-        let dayBegin = new Date(day.getTime());
-        let dayEnd = new Date(day.getTime());
-        dayBegin.setHours(0);
-        dayBegin.setMinutes(1);
-        dayEnd.setHours(23);
-        dayEnd.setMinutes(59);
+        let dayBegin = _getBeginningOfDay(day);
+        let dayEnd = _getEndOfDay(day);
 
         let tasks = this.getTasks(dayBegin, dayEnd);
 
@@ -280,6 +328,7 @@ Calendar.prototype = {
         }
 
         // Find the ordering for month/year in the calendar heading
+        this._headerFormatWithoutYear = '%B';
         switch (Gettext_gtk30.gettext('calendar:MY')) {
         case 'calendar:MY':
             this._headerFormat = '%B %Y';
@@ -417,7 +466,12 @@ Calendar.prototype = {
     },
 
     _update: function() {
-        this._dateLabel.text = this.selectedDate.toLocaleFormat(this._headerFormat);
+        let now = new Date();
+
+        if (_sameYear(this.selectedDate, now))
+            this._dateLabel.text = this.selectedDate.toLocaleFormat(this._headerFormatWithoutYear);
+        else
+            this._dateLabel.text = this.selectedDate.toLocaleFormat(this._headerFormat);
 
         // Remove everything but the topBox and the weekday labels
         let children = this.actor.get_children();
@@ -432,8 +486,6 @@ Calendar.prototype = {
         let daysToWeekStart = (7 + iter.getDay() - this._weekStart) % 7;
         iter.setTime(iter.getTime() - daysToWeekStart * MSECS_IN_DAY);
 
-        let now = new Date();
-
         let row = 2;
         while (true) {
             let button = new St.Button({ label: iter.getDate().toString() });
@@ -500,6 +552,8 @@ EventsList.prototype = {
         this._date = new Date();
         this._eventSource = eventSource;
         this._eventSource.connect('changed', Lang.bind(this, this._update));
+        this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
+        this._desktopSettings.connect('changed', Lang.bind(this, this._update));
         this._update();
     },
 
@@ -516,10 +570,12 @@ EventsList.prototype = {
                                           text: desc} ));
     },
 
-    _addPeriod: function(header, begin, end, includeDayName) {
+    _addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
         let tasks = this._eventSource.getTasks(begin, end);
 
-        if (tasks.length == 0)
+        let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;
+
+        if (tasks.length == 0 && !showNothingScheduled)
             return;
 
         let vbox = new St.BoxLayout( {vertical: true} );
@@ -538,50 +594,59 @@ EventsList.prototype = {
         for (let n = 0; n < tasks.length; n++) {
             let task = tasks[n];
             let dayString = _getEventDayAbbreviation(task.date.getDay());
-            let timeString = task.date.toLocaleFormat('%I:%M %p'); // TODO: locale considerations
+            let timeString = _formatTaskTime(task, clockFormat);
             let summaryString = task.summary;
             this._addEvent(dayNameBox, timeBox, eventTitleBox, includeDayName, dayString, timeString, summaryString);
         }
+
+        if (tasks.length == 0 && showNothingScheduled) {
+            let now = new Date();
+            /* Translators: Text to show if there are no events */
+            let nothingTask = new CalendarTask(now, _("Nothing Scheduled"), true);
+            let timeString = _formatTaskTime(nothingTask, clockFormat);
+            this._addEvent(dayNameBox, timeBox, eventTitleBox, false, "", timeString, nothingTask.summary);
+        }
     },
 
     _showOtherDay: function(day) {
         this.actor.destroy_children();
 
-        let dayBegin = new Date(day.getTime());
-        let dayEnd = new Date(day.getTime());
-        dayBegin.setHours(0);
-        dayBegin.setMinutes(1);
-        dayEnd.setHours(23);
-        dayEnd.setMinutes(59);
-        this._addPeriod(day.toLocaleFormat('%A, %B %d, %Y'), dayBegin, dayEnd, false);
+        let dayBegin = _getBeginningOfDay(day);
+        let dayEnd = _getEndOfDay(day);
+
+        let dayString;
+        let now = new Date();
+        if (_sameYear(day, now))
+            dayString = day.toLocaleFormat('%A, %B %d');
+        else
+            dayString = day.toLocaleFormat('%A, %B %d, %Y');
+        this._addPeriod(dayString, dayBegin, dayEnd, false, true);
     },
 
     _showToday: function() {
         this.actor.destroy_children();
 
-        let dayBegin = new Date();
-        let dayEnd = new Date();
-        dayBegin.setHours(0);
-        dayBegin.setMinutes(1);
-        dayEnd.setHours(23);
-        dayEnd.setMinutes(59);
-        this._addPeriod(_("Today"), dayBegin, dayEnd, false);
+        let now = new Date();
+        let dayBegin = _getBeginningOfDay(now);
+        let dayEnd = _getEndOfDay(now);
+        this._addPeriod(_("Today"), dayBegin, dayEnd, false, true);
 
         dayBegin.setDate(dayBegin.getDate() + 1);
         dayEnd.setDate(dayEnd.getDate() + 1);
-        this._addPeriod(_("Tomorrow"), dayBegin, dayEnd, false);
+        this._addPeriod(_("Tomorrow"), dayBegin, dayEnd, false, true);
 
         if (dayEnd.getDay() == 6 || dayEnd.getDay() == 0) {
             dayBegin.setDate(dayEnd.getDate() + 1);
             dayEnd.setDate(dayBegin.getDate() + 6 - dayBegin.getDay());
 
-            this._addPeriod(_("Next week"), dayBegin, dayEnd, true);
+            this._addPeriod(_("Next week"), dayBegin, dayEnd, true, true);
             return;
+        } else {
+            let d = 6 - dayEnd.getDay() - 1;
+            dayBegin.setDate(dayBegin.getDate() + 1);
+            dayEnd.setDate(dayEnd.getDate() + 1 + d);
+            this._addPeriod(_("This week"), dayBegin, dayEnd, true, true);
         }
-        let d = 6 - dayEnd.getDay() - 1;
-        dayBegin.setDate(dayBegin.getDate() + 1);
-        dayEnd.setDate(dayEnd.getDate() + 1 + d);
-        this._addPeriod(_("This week"), dayBegin, dayEnd, true);
     },
 
     // Sets the event list to show events from a specific date
diff --git a/js/ui/dateMenu.js b/js/ui/dateMenu.js
index 8616735..35f6558 100644
--- a/js/ui/dateMenu.js
+++ b/js/ui/dateMenu.js
@@ -55,8 +55,8 @@ DateMenuButton.prototype = {
         let hbox;
         let vbox;
 
-        this._eventSource = new Calendar.EmptyEventSource();
-        //this._eventSource = new Calendar.FakeEventSource();
+        //this._eventSource = new Calendar.EmptyEventSource();
+        this._eventSource = new Calendar.FakeEventSource();
         // TODO: write e.g. EvolutionEventSource
 
         PanelMenu.Button.prototype._init.call(this, St.Align.START);
@@ -64,12 +64,12 @@ DateMenuButton.prototype = {
         this._clock = new St.Label();
         this.actor.set_child(this._clock);
 
-        hbox = new St.BoxLayout({name: 'calendarHBox'});
+        hbox = new St.BoxLayout();
         this.menu.addActor(hbox);
 
         // Fill up the first column
 
-        vbox = new St.BoxLayout({vertical: true, name: 'calendarVBox1'});
+        vbox = new St.BoxLayout({vertical: true});
         hbox.add(vbox);
 
         // Date
@@ -87,18 +87,33 @@ DateMenuButton.prototype = {
                                }));
         vbox.add(this._calendar.actor);
 
+        //item = new St.Button({style_class: 'popup-menu-item', label: 'foobar'});
+        //vbox.add(item);
+        item = new PopupMenu.PopupSeparatorMenuItem();
+        item.actor.remove_actor(item._drawingArea);
+        vbox.add(item._drawingArea);
+        item = new PopupMenu.PopupMenuItem(_("Date and Time Settings"));
+        item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
+        vbox.add(item.actor);
+
         // Add vertical separator
 
         item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
                                     pseudo_class: 'highlighted' });
-        item.set_width(25); // TODO: don't hard-code the width
         item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
         hbox.add(item);
 
         // Fill up the second column
         //
+        vbox = new St.BoxLayout({vertical: true});
+        hbox.add(vbox);
+
         // Event list
-        hbox.add(this._eventList.actor);
+        vbox.add(this._eventList.actor);
+
+        item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
+        item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
+        vbox.add(item.actor, {y_align : St.Align.END, expand : true, y_fill : false});
 
         // Whenever the menu is opened, select today
         this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
@@ -113,12 +128,12 @@ DateMenuButton.prototype = {
         // Done with hbox for calendar and event list
 
         // Add separator
-        item = new PopupMenu.PopupSeparatorMenuItem();
-        this.menu.addMenuItem(item);
+        //item = new PopupMenu.PopupSeparatorMenuItem();
+        //this.menu.addMenuItem(item);
 
         // Add button to get to the Date and Time settings
-        this.menu.addAction(_("Date and Time Settings"),
-                            Lang.bind(this, this._onPreferencesActivate));
+        //this.menu.addAction(_("Date and Time Settings"),
+        //                    Lang.bind(this, this._onPreferencesActivate));
 
         // Track changes to clock settings
         this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
@@ -185,8 +200,10 @@ DateMenuButton.prototype = {
 
         this._clock.set_text(displayDate.toLocaleFormat(clockFormat));
 
-        /* Translators: This is the date format to use */
-        dateFormat = _("%B %e, %Y");
+        /* Translators: This is the date format to use when the calendar popup is
+         * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+         */
+        dateFormat = _("%A %B %e, %Y");
         this._date.set_text(displayDate.toLocaleFormat(dateFormat));
 
         Mainloop.timeout_add(msecRemaining, Lang.bind(this, this._updateClockAndDate));
@@ -194,6 +211,14 @@ DateMenuButton.prototype = {
     },
 
     _onPreferencesActivate: function() {
+        this.menu.close();
         Util.spawnDesktop('gnome-datetime-panel');
     },
+
+    _onOpenCalendarActivate: function() {
+        this.menu.close();
+        // TODO: pass '-c calendar' (to force the calendar at startup)
+        // TODO: pass the selected day
+        Util.spawnDesktop('evolution');
+    },
 };



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]