Add ISO week dates to the calendar

This patch adds ISO week dates to the calendar. Week dates are an
often used feature in business and government offices. Can be turned
on through gconf, off by default.

https://bugzilla.gnome.org/show_bug.cgi?id=603532
This commit is contained in:
Raphael Bosshard 2010-05-27 09:31:46 +02:00 committed by Florian Müllner
parent 155bee388d
commit e3eaa69948
4 changed files with 112 additions and 12 deletions

View File

@ -438,6 +438,20 @@
</locale> </locale>
</schema> </schema>
<!-- Calendar -->
<schema>
<key>/schemas/desktop/gnome/shell/calendar/show_weekdate</key>
<applyto>/desktop/gnome/shell/calendar/show_weekdate</applyto>
<owner>gnome-shell</owner>
<type>bool</type>
<default>false</default>
<locale name="C">
<short>Show the week date in the calendar</short>
<long>
If true, display the ISO week date in the calendar.
</long>
</locale>
</schema>
</schemalist> </schemalist>
</gconfschemafile> </gconfschemafile>

View File

@ -899,6 +899,10 @@ StTooltip {
padding: 0px 4px 2px 0px; padding: 0px 4px 2px 0px;
} }
.calendar-calendarweek {
color: #666666;
}
/* App Switcher */ /* App Switcher */
#altTabPopup { #altTabPopup {
padding: 8px; padding: 8px;

View File

