Merge lp:~izidor/gtg/bug1074140 into lp:~gtg/gtg/old-trunk

Proposed by Izidor Matušov on 2012-11-02
Status: Merged
Merged at revision: 1239
Proposed branch: lp:~izidor/gtg/bug1074140
Merge into: lp:~gtg/gtg/old-trunk
Diff against target: 346 lines (+123/-99)
1 file modified
GTG/plugins/notification_area/notification_area.py (+123/-99)
To merge this branch: bzr merge lp:~izidor/gtg/bug1074140
Reviewer Review Type Date Requested Status
Bertrand Rousseau (community) 2012-11-02 Approve on 2012-11-02
Review via email: mp+132654@code.launchpad.net

Description of the change

A patch to run notification area plugin even for systems without appindicator, e.g. Archlinux's XFCE4.

To post a comment you must log in.

I ran your code under Ubuntu 12.10/GNOME Shell, and could see the notification icon when activating the notification area plugin. So everything ok from that PoV.

Did you test it against Unity?

review: Needs Information

following discussion on IRC, it appears to have been tested on unity/xfce, so I approve

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== removed directory 'GTG/plugins/notification_area/data'
2=== removed directory 'GTG/plugins/notification_area/data/icons'
3=== removed directory 'GTG/plugins/notification_area/data/icons/hicolor'
4=== removed directory 'GTG/plugins/notification_area/data/icons/hicolor/22x22'
5=== removed directory 'GTG/plugins/notification_area/data/icons/hicolor/22x22/apps'
6=== modified file 'GTG/plugins/notification_area/notification_area.py'
7--- GTG/plugins/notification_area/notification_area.py 2012-07-18 12:09:41 +0000
8+++ GTG/plugins/notification_area/notification_area.py 2012-11-02 07:54:58 +0000
9@@ -24,11 +24,109 @@
10 pass
11
12 from GTG import _
13-from GTG import PLUGIN_DIR
14 from GTG.tools.borg import Borg
15 from GTG.tools.dates import Date
16
17
18+class TheIndicator(Borg):
19+ """
20+ Application indicator can be instantiated only once. The
21+ plugin api, when toggling the activation state of a plugin,
22+ instantiates different objects from the plugin class. Therefore,
23+ we need to keep a reference to the indicator object. This class
24+ does that.
25+ """
26+
27+ def __init__(self):
28+ super(TheIndicator, self).__init__()
29+ if not hasattr(self, "_indicator"):
30+ try:
31+ self._indicator = appindicator.Indicator( \
32+ "gtg",
33+ "indicator-messages",
34+ appindicator.CATEGORY_APPLICATION_STATUS)
35+ except:
36+ self._indicator = None
37+
38+ def get_indicator(self):
39+ return self._indicator
40+
41+
42+class IconIndicator:
43+ """
44+ A common interface to an app indicator and a status icon
45+ """
46+
47+ NORMAL_ICON = "gtg"
48+ ATTENTION_ICON = "gtg_need_attention"
49+
50+ def __init__(self):
51+ self._indicator = TheIndicator().get_indicator()
52+ self._icon = None
53+ self._menu = None
54+ self._attention = False
55+
56+ def activate(self, leftbtn_callback, menu):
57+ """ Setup the icon / the indicator """
58+
59+ self._menu = menu
60+
61+ if self._indicator:
62+ self._indicator.set_icon("gtg-panel")
63+ self._indicator.set_attention_icon(self.ATTENTION_ICON)
64+ self._indicator.set_menu(menu)
65+ self._indicator.set_status(appindicator.STATUS_ACTIVE)
66+ else:
67+ self._icon = gtk.StatusIcon()
68+ self._icon.set_from_icon_name(self.NORMAL_ICON)
69+ self._icon.set_tooltip("Getting Things GNOME!")
70+ self._icon.set_visible(True)
71+ self._icon.connect('activate', leftbtn_callback)
72+ self._icon.connect('popup-menu', self._on_icon_popup)
73+
74+ def deactivate(self):
75+ """ Hide the icon """
76+ if self._indicator:
77+ self._indicator.set_status(appindicator.STATUS_PASSIVE)
78+ else:
79+ self._icon.set_visible(False)
80+
81+ def update_menu(self):
82+ """ Force indicator to update menu """
83+ if self._indicator:
84+ self._indicator.set_menu(self._menu)
85+
86+ def set_attention(self, attention):
87+ """ Show a special icon when the indicator needs attention """
88+ # Change icon only when the attention change
89+ if self._attention == attention:
90+ return
91+
92+ if self._indicator:
93+ if attention:
94+ status = appindicator.STATUS_ATTENTION
95+ else:
96+ status = appindicator.STATUS_ACTIVE
97+
98+ self._indicator.set_status(status)
99+ else:
100+ if attention:
101+ icon = self.ATTENTION_ICON
102+ else:
103+ icon = self.NORMAL_ICON
104+
105+ self._icon.set_from_icon_name(icon)
106+
107+ self._attention = attention
108+
109+ def _on_icon_popup(self, icon, button, timestamp):
110+ """ Show the menu on right click on the icon """
111+ if not self._indicator:
112+ self._menu.popup(None, None, gtk.status_icon_position_menu,
113+ button, timestamp, icon)
114+
115+
116+
117 def _due_within(task, danger_zone):
118 """
119 Determine if a task is the danger zone.
120@@ -55,16 +153,10 @@
121 than time span (in days) defined by danger_zone.
122 """
123
124- STATUS = {'normal': appindicator.STATUS_ACTIVE,
125- 'high': appindicator.STATUS_ATTENTION}
126-
127- ICON = {'normal': 'gtg-panel',
128- 'high': 'gtg_need_attention'}
129-
130 def __init__(self, danger_zone, indicator, tree, req):
131 self.__tree = tree
132 self.__req = req
133- self.__indicator = indicator
134+ self._indicator = indicator
135 self.danger_zone = danger_zone
136
137 # Setup list of tasks in danger zone
138@@ -76,27 +168,13 @@
139 self.tasks_danger.append(tid)
140
141 # Set initial status
142- self.__update_indicator(self.level())
143-
144- def level(self):
145- """ Two states only: attention is either needed or not """
146- return 'high' if len(self.tasks_danger)>0 else 'normal'
147-
148- def __update_indicator(self, new, old=None):
149- """ Reset indicator status or update upon change in status """
150- if old is None or not old == new:
151- try:
152- # This works if __indicator implements the appindicator api
153- self.__indicator.set_status(self.STATUS[new])
154- except AttributeError:
155- # If we passed a status icon instead try this
156- self.__indicator.set_from_icon_name(self.ICON[new])
157- except:
158- raise
159+ self._update_indicator()
160+
161+ def _update_indicator(self):
162+ """ Set the proper icon for the indicator """
163+ self._indicator.set_attention(len(self.tasks_danger) > 0)
164
165 def update_on_task_modified(self, tid):
166- # Store current attention level
167- old_lev = self.level()
168 task = self.__req.get_task(tid)
169 if tid in self.tasks_danger:
170 if not _due_within(task, self.danger_zone):
171@@ -104,19 +182,14 @@
172 else:
173 if _due_within(task, self.danger_zone):
174 self.tasks_danger.append(tid)
175-
176- # Update icon only if attention level has changed
177- self.__update_indicator(self.level(), old_lev)
178+
179+ self._update_indicator()
180
181 def update_on_task_deleted(self, tid):
182- # Store current attention level
183- old_lev = self.level()
184-
185 if tid in self.tasks_danger:
186 self.tasks_danger.remove(tid)
187
188- # Update icon only if attention level has changed
189- self.__update_indicator(self.level(), old_lev)
190+ self._update_indicator()
191
192
193 class NotificationArea:
194@@ -131,31 +204,9 @@
195 MAX_TITLE_LEN = 30
196 MAX_ITEMS = 10
197
198- class TheIndicator(Borg):
199- """
200- Application indicator can be instantiated only once. The
201- plugin api, when toggling the activation state of a plugin,
202- instantiates different objects from the plugin class. Therefore,
203- we need to keep a reference to the indicator object. This class
204- does that.
205- """
206-
207- def __init__(self):
208- super(NotificationArea.TheIndicator, self).__init__()
209- if not hasattr(self, "_indicator"):
210- try:
211- self._indicator = appindicator.Indicator( \
212- "gtg",
213- "indicator-messages",
214- appindicator.CATEGORY_APPLICATION_STATUS)
215- except:
216- self._indicator = None
217-
218- def get_indicator(self):
219- return self._indicator
220
221 def __init__(self):
222- self.__indicator = NotificationArea.TheIndicator().get_indicator()
223+ self._indicator = IconIndicator()
224 self.__browser_handler = None
225 self.__liblarch_callbacks = []
226
227@@ -203,10 +254,7 @@
228
229 def deactivate(self, plugin_api):
230 """ Set everything back to normal """
231- if self.__indicator:
232- self.__indicator.set_status(appindicator.STATUS_PASSIVE)
233- else:
234- self.status_icon.set_visible(False)
235+ self._indicator.deactivate()
236
237 # Allow to close browser after deactivation
238 self.__set_browser_close_callback(None)
239@@ -224,13 +272,13 @@
240 def __init_gtk(self):
241 browser = self.__view_manager.get_browser()
242
243- self.__menu = gtk.Menu()
244+ menu = gtk.Menu()
245
246 #add "new task"
247 menuItem = gtk.ImageMenuItem(gtk.STOCK_ADD)
248 menuItem.get_children()[0].set_label(_('Add _New Task'))
249 menuItem.connect('activate', self.__open_task)
250- self.__menu.append(menuItem)
251+ menu.append(menuItem)
252
253 #view in main window checkbox
254 view_browser_checkbox = gtk.CheckMenuItem(_("_View Main Window"))
255@@ -239,48 +287,29 @@
256 self.__toggle_browser)
257 browser.connect('visibility-toggled', self.__on_browser_toggled,
258 view_browser_checkbox)
259- self.__menu.append(view_browser_checkbox)
260+ menu.append(view_browser_checkbox)
261 self.checkbox = view_browser_checkbox
262
263 #separator (it's intended to be after show_all)
264 # separator should be shown only when having tasks
265 self.__task_separator = gtk.SeparatorMenuItem()
266- self.__menu.append(self.__task_separator)
267- self.__menu_top_length = len(self.__menu)
268+ menu.append(self.__task_separator)
269+ menu_top_length = len(menu)
270
271- self.__menu.append(gtk.SeparatorMenuItem())
272+ menu.append(gtk.SeparatorMenuItem())
273
274 #quit item
275 menuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT)
276 menuItem.connect('activate', self.__view_manager.close_browser)
277- self.__menu.append(menuItem)
278+ menu.append(menuItem)
279
280- self.__menu.show_all()
281+ menu.show_all()
282 self.__task_separator.hide()
283
284 self.__tasks_menu = SortedLimitedMenu(self.MAX_ITEMS,
285- self.__menu, self.__menu_top_length)
286-
287- # Update the icon theme
288- icon_theme = os.path.join('notification_area', 'data', 'icons')
289- abs_theme_path = os.path.join(PLUGIN_DIR[0], icon_theme)
290- theme = gtk.icon_theme_get_default()
291- theme.append_search_path(abs_theme_path)
292-
293- if self.__indicator:
294- self.__indicator.set_icon_theme_path(abs_theme_path)
295- self.__indicator.set_icon("gtg-panel")
296- self.__indicator.set_attention_icon("gtg_need_attention")
297- self.__indicator.set_menu(self.__menu)
298- self.__indicator.set_status(appindicator.STATUS_ACTIVE)
299- else:
300- self.status_icon = gtk.StatusIcon()
301- self.status_icon.set_from_icon_name("gtg-panel")
302- self.status_icon.set_tooltip("Getting Things Gnome!")
303- self.status_icon.set_visible(True)
304- self.status_icon.connect('activate', self.__toggle_browser)
305- self.status_icon.connect('popup-menu',
306- self.__on_icon_popup, self.__menu)
307+ menu, menu_top_length)
308+
309+ self._indicator.activate(self.__toggle_browser, menu)
310
311 def __init_attention(self):
312 # Use two different viewtree for attention and menu
313@@ -290,7 +319,7 @@
314 if self.preferences['danger_zone'] > 0:
315 self.__attention = _Attention( \
316 self.preferences['danger_zone'],
317- self.__indicator if self.__indicator else self.status_icon,
318+ self._indicator,
319 self.__tree_att,
320 self.__requester)
321 else:
322@@ -340,8 +369,7 @@
323 menu_item.connect('activate', self.__open_task, tid)
324 self.__tasks_menu.add(tid, (task.get_due_date(), title), menu_item)
325
326- if self.__indicator:
327- self.__indicator.set_menu(self.__menu)
328+ self._indicator.update_menu()
329
330 def __on_task_deleted_att(self, tid, path):
331 # Update icon on deletion
332@@ -362,10 +390,6 @@
333 short_title = short_title.strip() + "..."
334 return short_title
335
336- def __on_icon_popup(self, icon, button, timestamp, menu=None):
337- if not self.__indicator:
338- menu.popup(None, None, gtk.status_icon_position_menu, \
339- button, timestamp, icon)
340
341 ### Preferences methods #######################################################
342 def preferences_load(self):
343
344=== renamed file 'GTG/plugins/notification_area/data/icons/hicolor/22x22/apps/gtg_need_attention.png' => 'data/icons/hicolor/22x22/apps/gtg_need_attention.png'
345=== renamed directory 'GTG/plugins/notification_area/data/icons/ubuntu-mono-dark' => 'data/icons/ubuntu-mono-dark'
346=== renamed directory 'GTG/plugins/notification_area/data/icons/ubuntu-mono-light' => 'data/icons/ubuntu-mono-light'

Subscribers

People subscribed via source and target branches

to status/vote changes: