Merge lp:~rafalcieslak256/ubuntu-accomplishments-viewer/uav-display-modes into lp:ubuntu-accomplishments-viewer

Proposed by Rafał Cieślak
Status: Merged
Merged at revision: 209
Proposed branch: lp:~rafalcieslak256/ubuntu-accomplishments-viewer/uav-display-modes
Merge into: lp:ubuntu-accomplishments-viewer
Diff against target: 1635 lines (+702/-460)
3 files modified
Changelog (+5/-0)
accomplishments_viewer/AccomplishmentsViewerWindow.py (+499/-383)
data/ui/AccomplishmentsViewerWindow.ui (+198/-77)
To merge this branch: bzr merge lp:~rafalcieslak256/ubuntu-accomplishments-viewer/uav-display-modes
Reviewer Review Type Date Requested Status
Matt Fischer Approve
Review via email: mp+133583@code.launchpad.net

Description of the change

This branch introduces lighting-fast filtering (fixes: #1076574), as well as search feature (#1076561) (which are actually fairly related).

It saves lots of time by not recreating treemodels everytime the filters change. Instead, this implementation makes heavy use of GtkTreeModelFilters. There are just two treemodels used (one for opportunities, one for trophies), and there are several overlay filters applied for them. As filter parameters change, we ask GTK to check which rows should be hidden. GTK calls our functions to check that, they simply return true/false having checked for desired conditions.

Another mass efficiency gain is from dropping that terrible update_views function, which used to recreate all data we had from scratch, and is was called far too frequently. I solved that by implementing set_display(...) function, that takes a lot of optional arguments which are used to set new filter details. Usually called with just one parameter that has to be changed in the filter, it determines how to apply that change, and what will need refiltering. It also hides unnecesary UI elements. It's quite elegant, and very clear to use from many other places in code. It's also easily extensible, if one day we'll want to add a 'welcome' page, or implement help within the main window, it will be a piece of cake.

The search bar UI design is a suggestion, we may want to reorder the entry field with label, place them horisontally etc. Note that searching works for both opportunities and trophies (I find it most amazing in the "latest trophies view", as searching through it may cause some groups of accomplishments to appear/hide).

I have tested this branch carefully on both Quantal and Precise. I might have missed some UX details, let me know about any concerns you have and we'll discuss solutions.

To post a comment you must log in.
Revision history for this message
Matt Fischer (mfisch) wrote :

I don't claim to understand all the GTK stuff here, but the theory makes sense to me. I do have a few questions.

What about the XXX on line 178? I don't follow what the optimization is?

Q: Why is the code in on_window_resized() all moved into a string?

I see this note about making these global, I think they'd make sense as global:
951 + today = datetime.date.today()
952 + margin_today = datetime.timedelta(days = 1)
953 + margin_week = datetime.timedelta(days = 7)
954 + margin_month = datetime.timedelta(days = 31)
955 + margin_sixmonths = datetime.timedelta(days = 180)

Why did you remove all these lines? What did they do?
 <property name="use_action_appearance">False</property>

Revision history for this message
Rafał Cieślak (rafalcieslak256) wrote :

Great thanks for taking time to look as this, Matt! :)

To answer your questions:

The XXX note in this diff's line 178 is an annotation for following prepare_models() call. The prepare_models() function is the only time-consuming bit now, as it initializes both treemodels. The call in line 178 is within function that runs when a new trophy is received. The point is that it makes no sense to clear both treemodels and recreating them from scratch when a new trophy is awarded, instead, we might just remove that new trophy from opprotunities list and add it to mytrophies. We do know the accomID of that new trophy, as daemon provides us with that data, so it's just a matter of implementing support for such action. It would be just faster.

Code in on_window_resized() is moved to a string because I wanted to remove it, but moving it to a comment looked to me as a more appropriate way of disabling it, so that the original code is left in case we wanted to reuse it some time. Currently it's not needed, as GTK now correctly manages iconview's width, and we don't need any silly workarounds to have the window properly resizing. Moreover, that trick we used as a workaround based on clearing and recreating all treemodels - and that's what this branch wants to avoid, since it's a very time-consuming operation.

I have not changed any of the "use_action_appearance", I am pretty sure Glade has edited that on it's own. Most likely a new version of glade finally recognizes that there is not much sense in specifying explicitly that "use_action_appearance" should be false, as that's GTK's default behavior. Thus, the removal of these lines shouldn't change anything.

If you want to, I can provide some more detail on how the GTK stuff works here.

218. By Rafał Cieślak

Using GtkSearchEntry instead of GtkEntry for the searchbar, it's much more intuitive this way

Revision history for this message
Matt Fischer (mfisch) wrote :

I think you should just remove the on_window_resized code, we can get it back via bzr history if we need to, otherwise it looks good. I don't figure you're going to get an in depth review from Jono anytime soon, maybe Michael Hall could review it? Personally I think you should just push it in, we'll need to do extensive testing on this code before 0.4 anyway and now is the time for major changes like this.

review: Approve
Revision history for this message
Rafał Cieślak (rafalcieslak256) wrote :

Thank you for the review. I'll push these changes, it's the first major thing for 0.4 so we'll have lots of time to test it afterwards.

219. By Rafał Cieślak

