← Back to team overview

gtg team mailing list archive

[Merge] lp:~antonio-roquentin/gtg/notification_colored_icon into lp:gtg

 

Antonio Roquentin has proposed merging lp:~antonio-roquentin/gtg/notification_colored_icon into lp:gtg.

Requested reviews:
  Gtg developers (gtg)
Related bugs:
  Bug #1001012 in Getting Things GNOME!: "Notification area icon should change color when there are urgent tasks"
  https://bugs.launchpad.net/gtg/+bug/1001012

For more details, see:
https://code.launchpad.net/~antonio-roquentin/gtg/notification_colored_icon/+merge/107491

Color the icon in the notification area when there are tasks in danger zone. Implementation of a feature described in bug #1001012. The modifications only affect the notification_area plugin. Setting a danger zone of 0 days in the preferences will reproduce the old behavior (icon is white all the time).
-- 
https://code.launchpad.net/~antonio-roquentin/gtg/notification_colored_icon/+merge/107491
Your team Gtg developers is requested to review the proposed merge of lp:~antonio-roquentin/gtg/notification_colored_icon into lp:gtg.
=== modified file 'AUTHORS'
--- AUTHORS	2012-05-25 16:57:38 +0000
+++ AUTHORS	2012-05-25 22:47:20 +0000
@@ -104,4 +104,7 @@
 * Marta Maria Casetti <mmcasetti@xxxxxxxxx>
 * Song Yangyu <flyfy1@xxxxxxxxx>
 * Saurabh Anand <saurabhanandiit@xxxxxxxxx>
+<<<<<<< TREE
 * Alan Gomes <alangalvino@xxxxxxxxx>
+=======
+* Antonio Roquentin <antonio.roqeuntin@xxxxxx>>>>>>>> MERGE-SOURCE

=== modified file 'GTG/plugins/notification-area.gtg-plugin'
--- GTG/plugins/notification-area.gtg-plugin	2012-03-05 15:23:05 +0000
+++ GTG/plugins/notification-area.gtg-plugin	2012-05-25 22:47:20 +0000
@@ -6,6 +6,6 @@
 that keeps the list of the currently workable tasks.
 To start GTG minimized,  click on the 'Configure Plugin' button 
 at the bottom of this window."""
-Authors="Paulo Cabido <paulo.cabido@xxxxxxxxx>, Luca Invernizzi <invernizzi.l@xxxxxxxxx>, Jono Bacon <jono@xxxxxxxxxx>, Izidor Matušov <izidor.matusov@xxxxxxxxx>"
+Authors="Paulo Cabido <paulo.cabido@xxxxxxxxx>, Luca Invernizzi <invernizzi.l@xxxxxxxxx>, Jono Bacon <jono@xxxxxxxxxx>, Izidor Matušov <izidor.matusov@xxxxxxxxx>, Antonio Roquentin <antonio.roqeuntin@xxxxxx>"
 Version=0.95
 Enabled=False

=== added file 'GTG/plugins/notification_area/gtg-need-attention.svg'
--- GTG/plugins/notification_area/gtg-need-attention.svg	1970-01-01 00:00:00 +0000
+++ GTG/plugins/notification_area/gtg-need-attention.svg	2012-05-25 22:47:20 +0000
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   id="svg3212"
+   height="16"
+   width="16"
+   version="1.0"
+   inkscape:version="0.48.3.1 r9886"
+   sodipodi:docname="gtg-need-attention.svg">
+  <defs
+     id="defs8" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="753"
+     inkscape:window-height="480"
+     id="namedview6"
+     showgrid="false"
+     inkscape:zoom="14.75"
+     inkscape:cx="8"
+     inkscape:cy="5.9661017"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="svg3212" />
+  <metadata
+     id="metadata13">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <path
+     id="path3208"
+     d="m2 2c-1.108 0-2 0.892-2 2v10c0 1.108 0.892 2 2 2h12c1.108 0 2-0.892 2-2v-10c0-1.108-0.892-2-2-2h-12zm9.0312 2c0.05517 0.0053 0.13563 0.03589 0.1875 0.0625l1.5625 0.78125c0.2075 0.10644 0.26963 0.36771 0.15625 0.5625l-4.875 8.375c-0.0853 0.149-0.2671 0.239-0.436 0.219-0.0517-0.006-0.1076-0.006-0.1562-0.031-0.0052-0.003-4.2501-3.313-4.25-3.313-0.2075-0.106-0.2697-0.367-0.1563-0.562l1.0312-1.25c0.1134-0.195 0.3863-0.2629 0.5938-0.1565l2.3125 1.7505 3.625-6.2192c0.085-0.1461 0.241-0.2346 0.406-0.2188z"
+     style="opacity:.3" />
+  <path
+     id="rect2386"
+     d="m2 1c-1.108 0-2 0.892-2 2v10c0 1.108 0.892 2 2 2h12c1.108 0 2-0.892 2-2v-10c0-1.108-0.892-2-2-2h-12zm9.0312 2c0.05517 0.0053 0.13563 0.03589 0.1875 0.0625l1.5625 0.78125c0.2075 0.10644 0.26963 0.36771 0.15625 0.5625l-4.875 8.375c-0.0853 0.149-0.2671 0.239-0.436 0.219-0.0517-0.006-0.1076-0.006-0.1562-0.031-0.0052-0.003-4.2501-3.3128-4.25-3.3128-0.2075-0.1064-0.2697-0.3677-0.1563-0.5624l1.0312-1.25c0.1134-0.1948 0.3863-0.2627 0.5938-0.1563l2.3125 1.75 3.625-6.2187c0.085-0.1461 0.241-0.2346 0.406-0.2188z"
+     style="fill:#1cbbed;fill-opacity:1" />
+</svg>

=== modified file 'GTG/plugins/notification_area/notification_area.py'
--- GTG/plugins/notification_area/notification_area.py	2012-04-22 17:05:12 +0000
+++ GTG/plugins/notification_area/notification_area.py	2012-05-25 22:47:20 +0000
@@ -25,7 +25,76 @@
 
 from GTG                   import _
 from GTG.tools.borg        import Borg
-
+from GTG.tools.dates       import Date
+
+# Determine how many days are left to do a task, 1 means due today.
+def _due_within(task, danger_zone):
+    ddate = task.get_due_date()
+    if (ddate != Date.no_date()):
+        if ddate.days_left() < danger_zone:
+            return True
+    return False
+
+class _Attention:
+
+    """
+    Define need attention state depending on whether there 
+    are tasks in danger zone.
+
+    There are two levels of attention:
+    0 = "relax": there are no tasks in danger zone
+    1 = "attention": there is at least one task in danger zone
+
+    A task is in danger zone if the number of days left is less
+    than time span (in days) defined by danger_zone.
+    """
+
+    ICONS = {'relax'     : 'gtg',
+             'attention' : 'gtg-need-attention'}
+
+    def __init__(self, tree, req, danger_zone=1):
+        self.__tree = tree 
+        self.__req = req
+        self.danger_zone = danger_zone
+        # Maintain a list of tasks in danger zone, use task id
+        self.tasks_danger = []
+        for tid in self.__tree.get_all_nodes():
+            task = self.__req.get_task(tid)
+            if _due_within(task, self.danger_zone):
+                self.tasks_danger.append(tid)
+
+    def level(self):
+        return 0 if len(self.tasks_danger)==0 else 1
+
+    def __update_indicator(self, indicator, old_level, new_level):
+        if old_level == 1 and new_level == 0:
+            indicator.set_icon(self.ICONS['relax'])
+        elif old_level == 0 and new_level == 1:
+            indicator.set_icon(self.ICONS['attention'])
+          
+    def update_on_task_modified(self, tid, indicator):
+        # Store current attention level
+        old_lev = self.level()
+        task = self.__req.get_task(tid)
+        if tid in self.tasks_danger:
+            if not _due_within(task, self.danger_zone):
+                self.tasks_danger.remove(tid)
+        else:
+            if _due_within(task, self.danger_zone):
+                self.tasks_danger.append(tid)
+
+        # Update icon only if attention level has changed
+        self.__update_indicator(indicator, old_lev, self.level())
+              
+    def update_on_task_deleted(self, tid, indicator):
+        # Store current attention level
+        old_lev = self.level()
+
+        if tid in self.tasks_danger:
+            self.tasks_danger.remove(tid)
+
+        # Update icon only if attention level has changed
+        self.__update_indicator(indicator, old_lev, self.level())
 
 class NotificationArea:
     """
@@ -33,7 +102,8 @@
     to quickly access tasks.
     """
 
-    DEFAULT_PREFERENCES = {"start_minimized": False}
+    DEFAULT_PREFERENCES = {"start_minimized": False,
+                           "danger_zone"    : 1}
     PLUGIN_NAME = "notification_area"
     MAX_TITLE_LEN = 30
     MAX_ITEMS = 10
@@ -76,12 +146,26 @@
         # them given the task id. Contains tuple of this format:
         # (title, key, gtk.MenuItem)
         self.__init_gtk()
-        self.__connect_to_tree()
 
-        #Load the preferences
+        # We load preferences before connecting to tree
         self.preference_dialog_init()
         self.preferences_load()
 
+        # Enable attention monitor.
+        # Request a new view so we do not influence anybody.
+        self.__tree_att = self.__requester.get_tasks_tree()
+        self.__tree_att = \
+            self.__tree_att.get_basetree().get_viewtree(refresh=False)
+        # Convention: if danger zone is <=0, disable attention
+        if self.preferences['danger_zone'] > 0:
+            self.__attention = _Attention(self.__tree_att, 
+                                          self.__requester,
+                                          self.preferences['danger_zone'])
+        else:
+            self.__attention = None
+
+        self.__connect_to_tree()
+
         # When no windows (browser or text editors) are shown, it tries to quit
         # With hidden browser and closing the only single text editor,
         # GTG would quit no matter what
@@ -195,6 +279,10 @@
         self.__tree.apply_filter('workview')
 
     def __on_task_added(self, tid, path):
+        # Update icon on modification
+        if self.__attention and self.__indicator:
+            self.__attention.update_on_task_modified(tid, self.__indicator)
+
         self.__task_separator.show()
         task = self.__requester.get_task(tid)
         if task is None:
@@ -212,6 +300,10 @@
             self.__indicator.set_menu(self.__menu)
 
     def __on_task_deleted(self, tid, path):
+        # Update icon on deletion
+        if self.__attention and self.__indicator:
+            self.__attention.update_on_task_deleted(tid, self.__indicator)
+
         self.__tasks_menu.remove(tid)
         if self.__tasks_menu.empty():
             self.__task_separator.hide()
@@ -234,10 +326,11 @@
     def preferences_load(self):
         data = self.__plugin_api.load_configuration_object(self.PLUGIN_NAME,
                                                          "preferences")
-        if not data or not isinstance(data, dict):
-            self.preferences = self.DEFAULT_PREFERENCES
-        else:
-            self.preferences = data
+        # We first load the preferences then update the dict
+        # This way new default options are recognized with old cfg files
+        self.preferences = self.DEFAULT_PREFERENCES
+        if isinstance(data, dict):
+            self.preferences.update(data)
 
     def preferences_store(self):
         self.__plugin_api.save_configuration_object(self.PLUGIN_NAME,
@@ -255,6 +348,8 @@
                     "notification_area.ui"))
         self.preferences_dialog = self.builder.get_object("preferences_dialog")
         self.chbox_minimized = self.builder.get_object("pref_chbox_minimized")
+        self.spinbutton_dangerzone = \
+            self.builder.get_object("pref_spinbutton_dangerzone")
         SIGNAL_CONNECTIONS_DIC = {
             "on_preferences_dialog_delete_event":
                 self.on_preferences_cancel,
@@ -267,6 +362,7 @@
 
     def configure_dialog(self, manager_dialog):
         self.chbox_minimized.set_active(self.preferences["start_minimized"])
+        self.spinbutton_dangerzone.set_value(self.preferences["danger_zone"])
         self.preferences_dialog.show_all()
         self.preferences_dialog.set_transient_for(manager_dialog)
 
@@ -276,6 +372,7 @@
 
     def on_preferences_ok(self, widget = None, data = None):
         self.preferences["start_minimized"] = self.chbox_minimized.get_active()
+        self.preferences["danger_zone"] = self.spinbutton_dangerzone.get_value()
         self.preferences_store()
         self.preferences_dialog.hide()
 

=== modified file 'GTG/plugins/notification_area/notification_area.ui'
--- GTG/plugins/notification_area/notification_area.ui	2012-03-05 15:23:05 +0000
+++ GTG/plugins/notification_area/notification_area.ui	2012-05-25 22:47:20 +0000
@@ -1,53 +1,113 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <requires lib="gtk+" version="2.16"/>
   <!-- interface-naming-policy toplevel-contextual -->
   <object class="GtkAccelGroup" id="accelgroup1"/>
+  <object class="GtkAdjustment" id="adjustment_dangerzone">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
   <object class="GtkWindow" id="preferences_dialog">
+    <property name="can_focus">False</property>
     <property name="border_width">10</property>
     <property name="window_position">center-on-parent</property>
     <property name="type_hint">dialog</property>
-    <signal name="delete_event" handler="on_preferences_dialog_delete_event"/>
+    <signal name="delete-event" handler="on_preferences_dialog_delete_event" swapped="no"/>
     <child>
       <object class="GtkVBox" id="vbox1">
         <property name="visible">True</property>
-        <property name="orientation">vertical</property>
+        <property name="can_focus">False</property>
         <child>
           <object class="GtkLabel" id="label1">
             <property name="visible">True</property>
+            <property name="can_focus">False</property>
             <property name="ypad">7</property>
             <property name="label" translatable="yes">Personalize your notification area plugin</property>
           </object>
           <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
             <property name="position">0</property>
           </packing>
         </child>
         <child>
           <object class="GtkVBox" id="vbox2">
             <property name="visible">True</property>
-            <property name="orientation">vertical</property>
+            <property name="can_focus">False</property>
             <child>
               <object class="GtkCheckButton" id="pref_chbox_minimized">
                 <property name="label" translatable="yes">Start gtg minimized</property>
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">False</property>
+                <property name="use_action_appearance">False</property>
                 <property name="image_position">top</property>
                 <property name="draw_indicator">True</property>
               </object>
               <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
                 <property name="position">0</property>
               </packing>
             </child>
           </object>
           <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
             <property name="position">1</property>
           </packing>
         </child>
         <child>
+          <object class="GtkHBox" id="hbox2">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <child>
+              <object class="GtkSpinButton" id="pref_spinbutton_dangerzone">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="invisible_char">●</property>
+                <property name="width_chars">2</property>
+                <property name="primary_icon_activatable">False</property>
+                <property name="secondary_icon_activatable">False</property>
+                <property name="primary_icon_sensitive">True</property>
+                <property name="secondary_icon_sensitive">True</property>
+                <property name="adjustment">adjustment_dangerzone</property>
+                <property name="numeric">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label2">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="has_tooltip">True</property>
+                <property name="tooltip_text" translatable="yes">The icon in the notification area is colored if there are tasks in the danger zone. 
+A danger zone of 0 disables icon coloring.</property>
+                <property name="label" translatable="yes"> Danger zone (in days) </property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child>
           <object class="GtkHBox" id="hbox1">
             <property name="height_request">30</property>
             <property name="visible">True</property>
+            <property name="can_focus">False</property>
             <property name="spacing">50</property>
             <child>
               <object class="GtkButton" id="btn_preferences_cancel">
@@ -55,10 +115,13 @@
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
+                <property name="use_action_appearance">False</property>
                 <property name="use_stock">True</property>
-                <signal name="clicked" handler="on_btn_preferences_cancel_clicked"/>
+                <signal name="clicked" handler="on_btn_preferences_cancel_clicked" swapped="no"/>
               </object>
               <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
                 <property name="position">0</property>
               </packing>
             </child>
@@ -68,16 +131,21 @@
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
+                <property name="use_action_appearance">False</property>
                 <property name="use_stock">True</property>
-                <signal name="clicked" handler="on_btn_preferences_ok_clicked"/>
+                <signal name="clicked" handler="on_btn_preferences_ok_clicked" swapped="no"/>
               </object>
               <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
                 <property name="position">1</property>
               </packing>
             </child>
           </object>
           <packing>
-            <property name="position">2</property>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">3</property>
           </packing>
         </child>
       </object>