Merge lp:~mmcg069/software-center/pathbar-atk into lp:software-center

Proposed by Matthew McGowan on 2010-04-04
Status: Merged
Merged at revision: not available
Proposed branch: lp:~mmcg069/software-center/pathbar-atk
Merge into: lp:software-center
Diff against target: 4408 lines (+1961/-1978)
12 files modified
softwarecenter/app.py (+3/-2)
softwarecenter/view/appview.py (+8/-3)
softwarecenter/view/availablepane.py (+123/-78)
softwarecenter/view/channelpane.py (+6/-1)
softwarecenter/view/installedpane.py (+3/-1)
softwarecenter/view/navhistory.py (+147/-88)
softwarecenter/view/softwarepane.py (+1/-1)
softwarecenter/view/widgets/backforward.py (+140/-132)
softwarecenter/view/widgets/pathbar2.py (+0/-1605)
softwarecenter/view/widgets/pathbar_common.py (+827/-0)
softwarecenter/view/widgets/pathbar_gtk_atk.py (+703/-0)
softwarecenter/view/widgets/rgb.py (+0/-67)
To merge this branch: bzr merge lp:~mmcg069/software-center/pathbar-atk
Reviewer Review Type Date Requested Status
software-store-developers 2010-04-04 Pending
Review via email: mp+22778@code.launchpad.net

Description of the change

most notable:
* Modify PathBar to now use gtk.EventBox's for PathPart's
* Enable atk accessibility support
* Pathbar now provides for keyboard navigation, complete with focus box drawing
* Improve theme coverage
* All features of old PathBar retained in shift to new PathBar design

