Bug 415548 - Support filtering of tasks in Today Pane (like in Task Mode) - Part 1: Context Menu. r=philipp, a=philipp
authorMatthew Mecca <matthew.mecca@gmail.com>
Wed, 21 Sep 2011 22:31:56 -0400
changeset 8848 cc5f843c694176dcf60e1e91b6ed72fc756662ac
parent 8847 5525d1a7f9b1a7e046cac70af64965fa6f19ba76
child 8849 f22d01143155f3f25fba6d4a3e91b5e34a9edf9f
push idunknown
push userunknown
push dateunknown
reviewersphilipp, philipp
bugs415548
Bug 415548 - Support filtering of tasks in Today Pane (like in Task Mode) - Part 1: Context Menu. r=philipp, a=philipp
calendar/base/content/calendar-common-sets.js
calendar/base/content/calendar-common-sets.xul
calendar/base/content/calendar-task-tree.js
calendar/base/content/calendar-task-tree.xml
calendar/base/content/calendar-unifinder-todo.js
calendar/base/content/calendar-unifinder-todo.xul
calendar/base/src/calFilter.js
--- a/calendar/base/content/calendar-common-sets.js
+++ b/calendar/base/content/calendar-common-sets.js
@@ -78,16 +78,17 @@ var calendarController = {
         "calendar_toggle_workdays_only_command": true,
 
         "calendar_day-view_command": true,
         "calendar_week-view_command": true,
         "calendar_multiweek-view_command": true,
         "calendar_month-view_command": true,
 
         "calendar_task_filter_command": true,
+        "calendar_task_filter_todaypane_command": true,
         "calendar_reload_remote_calendars": true,
         "calendar_show_unifinder_command": true,
         "calendar_toggle_completed_command": true,
         "calendar_percentComplete-0_command": true,
         "calendar_percentComplete-25_command": true,
         "calendar_percentComplete-50_command": true,
         "calendar_percentComplete-75_command": true,
         "calendar_percentComplete-100_command": true,
--- a/calendar/base/content/calendar-common-sets.xul
+++ b/calendar/base/content/calendar-common-sets.xul
@@ -18,16 +18,17 @@
    -   Philipp Kewisch <mozilla@kewis.ch>
    - Portions created by the Initial Developer are Copyright (C) 2007
    - the Initial Developer. All Rights Reserved.
    -
    - Contributor(s):
    - Simon Paquet <bugzilla@babylonsounds.com>
    - Berend Cornelius <berend.cornelius@sun.com>
    - Martin Schroeder <mschroeder@mozilla.x-home.org>
+   - Matthew Mecca <matthew.mecca@gmail.com>
    -
    - Alternatively, the contents of this file may be used under the terms of
    - either the GNU General Public License Version 2 or later (the "GPL"), or
    - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
    - in which case the provisions of the GPL or the LGPL are applicable instead
    - of those above. If you wish to allow use of your version of this file only
    - under the terms of either the GPL or the LGPL, and not to allow others to
    - use your version of this file under the terms of the MPL, indicate your
@@ -49,16 +50,19 @@
   <stringbundleset id="calendar_stringbundles">
     <stringbundle id="bundle_branding" src="chrome://branding/locale/brand.properties"/>
   </stringbundleset>
   <script type="application/javascript" src="chrome://calendar/content/calendar-common-sets.js"/>
 
   <broadcasterset id="calendar_broadcasters">
     <broadcaster id="modeBroadcaster" mode="calendar"/>
     <broadcaster id="calendarviewBroadcaster"/>
+    <broadcaster id="unifinder-todo-filter-broadcaster"
+                 persist="value"
+                 value="throughcurrent"/>
   </broadcasterset>
 
   <commandset id="calendar_commands"
               commandupdater="true"
               events="calendar_commands"
               oncommandupdate="calendarController.updateCommands()">
     <command id="calendar_new_event_command" oncommand="goDoCommand('calendar_new_event_command')"/>
     <command id="calendar_new_event_context_command" oncommand="goDoCommand('calendar_new_event_context_command')"/>
@@ -110,16 +114,17 @@
     <command id="calendar_general-progress_command" oncommand="goDoCommand('calendar_general-priority_command')"/>
     <command id="calendar_toggle_orientation_command" persist="checked" oncommand="goDoCommand('calendar_toggle_orientation_command')"/>
     <command id="calendar_toggle_workdays_only_command" persist="checked" oncommand="goDoCommand('calendar_toggle_workdays_only_command')"/>
     <command id="calendar_toggle_tasks_in_view_command" persist="checked" oncommand="toggleTasksInView()"/>
     <command id="calendar_toggle_show_completed_in_view_command" persist="checked" oncommand="toggleShowCompletedInView()"/>
     <command id="calendar_toggle_minimonthpane_command" oncommand="document.getElementById('minimonth-pane').togglePane(event)"/>
     <command id="calendar_toggle_calendarlist_command" oncommand="document.getElementById('calendar-list-pane').togglePane(event)"/>
     <command id="calendar_task_filter_command" oncommand="taskViewUpdate(event.explicitOriginalTarget.getAttribute('value'))"/>
+    <command id="calendar_task_filter_todaypane_command" oncommand="updateCalendarToDoUnifinder(event.explicitOriginalTarget.getAttribute('value'))"/>
     <command id="calendar_toggle_filter_command" oncommand="document.getElementById('task-filter-pane').togglePane(event)"/>
     <command id="calendar_view_next_command" oncommand="goDoCommand('calendar_view_next_command')"/>
     <command id="calendar_view_today_command" oncommand="currentView().moveView()"/>
     <command id="calendar_view_prev_command" oncommand="goDoCommand('calendar_view_prev_command')"/>
 
     <!-- this is a pseudo-command that is disabled when in calendar mode -->
     <command id="calendar_in_foreground"/>
     <!-- this is a pseudo-command that is disabled when not in calendar mode -->
@@ -480,11 +485,77 @@
                     label="&read.only.decline.label;" value="DECLINED"/>
           <menuitem id="task-context-menu-attend-needsaction-all-menuitem"
                     type="radio"
                     scope="all-occurrences"
                     name="task-context-attendance-all"
                     label="&read.only.needs.action.label;" value="NEEDS-ACTION"/>
         </menupopup>
       </menu>
+      <menuseparator id="task-context-menu-separator-filter"/>
+      <menu id="task-context-menu-filter-todaypane"
+            label="&calendar.tasks.view.filtertasks.label;"
+            accesskey="&calendar.tasks.view.filtertasks.accesskey;">
+        <menupopup id="task-context-menu-filter-todaypane-popup">
+          <observes element="unifinder-todo-filter-broadcaster"
+                    attribute="value"
+                    onbroadcast="checkRadioControl(this.parentNode, document.getElementById(this.getAttribute('element')).getAttribute('value'));"/>
+          <menuitem id="task-context-menu-filter-todaypane-current"
+                    name="filtergrouptodaypane"
+                    value="throughcurrent"
+                    type="radio"
+                    command="calendar_task_filter_todaypane_command"
+                    label="&calendar.task.filter.current.label;"
+                    accesskey="&calendar.task.filter.current.accesskey;"/>
+          <menuitem id="task-context-menu-filter-todaypane-today"
+                    name="filtergrouptodaypane"
+                    value="today"
+                    type="radio"
+                    command="calendar_task_filter_todaypane_command"
+                    label="&calendar.task.filter.today.label;"
+                    accesskey="&calendar.task.filter.today.accesskey;"/>
+          <menuitem id="task-context-menu-filter-todaypane-next7days"
+                    name="filtergrouptodaypane"
+                    value="next7days"
+                    type="radio"
+                    command="calendar_task_filter_todaypane_command"
+                    label="&calendar.task.filter.next7days.label;"
+                    accesskey="&calendar.task.filter.next7days.accesskey;"/>
+          <menuitem id="task-context-menu-filter-todaypane-notstarted"
+                    name="filtergrouptodaypane"
+                    value="notstarted"
+                    type="radio"
+                    command="calendar_task_filter_todaypane_command"
+                    label="&calendar.task.filter.notstarted.label;"
+                    accesskey="&calendar.task.filter.notstarted.accesskey;"/>
+          <menuitem id="task-context-menu-filter-todaypane-overdue"
+                    name="filtergrouptodaypane"
+                    value="overdue"
+                    type="radio"
+                    command="calendar_task_filter_todaypane_command"
+                    label="&calendar.task.filter.overdue.label;"
+                    accesskey="&calendar.task.filter.overdue.accesskey;"/>
+          <menuitem id="task-context-menu-filter-todaypane-completed"
+                    name="filtergrouptodaypane"
+                    type="radio"
+                    value="completed"
+                    command="calendar_task_filter_todaypane_command"
+                    label="&calendar.task.filter.completed.label;"
+                    accesskey="&calendar.task.filter.completed.accesskey;"/>
+          <menuitem id="task-context-menu-filter-todaypane-open"
+                    name="filtergrouptodaypane"
+                    type="radio"
+                    value="open"
+                    command="calendar_task_filter_todaypane_command"
+                    label="&calendar.task.filter.open.label;"
+                    accesskey="&calendar.task.filter.open.accesskey;"/>
+          <menuitem id="task-context-menu-filter-todaypane-all"
+                    name="filtergrouptodaypane"
+                    value="all"
+                    type="radio"
+                    command="calendar_task_filter_todaypane_command"
+                    label="&calendar.task.filter.all.label;"
+                    accesskey="&calendar.task.filter.all.accesskey;"/>
+        </menupopup>
+      </menu>
     </menupopup>
   </popupset>
 </overlay>
--- a/calendar/base/content/calendar-task-tree.js
+++ b/calendar/base/content/calendar-task-tree.js
@@ -105,27 +105,37 @@ function changeContextMenuForTask(aEvent
     document.getElementById("task-context-menu-new").hidden =
         (idnode == "unifinder-todo-tree" && !sunbird);
     document.getElementById("task-context-menu-modify").hidden =
         (idnode == "unifinder-todo-tree" && !sunbird);
     document.getElementById("task-context-menu-new-todaypane").hidden =
         (idnode == "calendar-task-tree" || sunbird);
     document.getElementById("task-context-menu-modify-todaypane").hidden =
         (idnode == "calendar-task-tree" || sunbird);
+    document.getElementById("task-context-menu-filter-todaypane").hidden =
+        (idnode == "calendar-task-tree" || sunbird);
+    document.getElementById("task-context-menu-separator-filter").hidden =
+        (idnode == "calendar-task-tree" || sunbird);
+
     let tasksSelected = (items.length > 0);
     applyAttributeToMenuChildren(aEvent.target, "disabled", (!tasksSelected));
     if (calendarController.isCommandEnabled("calendar_new_todo_command") &&
         calendarController.isCommandEnabled("calendar_new_todo_todaypane_command")) {
         document.getElementById("calendar_new_todo_command").removeAttribute("disabled");
         document.getElementById("calendar_new_todo_todaypane_command").removeAttribute("disabled");
     } else {
         document.getElementById("calendar_new_todo_command").setAttribute("disabled", "true");
         document.getElementById("calendar_new_todo_todaypane_command").setAttribute("disabled", "true");
     }
 
+    // make sure the filter menu is enabled
+    document.getElementById("task-context-menu-filter-todaypane").removeAttribute("disabled");
+    applyAttributeToMenuChildren(document.getElementById("task-context-menu-filter-todaypane-popup"),
+                                 "disabled", false);
+
     changeMenuForTask(aEvent);
 
     let menu = document.getElementById("task-context-menu-attendance-menu");
     setupAttendanceMenu(menu, items);
 }
 
 /**
  * Notify the task tree that the context menu open state has changed.
--- a/calendar/base/content/calendar-task-tree.xml
+++ b/calendar/base/content/calendar-task-tree.xml
@@ -26,16 +26,17 @@
    -   ArentJan Banck <ajbanck@planet.nl>
    -   Curtis Jewell <csjewell@mail.freeshell.org>
    -   Eric Belhaire <eric.belhaire@ief.u-psud.fr>
    -   Mark Swaffer <swaff@fudo.org>
    -   Michael Buettner <michael.buettner@sun.com>
    -   Philipp Kewisch <mozilla@kewis.ch>
    -   Lars Wohlfahrt <thetux.moz@googlemail.com>
    -   Fred Jendrzejewski <fred.jen@web.de>
+   -   Matthew Mecca <matthew.mecca@gmail.com>
    -
    - Alternatively, the contents of this file may be used under the terms of
    - either the GNU General Public License Version 2 or later (the "GPL"), or
    - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
    - in which case the provisions of the GPL or the LGPL are applicable instead
    - of those above. If you wish to allow use of your version of this file only
    - under the terms of either the GPL or the LGPL, and not to allow others to
    - use your version of this file under the terms of the MPL, indicate your
@@ -1241,11 +1242,23 @@
   <binding id="calendar-task-tree-todaypane" extends="chrome://calendar/content/calendar-task-tree.xml#calendar-task-tree">
     <implementation>
       <method name="getInitialDate">
             <body><![CDATA[
               let initialDate = agendaListbox.today ? agendaListbox.today.start : now();
               return initialDate ? initialDate : now();
             ]]></body>
       </method>
+      <method name="updateFilter">
+        <parameter name="aFilter"/>
+        <body><![CDATA[
+            this.mFilter.selectedDate = agendaListbox.today && agendaListbox.today.start ? 
+                                        agendaListbox.today.start : now();
+            this.mFilter.propertyFilter = aFilter || this.mFilter.propertyFilter || "all";
+            this.mDateRangeFilter = aFilter || this.mDateRangeFilter || "all";
+            this.mFilter.setDateFilter(this.mDateRangeFilter);
+
+            this.refresh();
+        ]]></body>
+      </method>
     </implementation>    
   </binding>
 </bindings>
--- a/calendar/base/content/calendar-unifinder-todo.js
+++ b/calendar/base/content/calendar-unifinder-todo.js
@@ -23,16 +23,17 @@
  *                 Chris Charabaruk <coldacid@meldstar.com>
  *                 Colin Phillips <colinp@oeone.com>
  *                 ArentJan Banck <ajbanck@planet.nl>
  *                 Curtis Jewell <csjewell@mail.freeshell.org>
  *                 Eric Belhaire <eric.belhaire@ief.u-psud.fr>
  *                 Mark Swaffer <swaff@fudo.org>
  *                 Michael Buettner <michael.buettner@sun.com>
  *                 Philipp Kewisch <mozilla@kewis.ch>
+ *                 Matthew Mecca <matthew.mecca@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -54,26 +55,40 @@ function prepareCalendarToDoUnifinder() 
     // add listener to update the date filters
     getViewDeck().addEventListener("dayselect", updateCalendarToDoUnifinder, false);
 
     updateCalendarToDoUnifinder();
 }
 
 /**
  * Updates the applied filter and show completed view of the unifinder todo.
+ *
+ * @param aFilter        The filter name to set.
  */
-function updateCalendarToDoUnifinder() {
+function updateCalendarToDoUnifinder(aFilter) {
     // Set up hiding completed tasks for the unifinder-todo tree
-    var showCompleted = document.getElementById("show-completed-checkbox").checked;
-    var tree = document.getElementById("unifinder-todo-tree");
+    let showCompleted = document.getElementById("show-completed-checkbox").checked;
+    let tree = document.getElementById("unifinder-todo-tree");
+    let oldFilter = document.getElementById("unifinder-todo-filter-broadcaster").getAttribute("value");
+    let filter = oldFilter;
+
+    // This function acts as an event listener, in which case we get the Event as the 
+    // parameter instead of a filter.
+    if (aFilter && !(aFilter instanceof Event)) {
+        filter = aFilter;
+    }
+
+    if (filter && (filter != oldFilter)) {
+        document.getElementById("unifinder-todo-filter-broadcaster").setAttribute("value", aFilter);
+    }
 
     // update the filter
     tree.showCompleted = showCompleted;
-    tree.updateFilter();
+    tree.updateFilter(filter);
 }
 
 /**
  * Called when the window is unloaded to clean up the unifinder-todo.
  */
 function finishCalendarToDoUnifinder() {
     // remove listeners
     getViewDeck().removeEventListener("dayselect", updateCalendarToDoUnifinder, false);
-}
\ No newline at end of file
+}
--- a/calendar/base/content/calendar-unifinder-todo.xul
+++ b/calendar/base/content/calendar-unifinder-todo.xul
@@ -48,17 +48,17 @@
 <?xml-stylesheet type="text/css" href="chrome://calendar/content/calendar-bindings.css"?>
 <?xml-stylesheet type="text/css" href="chrome://calendar/skin/calendar-task-view.css"?>
 
 <overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <script type="application/javascript" src="chrome://calendar/content/calendar-task-tree.js"/>
   <script type="application/javascript" src="chrome://calendar/content/calFilter.js"/>  <script type="application/javascript" src="chrome://calendar/content/calendar-unifinder-todo.js"/>
   <script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
-  
+
   <vbox id="todo-tab-panel" persist="height,collapsed" flex="1">
     <box id="todo-label" align="left" collapsed="true">
       <label flex="1" crop="end" style="font-weight: bold" value="&calendar.unifinder.todoitems.label;" control="unifinder-todo-tree"/>
     </box>
     <box align="center">
       <checkbox id="show-completed-checkbox"
                 label="&calendar.unifinder.showcompletedtodos.label;"
                 flex="1"
--- a/calendar/base/src/calFilter.js
+++ b/calendar/base/src/calFilter.js
@@ -77,16 +77,17 @@ function calFilter() {
   this.wrappedJSObject = this;
 }
 
 calFilter.prototype = {
     mStartDate: null,
     mEndDate: null,
     mTextFilterField: null,
     mPropertyFilter: filterAll,
+    mSelectedDate: null,
 
     // a number of prefined Filters for properties
     mPropertyFilterBag: { 
         all: filterAll,
         notstarted: function cF_filterNotStarted(item) {
             return (percentCompleted(item) <= 0);
         },
         overdue: function cF_filterOverdue(item) {
@@ -128,26 +129,34 @@ calFilter.prototype = {
     },
 
     get endDate() {
         return this.mEndDate;
     },
 
     set endDate(aEndDate) {
         return (this.mEndDate = aEndDate);
-    },    
+    },
 
     set textFilterField(aId) {
         return (this.mTextFilterField = aId);
     },
 
     get textFilterField() {
         return this.mTextFilterField;
     },
 
+    get selectedDate() {
+        return this.mSelectedDate;
+    },
+
+    set selectedDate(aSelectedDate) {
+        return (this.mSelectedDate = aSelectedDate);
+    },
+
     // checks if the item contains the text of mTextFilterField
     textFilter: function cF_filterByText(aItem) {
         filterByText.mTextFilterField = this.mTextFilterField;
         var inIt = filterByText(aItem);
         return inIt;
     },
 
     get propertyFilter() {
@@ -182,17 +191,17 @@ calFilter.prototype = {
           return (this.mPropertyFilter = aFilter);
         }  
         
         return (this.mPropertyFilter = filterAll);
     },
 
     // set's the startDate and the endDate by using getDatesForFilter 
     setDateFilter: function cF_setDateFilter(aFilter) {
-      var [startDate, endDate] = getDatesForFilter(aFilter);
+      var [startDate, endDate] = getDatesForFilter(aFilter, this.mSelectedDate);
       this.mStartDate = startDate;
       this.mEndDate = endDate;
       return [this.mStartdate, this.mEndDate];
     },
 
     // checks if the item is between startDate and endDate
     isItemWithinRange: function cF_isDateWithinRange(aItem) {
         return checkIfInRange(aItem, this.mStartDate, this.mEndDate);
@@ -200,28 +209,30 @@ calFilter.prototype = {
 
     // checks if the item is between startDate and endDate and its properties
     isItemInFilters: function cF_isItemInFilters(aItem) {
         return (this.isItemWithinRange(aItem) && this.propertyFilter(aItem));
     }
 };
 
 /**
- * @param aFilter a String describing the filter, it should met a regEx to call 
- *                duration from filter otherwise a costumized filter is called
+ * @param aFilter         a String describing the filter, it should met a regEx to call 
+ *                        duration from filter otherwise a costumized filter is called
+ * @param aSelectedDate   Optional - the selected date to use for filters that require it.
+ *                        The selected day of the current view will be used by default.
  * @return        [startDate, endDate]
  */
 
-function getDatesForFilter(aFilter) {
+function getDatesForFilter(aFilter, aSelectedDate) {
     let endDate = cal.createDateTime();
     let startDate = cal.createDateTime();
     let duration = cal.createDuration();
     let oneDay = cal.createDuration();
     oneDay.days = 1;
-    let selectedDate = currentView().selectedDay;
+    let selectedDate = aSelectedDate || currentView().selectedDay;
 
     let durFilterReg = /next|last\d+\D+$/
     if (durFilterReg.exec(aFilter)) {
         duration =  durationFromFilter(aFilter);
         if (!duration) {
             endDate = null;
             startDate = null;
         } else {