@ -3,10 +3,14 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Lang = imports.lang; const Lang = imports.lang;
const St = imports.gi.St; const St = imports.gi.St;
const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const Gettext_gtk20 = imports.gettext.domain('gtk20'); const Gettext_gtk20 = imports.gettext.domain('gtk20');
const MSECS_IN_DAY = 24 * 60 * 60 * 1000; const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
const MSECS_IN_WEEK = MSECS_IN_DAY * 7;
const WEEKDATE_HEADER_WIDTH_DIGITS = 3;
const SHOW_WEEKDATE_KEY = 'calendar/show_weekdate';
function _sameDay(dateA, dateB) { function _sameDay(dateA, dateB) {
return (dateA.getDate() == dateB.getDate() && return (dateA.getDate() == dateB.getDate() &&
@ -14,6 +18,24 @@ function _sameDay(dateA, dateB) {
dateA.getYear() == dateB.getYear()); dateA.getYear() == dateB.getYear());
} }
function _getCalendarWeekForDate(date) {
let startOfYear = new Date(date.getFullYear(), 0, 1);
let sday = startOfYear.getDay();
let offset = 4 + (7 * Math.floor(sday * (1/5))) - sday;
let firstThursday = new Date(date.getFullYear(), 0, 1 + offset);
let weekOfYear = Math.ceil((date.getTime() - firstThursday.getTime()) / MSECS_IN_WEEK);
return weekOfYear;
}
function _getDigitWidth(actor){
let context = actor.get_pango_context();
let themeNode = actor.get_theme_node();
let font = themeNode.get_font();
let metrics = context.get_metrics(font, context.get_language());
let width = metrics.get_approximate_digit_width();
return width;
}
function Calendar() { function Calendar() {
this._init(); this._init();
} }
@ -24,6 +46,13 @@ Calendar.prototype = {
// GTK+ by preference uses nl_langinfo (NL_TIME_FIRST_WEEKDAY). We probably // GTK+ by preference uses nl_langinfo (NL_TIME_FIRST_WEEKDAY). We probably
// should add a C function so we can do the full handling. // should add a C function so we can do the full handling.
this._weekStart = NaN; this._weekStart = NaN;
this._weekdate = NaN;
this._digitWidth = NaN;
this._gconf = Shell.GConf.get_default();
this._gconf.connect('changed', Lang.bind(this, this._onSettingsChange));
this._useWeekdate = this._gconf.get_boolean(SHOW_WEEKDATE_KEY);
let weekStartString = Gettext_gtk20.gettext('calendar:week_start:0'); let weekStartString = Gettext_gtk20.gettext('calendar:week_start:0');
if (weekStartString.indexOf('calendar:week_start:') == 0) { if (weekStartString.indexOf('calendar:week_start:') == 0) {
this._weekStart = parseInt(weekStartString.substring(20)); this._weekStart = parseInt(weekStartString.substring(20));
@ -58,11 +87,28 @@ Calendar.prototype = {
this.actor.connect('scroll-event', this.actor.connect('scroll-event',
Lang.bind(this, this._onScroll)); Lang.bind(this, this._onScroll));
this._buildHeader ();
this._update();
},
// Sets the calendar to show a specific date
setDate: function(date) {
if (!_sameDay(date, this.date)) {
this.date = date;
this._update();
}
},
_buildHeader: function() {
let offsetCols = this._useWeekdate ? 1 : 0;
this.actor.destroy_children();
// Top line of the calendar '<| September 2009 |>' // Top line of the calendar '<| September 2009 |>'
this._topBox = new St.BoxLayout(); this._topBox = new St.BoxLayout();
this.actor.add(this._topBox, this.actor.add(this._topBox,
{ row: 0, col: 0, col_span: 7 }); { row: 0, col: 0, col_span: offsetCols + 7 });
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChange));
let [backlabel, forwardlabel] = ['&lt;', '&gt;']; let [backlabel, forwardlabel] = ['&lt;', '&gt;'];
if (St.Widget.get_default_direction () == St.TextDirection.RTL) { if (St.Widget.get_default_direction () == St.TextDirection.RTL) {
[backlabel, forwardlabel] = [forwardlabel, backlabel]; [backlabel, forwardlabel] = [forwardlabel, backlabel];
@ -85,25 +131,39 @@ Calendar.prototype = {
let iter = new Date(this.date); let iter = new Date(this.date);
iter.setSeconds(0); // Leap second protection. Hah! iter.setSeconds(0); // Leap second protection. Hah!
iter.setHours(12); iter.setHours(12);
if (this._useWeekdate) {
this._weekdateHeader = new St.Label();
this.actor.add(this._weekdateHeader,
{ row: 1,
col: 0,
x_fill: false, x_align: St.Align.MIDDLE });
this._setWeekdateHeaderWidth();
} else {
this._weekdateHeader = null;
}
for (let i = 0; i < 7; i++) { for (let i = 0; i < 7; i++) {
this.actor.add(new St.Label({ text: iter.toLocaleFormat('%a') }), this.actor.add(new St.Label({ text: iter.toLocaleFormat('%a') }),
{ row: 1, { row: 1,
col: (7 + iter.getDay() - this._weekStart) % 7, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7,
x_fill: false, x_align: St.Align.END }); x_fill: false, x_align: St.Align.END });
iter.setTime(iter.getTime() + MSECS_IN_DAY); iter.setTime(iter.getTime() + MSECS_IN_DAY);
} }
// All the children after this are days, and get removed when we update the calendar // All the children after this are days, and get removed when we update the calendar
this._firstDayIndex = this.actor.get_children().length; this._firstDayIndex = this.actor.get_children().length;
this._update();
}, },
// Sets the calendar to show a specific date _onStyleChange: function(actor, event) {
setDate: function(date) { // width of a digit in pango units
if (!_sameDay(date, this.date)) { this._digitWidth = _getDigitWidth(this.actor) / Pango.SCALE;
this.date = date; this._setWeekdateHeaderWidth();
this._update(); },
_setWeekdateHeaderWidth: function() {
if (this.digitWidth != NaN && this._useWeekdate && this._weekdateHeader) {
this._weekdateHeader.set_width (this._digitWidth * WEEKDATE_HEADER_WIDTH_DIGITS);
} }
}, },
@ -140,6 +200,12 @@ Calendar.prototype = {
this._update(); this._update();
}, },
_onSettingsChange: function() {
this._useWeekdate = this._gconf.get_boolean(SHOW_WEEKDATE_KEY);
this._buildHeader();
this._update();
},
_update: function() { _update: function() {
this._dateLabel.text = this.date.toLocaleFormat(this._headerFormat); this._dateLabel.text = this.date.toLocaleFormat(this._headerFormat);
@ -166,10 +232,20 @@ Calendar.prototype = {
label.style_class = 'calendar-day calendar-other-month-day'; label.style_class = 'calendar-day calendar-other-month-day';
else else
label.style_class = 'calendar-day'; label.style_class = 'calendar-day';
let offsetCols = this._useWeekdate ? 1 : 0;
this.actor.add(label, this.actor.add(label,
{ row: row, col: (7 + iter.getDay() - this._weekStart) % 7, { row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7,
x_fill: false, x_align: St.Align.END }); x_fill: false, x_align: St.Align.END });
if (this._useWeekdate && iter.getDay() == 4) {
let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(),
style_class: 'calendar-day calendar-calendarweek'});
this.actor.add(label,
{ row: row, col: 0,
x_fill: false, x_align: St.Align.MIDDLE });
}
iter.setTime(iter.getTime() + MSECS_IN_DAY); iter.setTime(iter.getTime() + MSECS_IN_DAY);
if (iter.getDay() == this._weekStart) { if (iter.getDay() == this._weekStart) {
// We stop on the first "first day of the week" after the month we are displaying // We stop on the first "first day of the week" after the month we are displaying

View File

@ -1268,6 +1268,7 @@ CalendarPopup.prototype = {
Main.chrome.addActor(this.actor, { visibleInOverview: true, Main.chrome.addActor(this.actor, { visibleInOverview: true,
affectsStruts: false }); affectsStruts: false });
this.actor.y = (panelActor.y + panelActor.height - this.actor.height); this.actor.y = (panelActor.y + panelActor.height - this.actor.height);
this.calendar.actor.connect('notify::width', Lang.bind(this, this._centerPopup));
}, },
show: function() { show: function() {
@ -1276,7 +1277,7 @@ CalendarPopup.prototype = {
// Reset the calendar to today's date // Reset the calendar to today's date
this.calendar.setDate(new Date()); this.calendar.setDate(new Date());
this.actor.x = Math.round(panelActor.x + (panelActor.width - this.actor.width) / 2); this._centerPopup();
this.actor.lower(panelActor); this.actor.lower(panelActor);
this.actor.show(); this.actor.show();
Tweener.addTween(this.actor, Tweener.addTween(this.actor,
@ -1296,5 +1297,10 @@ CalendarPopup.prototype = {
onComplete: function() { this.actor.hide(); }, onComplete: function() { this.actor.hide(); },
onCompleteScope: this onCompleteScope: this
}); });
},
_centerPopup: function() {
let panelActor = Main.panel.actor;
this.actor.x = Math.round(panelActor.x + (panelActor.width - this.actor.width) / 2);
} }
}; };