other:
* Tweak navhistory behaviour to better work with new Pathbar
* Other minor tweaks

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'softwarecenter/app.py'
2--- softwarecenter/app.py 2010-04-01 19:48:40 +0000
3+++ softwarecenter/app.py 2010-04-04 06:00:32 +0000
4@@ -214,7 +214,8 @@
5
6 # default focus
7 self.available_pane.searchentry.grab_focus()
8-
9+ self.window_main.set_size_request(600, 400)
10+
11 # restore state
12 self.config = get_config()
13 self.restore_state()
14@@ -561,7 +562,7 @@
15 def restore_state(self):
16 if self.config.has_option("general", "size"):
17 (x, y) = self.config.get("general", "size").split(",")
18- self.window_main.resize(int(x), int(y))
19+ self.window_main.set_default_size(int(x), int(y))
20 if (self.config.has_option("general", "maximized") and
21 self.config.getboolean("general", "maximized")):
22 self.window_main.maximize()
23
24=== modified file 'softwarecenter/view/appview.py'
25--- softwarecenter/view/appview.py 2010-04-01 19:48:40 +0000
26+++ softwarecenter/view/appview.py 2010-04-04 06:00:32 +0000
27@@ -96,6 +96,7 @@
28 data further. A python function that gets a pkgname
29 """
30 gtk.GenericTreeModel.__init__(self)
31+ self.search_query = search_query
32 self.cache = cache
33 self.db = db
34 self.icons = icons
35@@ -258,7 +259,7 @@
36 summary = app.pkgname
37 if self.db.is_appname_duplicated(appname):
38 appname = "%s (%s)" % (appname, app.pkgname)
39- s = "%s\n<small>%s</small>" % (
40+ s = "<b>%s</b>\n<small>%s</small>" % (
41 gobject.markup_escape_text(appname),
42 gobject.markup_escape_text(summary))
43 return s
44@@ -900,7 +901,9 @@
45 """
46 (path, column) = self.get_cursor()
47 model = self.get_model()
48- action_in_progress = (model[path][AppStore.COL_ACTION_IN_PROGRESS] != -1)
49+ action_in_progress = False
50+ if path:
51+ action_in_progress = (model[path][AppStore.COL_ACTION_IN_PROGRESS] != -1)
52 return action_in_progress
53
54 def _on_realize(self, widget, tr):
55@@ -948,7 +951,9 @@
56 def _on_cursor_changed(self, view):
57 # trigger callback, if we do it here get_selection() returns
58 # the previous selected row for some reason
59- gobject.timeout_add(10, self._app_selected_timeout_cb, view)
60+
61+ # mvo: can we make this timeout 1? The AppView is much nicer this way...
62+ gobject.timeout_add(1, self._app_selected_timeout_cb, view)
63
64 def _app_selected_timeout_cb(self, view):
65 selection = view.get_selection()
66
67=== modified file 'softwarecenter/view/availablepane.py'
68--- softwarecenter/view/availablepane.py 2010-03-05 15:53:27 +0000
69+++ softwarecenter/view/availablepane.py 2010-04-04 06:00:32 +0000
70@@ -48,7 +48,7 @@
71 (PAGE_CATEGORY,
72 PAGE_APPLIST,
73 PAGE_APP_DETAILS) = range(3)
74-
75+
76 # define ID values for the various buttons found in the navigation bar
77 NAV_BUTTON_ID_CATEGORY = "category"
78 NAV_BUTTON_ID_LIST = "list"
79@@ -73,14 +73,14 @@
80 self.connect("app-list-changed", self._on_app_list_changed)
81 self.current_app_by_category = {}
82 self.current_app_by_subcategory = {}
83+ # track navigation history
84+ self.nav_history = NavigationHistory(self)
85 # UI
86 self._build_ui()
87- # track navigation history
88- self.nav_history = NavigationHistory(self)
89
90 def _build_ui(self):
91 # categories, appview and details into the notebook in the bottom
92- self.cat_view = CategoriesView(self.datadir, APP_INSTALL_PATH,
93+ self.cat_view = CategoriesView(self.datadir, APP_INSTALL_PATH,
94 self.db,
95 self.icons)
96 scroll_categories = gtk.ScrolledWindow()
97@@ -88,8 +88,8 @@
98 scroll_categories.add(self.cat_view)
99 self.notebook.append_page(scroll_categories, gtk.Label("categories"))
100 # sub-categories view
101- self.subcategories_view = CategoriesView(self.datadir,
102- APP_INSTALL_PATH,
103+ self.subcategories_view = CategoriesView(self.datadir,
104+ APP_INSTALL_PATH,
105 self.db,
106 self.icons,
107 self.cat_view.categories[0])
108@@ -108,7 +108,7 @@
109 self.top_hbox.pack_start(self.back_forward, expand=False, padding=self.PADDING)
110 # nav buttons first in the panel
111 self.top_hbox.reorder_child(self.back_forward, 0)
112- # now a vbox for subcategories and applist
113+ # now a vbox for subcategories and applist
114 apps_vbox = gtk.VPaned()
115 apps_vbox.pack1(self.scroll_subcategories, resize=True)
116 apps_vbox.pack2(self.scroll_app_list)
117@@ -120,9 +120,10 @@
118 # set status text
119 self._update_status_text(len(self.db))
120 # home button
121- self.navigation_bar.add_with_id(_("Get Software"),
122+ self.navigation_bar.add_with_id(_("Get Software"),
123 self.on_navigation_category,
124 self.NAV_BUTTON_ID_CATEGORY,
125+ do_callback=True,
126 animate=False)
127
128 def _get_query(self):
129@@ -137,7 +138,7 @@
130 elif self.apps_category:
131 cat_query = self.apps_category.query
132 # mix category with the search terms and return query
133- return self.db.get_query_list_from_search_entry(self.apps_search_term,
134+ return self.db.get_query_list_from_search_entry(self.apps_search_term,
135 cat_query)
136
137 def _in_no_display_category(self):
138@@ -150,7 +151,7 @@
139 def _show_hide_subcategories(self):
140 # check if have subcategories and are not in a subcategory
141 # view - if so, show it
142- if (self.apps_category and
143+ if (self.apps_category and
144 self.apps_category.subcategories and
145 not (self.apps_search_term or self.apps_subcategory)):
146 self.scroll_subcategories.show()
147@@ -163,16 +164,16 @@
148 # not hide it
149 model = self.app_view.get_model()
150 if (model and
151- len(model) == 0 and
152+ len(model) == 0 and
153 self.apps_category and
154- self.apps_category.subcategories and
155+ self.apps_category.subcategories and
156 not self.apps_subcategory):
157 self.scroll_app_list.hide()
158 else:
159 self.scroll_app_list.show()
160
161 def refresh_apps(self):
162- """refresh the applist after search changes and update the
163+ """refresh the applist after search changes and update the
164 navigation bar
165 """
166 logging.debug("refresh_apps")
167@@ -185,18 +186,28 @@
168 def _refresh_apps_with_apt_cache(self):
169 # build query
170 query = self._get_query()
171- logging.debug("availablepane query: %s" % query)
172- # *ugh* deactivate the old model because otherwise it keeps
173- # getting progress_changed events and eats CPU time until its
174- # garbage collected
175+
176 old_model = self.app_view.get_model()
177 if old_model is not None:
178- old_model.active = False
179+ # check if new AppStore query == old query. if yes, do nothing
180+ if isinstance(old_model, AppStore) and \
181+ str(old_model.search_query) == str(query):
182+ # check if we show subcategoriy
183+ self._show_hide_applist()
184+ self.emit("app-list-changed", len(old_model))
185+ return
186+ else:
187+ # *ugh* deactivate the old model because otherwise it keeps
188+ # getting progress_changed events and eats CPU time until its
189+ # garbage collected
190+ old_model.active = False
191+
192+ logging.debug("availablepane query: %s" % query)
193 # create new model and attach it
194 new_model = AppStore(self.cache,
195- self.db,
196- self.icons,
197- query,
198+ self.db,
199+ self.icons,
200+ query,
201 limit=self.apps_limit,
202 sort=self.apps_sorted,
203 filter=self.apps_filter)
204@@ -210,15 +221,14 @@
205 """Update the navigation button"""
206 if self.apps_category and not self.apps_search_term:
207 cat = self.apps_category.name
208- self.navigation_bar.add_with_id(cat,
209+ self.navigation_bar.add_with_id(cat,
210 self.on_navigation_list,
211- self.NAV_BUTTON_ID_LIST,
212- None)
213+ self.NAV_BUTTON_ID_LIST, True)
214+
215 elif self.apps_search_term:
216 self.navigation_bar.add_with_id(_("Search Results"),
217- self.on_navigation_search,
218- self.NAV_BUTTON_ID_SEARCH,
219- None)
220+ self.on_navigation_search,
221+ self.NAV_BUTTON_ID_SEARCH, True)
222
223 # status text woo
224 def get_status_text(self):
225@@ -228,7 +238,7 @@
226 self._in_no_display_category()):
227 return ""
228 return self._status_text
229-
230+
231 def get_current_app(self):
232 """return the current active application object"""
233 if self.is_category_view_showing():
234@@ -238,13 +248,13 @@
235 return self.current_app_by_subcategory.get(self.apps_subcategory)
236 else:
237 return self.current_app_by_category.get(self.apps_category)
238-
239+
240 def _on_app_list_changed(self, pane, length):
241 """internal helper that keeps the status text up-to-date by
242 keeping track of the app-list-changed signals
243 """
244 self._update_status_text(length)
245-
246+
247 def _update_status_text(self, length):
248 """
249 update the text in the status bar
250@@ -280,11 +290,17 @@
251 self.apps_search_term = ""
252 self.navigation_bar.remove_id(self.NAV_BUTTON_ID_SEARCH)
253
254+ def _check_nav_history(self, display_cb):
255+ if self.navigation_bar.get_last().label != self.nav_history.get_last_label():
256+ nav_item = NavigationItem(self, display_cb)
257+ self.nav_history.navigate_no_cursor_step(nav_item)
258+ return
259+
260 # callbacks
261 def on_cache_ready(self, cache):
262 """ refresh the application list when the cache is re-opened """
263- # just re-draw in the available pane, nothing but the
264- # "is-installed" overlay icon will change when something
265+ # just re-draw in the available pane, nothing but the
266+ # "is-installed" overlay icon will change when something
267 # is installed or removed in the available pane
268 self.app_view.queue_draw()
269
270@@ -322,67 +338,89 @@
271 self.refresh_apps()
272 self._show_category_overview()
273
274+ def display_category(self):
275+ self._clear_search()
276+ self._show_category_overview()
277+ return
278+
279+ def display_search(self):
280+ self.navigation_bar.remove_id(self.NAV_BUTTON_ID_DETAILS)
281+ self.notebook.set_current_page(self.PAGE_APPLIST)
282+ self.emit("app-list-changed", len(self.app_view.get_model()))
283+ self.searchentry.show()
284+ return
285+
286+ def display_list(self):
287+ self.navigation_bar.remove_id(self.NAV_BUTTON_ID_SUBCAT)
288+ self.navigation_bar.remove_id(self.NAV_BUTTON_ID_DETAILS)
289+
290+ if self.apps_subcategory:
291+ self.apps_subcategory = None
292+ self.set_category(self.apps_category)
293+ if self.apps_search_term:
294+ self._clear_search()
295+ self.refresh_apps()
296+
297+ self.notebook.set_current_page(self.PAGE_APPLIST)
298+ model = self.app_view.get_model()
299+ self.emit("app-list-changed", len(model))
300+ self.searchentry.show()
301+ return
302+
303+ def display_list_subcat(self):
304+ if self.apps_search_term:
305+ self._clear_search()
306+ self.refresh_apps()
307+ self.set_category(self.apps_subcategory)
308+ self.navigation_bar.remove_id(self.NAV_BUTTON_ID_DETAILS)
309+ self.notebook.set_current_page(self.PAGE_APPLIST)
310+ self.emit("app-list-changed", len(self.app_view.get_model()))
311+ self.searchentry.show()
312+ return
313+
314+ def display_details(self):
315+ self.notebook.set_current_page(self.PAGE_APP_DETAILS)
316+ self.searchentry.hide()
317+ return
318+
319 def on_navigation_category(self, pathbar, part):
320 """callback when the navigation button with id 'category' is clicked"""
321- if pathbar and not pathbar.get_active():
322- return
323 # clear the search
324- self._clear_search()
325- self._show_category_overview()
326- self.nav_history.navigate(CategoryViewNavigationItem(self))
327+ self.display_category()
328+ nav_item = NavigationItem(self, self.display_category)
329+ self.nav_history.navigate(nav_item)
330
331 def on_navigation_search(self, pathbar, part):
332 """ callback when the navigation button with id 'search' is clicked"""
333- self.navigation_bar.remove_id(self.NAV_BUTTON_ID_DETAILS)
334- self.notebook.set_current_page(self.PAGE_APPLIST)
335- self.emit("app-list-changed", len(self.app_view.get_model()))
336- self.nav_history.navigate(SearchNavigationItem(self))
337- self.searchentry.show()
338+ self.display_search()
339+ nav_item = NavigationItem(self, self.display_search)
340+ self.nav_history.navigate(nav_item)
341
342 def on_navigation_list(self, pathbar, part):
343 """callback when the navigation button with id 'list' is clicked"""
344- if pathbar and not pathbar.get_active():
345- return
346- self.navigation_bar.remove_id(self.NAV_BUTTON_ID_SUBCAT)
347- self.navigation_bar.remove_id(self.NAV_BUTTON_ID_DETAILS)
348- if self.apps_subcategory:
349- self.apps_subcategory = None
350- self.set_category(self.apps_category)
351- if self.apps_search_term:
352- self._clear_search()
353- self.refresh_apps()
354- self.notebook.set_current_page(self.PAGE_APPLIST)
355- model = self.app_view.get_model()
356- self.emit("app-list-changed", len(model))
357- self.searchentry.show()
358- self.nav_history.navigate(AppListNavigationItem(self))
359+ self.display_list()
360+ nav_item = NavigationItem(self, self.display_list)
361+ self.nav_history.navigate(nav_item)
362
363 def on_navigation_list_subcategory(self, pathbar, part):
364- if pathbar and not pathbar.get_active():
365- return
366- if self.apps_search_term:
367- self._clear_search()
368- self.refresh_apps()
369- self.set_category(self.apps_subcategory)
370- self.navigation_bar.remove_id(self.NAV_BUTTON_ID_DETAILS)
371- self.notebook.set_current_page(self.PAGE_APPLIST)
372- self.emit("app-list-changed", len(self.app_view.get_model()))
373- self.searchentry.show()
374- self.nav_history.navigate(AppListSubcategoryNavigationItem(self))
375+ self.display_list_subcat()
376+ nav_item = NavigationItem(self, self.display_list_subcat)
377+ self.nav_history.navigate(nav_item)
378
379 def on_navigation_details(self, pathbar, part):
380 """callback when the navigation button with id 'details' is clicked"""
381- if pathbar and not pathbar.get_active():
382- return
383- self.notebook.set_current_page(self.PAGE_APP_DETAILS)
384- self.searchentry.hide()
385- self.nav_history.navigate(AppDetailsNavigationItem(self))
386+ self.display_details()
387+ nav_item = NavigationItem(self, self.display_details)
388+ self.nav_history.navigate(nav_item)
389
390 def on_subcategory_activated(self, cat_view, category):
391 #print cat_view, name, query
392 logging.debug("on_subcategory_activated: %s %s" % (
393 category.name, category))
394 self.apps_subcategory = category
395+
396+ #self._check_nav_history(self.display_list)
397+
398 self.navigation_bar.add_with_id(
399 category.name, self.on_navigation_list_subcategory, self.NAV_BUTTON_ID_SUBCAT)
400
401@@ -392,21 +430,24 @@
402 category.name, category))
403 self.apps_category = category
404 self.set_category(category)
405-
406+
407 def on_application_selected(self, appview, app):
408 """callback when an app is selected"""
409 logging.debug("on_application_selected: '%s'" % app)
410+
411 if self.apps_subcategory:
412+ #self._check_nav_history(self.display_list_subcat)
413 self.current_app_by_subcategory[self.apps_subcategory] = app
414 else:
415+ #self._check_nav_history(self.display_list)
416 self.current_app_by_category[self.apps_category] = app
417-
418+
419 def on_nav_back_clicked(self, widget, event):
420 self.nav_history.nav_back()
421
422 def on_nav_forward_clicked(self, widget, event):
423 self.nav_history.nav_forward()
424-
425+
426 def is_category_view_showing(self):
427 # check if we are in the category page or if we display a
428 # sub-category page that has no visible applications
429@@ -414,9 +455,13 @@
430 not self.scroll_app_list.props.visible)
431
432 def set_category(self, category):
433+ def _cb():
434+ self.refresh_apps()
435+ self.notebook.set_current_page(self.PAGE_APPLIST)
436+ return False
437+
438 self.update_navigation_button()
439- self.refresh_apps()
440- self.notebook.set_current_page(self.PAGE_APPLIST)
441+ gobject.idle_add(_cb)
442
443 if __name__ == "__main__":
444 #logging.basicConfig(level=logging.DEBUG)
445
446=== modified file 'softwarecenter/view/channelpane.py'
447--- softwarecenter/view/channelpane.py 2010-03-23 16:50:23 +0000
448+++ softwarecenter/view/channelpane.py 2010-04-04 06:00:32 +0000
449@@ -23,6 +23,7 @@
450 import os
451 import sys
452 import xapian
453+import gobject
454
455 from gettext import gettext as _
456
457@@ -99,6 +100,10 @@
458 old_model = self.app_view.get_model()
459 if old_model is not None:
460 old_model.active = False
461+ gobject.idle_add(self._make_new_model, query)
462+ return False
463+
464+ def _make_new_model(self, query):
465 # get a new store and attach it to the view
466 new_model = AppStore(self.cache,
467 self.db,
468@@ -110,7 +115,7 @@
469 self.app_view.set_model(new_model)
470 self.emit("app-list-changed", len(new_model))
471 return False
472-
473+
474 def set_channel(self, channel):
475 """
476 set the current software channel object for display in the channel pane
477
478=== modified file 'softwarecenter/view/installedpane.py'
479--- softwarecenter/view/installedpane.py 2010-03-03 15:39:40 +0000
480+++ softwarecenter/view/installedpane.py 2010-04-04 06:00:32 +0000
481@@ -50,6 +50,7 @@
482 # UI
483 self._build_ui()
484 def _build_ui(self):
485+ self.navigation_bar.set_size_request(26, -1)
486 self.notebook.append_page(self.scroll_app_list, gtk.Label("installed"))
487 # details
488 self.notebook.append_page(self.scroll_details, gtk.Label("details"))
489@@ -82,7 +83,8 @@
490 query = None
491 self.navigation_bar.add_with_id(_("Installed Software"),
492 self.on_navigation_list,
493- "list")
494+ "list",
495+ animate=False)
496 # *ugh* deactivate the old model because otherwise it keeps
497 # getting progress_changed events and eats CPU time until it's
498 # garbage collected
499
500=== modified file 'softwarecenter/view/navhistory.py'
501--- softwarecenter/view/navhistory.py 2010-03-19 21:27:31 +0000
502+++ softwarecenter/view/navhistory.py 2010-04-04 06:00:32 +0000
503@@ -15,7 +15,8 @@
504 # You should have received a copy of the GNU General Public License along with
505 # this program; if not, write to the Free Software Foundation, Inc.,
506 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
507-import copy
508+
509+import gobject
510 import logging
511
512 from softwarecenter.utils import unescape
513@@ -28,94 +29,117 @@
514 class to manage navigation history in the "Get Software" section (the
515 available pane).
516 """
517-
518+
519+ MAX_NAV_ITEMS = 10 # limit number of NavItems allowed in the NavStack
520+
521+
522 def __init__(self, available_pane):
523 self.available_pane = available_pane
524- # always start at main category view
525- self._current_nav_item = CategoryViewNavigationItem(available_pane)
526 # use stacks to track navigation history
527- self._nav_back_stack = []
528- self._nav_forward_stack = []
529-
530- def navigate(self, dest_nav_item):
531+ self._nav_stack = NavigationStack(self.MAX_NAV_ITEMS)
532+
533+ def navigate(self, nav_item):
534 """
535 append a new NavigationItem to the history stack
536 """
537 if in_replay_history_mode:
538 return
539- logging.debug("submit navitem for history: %s" % dest_nav_item)
540- # TODO: Detect multiple clicks on the same nav button and filter
541- # them out - we don't want them in the history
542- dest_nav_item.parent = self
543- self._nav_back_stack.append(self._current_nav_item)
544- self._current_nav_item = dest_nav_item
545- # reset navigation forward stack on a direct navigation
546- self._nav_forward_stack = []
547- # update buttons
548- self.available_pane.back_forward.left.set_sensitive(True)
549+
550+ nav_item.parent = self
551+ self._nav_stack.append(nav_item)
552+
553+ if self._nav_stack.cursor > 0:
554+ self.available_pane.back_forward.left.set_sensitive(True)
555 self.available_pane.back_forward.right.set_sensitive(False)
556-
557+
558+ def navigate_no_cursor_step(self, nav_item):
559+ if in_replay_history_mode:
560+ return
561+
562+ nav_item.parent = self
563+ self._nav_stack.append_no_cursor_step(nav_item)
564+ return
565+
566 def nav_forward(self):
567 """
568 navigate forward one item in the history stack
569 """
570+ nav_item = self._nav_stack.step_forward()
571+ nav_item.navigate_to()
572+
573 self.available_pane.back_forward.left.set_sensitive(True)
574- if len(self._nav_forward_stack) <= 1:
575+ if self._nav_stack.at_end():
576+ if self.available_pane.back_forward.right.has_focus():
577+ self.available_pane.back_forward.left.grab_focus()
578 self.available_pane.back_forward.right.set_sensitive(False)
579- nav_item = self._nav_forward_stack.pop()
580- self._nav_back_stack.append(self._current_nav_item)
581- self._current_nav_item = nav_item
582- nav_item.navigate_to()
583-
584+
585 def nav_back(self):
586 """
587 navigate back one item in the history stack
588 """
589+ nav_item = self._nav_stack.step_back()
590+ nav_item.navigate_to()
591+
592 self.available_pane.back_forward.right.set_sensitive(True)
593- if len(self._nav_back_stack) <= 1:
594+ if self._nav_stack.at_start():
595+ if self.available_pane.back_forward.left.has_focus():
596+ self.available_pane.back_forward.right.grab_focus()
597 self.available_pane.back_forward.left.set_sensitive(False)
598- nav_item = self._nav_back_stack.pop()
599- logging.debug("nav_back: %s" % nav_item)
600- self._nav_forward_stack.append(self._current_nav_item)
601- self._current_nav_item = nav_item
602- nav_item.navigate_to()
603-
604+
605+ def get_last_label(self):
606+ if self._nav_stack.stack:
607+ if self._nav_stack[-1].parts:
608+ return self._nav_stack[-1].parts[-1].label
609+ return None
610+
611+
612 class NavigationItem(object):
613 """
614 class to implement navigation points to be managed in the history queues
615 """
616
617- def __init__(self, available_pane):
618+ def __init__(self, available_pane, update_available_pane_cb):
619 self.available_pane = available_pane
620+ self.update_available_pane = update_available_pane_cb
621 self.apps_category = available_pane.apps_category
622 self.apps_subcategory = available_pane.apps_subcategory
623 self.apps_search_term = available_pane.apps_search_term
624 self.current_app = available_pane.get_current_app()
625- self.parts = self.available_pane.navigation_bar.get_parts()[:]
626-
627+ self.parts = self.available_pane.navigation_bar.get_parts()
628+
629 def navigate_to(self):
630 """
631 navigate to the view that corresponds to this NavigationItem
632 """
633 global in_replay_history_mode
634 in_replay_history_mode = True
635- self.available_pane.apps_category = self.apps_category
636- self.available_pane.apps_subcategory = self.apps_subcategory
637- self.available_pane.apps_search_term = self.apps_search_term
638- self.available_pane.searchentry.set_text(self.apps_search_term)
639- self.available_pane.searchentry.set_position(-1)
640- self.available_pane.app_details.show_app(self.current_app)
641- # first part is special and kept in remove_all
642- self.available_pane.navigation_bar.remove_all()
643+ available_pane = self.available_pane
644+ available_pane.apps_category = self.apps_category
645+ available_pane.apps_subcategory = self.apps_subcategory
646+ available_pane.apps_search_term = self.apps_search_term
647+ available_pane.searchentry.set_text(self.apps_search_term)
648+ available_pane.searchentry.set_position(-1)
649+ available_pane.app_details.show_app(self.current_app)
650+
651+ nav_bar = self.available_pane.navigation_bar
652+ nav_bar.remove_all(do_callback=False)
653+
654 for part in self.parts[1:]:
655- self.available_pane.navigation_bar.add_with_id(unescape(part.label),
656- part.callback,
657- part.id,
658- do_callback=False,
659- animate=False)
660- self.parts[-1].activate()
661+ nav_bar.add_with_id(unescape(part.label),
662+ part.callback,
663+ part.get_name(),
664+ do_callback=False,
665+ animate=False)
666+
667+ gobject.idle_add(self._update_available_pane_cb, nav_bar)
668 in_replay_history_mode = False
669-
670+
671+ def _update_available_pane_cb(self, nav_bar):
672+ last_part = nav_bar.get_parts()[-1]
673+ nav_bar.set_active_no_callback(last_part)
674+ self.update_available_pane()
675+ return False
676+
677 def __str__(self):
678 details = []
679 details.append("\n%s" % type(self))
680@@ -130,41 +154,76 @@
681 details.append(" current_app: %s" % self.current_app)
682 details.append(" apps_search_term: %s" % self.apps_search_term)
683 return '\n'.join(details)
684-
685-class CategoryViewNavigationItem(NavigationItem):
686- """
687- navigation item that corresponds to the main category view
688- Note: all subclasses of NavigationItem are for debug use only and
689- can be collapsed to the NavigationItem class if desired
690- """
691-
692-class AppListNavigationItem(NavigationItem):
693- """
694- navigation item that corresponds to the application list for the
695- specified category
696- Note: all subclasses of NavigationItem are for debug use only and
697- can be collapsed to the NavigationItem class if desired
698- """
699-
700-class AppListSubcategoryNavigationItem(NavigationItem):
701- """
702- navigation item that corresponds to the application list for the
703- specified category and subcategory
704- Note: all subclasses of NavigationItem are for debug use only and
705- can be collapsed to the NavigationItem class if desired
706- """
707-
708-class AppDetailsNavigationItem(NavigationItem):
709- """
710- navigation item that corresponds to the details view for the
711- specified application
712- Note: all subclasses of NavigationItem are for debug use only and
713- can be collapsed to the NavigationItem class if desired
714- """
715-
716-class SearchNavigationItem(NavigationItem):
717- """
718- navigation item that corresponds to a search in progress
719- Note: all subclasses of NavigationItem are for debug use only and
720- can be collapsed to the NavigationItem class if desired
721- """
722+
723+
724+class NavigationStack(object):
725+
726+ def __init__(self, max_length):
727+ self.max_length = max_length
728+ self.stack = []
729+ self.cursor = 0
730+ return
731+
732+ def __len__(self):
733+ return len(self.stack)
734+
735+ def __repr__(self):
736+ BOLD = "\033[1m"
737+ RESET = "\033[0;0m"
738+ s = '['
739+ for i, item in enumerate(self.stack):
740+ if i != self.cursor:
741+ s += str(item.parts[-1].label) + ', '
742+ else:
743+ s += BOLD + str(item.parts[-1].label) + RESET + ', '
744+ return s + ']'
745+
746+ def __getitem__(self, item):
747+ return self.stack[item]
748+
749+ def _isok(self, item):
750+ if len(self.stack) == 0: return True
751+ pre_item = self.stack[-1]
752+ if pre_item.parts[-1].label == item.parts[-1].label:
753+ if pre_item.apps_search_term != item.apps_search_term:
754+ return True
755+ return False
756+ return True
757+
758+ def append(self, item):
759+ if not self._isok(item):
760+ self.cursor = len(self.stack)-1
761+ print 'A:', repr(self)
762+ return
763+ if len(self.stack) + 1 > self.max_length:
764+ self.stack.pop(0)
765+ self.stack.append(item)
766+ self.cursor = len(self.stack)-1
767+ print 'A:', repr(self)
768+ return
769+
770+ def append_no_cursor_step(self, item):
771+ if not self._isok(item):
772+ print 'a:', repr(self)
773+ return
774+ if len(self.stack) + 1 > self.max_length:
775+ self.stack.pop(0)
776+ self.stack.append(item)
777+ print 'a:', repr(self)
778+ return
779+
780+ def step_back(self):
781+ self.cursor -= 1
782+ print 'B:', repr(self)
783+ return self.stack[self.cursor]
784+
785+ def step_forward(self):
786+ self.cursor += 1
787+ print 'B:', repr(self)
788+ return self.stack[self.cursor]
789+
790+ def at_end(self):
791+ return self.cursor == len(self.stack)-1
792+
793+ def at_start(self):
794+ return self.cursor == 0
795
796=== modified file 'softwarecenter/view/softwarepane.py'
797--- softwarecenter/view/softwarepane.py 2010-03-29 20:40:17 +0000
798+++ softwarecenter/view/softwarepane.py 2010-04-04 06:00:32 +0000
799@@ -29,7 +29,7 @@
800 if "SOFTWARE_CENTER_OLD_PATHBAR" in os.environ:
801 from widgets.navigationbar import NavigationBar
802 else:
803- from widgets.pathbar2 import NavigationBar
804+ from widgets.pathbar_gtk_atk import NavigationBar
805
806 from widgets.searchentry import SearchEntry
807
808
809=== modified file 'softwarecenter/view/widgets/backforward.py'
810--- softwarecenter/view/widgets/backforward.py 2010-02-15 02:53:26 +0000
811+++ softwarecenter/view/widgets/backforward.py 2010-04-04 06:00:32 +0000
812@@ -17,14 +17,14 @@
813 # along with this program. If not, see <http://www.gnu.org/licenses/>.
814
815
816-
817-import rgb
818+import atk
819 import gtk
820 import cairo
821 import gobject
822-import pathbar2
823-
824-from rgb import to_float as f
825+import pathbar_common
826+
827+from gettext import gettext as _
828+
829
830 # pi constants
831 M_PI = 3.1415926535897931
832@@ -43,72 +43,132 @@
833
834 def __init__(self):
835 gtk.HBox.__init__(self)
836+ self.theme = pathbar_common.PathBarStyle(self)
837 sep = SeparatorPart()
838
839 if self.get_direction() != gtk.TEXT_DIR_RTL:
840+ # ltr
841 self.left = ButtonPartLeft('left-clicked')
842 self.right = ButtonPartRight('right-clicked')
843+ self.set_button_atk_info_ltr()
844 else:
845+ # rtl
846 self.left = ButtonPartRight('left-clicked')
847 self.right = ButtonPartLeft('right-clicked')
848+ self.set_button_atk_info_rtl()
849+
850+ atk_obj = self.get_accessible()
851+ atk_obj.set_name(_('History Navigation'))
852+ atk_obj.set_description(_('Navigate forwards and backwards.'))
853+ atk_obj.set_role(atk.ROLE_PANEL)
854
855 self.pack_start(self.left)
856 self.pack_start(sep, False)
857 self.pack_end(self.right)
858
859- self.theme = self._pick_theme()
860- self.connect("realize", self._on_realize)
861- return
862-
863- def _pick_theme(self, name=None):
864- name = name or gtk.settings_get_default().get_property("gtk-theme-name")
865- themes = pathbar2.PathBarThemes.DICT
866- if themes.has_key(name):
867- return themes[name]()
868- print "No styling hints for %s are available" % name
869- return pathbar2.PathBarThemeHuman()
870-
871- def _on_realize(self, widget):
872- self.theme.load(self.style)
873- return
874-
875+ sep.connect_after("style-set", self._on_style_set)
876+ return
877+
878+ def set_button_atk_info_ltr(self):
879+ # left button
880+ atk_obj = self.left.get_accessible()
881+ atk_obj.set_name(_('Back Button'))
882+ atk_obj.set_description(_('Navigates back.'))
883+ atk_obj.set_role(atk.ROLE_PUSH_BUTTON)
884+
885+ # right button
886+ atk_obj = self.right.get_accessible()
887+ atk_obj.set_name(_('Forward Button'))
888+ atk_obj.set_description(_('Navigates forward.'))
889+ atk_obj.set_role(atk.ROLE_PUSH_BUTTON)
890+ return
891+
892+ def set_button_atk_info_rtl(self):
893+ # right button
894+ atk_obj = self.right.get_accessible()
895+ atk_obj.set_name(_('Back Button'))
896+ atk_obj.set_description(_('Navigates back.'))
897+ atk_obj.set_role(atk.ROLE_PUSH_BUTTON)
898+
899+ # left button
900+ atk_obj = self.left.get_accessible()
901+ atk_obj.set_name(_('Forward Button'))
902+ atk_obj.set_description(_('Navigates forward.'))
903+ atk_obj.set_role(atk.ROLE_PUSH_BUTTON)
904+ return
905+
906+ def _on_style_set(self, widget, oldstyle):
907+ # when alloc.width == 1, this is typical of an unallocated widget,
908+ # lets not break a sweat for nothing...
909+ if self.allocation.width == 1:
910+ return
911+
912+ old_xthickness = self.theme['xthickness']
913+ self.theme = pathbar_common.PathBarStyle(self)
914+ if old_xthickness > self.theme['xthickness']:
915+ a = self.allocation
916+ self.queue_draw_area(a.x, a.y,
917+ a.width+self.theme['xthickness'], a.height)
918+ else:
919+ self.queue_draw()
920+ return
921
922 class SeparatorPart(gtk.DrawingArea):
923
924 def __init__(self):
925 gtk.DrawingArea.__init__(self)
926- self.set_size_request(1, -1)
927+ self.theme = pathbar_common.PathBarStyle(self)
928+ self.set_size_request(self.theme['xthickness'], -1)
929+
930+ atk_obj = self.get_accessible()
931+ atk_obj.set_role(atk.ROLE_SEPARATOR)
932+
933 self.connect("expose-event", self._on_expose)
934+ self.connect("style-set", self._on_style_set)
935 return
936
937 def _on_expose(self, widget, event):
938+ parent = self.get_parent()
939+ if not parent: return
940 cr = widget.window.cairo_create()
941- a = event.area
942- cr.rectangle(a.x, a.y+1, a.width, a.height-2)
943- cr.set_source_rgba(0, 0, 0, 0.45)
944+ cr.rectangle(event.area)
945+ cr.set_source_rgb(*self.theme.dark_line[self.state].tofloats())
946 cr.fill()
947 del cr
948 return
949
950+ def _on_style_set(self, widget, old_style):
951+ self.theme = pathbar_common.PathBarStyle(self)
952+ self.set_size_request(self.theme['xthickness'], -1)
953+ return
954+
955
956 class ButtonPart(gtk.DrawingArea):
957
958 ARROW_SIZE = (12,12)
959- DEFAULT_SIZE = (30, 28)
960+ DEFAULT_SIZE = (31, 27)
961
962 def __init__(self, arrow_type, signal_name):
963 gtk.DrawingArea.__init__(self)
964 self.set_size_request(*self.DEFAULT_SIZE)
965+ self.shape = pathbar_common.SHAPE_RECTANGLE
966 self.button_down = False
967 self.shadow_type = gtk.SHADOW_OUT
968 self.arrow_type = arrow_type
969+
970+ self.set_flags(gtk.CAN_FOCUS)
971 self.set_events(gtk.gdk.ENTER_NOTIFY_MASK|
972 gtk.gdk.LEAVE_NOTIFY_MASK|
973 gtk.gdk.BUTTON_PRESS_MASK|
974 gtk.gdk.BUTTON_RELEASE_MASK)
975+
976 self.connect("enter-notify-event", self._on_enter)
977 self.connect("leave-notify-event", self._on_leave)
978 self.connect("button-press-event", self._on_press)
979+ self.connect("key-press-event", self._on_key_press)
980+ self.connect("key-release-event", self._on_key_release, signal_name)
981+ self.connect('focus-in-event', self._on_focus_in)
982+ self.connect('focus-out-event', self._on_focus_out)
983 self.connect("button-release-event", self._on_release, signal_name)
984 return
985
986@@ -135,11 +195,32 @@
987 self.set_active(True)
988 return
989
990+ def _on_key_press(self, widget, event):
991+ # react to spacebar, enter, numpad-enter
992+ if event.keyval in (32, 65293, 65421):
993+ self.set_state(gtk.STATE_ACTIVE)
994+ return
995+
996+ def _on_key_release(self, widget, event, signal_name):
997+ # react to spacebar, enter, numpad-enter
998+ if event.keyval in (32, 65293, 65421):
999+ self.set_state(gtk.STATE_SELECTED)
1000+ self.get_parent().emit(signal_name, event)
1001+ return
1002+
1003 def _on_leave(self, widget, event):
1004 if self.state == gtk.STATE_INSENSITIVE: return
1005 self.set_active(False)
1006 return
1007
1008+ def _on_focus_in(self, widget, event):
1009+ self.queue_draw()
1010+ return
1011+
1012+ def _on_focus_out(self, widget, event):
1013+ self.queue_draw()
1014+ return
1015+
1016 def _on_press(self, widget, event):
1017 if self.state == gtk.STATE_INSENSITIVE: return
1018 self.button_down = True
1019@@ -160,117 +241,41 @@
1020 self.set_state(gtk.STATE_NORMAL)
1021 return
1022
1023-# def expose_gtk(self, widget, area, x, y, width, height):
1024-# # button background
1025-# widget.style.paint_box(widget.window,
1026-# self.state,
1027-# self.shadow_type,
1028-# area,
1029-# widget,
1030-# "button",
1031-# x,
1032-# y,
1033-# width,
1034-# height)
1035-
1036-# # arrow
1037-# aw, ah = self.ARROW_SIZE
1038-# widget.style.paint_arrow(widget.window,
1039-# self.state,
1040-# self.shadow_type,
1041-# area,
1042-# widget,
1043-# "button",
1044-# self.arrow_type,
1045-# True,
1046-# (area.width - aw)/2,
1047-# (area.height - ah)/2,
1048-# aw,
1049-# ah)
1050-# return
1051-
1052- def expose_pathbar(self, widget, area, x, y, width, height):
1053+ def expose_pathbar(self, widget, area, x, y, w, h, xo=0, wo=0):
1054+ if not self.parent: return
1055 # background
1056 cr = widget.window.cairo_create()
1057 cr.rectangle(area)
1058 cr.clip()
1059
1060- cr.translate(x, y)
1061-
1062- self._draw_bg(cr,
1063- width,
1064- height,
1065- self.state,
1066- self.style,
1067- self.get_parent().theme,
1068- self.get_parent().theme.curvature)
1069+ self.parent.theme.paint_bg(cr,
1070+ self,
1071+ x, y, w, h)
1072 del cr
1073
1074 # arrow
1075+ if self.has_focus():
1076+ self.style.paint_focus(self.window,
1077+ self.state,
1078+ (x+4+xo, y+4, w-8+wo, h-8),
1079+ self,
1080+ 'button',
1081+ x+4+xo, y+4,
1082+ w-8+wo, h-8)
1083+
1084 aw, ah = self.ARROW_SIZE
1085- widget.style.paint_arrow(widget.window,
1086- self.state,
1087- self.shadow_type,
1088- area,
1089- widget,
1090- "button",
1091- self.arrow_type,
1092- True,
1093- (area.width - aw)/2,
1094- (area.height - ah)/2,
1095- aw,
1096- ah)
1097- return
1098-
1099- def _draw_bg(self, cr, w, h, state, style, theme, r):
1100- # outer slight bevel or focal highlight
1101- self._draw_rect(cr, 0, 0, w, h, r)
1102- cr.set_source_rgba(0, 0, 0, 0.055)
1103- cr.fill()
1104-
1105- # colour scheme dicts
1106- bg = theme.bg_colors
1107- outer = theme.dark_line_colors
1108- inner = theme.light_line_colors
1109-
1110- # bg linear vertical gradient
1111- if state != gtk.STATE_PRELIGHT:
1112- color1, color2 = bg[state]
1113- else:
1114- if self.state == gtk.STATE_ACTIVE:
1115- color1, color2 = bg[theme.PRELIT_NORMAL]
1116- else:
1117- color1, color2 = bg[theme.PRELIT_ACTIVE]
1118-
1119- self._draw_rect(cr, 1, 1, w-1, h-1, r)
1120- lin = cairo.LinearGradient(0, 0, 0, h-1)
1121- lin.add_color_stop_rgb(0.0, *color1)
1122- lin.add_color_stop_rgb(1.0, *color2)
1123- cr.set_source(lin)
1124- cr.fill()
1125-
1126- cr.set_line_width(1.0)
1127- # strong outline
1128- self._draw_rect(cr, 1.5, 1.5, w-1.5, h-1.5, r)
1129- cr.set_source_rgb(*outer[state])
1130- cr.stroke()
1131-
1132- # inner bevel/highlight
1133- if theme.light_line_colors[state]:
1134- self._draw_rect(cr, 2.5, 2.5, w-2.5, h-2.5, r)
1135- r, g, b = inner[state]
1136- cr.set_source_rgba(r, g, b, 0.6)
1137- cr.stroke()
1138- return
1139-
1140- def _draw_rect(self, cr, x, y, w, h, r):
1141- global M_PI, PI_OVER_180
1142- cr.new_sub_path()
1143- cr.arc(r+x, r+y, r, M_PI, 270*PI_OVER_180)
1144- cr.arc(w-r, r+y, r, 270*PI_OVER_180, 0)
1145- cr.arc(w-r, h-r, r, 0, 90*PI_OVER_180)
1146- cr.arc(r+x, h-r, r, 90*PI_OVER_180, M_PI)
1147- cr.close_path()
1148+ ax, ay = (area.width - aw)/2, (area.height - ah)/2,
1149+
1150+ self.style.paint_arrow(self.window,
1151+ self.state,
1152+ self.shadow_type,
1153+ (ax, ay, aw, ah),
1154+ self,
1155+ "button",
1156+ self.arrow_type,
1157+ True,
1158+ ax, ay,
1159+ aw, ah)
1160 return
1161
1162
1163@@ -288,7 +293,8 @@
1164 area.x,
1165 area.y,
1166 area.width + 10,
1167- area.height)
1168+ area.height,
1169+ wo=-10)
1170 return
1171
1172
1173@@ -303,8 +309,10 @@
1174 area = event.area
1175 expose_func(widget,
1176 area,
1177- area.x - 10,
1178+ area.x-10,
1179 area.y,
1180- area.width + 10,
1181- area.height)
1182+ area.width+10,
1183+ area.height,
1184+ xo=10,
1185+ wo=-10)
1186 return
1187
1188=== removed file 'softwarecenter/view/widgets/pathbar2.py'
1189--- softwarecenter/view/widgets/pathbar2.py 2010-03-03 09:18:00 +0000
1190+++ softwarecenter/view/widgets/pathbar2.py 1970-01-01 00:00:00 +0000
1191@@ -1,1605 +0,0 @@
1192-# Copyright (C) 2009 Matthew McGowan
1193-#
1194-# Authors:
1195-# Matthew McGowan
1196-#
1197-# This program is free software: you can redistribute it and/or modify
1198-# it under the terms of the GNU General Public License as published by
1199-# the Free Software Foundation, either version 3 of the License, or
1200-# (at your option) any later version.
1201-#
1202-# This program is distributed in the hope that it will be useful,
1203-# but WITHOUT ANY WARRANTY; without even the implied warranty of
1204-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1205-# GNU General Public License for more details.
1206-#
1207-# You should have received a copy of the GNU General Public License
1208-# along with this program. If not, see <http://www.gnu.org/licenses/>.
1209-
1210-import atk
1211-import cairo
1212-import gobject
1213-import gtk
1214-import pango
1215-import rgb
1216-
1217-from rgb import to_float as f
1218-
1219-# pi constants
1220-M_PI = 3.1415926535897931
1221-PI_OVER_180 = 0.017453292519943295
1222-
1223-from gettext import gettext as _
1224-
1225-class PathBar(gtk.DrawingArea):
1226-
1227- # shapes
1228- SHAPE_RECTANGLE = 0
1229- SHAPE_START_ARROW = 1
1230- SHAPE_MID_ARROW = 2
1231- SHAPE_END_CAP = 3
1232-
1233- def __init__(self, group=None):
1234- gtk.DrawingArea.__init__(self)
1235- self.__init_drawing()
1236- self.set_redraw_on_allocate(False)
1237-
1238- self.__parts = []
1239- self.__active_part = None
1240- self.__focal_part = None
1241- self.__button_down = False, None
1242-
1243- self.__scroller = None
1244- self.__scroll_xO = 0
1245-
1246- self.theme = self.__pick_theme()
1247-
1248- atk_desc = self.get_accessible()
1249- # Accessibility name for the pathbar
1250- atk_desc.set_name(_("You are here:"))
1251- atk_desc.set_role(atk.ROLE_PANEL)
1252-
1253- # setup event handling
1254- self.set_flags(gtk.CAN_FOCUS)
1255- self.set_events(gtk.gdk.POINTER_MOTION_MASK|
1256- gtk.gdk.BUTTON_PRESS_MASK|
1257- gtk.gdk.BUTTON_RELEASE_MASK|
1258- gtk.gdk.KEY_RELEASE_MASK|
1259- gtk.gdk.KEY_PRESS_MASK|
1260- gtk.gdk.ENTER_NOTIFY_MASK|
1261- gtk.gdk.LEAVE_NOTIFY_MASK)
1262-
1263- self.connect("motion-notify-event", self.__motion_notify_cb)
1264- self.connect("enter-notify-event", self.__enter_notify_cb)
1265- self.connect("leave-notify-event", self.__leave_notify_cb)
1266- self.connect("button-press-event", self.__button_press_cb)
1267- self.connect("button-release-event", self.__button_release_cb)
1268-# self.connect("key-release-event", self.__key_release_cb)
1269-
1270- self.connect("realize", self.__realize_cb)
1271- self.connect("expose-event", self.__expose_cb)
1272- self.connect("style-set", self.__style_change_cb)
1273- self.connect("size-allocate", self.__allocation_change_cb)
1274- return
1275-
1276- def get_parts(self):
1277- return self.__parts
1278-
1279- def set_active(self, part, do_callback=True):
1280- part.set_state(gtk.STATE_ACTIVE)
1281- prev, redraw = self.__set_active(part, do_callback)
1282- if redraw:
1283- self.queue_draw_area(*prev.get_allocation_tuple())
1284- self.queue_draw_area(*part.get_allocation_tuple())
1285- return
1286-
1287- def get_active(self):
1288- return self.__active_part
1289-
1290-# def get_left_part(self):
1291-# active = self.get_active()
1292-# if not active:
1293-# return self.__parts[0]
1294-
1295-# i = self.__parts.index(active)+1
1296-# if i > len(self.__parts)-1:
1297-# i = 0
1298-# return self.__parts[i]
1299-
1300-# def get_right_part(self):
1301-# active = self.get_active()
1302-# if not active:
1303-# return self.__parts[0]
1304-
1305-# i = self.__parts.index(active)-1
1306-# if i < 0:
1307-# i = len(self.__parts)-1
1308-# return self.__parts[i]
1309-
1310- def append(self, part, do_callback=True, animate=True):
1311- prev, did_shrink = self.__append(part, do_callback)
1312- if not self.get_property("visible"):
1313- return False
1314-
1315- if animate and self.theme.animate and len(self.__parts) > 1:
1316- aw = self.theme.arrow_width
1317-
1318- # calc draw_area
1319- x,y,w,h = part.get_allocation_tuple()
1320- w += aw
1321-
1322- # begin scroll animation
1323- self.__hscroll_out_init(
1324- part.get_width(),
1325- gtk.gdk.Rectangle(x,y,w,h),
1326- self.theme.scroll_duration_ms,
1327- self.theme.scroll_fps
1328- )
1329- else:
1330- self.queue_draw_area(*part.get_allocation_tuple())
1331- return False
1332-
1333- def append_no_callback(self, part):
1334- self.append(part, do_callback=False)
1335-
1336- def remove(self, part):
1337- if len(self.__parts)-1 < 1:
1338- print 'The first part is sacred ;)'
1339- return
1340-
1341- old_w = self.__draw_width()
1342-
1343- # remove part from interal part list
1344- try:
1345- del self.__parts[self.__parts.index(part)]
1346- except:
1347- print 'part not in list!'
1348- return
1349- self.__compose_parts(self.__parts[-1], False)
1350-
1351- if old_w >= self.allocation.width:
1352- self.__grow_check(old_w, self.allocation)
1353- self.queue_draw()
1354-
1355- else:
1356- self.queue_draw_area(*part.get_allocation_tuple())
1357- self.queue_draw_area(*self.__parts[-1].get_allocation_tuple())
1358- return
1359-
1360- def remove_all(self, keep_first_part=True):
1361- """remove all elements"""
1362- if keep_first_part:
1363- self.__parts = [self.__parts[0],] # keep first part though!
1364- self.__compose_parts(self.__parts[-1], False)
1365- else:
1366- self.__parts = []
1367- self.id_to_part = {}
1368- self.queue_draw()
1369- return
1370-
1371- def navigate_up(self):
1372- if len(self.__parts) > 1:
1373- nav_part = self.__parts[len(self.__parts) - 2]
1374- self.set_active(nav_part)
1375- return
1376-
1377-# def navigate_up(self, remove_pathparts=False):
1378-# index = self.__parts.index(self.__active_part)
1379-# if index-1 == -1: return None, index-1, len(self.__parts)
1380-# self.set_active(self.__parts[index-1], remove_pathparts)
1381-# return self.__parts[index-1], index-1, len(self.__parts)
1382-
1383-# def navigate_down(self):
1384-# index = self.__parts.index(self.__active_part)
1385-# if self.__parts[index] == self.__parts[-1]: return None, index+1, len(self.__parts)
1386-# self.set_active(self.__parts[index+1], False)
1387-# return self.__parts[index+1], index+1, len(self.__parts)
1388-
1389- def __set_active(self, part, do_callback):
1390- prev_active = self.__active_part
1391- redraw = False
1392- if part.callback and do_callback:
1393- part.callback(self, part)
1394- if prev_active and prev_active != part:
1395- prev_active.set_state(gtk.STATE_NORMAL)
1396- redraw = True
1397-
1398- self.__active_part = part
1399- return prev_active, redraw
1400-
1401- def __append(self, part, do_callback=True):
1402- # clean up any exisitng scroll callbacks
1403- if self.__scroller:
1404- gobject.source_remove(self.__scroller)
1405- self.__scroll_xO = 0
1406-
1407- # the basics
1408- x = self.__draw_width()
1409- self.__parts.append(part)
1410- part.set_pathbar(self)
1411-
1412- self.set_active(part, do_callback)
1413-
1414- # determin part shapes, and calc modified parts widths
1415- prev = self.__compose_parts(part, True)
1416- # set the position of new part
1417- part.set_x(x)
1418-
1419- # check parts fit to widgets allocated width
1420- if x + part.get_width() > self.allocation.width and \
1421- self.allocation.width != 1:
1422- self.__shrink_check(self.allocation)
1423- return prev, True
1424-
1425- return prev, False
1426-
1427-# def __shorten(self, n):
1428-# n = int(n)
1429-# old_w = self.__draw_width()
1430-# end_active = self.get_active() == self.__parts[-1]
1431-
1432-# if len(self.__parts)-n < 1:
1433-# print WARNING + 'The first part is sacred ;)' + ENDC
1434-# return old_w, False
1435-
1436-# del self.__parts[-n:]
1437-# self.__compose_parts(self.__parts[-1], False)
1438-
1439-# if end_active:
1440-# self.set_active(self.__parts[-1])
1441-
1442-# if old_w >= self.allocation.width:
1443-# self.__grow_check(old_w, self.allocation)
1444-# return old_w, True
1445-
1446-# return old_w, False
1447-
1448- def __shrink_check(self, allocation):
1449- path_w = self.__draw_width()
1450- shrinkage = path_w - allocation.width
1451- mpw = self.theme.min_part_width
1452- xO = 0
1453-
1454- for part in self.__parts[:-1]:
1455- w = part.get_width()
1456- dw = 0
1457-
1458- if w - shrinkage <= mpw:
1459- dw = w - mpw
1460- shrinkage -= dw
1461- part.set_size(mpw, -1)
1462- part.set_x(part.get_x() - xO)
1463-
1464- else:
1465- part.set_size(w - shrinkage, -1)
1466- part.set_x(part.get_x() - xO)
1467- dw = shrinkage
1468- shrinkage = 0
1469-
1470- xO += dw
1471-
1472- last = self.__parts[-1]
1473- last.set_x(last.get_x() - xO)
1474- return
1475-
1476- def __grow_check(self, old_width, allocation):
1477- parts = self.__parts
1478- if len(parts) == 0:
1479- return
1480-
1481- growth = old_width - self.__draw_width()
1482- parts.reverse()
1483-
1484- for part in parts:
1485- bw = part.get_size_requisition()[0]
1486- w = part.get_width()
1487-
1488- if w < bw:
1489- dw = bw - w
1490-
1491- if dw <= growth:
1492- growth -= dw
1493- part.set_size(bw, -1)
1494- part.set_x(part.get_x() + growth)
1495-
1496- else:
1497- part.set_size(w + growth, -1)
1498- growth = 0
1499-
1500- else:
1501- part.set_x(part.get_x() + growth)
1502-
1503- parts.reverse()
1504- shift = parts[0].get_x()
1505-
1506- # left align parts
1507- if shift > 0:
1508- for part in parts: part.set_x(part.get_x() - shift)
1509- return
1510-
1511- def __compose_parts(self, last, prev_set_size):
1512- parts = self.__parts
1513-
1514- if len(parts) == 1:
1515- last.set_shape(self.SHAPE_RECTANGLE)
1516- last.set_size(*last.calc_size_requisition())
1517- prev = None
1518-
1519- elif len(parts) == 2:
1520- prev = parts[0]
1521- prev.set_shape(self.SHAPE_START_ARROW)
1522- prev.calc_size_requisition()
1523-
1524- last.set_shape(self.SHAPE_END_CAP)
1525- last.set_size(*last.calc_size_requisition())
1526-
1527- else:
1528- prev = parts[-2]
1529- prev.set_shape(self.SHAPE_MID_ARROW)
1530- prev.calc_size_requisition()
1531-
1532- last.set_shape(self.SHAPE_END_CAP)
1533- last.set_size(*last.calc_size_requisition())
1534-
1535- if prev and prev_set_size:
1536- prev.set_size(*prev.get_size_requisition())
1537- return prev
1538-
1539- def __draw_width(self):
1540- l = len(self.__parts)
1541- if l == 0:
1542- return 0
1543- a = self.__parts[-1].allocation
1544- return a[0] + a[2]
1545-
1546- def __hscroll_out_init(self, distance, draw_area, duration, fps):
1547- self.__scroller = gobject.timeout_add(
1548- int(1000.0 / fps), # interval
1549- self.__hscroll_out_cb,
1550- distance,
1551- duration*0.001, # 1 over duration (converted to seconds)
1552- gobject.get_current_time(),
1553- draw_area.x,
1554- draw_area.y,
1555- draw_area.width,
1556- draw_area.height)
1557- return
1558-
1559- def __hscroll_out_cb(self, distance, duration, start_t, x, y, w, h):
1560- cur_t = gobject.get_current_time()
1561- xO = distance - distance*((cur_t - start_t) / duration)
1562-
1563- if xO > 0:
1564- self.__scroll_xO = xO
1565- self.queue_draw_area(x, y, w, h)
1566- else: # final frame
1567- self.__scroll_xO = 0
1568- # redraw the entire widget
1569- # incase some timeouts are skipped due to high system load
1570- self.queue_draw()
1571- self.__scroller = None
1572- return False
1573- return True
1574-
1575- def __part_at_xy(self, x, y):
1576- for part in self.__parts:
1577- a = part.get_allocation()
1578- region = gtk.gdk.region_rectangle(a)
1579-
1580- if region.point_in(int(x), int(y)):
1581- return part
1582- return None
1583-
1584- def __draw_hscroll(self, cr):
1585- if len(self.__parts) < 2:
1586- return
1587-
1588- # draw the last two parts
1589- prev, last = self.__parts[-2:]
1590-
1591- # style theme stuff
1592- style, r, aw, shapes = self.style, self.theme.curvature, \
1593- self.theme.arrow_width, self.__shapes
1594-
1595- # draw part that need scrolling
1596- self.__draw_part(cr,
1597- last,
1598- style,
1599- r,
1600- aw,
1601- shapes,
1602- self.__scroll_xO)
1603-
1604- # draw the last part that does not scroll
1605- self.__draw_part(cr,
1606- prev,
1607- style,
1608- r,
1609- aw,
1610- shapes)
1611- return
1612-
1613- def __draw_all(self, cr, event_area):
1614- style = self.style
1615- r = self.theme.curvature
1616- aw = self.theme.arrow_width
1617- shapes = self.__shapes
1618- region = gtk.gdk.region_rectangle(event_area)
1619-
1620- # if a scroll is pending we want to not draw the final part,
1621- # as we don't want to prematurely reveal the part befor the
1622- # scroll animation has had a chance to start
1623- if self.__scroller:
1624- parts = self.__parts[:-1]
1625- else:
1626- parts = self.__parts
1627-
1628- parts.reverse()
1629- for part in parts:
1630- if region.rect_in(part.get_allocation()) != gtk.gdk.OVERLAP_RECTANGLE_OUT:
1631- self.__draw_part(cr, part, style, r, aw, shapes)
1632- parts.reverse()
1633- return
1634-
1635- def __draw_part_ltr(self, cr, part, style, r, aw, shapes, sxO=0):
1636- x, y, w, h = part.get_allocation()
1637- shape = part.shape
1638- state = part.state
1639- icon_pb = part.icon.pixbuf
1640-
1641- cr.save()
1642- cr.translate(x-sxO, y)
1643-
1644- # draw bg
1645- self.__draw_part_bg(cr, part, w, h, state, shape, style,r, aw, shapes)
1646-
1647- # determine left margin. left margin depends on part shape
1648- # and whether there exists an icon or not
1649- if shape == self.SHAPE_MID_ARROW or shape == self.SHAPE_END_CAP:
1650- margin = int(0.75*self.theme.arrow_width + self.theme.xpadding)
1651- else:
1652- margin = self.theme.xpadding
1653-
1654- # draw icon
1655- if icon_pb:
1656- cr.set_source_pixbuf(
1657- icon_pb,
1658- self.theme.xpadding-sxO,
1659- (alloc.height - icon_pb.get_height())/2)
1660- cr.paint()
1661- margin += icon_pb.get_width() + self.theme.spacing
1662-
1663- # if space is limited and an icon is set, dont draw label
1664- # otherwise, draw label
1665- if w == self.theme.min_part_width and icon_pb:
1666- pass
1667-
1668- else:
1669- layout = part.get_layout()
1670- lw, lh = layout.get_pixel_size()
1671- dst_x = x + margin - int(sxO)
1672- dst_y = (self.allocation.height - lh)/2+1
1673- style.paint_layout(
1674- self.window,
1675- self.theme.text_state[state],
1676- False,
1677- (dst_x, dst_y, lw+4, lh), # clip area
1678- self,
1679- None,
1680- dst_x,
1681- dst_y,
1682- layout)
1683-
1684- cr.restore()
1685- return
1686-
1687- def __draw_part_rtl(self, cr, part, style, r, aw, shapes, sxO=0):
1688- x, y, w, h = part.get_allocation()
1689- shape = part.shape
1690- state = part.state
1691- icon_pb = part.icon.pixbuf
1692-
1693- cr.save()
1694- cr.translate(x+sxO, y)
1695-
1696- # draw bg
1697- self.__draw_part_bg(cr, part, w, h, state, shape, style,r, aw, shapes)
1698-
1699- # determine left margin. left margin depends on part shape
1700- # and whether there exists an icon or not
1701- if shape == self.SHAPE_MID_ARROW or shape == self.SHAPE_END_CAP:
1702- margin = self.theme.arrow_width + self.theme.xpadding
1703- else:
1704- margin = self.theme.xpadding
1705-
1706- # draw icon
1707- if icon_pb:
1708- margin += icon_pb.get_width()
1709- cr.set_source_pixbuf(
1710- icon_pb,
1711- w - margin + sxO,
1712- (h - icon_pb.get_height())/2)
1713- cr.paint()
1714- margin += self.spacing
1715-
1716- # if space is limited and an icon is set, dont draw label
1717- # otherwise, draw label
1718- if w == self.theme.min_part_width and icon_pb:
1719- pass
1720-
1721- else:
1722- layout = part.get_layout()
1723- lw, lh = layout.get_pixel_size()
1724- dst_x = x + part.get_width() - margin - lw + int(sxO)
1725- dst_y = (self.allocation.height - lh)/2+1
1726- style.paint_layout(
1727- self.window,
1728- self.theme.text_state[state],
1729- False,
1730- None,
1731- self,
1732- None,
1733- dst_x,
1734- dst_y,
1735- layout)
1736-
1737- cr.restore()
1738- return
1739-
1740- def __draw_part_bg(self, cr, part, w, h, state, shape, style, r, aw, shapes):
1741- # outer slight bevel or focal highlight
1742- shapes[shape](cr, 0, 0, w, h, r, aw)
1743- cr.set_source_rgba(0, 0, 0, 0.055)
1744- cr.fill()
1745-
1746- # colour scheme dicts
1747- bg = self.theme.bg_colors
1748- outer = self.theme.dark_line_colors
1749- inner = self.theme.light_line_colors
1750-
1751- # bg linear vertical gradient
1752- if state != gtk.STATE_PRELIGHT:
1753- color1, color2 = bg[state]
1754- else:
1755- if part != self.get_active():
1756- color1, color2 = bg[self.theme.PRELIT_NORMAL]
1757- else:
1758- color1, color2 = bg[self.theme.PRELIT_ACTIVE]
1759-
1760- shapes[shape](cr, 1, 1, w-1, h-1, r, aw)
1761- lin = cairo.LinearGradient(0, 0, 0, h-1)
1762- lin.add_color_stop_rgb(0.0, *color1)
1763- lin.add_color_stop_rgb(1.0, *color2)
1764- cr.set_source(lin)
1765- cr.fill()
1766-
1767- cr.set_line_width(1.0)
1768- # strong outline
1769- shapes[shape](cr, 1.5, 1.5, w-1.5, h-1.5, r, aw)
1770- cr.set_source_rgb(*outer[state])
1771- cr.stroke()
1772-
1773- # inner bevel/highlight
1774- if self.theme.light_line_colors[state]:
1775- shapes[shape](cr, 2.5, 2.5, w-2.5, h-2.5, r, aw)
1776- r, g, b = inner[state]
1777- cr.set_source_rgba(r, g, b, 0.6)
1778- cr.stroke()
1779- return
1780-
1781- def __shape_rect(self, cr, x, y, w, h, r, aw):
1782- global M_PI, PI_OVER_180
1783- cr.new_sub_path()
1784- cr.arc(r+x, r+y, r, M_PI, 270*PI_OVER_180)
1785- cr.arc(w-r, r+y, r, 270*PI_OVER_180, 0)
1786- cr.arc(w-r, h-r, r, 0, 90*PI_OVER_180)
1787- cr.arc(r+x, h-r, r, 90*PI_OVER_180, M_PI)
1788- cr.close_path()
1789- return
1790-
1791- def __shape_start_arrow_ltr(self, cr, x, y, w, h, r, aw):
1792- global M_PI, PI_OVER_180
1793- cr.new_sub_path()
1794- cr.arc(r+x, r+y, r, M_PI, 270*PI_OVER_180)
1795- # arrow head
1796- cr.line_to(w-aw+1, y)
1797- cr.line_to(w, (h+y)*0.5)
1798- cr.line_to(w-aw+1, h)
1799- cr.arc(r+x, h-r, r, 90*PI_OVER_180, M_PI)
1800- cr.close_path()
1801- return
1802-
1803- def __shape_mid_arrow_ltr(self, cr, x, y, w, h, r, aw):
1804- cr.move_to(-1, y)
1805- # arrow head
1806- cr.line_to(w-aw+1, y)
1807- cr.line_to(w, (h+y)*0.5)
1808- cr.line_to(w-aw+1, h)
1809- cr.line_to(-1, h)
1810- cr.close_path()
1811- return
1812-
1813- def __shape_end_cap_ltr(self, cr, x, y, w, h, r, aw):
1814- global M_PI, PI_OVER_180
1815- cr.move_to(-1, y)
1816- cr.arc(w-r, r+y, r, 270*PI_OVER_180, 0)
1817- cr.arc(w-r, h-r, r, 0, 90*PI_OVER_180)
1818- cr.line_to(-1, h)
1819- cr.close_path()
1820- return
1821-
1822- def __shape_start_arrow_rtl(self, cr, x, y, w, h, r, aw):
1823- global M_PI, PI_OVER_180
1824- cr.new_sub_path()
1825- cr.move_to(x, (h+y)*0.5)
1826- cr.line_to(aw-1, y)
1827- cr.arc(w-r, r+y, r, 270*PI_OVER_180, 0)
1828- cr.arc(w-r, h-r, r, 0, 90*PI_OVER_180)
1829- cr.line_to(aw-1, h)
1830- cr.close_path()
1831- return
1832-
1833- def __shape_mid_arrow_rtl(self, cr, x, y, w, h, r, aw):
1834- cr.move_to(x, (h+y)*0.5)
1835- cr.line_to(aw-1, y)
1836- cr.line_to(w+1, y)
1837- cr.line_to(w+1, h)
1838- cr.line_to(aw-1, h)
1839- cr.close_path()
1840- return
1841-
1842- def __shape_end_cap_rtl(self, cr, x, y, w, h, r, aw):
1843- global M_PI, PI_OVER_180
1844- cr.arc(r+x, r+y, r, M_PI, 270*PI_OVER_180)
1845- cr.line_to(w+1, y)
1846- cr.line_to(w+1, h)
1847- cr.arc(r+x, h-r, r, 90*PI_OVER_180, M_PI)
1848- cr.close_path()
1849- return
1850-
1851- def __state(self, part):
1852- # returns the idle state of the part depending on
1853- # whether part is active or not.
1854- if part == self.__active_part:
1855- return gtk.STATE_ACTIVE
1856- return gtk.STATE_NORMAL
1857-
1858- def __tooltip_check(self, part):
1859- # only show a tooltip if part is truncated, i.e. not all label text is
1860- # visible.
1861- if part.is_truncated():
1862- self.set_has_tooltip(False)
1863- gobject.timeout_add(50, self.__set_tooltip_cb, part.label)
1864- else:
1865- self.set_has_tooltip(False)
1866- return
1867-
1868- def __set_tooltip_cb(self, text):
1869- # callback allows the tooltip position to be updated as pointer moves
1870- # accross different parts
1871- self.set_has_tooltip(True)
1872- self.set_tooltip_markup(text)
1873- return False
1874-
1875- def __pick_theme(self, name=None):
1876- name = name or gtk.settings_get_default().get_property("gtk-theme-name")
1877- themes = PathBarThemes.DICT
1878- if themes.has_key(name):
1879- return themes[name]()
1880- print "No styling hints for %s are available" % name
1881- return PathBarThemeHuman()
1882-
1883- def __init_drawing(self):
1884- if self.get_direction() != gtk.TEXT_DIR_RTL:
1885- self.__draw_part = self.__draw_part_ltr
1886- self.__shapes = {
1887- self.SHAPE_RECTANGLE : self.__shape_rect,
1888- self.SHAPE_START_ARROW : self.__shape_start_arrow_ltr,
1889- self.SHAPE_MID_ARROW : self.__shape_mid_arrow_ltr,
1890- self.SHAPE_END_CAP : self.__shape_end_cap_ltr}
1891- else:
1892- self.__draw_part = self.__draw_part_rtl
1893- self.__shapes = {
1894- self.SHAPE_RECTANGLE : self.__shape_rect,
1895- self.SHAPE_START_ARROW : self.__shape_start_arrow_rtl,
1896- self.SHAPE_MID_ARROW : self.__shape_mid_arrow_rtl,
1897- self.SHAPE_END_CAP : self.__shape_end_cap_rtl}
1898- return
1899-
1900- def __motion_notify_cb(self, widget, event):
1901- if self.__scroll_xO > 0:
1902- return
1903-
1904- part = self.__part_at_xy(event.x, event.y)
1905- prev_focal = self.__focal_part
1906-
1907- if self.__button_down[0]:
1908- if part and prev_focal and part != prev_focal:
1909- if self.__button_down[1] == part:
1910- part.set_state(gtk.STATE_SELECTED)
1911- else:
1912- part.set_state(gtk.STATE_PRELIGHT)
1913-
1914- prev_focal.set_state(self.__state(prev_focal))
1915- self.queue_draw_area(*prev_focal.get_allocation_tuple())
1916- self.queue_draw_area(*part.get_allocation_tuple())
1917- self.__focal_part = part
1918- return
1919-
1920- if part and part.state != gtk.STATE_PRELIGHT:
1921- self.__tooltip_check(part)
1922- part.set_state(gtk.STATE_PRELIGHT)
1923-
1924- if prev_focal:
1925- prev_focal.set_state(self.__state(prev_focal))
1926- self.queue_draw_area(*prev_focal.get_allocation_tuple())
1927-
1928- self.__focal_part = part
1929- self.queue_draw_area(*part.get_allocation_tuple())
1930-
1931- elif not part and prev_focal != None and \
1932- not widget.window.get_pointer()[2] & gtk.gdk.BUTTON1_MASK:
1933- prev_focal.set_state(self.__state(prev_focal))
1934- self.queue_draw_area(*prev_focal.get_allocation_tuple())
1935- self.__focal_part = None
1936- return
1937-
1938- def __enter_notify_cb(self, widget, event):
1939- if not self.__button_down[0] and not widget.window.get_pointer()[2] & gtk.gdk.BUTTON1_MASK:
1940- return
1941-
1942- part = self.__part_at_xy(event.x, event.y)
1943- prev_focal = self.__focal_part
1944-
1945- if part and prev_focal == part:
1946- part.set_state(gtk.STATE_SELECTED)
1947- self.queue_draw_area(*part.get_allocation_tuple())
1948- return
1949-
1950- def __leave_notify_cb(self, widget, event):
1951- prev_focal = self.__focal_part
1952- if prev_focal:
1953- prev_focal.set_state(self.__state(prev_focal))
1954- self.queue_draw_area(*prev_focal.get_allocation_tuple())
1955-
1956- if not widget.window.get_pointer()[2] & gtk.gdk.BUTTON1_MASK:
1957- self.__focal_part = None
1958- return
1959-
1960- def __button_press_cb(self, widget, event):
1961- part = self.__part_at_xy(event.x, event.y)
1962- self.__button_down = True, part
1963- if part:
1964- part.set_state(gtk.STATE_SELECTED)
1965- self.queue_draw_area(*part.get_allocation_tuple())
1966- self.__focal_part = part
1967- return
1968-
1969- def __button_release_cb(self, widget, event):
1970- part = self.__part_at_xy(event.x, event.y)
1971- if self.__focal_part and self.__focal_part != part:
1972- pass
1973- elif part and self.__button_down[0]:
1974- prev_active, redraw = self.__set_active(part, True)
1975- part.set_state(gtk.STATE_PRELIGHT)
1976- self.queue_draw_area(*part.get_allocation_tuple())
1977-
1978- if redraw:
1979- self.queue_draw_area(*prev_active.get_allocation_tuple())
1980- self.__button_down = False, None
1981- return
1982-
1983-# def __key_release_cb(self, widget, event):
1984-# part = None
1985-
1986-# # left key pressed
1987-# if event.keyval == 65363:
1988-# part = self.get_left_part()
1989-
1990-# # right key pressed
1991-# elif event.keyval == 65361:
1992-# part = self.get_right_part()
1993-
1994-# if not part: return
1995-
1996-# prev_active = self.set_active(part)
1997-# self.queue_draw_area(*part.allocation)
1998-# if prev_active:
1999-# self.queue_draw_area(*prev_active.allocation)
2000-
2001-# part.emit("clicked", event.copy())
2002-# return
2003-
2004- def __realize_cb(self, widget):
2005- self.theme.load(widget.style)
2006- return
2007-
2008- def __expose_cb(self, widget, event):
2009- cr = widget.window.cairo_create()
2010-
2011- if self.theme.base_hack:
2012- cr.set_source_rgb(*self.theme.base_hack)
2013- cr.paint()
2014-
2015- if self.__scroll_xO:
2016- self.__draw_hscroll(cr)
2017- else:
2018- self.__draw_all(cr, event.area)
2019-
2020- del cr
2021- return
2022-
2023- def __style_change_cb(self, widget, old_style):
2024- # when alloc.width == 1, this is typical of an unallocated widget,
2025- # lets not break a sweat for nothing...
2026- if self.allocation.width == 1:
2027- return
2028-
2029- self.theme = self.__pick_theme()
2030- self.theme.load(widget.style)
2031- # set height to 0 so that if part height has been reduced the widget will
2032- # shrink to an appropriate new height based on new font size
2033- self.set_size_request(-1, 28)
2034-
2035- parts = self.__parts
2036- self.__parts = []
2037-
2038- # recalc best fits, re-append then draw all
2039- for part in parts:
2040-
2041- if part.icon.pixbuf:
2042- part.icon.load_pixbuf()
2043-
2044- part.calc_size_requisition()
2045- self.__append(part)
2046-
2047- self.queue_draw()
2048- return
2049-
2050- def __allocation_change_cb(self, widget, allocation):
2051- if allocation.width == 1:
2052- return
2053-
2054- path_w = self.__draw_width()
2055- if path_w == allocation.width:
2056- return
2057- elif path_w > allocation.width:
2058- self.__shrink_check(allocation)
2059- else:
2060- self.__grow_check(allocation.width, allocation)
2061-
2062- self.queue_draw()
2063- return
2064-
2065-# FIXME: stubs currently and not working
2066-class IAtkComponent(atk.Component):
2067- # atk --------------------------------------------------------
2068- def contains(x, y, coord_type):
2069- # atk stub
2070- return False
2071- def ref_accessible_at_point(x, y, coord_type):
2072- # atk stub
2073- pass
2074- def get_extents(coord_type):
2075- # atk stub
2076- (0, 0, 0, 0)
2077- def get_position(coord_type):
2078- # atk stub
2079- (0, 0)
2080- def get_size(self):
2081- # atk stub
2082- (0, 0)
2083- def grab_focus(self):
2084- # atk stub
2085- return False
2086- def remove_focus_handler(self, handler_id):
2087- # atk stub
2088- pass
2089- def set_extents(self, x, y, width, height, coord_type):
2090- # atk stub
2091- return False
2092- def set_position(self, x, y, coord_type):
2093- # atk stub
2094- return False
2095- def set_size(self, width, height):
2096- # atk stub
2097- return False
2098- def get_layer(self):
2099- # atk stub
2100- return atk.LAYER_WIDGET
2101- def get_mdi_zorder(self):
2102- # atk stub
2103- return 1
2104- #--------------------------------
2105-
2106-
2107-class PathPart(atk.Object, IAtkComponent):
2108-
2109- def __init__(self, parent, label=None, callback=None):
2110- atk.Object.__init__(self)
2111- self.__requisition = (0,0)
2112- self.__layout = None
2113- self.__pbar = None
2114-
2115- # self.set_name() would work as well, *but* we have that
2116- # function already for a different purpose, so we need to
2117- # explicitely call
2118- parent_atk = parent.get_accessible()
2119- atk.Object.set_name(self, label)
2120- atk.Object.set_role(self, atk.ROLE_PUSH_BUTTON)
2121- atk.Object.add_relationship(self, atk.RELATION_MEMBER_OF, parent_atk)
2122- atk.Object.set_parent(self, parent_atk)
2123- #print parent_atk
2124- #print parent_atk.get_n_accessible_children()
2125-
2126- self.allocation = [0, 0, 0, 0]
2127- self.state = gtk.STATE_NORMAL
2128- self.shape = PathBar.SHAPE_RECTANGLE
2129-
2130- self.name = None
2131- self.callback = callback
2132- self.set_label(label or "")
2133- self.icon = PathBarIcon()
2134- return
2135-
2136- def set_callback(self, cb):
2137- self.callback = cb
2138- return
2139-
2140- def set_name(self, name):
2141- self.name = name
2142- return
2143-
2144- def set_label(self, label):
2145- # escape special characters
2146- label = gobject.markup_escape_text(label.strip())
2147- # some hackery to preserve italics markup
2148- label = label.replace('&lt;i&gt;', '<i>').replace('&lt;/i&gt;', '</i>')
2149- self.label = label
2150- atk.Object.set_name(self, label)
2151- return
2152-
2153- def set_icon(self, stock_icon, size=gtk.ICON_SIZE_BUTTON):
2154- self.icon.specify(stock_icon, size)
2155- self.icon.load_pixbuf()
2156- return
2157-
2158- def set_state(self, gtk_state):
2159- self.state = gtk_state
2160- return
2161-
2162- def set_shape(self, shape):
2163- self.shape = shape
2164- return
2165-
2166- def set_x(self, x):
2167- self.allocation[0] = int(x)
2168- return
2169-
2170- def set_size(self, w, h):
2171- if w != -1: self.allocation[2] = int(w)
2172- if h != -1: self.allocation[3] = int(h)
2173- self.__calc_layout_width(self.__layout, self.shape, self.__pbar)
2174- return
2175-
2176- def set_pathbar(self, path_bar):
2177- self.__pbar = path_bar
2178- return
2179-
2180- def get_x(self):
2181- return self.allocation[0]
2182-
2183- def get_width(self):
2184- return self.allocation[2]
2185-
2186- def get_height(self):
2187- return self.allocation[3]
2188-
2189- def get_label(self):
2190- return self.label
2191-
2192- def get_allocation(self):
2193- return gtk.gdk.Rectangle(*self.get_allocation_tuple())
2194-
2195- def get_allocation_tuple(self):
2196- if self.__pbar.get_direction() != gtk.TEXT_DIR_RTL:
2197- return self.allocation
2198- x, y, w, h = self.allocation
2199- x = self.__pbar.allocation[2]-x-w
2200- return x, y, w, h
2201-
2202- def get_size_requisition(self):
2203- return self.__requisition
2204-
2205- def get_layout(self):
2206- return self.__layout
2207-
2208- def activate(self, do_callback=True):
2209- self.__pbar.set_active(self, do_callback)
2210- return
2211-
2212- def calc_size_requisition(self):
2213- pbar = self.__pbar
2214-
2215- # determine widget size base on label width
2216- self.__layout = self.__layout_text(self.label, pbar.get_pango_context())
2217- extents = self.__layout.get_pixel_extents()
2218-
2219- # calc text width + 2 * padding, text height + 2 * ypadding
2220- w = extents[1][2] + 2*pbar.theme.xpadding
2221- h = max(extents[1][3] + 2*pbar.theme.ypadding, pbar.get_size_request()[1])
2222-
2223- # if has icon add some more pixels on
2224- if self.icon.pixbuf:
2225- w += self.icon.pixbuf.get_width() + pbar.theme.spacing
2226- h = max(self.icon.pixbuf.get_height() + 2*pbar.theme.ypadding, h)
2227-
2228- # extend width depending on part shape ...
2229- if self.shape == PathBar.SHAPE_START_ARROW or \
2230- self.shape == PathBar.SHAPE_END_CAP:
2231- w += pbar.theme.arrow_width
2232-
2233- elif self.shape == PathBar.SHAPE_MID_ARROW:
2234- w += 2*pbar.theme.arrow_width
2235-
2236- # if height greater than current height request,
2237- # reset height request to higher value
2238- # i get the feeling this should be in set_size_request(), but meh
2239- if h > pbar.get_size_request()[1]:
2240- pbar.set_size_request(-1, h)
2241-
2242- self.__requisition = (w,h)
2243- return w, h
2244-
2245- def is_truncated(self):
2246- return self.__requisition[0] != self.allocation[2]
2247-
2248- def __layout_text(self, text, pango_context):
2249- layout = pango.Layout(pango_context)
2250- layout.set_markup('%s' % text)
2251- layout.set_ellipsize(pango.ELLIPSIZE_END)
2252- return layout
2253-
2254- def __calc_layout_width(self, layout, shape, pbar):
2255- # set layout width
2256- if self.icon.pixbuf:
2257- icon_w = self.icon.pixbuf.get_width() + pbar.theme.spacing
2258- else:
2259- icon_w = 0
2260-
2261- w = self.allocation[2]
2262- if shape == PathBar.SHAPE_MID_ARROW:
2263- layout.set_width((w - 2*pbar.theme.arrow_width -
2264- 2*pbar.theme.xpadding - icon_w)*pango.SCALE)
2265-
2266- elif shape == PathBar.SHAPE_START_ARROW or \
2267- shape == PathBar.SHAPE_END_CAP:
2268- layout.set_width((w - pbar.theme.arrow_width - 2*pbar.theme.xpadding -
2269- icon_w)*pango.SCALE)
2270- else:
2271- layout.set_width((w - 2*pbar.theme.xpadding - icon_w)*pango.SCALE)
2272- return
2273-
2274-
2275-class PathBarIcon:
2276-
2277- def __init__(self, name=None, size=None):
2278- self.name = name
2279- self.size = size
2280- self.pixbuf = None
2281- return
2282-
2283- def specify(self, name, size):
2284- self.name = name
2285- self.size = size
2286- return
2287-
2288- def load_pixbuf(self):
2289- if not self.name:
2290- print 'Error: No icon specified.'
2291- return
2292- if not self.size:
2293- print 'Note: No icon size specified.'
2294-
2295- def render_icon(icon_set, name, size):
2296- self.pixbuf = icon_set.render_icon(
2297- style,
2298- gtk.TEXT_DIR_NONE,
2299- gtk.STATE_NORMAL,
2300- self.size or gtk.ICON_SIZE_BUTTON,
2301- gtk.Image(),
2302- None)
2303- return
2304-
2305- style = gtk.Style()
2306- icon_set = style.lookup_icon_set(self.name)
2307-
2308- if not icon_set:
2309- t = gtk.icon_theme_get_default()
2310- self.pixbuf = t.lookup_icon(self.name, self.size, 0).load_icon()
2311- else:
2312- icon_set = style.lookup_icon_set(self.name)
2313- render_icon(icon_set, self.name, self.size)
2314-
2315- if not self.pixbuf:
2316- print 'Error: No name failed to match any installed icon set.'
2317- self.name = gtk.STOCK_MISSING_IMAGE
2318- icon_set = style.lookup_icon_set(self.name)
2319- render_icon(icon_set, self.name, self.size)
2320- return
2321-
2322-
2323-class PathBarThemeHuman:
2324-
2325- PRELIT_NORMAL = 10
2326- PRELIT_ACTIVE = 11
2327-
2328- curvature = 2.5
2329- min_part_width = 56
2330- xpadding = 8
2331- ypadding = 2
2332- spacing = 4
2333- arrow_width = 13
2334- scroll_duration_ms = 150
2335- scroll_fps = 50
2336- animate = gtk.settings_get_default().get_property("gtk-enable-animations")
2337-
2338- def __init__(self):
2339- return
2340-
2341- def load(self, style):
2342- mid = style.mid
2343- dark = style.dark
2344- light = style.light
2345- text = style.text
2346- active = rgb.mix_color(mid[gtk.STATE_NORMAL],
2347- mid[gtk.STATE_SELECTED], 0.25)
2348-
2349- self.bg_colors = {
2350- gtk.STATE_NORMAL: (f(rgb.shade(mid[gtk.STATE_NORMAL], 1.2)),
2351- f(mid[gtk.STATE_NORMAL])),
2352-
2353- gtk.STATE_ACTIVE: (f(rgb.shade(active, 1.2)),
2354- f(active)),
2355-
2356- gtk.STATE_SELECTED: (f(mid[gtk.STATE_ACTIVE]),
2357- f(mid[gtk.STATE_ACTIVE])),
2358-
2359- gtk.STATE_INSENSITIVE: (f(mid[gtk.STATE_INSENSITIVE]),
2360- f(mid[gtk.STATE_INSENSITIVE])),
2361-
2362- self.PRELIT_NORMAL: (f(rgb.shade(mid[gtk.STATE_NORMAL], 1.25)),
2363- f(rgb.shade(mid[gtk.STATE_NORMAL], 1.05))),
2364-
2365- self.PRELIT_ACTIVE: (f(rgb.shade(active, 1.25)),
2366- f(rgb.shade(active, 1.05)))
2367- }
2368-
2369- self.dark_line_colors = {
2370- gtk.STATE_NORMAL: f(dark[gtk.STATE_NORMAL]),
2371- gtk.STATE_ACTIVE: f(dark[gtk.STATE_ACTIVE]),
2372- gtk.STATE_SELECTED: f(rgb.shade(dark[gtk.STATE_ACTIVE], 0.9)),
2373- gtk.STATE_PRELIGHT: f(dark[gtk.STATE_PRELIGHT]),
2374- gtk.STATE_INSENSITIVE: f(dark[gtk.STATE_PRELIGHT])
2375- }
2376-
2377- self.light_line_colors = {
2378- gtk.STATE_NORMAL: f(light[gtk.STATE_NORMAL]),
2379- gtk.STATE_ACTIVE: f(light[gtk.STATE_ACTIVE]),
2380- gtk.STATE_SELECTED: None,
2381- gtk.STATE_PRELIGHT: f(light[gtk.STATE_PRELIGHT]),
2382- gtk.STATE_INSENSITIVE: f(mid[gtk.STATE_PRELIGHT])
2383- }
2384-
2385- self.text_state = {
2386- gtk.STATE_NORMAL: gtk.STATE_NORMAL,
2387- gtk.STATE_ACTIVE: gtk.STATE_ACTIVE,
2388- gtk.STATE_SELECTED: gtk.STATE_ACTIVE,
2389- gtk.STATE_PRELIGHT: gtk.STATE_PRELIGHT,
2390- gtk.STATE_INSENSITIVE: gtk.STATE_INSENSITIVE
2391- }
2392-
2393- self.base_hack = None
2394- return
2395-
2396-
2397-class PathBarThemeInHuman(PathBarThemeHuman):
2398-
2399- def __init__(self):
2400- PathBarThemeHuman.__init__(self)
2401- return
2402-
2403- def load(self, style):
2404- mid = style.mid
2405- dark = style.dark
2406- light = style.light
2407- text = style.text
2408- active = rgb.mix_color(mid[gtk.STATE_NORMAL],
2409- mid[gtk.STATE_SELECTED], 0.25)
2410-
2411- self.bg_colors = {
2412- gtk.STATE_NORMAL: (f(rgb.shade(mid[gtk.STATE_NORMAL], 1.175)),
2413- f(mid[gtk.STATE_NORMAL])),
2414-
2415- gtk.STATE_ACTIVE: (f(rgb.shade(active, 1.2)),
2416- f(active)),
2417-
2418- gtk.STATE_SELECTED: (f(mid[gtk.STATE_ACTIVE]),
2419- f(mid[gtk.STATE_ACTIVE])),
2420-
2421- gtk.STATE_INSENSITIVE: (f(rgb.shade(mid[gtk.STATE_INSENSITIVE], 1.15)),
2422- f(rgb.shade(mid[gtk.STATE_INSENSITIVE], 1.1))),
2423-
2424- self.PRELIT_NORMAL: (f(rgb.shade(mid[gtk.STATE_NORMAL], 1.25)),
2425- f(rgb.shade(mid[gtk.STATE_NORMAL], 1.05))),
2426-
2427- self.PRELIT_ACTIVE: (f(rgb.shade(active, 1.25)),
2428- f(rgb.shade(active, 1.05)))
2429- }
2430-
2431- self.dark_line_colors = {
2432- gtk.STATE_NORMAL: f(dark[gtk.STATE_NORMAL]),
2433- gtk.STATE_ACTIVE: f(dark[gtk.STATE_ACTIVE]),
2434- gtk.STATE_SELECTED: f(rgb.shade(dark[gtk.STATE_ACTIVE], 0.9)),
2435- gtk.STATE_PRELIGHT: f(dark[gtk.STATE_PRELIGHT]),
2436- gtk.STATE_INSENSITIVE: f(dark[gtk.STATE_PRELIGHT])
2437- }
2438-
2439- self.light_line_colors = {
2440- gtk.STATE_NORMAL: f(light[gtk.STATE_NORMAL]),
2441- gtk.STATE_ACTIVE: f(light[gtk.STATE_ACTIVE]),
2442- gtk.STATE_SELECTED: None,
2443- gtk.STATE_PRELIGHT: f(light[gtk.STATE_PRELIGHT]),
2444- gtk.STATE_INSENSITIVE: f(light[gtk.STATE_PRELIGHT])
2445- }
2446-
2447- self.text_state = {
2448- gtk.STATE_NORMAL: gtk.STATE_NORMAL,
2449- gtk.STATE_ACTIVE: gtk.STATE_ACTIVE,
2450- gtk.STATE_SELECTED: gtk.STATE_ACTIVE,
2451- gtk.STATE_PRELIGHT: gtk.STATE_PRELIGHT,
2452- gtk.STATE_INSENSITIVE: gtk.STATE_INSENSITIVE
2453- }
2454-
2455- self.base_hack = None
2456- return
2457-
2458-
2459-class PathBarThemeHumanClearlooks(PathBarThemeHuman):
2460-
2461- def __init__(self):
2462- PathBarThemeHuman.__init__(self)
2463- return
2464-
2465- def load(self, style):
2466- mid = style.mid
2467- dark = style.dark
2468- light = style.light
2469- text = style.text
2470- active = rgb.mix_color(mid[gtk.STATE_NORMAL],
2471- mid[gtk.STATE_SELECTED], 0.25)
2472-
2473- self.bg_colors = {
2474- gtk.STATE_NORMAL: (f(rgb.shade(mid[gtk.STATE_NORMAL], 1.20)),
2475- f(rgb.shade(mid[gtk.STATE_NORMAL], 1.05))),
2476-
2477- gtk.STATE_ACTIVE: (f(rgb.shade(active, 1.20)),
2478- f(rgb.shade(active, 1.05))),
2479-
2480- gtk.STATE_SELECTED: (f(rgb.shade(mid[gtk.STATE_ACTIVE], 1.15)),
2481- f(mid[gtk.STATE_ACTIVE])),
2482-
2483- gtk.STATE_INSENSITIVE: (f(mid[gtk.STATE_INSENSITIVE]),
2484- f(mid[gtk.STATE_INSENSITIVE])),
2485-
2486- self.PRELIT_NORMAL: (f(rgb.shade(mid[gtk.STATE_NORMAL], 1.35)),
2487- f(rgb.shade(mid[gtk.STATE_NORMAL], 1.15))),
2488-
2489- self.PRELIT_ACTIVE: (f(rgb.shade(active, 1.35)),
2490- f(rgb.shade(active, 1.15)))
2491- }
2492-
2493- self.dark_line_colors = {
2494- gtk.STATE_NORMAL: f(rgb.shade(dark[gtk.STATE_ACTIVE], 0.975)),
2495- gtk.STATE_ACTIVE: f(rgb.shade(dark[gtk.STATE_ACTIVE], 0.975)),
2496- gtk.STATE_SELECTED: f(rgb.shade(dark[gtk.STATE_ACTIVE], 0.95)),
2497- gtk.STATE_PRELIGHT: f(dark[gtk.STATE_PRELIGHT]),
2498- gtk.STATE_INSENSITIVE: f(dark[gtk.STATE_INSENSITIVE])
2499- }
2500-
2501- self.light_line_colors = {
2502- gtk.STATE_NORMAL: None,
2503- gtk.STATE_ACTIVE: None,
2504- gtk.STATE_SELECTED: f(mid[gtk.STATE_ACTIVE]),
2505- gtk.STATE_PRELIGHT: f(light[gtk.STATE_PRELIGHT]),
2506- gtk.STATE_INSENSITIVE: f(light[gtk.STATE_INSENSITIVE])
2507- }
2508-
2509- self.text_state = {
2510- gtk.STATE_NORMAL: gtk.STATE_NORMAL,
2511- gtk.STATE_ACTIVE: gtk.STATE_ACTIVE,
2512- gtk.STATE_SELECTED: gtk.STATE_NORMAL,
2513- gtk.STATE_PRELIGHT: gtk.STATE_PRELIGHT,
2514- gtk.STATE_INSENSITIVE: gtk.STATE_INSENSITIVE
2515- }
2516-
2517- self.base_hack = None
2518- return
2519-
2520-
2521-class PathBarThemeDust(PathBarThemeHuman):
2522-
2523- def __init__(self):
2524- PathBarThemeHuman.__init__(self)
2525- return
2526-
2527- def load(self, style):
2528- mid = style.mid
2529- dark = style.dark
2530- light = style.light
2531- text = style.text
2532- active = rgb.mix_color(mid[gtk.STATE_NORMAL],
2533- light[gtk.STATE_SELECTED], 0.3)
2534-
2535- self.bg_colors = {
2536- gtk.STATE_NORMAL: (f(rgb.shade(mid[gtk.STATE_NORMAL], 1.3)),
2537- f(mid[gtk.STATE_NORMAL])),
2538-
2539- gtk.STATE_ACTIVE: (f(rgb.shade(active, 1.3)),
2540- f(active)),
2541-
2542- gtk.STATE_SELECTED: (f(rgb.shade(mid[gtk.STATE_NORMAL], 0.95)),
2543- f(rgb.shade(mid[gtk.STATE_NORMAL], 0.95))),
2544-
2545- self.PRELIT_NORMAL: (f(rgb.shade(mid[gtk.STATE_NORMAL], 1.35)),
2546- f(rgb.shade(mid[gtk.STATE_NORMAL], 1.15))),
2547-
2548- gtk.STATE_INSENSITIVE: (f(rgb.shade(mid[gtk.STATE_INSENSITIVE], 1.09)),
2549- f(rgb.shade(mid[gtk.STATE_INSENSITIVE], 1.08))),
2550-
2551- self.PRELIT_ACTIVE: (f(rgb.shade(active, 1.35)),
2552- f(rgb.shade(active, 1.15)))
2553- }
2554-
2555- self.dark_line_colors = {
2556- gtk.STATE_NORMAL: f(dark[gtk.STATE_ACTIVE]),
2557- gtk.STATE_ACTIVE: f(dark[gtk.STATE_ACTIVE]),
2558- gtk.STATE_SELECTED: f(rgb.shade(dark[gtk.STATE_ACTIVE], 0.95)),
2559- gtk.STATE_PRELIGHT: f(dark[gtk.STATE_PRELIGHT]),
2560- gtk.STATE_INSENSITIVE: f(dark[gtk.STATE_INSENSITIVE])
2561- }
2562-
2563- self.light_line_colors = {
2564- gtk.STATE_NORMAL: f(light[gtk.STATE_NORMAL]),
2565- gtk.STATE_ACTIVE: f(light[gtk.STATE_NORMAL]),
2566- gtk.STATE_SELECTED: None,
2567- gtk.STATE_PRELIGHT: f(light[gtk.STATE_PRELIGHT]),
2568- gtk.STATE_INSENSITIVE: f(rgb.shade(light[gtk.STATE_INSENSITIVE], 0.96))
2569- }
2570-
2571- self.text_state = {
2572- gtk.STATE_NORMAL: gtk.STATE_NORMAL,
2573- gtk.STATE_ACTIVE: gtk.STATE_ACTIVE,
2574- gtk.STATE_SELECTED: gtk.STATE_NORMAL,
2575- gtk.STATE_PRELIGHT: gtk.STATE_PRELIGHT,
2576- gtk.STATE_INSENSITIVE: gtk.STATE_INSENSITIVE
2577- }
2578-
2579- self.base_hack = None
2580- return
2581-
2582-
2583-class PathBarThemeNewWave(PathBarThemeHuman):
2584-
2585- curvature = 1.5
2586-
2587- def __init__(self):
2588- PathBarThemeHuman.__init__(self)
2589- return
2590-
2591- def load(self, style):
2592- mid = style.mid
2593- dark = style.dark
2594- light = style.light
2595- text = style.text
2596- active = rgb.mix_color(mid[gtk.STATE_NORMAL],
2597- light[gtk.STATE_SELECTED], 0.5)
2598- top_step = gtk.gdk.color_parse('#FDCF9D')
2599- btm_step = gtk.gdk.color_parse('#FCAE87')
2600-
2601- self.bg_colors = {
2602- gtk.STATE_NORMAL: (f(rgb.shade(mid[gtk.STATE_NORMAL], 1.01)),
2603- f(mid[gtk.STATE_NORMAL])),
2604-
2605- gtk.STATE_ACTIVE: (f(top_step),
2606- f(btm_step)),
2607-
2608- gtk.STATE_SELECTED: (f(top_step),
2609- f(btm_step)),
2610-
2611- gtk.STATE_INSENSITIVE: (f(rgb.shade(mid[gtk.STATE_INSENSITIVE], 1.075)),
2612- f(rgb.shade(mid[gtk.STATE_INSENSITIVE], 1.075))),
2613-
2614- self.PRELIT_NORMAL: (f(rgb.shade(mid[gtk.STATE_NORMAL], 1.2)),
2615- f(rgb.shade(mid[gtk.STATE_NORMAL], 1.15))),
2616-
2617- self.PRELIT_ACTIVE: (f(rgb.shade(top_step, 1.11)),
2618- f(rgb.shade(btm_step, 1.06))),
2619- }
2620-
2621- self.dark_line_colors = {
2622- gtk.STATE_NORMAL: f(rgb.shade(dark[gtk.STATE_ACTIVE], 0.95)),
2623- gtk.STATE_ACTIVE: f(rgb.shade(dark[gtk.STATE_ACTIVE], 0.95)),
2624- gtk.STATE_SELECTED: f(rgb.shade(dark[gtk.STATE_ACTIVE], 0.95)),
2625- gtk.STATE_PRELIGHT: f(dark[gtk.STATE_PRELIGHT]),
2626- gtk.STATE_INSENSITIVE: f(dark[gtk.STATE_INSENSITIVE])
2627- }
2628-
2629- self.light_line_colors = {
2630- gtk.STATE_NORMAL: f(rgb.shade(light[gtk.STATE_NORMAL], 1.2)),
2631- gtk.STATE_ACTIVE: f(rgb.shade(light[gtk.STATE_NORMAL], 1.2)),
2632- gtk.STATE_SELECTED: None,
2633- gtk.STATE_PRELIGHT: f(rgb.shade(light[gtk.STATE_PRELIGHT], 1.2)),
2634- gtk.STATE_INSENSITIVE: f(light[gtk.STATE_INSENSITIVE])
2635- }
2636-
2637- self.text_state = {
2638- gtk.STATE_NORMAL: gtk.STATE_NORMAL,
2639- gtk.STATE_ACTIVE: gtk.STATE_ACTIVE,
2640- gtk.STATE_SELECTED: gtk.STATE_NORMAL,
2641- gtk.STATE_PRELIGHT: gtk.STATE_PRELIGHT,
2642- gtk.STATE_INSENSITIVE: gtk.STATE_INSENSITIVE
2643- }
2644-
2645- self.base_hack = f(gtk.gdk.color_parse("#F2F2F2"))
2646- return
2647-
2648-
2649-class PathBarThemeHicolor:
2650-
2651- PRELIT_NORMAL = 10
2652- PRELIT_ACTIVE = 11
2653-
2654- curvature = 0.5
2655- min_part_width = 56
2656- xpadding = 15
2657- ypadding = 10
2658- spacing = 10
2659- arrow_width = 15
2660- scroll_duration_ms = 150
2661- scroll_fps = 50
2662- animate = gtk.settings_get_default().get_property("gtk-enable-animations")
2663-
2664- def __init__(self):
2665- return
2666-
2667- def load(self, style):
2668- mid = style.mid
2669- dark = style.dark
2670- light = style.light
2671- text = style.text
2672-
2673- self.bg_colors = {
2674- gtk.STATE_NORMAL: (f(mid[gtk.STATE_NORMAL]),
2675- f(mid[gtk.STATE_NORMAL])),
2676-
2677- gtk.STATE_ACTIVE: (f(mid[gtk.STATE_ACTIVE]),
2678- f(mid[gtk.STATE_ACTIVE])),
2679-
2680- gtk.STATE_SELECTED: (f(mid[gtk.STATE_SELECTED]),
2681- f(mid[gtk.STATE_SELECTED])),
2682-
2683- gtk.STATE_INSENSITIVE: (f(mid[gtk.STATE_INSENSITIVE]),
2684- f(mid[gtk.STATE_INSENSITIVE])),
2685-
2686- self.PRELIT_NORMAL: (f(mid[gtk.STATE_PRELIGHT]),
2687- f(mid[gtk.STATE_PRELIGHT])),
2688-
2689- self.PRELIT_ACTIVE: (f(mid[gtk.STATE_PRELIGHT]),
2690- f(mid[gtk.STATE_PRELIGHT]))
2691- }
2692-
2693- self.dark_line_colors = {
2694- gtk.STATE_NORMAL: f(dark[gtk.STATE_NORMAL]),
2695- gtk.STATE_ACTIVE: f(dark[gtk.STATE_ACTIVE]),
2696- gtk.STATE_SELECTED: f(dark[gtk.STATE_SELECTED]),
2697- gtk.STATE_PRELIGHT: f(dark[gtk.STATE_PRELIGHT]),
2698- gtk.STATE_INSENSITIVE: f(dark[gtk.STATE_INSENSITIVE])
2699- }
2700-
2701- self.light_line_colors = {
2702- gtk.STATE_NORMAL: f(light[gtk.STATE_NORMAL]),
2703- gtk.STATE_ACTIVE: f(light[gtk.STATE_ACTIVE]),
2704- gtk.STATE_SELECTED: None,
2705- gtk.STATE_PRELIGHT: f(light[gtk.STATE_PRELIGHT]),
2706- gtk.STATE_INSENSITIVE: f(light[gtk.STATE_INSENSITIVE])
2707- }
2708-
2709- self.text_state = {
2710- gtk.STATE_NORMAL: gtk.STATE_NORMAL,
2711- gtk.STATE_ACTIVE: gtk.STATE_ACTIVE,
2712- gtk.STATE_SELECTED: gtk.STATE_SELECTED,
2713- gtk.STATE_PRELIGHT: gtk.STATE_PRELIGHT,
2714- gtk.STATE_INSENSITIVE: gtk.STATE_INSENSITIVE
2715- }
2716-
2717- self.base_hack = None
2718- return
2719-
2720-
2721-class PathBarThemes:
2722-
2723- DICT = {
2724- "Human": PathBarThemeHuman,
2725- "Human-Clearlooks": PathBarThemeHumanClearlooks,
2726- "InHuman": PathBarThemeInHuman,
2727- "HighContrastInverse": PathBarThemeHicolor,
2728- "HighContrastLargePrintInverse": PathBarThemeHicolor,
2729- "Dust": PathBarThemeDust,
2730- "Dust Sand": PathBarThemeDust,
2731- "New Wave": PathBarThemeNewWave
2732- }
2733-
2734-
2735-class NavigationBar(PathBar):
2736- def __init__(self, group=None):
2737- PathBar.__init__(self)
2738- self.set_size_request(-1, 28)
2739- self.id_to_part = {}
2740- return
2741-
2742- def add_with_id(self, label, callback, id, icon=None, do_callback=True, animate=True):
2743- """
2744- Add a new button with the given label/callback
2745-
2746- If there is the same id already, replace the existing one
2747- with the new one
2748- """
2749-
2750- # check if we have the button of that id or need a new one
2751- if id in self.id_to_part:
2752- part = self.id_to_part[id]
2753- part.set_label(label)
2754- else:
2755- part = PathPart(parent=self, label=label, callback=callback)
2756- part.set_name(id)
2757- part.set_pathbar(self)
2758- part.id = id
2759- self.id_to_part[id] = part
2760- # check if animation should be used
2761- if animate:
2762- if do_callback:
2763- gobject.timeout_add(150, self.append, part)
2764- else:
2765- gobject.timeout_add(150, self.append_no_callback, part)
2766- else:
2767- self.append(part, do_callback, animate=False)
2768-
2769- if icon:
2770- part.set_icon(icon)
2771- return
2772-
2773- def remove_id(self, id):
2774- if not id in self.id_to_part:
2775- return
2776-
2777- part = self.id_to_part[id]
2778- del self.id_to_part[id]
2779- self.remove(part)
2780- return
2781-
2782- def get_button_from_id(self, id):
2783- """
2784- return the button for the given id (or None)
2785- """
2786- if not id in self.id_to_part:
2787- return None
2788- return self.id_to_part[id]
2789-
2790- def get_label(self, id):
2791- """
2792- Return the label of the navigation button with the given id
2793- """
2794- if not id in self.id_to_part:
2795- return
2796-
2797
2798=== added file 'softwarecenter/view/widgets/pathbar_common.py'
2799--- softwarecenter/view/widgets/pathbar_common.py 1970-01-01 00:00:00 +0000
2800+++ softwarecenter/view/widgets/pathbar_common.py 2010-04-04 06:00:32 +0000
2801@@ -0,0 +1,827 @@
2802+# Copyright (C) 2010 Matthew McGowan
2803+#
2804+# Authors:
2805+# Matthew McGowan
2806+#
2807+# This program is free software: you can redistribute it and/or modify
2808+# it under the terms of the GNU General Public License as published by
2809+# the Free Software Foundation, either version 3 of the License, or
2810+# (at your option) any later version.
2811+#
2812+# This program is distributed in the hope that it will be useful,
2813+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2814+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2815+# GNU General Public License for more details.
2816+#
2817+# You should have received a copy of the GNU General Public License
2818+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2819+
2820+
2821+import gtk
2822+import cairo
2823+import colorsys
2824+
2825+
2826+# pi constants
2827+M_PI = 3.1415926535897931
2828+PI_OVER_180 = 0.017453292519943295
2829+
2830+SHAPE_RECTANGLE = 0
2831+SHAPE_START_ARROW = 1
2832+SHAPE_MID_ARROW = 2
2833+SHAPE_END_CAP = 3
2834+
2835+
2836+class PathBarStyle:
2837+
2838+ def __init__(self, pathbar):
2839+ self.shape_map = self._load_shape_map(pathbar)
2840+
2841+ gtk_settings = gtk.settings_get_default()
2842+ self.theme = self._load_theme(gtk_settings)
2843+ self.theme.build_palette(gtk_settings)
2844+ self.properties = self.theme.get_properties(gtk_settings)
2845+ self.gradients = self.theme.get_grad_palette()
2846+ self.dark_line = self.theme.get_dark_line_palette()
2847+ self.light_line = self.theme.get_light_line_palette()
2848+ self.text = self.theme.get_text_palette()
2849+ self.text_states = self.theme.get_text_states()
2850+ self.base_color = None
2851+ return
2852+
2853+ def __getitem__(self, item):
2854+ if self.properties.has_key(item):
2855+ return self.properties[item]
2856+ print 'Key does not exist in the style profile:', item
2857+ return None
2858+
2859+ def _load_shape_map(self, pathbar):
2860+ if pathbar.get_direction() != gtk.TEXT_DIR_RTL:
2861+ shmap = {SHAPE_RECTANGLE: self._shape_rectangle,
2862+ SHAPE_START_ARROW: self._shape_start_arrow_ltr,
2863+ SHAPE_MID_ARROW: self._shape_mid_arrow_ltr,
2864+ SHAPE_END_CAP: self._shape_end_cap_ltr}
2865+ else:
2866+ shmap = {SHAPE_RECTANGLE: self._shape_rectangle,
2867+ SHAPE_START_ARROW: self._shape_start_arrow_rtl,
2868+ SHAPE_MID_ARROW: self._shape_mid_arrow_rtl,
2869+ SHAPE_END_CAP: self._shape_end_cap_rtl}
2870+ return shmap
2871+
2872+ def _load_theme(self, gtksettings):
2873+ name = gtksettings.get_property("gtk-theme-name")
2874+ r = ThemeRegistry()
2875+ return r.retrieve(name)
2876+
2877+ def _shape_rectangle(self, cr, x, y, w, h, r, aw):
2878+ global M_PI, PI_OVER_180
2879+ cr.new_sub_path()
2880+ cr.arc(r+x, r+y, r, M_PI, 270*PI_OVER_180)
2881+ cr.arc(w-r, r+y, r, 270*PI_OVER_180, 0)
2882+ cr.arc(w-r, h-r, r, 0, 90*PI_OVER_180)
2883+ cr.arc(r+x, h-r, r, 90*PI_OVER_180, M_PI)
2884+ cr.close_path()
2885+ return
2886+
2887+ def _shape_start_arrow_ltr(self, cr, x, y, w, h, r, aw):
2888+ global M_PI, PI_OVER_180
2889+ cr.new_sub_path()
2890+ cr.arc(r+x, r+y, r, M_PI, 270*PI_OVER_180)
2891+ # arrow head
2892+ cr.line_to(w-aw, y)
2893+ cr.line_to(w-x+1, (h+y)/2)
2894+ cr.line_to(w-aw, h)
2895+ cr.arc(r+x, h-r, r, 90*PI_OVER_180, M_PI)
2896+ cr.close_path()
2897+ return
2898+
2899+ def _shape_mid_arrow_ltr(self, cr, x, y, w, h, r, aw):
2900+ cr.move_to(0, y)
2901+ # arrow head
2902+ cr.line_to(w-aw, y)
2903+ cr.line_to(w-x+1, (h+y)/2)
2904+ cr.line_to(w-aw, h)
2905+ cr.line_to(0, h)
2906+ cr.close_path()
2907+ return
2908+
2909+ def _shape_end_cap_ltr(self, cr, x, y, w, h, r, aw):
2910+ global M_PI, PI_OVER_180
2911+ cr.move_to(0, y)
2912+ cr.arc(w-r, r+y, r, 270*PI_OVER_180, 0)
2913+ cr.arc(w-r, h-r, r, 0, 90*PI_OVER_180)
2914+ cr.line_to(0, h)
2915+ cr.close_path()
2916+ return
2917+
2918+ def _shape_start_arrow_rtl(self, cr, x, y, w, h, r, aw):
2919+ global M_PI, PI_OVER_180
2920+ cr.new_sub_path()
2921+ cr.move_to(x, (h+y)/2)
2922+ cr.line_to(aw, y)
2923+ cr.arc(w-r, r+y, r, 270*PI_OVER_180, 0)
2924+ cr.arc(w-r, h-r, r, 0, 90*PI_OVER_180)
2925+ cr.line_to(aw, h)
2926+ cr.close_path()
2927+ return
2928+
2929+ def _shape_mid_arrow_rtl(self, cr, x, y, w, h, r, aw):
2930+ cr.move_to(x, (h+y)/2)
2931+ cr.line_to(aw, y)
2932+ cr.line_to(w, y)
2933+ cr.line_to(w, h)
2934+ cr.line_to(aw, h)
2935+ cr.close_path()
2936+ return
2937+
2938+ def _shape_end_cap_rtl(self, cr, x, y, w, h, r, aw):
2939+ global M_PI, PI_OVER_180
2940+ cr.arc(r+x, r+y, r, M_PI, 270*PI_OVER_180)
2941+ cr.line_to(w, y)
2942+ cr.line_to(w, h)
2943+ cr.arc(r+x, h-r, r, 90*PI_OVER_180, M_PI)
2944+ cr.close_path()
2945+ return
2946+
2947+ def set_direction(self, direction):
2948+ if direction != gtk.TEXT_DIR_RTL:
2949+ self.shape_map = {SHAPE_RECTANGLE: self._shape_rectangle,
2950+ SHAPE_START_ARROW: self._shape_start_arrow_ltr,
2951+ SHAPE_MID_ARROW: self._shape_mid_arrow_ltr,
2952+ SHAPE_END_CAP: self._shape_end_cap_ltr}
2953+ else:
2954+ self.shape_map = {SHAPE_RECTANGLE: self._shape_rectangle,
2955+ SHAPE_START_ARROW: self._shape_start_arrow_rtl,
2956+ SHAPE_MID_ARROW: self._shape_mid_arrow_rtl,
2957+ SHAPE_END_CAP: self._shape_end_cap_rtl}
2958+ return
2959+
2960+ def paint_bg(self, cr, part, x, y, w, h, sxO=0):
2961+ shape = self.shape_map[part.shape]
2962+ state = part.state
2963+ r = self["curvature"]
2964+ aw = self["arrow_width"]
2965+
2966+ cr.save()
2967+ cr.rectangle(x, y, w+1, h)
2968+ cr.clip()
2969+ cr.translate(x+0.5-sxO, y+0.5)
2970+
2971+ w -= 1
2972+ h -= 1
2973+
2974+ # bg linear vertical gradient
2975+ color1, color2 = self.gradients[state]
2976+
2977+ shape(cr, 0, 0, w, h, r, aw)
2978+ lin = cairo.LinearGradient(0, 0, 0, h)
2979+ lin.add_color_stop_rgb(0.0, *color1.tofloats())
2980+ lin.add_color_stop_rgb(1.0, *color2.tofloats())
2981+ cr.set_source(lin)
2982+ cr.fill()
2983+
2984+ cr.set_line_width(1.0)
2985+ # strong outline
2986+ shape(cr, 0, 0, w, h, r, aw)
2987+ cr.set_source_rgb(*self.dark_line[state].tofloats())
2988+ cr.stroke()
2989+
2990+ # inner bevel/highlight
2991+ if r == 0: w += 1
2992+ shape(cr, 1, 1, w-1, h-1, r, aw)
2993+ cr.set_source_rgb(*self.light_line[state].tofloats())
2994+ cr.stroke()
2995+ cr.restore()
2996+ return
2997+
2998+ def paint_layout(self, widget, part, x, y, sxO=0):
2999+ # draw layout
3000+ layout = part.get_layout()
3001+ widget.style.paint_layout(widget.window,
3002+ self.text_states[part.state],
3003+ False,
3004+ None, # clip area
3005+ widget,
3006+ None,
3007+ x, y,
3008+ layout)
3009+ return
3010+
3011+ def paint_focus(self, cr, x, y, w, h):
3012+ self._shape_rectangle(cr, 4, 4, w-4, h-4, self["curvature"], 0)
3013+ cr.set_source_rgb(*self.theme.bg[gtk.STATE_SELECTED].tofloats())
3014+ cr.stroke()
3015+ return
3016+
3017+
3018+class PathBarColorArray:
3019+
3020+ def __init__(self, color_array):
3021+ self.color_array = {}
3022+ for state in (gtk.STATE_NORMAL, gtk.STATE_ACTIVE, gtk.STATE_SELECTED, \
3023+ gtk.STATE_PRELIGHT, gtk.STATE_INSENSITIVE):
3024+ self.color_array[state] = color_from_gdkcolor(color_array[state])
3025+ return
3026+
3027+ def __getitem__(self, state):
3028+ return self.color_array[state]
3029+
3030+
3031+class PathBarColor:
3032+
3033+ def __init__(self, red, green, blue):
3034+ self.red = red
3035+ self.green = green
3036+ self.blue = blue
3037+ return
3038+
3039+ def set_alpha(self, value):
3040+ self.alpha = value
3041+ return
3042+
3043+ def tofloats(self):
3044+ return self.red, self.green, self.blue
3045+
3046+ def toclutter(self):
3047+ try:
3048+ from clutter import Color
3049+ except Exception, e:
3050+ print 'Error parsing color:', e
3051+ raise SystemExit
3052+ r,g,b = self.tofloats()
3053+ return Color(int(r*255), int(g*255), int(b*255))
3054+
3055+ def togtkgdk(self):
3056+ r,g,b = self.tofloats()
3057+ return gtk.gdk.Color(int(r*65535), int(g*65535), int(b*65535))
3058+
3059+ def lighten(self):
3060+ return self.shade(1.3)
3061+
3062+ def darken(self):
3063+ return self.shade(0.7)
3064+
3065+ def shade(self, factor):
3066+ # as seen in clutter-color.c
3067+ h,l,s = colorsys.rgb_to_hls(*self.tofloats())
3068+
3069+ l *= factor
3070+ if l > 1.0:
3071+ l = 1.0
3072+ elif l < 0:
3073+ l = 0
3074+
3075+ s *= factor
3076+ if s > 1.0:
3077+ s = 1.0
3078+ elif s < 0:
3079+ s = 0
3080+
3081+ r,g,b = colorsys.hls_to_rgb(h,l,s)
3082+ return PathBarColor(r,g,b)
3083+
3084+ def mix(self, color2, mix_factor):
3085+ # as seen in Murrine's cairo-support.c
3086+ r1, g1, b1 = self.tofloats()
3087+ r2, g2, b2 = color2.tofloats()
3088+ r = r1*(1-mix_factor)+r2*mix_factor
3089+ g = g1*(1-mix_factor)+g2*mix_factor
3090+ b = b1*(1-mix_factor)+b2*mix_factor
3091+ return PathBarColor(r,g,b)
3092+
3093+
3094+class Theme:
3095+
3096+ def build_palette(self, gtksettings):
3097+ style = gtk.rc_get_style_by_paths(gtksettings,
3098+ 'GtkWindow',
3099+ 'GtkWindow',
3100+ gtk.Window)
3101+
3102+ style = style or gtk.widget_get_default_style()
3103+
3104+ # build pathbar color palette
3105+ self.fg = PathBarColorArray(style.fg)
3106+ self.bg = PathBarColorArray(style.bg)
3107+ self.text = PathBarColorArray(style.text)
3108+ self.base = PathBarColorArray(style.base)
3109+ self.light = PathBarColorArray(style.base)
3110+ self.mid = PathBarColorArray(style.base)
3111+ self.dark = PathBarColorArray(style.base)
3112+ return
3113+
3114+
3115+class Human(Theme):
3116+
3117+ def get_properties(self, gtksettings):
3118+ props = {
3119+ 'curvature': 2.5,
3120+ 'min_part_width': 48,
3121+ 'xpad': 8,
3122+ 'ypad': 4,
3123+ 'xthickness': 1,
3124+ 'ythickness': 1,
3125+ 'spacing': 5,
3126+ 'arrow_width': 13,
3127+ 'scroll_duration': 150,
3128+ 'enable-animations': gtksettings.get_property("gtk-enable-animations"),
3129+ 'override_base': False
3130+ }
3131+ return props
3132+
3133+ def get_grad_palette(self):
3134+ # provide two colours per state for background vertical linear gradients
3135+ palette = {gtk.STATE_NORMAL: (self.bg[gtk.STATE_NORMAL].shade(1.1),
3136+ self.bg[gtk.STATE_NORMAL].shade(0.95)),
3137+
3138+ gtk.STATE_ACTIVE: (self.bg[gtk.STATE_NORMAL].shade(1.00),
3139+ self.bg[gtk.STATE_NORMAL].shade(0.75)),
3140+
3141+ gtk.STATE_SELECTED: (self.bg[gtk.STATE_NORMAL].shade(1.11),
3142+ self.bg[gtk.STATE_NORMAL]),
3143+
3144+ gtk.STATE_PRELIGHT: (self.bg[gtk.STATE_NORMAL].shade(0.96),
3145+ self.bg[gtk.STATE_NORMAL].shade(0.91)),
3146+
3147+ gtk.STATE_INSENSITIVE: (self.bg[gtk.STATE_INSENSITIVE],
3148+ self.bg[gtk.STATE_INSENSITIVE])
3149+ }
3150+ return palette
3151+
3152+ def get_text_palette(self):
3153+ palette = {gtk.STATE_NORMAL: self.fg[gtk.STATE_NORMAL],
3154+ gtk.STATE_ACTIVE: self.fg[gtk.STATE_NORMAL],
3155+ gtk.STATE_SELECTED: self.fg[gtk.STATE_NORMAL],
3156+ gtk.STATE_PRELIGHT: self.fg[gtk.STATE_NORMAL],
3157+ gtk.STATE_INSENSITIVE: self.text[gtk.STATE_INSENSITIVE]}
3158+ return palette
3159+
3160+ def get_dark_line_palette(self):
3161+ palette = {gtk.STATE_NORMAL: self.bg[gtk.STATE_NORMAL].darken(),
3162+ gtk.STATE_ACTIVE: self.bg[gtk.STATE_NORMAL].darken(),
3163+ gtk.STATE_PRELIGHT: self.bg[gtk.STATE_NORMAL].darken(),
3164+ gtk.STATE_SELECTED: self.bg[gtk.STATE_NORMAL].darken(),
3165+ gtk.STATE_INSENSITIVE: self.bg[gtk.STATE_INSENSITIVE].darken()}
3166+ return palette
3167+
3168+ def get_light_line_palette(self):
3169+ palette = {gtk.STATE_NORMAL: self.bg[gtk.STATE_NORMAL].lighten(),
3170+ gtk.STATE_ACTIVE: self.fg[gtk.STATE_NORMAL],
3171+ gtk.STATE_PRELIGHT: self.bg[gtk.STATE_NORMAL].lighten(),
3172+ gtk.STATE_SELECTED: self.bg[gtk.STATE_NORMAL].lighten(),
3173+ gtk.STATE_INSENSITIVE: self.light[gtk.STATE_INSENSITIVE]}
3174+ return palette
3175+
3176+ def get_text_states(self):
3177+ states = {gtk.STATE_NORMAL: gtk.STATE_NORMAL,
3178+ gtk.STATE_ACTIVE: gtk.STATE_NORMAL,
3179+ gtk.STATE_PRELIGHT: gtk.STATE_NORMAL,
3180+ gtk.STATE_SELECTED: gtk.STATE_NORMAL,
3181+ gtk.STATE_INSENSITIVE: gtk.STATE_INSENSITIVE}
3182+ return states
3183+
3184+
3185+class Clearlooks(Human):
3186+
3187+ def get_properties(self, gtksettings):
3188+ props = Human.get_properties(self, gtksettings)
3189+ props['curvature'] = 3.5
3190+ return props
3191+
3192+ def get_grad_palette(self):
3193+ # provide two colours per state for background vertical linear gradients
3194+
3195+ selected_color = self.bg[gtk.STATE_NORMAL].mix(self.bg[gtk.STATE_SELECTED],
3196+ 0.2)
3197+
3198+ palette = {gtk.STATE_NORMAL: (self.bg[gtk.STATE_NORMAL].shade(1.15),
3199+ self.bg[gtk.STATE_NORMAL].shade(0.95)),
3200+
3201+ gtk.STATE_ACTIVE: (self.bg[gtk.STATE_ACTIVE],
3202+ self.bg[gtk.STATE_ACTIVE]),
3203+
3204+ gtk.STATE_SELECTED: (selected_color.shade(1.175),
3205+ selected_color),
3206+
3207+ gtk.STATE_PRELIGHT: (self.bg[gtk.STATE_NORMAL].shade(1.3),
3208+ selected_color.shade(1.1)),
3209+
3210+ gtk.STATE_INSENSITIVE: (self.bg[gtk.STATE_INSENSITIVE],
3211+ self.bg[gtk.STATE_INSENSITIVE])
3212+ }
3213+ return palette
3214+
3215+ def get_light_line_palette(self):
3216+ palette = Human.get_light_line_palette(self)
3217+ palette[gtk.STATE_ACTIVE] = self.bg[gtk.STATE_ACTIVE]
3218+ return palette
3219+
3220+
3221+class InHuman(Theme):
3222+
3223+ def get_properties(self, gtksettings):
3224+ props = {
3225+ 'curvature': 2.5,
3226+ 'min_part_width': 48,
3227+ 'xpad': 8,
3228+ 'ypad': 4,
3229+ 'xthickness': 1,
3230+ 'ythickness': 1,
3231+ 'spacing': 5,
3232+ 'arrow_width': 13,
3233+ 'scroll_duration': 150,
3234+ 'enable-animations': gtksettings.get_property("gtk-enable-animations"),
3235+ 'override_base': False
3236+ }
3237+ return props
3238+
3239+ def get_grad_palette(self):
3240+ # provide two colours per state for background vertical linear gradients
3241+ palette = {gtk.STATE_NORMAL: (self.bg[gtk.STATE_NORMAL].shade(1.1),
3242+ self.bg[gtk.STATE_NORMAL].shade(0.95)),
3243+
3244+ gtk.STATE_ACTIVE: (self.bg[gtk.STATE_NORMAL].shade(1.00),
3245+ self.bg[gtk.STATE_NORMAL].shade(0.75)),
3246+
3247+ gtk.STATE_SELECTED: (self.bg[gtk.STATE_NORMAL].shade(1.09),
3248+ self.bg),
3249+
3250+ gtk.STATE_PRELIGHT: (self.bg[gtk.STATE_SELECTED].shade(1.35),
3251+ self.bg[gtk.STATE_SELECTED].shade(1.1)),
3252+
3253+ gtk.STATE_INSENSITIVE: (self.bg[gtk.STATE_INSENSITIVE],
3254+ self.bg[gtk.STATE_INSENSITIVE])
3255+ }
3256+ return palette
3257+
3258+ def get_text_palette(self):
3259+ palette = {gtk.STATE_NORMAL: self.text[gtk.STATE_NORMAL],
3260+ gtk.STATE_ACTIVE: self.text[gtk.STATE_NORMAL],
3261+ gtk.STATE_SELECTED: self.text[gtk.STATE_NORMAL],
3262+ gtk.STATE_PRELIGHT: self.text[gtk.STATE_PRELIGHT],
3263+ gtk.STATE_INSENSITIVE: self.text[gtk.STATE_INSENSITIVE]}
3264+ return palette
3265+
3266+ def get_dark_line_palette(self):
3267+ palette = {gtk.STATE_NORMAL: self.bg[gtk.STATE_NORMAL].darken(),
3268+ gtk.STATE_ACTIVE: self.bg[gtk.STATE_NORMAL].darken(),
3269+ gtk.STATE_PRELIGHT: self.bg[gtk.STATE_NORMAL].darken(),
3270+ gtk.STATE_SELECTED: self.bg[gtk.STATE_NORMAL].darken(),
3271+ gtk.STATE_INSENSITIVE: self.bg[gtk.STATE_INSENSITIVE].darken()}
3272+ return palette
3273+
3274+ def get_light_line_palette(self):
3275+ palette = {gtk.STATE_NORMAL: self.bg[gtk.STATE_NORMAL].lighten(),
3276+ gtk.STATE_ACTIVE: self.bg[gtk.STATE_ACTIVE].lighten(),
3277+ gtk.STATE_PRELIGHT: self.bg[gtk.STATE_NORMAL].lighten(),
3278+ gtk.STATE_SELECTED: self.bg[gtk.STATE_NORMAL].lighten(),
3279+ gtk.STATE_INSENSITIVE: self.bg[gtk.STATE_INSENSITIVE]}
3280+ return palette
3281+
3282+ def get_text_states(self):
3283+ states = {gtk.STATE_NORMAL: gtk.STATE_NORMAL,
3284+ gtk.STATE_ACTIVE: gtk.STATE_NORMAL,
3285+ gtk.STATE_PRELIGHT: gtk.STATE_NORMAL,
3286+ gtk.STATE_SELECTED: gtk.STATE_NORMAL,
3287+ gtk.STATE_INSENSITIVE: gtk.STATE_INSENSITIVE}
3288+ return states
3289+
3290+
3291+class DustSand(Theme):
3292+
3293+ def get_properties(self, gtksettings):
3294+ props = {
3295+ 'curvature': 2.5,
3296+ 'min_part_width': 48,
3297+ 'xpad': 8,
3298+ 'ypad': 4,
3299+ 'xthickness': 1,
3300+ 'ythickness': 1,
3301+ 'spacing': 5,
3302+ 'arrow_width': 13,
3303+ 'scroll_duration': 150,
3304+ 'enable-animations': gtksettings.get_property("gtk-enable-animations"),
3305+ 'override_base': False
3306+ }
3307+ return props
3308+
3309+ def get_grad_palette(self):
3310+
3311+ selected_color = self.bg[gtk.STATE_NORMAL].mix(self.bg[gtk.STATE_SELECTED],
3312+ 0.4)
3313+
3314+ prelight_color = self.bg[gtk.STATE_NORMAL].mix(self.bg[gtk.STATE_SELECTED],
3315+ 0.175)
3316+
3317+ # provide two colours per state for background vertical linear gradients
3318+ palette = {gtk.STATE_NORMAL: (self.bg[gtk.STATE_NORMAL].shade(1.42),
3319+ self.bg[gtk.STATE_NORMAL].shade(1.1)),
3320+
3321+ gtk.STATE_ACTIVE: (prelight_color,
3322+ prelight_color.shade(1.07)),
3323+
3324+ gtk.STATE_SELECTED: (selected_color.shade(1.35),
3325+ selected_color.shade(1.1)),
3326+
3327+ gtk.STATE_PRELIGHT: (prelight_color.shade(1.74),
3328+ prelight_color.shade(1.42)),
3329+
3330+ gtk.STATE_INSENSITIVE: (self.bg[gtk.STATE_INSENSITIVE],
3331+ self.bg[gtk.STATE_INSENSITIVE])
3332+ }
3333+ return palette
3334+
3335+ def get_text_palette(self):
3336+ palette = {gtk.STATE_NORMAL: self.text[gtk.STATE_NORMAL],
3337+ gtk.STATE_ACTIVE: self.text[gtk.STATE_ACTIVE],
3338+ gtk.STATE_SELECTED: self.text[gtk.STATE_SELECTED],
3339+ gtk.STATE_PRELIGHT: self.text[gtk.STATE_PRELIGHT],
3340+ gtk.STATE_INSENSITIVE: self.text[gtk.STATE_INSENSITIVE]}
3341+ return palette
3342+
3343+ def get_dark_line_palette(self):
3344+ palette = {gtk.STATE_NORMAL: self.bg[gtk.STATE_NORMAL].shade(0.575),
3345+ gtk.STATE_ACTIVE: self.bg[gtk.STATE_ACTIVE].shade(0.5),
3346+ gtk.STATE_PRELIGHT: self.bg[gtk.STATE_PRELIGHT].shade(0.575),
3347+ gtk.STATE_SELECTED: self.bg[gtk.STATE_SELECTED].shade(0.575),
3348+ gtk.STATE_INSENSITIVE: self.bg[gtk.STATE_NORMAL].darken()}
3349+ return palette
3350+
3351+ def get_light_line_palette(self):
3352+ palette = {gtk.STATE_NORMAL: self.bg[gtk.STATE_NORMAL].lighten(),
3353+ gtk.STATE_ACTIVE: self.bg[gtk.STATE_ACTIVE].shade(0.95),
3354+ gtk.STATE_PRELIGHT: self.bg[gtk.STATE_PRELIGHT].lighten(),
3355+ gtk.STATE_SELECTED: self.bg[gtk.STATE_NORMAL].lighten(),
3356+ gtk.STATE_INSENSITIVE: self.bg[gtk.STATE_INSENSITIVE]}
3357+ return palette
3358+
3359+ def get_text_states(self):
3360+ states = {gtk.STATE_NORMAL: gtk.STATE_NORMAL,
3361+ gtk.STATE_ACTIVE: gtk.STATE_NORMAL,
3362+ gtk.STATE_PRELIGHT: gtk.STATE_NORMAL,
3363+ gtk.STATE_SELECTED: gtk.STATE_NORMAL,
3364+ gtk.STATE_INSENSITIVE: gtk.STATE_INSENSITIVE}
3365+ return states
3366+
3367+
3368+class Dust(DustSand):
3369+
3370+ def get_grad_palette(self):
3371+
3372+ selected_color = self.bg[gtk.STATE_NORMAL].mix(self.bg[gtk.STATE_SELECTED],
3373+ 0.5)
3374+
3375+ prelight_color = self.bg[gtk.STATE_NORMAL].mix(self.bg[gtk.STATE_SELECTED],
3376+ 0.175)
3377+
3378+ # provide two colours per state for background vertical linear gradients
3379+ palette = {gtk.STATE_NORMAL: (self.bg[gtk.STATE_NORMAL].shade(1.4),
3380+ self.bg[gtk.STATE_NORMAL].shade(1.1)),
3381+
3382+ gtk.STATE_ACTIVE: (self.bg[gtk.STATE_ACTIVE].shade(1.2),
3383+ self.bg[gtk.STATE_ACTIVE]),
3384+
3385+ gtk.STATE_SELECTED: (selected_color.shade(1.5),
3386+ selected_color.shade(1.2)),
3387+
3388+ gtk.STATE_PRELIGHT: (prelight_color.shade(1.74),
3389+ prelight_color.shade(1.42)),
3390+
3391+ gtk.STATE_INSENSITIVE: (self.bg[gtk.STATE_INSENSITIVE],
3392+ self.bg[gtk.STATE_INSENSITIVE])
3393+ }
3394+ return palette
3395+
3396+ def get_dark_line_palette(self):
3397+ palette = DustSand.get_dark_line_palette(self)
3398+ palette[gtk.STATE_SELECTED] = self.bg[gtk.STATE_NORMAL].shade(0.575)
3399+ return palette
3400+
3401+ def get_light_line_palette(self):
3402+ palette = DustSand.get_light_line_palette(self)
3403+ palette[gtk.STATE_SELECTED] = self.bg[gtk.STATE_NORMAL].shade(1.15)
3404+ return palette
3405+
3406+
3407+class Ambiance(DustSand):
3408+
3409+ def get_properties(self, gtksettings):
3410+ props = DustSand.get_properties(self, gtksettings)
3411+ props['curvature'] = 4.5
3412+ return props
3413+
3414+ def get_grad_palette(self):
3415+ focus_color = color_from_string('#FE765E')
3416+ selected_color = self.bg[gtk.STATE_NORMAL].mix(focus_color,
3417+ 0.07)
3418+ prelight_color = self.bg[gtk.STATE_NORMAL].mix(focus_color,
3419+ 0.33)
3420+
3421+ # provide two colours per state for background vertical linear gradients
3422+ palette = {gtk.STATE_NORMAL: (self.bg[gtk.STATE_NORMAL].shade(1.2),
3423+ self.bg[gtk.STATE_NORMAL].shade(0.85)),
3424+
3425+ gtk.STATE_ACTIVE: (self.bg[gtk.STATE_NORMAL].shade(0.96),
3426+ self.bg[gtk.STATE_NORMAL].shade(0.65)),
3427+
3428+ gtk.STATE_SELECTED: (selected_color.shade(1.075),
3429+ selected_color.shade(0.875)),
3430+
3431+ gtk.STATE_PRELIGHT: (prelight_color.shade(1.35),
3432+ prelight_color.shade(1.1)),
3433+
3434+ gtk.STATE_INSENSITIVE: (self.bg[gtk.STATE_INSENSITIVE],
3435+ self.bg[gtk.STATE_INSENSITIVE])
3436+ }
3437+ return palette
3438+
3439+
3440+class Radiance(Ambiance):
3441+
3442+ def get_grad_palette(self):
3443+ palette = Ambiance.get_grad_palette(self)
3444+ palette[gtk.STATE_NORMAL] = (self.mid[gtk.STATE_NORMAL].shade(1.25),
3445+ self.bg[gtk.STATE_NORMAL].shade(0.9))
3446+ return palette
3447+
3448+
3449+class NewWave(Theme):
3450+
3451+ def get_properties(self, gtksettings):
3452+ props = {
3453+ 'curvature': 2,
3454+ 'min_part_width': 48,
3455+ 'xpad': 8,
3456+ 'ypad': 4,
3457+ 'xthickness': 1,
3458+ 'ythickness': 1,
3459+ 'spacing': 4,
3460+ 'arrow_width': 13,
3461+ 'scroll_duration': 150,
3462+ 'enable-animations': gtksettings.get_property("gtk-enable-animations"),
3463+ 'override_base': True
3464+ }
3465+ return props
3466+
3467+ def get_grad_palette(self):
3468+ # provide two colours per state for background vertical linear gradients
3469+
3470+ active_color = self.bg[gtk.STATE_ACTIVE].mix(color_from_string('#FDCF9D'),
3471+ 0.45)
3472+
3473+ selected_color = self.bg[gtk.STATE_NORMAL].mix(color_from_string('#FDCF9D'),
3474+ 0.2)
3475+
3476+ palette = {gtk.STATE_NORMAL: (self.bg[gtk.STATE_NORMAL].shade(1.1),
3477+ self.bg[gtk.STATE_NORMAL].shade(0.95)),
3478+
3479+ gtk.STATE_ACTIVE: (active_color.shade(1.1),
3480+ self.bg[gtk.STATE_ACTIVE].shade(0.95)),
3481+
3482+ gtk.STATE_PRELIGHT: (color_from_string('#FDCF9D'),
3483+ color_from_string('#FCAE87')),
3484+
3485+ gtk.STATE_SELECTED: (selected_color.shade(1.2),
3486+ selected_color),
3487+
3488+ gtk.STATE_INSENSITIVE: (self.bg[gtk.STATE_INSENSITIVE],
3489+ self.bg[gtk.STATE_INSENSITIVE])
3490+ }
3491+ return palette
3492+
3493+ def get_text_palette(self):
3494+ palette = {gtk.STATE_NORMAL: self.text[gtk.STATE_NORMAL],
3495+ gtk.STATE_ACTIVE: self.text[gtk.STATE_NORMAL],
3496+ gtk.STATE_PRELIGHT: self.text[gtk.STATE_NORMAL],
3497+ gtk.STATE_SELECTED: self.text[gtk.STATE_SELECTED],
3498+ gtk.STATE_INSENSITIVE: self.text[gtk.STATE_INSENSITIVE]}
3499+ return palette
3500+
3501+ def get_dark_line_palette(self):
3502+ palette = {gtk.STATE_NORMAL: self.bg[gtk.STATE_NORMAL].darken(),
3503+ gtk.STATE_ACTIVE: self.bg[gtk.STATE_NORMAL].darken(),
3504+ gtk.STATE_PRELIGHT: self.bg[gtk.STATE_NORMAL].darken(),
3505+ gtk.STATE_SELECTED: self.bg[gtk.STATE_NORMAL].darken(),
3506+ gtk.STATE_INSENSITIVE: self.bg[gtk.STATE_INSENSITIVE].darken()}
3507+ return palette
3508+
3509+ def get_light_line_palette(self):
3510+ palette = {gtk.STATE_NORMAL: self.bg[gtk.STATE_NORMAL].lighten(),
3511+ gtk.STATE_ACTIVE: self.bg[gtk.STATE_ACTIVE].shade(0.97),
3512+ gtk.STATE_PRELIGHT: color_from_string('#FDCF9D'),
3513+ gtk.STATE_SELECTED: self.bg[gtk.STATE_SELECTED].lighten(),
3514+ gtk.STATE_INSENSITIVE: self.bg[gtk.STATE_INSENSITIVE]}
3515+ return palette
3516+
3517+ def get_text_states(self):
3518+ states = {gtk.STATE_NORMAL: gtk.STATE_NORMAL,
3519+ gtk.STATE_ACTIVE: gtk.STATE_NORMAL,
3520+ gtk.STATE_PRELIGHT: gtk.STATE_NORMAL,
3521+ gtk.STATE_SELECTED: gtk.STATE_NORMAL,
3522+ gtk.STATE_INSENSITIVE: gtk.STATE_INSENSITIVE}
3523+ return states
3524+
3525+
3526+class Hicolor(Theme):
3527+
3528+ def get_properties(self, gtksettings):
3529+ props = {
3530+ 'curvature': 0,
3531+ 'min_part_width': 48,
3532+ 'xpad': 15,
3533+ 'ypad': 10,
3534+ 'xthickness': 2,
3535+ 'ythickness': 2,
3536+ 'spacing': 10,
3537+ 'arrow_width': 15,
3538+ 'scroll_duration': 150,
3539+ 'enable-animations': gtksettings.get_property("gtk-enable-animations"),
3540+ 'override_base': False
3541+ }
3542+ return props
3543+
3544+ def get_grad_palette(self):
3545+ # provide two colours per state for background vertical linear gradients
3546+ palette = {gtk.STATE_NORMAL: (self.mid[gtk.STATE_NORMAL],
3547+ self.mid[gtk.STATE_NORMAL]),
3548+
3549+ gtk.STATE_ACTIVE: (self.mid[gtk.STATE_ACTIVE],
3550+ self.mid[gtk.STATE_ACTIVE]),
3551+
3552+ gtk.STATE_SELECTED: (self.mid[gtk.STATE_SELECTED],
3553+ self.mid[gtk.STATE_SELECTED]),
3554+
3555+ gtk.STATE_PRELIGHT: (self.mid[gtk.STATE_PRELIGHT],
3556+ self.mid[gtk.STATE_PRELIGHT]),
3557+
3558+ gtk.STATE_INSENSITIVE: (self.bg[gtk.STATE_INSENSITIVE],
3559+ self.bg[gtk.STATE_INSENSITIVE])
3560+ }
3561+ return palette
3562+
3563+ def get_text_palette(self):
3564+ palette = {gtk.STATE_NORMAL: self.text[gtk.STATE_NORMAL],
3565+ gtk.STATE_ACTIVE: self.text[gtk.STATE_ACTIVE],
3566+ gtk.STATE_SELECTED: self.text[gtk.STATE_SELECTED],
3567+ gtk.STATE_PRELIGHT: self.text[gtk.STATE_PRELIGHT],
3568+ gtk.STATE_INSENSITIVE: self.text[gtk.STATE_INSENSITIVE]}
3569+ return palette
3570+
3571+ def get_dark_line_palette(self):
3572+ palette = {gtk.STATE_NORMAL: self.bg[gtk.STATE_SELECTED],
3573+ gtk.STATE_ACTIVE: self.dark[gtk.STATE_ACTIVE],
3574+ gtk.STATE_PRELIGHT: self.dark[gtk.STATE_PRELIGHT],
3575+ gtk.STATE_SELECTED: self.dark[gtk.STATE_SELECTED],
3576+ gtk.STATE_INSENSITIVE: self.dark[gtk.STATE_INSENSITIVE]}
3577+ return palette
3578+
3579+ def get_light_line_palette(self):
3580+ palette = {gtk.STATE_NORMAL: self.bg[gtk.STATE_SELECTED],
3581+ gtk.STATE_ACTIVE: self.light[gtk.STATE_ACTIVE],
3582+ gtk.STATE_PRELIGHT: self.light[gtk.STATE_PRELIGHT],
3583+ gtk.STATE_SELECTED: self.light[gtk.STATE_SELECTED],
3584+ gtk.STATE_INSENSITIVE: self.light[gtk.STATE_INSENSITIVE]}
3585+ return palette
3586+
3587+ def get_text_states(self):
3588+ states = {gtk.STATE_NORMAL: gtk.STATE_NORMAL,
3589+ gtk.STATE_ACTIVE: gtk.STATE_ACTIVE,
3590+ gtk.STATE_PRELIGHT: gtk.STATE_PRELIGHT,
3591+ gtk.STATE_SELECTED: gtk.STATE_SELECTED,
3592+ gtk.STATE_INSENSITIVE: gtk.STATE_INSENSITIVE}
3593+ return states
3594+
3595+
3596+class ThemeRegistry:
3597+
3598+ REGISTRY = {"Human": Human,
3599+ "Human-Clearlooks": Clearlooks,
3600+ "Clearlooks": Clearlooks,
3601+ "InHuman": InHuman,
3602+ "HighContrastInverse": Hicolor,
3603+ "HighContrastLargePrintInverse": Hicolor,
3604+ "Dust": Dust,
3605+ "Dust Sand": DustSand,
3606+ "New Wave": NewWave,
3607+ "Ambiance": Ambiance,
3608+ "Radiance": Radiance}
3609+
3610+ def retrieve(self, theme_name):
3611+ if self.REGISTRY.has_key(theme_name):
3612+ print 'Styling hints found for %s...' % theme_name
3613+ return self.REGISTRY[theme_name]()
3614+ print "No styling hints for %s were found... using Human hints." % theme_name
3615+ return Clearlooks()
3616+
3617+
3618+def color_from_gdkcolor(gdkcolor):
3619+ return PathBarColor(gdkcolor.red_float, gdkcolor.green_float, gdkcolor.blue_float)
3620+
3621+
3622+def color_from_string(spec):
3623+ color = gtk.gdk.color_parse(spec)
3624+ return PathBarColor(color.red_float, color.green_float, color.blue_float)
3625+
3626+
3627+
3628+
3629
3630=== added file 'softwarecenter/view/widgets/pathbar_gtk_atk.py'
3631--- softwarecenter/view/widgets/pathbar_gtk_atk.py 1970-01-01 00:00:00 +0000
3632+++ softwarecenter/view/widgets/pathbar_gtk_atk.py 2010-04-04 06:00:32 +0000
3633@@ -0,0 +1,703 @@
3634+# Copyright (C) 2010 Matthew McGowan
3635+#
3636+# Authors:
3637+# Matthew McGowan
3638+#
3639+# This program is free software: you can redistribute it and/or modify
3640+# it under the terms of the GNU General Public License as published by
3641+# the Free Software Foundation, either version 3 of the License, or
3642+# (at your option) any later version.
3643+#
3644+# This program is distributed in the hope that it will be useful,
3645+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3646+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3647+# GNU General Public License for more details.
3648+#
3649+# You should have received a copy of the GNU General Public License
3650+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3651+
3652+import atk
3653+import cairo
3654+import gobject
3655+import gtk
3656+import pango
3657+import pathbar_common
3658+
3659+from gettext import gettext as _
3660+
3661+
3662+class PathBar(gtk.HBox):
3663+
3664+ ANIMATE_FPS = 50
3665+ ANIMATE_DELAY = 150
3666+ ANIMATE_DURATION = 150
3667+
3668+ def __init__(self, group=None):
3669+ gtk.HBox.__init__(self)
3670+ self.set_redraw_on_allocate(False)
3671+
3672+ self._width = 0
3673+ self._queue = []
3674+ self._active_part = None
3675+ self._out_of_width = False
3676+ self._button_press_origin = None
3677+
3678+ self._animate = False, None
3679+ self._scroll_xO = 0
3680+ self._no_draw = False
3681+ self._scroller = None
3682+
3683+ self.theme = pathbar_common.PathBarStyle(self)
3684+
3685+ # Accessibility info
3686+ atk_desc = self.get_accessible()
3687+ atk_desc.set_name(_("You are here:"))
3688+ atk_desc.set_role(atk.ROLE_PANEL)
3689+
3690+ self.set_events(gtk.gdk.EXPOSURE_MASK)
3691+ self.connect('expose-event', self._on_expose_event)
3692+ self.connect('size-allocate', self._on_size_allocate)
3693+ self.connect('style-set', self._on_style_set)
3694+ self.connect('realize', self._append_on_realize)
3695+ return
3696+
3697+ def _shrink_check(self, allocation):
3698+ path_w = self._width
3699+ overhang = path_w - allocation.width
3700+ self._width -= overhang
3701+ mpw = self.theme['min_part_width']
3702+ for part in self.get_children():
3703+ w = part.get_size_request()[0]
3704+ dw = 0
3705+ if w - overhang <= mpw:
3706+ overhang -= w-mpw
3707+ part.set_width(mpw)
3708+ else:
3709+ part.set_width(w-overhang)
3710+ break
3711+ self._out_of_width = True
3712+ self.queue_draw()
3713+ return
3714+
3715+ def _grow_check(self, allocation):
3716+ w_freed = allocation.width - self._width
3717+ parts = self.get_children()
3718+ parts.reverse()
3719+ for part in parts:
3720+ bw = part.get_best_width()
3721+ w = part.get_size_request()[0]
3722+ if w < bw:
3723+ dw = bw - w
3724+ if dw <= w_freed:
3725+ w_freed -= dw
3726+ part.restore_best_width()
3727+ else:
3728+ part.set_width(w + w_freed)
3729+ w_freed = 0
3730+ break
3731+
3732+ self._width = allocation.width - w_freed
3733+ if self._width < allocation.width:
3734+ self._out_of_width = False
3735+ self.queue_draw()
3736+ return
3737+
3738+ def _compose_on_append(self, last_part):
3739+ parts = self.get_children()
3740+ if len(parts) == 0:
3741+ last_part.set_shape(pathbar_common.SHAPE_RECTANGLE)
3742+ elif len(parts) == 1:
3743+ root_part = parts[0]
3744+ root_part.set_shape(pathbar_common.SHAPE_START_ARROW)
3745+ last_part.set_shape(pathbar_common.SHAPE_END_CAP)
3746+ else:
3747+ tail_part = parts[-1]
3748+ tail_part.set_shape(pathbar_common.SHAPE_MID_ARROW)
3749+ last_part.set_shape(pathbar_common.SHAPE_END_CAP)
3750+ return
3751+
3752+ def _compose_on_remove(self, last_part):
3753+ parts = self.get_children()
3754+ if len(parts) == 1:
3755+ last_part.set_shape(pathbar_common.SHAPE_RECTANGLE)
3756+ elif len(parts) == 2:
3757+ root_part = parts[0]
3758+ root_part.set_shape(pathbar_common.SHAPE_START_ARROW)
3759+ last_part.set_shape(pathbar_common.SHAPE_END_CAP)
3760+ else:
3761+ tail_part = parts[-1]
3762+ tail_part.set_shape(pathbar_common.SHAPE_MID_ARROW)
3763+ last_part.set_shape(pathbar_common.SHAPE_END_CAP)
3764+ return
3765+
3766+ def _part_enter_notify(self, part, event):
3767+ if part == self._button_press_origin:
3768+ part.set_state(gtk.STATE_ACTIVE)
3769+ else:
3770+ part.set_state(gtk.STATE_PRELIGHT)
3771+ self._part_queue_draw(part)
3772+ return
3773+
3774+ def _part_leave_notify(self, part, event):
3775+ if part == self._active_part:
3776+ part.set_state(gtk.STATE_SELECTED)
3777+ else:
3778+ part.set_state(gtk.STATE_NORMAL)
3779+ self._part_queue_draw(part)
3780+ return
3781+
3782+ def _part_button_press(self, part, event):
3783+ if event.button != 1: return
3784+ self._button_press_origin = part
3785+ part.set_state(gtk.STATE_ACTIVE)
3786+ self._part_queue_draw(part)
3787+ return
3788+
3789+ def _part_button_release(self, part, event):
3790+ if event.button != 1: return
3791+
3792+ part_region = gtk.gdk.region_rectangle(part.allocation)
3793+ if not part_region.point_in(*self.window.get_pointer()[:2]):
3794+ self._button_press_origin = None
3795+ return
3796+ if part != self._button_press_origin: return
3797+ if self._active_part:
3798+ self._active_part.set_state(gtk.STATE_NORMAL)
3799+ self._part_queue_draw(self._active_part)
3800+
3801+ self.set_active(part)
3802+ part.set_state(gtk.STATE_PRELIGHT)
3803+ self._button_press_origin = None
3804+ self._part_queue_draw(part)
3805+ return
3806+
3807+ def _part_key_press(self, part, event):
3808+ # react to spacebar, enter, numpad-enter
3809+ if event.keyval in (32, 65293, 65421):
3810+ part.set_state(gtk.STATE_ACTIVE)
3811+ self._part_queue_draw(part)
3812+ return
3813+
3814+ def _part_key_release(self, part, event):
3815+ # react to spacebar, enter, numpad-enter
3816+ if event.keyval in (32, 65293, 65421):
3817+ self.set_active(part)
3818+ part.set_state(gtk.STATE_SELECTED)
3819+ self._part_queue_draw(part)
3820+ return
3821+
3822+ def _part_focus_in(self, part, event):
3823+ self._part_queue_draw(part)
3824+ return
3825+
3826+ def _part_focus_out(self, part, event):
3827+ self._part_queue_draw(part)
3828+ return
3829+
3830+ def _part_connect_signals(self, part):
3831+ part.connect('enter-notify-event', self._part_enter_notify)
3832+ part.connect('leave-notify-event', self._part_leave_notify)
3833+ part.connect("button-press-event", self._part_button_press)
3834+ part.connect("button-release-event", self._part_button_release)
3835+ part.connect("key-press-event", self._part_key_press)
3836+ part.connect("key-release-event", self._part_key_release)
3837+ part.connect('focus-in-event', self._part_focus_in)
3838+ part.connect('focus-out-event', self._part_focus_out)
3839+ return
3840+
3841+ def _part_queue_draw(self, part):
3842+ a = part.get_allocation()
3843+ x, y, h = a.x, a.y, a.height
3844+ w = part.get_draw_width()
3845+ xo = part.get_draw_xoffset()
3846+ self.queue_draw_area(x+xo, y, w, h)
3847+ return
3848+
3849+ def _on_expose_event(self, widget, event):
3850+ if self._scroll_xO:
3851+ self._expose_scroll(widget, event)
3852+ else:
3853+ self._expose_normal(widget, event)
3854+ return
3855+
3856+ def _expose_normal(self, widget, event):
3857+ theme = self.theme
3858+ parts = self.get_children()
3859+ parts.reverse()
3860+ region = gtk.gdk.region_rectangle(event.area)
3861+
3862+ cr = widget.window.cairo_create()
3863+ cr.rectangle(event.area)
3864+ cr.clip()
3865+
3866+ for part in parts:
3867+ if not part.invisible:
3868+ a = part.get_allocation()
3869+ xo = part.get_draw_xoffset()
3870+ x, y, w, h = a.x, a.y, a.width, a.height
3871+ w = part.get_draw_width()
3872+ theme.paint_bg(cr, part, x+xo, y, w, h)
3873+
3874+ x, y, w, h = part.get_layout_points()
3875+
3876+ if part.has_focus():
3877+ self.style.paint_focus(self.window,
3878+ part.state,
3879+ (a.x+x-4, a.y+y-2, w+8, h+4),
3880+ self,
3881+ 'button',
3882+ a.x+x-4, a.y+y-2, w+8, h+4)
3883+
3884+ theme.paint_layout(widget, part, a.x+x, a.y+y)
3885+ else:
3886+ part.invisible = False
3887+ del cr
3888+ return
3889+
3890+ def _expose_scroll(self, widget, event):
3891+ parts = self.get_children()
3892+ if len(parts) < 2: return
3893+ static_tail, scroller = parts[-2:]
3894+
3895+ if self.get_direction() != gtk.TEXT_DIR_RTL:
3896+ sxO = self._scroll_xO
3897+ else:
3898+ sxO = -self._scroll_xO
3899+
3900+ theme = self.theme
3901+
3902+ cr = widget.window.cairo_create()
3903+ cr.rectangle(event.area)
3904+ cr.clip()
3905+
3906+ a = scroller.get_allocation()
3907+ x, y, w, h = a.x, a.y, a.width, a.height
3908+ w = scroller.get_draw_width()
3909+ xo = scroller.get_draw_xoffset()
3910+ theme.paint_bg(cr, scroller, x+xo-sxO, y, w, h)
3911+ x, y, w, h = scroller.get_layout_points()
3912+ theme.paint_layout(widget, scroller, a.x+x-int(sxO), a.y+y)
3913+
3914+ a = static_tail.get_allocation()
3915+ x, y, w, h = a.x, a.y, a.width, a.height
3916+ w = static_tail.get_draw_width()
3917+ xo = static_tail.get_draw_xoffset()
3918+ theme.paint_bg(cr, static_tail, x+xo, y, w, h)
3919+ del cr
3920+ return
3921+
3922+ def _on_size_allocate(self, widget, allocation):
3923+ if self._width < allocation.width and self._out_of_width:
3924+ self._grow_check(allocation)
3925+ elif self._width >= allocation.width:
3926+ self._shrink_check(allocation)
3927+
3928+ if self._animate[0] and self.theme['enable-animations']:
3929+ part = self._animate[1]
3930+ part.invisible = True
3931+ self._animate = False, None
3932+ gobject.timeout_add(self.ANIMATE_DELAY, self._scroll_out_init, part)
3933+ else:
3934+ self.queue_draw()
3935+ return
3936+
3937+ def _on_style_set(self, widget, old_style):
3938+ self.theme = pathbar_common.PathBarStyle(self)
3939+ for part in self.get_children():
3940+ part.recalc_dimensions()
3941+ self.queue_draw()
3942+ return
3943+
3944+ def _append_on_realize(self, widget):
3945+ for part, do_callback, animate in self._queue:
3946+ self.append(part, do_callback, animate)
3947+ return
3948+
3949+ def _scroll_out_init(self, part):
3950+ draw_area = part.get_allocation()
3951+ self._scroller = gobject.timeout_add(
3952+ max(int(1000.0 / self.ANIMATE_FPS), 10), # interval
3953+ self._scroll_out_cb,
3954+ part.get_size_request()[0],
3955+ self.ANIMATE_DURATION*0.001, # 1 over duration (converted to seconds)
3956+ gobject.get_current_time(),
3957+ (draw_area.x, draw_area.y,
3958+ draw_area.width, draw_area.height))
3959+ return False
3960+
3961+ def _scroll_out_cb(self, distance, duration, start_t, draw_area):
3962+ cur_t = gobject.get_current_time()
3963+ xO = distance - distance*((cur_t - start_t) / duration)
3964+
3965+ if xO > 0:
3966+ self._scroll_xO = xO
3967+ self.queue_draw_area(*draw_area)
3968+
3969+ else: # final frame
3970+ self._scroll_xO = 0
3971+ # redraw the entire widget
3972+ # incase some timeouts are skipped due to high system load
3973+ self.queue_draw_area(*draw_area)
3974+ self._scroller = None
3975+ return False
3976+ return True
3977+
3978+ def has_parts(self):
3979+ return self.get_children() == True
3980+
3981+ def get_parts(self):
3982+ return self.get_children()
3983+
3984+ def get_last(self):
3985+ if self.get_children():
3986+ return self.get_children()[-1]
3987+ return None
3988+
3989+ def set_active(self, part, do_callback=True):
3990+ if part == self._active_part: return
3991+ if self._active_part:
3992+ self._active_part.set_state(gtk.STATE_NORMAL)
3993+ self._part_queue_draw(self._active_part)
3994+
3995+ part.set_state(gtk.STATE_SELECTED)
3996+ self._part_queue_draw(part)
3997+ self._active_part = part
3998+ if do_callback and part.callback:
3999+ part.callback(self, part)
4000+ return
4001+
4002+ def get_active(self):
4003+ return self._active_part
4004+
4005+ def set_active_no_callback(self, part):
4006+ self.set_active(part, False)
4007+ return
4008+
4009+ def append(self, part, do_callback=True, animate=True):
4010+ if not self.get_property('visible'):
4011+ self._queue.append([part, do_callback, animate])
4012+ return
4013+
4014+ if self._scroller:
4015+ gobject.source_remove(self._scroller)
4016+ self._scroll_xO = 0
4017+
4018+ self._compose_on_append(part)
4019+ self._width += part.get_size_request()[0]
4020+
4021+ self.pack_start(part, False)
4022+ self._part_connect_signals(part)
4023+ self._animate = animate, part
4024+ part.show()
4025+
4026+ if do_callback:
4027+ self.set_active(part)
4028+ else:
4029+ self.set_active_no_callback(part)
4030+ return
4031+
4032+ def append_no_callback(self, part):
4033+ self.append(part, do_callback=False)
4034+
4035+ def remove(self, part):
4036+ parts = self.get_children()
4037+ if len(parts) <= 1: return # protect last part
4038+ self._width -= part.get_size_request()[0]
4039+ part.destroy()
4040+ self._compose_on_remove(parts[-2])
4041+ return
4042+
4043+ def remove_all(self, keep_first_part=True, do_callback=True):
4044+ parts = self.get_children()
4045+ if len(parts) < 1: return
4046+ if keep_first_part:
4047+ if len(parts) <= 1: return
4048+ parts = parts[1:]
4049+ for part in parts:
4050+ part.destroy()
4051+
4052+ self._width = 0
4053+ if keep_first_part:
4054+ root = self.get_parts()[0]
4055+ root.set_shape(pathbar_common.SHAPE_RECTANGLE)
4056+ self._width = root.get_size_request()[0]
4057+ if do_callback: root.callback(self, root)
4058+ return
4059+
4060+ def navigate_up(self):
4061+ parts = self.get_children()
4062+ if len(parts) > 1:
4063+ nav_part = parts[len(parts) - 2]
4064+ self.set_active(nav_part)
4065+ return
4066+
4067+
4068+class PathPart(gtk.EventBox):
4069+
4070+ def __init__(self, parent, label, callback=None):
4071+ gtk.EventBox.__init__(self)
4072+ self.set_redraw_on_allocate(False)
4073+ self.set_visible_window(False)
4074+
4075+ part_atk = self.get_accessible()
4076+ part_atk.set_name(label)
4077+ part_atk.set_description(_('Navigates to the %s page.' % label))
4078+ part_atk.set_role(atk.ROLE_PUSH_BUTTON)
4079+
4080+ self.invisible = False
4081+ self._parent = parent
4082+ self._draw_shift = 0
4083+ self._draw_width = 0
4084+ self._best_width = 0
4085+ self._layout_points = 0,0,0,0
4086+ self._size_requisition = 0,0
4087+
4088+ self.label = None
4089+ self.shape = pathbar_common.SHAPE_RECTANGLE
4090+ self.layout = None
4091+ self.callback = callback
4092+ self.set_label(label)
4093+
4094+ self.set_flags(gtk.CAN_FOCUS)
4095+ self.set_events(gtk.gdk.BUTTON_PRESS_MASK|
4096+ gtk.gdk.BUTTON_RELEASE_MASK|
4097+ gtk.gdk.KEY_RELEASE_MASK|
4098+ gtk.gdk.KEY_PRESS_MASK|
4099+ gtk.gdk.ENTER_NOTIFY_MASK|
4100+ gtk.gdk.LEAVE_NOTIFY_MASK)
4101+ return
4102+
4103+ def __repr__(self):
4104+ return self.label
4105+
4106+ def _make_layout(self):
4107+ pc = self._parent.get_pango_context()
4108+ layout = pango.Layout(pc)
4109+ layout.set_markup(self.label)
4110+ layout.set_ellipsize(pango.ELLIPSIZE_END)
4111+ self.layout = layout
4112+ return
4113+
4114+ def _set_best_width(self, best_width):
4115+ self._best_width = best_width
4116+ return
4117+
4118+ def _calc_layout_points(self):
4119+ if not self.layout: self._make_layout()
4120+ x = self._parent.theme['xpad']
4121+ y = self._parent.theme['ypad']
4122+ w, h = self.layout.get_pixel_extents()[1][2:]
4123+ self._layout_points = [x, y, w, h]
4124+ return
4125+
4126+ def _adjust_width(self, shape, w):
4127+ self._draw_xoffset = 0
4128+ self._draw_width = w
4129+
4130+ arrow_width = self._parent.theme['arrow_width']
4131+ if shape == pathbar_common.SHAPE_RECTANGLE:
4132+ return w
4133+
4134+ elif shape == pathbar_common.SHAPE_START_ARROW:
4135+ self._draw_width += arrow_width
4136+ if self.get_direction() == gtk.TEXT_DIR_RTL:
4137+ self._draw_xoffset -= arrow_width
4138+
4139+ elif shape == pathbar_common.SHAPE_END_CAP:
4140+ w += arrow_width
4141+ self._draw_width += arrow_width
4142+ if self.get_direction() != gtk.TEXT_DIR_RTL:
4143+ self._layout_points[0] += arrow_width
4144+
4145+ elif shape == pathbar_common.SHAPE_MID_ARROW:
4146+ w += arrow_width
4147+ self._draw_width += 2*arrow_width
4148+ if self.get_direction() == gtk.TEXT_DIR_RTL:
4149+ self._draw_xoffset -= arrow_width
4150+ else:
4151+ self._layout_points[0] += arrow_width
4152+ return w
4153+
4154+ def _calc_size(self, shape):
4155+ lx, ly, w, h = self.layout.get_pixel_extents()[1]
4156+ w += 2*self._parent.theme['xpad']
4157+ h += 2*self._parent.theme['ypad']
4158+
4159+ w = self._adjust_width(shape, w)
4160+ if not self.get_best_width():
4161+ self._set_best_width(w)
4162+ self.set_size_request(w, h)
4163+ return
4164+
4165+ def do_callback(self):
4166+ self.callback(self._parent, self)
4167+ return
4168+
4169+ def set_label(self, label):
4170+ if label == self.label: return
4171+ self.label = gobject.markup_escape_text(label.strip())
4172+ if not self.layout:
4173+ self._make_layout()
4174+ else:
4175+ self.layout.set_markup(self.label)
4176+
4177+ self._calc_layout_points()
4178+ self._calc_size(self.shape)
4179+ return
4180+
4181+ def set_shape(self, shape):
4182+ self.shape = shape
4183+ self._calc_layout_points()
4184+ self._calc_size(shape)
4185+ return
4186+
4187+ def set_width(self, w):
4188+ theme = self._parent.theme
4189+ lw = w-theme['arrow_width']
4190+ if self.shape != pathbar_common.SHAPE_START_ARROW:
4191+ lw -= theme['xpad']
4192+ if self.shape == pathbar_common.SHAPE_MID_ARROW:
4193+ lw -= theme['arrow_width']
4194+ self.layout.set_width(lw*pango.SCALE)
4195+
4196+ self._draw_width = w+theme['arrow_width']
4197+ self.set_size_request(w, -1)
4198+ return
4199+
4200+ def restore_best_width(self):
4201+ w = self.get_best_width()
4202+ arrow_width = self._parent.theme['arrow_width']
4203+
4204+ if self.shape == pathbar_common.SHAPE_MID_ARROW:
4205+ w += arrow_width
4206+ if self.shape == pathbar_common.SHAPE_END_CAP and \
4207+ self.get_direction() == gtk.TEXT_DIR_RTL:
4208+ self._draw_xoffset -= arrow_width
4209+ self._layout_points[0] -= arrow_width
4210+
4211+ self.layout.set_width(-1)
4212+ self._draw_width = w+arrow_width
4213+ self.set_size_request(w, -1)
4214+ return
4215+
4216+ def get_draw_width(self):
4217+ return self._draw_width
4218+
4219+ def get_draw_xoffset(self):
4220+ return self._draw_xoffset
4221+
4222+ def get_best_width(self):
4223+ return self._best_width
4224+
4225+ def get_layout_points(self):
4226+ x, y, w, h = self._layout_points
4227+ y = int(max((self.allocation.height-h)*0.5+0.5, y))
4228+ return x, y, w, h
4229+
4230+ def get_layout(self):
4231+ return self.layout
4232+
4233+ def recalc_dimensions(self):
4234+ self.layout = None
4235+ self._calc_layout_points()
4236+ self._calc_size(self.shape)
4237+ return
4238+
4239+class NavigationBar(PathBar):
4240+
4241+ APPEND_DELAY = 150
4242+
4243+ def __init__(self, group=None):
4244+ PathBar.__init__(self)
4245+ self.id_to_part = {}
4246+ return
4247+
4248+ def add_with_id(self, label, callback, id, do_callback=True, animate=True):
4249+ """
4250+ Add a new button with the given label/callback
4251+
4252+ If there is the same id already, replace the existing one
4253+ with the new one
4254+ """
4255+
4256+ # check if we have the button of that id or need a new one
4257+ if id in self.id_to_part:
4258+ part = self.id_to_part[id]
4259+ part.set_label(label)
4260+ else:
4261+ part = PathPart(parent=self, label=label, callback=callback)
4262+ part.set_name(id)
4263+ self.id_to_part[id] = part
4264+ self.append(part, do_callback, animate)
4265+ return
4266+
4267+ def remove_id(self, id):
4268+ if not id in self.id_to_part:
4269+ return
4270+ part = self.id_to_part[id]
4271+ del self.id_to_part[id]
4272+ self.remove(part)
4273+ return
4274+
4275+ def remove_all(self, keep_first_part=True, do_callback=True):
4276+ if len(self.get_parts()) <= 1: return
4277+ root = self.get_children()[0]
4278+ self.id_to_part = {root.get_name(): root}
4279+ PathBar.remove_all(self, do_callback=do_callback)
4280+ return
4281+
4282+ def get_button_from_id(self, id):
4283+ """
4284+ return the button for the given id (or None)
4285+ """
4286+ if not id in self.id_to_part:
4287+ return None
4288+ return self.id_to_part[id]
4289+
4290+
4291+class Test:
4292+
4293+ def __init__(self):
4294+ self.counter = 0
4295+ w = gtk.Window()
4296+ w.connect("destroy", gtk.main_quit)
4297+ w.set_size_request(384, -1)
4298+ w.set_default_size(512, -1)
4299+ w.set_border_width(3)
4300+
4301+ vb = gtk.VBox()
4302+ w.add(vb)
4303+
4304+ pb = PathBar()
4305+ vb.pack_start(pb, False)
4306+ part = PathPart(pb, 'Get Free Software?')
4307+ pb.append(part)
4308+
4309+ add = gtk.Button(stock=gtk.STOCK_ADD)
4310+ rem = gtk.Button(stock=gtk.STOCK_REMOVE)
4311+ self.entry = gtk.Entry()
4312+
4313+ vb.pack_start(add, False)
4314+ vb.pack_start(self.entry, False)
4315+ vb.pack_start(rem, False)
4316+ add.connect('clicked', self.add_cb, pb)
4317+ rem.connect('clicked', self.rem_cb, pb)
4318+
4319+ w.show_all()
4320+ gtk.main()
4321+ return
4322+
4323+ def add_cb(self, widget, pb):
4324+ text = self.entry.get_text() or ('unnammed%s' % self.counter)
4325+ part = PathPart(pb, text)
4326+ pb.append(part)
4327+ self.counter += 1
4328+ return
4329+
4330+ def rem_cb(self, widget, pb):
4331+ last = pb.get_children()[-1]
4332+ pb.remove(last)
4333+ return
4334+
4335+if __name__ == '__main__':
4336+ Test()
4337
4338=== removed file 'softwarecenter/view/widgets/rgb.py'
4339--- softwarecenter/view/widgets/rgb.py 2009-10-09 13:44:41 +0000
4340+++ softwarecenter/view/widgets/rgb.py 1970-01-01 00:00:00 +0000
4341@@ -1,67 +0,0 @@
4342-# Copyright (C) 2009 Matthew McGowan
4343-#
4344-# Authors:
4345-# Matthew McGowan
4346-#
4347-# This program is free software: you can redistribute it and/or modify
4348-# it under the terms of the GNU General Public License as published by
4349-# the Free Software Foundation, either version 3 of the License, or
4350-# (at your option) any later version.
4351-#
4352-# This program is distributed in the hope that it will be useful,
4353-# but WITHOUT ANY WARRANTY; without even the implied warranty of
4354-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4355-# GNU General Public License for more details.
4356-#
4357-# You should have received a copy of the GNU General Public License
4358-# along with this program. If not, see <http://www.gnu.org/licenses/>.
4359-
4360-
4361-import colorsys
4362-from gtk.gdk import Color
4363-
4364-
4365-def parse_colour_scheme(colour_scheme_str):
4366- scheme_dict = {}
4367- for ln in colour_scheme_str.splitlines():
4368- k, v = ln.split(':')
4369- scheme_dict[k.strip()] = gtk.gdk.color_parse(v.strip())
4370- return scheme_dict
4371-
4372-
4373-def shade(color, k):
4374- # as seen in Murrine's cairo-support.c
4375- r = color.red_float
4376- g = color.green_float
4377- b = color.blue_float
4378-
4379- if (k == 1.0):
4380- return color
4381-
4382- h,l,s = colorsys.rgb_to_hls(r,g,b)
4383-
4384- l *= k
4385- if (l > 1.0):
4386- l = 1.0
4387- elif (l < 0.0):
4388- l = 0.0
4389-
4390- s *= k
4391- if (s > 1.0):
4392- s = 1.0
4393- elif (s < 0.0):
4394- s = 0.0
4395-
4396- r, g, b = colorsys.hls_to_rgb(h,l,s)
4397-
4398- return Color(int(r*65535), int(g*65535), int(b*65535))
4399-
4400-def mix_color(color1, color2, mix_factor):
4401- # as seen in Murrine's cairo-support.c
4402- r = color1.red_float*(1-mix_factor)+color2.red_float*mix_factor
4403- g = color1.green_float*(1-mix_factor)+color2.green_float*mix_factor
4404- b = color1.blue_float*(1-mix_factor)+color2.blue_float*mix_factor
4405- return Color(int(r*65535), int(g*65535), int(b*65535))
4406-
4407-def to_float(color):
4408- return color.red_float, color.green_float, color.blue_float