Finalising fix; added changelog entry and ironed out minor cosmetics

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Changelog'
2--- Changelog 2012-10-21 03:17:20 +0000
3+++ Changelog 2012-11-16 07:15:23 +0000
4@@ -18,6 +18,11 @@
5
6 Next Version Target: 0.4
7
8+ [Rafał Cieślak]
9+ * Added search feature (LP: #1076561)
10+ * Significantly improved filtering performance and responsivness (LP: #1076574)
11+ * Reorganised code that filters trophies, now using GtkTreeModelFilters.
12+
13 [Matt Fischer]
14 * Fix lintian issues
15 * Added a manpage (LP: #1069264)
16
17=== modified file 'accomplishments_viewer/AccomplishmentsViewerWindow.py'
18--- accomplishments_viewer/AccomplishmentsViewerWindow.py 2012-09-13 19:19:11 +0000
19+++ accomplishments_viewer/AccomplishmentsViewerWindow.py 2012-11-16 07:15:23 +0000
20@@ -66,9 +66,32 @@
21 COL_LOCKED = 3
22 COL_COLLECTION = 4
23 COL_ID = 5
24-
25-MYTROPHIES_FILTER_ALL = 0
26-MYTROPHIES_FILTER_LATEST = 1
27+COL_DATE_ACCOMPLISHED = 6
28+COL_CATEGORIES = 7
29+
30+MYTROPHIES_FILTER_UNSPECIFIED = 0
31+MYTROPHIES_FILTER_ALL = 1
32+MYTROPHIES_FILTER_LATEST = 2
33+
34+DISPLAY_MODE_UNSPECIFIED = 0
35+DISPLAY_MODE_DETAILS = 1
36+DISPLAY_MODE_TROPHIES = 2
37+DISPLAY_MODE_OPPORTUNITIES = 3
38+
39+DISPLAY_FILTER_LOCKED_UNSPECIFIED = 0
40+DISPLAY_FILTER_LOCKED_SHOW = 1
41+DISPLAY_FILTER_LOCKED_HIDE = 2
42+
43+DISPLAY_FILTER_COLLECTION_UNSPECIFIED = 0
44+DISPLAY_FILTER_CATEGORY_UNSPECIFIED = 0
45+DISPLAY_FILTER_SUBCAT_UNSPECIFIED = 0
46+DISPLAY_FILTER_SEARCH_UNSPECIFIED = 0
47+
48+TROPHIES_FILTER_TODAY = 1
49+TROPHIES_FILTER_WEEK = 2
50+TROPHIES_FILTER_MONTH = 3
51+TROPHIES_FILTER_SIXMONTHS = 4
52+TROPHIES_FILTER_EARLIER = 100
53
54 TROPHY_GALLERY_URL = 'http://213.138.100.229:8000'
55
56@@ -88,9 +111,19 @@
57 self.EditExtraDialog.parent = self
58 self.curr_height = 0
59 self.curr_width = 0
60- self.do_not_react_on_cat_changes = False
61- self.mytrophies_filtermode = MYTROPHIES_FILTER_ALL
62- self.mytrophies_store_all = []
63+
64+ # Following variables store current display settings.
65+ self.display_mytrophies_filtermode = MYTROPHIES_FILTER_ALL
66+ self.display_mode = DISPLAY_MODE_OPPORTUNITIES
67+ self.display_filter_locked = DISPLAY_FILTER_LOCKED_SHOW
68+ self.display_filter_collection = ""
69+ self.display_filter_category = ""
70+ self.display_filter_subcat = ""
71+ self.display_filter_search = ""
72+
73+ # These two store list of pairs [filter, iconview] for all accomplishment groups in mytrophies view.
74+ self.trophies_collection_filters = []
75+ self.mytrophies_latest_boxes = []
76 # Code for other initialization actions should be added here.
77
78
79@@ -142,10 +175,16 @@
80 self.subcats_forward = self.builder.get_object("subcats_forward")
81 self.subcats_buttonbox = self.builder.get_object("subcats_buttonbox")
82 self.subcats_container = self.builder.get_object("subcats_container")
83- self.mytrophies_mainbox = self.builder.get_object("mytrophies_mainbox")
84 self.mytrophies_filter_latest = self.builder.get_object("mytrophies_filter_latest")
85 self.mytrophies_filter_all = self.builder.get_object("mytrophies_filter_all")
86 self.opp_frame = self.builder.get_object("opp_frame")
87+ self.mytrophies_box_latest = self.builder.get_object("mytrophies_box_latest")
88+ self.mytrophies_box_all = self.builder.get_object("mytrophies_box_all")
89+ self.mytrophies_box_latest_window = self.builder.get_object("mytrophies_box_latest_window")
90+ self.mytrophies_box_all_window = self.builder.get_object("mytrophies_box_all_window")
91+ self.mytrophies_notebook = self.builder.get_object("mytrophies_notebook")
92+ self.searchbar = self.builder.get_object("searchbar")
93+ self.searchbar_box = self.builder.get_object("searchbar_box")
94
95 # don't display the sub-cats scrollbars
96 sb_h = self.subcats_scroll.get_hscrollbar()
97@@ -161,38 +200,34 @@
98 context = self.toolbar.get_style_context()
99 context.add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR)
100
101- # create the stores used by the IconViews in the Latest View
102-
103- self.mytrophies_filter_today = Gtk.ListStore(str, GdkPixbuf.Pixbuf, bool, bool, str, str) # title, icon accomplished, locked, col, accomplishment
104- self.mytrophies_filter_today.set_sort_column_id(COL_TITLE, Gtk.SortType.ASCENDING)
105-
106- self.mytrophies_filter_week = Gtk.ListStore(str, GdkPixbuf.Pixbuf, bool, bool, str, str) # title, icon accomplished, locked, col, accomplishment
107- self.mytrophies_filter_week.set_sort_column_id(COL_TITLE, Gtk.SortType.ASCENDING)
108-
109- self.mytrophies_filter_month = Gtk.ListStore(str, GdkPixbuf.Pixbuf, bool, bool, str, str) # title, icon accomplished, locked, col, accomplishment
110- self.mytrophies_filter_month.set_sort_column_id(COL_TITLE, Gtk.SortType.ASCENDING)
111-
112- self.mytrophies_filter_sixmonths = Gtk.ListStore(str, GdkPixbuf.Pixbuf, bool, bool, str, str) # title, icon accomplished, locked, col, accomplishment
113- self.mytrophies_filter_sixmonths.set_sort_column_id(COL_TITLE, Gtk.SortType.ASCENDING)
114-
115- self.mytrophies_filter_earlier = Gtk.ListStore(str, GdkPixbuf.Pixbuf, bool, bool, str, str) # title, icon accomplished, locked, col, accomplishment
116- self.mytrophies_filter_earlier.set_sort_column_id(COL_TITLE, Gtk.SortType.ASCENDING)
117-
118-
119- self.oppstore = Gtk.ListStore(str, GdkPixbuf.Pixbuf, bool, bool, str, str) # title, icon, accomplished, locked, col, accomplishment
120+ # Create stores and corelated filters
121+
122+ self.oppstore = Gtk.ListStore(str, GdkPixbuf.Pixbuf, bool, bool, str, str, str, str) # title, icon, accomplished, locked, col, accomplishment, date-accomplished, categories
123 self.oppstore.set_sort_column_id(COL_TITLE, Gtk.SortType.ASCENDING)
124- self.opp_icon.set_model(self.oppstore)
125-
126- #self.trophy_icon.set_text_column(COL_TITLE)
127- #self.trophy_icon.set_pixbuf_column(COL_PIXBUF)
128-
129+ self.oppstore_filtered = self.oppstore.filter_new()
130+ # The following sets the function for tree model filter. That function has
131+ # to return true if a given row has to be visible. This way we can control
132+ # which opportunities are displayed, and which are not.
133+ self.oppstore_filtered.set_visible_func(self._opp_visible_func)
134+
135+ self.trophiesstore = Gtk.ListStore(str, GdkPixbuf.Pixbuf, bool, bool, str, str, str, str) # title, icon, accomplished, locked, col, accomplishment, date-accomplished, categories
136+ self.trophiesstore.set_sort_column_id(COL_TITLE, Gtk.SortType.ASCENDING)
137+ self.trophiesstore_filter_today = self.trophiesstore.filter_new()
138+ self.trophiesstore_filter_today.set_visible_func(self._trophy_recent_visible_func,TROPHIES_FILTER_TODAY)
139+ self.trophiesstore_filter_week = self.trophiesstore.filter_new()
140+ self.trophiesstore_filter_week.set_visible_func(self._trophy_recent_visible_func,TROPHIES_FILTER_WEEK)
141+ self.trophiesstore_filter_month = self.trophiesstore.filter_new()
142+ self.trophiesstore_filter_month.set_visible_func(self._trophy_recent_visible_func,TROPHIES_FILTER_MONTH)
143+ self.trophiesstore_filter_sixmonths = self.trophiesstore.filter_new()
144+ self.trophiesstore_filter_sixmonths.set_visible_func(self._trophy_recent_visible_func,TROPHIES_FILTER_SIXMONTHS)
145+ self.trophiesstore_filter_earlier = self.trophiesstore.filter_new()
146+ self.trophiesstore_filter_earlier.set_visible_func(self._trophy_recent_visible_func,TROPHIES_FILTER_EARLIER)
147+
148+ self.opp_icon.set_model(self.oppstore_filtered)
149 self.opp_icon.set_text_column(COL_TITLE)
150 self.opp_icon.set_pixbuf_column(COL_PIXBUF)
151
152- #self.opp_icon.show()
153-
154 # set up webkit
155-
156 self.webview = WebKit.WebView()
157 self.scrolledwindow.add(self.webview)
158 self.webview.props.settings.props.enable_default_context_menu = False
159@@ -257,7 +292,7 @@
160 # IMPORTANT: This function should do no initialisations that depend
161 # on having the daemon running. This is because if the daemon is not
162 # yet started it will take some time to connect to it. Such
163- # initialistions should land in appropriate place in run_daemon_timeout(...).
164+ # initialistions should land in appropriate place in finalise_daemon_connection(...).
165
166 self.datapath = get_data_path()
167
168@@ -285,6 +320,7 @@
169 def on_reload_accomplishments_clicked(self, widget):
170 self.additional_no_collections.set_visible(False)
171 self.reload_accomplishments()
172+ self.set_display(DISPLAY_MODE_OPPORTUNITIES)
173
174 def reload_accomplishments(self):
175 if not self.connected:
176@@ -295,10 +331,9 @@
177 self.libaccom.reload_accom_database()
178 self.statusbar_reload_msg_stop()
179
180- self.populate_opp_combos()
181+ self._load_accomplishments()
182 if len(self.accomdb) == 0:
183 self.add_no_collections_installed()
184- self.update_views(None)
185
186 def statusbar_reload_msg_start(self):
187 self.statusbar.set_text(_("Reloading accomplishments collections..."))
188@@ -324,7 +359,9 @@
189
190 # run this to refresh our accomplishments list
191 self._load_accomplishments()
192- self.update_views(None)
193+
194+ #XXX: It would be MUCH faster if we determined the new accomID and added just it, not recreating whole trees!
195+ self.prepare_models()
196
197 # set the Launcher icon to be urgent and show new trophy count
198 self.launcher.set_property("urgent", True)
199@@ -334,9 +371,6 @@
200 self.launcher.set_property("count_visible", True)
201 else:
202 self.launcher.set_property("count_visible", False)
203-
204- if not self.notebook.get_current_page() == 0:
205- self.on_tb_mytrophies_clicked(None)
206
207 def on_help_askubuntu_activate(self, widget):
208 webbrowser.open("http://askubuntu.com/questions/ask?tags=accomplishments", True)
209@@ -360,33 +394,6 @@
210 if bool(self.has_u1) is True and bool(self.has_verif) is True:
211 self.check_for_extra_info_required()
212
213- def add_mytrophies_view(self, section, liststore):
214- outerbox = Gtk.VBox()
215- header = Gtk.Label("<span font_family='Ubuntu' size='18000' weight='light'>" + section + "</span>")
216- header.set_use_markup(True)
217- header.set_property("xalign", 0)
218- header.set_property("margin_left", 10)
219- header.set_property("margin_top", 5)
220- header.set_property("margin_bottom", 2)
221- separator = Gtk.Separator()
222- separator.set_property("margin_left", 10)
223- separator.set_property("margin_right", 10)
224-
225- iconview = Gtk.IconView()
226- iconview.set_model(liststore)
227- iconview.set_text_column(COL_TITLE)
228- iconview.set_pixbuf_column(COL_PIXBUF)
229- iconview.set_item_width(120)
230- iconview.set_columns(-1)
231- iconview.connect("selection-changed",self.mytrophy_clicked)
232-
233- outerbox.pack_start(header, False, False, 0)
234- outerbox.pack_start(separator, False, False, 0)
235- outerbox.pack_start(iconview, False, False, 0)
236- outerbox.show_all()
237-
238- self.mytrophies_mainbox.add(outerbox)
239-
240 def connect_to_daemon(self):
241 """Tries to connect to the daemon"""
242
243@@ -496,13 +503,12 @@
244
245 def finalise_daemon_connection(self):
246 self.libaccom.create_all_trophy_icons()
247+ self._load_accomplishments()
248+ self.prepare_models()
249 self.populate_opp_combos()
250 if len(self.accomdb) == 0:
251 self.add_no_collections_installed()
252- self.update_views(None)
253- self.check_and_ask_for_info()
254- self.notebook.set_current_page(2)
255- self.tb_opportunities.set_active(1)
256+ self.set_display(DISPLAY_MODE_OPPORTUNITIES)
257
258
259 def update_widgets_sensitivity(self):
260@@ -542,56 +548,8 @@
261 self.subcats_back.set_sensitive(True)
262 self.subcats_forward.set_sensitive(True)
263
264- def subcats_show(self, col, cat):
265- tempcats = []
266- if cat == "everything":
267- self.subcats_container.hide()
268- else:
269- # set up the subcats
270- cats = self.libaccom.get_collection_categories(col)
271- for c in cats:
272- if c == cat:
273- tempcats = cats[c]
274-
275- finalcats = []
276-
277- for s in tempcats:
278- for i in self.accomdb:
279- if i["collection"] == col and i["categories"][0] == cat + ":" + s and i["accomplished"] == False:
280- finalcats.append(s)
281-
282- # convert to a set to remove dupes
283- finalcats = set(finalcats)
284-
285- # remove previous buttons from the button box
286- for b in self.subcats_buttonbox.get_children():
287- self.subcats_buttonbox.remove(b)
288-
289- # Add 'All' button
290- button = Gtk.Button(_("All"))
291- button.props.relief = Gtk.ReliefStyle.NONE
292- button.connect("clicked", self.subcat_clicked, cat)
293- self.subcats_buttonbox.add(button)
294- button.show()
295-
296- # fill the button box with the sub categories
297- for s in finalcats:
298- button = Gtk.Button(s)
299- button.props.relief = Gtk.ReliefStyle.NONE
300- button.connect("clicked", self.subcat_clicked, cat)
301- self.subcats_buttonbox.add(button)
302- button.show()
303-
304- if len(finalcats) > 0:
305- self.subcats_buttonbox.show_all()
306- self.subcats_container.show()
307- else:
308- self.subcats_container.hide()
309-
310-
311 def subcat_clicked(self, button, data):
312- self.subcat = button.get_label()
313- self.update_views(None)
314+ self.set_display(filter_subcat = data)
315
316 def subcats_back_button(self, widget):
317 h = self.subcats_scroll.get_hadjustment()
318@@ -662,7 +620,7 @@
319
320 if uri.startswith('accomplishment:'):
321 id = uri[17:]
322- self.accomplishment_info(id)
323+ self.set_display(DISPLAY_MODE_DETAILS,accomID=id)
324 return True
325
326 pol_dec.ignore()
327@@ -750,6 +708,8 @@
328 self.additional_ubuntu1.set_visible(True)
329
330 def on_window_resized(self,widget):
331+ """
332+ Since there is no more need to recalculate data when resized, the following is no more needed.
333 # get the new size
334 new_width = widget.get_size()[0]
335 new_height = widget.get_size()[1]
336@@ -760,278 +720,73 @@
337 self.curr_height = new_height
338 # and refill iconviews with icons to adjust columns number
339 if self.connected:
340- self.update_views(widget)
341- self.update_mytrophy_filter()
342+ if self.display_mode is DISPLAY_MODE_OPPORTUNITIES:
343+ self._update_opportunities_view(None)
344+ if self.display_mode is DISPLAY_MODE_TROPHIES:
345+ self._update_mytrophy_filter()
346 else:
347 # Control flow may reach here if the daemon is not yet running
348 # and therefore connection is yet to be made. Passing here will
349 # avoid errors about not-existing libaccom.
350 pass
351-
352-
353- def update_views(self, widget):
354- """Update all of the views to reflect the current state of Trophies and Opportunities."""
355- status_trophies = 0
356- status_opps = 0
357-
358- show_locked = True
359-
360- if self.opp_showlocked.get_active():
361- show_locked = True
362- else:
363- show_locked = False
364-
365- self.mytrophies_store_all = []
366-
367- #trophymodel = self.trophy_icon.get_model()
368- oppmodel = self.opp_icon.get_model()
369-
370- # clear the models
371- oppmodel.clear()
372- self.mytrophies_filter_today.clear()
373- self.mytrophies_filter_week.clear()
374- self.mytrophies_filter_month.clear()
375- self.mytrophies_filter_sixmonths.clear()
376- self.mytrophies_filter_earlier.clear()
377-
378- coltree_iter = self.opp_combo_col.get_active_iter()
379- colmodel = self.opp_combo_col.get_model()
380-
381- if coltree_iter == None:
382- col = ""
383- colname = ""
384- else:
385- col, colname = colmodel[coltree_iter][:2]
386-
387- col_active_item = self.opp_combo_col.get_active()
388-
389- if col_active_item == 0:
390- self.opp_combo_cat.set_sensitive(False)
391- else:
392- self.opp_combo_cat.set_sensitive(True)
393-
394- cattree_iter = self.opp_combo_cat.get_active_iter()
395- catmodel = self.opp_combo_cat.get_model()
396-
397- if cattree_iter == None:
398- cat = ""
399- catname = ""
400- else:
401- cat, catname = catmodel[cattree_iter][:2]
402-
403- if cat == "":
404- self.subcats_container.hide()
405- else:
406- self.subcats_show(col, cat)
407-
408- # update opportunities
409- for acc in self.accomdb:
410- icon = None
411- icon = GdkPixbuf.Pixbuf.new_from_file_at_size(str(acc["iconpath"]), 90, 90)
412-
413- if str(acc["accomplished"]) == '1':
414- #self.mytrophies_filter_all = testlist.append( { acc["collection-human"] : [acc["title"], icon, bool(acc["accomplished"]), acc["locked"], acc["collection"], acc["id"]] } )
415- self.mytrophies_store_all.append([{ "title" : acc["title"], "icon" : icon, "accomplished" : bool(acc["accomplished"]), "locked" : acc["locked"], "collection" : acc["collection"], "id" : acc["id"], "collection-human" : acc["collection-human"] }])
416-
417- today = datetime.date.today()
418- margin_today = datetime.timedelta(days = 1)
419- margin_week = datetime.timedelta(days = 7)
420- margin_month = datetime.timedelta(days = 31)
421- margin_sixmonths = datetime.timedelta(days = 180)
422-
423- match = False
424-
425- if str(acc["date-accomplished"]) == "None":
426- pass
427- else:
428- year = int(acc["date-accomplished"].split("-")[0])
429- month = int(acc["date-accomplished"].split("-")[1])
430- day = int(acc["date-accomplished"].split("-")[2].split(" ")[0])
431-
432- if (today - margin_today <= datetime.date(year, month, day) <= today + margin_today) == True:
433- self.mytrophies_filter_today.append([acc["title"], icon, bool(acc["accomplished"]), bool(acc["locked"]), acc["collection"], acc["id"]])
434- elif (today - margin_week <= datetime.date(year, month, day) <= today + margin_week) == True:
435- self.mytrophies_filter_week.append([acc["title"], icon, bool(acc["accomplished"]), bool(acc["locked"]), acc["collection"], acc["id"]])
436- elif (today - margin_month <= datetime.date(year, month, day) <= today + margin_month) == True:
437- self.mytrophies_filter_month.append([acc["title"], icon, bool(acc["accomplished"]), bool(acc["locked"]), acc["collection"], acc["id"]])
438- elif (today - margin_sixmonths <= datetime.date(year, month, day) <= today + margin_sixmonths) == True:
439- self.mytrophies_filter_sixmonths.append([acc["title"], icon, bool(acc["accomplished"]), bool(acc["locked"]), acc["collection"], acc["id"]])
440- #else:
441- # self.mytrophies_filter_earlier.append([acc["title"], icon, bool(acc["accomplished"]), bool(acc["locked"]), acc["collection"], acc["id"]])
442-
443- status_trophies = status_trophies + 1
444- else:
445- subcat = ""
446- thiscat = ""
447-
448- c = [i for i in acc["categories"] if i == cat]
449- if len(c) is not 0:
450- thiscat = c[0]
451- else:
452- thiscat = ""
453-
454- status_opps = status_opps + 1
455-
456- if self.subcat is not None:
457- subcat = str(cat) + ":" + str(self.subcat)
458- if self.subcat == "All":
459- if acc["collection"] == col and cat in list(acc["categories"])[0]:
460- if not acc["locked"] or show_locked:
461- oppmodel.append([acc["title"], icon, bool(acc["accomplished"]), bool(acc["locked"]), acc["collection"], acc["id"]])
462- else:
463- if acc["collection"] == col and list(acc["categories"])[0] == subcat:
464- if not acc["locked"] or show_locked:
465- oppmodel.append([acc["title"], icon, bool(acc["accomplished"]), bool(acc["locked"]), acc["collection"], acc["id"]])
466- else:
467- if acc["collection"] == col and cat in list(acc["categories"])[0]:
468- if not acc["locked"] or show_locked:
469- oppmodel.append([acc["title"], icon, bool(acc["accomplished"]), bool(acc["locked"]), acc["collection"], acc["id"]])
470- elif col == "" and cat == "":
471- if not acc["locked"] or show_locked:
472- oppmodel.append([acc["title"], icon, bool(acc["accomplished"]), bool(acc["locked"]), acc["collection"], acc["id"]])
473- elif acc["collection"] == col and cat == "":
474- if not acc["locked"] or show_locked:
475- oppmodel.append([acc["title"], icon, bool(acc["accomplished"]), bool(acc["locked"]), acc["collection"], acc["id"]])
476+ """
477+ pass
478
479 def populate_opp_combos(self):
480-
481- # grab data
482- self._load_accomplishments()
483-
484 temp = []
485-
486 for i in self.accomdb:
487 temp.append({i["collection"] : i["collection-human"] })
488-
489 # uniqify the values
490 result = [dict(tupleized) for tupleized in set(tuple(item.items()) for item in temp)]
491-
492+
493 # set up app
494 self.opp_col_store.append(["", "All"])
495-
496 for i in sorted(result):
497 self.opp_col_store.append([i.keys()[0], i.values()[0]])
498-
499 self.opp_combo_col.set_model(self.opp_col_store)
500-
501 self.opp_combo_col.set_active(0)
502 self.opp_combo_col.show()
503
504- # set up cat
505-
506+ # Prepare categories combo
507 self.opp_combo_cat.set_model(self.opp_cat_store)
508-
509 self.opp_combo_cat.show()
510
511-
512- def opp_app_updated(self, widget):
513- self.do_not_react_on_cat_changes = True
514- catlist = set()
515- tree_iter = widget.get_active_iter()
516- model = widget.get_model()
517- col, name = model[tree_iter][:2]
518-
519- if col == "":
520- self.opp_cat_store.clear()
521- self.opp_cat_store.append(["", _("everything")])
522-
523- self.subcat = None
524-
525- self.update_views(None)
526- else:
527- cats = self.libaccom.get_collection_categories(col)
528-
529- for i in cats:
530- catlist.add(i)
531-
532- self.opp_cat_store.clear()
533-
534- self.opp_cat_store.append(["", _("everything")])
535-
536- for i in sorted(catlist):
537- self.opp_cat_store.append([i, i])
538-
539- self.do_not_react_on_cat_changes = False
540- self.opp_combo_cat.set_active(0)
541-
542- self.subcats_container.hide()
543- # Following does not have to be done, because using
544- # opp_combo_cat.set_active will cause opp_cat_updated
545- # to run update_views
546- #self.update_views(None)
547-
548+ def on_filter_collection_changed(self,widget):
549+ tree_iter = widget.get_active_iter()
550+ model = widget.get_model()
551+ collection, name = model[tree_iter][:2]
552+ self.set_display(filter_collection = collection)
553+
554+ def on_filter_category_changed(self, widget):
555+ tree_iter = widget.get_active_iter()
556+ if tree_iter == None: # Special case if the categories combo is not sensitive
557+ return
558+ model = widget.get_model()
559+ category, name = model[tree_iter][:2]
560+ self.set_display(filter_category = category)
561+
562+ def on_filter_show_locked_clicked(self, widget):
563+ if widget.get_active():
564+ self.set_display(filter_locked=DISPLAY_FILTER_LOCKED_SHOW)
565+ else:
566+ self.set_display(filter_locked=DISPLAY_FILTER_LOCKED_HIDE)
567+
568+ def on_search_changed(self,widget):
569+ value = widget.get_text()
570+ self.set_display(search_query=value)
571+
572+ def on_search_clear_clicked(self,widget,icon,data):
573+ self.searchbar.set_text("")
574+
575 def check_accomplishments(self, widget):
576 """Called when Check Accomplishments is selected in the interface."""
577-
578 self.libaccom.run_scripts(True)
579- #self.notebook.set_current_page(2)
580-
581- def opp_cat_updated(self, widget):
582- if self.do_not_react_on_cat_changes:
583- return
584-
585- cattree_iter = self.opp_combo_cat.get_active_iter()
586- catmodel = self.opp_combo_cat.get_model()
587-
588- if cattree_iter == None:
589- cat = ""
590- catname = ""
591- else:
592- cat, catname = catmodel[cattree_iter][:2]
593-
594- self.subcat = None
595-
596- self.update_views(None)
597-
598- def update_mytrophy_filter(self):
599-
600- kids = self.mytrophies_mainbox.get_children()
601-
602- if len(kids) > 0:
603- for k in kids:
604- self.mytrophies_mainbox.remove(k)
605-
606- if (self.mytrophies_filtermode == MYTROPHIES_FILTER_ALL):
607- collections = self.libaccom.list_collections()
608-
609- for c in collections:
610- ls = Gtk.ListStore(str, GdkPixbuf.Pixbuf, bool, bool, str, str) # title, icon accomplished, locked, col, accomplishment
611- ls.set_sort_column_id(COL_TITLE, Gtk.SortType.ASCENDING)
612- ls.clear()
613- collectionhuman = ""
614- for i in self.mytrophies_store_all:
615- if i[0]["collection"] == c:
616- collectionhuman = i[0]["collection-human"]
617- ls.append([i[0]["title"], i[0]["icon"], i[0]["accomplished"], i[0]["locked"], i[0]["collection"], i[0]["id"]])
618-
619- if len(ls) > 0:
620- self.add_mytrophies_view(collectionhuman, ls)
621- elif (self.mytrophies_filtermode == MYTROPHIES_FILTER_LATEST):
622- if len(self.mytrophies_filter_today) > 0:
623- self.add_mytrophies_view(_("Today"), self.mytrophies_filter_today)
624-
625- if len(self.mytrophies_filter_week) > 0:
626- self.add_mytrophies_view(_("This Week"), self.mytrophies_filter_week)
627-
628- if len(self.mytrophies_filter_month) > 0:
629- self.add_mytrophies_view(_("This Month"), self.mytrophies_filter_month)
630-
631- if len(self.mytrophies_filter_sixmonths) > 0:
632- self.add_mytrophies_view(_("Last Six Months"), self.mytrophies_filter_sixmonths)
633-
634- if len(self.mytrophies_filter_earlier) > 0:
635- self.add_mytrophies_view(_("Earlier"), self.mytrophies_filter_earlier)
636-
637-
638-
639+
640 def on_mytrophies_filter_latest_toggled(self, widget):
641- self.mytrophies_filtermode = MYTROPHIES_FILTER_LATEST
642- self.update_mytrophy_filter()
643-
644+ self.set_display(trophies_mode=MYTROPHIES_FILTER_LATEST)
645
646 def on_mytrophies_filter_all_toggled(self, widget):
647- self.mytrophies_filtermode = MYTROPHIES_FILTER_ALL
648- self.update_mytrophy_filter()
649+ self.set_display(trophies_mode=MYTROPHIES_FILTER_ALL)
650
651 def on_tb_mytrophies_clicked(self, widget):
652 """Called when the My Trophies button is clicked."""
653@@ -1040,14 +795,9 @@
654 opportunities_toggled = self.tb_opportunities.get_active()
655
656 if mytrophies_toggled == True:
657- self.tb_opportunities.handler_block_by_func(self.on_tb_opportunities_clicked)
658- self.tb_opportunities.set_active(False)
659- self.tb_opportunities.handler_unblock_by_func(self.on_tb_opportunities_clicked)
660- self.mytrophies_filter_all.set_active(True)
661- self.on_mytrophies_filter_all_toggled(None)
662- self.notebook.set_current_page(1)
663+ self.set_display(DISPLAY_MODE_TROPHIES)
664 else:
665- self.tb_mytrophies.set_active(True)
666+ self.tb_mytrophies.set_active(True) # This also fires the signal handler
667
668 def on_tb_opportunities_clicked(self, widget):
669 """Called when the Opportunities button is clicked."""
670@@ -1060,12 +810,9 @@
671 opportunities_toggled = self.tb_opportunities.get_active()
672
673 if opportunities_toggled == True:
674- self.tb_mytrophies.handler_block_by_func(self.on_tb_mytrophies_clicked)
675- self.tb_mytrophies.set_active(False)
676- self.tb_mytrophies.handler_unblock_by_func(self.on_tb_mytrophies_clicked)
677- self.notebook.set_current_page(2)
678+ self.set_display(DISPLAY_MODE_OPPORTUNITIES)
679 else:
680- self.tb_opportunities.set_active(True)
681+ self.tb_opportunities.set_active(True) # This also fires the signal handler
682
683 def menu_prefs_clicked(self,widget):
684 """Display the preferences window."""
685@@ -1143,7 +890,7 @@
686 widget.unselect_path(item)
687 model = widget.get_model()
688 accomID = model[item][COL_ID]
689- self.accomplishment_info(accomID)
690+ self.set_display(DISPLAY_MODE_DETAILS,accomID=accomID)
691
692 def mytrophy_clicked(self, widget):
693 selection = widget.get_selected_items()
694@@ -1153,7 +900,7 @@
695 widget.unselect_path(item)
696 model = widget.get_model()
697 accomID = model[item][COL_ID]
698- self.accomplishment_info(accomID)
699+ self.set_display(DISPLAY_MODE_DETAILS,accomID=accomID)
700
701 def optparse_accomplishment(self, accom_id):
702 """Process the -a command line option"""
703@@ -1162,9 +909,379 @@
704 print "There is no accomplishment with this ID."
705 return
706
707- self.accomplishment_info(accom_id)
708-
709- def accomplishment_info(self, accomID):
710+ self.set_display(DISPLAY_MODE_DETAILS,accomID=accom_id)
711+
712+ def prepare_models(self):
713+ """
714+ This function is the only one that clears liststores and fills them with data.
715+ It also prepares some of filters - these which are used in mytrophies view.
716+ """
717+ self.oppstore.clear()
718+ self.trophiesstore.clear()
719+ # Fill in the opportunities tree
720+ for acc in self.accomdb:
721+ icon = GdkPixbuf.Pixbuf.new_from_file_at_size(str(acc["iconpath"]), 90, 90)
722+ if str(acc["accomplished"]) != '1':
723+ self.oppstore.append([acc["title"], icon, bool(acc["accomplished"]), bool(acc["locked"]), acc["collection"], acc["id"], acc["date-accomplished"], '|'.join(acc["categories"])])
724+ else:
725+ self.trophiesstore.append([acc["title"], icon, bool(acc["accomplished"]), bool(acc["locked"]), acc["collection"], acc["id"], acc["date-accomplished"], '|'.join(acc["categories"])])
726+ # Prepare latest trophies iconviews
727+ if len(self.mytrophies_box_latest.get_children()) == 0:
728+ self.mytrophies_latest_boxes = []
729+ box = self.add_mytrophies_view(self.mytrophies_box_latest,_("Today"), self.trophiesstore_filter_today)
730+ self.mytrophies_latest_boxes.append([self.trophiesstore_filter_today,box])
731+ box = self.add_mytrophies_view(self.mytrophies_box_latest,_("This Week"), self.trophiesstore_filter_week)
732+ self.mytrophies_latest_boxes.append([self.trophiesstore_filter_week,box])
733+ box = self.add_mytrophies_view(self.mytrophies_box_latest,_("This Month"), self.trophiesstore_filter_month)
734+ self.mytrophies_latest_boxes.append([self.trophiesstore_filter_month,box])
735+ box = self.add_mytrophies_view(self.mytrophies_box_latest,_("Last Six Months"), self.trophiesstore_filter_sixmonths)
736+ self.mytrophies_latest_boxes.append([self.trophiesstore_filter_sixmonths,box])
737+ box = self.add_mytrophies_view(self.mytrophies_box_latest,_("Earlier"), self.trophiesstore_filter_earlier)
738+ self.mytrophies_latest_boxes.append([self.trophiesstore_filter_earlier,box])
739+
740+ # Prepare all trophies iconviews
741+ kids = self.mytrophies_box_all.get_children()
742+ for kid in kids:
743+ self.mytrophies_box_all.remove(k)
744+ for f in self.trophies_collection_filters:
745+ del f[0] #Remove the filter!
746+ self.trophies_collection_filters = []
747+ collections = self.libaccom.list_collections()
748+ for c in collections:
749+ new_filter = self.trophiesstore.filter_new()
750+ new_filter.set_visible_func(self._trophy_all_visible_func,c)
751+ box = self.add_mytrophies_view(self.mytrophies_box_all, self.libaccom.get_collection_name(c), new_filter)
752+ self.trophies_collection_filters.append([new_filter,box])
753+
754+ def add_mytrophies_view(self, parent, section, model):
755+ """
756+ This function is used for adding a new group of accomplishments in mytrophies view.
757+ It creates unified UI elements, packs them and adds to @parent. The @section argument
758+ will be the header of the section. The @model argument should be the treemodel of this
759+ new iconview (prefferably a treemodelfilter).
760+ """
761+ outerbox = Gtk.VBox()
762+ header = Gtk.Label("<span font_family='Ubuntu' size='18000' weight='light'>" + section + "</span>")
763+ header.set_use_markup(True)
764+ header.set_property("xalign", 0)
765+ header.set_property("margin_left", 10)
766+ header.set_property("margin_top", 5)
767+ header.set_property("margin_bottom", 2)
768+ separator = Gtk.Separator()
769+ separator.set_property("margin_left", 10)
770+ separator.set_property("margin_right", 10)
771+
772+ iconview = Gtk.IconView()
773+ iconview.set_model(model)
774+ iconview.set_text_column(COL_TITLE)
775+ iconview.set_pixbuf_column(COL_PIXBUF)
776+ iconview.set_item_width(120)
777+ iconview.set_columns(-1)
778+ iconview.connect("selection-changed",self.mytrophy_clicked)
779+
780+ outerbox.pack_start(header, False, False, 0)
781+ outerbox.pack_start(separator, False, False, 0)
782+ outerbox.pack_start(iconview, False, False, 0)
783+ outerbox.show_all()
784+
785+ parent.add(outerbox)
786+ return outerbox
787+
788+ def set_display(self,
789+ mode = DISPLAY_MODE_UNSPECIFIED,
790+ accomID = "",
791+ trophies_mode = MYTROPHIES_FILTER_UNSPECIFIED,
792+ filter_locked = DISPLAY_FILTER_LOCKED_UNSPECIFIED,
793+ filter_collection = DISPLAY_FILTER_COLLECTION_UNSPECIFIED,
794+ filter_category = DISPLAY_FILTER_CATEGORY_UNSPECIFIED,
795+ filter_subcat = DISPLAY_FILTER_SUBCAT_UNSPECIFIED,
796+ search_query = DISPLAY_FILTER_SEARCH_UNSPECIFIED):
797+ """
798+ Switches display mode as specified in arguments.
799+ It takes care about flipping notebook pages, hiding unnecessary UI pieces etc.
800+ This function shouldn't be called with many arguments, pass only these that you want to override.
801+ """
802+ # The ordering of following IF statements *IS* important!
803+ # For example, passing both collection and category to this function
804+ # may not result in skipping some of these data as they get cleared
805+ # later on. Therefore hierarhical order is desired.
806+ if mode is not DISPLAY_MODE_UNSPECIFIED:
807+ self.display_mode = mode
808+ # Reflect changes in the UI
809+ if self.display_mode is DISPLAY_MODE_DETAILS:
810+ #Displaying details for an accomplishment
811+
812+ if accomID == "":
813+ print "Unable to display details view, you probably forgot the accomID argument."
814+ return
815+
816+ # Set togglable buttons to reflect current state
817+ self.tb_mytrophies.handler_block_by_func(self.on_tb_mytrophies_clicked)
818+ self.tb_opportunities.handler_block_by_func(self.on_tb_opportunities_clicked)
819+ self.tb_mytrophies.set_active(False)
820+ self.tb_opportunities.set_active(False)
821+ self.tb_mytrophies.handler_unblock_by_func(self.on_tb_mytrophies_clicked)
822+ self.tb_opportunities.handler_unblock_by_func(self.on_tb_opportunities_clicked)
823+
824+ # Select all characters in searchbar
825+ self.searchbar.grab_focus()
826+
827+ self.notebook.set_current_page(0)
828+ self.searchbar_box.hide()
829+
830+ elif self.display_mode is DISPLAY_MODE_TROPHIES:
831+ #Display the list of trophies
832+
833+ # Set togglable buttons to reflect current state
834+ self.tb_mytrophies.handler_block_by_func(self.on_tb_mytrophies_clicked)
835+ self.tb_opportunities.handler_block_by_func(self.on_tb_opportunities_clicked)
836+ self.tb_mytrophies.set_active(True)
837+ self.tb_opportunities.set_active(False)
838+ self.tb_mytrophies.handler_unblock_by_func(self.on_tb_mytrophies_clicked)
839+ self.tb_opportunities.handler_unblock_by_func(self.on_tb_opportunities_clicked)
840+
841+ # Select all characters in searchbar
842+ self.searchbar.grab_focus()
843+
844+ self.notebook.set_current_page(1)
845+ self.searchbar_box.show()
846+
847+ elif self.display_mode is DISPLAY_MODE_OPPORTUNITIES:
848+
849+ # Set togglable buttons to reflect current state
850+ self.tb_mytrophies.handler_block_by_func(self.on_tb_mytrophies_clicked)
851+ self.tb_opportunities.handler_block_by_func(self.on_tb_opportunities_clicked)
852+ self.tb_mytrophies.set_active(False)
853+ self.tb_opportunities.set_active(True)
854+ self.tb_mytrophies.handler_unblock_by_func(self.on_tb_mytrophies_clicked)
855+ self.tb_opportunities.handler_unblock_by_func(self.on_tb_opportunities_clicked)
856+
857+ self.notebook.set_current_page(2)
858+ self.searchbar_box.show()
859+
860+ if trophies_mode is not MYTROPHIES_FILTER_UNSPECIFIED:
861+ self.display_mytrophies_filtermode = trophies_mode
862+ # Show/hide appropriate iconview
863+ if self.display_mytrophies_filtermode is MYTROPHIES_FILTER_ALL:
864+ self.mytrophies_notebook.set_current_page(0)
865+ elif self.display_mytrophies_filtermode is MYTROPHIES_FILTER_LATEST:
866+ self.mytrophies_notebook.set_current_page(1)
867+
868+ if filter_locked is not DISPLAY_FILTER_LOCKED_UNSPECIFIED:
869+ self.display_filter_locked = filter_locked
870+ if filter_collection is not DISPLAY_FILTER_COLLECTION_UNSPECIFIED:
871+ self.display_filter_collection = filter_collection
872+
873+ # As the requested collection changed, we need to update the categories combo.
874+ if filter_collection == "":
875+ self.opp_cat_store.clear()
876+ self.opp_cat_store.append(["", _("everything")])
877+ self.opp_combo_cat.set_sensitive(False)
878+ else:
879+ cats = self.libaccom.get_collection_categories(filter_collection)
880+ self.opp_cat_store.clear()
881+ self.opp_cat_store.append(["", _("everything")])
882+ for i in sorted(cats):
883+ self.opp_cat_store.append([i, i])
884+ self.opp_combo_cat.set_sensitive(True)
885+
886+ # Set the active item to "everything".
887+ self.display_filter_category = ""
888+ self.opp_combo_cat.handler_block_by_func(self.on_filter_category_changed)
889+ self.opp_combo_cat.set_active(0)
890+ self.opp_combo_cat.handler_unblock_by_func(self.on_filter_category_changed)
891+
892+ # It is likely that we need to update the subcategories.
893+ # A special case is when it needs to be hidden after collection change.
894+ self._update_subcats()
895+
896+ if filter_category is not DISPLAY_FILTER_CATEGORY_UNSPECIFIED:
897+ self.display_filter_category = filter_category
898+
899+ # Changing category, therefore we should display the subcats bar too.
900+ self._update_subcats()
901+
902+ if filter_subcat is not DISPLAY_FILTER_SUBCAT_UNSPECIFIED:
903+ self.display_filter_subcat = filter_subcat
904+ if search_query is not DISPLAY_FILTER_SEARCH_UNSPECIFIED:
905+ self.display_filter_search = search_query
906+
907+
908+ # Finally, pass refreshing/rerendering to specialised functions
909+ if self.display_mode is DISPLAY_MODE_DETAILS:
910+ self._accomplishment_info(accomID)
911+ elif self.display_mode is DISPLAY_MODE_TROPHIES:
912+ self._update_mytrophy_view()
913+ elif self.display_mode is DISPLAY_MODE_OPPORTUNITIES:
914+ self._update_opportunities_view()
915+
916+ def _update_mytrophy_view(self):
917+ # Causes the treemodel to call visible_func for all rows.
918+ # It also hides/shows boxes depending on whether they are empty.
919+ if self.display_mytrophies_filtermode is MYTROPHIES_FILTER_ALL:
920+ filterlist = self.trophies_collection_filters
921+ elif self.display_mytrophies_filtermode is MYTROPHIES_FILTER_LATEST:
922+ filterlist = self.mytrophies_latest_boxes
923+
924+ for f in filterlist:
925+ f[0].refilter()
926+ if len(f[0]) is 0:
927+ f[1].hide()
928+ else:
929+ f[1].show()
930+
931+ def _update_opportunities_view(self):
932+ # Causes the treemodel to call visible_func for all rows.
933+ self.oppstore_filtered.refilter()
934+
935+ def _opp_visible_func(self, model, iterator, data):
936+ """
937+ This function is crucial for filtering opportunities. It is called
938+ by some internal GTK callbacks, whenever the treemodel changes.
939+ It has to return True/False, which states whether the given row
940+ should be displayed or not.
941+ """
942+ # If we are hiding locked accoms:
943+ if (self.display_filter_locked is DISPLAY_FILTER_LOCKED_HIDE) and model.get_value(iterator,COL_LOCKED):
944+ return False
945+ # If we ale looking for a certain collection:
946+ if (self.display_filter_collection != "") and (self.display_filter_collection != model.get_value(iterator,COL_COLLECTION)):
947+ return False
948+ # If we ale looking for a certain category...
949+ if (self.display_filter_category != ""):
950+ #...and a subcategory
951+ if (self.display_filter_subcat != ""):
952+ q = self.display_filter_category + ":" + self.display_filter_subcat
953+ if not (q in model.get_value(iterator,COL_CATEGORIES)):
954+ return False
955+ if not (self.display_filter_category in model.get_value(iterator,COL_CATEGORIES)):
956+ return False
957+ # If there is a search term and this row does not match the query:
958+ if (self.display_filter_search != "") and not (self.display_filter_search.lower() in model.get_value(iterator,COL_TITLE).lower()) and not (self.display_filter_search.lower() in model.get_value(iterator,COL_ID).split("/")[1].lower()):
959+ return False
960+ return True
961+
962+ def _trophy_recent_visible_func(self,model,iterator,data):
963+ """
964+ This function is crucial for filtering recently awarded trophies. It is called
965+ by some internal GTK callbacks, whenever the treemodel changes.
966+ It has to return True/False, which states whether the given row
967+ should be displayed or not.
968+ The @data argument specifies which box's filter it actually is,
969+ be it "today" or "last month"
970+ """
971+
972+ #XXX: Making these constants global might save some filtering time.
973+ today = datetime.date.today()
974+ margin_today = datetime.timedelta(days = 1)
975+ margin_week = datetime.timedelta(days = 7)
976+ margin_month = datetime.timedelta(days = 31)
977+ margin_sixmonths = datetime.timedelta(days = 180)
978+
979+ when = model.get_value(iterator,COL_DATE_ACCOMPLISHED)
980+ if when == "None":
981+ return False
982+ year, month, day = when.split("-")
983+ when = datetime.date(int(year), int(month), int(day.split(" ")[0]))
984+
985+ if (today - margin_today <= when <= today + margin_today):
986+ if data is TROPHIES_FILTER_TODAY:
987+ pass #proceed to further filtering
988+ else:
989+ return False
990+ elif (today - margin_week <= when <= today + margin_week):
991+ if data is TROPHIES_FILTER_WEEK:
992+ pass #proceed to further filtering
993+ else:
994+ return False
995+ elif (today - margin_month <= when <= today + margin_month):
996+ if data is TROPHIES_FILTER_MONTH:
997+ pass #proceed to further filtering
998+ else:
999+ return False
1000+ elif (today - margin_sixmonths <= when <= today + margin_sixmonths):
1001+ if data is TROPHIES_FILTER_SIXMONTHS:
1002+ pass #proceed to further filtering
1003+ else:
1004+ return False
1005+ else:
1006+ if data is TROPHIES_FILTER_EARLIER:
1007+ pass #proceed to further filtering
1008+ else:
1009+ return False
1010+
1011+ # If there is a search term and this row does not match the query:
1012+ if (self.display_filter_search != "") and not (self.display_filter_search.lower() in model.get_value(iterator,COL_TITLE).lower()) and not (self.display_filter_search.lower() in model.get_value(iterator,COL_ID).split("/")[1].lower()):
1013+ return False
1014+
1015+ return True
1016+
1017+ def _trophy_all_visible_func(self,model,iterator,collection):
1018+ """
1019+ This function is crucial for filtering mytrophies. It is called
1020+ by some internal GTK callbacks, whenever the treemodel changes.
1021+ It has to return True/False, which states whether the given row
1022+ should be displayed or not.
1023+ The @data argument specifies filtered collection.
1024+ """
1025+ # If row's collection matches the desired for this filter
1026+ if (collection != model.get_value(iterator,COL_COLLECTION)):
1027+ return False
1028+ # If there is a search term and this row does not match the query:
1029+ if (self.display_filter_search != "") and not (self.display_filter_search.lower() in model.get_value(iterator,COL_TITLE).lower()) and not (self.display_filter_search.lower() in model.get_value(iterator,COL_ID).split("/")[1].lower()):
1030+ return False
1031+ return True
1032+
1033+ def _update_subcats(self):
1034+ """
1035+ This function creates the buttons for subcategories, requesting required data from the daemon.
1036+ """
1037+ tempcats = []
1038+ if self.display_filter_category == "" or self.display_filter_collection == "":
1039+ self.subcats_container.hide()
1040+ else:
1041+ # set up the subcats
1042+ cats = self.libaccom.get_collection_categories(self.display_filter_collection)
1043+ for c in cats:
1044+ if c == self.display_filter_category:
1045+ tempcats = cats[c]
1046+
1047+ finalcats = []
1048+
1049+ for s in tempcats:
1050+ for i in self.accomdb:
1051+ if i["collection"] == self.display_filter_collection and i["categories"][0] == self.display_filter_category + ":" + s and i["accomplished"] == False:
1052+ finalcats.append(s)
1053+
1054+ # convert to a set to remove dupes
1055+ finalcats = set(finalcats)
1056+
1057+ # remove previous buttons from the button box
1058+ for b in self.subcats_buttonbox.get_children():
1059+ self.subcats_buttonbox.remove(b)
1060+
1061+ if len(finalcats) > 1:
1062+ # Add 'All' button
1063+ button = Gtk.Button(_("All"))
1064+ button.props.relief = Gtk.ReliefStyle.NONE
1065+ button.connect("clicked", self.subcat_clicked, "")
1066+ self.subcats_buttonbox.add(button)
1067+ button.show()
1068+
1069+ # fill the button box with the sub categories
1070+ for s in finalcats:
1071+ button = Gtk.Button(s)
1072+ button.props.relief = Gtk.ReliefStyle.NONE
1073+ button.connect("clicked", self.subcat_clicked, s)
1074+ self.subcats_buttonbox.add(button)
1075+ button.show()
1076+
1077+ self.subcats_buttonbox.show_all()
1078+ self.subcats_container.show()
1079+ else:
1080+ self.subcats_container.hide()
1081+
1082+ def _accomplishment_info(self, accomID):
1083 """Display information about the selected accomplishment."""
1084 data = []
1085
1086@@ -1428,6 +1545,5 @@
1087 </html>"
1088
1089 self.webview.load_html_string(html, "file:///")
1090- self.notebook.set_current_page(0)
1091 self.webview.show()
1092
1093
1094=== modified file 'data/ui/AccomplishmentsViewerWindow.ui'
1095--- data/ui/AccomplishmentsViewerWindow.ui 2012-09-07 18:16:29 +0000
1096+++ data/ui/AccomplishmentsViewerWindow.ui 2012-11-16 07:15:23 +0000
1097@@ -1,14 +1,14 @@
1098 <?xml version="1.0" encoding="UTF-8"?>
1099 <interface>
1100+ <!-- interface-requires accomplishments_viewer_window 1.0 -->
1101 <!-- interface-requires gtk+ 3.0 -->
1102- <!-- interface-requires accomplishments_viewer_window 1.0 -->
1103 <!-- interface-local-resource-path ../media -->
1104 <object class="AccomplishmentsViewerWindow" id="accomplishments_viewer_window">
1105 <property name="height_request">270</property>
1106 <property name="can_focus">False</property>
1107 <property name="title" translatable="yes">Accomplishment Information</property>
1108 <property name="window_position">center</property>
1109- <property name="default_width">705</property>
1110+ <property name="default_width">735</property>
1111 <property name="default_height">450</property>
1112 <property name="icon_name">distributor-logo</property>
1113 <signal name="check-resize" handler="on_window_resized" swapped="no"/>
1114@@ -22,7 +22,6 @@
1115 <property name="can_focus">False</property>
1116 <child>
1117 <object class="GtkMenuItem" id="mnu_file">
1118- <property name="use_action_appearance">False</property>
1119 <property name="visible">True</property>
1120 <property name="can_focus">False</property>
1121 <property name="label" translatable="yes">_File</property>
1122@@ -34,7 +33,6 @@
1123 <child>
1124 <object class="GtkImageMenuItem" id="mnu_check_acc">
1125 <property name="label" translatable="yes">Check Accomplishments</property>
1126- <property name="use_action_appearance">False</property>
1127 <property name="visible">True</property>
1128 <property name="can_focus">False</property>
1129 <property name="use_stock">False</property>
1130@@ -48,7 +46,6 @@
1131 </child>
1132 <child>
1133 <object class="GtkMenuItem" id="mnu_edit">
1134- <property name="use_action_appearance">False</property>
1135 <property name="visible">True</property>
1136 <property name="can_focus">False</property>
1137 <property name="label" translatable="yes">_Edit</property>
1138@@ -60,7 +57,6 @@
1139 <child>
1140 <object class="GtkImageMenuItem" id="mnu_preferences">
1141 <property name="label">gtk-preferences</property>
1142- <property name="use_action_appearance">False</property>
1143 <property name="visible">True</property>
1144 <property name="can_focus">False</property>
1145 <property name="use_underline">True</property>
1146@@ -70,7 +66,6 @@
1147 </child>
1148 <child>
1149 <object class="GtkMenuItem" id="mnu_edit_reload_accoms">
1150- <property name="use_action_appearance">False</property>
1151 <property name="visible">True</property>
1152 <property name="can_focus">False</property>
1153 <property name="label" translatable="yes">_Reload accomplishments collections...</property>
1154@@ -81,7 +76,6 @@
1155 <child>
1156 <object class="GtkImageMenuItem" id="mnu_edit_ident">
1157 <property name="label" translatable="yes">_Identification...</property>
1158- <property name="use_action_appearance">False</property>
1159 <property name="visible">True</property>
1160 <property name="can_focus">False</property>
1161 <property name="use_underline">True</property>
1162@@ -96,7 +90,6 @@
1163 </child>
1164 <child>
1165 <object class="GtkMenuItem" id="mnu_help">
1166- <property name="use_action_appearance">False</property>
1167 <property name="visible">True</property>
1168 <property name="can_focus">False</property>
1169 <property name="label" translatable="yes">_Help</property>
1170@@ -108,7 +101,6 @@
1171 <child>
1172 <object class="GtkImageMenuItem" id="mnu_contents">
1173 <property name="label" translatable="yes">Contents</property>
1174- <property name="use_action_appearance">False</property>
1175 <property name="visible">True</property>
1176 <property name="can_focus">False</property>
1177 <property name="image">image2</property>
1178@@ -118,7 +110,6 @@
1179 </child>
1180 <child>
1181 <object class="GtkMenuItem" id="help_askubuntu">
1182- <property name="use_action_appearance">False</property>
1183 <property name="visible">True</property>
1184 <property name="can_focus">False</property>
1185 <property name="label" translatable="yes">Ask a question...</property>
1186@@ -129,7 +120,6 @@
1187 </child>
1188 <child>
1189 <object class="GtkSeparatorMenuItem" id="menuitem1">
1190- <property name="use_action_appearance">False</property>
1191 <property name="visible">True</property>
1192 <property name="can_focus">False</property>
1193 </object>
1194@@ -137,7 +127,6 @@
1195 <child>
1196 <object class="GtkImageMenuItem" id="mnu_about">
1197 <property name="label">gtk-about</property>
1198- <property name="use_action_appearance">False</property>
1199 <property name="visible">True</property>
1200 <property name="can_focus">False</property>
1201 <property name="use_underline">True</property>
1202@@ -163,10 +152,8 @@
1203 <property name="toolbar_style">both</property>
1204 <child>
1205 <object class="GtkToggleToolButton" id="tb_mytrophies">
1206- <property name="use_action_appearance">False</property>
1207 <property name="visible">True</property>
1208 <property name="can_focus">False</property>
1209- <property name="use_action_appearance">False</property>
1210 <property name="label" translatable="yes">My Trophies</property>
1211 <property name="use_underline">True</property>
1212 <property name="icon_widget">image9</property>
1213@@ -178,12 +165,10 @@
1214 </child>
1215 <child>
1216 <object class="GtkToggleToolButton" id="tb_opportunities">
1217- <property name="use_action_appearance">False</property>
1218 <property name="visible">True</property>
1219 <property name="can_focus">True</property>
1220 <property name="has_focus">True</property>
1221 <property name="is_focus">True</property>
1222- <property name="use_action_appearance">False</property>
1223 <property name="label" translatable="yes">Opportunities</property>
1224 <property name="use_underline">True</property>
1225 <property name="icon_widget">image4</property>
1226@@ -193,6 +178,69 @@
1227 <property name="homogeneous">True</property>
1228 </packing>
1229 </child>
1230+ <child>
1231+ <object class="GtkToolItem" id="toolbutton1">
1232+ <property name="visible">True</property>
1233+ <property name="can_focus">False</property>
1234+ <child>
1235+ <object class="GtkBox" id="box12">
1236+ <property name="visible">True</property>
1237+ <property name="can_focus">False</property>
1238+ <child>
1239+ <placeholder/>
1240+ </child>
1241+ <child>
1242+ <object class="GtkBox" id="searchbar_box">
1243+ <property name="visible">True</property>
1244+ <property name="can_focus">False</property>
1245+ <property name="margin_right">5</property>
1246+ <property name="margin_bottom">5</property>
1247+ <property name="orientation">vertical</property>
1248+ <child>
1249+ <object class="GtkEntry" id="searchbar">
1250+ <property name="visible">True</property>
1251+ <property name="can_focus">True</property>
1252+ <property name="invisible_char">•</property>
1253+ <property name="secondary_icon_stock">gtk-clear</property>
1254+ <signal name="changed" handler="on_search_changed" swapped="no"/>
1255+ <signal name="icon-press" handler="on_search_clear_clicked" swapped="no"/>
1256+ </object>
1257+ <packing>
1258+ <property name="expand">False</property>
1259+ <property name="fill">True</property>
1260+ <property name="pack_type">end</property>
1261+ <property name="position">0</property>
1262+ </packing>
1263+ </child>
1264+ <child>
1265+ <object class="GtkLabel" id="label9">
1266+ <property name="visible">True</property>
1267+ <property name="can_focus">False</property>
1268+ <property name="label" translatable="yes">Search:</property>
1269+ </object>
1270+ <packing>
1271+ <property name="expand">False</property>
1272+ <property name="fill">True</property>
1273+ <property name="pack_type">end</property>
1274+ <property name="position">1</property>
1275+ </packing>
1276+ </child>
1277+ </object>
1278+ <packing>
1279+ <property name="expand">False</property>
1280+ <property name="fill">False</property>
1281+ <property name="pack_type">end</property>
1282+ <property name="position">1</property>
1283+ </packing>
1284+ </child>
1285+ </object>
1286+ </child>
1287+ </object>
1288+ <packing>
1289+ <property name="expand">True</property>
1290+ <property name="homogeneous">True</property>
1291+ </packing>
1292+ </child>
1293 </object>
1294 <packing>
1295 <property name="expand">False</property>
1296@@ -276,12 +324,10 @@
1297 <child>
1298 <object class="GtkButton" id="button3">
1299 <property name="label">gtk-ok</property>
1300- <property name="use_action_appearance">False</property>
1301 <property name="visible">True</property>
1302 <property name="can_focus">True</property>
1303 <property name="receives_default">True</property>
1304 <property name="margin_right">10</property>
1305- <property name="use_action_appearance">False</property>
1306 <property name="use_stock">True</property>
1307 <signal name="clicked" handler="daemon_session_ok" swapped="no"/>
1308 </object>
1309@@ -386,12 +432,10 @@
1310 <child>
1311 <object class="GtkButton" id="add_no_collections_scan">
1312 <property name="label">Scan Again</property>
1313- <property name="use_action_appearance">False</property>
1314 <property name="visible">True</property>
1315 <property name="can_focus">True</property>
1316 <property name="receives_default">True</property>
1317 <property name="margin_right">10</property>
1318- <property name="use_action_appearance">False</property>
1319 <signal name="clicked" handler="on_reload_accomplishments_clicked" swapped="no"/>
1320 </object>
1321 <packing>
1322@@ -403,12 +447,10 @@
1323 <child>
1324 <object class="GtkButton" id="add_no_collections_quit">
1325 <property name="label">gtk-quit</property>
1326- <property name="use_action_appearance">False</property>
1327 <property name="visible">True</property>
1328 <property name="can_focus">True</property>
1329 <property name="receives_default">True</property>
1330 <property name="margin_right">10</property>
1331- <property name="use_action_appearance">False</property>
1332 <property name="use_stock">True</property>
1333 </object>
1334 <packing>
1335@@ -524,12 +566,10 @@
1336 <child>
1337 <object class="GtkButton" id="button1">
1338 <property name="label" translatable="yes">Edit credentials</property>
1339- <property name="use_action_appearance">False</property>
1340 <property name="visible">True</property>
1341 <property name="can_focus">True</property>
1342 <property name="receives_default">True</property>
1343 <property name="margin_right">10</property>
1344- <property name="use_action_appearance">False</property>
1345 <signal name="clicked" handler="edit_auth_info" swapped="no"/>
1346 </object>
1347 <packing>
1348@@ -541,11 +581,9 @@
1349 <child>
1350 <object class="GtkButton" id="button4">
1351 <property name="label" translatable="yes">Later</property>
1352- <property name="use_action_appearance">False</property>
1353 <property name="visible">True</property>
1354 <property name="can_focus">True</property>
1355 <property name="receives_default">True</property>
1356- <property name="use_action_appearance">False</property>
1357 <property name="image_position">right</property>
1358 <signal name="clicked" handler="edit_auth_info_cancel" swapped="no"/>
1359 </object>
1360@@ -728,12 +766,10 @@
1361 <child>
1362 <object class="GtkButton" id="button2">
1363 <property name="label" translatable="yes">Don't Use Ubuntu One</property>
1364- <property name="use_action_appearance">False</property>
1365 <property name="visible">True</property>
1366 <property name="can_focus">True</property>
1367 <property name="receives_default">True</property>
1368 <property name="valign">start</property>
1369- <property name="use_action_appearance">False</property>
1370 <property name="yalign">0.46000000834465027</property>
1371 <signal name="clicked" handler="cancel_register_with_u1" swapped="no"/>
1372 </object>
1373@@ -825,11 +861,9 @@
1374 <child>
1375 <object class="GtkRadioButton" id="mytrophies_filter_all">
1376 <property name="label" translatable="yes">All</property>
1377- <property name="use_action_appearance">False</property>
1378 <property name="visible">True</property>
1379 <property name="can_focus">True</property>
1380 <property name="receives_default">False</property>
1381- <property name="use_action_appearance">False</property>
1382 <property name="xalign">0</property>
1383 <property name="active">True</property>
1384 <property name="draw_indicator">False</property>
1385@@ -843,11 +877,9 @@
1386 <child>
1387 <object class="GtkRadioButton" id="mytrophies_filter_latest">
1388 <property name="label" translatable="yes">Latest</property>
1389- <property name="use_action_appearance">False</property>
1390 <property name="visible">True</property>
1391 <property name="can_focus">True</property>
1392 <property name="receives_default">False</property>
1393- <property name="use_action_appearance">False</property>
1394 <property name="xalign">0</property>
1395 <property name="active">True</property>
1396 <property name="draw_indicator">False</property>
1397@@ -867,48 +899,143 @@
1398 </packing>
1399 </child>
1400 <child>
1401- <object class="GtkScrolledWindow" id="scrolledwindow2">
1402+ <object class="GtkNotebook" id="mytrophies_notebook">
1403 <property name="visible">True</property>
1404 <property name="can_focus">True</property>
1405- <property name="hscrollbar_policy">always</property>
1406- <child>
1407- <object class="GtkViewport" id="viewport3">
1408- <property name="visible">True</property>
1409- <property name="can_focus">False</property>
1410- <property name="shadow_type">none</property>
1411- <child>
1412- <object class="GtkGrid" id="mytrophies_mainbox">
1413- <property name="visible">True</property>
1414- <property name="can_focus">False</property>
1415- <property name="orientation">vertical</property>
1416- <property name="column_homogeneous">True</property>
1417- <child>
1418- <placeholder/>
1419- </child>
1420- <child>
1421- <placeholder/>
1422- </child>
1423- <child>
1424- <placeholder/>
1425- </child>
1426- <child>
1427- <placeholder/>
1428- </child>
1429- <child>
1430- <placeholder/>
1431- </child>
1432- <child>
1433- <placeholder/>
1434- </child>
1435- </object>
1436- </child>
1437- </object>
1438+ <property name="show_tabs">False</property>
1439+ <child>
1440+ <object class="GtkScrolledWindow" id="mytrophies_box_all_window">
1441+ <property name="visible">True</property>
1442+ <property name="can_focus">True</property>
1443+ <property name="hscrollbar_policy">always</property>
1444+ <child>
1445+ <object class="GtkViewport" id="viewport3">
1446+ <property name="visible">True</property>
1447+ <property name="can_focus">False</property>
1448+ <property name="shadow_type">none</property>
1449+ <child>
1450+ <object class="GtkGrid" id="mytrophies_box_all">
1451+ <property name="visible">True</property>
1452+ <property name="can_focus">False</property>
1453+ <property name="orientation">vertical</property>
1454+ <property name="column_homogeneous">True</property>
1455+ <child>
1456+ <placeholder/>
1457+ </child>
1458+ <child>
1459+ <placeholder/>
1460+ </child>
1461+ <child>
1462+ <placeholder/>
1463+ </child>
1464+ <child>
1465+ <placeholder/>
1466+ </child>
1467+ <child>
1468+ <placeholder/>
1469+ </child>
1470+ <child>
1471+ <placeholder/>
1472+ </child>
1473+ <child>
1474+ <placeholder/>
1475+ </child>
1476+ <child>
1477+ <placeholder/>
1478+ </child>
1479+ <child>
1480+ <placeholder/>
1481+ </child>
1482+ </object>
1483+ </child>
1484+ </object>
1485+ </child>
1486+ </object>
1487+ </child>
1488+ <child type="tab">
1489+ <object class="GtkLabel" id="label11">
1490+ <property name="visible">True</property>
1491+ <property name="can_focus">False</property>
1492+ <property name="label" translatable="yes">page 1</property>
1493+ </object>
1494+ <packing>
1495+ <property name="tab_fill">False</property>
1496+ </packing>
1497+ </child>
1498+ <child>
1499+ <object class="GtkScrolledWindow" id="mytrophies_box_latest_window">
1500+ <property name="visible">True</property>
1501+ <property name="can_focus">True</property>
1502+ <property name="hscrollbar_policy">always</property>
1503+ <child>
1504+ <object class="GtkViewport" id="viewport4">
1505+ <property name="visible">True</property>
1506+ <property name="can_focus">False</property>
1507+ <property name="shadow_type">none</property>
1508+ <child>
1509+ <object class="GtkGrid" id="mytrophies_box_latest">
1510+ <property name="visible">True</property>
1511+ <property name="can_focus">False</property>
1512+ <property name="orientation">vertical</property>
1513+ <property name="column_homogeneous">True</property>
1514+ <child>
1515+ <placeholder/>
1516+ </child>
1517+ <child>
1518+ <placeholder/>
1519+ </child>
1520+ <child>
1521+ <placeholder/>
1522+ </child>
1523+ <child>
1524+ <placeholder/>
1525+ </child>
1526+ <child>
1527+ <placeholder/>
1528+ </child>
1529+ <child>
1530+ <placeholder/>
1531+ </child>
1532+ <child>
1533+ <placeholder/>
1534+ </child>
1535+ <child>
1536+ <placeholder/>
1537+ </child>
1538+ <child>
1539+ <placeholder/>
1540+ </child>
1541+ </object>
1542+ </child>
1543+ </object>
1544+ </child>
1545+ </object>
1546+ <packing>
1547+ <property name="position">1</property>
1548+ </packing>
1549+ </child>
1550+ <child type="tab">
1551+ <object class="GtkLabel" id="label15">
1552+ <property name="visible">True</property>
1553+ <property name="can_focus">False</property>
1554+ <property name="label" translatable="yes">page 2</property>
1555+ </object>
1556+ <packing>
1557+ <property name="position">1</property>
1558+ <property name="tab_fill">False</property>
1559+ </packing>
1560+ </child>
1561+ <child>
1562+ <placeholder/>
1563+ </child>
1564+ <child type="tab">
1565+ <placeholder/>
1566 </child>
1567 </object>
1568 <packing>
1569 <property name="expand">True</property>
1570 <property name="fill">True</property>
1571- <property name="position">1</property>
1572+ <property name="position">3</property>
1573 </packing>
1574 </child>
1575 </object>
1576@@ -977,7 +1104,7 @@
1577 <object class="GtkComboBox" id="opp_combo_app">
1578 <property name="visible">True</property>
1579 <property name="can_focus">False</property>
1580- <signal name="changed" handler="opp_app_updated" swapped="no"/>
1581+ <signal name="changed" handler="on_filter_collection_changed" swapped="no"/>
1582 </object>
1583 <packing>
1584 <property name="expand">False</property>
1585@@ -1001,7 +1128,7 @@
1586 <object class="GtkComboBox" id="opp_combo_cat">
1587 <property name="visible">True</property>
1588 <property name="can_focus">False</property>
1589- <signal name="changed" handler="opp_cat_updated" swapped="no"/>
1590+ <signal name="changed" handler="on_filter_category_changed" swapped="no"/>
1591 </object>
1592 <packing>
1593 <property name="expand">False</property>
1594@@ -1012,16 +1139,14 @@
1595 <child>
1596 <object class="GtkCheckButton" id="opp_showlocked">
1597 <property name="label" translatable="yes">Show Locked</property>
1598- <property name="use_action_appearance">False</property>
1599 <property name="visible">True</property>
1600 <property name="can_focus">True</property>
1601 <property name="receives_default">False</property>
1602 <property name="margin_left">5</property>
1603- <property name="use_action_appearance">False</property>
1604 <property name="xalign">0</property>
1605 <property name="active">True</property>
1606 <property name="draw_indicator">True</property>
1607- <signal name="toggled" handler="update_views" swapped="no"/>
1608+ <signal name="toggled" handler="on_filter_show_locked_clicked" swapped="no"/>
1609 </object>
1610 <packing>
1611 <property name="expand">False</property>
1612@@ -1056,11 +1181,9 @@
1613 <property name="can_focus">False</property>
1614 <child>
1615 <object class="GtkButton" id="subcats_back">
1616- <property name="use_action_appearance">False</property>
1617 <property name="visible">True</property>
1618 <property name="can_focus">True</property>
1619 <property name="receives_default">True</property>
1620- <property name="use_action_appearance">False</property>
1621 <property name="relief">none</property>
1622 <signal name="clicked" handler="subcats_back_button" swapped="no"/>
1623 <child>
1624@@ -1114,11 +1237,9 @@
1625 </child>
1626 <child>
1627 <object class="GtkButton" id="subcats_forward">
1628- <property name="use_action_appearance">False</property>
1629 <property name="visible">True</property>
1630 <property name="can_focus">True</property>
1631 <property name="receives_default">True</property>
1632- <property name="use_action_appearance">False</property>
1633 <property name="relief">none</property>
1634 <signal name="clicked" handler="subcats_forward_button" swapped="no"/>
1635 <child>

Subscribers

People subscribed via source and target branches

to all changes: