diff options
author | Richard Purdie <richard.purdie@linuxfoundation.org> | 2016-05-13 14:11:02 +0100 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2016-06-02 08:24:03 +0100 |
commit | 8a93f5f32e141b4d153156152b9aeb1af25325ba (patch) | |
tree | 53e312db1ade4f78829221fb16c8cb713a846f53 /bitbake/lib/bb/ui/crumbs/hobwidget.py | |
parent | 685628ebc459dd1a09ce0dafc88a159aaaead732 (diff) | |
download | poky-8a93f5f32e141b4d153156152b9aeb1af25325ba.tar.gz |
bitbake: goggle/image-writer: Drop since bitrotting and no longer used
The upgrade to python3 is the final nail in the coffin for image-writer
and the goggle UI. Neither seem used or recieve patches and are based
on old versions of GTK+ so drop them, and the remaining crumbs support
pieces.
(Bitbake rev: ee7df1ca00c76f755057c157c093294efb9078d8)
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/bb/ui/crumbs/hobwidget.py')
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/hobwidget.py | 904 |
1 files changed, 0 insertions, 904 deletions
diff --git a/bitbake/lib/bb/ui/crumbs/hobwidget.py b/bitbake/lib/bb/ui/crumbs/hobwidget.py deleted file mode 100644 index 3f6138b277..0000000000 --- a/bitbake/lib/bb/ui/crumbs/hobwidget.py +++ /dev/null | |||
@@ -1,904 +0,0 @@ | |||
1 | # BitBake Graphical GTK User Interface | ||
2 | # | ||
3 | # Copyright (C) 2011-2012 Intel Corporation | ||
4 | # | ||
5 | # Authored by Dongxiao Xu <dongxiao.xu@intel.com> | ||
6 | # Authored by Shane Wang <shane.wang@intel.com> | ||
7 | # | ||
8 | # This program is free software; you can redistribute it and/or modify | ||
9 | # it under the terms of the GNU General Public License version 2 as | ||
10 | # published by the Free Software Foundation. | ||
11 | # | ||
12 | # This program is distributed in the hope that it will be useful, | ||
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | # GNU General Public License for more details. | ||
16 | # | ||
17 | # You should have received a copy of the GNU General Public License along | ||
18 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
20 | import gtk | ||
21 | import gobject | ||
22 | import os | ||
23 | import os.path | ||
24 | import sys | ||
25 | import pango, pangocairo | ||
26 | import cairo | ||
27 | import math | ||
28 | |||
29 | from bb.ui.crumbs.hobcolor import HobColors | ||
30 | from bb.ui.crumbs.persistenttooltip import PersistentTooltip | ||
31 | |||
32 | class hwc: | ||
33 | |||
34 | MAIN_WIN_WIDTH = 1024 | ||
35 | MAIN_WIN_HEIGHT = 700 | ||
36 | |||
37 | class hic: | ||
38 | |||
39 | HOB_ICON_BASE_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), ("ui/icons/")) | ||
40 | |||
41 | ICON_RCIPE_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('recipe/recipe_display.png')) | ||
42 | ICON_RCIPE_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('recipe/recipe_hover.png')) | ||
43 | ICON_PACKAGES_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('packages/packages_display.png')) | ||
44 | ICON_PACKAGES_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('packages/packages_hover.png')) | ||
45 | ICON_LAYERS_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('layers/layers_display.png')) | ||
46 | ICON_LAYERS_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('layers/layers_hover.png')) | ||
47 | ICON_IMAGES_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('images/images_display.png')) | ||
48 | ICON_IMAGES_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('images/images_hover.png')) | ||
49 | ICON_SETTINGS_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('settings/settings_display.png')) | ||
50 | ICON_SETTINGS_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('settings/settings_hover.png')) | ||
51 | ICON_INFO_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('info/info_display.png')) | ||
52 | ICON_INFO_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('info/info_hover.png')) | ||
53 | ICON_INDI_CONFIRM_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/confirmation.png')) | ||
54 | ICON_INDI_ERROR_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/denied.png')) | ||
55 | ICON_INDI_REMOVE_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/remove.png')) | ||
56 | ICON_INDI_REMOVE_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/remove-hover.png')) | ||
57 | ICON_INDI_ADD_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/add.png')) | ||
58 | ICON_INDI_ADD_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/add-hover.png')) | ||
59 | ICON_INDI_REFRESH_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/refresh.png')) | ||
60 | ICON_INDI_ALERT_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/alert.png')) | ||
61 | ICON_INDI_TICK_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/tick.png')) | ||
62 | ICON_INDI_INFO_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/info.png')) | ||
63 | |||
64 | class HobViewTable (gtk.VBox): | ||
65 | """ | ||
66 | A VBox to contain the table for different recipe views and package view | ||
67 | """ | ||
68 | __gsignals__ = { | ||
69 | "toggled" : (gobject.SIGNAL_RUN_LAST, | ||
70 | gobject.TYPE_NONE, | ||
71 | (gobject.TYPE_PYOBJECT, | ||
72 | gobject.TYPE_STRING, | ||
73 | gobject.TYPE_INT, | ||
74 | gobject.TYPE_PYOBJECT,)), | ||
75 | "row-activated" : (gobject.SIGNAL_RUN_LAST, | ||
76 | gobject.TYPE_NONE, | ||
77 | (gobject.TYPE_PYOBJECT, | ||
78 | gobject.TYPE_PYOBJECT,)), | ||
79 | "cell-fadeinout-stopped" : (gobject.SIGNAL_RUN_LAST, | ||
80 | gobject.TYPE_NONE, | ||
81 | (gobject.TYPE_PYOBJECT, | ||
82 | gobject.TYPE_PYOBJECT, | ||
83 | gobject.TYPE_PYOBJECT,)), | ||
84 | } | ||
85 | |||
86 | def __init__(self, columns, name): | ||
87 | gtk.VBox.__init__(self, False, 6) | ||
88 | self.table_tree = gtk.TreeView() | ||
89 | self.table_tree.set_headers_visible(True) | ||
90 | self.table_tree.set_headers_clickable(True) | ||
91 | self.table_tree.set_rules_hint(True) | ||
92 | self.table_tree.set_enable_tree_lines(True) | ||
93 | self.table_tree.get_selection().set_mode(gtk.SELECTION_SINGLE) | ||
94 | self.toggle_columns = [] | ||
95 | self.table_tree.connect("row-activated", self.row_activated_cb) | ||
96 | self.top_bar = None | ||
97 | self.tab_name = name | ||
98 | |||
99 | for i, column in enumerate(columns): | ||
100 | col_name = column['col_name'] | ||
101 | col = gtk.TreeViewColumn(col_name) | ||
102 | col.set_clickable(True) | ||
103 | col.set_resizable(True) | ||
104 | if self.tab_name.startswith('Included'): | ||
105 | if col_name!='Included': | ||
106 | col.set_sort_column_id(column['col_id']) | ||
107 | else: | ||
108 | col.set_sort_column_id(column['col_id']) | ||
109 | if 'col_min' in column.keys(): | ||
110 | col.set_min_width(column['col_min']) | ||
111 | if 'col_max' in column.keys(): | ||
112 | col.set_max_width(column['col_max']) | ||
113 | if 'expand' in column.keys(): | ||
114 | col.set_expand(True) | ||
115 | self.table_tree.append_column(col) | ||
116 | |||
117 | if (not 'col_style' in column.keys()) or column['col_style'] == 'text': | ||
118 | cell = gtk.CellRendererText() | ||
119 | col.pack_start(cell, True) | ||
120 | col.set_attributes(cell, text=column['col_id']) | ||
121 | if 'col_t_id' in column.keys(): | ||
122 | col.add_attribute(cell, 'font', column['col_t_id']) | ||
123 | elif column['col_style'] == 'check toggle': | ||
124 | cell = HobCellRendererToggle() | ||
125 | cell.set_property('activatable', True) | ||
126 | cell.connect("toggled", self.toggled_cb, i, self.table_tree) | ||
127 | cell.connect_render_state_changed(self.stop_cell_fadeinout_cb, self.table_tree) | ||
128 | self.toggle_id = i | ||
129 | col.pack_end(cell, True) | ||
130 | col.set_attributes(cell, active=column['col_id']) | ||
131 | self.toggle_columns.append(col_name) | ||
132 | if 'col_group' in column.keys(): | ||
133 | col.set_cell_data_func(cell, self.set_group_number_cb) | ||
134 | elif column['col_style'] == 'radio toggle': | ||
135 | cell = gtk.CellRendererToggle() | ||
136 | cell.set_property('activatable', True) | ||
137 | cell.set_radio(True) | ||
138 | cell.connect("toggled", self.toggled_cb, i, self.table_tree) | ||
139 | self.toggle_id = i | ||
140 | col.pack_end(cell, True) | ||
141 | col.set_attributes(cell, active=column['col_id']) | ||
142 | self.toggle_columns.append(col_name) | ||
143 | elif column['col_style'] == 'binb': | ||
144 | cell = gtk.CellRendererText() | ||
145 | col.pack_start(cell, True) | ||
146 | col.set_cell_data_func(cell, self.display_binb_cb, column['col_id']) | ||
147 | if 'col_t_id' in column.keys(): | ||
148 | col.add_attribute(cell, 'font', column['col_t_id']) | ||
149 | |||
150 | self.scroll = gtk.ScrolledWindow() | ||
151 | self.scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) | ||
152 | self.scroll.add(self.table_tree) | ||
153 | |||
154 | self.pack_end(self.scroll, True, True, 0) | ||
155 | |||
156 | def add_no_result_bar(self, entry): | ||
157 | color = HobColors.KHAKI | ||
158 | self.top_bar = gtk.EventBox() | ||
159 | self.top_bar.set_size_request(-1, 70) | ||
160 | self.top_bar.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(color)) | ||
161 | self.top_bar.set_flags(gtk.CAN_DEFAULT) | ||
162 | self.top_bar.grab_default() | ||
163 | |||
164 | no_result_tab = gtk.Table(5, 20, True) | ||
165 | self.top_bar.add(no_result_tab) | ||
166 | |||
167 | label = gtk.Label() | ||
168 | label.set_alignment(0.0, 0.5) | ||
169 | title = "No results matching your search" | ||
170 | label.set_markup("<span size='x-large'><b>%s</b></span>" % title) | ||
171 | no_result_tab.attach(label, 1, 14, 1, 4) | ||
172 | |||
173 | clear_button = HobButton("Clear search") | ||
174 | clear_button.set_tooltip_text("Clear search query") | ||
175 | clear_button.connect('clicked', self.set_search_entry_clear_cb, entry) | ||
176 | no_result_tab.attach(clear_button, 16, 19, 1, 4) | ||
177 | |||
178 | self.pack_start(self.top_bar, False, True, 12) | ||
179 | self.top_bar.show_all() | ||
180 | |||
181 | def set_search_entry_clear_cb(self, button, search): | ||
182 | if search.get_editable() == True: | ||
183 | search.set_text("") | ||
184 | search.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, False) | ||
185 | search.grab_focus() | ||
186 | |||
187 | def display_binb_cb(self, col, cell, model, it, col_id): | ||
188 | binb = model.get_value(it, col_id) | ||
189 | # Just display the first item | ||
190 | if binb: | ||
191 | bin = binb.split(', ') | ||
192 | total_no = len(bin) | ||
193 | if total_no > 1 and bin[0] == "User Selected": | ||
194 | if total_no > 2: | ||
195 | present_binb = bin[1] + ' (+' + str(total_no - 1) + ')' | ||
196 | else: | ||
197 | present_binb = bin[1] | ||
198 | else: | ||
199 | if total_no > 1: | ||
200 | present_binb = bin[0] + ' (+' + str(total_no - 1) + ')' | ||
201 | else: | ||
202 | present_binb = bin[0] | ||
203 | cell.set_property('text', present_binb) | ||
204 | else: | ||
205 | cell.set_property('text', "") | ||
206 | return True | ||
207 | |||
208 | def set_model(self, tree_model): | ||
209 | self.table_tree.set_model(tree_model) | ||
210 | |||
211 | def toggle_default(self): | ||
212 | model = self.table_tree.get_model() | ||
213 | if not model: | ||
214 | return | ||
215 | iter = model.get_iter_first() | ||
216 | if iter: | ||
217 | rowpath = model.get_path(iter) | ||
218 | model[rowpath][self.toggle_id] = True | ||
219 | |||
220 | def toggled_cb(self, cell, path, columnid, tree): | ||
221 | self.emit("toggled", cell, path, columnid, tree) | ||
222 | |||
223 | def row_activated_cb(self, tree, path, view_column): | ||
224 | if not view_column.get_title() in self.toggle_columns: | ||
225 | self.emit("row-activated", tree.get_model(), path) | ||
226 | |||
227 | def stop_cell_fadeinout_cb(self, ctrl, cell, tree): | ||
228 | self.emit("cell-fadeinout-stopped", ctrl, cell, tree) | ||
229 | |||
230 | def set_group_number_cb(self, col, cell, model, iter): | ||
231 | if model and (model.iter_parent(iter) == None): | ||
232 | cell.cell_attr["number_of_children"] = model.iter_n_children(iter) | ||
233 | else: | ||
234 | cell.cell_attr["number_of_children"] = 0 | ||
235 | |||
236 | def connect_group_selection(self, cb_func): | ||
237 | self.table_tree.get_selection().connect("changed", cb_func) | ||
238 | |||
239 | """ | ||
240 | A method to calculate a softened value for the colour of widget when in the | ||
241 | provided state. | ||
242 | |||
243 | widget: the widget whose style to use | ||
244 | state: the state of the widget to use the style for | ||
245 | |||
246 | Returns a string value representing the softened colour | ||
247 | """ | ||
248 | def soften_color(widget, state=gtk.STATE_NORMAL): | ||
249 | # this colour munging routine is heavily inspired bu gdu_util_get_mix_color() | ||
250 | # from gnome-disk-utility: | ||
251 | # http://git.gnome.org/browse/gnome-disk-utility/tree/src/gdu-gtk/gdu-gtk.c?h=gnome-3-0 | ||
252 | blend = 0.7 | ||
253 | style = widget.get_style() | ||
254 | color = style.text[state] | ||
255 | color.red = color.red * blend + style.base[state].red * (1.0 - blend) | ||
256 | color.green = color.green * blend + style.base[state].green * (1.0 - blend) | ||
257 | color.blue = color.blue * blend + style.base[state].blue * (1.0 - blend) | ||
258 | return color.to_string() | ||
259 | |||
260 | class BaseHobButton(gtk.Button): | ||
261 | """ | ||
262 | A gtk.Button subclass which follows the visual design of Hob for primary | ||
263 | action buttons | ||
264 | |||
265 | label: the text to display as the button's label | ||
266 | """ | ||
267 | def __init__(self, label): | ||
268 | gtk.Button.__init__(self, label) | ||
269 | HobButton.style_button(self) | ||
270 | |||
271 | @staticmethod | ||
272 | def style_button(button): | ||
273 | style = button.get_style() | ||
274 | style = gtk.rc_get_style_by_paths(gtk.settings_get_default(), 'gtk-button', 'gtk-button', gobject.TYPE_NONE) | ||
275 | |||
276 | #FIXME button.set_flags(gtk.CAN_DEFAULT) | ||
277 | button.grab_default() | ||
278 | |||
279 | # label = "<span size='x-large'><b>%s</b></span>" % gobject.markup_escape_text(button.get_label()) | ||
280 | label = button.get_label() | ||
281 | button.set_label(label) | ||
282 | #FIXME button.child.set_use_markup(True) | ||
283 | |||
284 | class HobButton(BaseHobButton): | ||
285 | """ | ||
286 | A gtk.Button subclass which follows the visual design of Hob for primary | ||
287 | action buttons | ||
288 | |||
289 | label: the text to display as the button's label | ||
290 | """ | ||
291 | def __init__(self, label): | ||
292 | BaseHobButton.__init__(self, label) | ||
293 | HobButton.style_button(self) | ||
294 | |||
295 | class HobAltButton(BaseHobButton): | ||
296 | """ | ||
297 | A gtk.Button subclass which has no relief, and so is more discrete | ||
298 | """ | ||
299 | def __init__(self, label): | ||
300 | BaseHobButton.__init__(self, label) | ||
301 | HobAltButton.style_button(self) | ||
302 | |||
303 | """ | ||
304 | A callback for the state-changed event to ensure the text is displayed | ||
305 | differently when the widget is not sensitive | ||
306 | """ | ||
307 | @staticmethod | ||
308 | def desensitise_on_state_change_cb(button, state): | ||
309 | if not button.get_property("sensitive"): | ||
310 | HobAltButton.set_text(button, False) | ||
311 | else: | ||
312 | HobAltButton.set_text(button, True) | ||
313 | |||
314 | """ | ||
315 | Set the button label with an appropriate colour for the current widget state | ||
316 | """ | ||
317 | @staticmethod | ||
318 | def set_text(button, sensitive=True): | ||
319 | if sensitive: | ||
320 | colour = HobColors.PALE_BLUE | ||
321 | else: | ||
322 | colour = HobColors.LIGHT_GRAY | ||
323 | button.set_label("<span size='large' color='%s'><b>%s</b></span>" % (colour, gobject.markup_escape_text(button.text))) | ||
324 | button.child.set_use_markup(True) | ||
325 | |||
326 | class HobImageButton(gtk.Button): | ||
327 | """ | ||
328 | A gtk.Button with an icon and two rows of text, the second of which is | ||
329 | displayed in a blended colour. | ||
330 | |||
331 | primary_text: the main button label | ||
332 | secondary_text: optional second line of text | ||
333 | icon_path: path to the icon file to display on the button | ||
334 | """ | ||
335 | def __init__(self, primary_text, secondary_text="", icon_path="", hover_icon_path=""): | ||
336 | gtk.Button.__init__(self) | ||
337 | self.set_relief(gtk.RELIEF_NONE) | ||
338 | |||
339 | self.icon_path = icon_path | ||
340 | self.hover_icon_path = hover_icon_path | ||
341 | |||
342 | hbox = gtk.HBox(False, 10) | ||
343 | hbox.show() | ||
344 | self.add(hbox) | ||
345 | self.icon = gtk.Image() | ||
346 | self.icon.set_from_file(self.icon_path) | ||
347 | self.icon.set_alignment(0.5, 0.0) | ||
348 | self.icon.show() | ||
349 | if self.hover_icon_path and len(self.hover_icon_path): | ||
350 | self.connect("enter-notify-event", self.set_hover_icon_cb) | ||
351 | self.connect("leave-notify-event", self.set_icon_cb) | ||
352 | hbox.pack_start(self.icon, False, False, 0) | ||
353 | label = gtk.Label() | ||
354 | label.set_alignment(0.0, 0.5) | ||
355 | colour = soften_color(label) | ||
356 | mark = "<span size='x-large'>%s</span>\n<span size='medium' fgcolor='%s' weight='ultralight'>%s</span>" % (primary_text, colour, secondary_text) | ||
357 | label.set_markup(mark) | ||
358 | label.show() | ||
359 | hbox.pack_start(label, True, True, 0) | ||
360 | |||
361 | def set_hover_icon_cb(self, widget, event): | ||
362 | self.icon.set_from_file(self.hover_icon_path) | ||
363 | |||
364 | def set_icon_cb(self, widget, event): | ||
365 | self.icon.set_from_file(self.icon_path) | ||
366 | |||
367 | class HobInfoButton(gtk.EventBox): | ||
368 | """ | ||
369 | This class implements a button-like widget per the Hob visual and UX designs | ||
370 | which will display a persistent tooltip, with the contents of tip_markup, when | ||
371 | clicked. | ||
372 | |||
373 | tip_markup: the Pango Markup to be displayed in the persistent tooltip | ||
374 | """ | ||
375 | def __init__(self, tip_markup, parent=None): | ||
376 | gtk.EventBox.__init__(self) | ||
377 | self.image = gtk.Image() | ||
378 | self.image.set_from_file( | ||
379 | hic.ICON_INFO_DISPLAY_FILE) | ||
380 | self.image.show() | ||
381 | self.add(self.image) | ||
382 | self.tip_markup = tip_markup | ||
383 | self.my_parent = parent | ||
384 | |||
385 | self.set_events(gtk.gdk.BUTTON_RELEASE | | ||
386 | gtk.gdk.ENTER_NOTIFY_MASK | | ||
387 | gtk.gdk.LEAVE_NOTIFY_MASK) | ||
388 | |||
389 | self.connect("button-release-event", self.button_release_cb) | ||
390 | self.connect("enter-notify-event", self.mouse_in_cb) | ||
391 | self.connect("leave-notify-event", self.mouse_out_cb) | ||
392 | |||
393 | """ | ||
394 | When the mouse click is released emulate a button-click and show the associated | ||
395 | PersistentTooltip | ||
396 | """ | ||
397 | def button_release_cb(self, widget, event): | ||
398 | from bb.ui.crumbs.hig.propertydialog import PropertyDialog | ||
399 | self.dialog = PropertyDialog(title = '', | ||
400 | parent = self.my_parent, | ||
401 | information = self.tip_markup, | ||
402 | flags = gtk.DIALOG_DESTROY_WITH_PARENT) | ||
403 | #FMXIE | gtk.DIALOG_NO_SEPARATOR | ||
404 | |||
405 | button = self.dialog.add_button("Close", gtk.RESPONSE_CANCEL) | ||
406 | HobAltButton.style_button(button) | ||
407 | button.connect("clicked", lambda w: self.dialog.destroy()) | ||
408 | self.dialog.show_all() | ||
409 | self.dialog.run() | ||
410 | |||
411 | """ | ||
412 | Change to the prelight image when the mouse enters the widget | ||
413 | """ | ||
414 | def mouse_in_cb(self, widget, event): | ||
415 | self.image.set_from_file(hic.ICON_INFO_HOVER_FILE) | ||
416 | |||
417 | """ | ||
418 | Change to the stock image when the mouse enters the widget | ||
419 | """ | ||
420 | def mouse_out_cb(self, widget, event): | ||
421 | self.image.set_from_file(hic.ICON_INFO_DISPLAY_FILE) | ||
422 | |||
423 | class HobIndicator(gtk.DrawingArea): | ||
424 | def __init__(self, count): | ||
425 | gtk.DrawingArea.__init__(self) | ||
426 | # Set no window for transparent background | ||
427 | self.set_has_window(False) | ||
428 | self.set_size_request(38,38) | ||
429 | # We need to pass through button clicks | ||
430 | self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK) | ||
431 | |||
432 | self.connect('expose-event', self.expose) | ||
433 | |||
434 | self.count = count | ||
435 | self.color = HobColors.GRAY | ||
436 | |||
437 | def expose(self, widget, event): | ||
438 | if self.count and self.count > 0: | ||
439 | ctx = widget.window.cairo_create() | ||
440 | |||
441 | x, y, w, h = self.allocation | ||
442 | |||
443 | ctx.set_operator(cairo.OPERATOR_OVER) | ||
444 | ctx.set_source_color(gtk.gdk.color_parse(self.color)) | ||
445 | ctx.translate(w/2, h/2) | ||
446 | ctx.arc(x, y, min(w,h)/2 - 2, 0, 2*math.pi) | ||
447 | ctx.fill_preserve() | ||
448 | |||
449 | layout = self.create_pango_layout(str(self.count)) | ||
450 | textw, texth = layout.get_pixel_size() | ||
451 | x = (w/2)-(textw/2) + x | ||
452 | y = (h/2) - (texth/2) + y | ||
453 | ctx.move_to(x, y) | ||
454 | self.window.draw_layout(self.style.light_gc[gtk.STATE_NORMAL], int(x), int(y), layout) | ||
455 | |||
456 | def set_count(self, count): | ||
457 | self.count = count | ||
458 | |||
459 | def set_active(self, active): | ||
460 | if active: | ||
461 | self.color = HobColors.DEEP_RED | ||
462 | else: | ||
463 | self.color = HobColors.GRAY | ||
464 | |||
465 | class HobTabLabel(gtk.HBox): | ||
466 | def __init__(self, text, count=0): | ||
467 | gtk.HBox.__init__(self, False, 0) | ||
468 | self.indicator = HobIndicator(count) | ||
469 | self.indicator.show() | ||
470 | self.pack_end(self.indicator, False, False) | ||
471 | self.lbl = gtk.Label(text) | ||
472 | self.lbl.set_alignment(0.0, 0.5) | ||
473 | self.lbl.show() | ||
474 | self.pack_end(self.lbl, True, True, 6) | ||
475 | |||
476 | def set_count(self, count): | ||
477 | self.indicator.set_count(count) | ||
478 | |||
479 | def set_active(self, active=True): | ||
480 | self.indicator.set_active(active) | ||
481 | |||
482 | class HobNotebook(gtk.Notebook): | ||
483 | def __init__(self): | ||
484 | gtk.Notebook.__init__(self) | ||
485 | self.set_property('homogeneous', True) | ||
486 | |||
487 | self.pages = [] | ||
488 | |||
489 | self.search = None | ||
490 | self.search_focus = False | ||
491 | self.page_changed = False | ||
492 | |||
493 | self.connect("switch-page", self.page_changed_cb) | ||
494 | |||
495 | self.show_all() | ||
496 | |||
497 | def page_changed_cb(self, nb, page, page_num): | ||
498 | for p, lbl in enumerate(self.pages): | ||
499 | if p == page_num: | ||
500 | lbl.set_active() | ||
501 | else: | ||
502 | lbl.set_active(False) | ||
503 | |||
504 | if self.search: | ||
505 | self.page_changed = True | ||
506 | self.reset_entry(self.search, page_num) | ||
507 | |||
508 | def append_page(self, child, tab_label, tab_tooltip=None): | ||
509 | label = HobTabLabel(tab_label) | ||
510 | if tab_tooltip: | ||
511 | label.set_tooltip_text(tab_tooltip) | ||
512 | label.set_active(False) | ||
513 | self.pages.append(label) | ||
514 | gtk.Notebook.append_page(self, child, label) | ||
515 | |||
516 | def set_entry(self, names, tips): | ||
517 | self.search = gtk.Entry() | ||
518 | self.search_names = names | ||
519 | self.search_tips = tips | ||
520 | style = self.search.get_style() | ||
521 | style.text[gtk.STATE_NORMAL] = self.get_colormap().alloc_color(HobColors.GRAY, False, False) | ||
522 | self.search.set_style(style) | ||
523 | self.search.set_text(names[0]) | ||
524 | self.search.set_tooltip_text(self.search_tips[0]) | ||
525 | self.search.props.has_tooltip = True | ||
526 | |||
527 | self.search.set_editable(False) | ||
528 | self.search.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, gtk.STOCK_CLEAR) | ||
529 | self.search.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, False) | ||
530 | self.search.connect("icon-release", self.set_search_entry_clear_cb) | ||
531 | self.search.set_width_chars(30) | ||
532 | self.search.show() | ||
533 | |||
534 | self.search.connect("focus-in-event", self.set_search_entry_editable_cb) | ||
535 | self.search.connect("focus-out-event", self.set_search_entry_reset_cb) | ||
536 | self.set_action_widget(self.search, gtk.PACK_END) | ||
537 | |||
538 | def show_indicator_icon(self, title, number): | ||
539 | for child in self.pages: | ||
540 | if child.lbl.get_label() == title: | ||
541 | child.set_count(number) | ||
542 | |||
543 | def hide_indicator_icon(self, title): | ||
544 | for child in self.pages: | ||
545 | if child.lbl.get_label() == title: | ||
546 | child.set_count(0) | ||
547 | |||
548 | def set_search_entry_editable_cb(self, search, event): | ||
549 | self.search_focus = True | ||
550 | search.set_editable(True) | ||
551 | text = search.get_text() | ||
552 | if text in self.search_names: | ||
553 | search.set_text("") | ||
554 | style = self.search.get_style() | ||
555 | style.text[gtk.STATE_NORMAL] = self.get_colormap().alloc_color(HobColors.BLACK, False, False) | ||
556 | search.set_style(style) | ||
557 | |||
558 | def set_search_entry_reset_cb(self, search, event): | ||
559 | page_num = self.get_current_page() | ||
560 | text = search.get_text() | ||
561 | if not text: | ||
562 | self.reset_entry(search, page_num) | ||
563 | |||
564 | def reset_entry(self, entry, page_num): | ||
565 | style = entry.get_style() | ||
566 | style.text[gtk.STATE_NORMAL] = self.get_colormap().alloc_color(HobColors.GRAY, False, False) | ||
567 | entry.set_style(style) | ||
568 | entry.set_text(self.search_names[page_num]) | ||
569 | entry.set_tooltip_text(self.search_tips[page_num]) | ||
570 | entry.set_editable(False) | ||
571 | entry.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, False) | ||
572 | |||
573 | def set_search_entry_clear_cb(self, search, icon_pos, event): | ||
574 | if search.get_editable() == True: | ||
575 | search.set_text("") | ||
576 | search.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, False) | ||
577 | search.grab_focus() | ||
578 | |||
579 | def set_page(self, title): | ||
580 | for child in self.pages: | ||
581 | if child.lbl.get_label() == title: | ||
582 | child.grab_focus() | ||
583 | self.set_current_page(self.pages.index(child)) | ||
584 | return | ||
585 | |||
586 | class HobWarpCellRendererText(gtk.CellRendererText): | ||
587 | def __init__(self, col_number): | ||
588 | gtk.CellRendererText.__init__(self) | ||
589 | self.set_property("wrap-mode", pango.WRAP_WORD_CHAR) | ||
590 | self.set_property("wrap-width", 300) # default value wrap width is 300 | ||
591 | self.col_n = col_number | ||
592 | |||
593 | def do_render(self, window, widget, background_area, cell_area, expose_area, flags): | ||
594 | if widget: | ||
595 | self.props.wrap_width = self.get_resized_wrap_width(widget, widget.get_column(self.col_n)) | ||
596 | return gtk.CellRendererText.do_render(self, window, widget, background_area, cell_area, expose_area, flags) | ||
597 | |||
598 | def get_resized_wrap_width(self, treeview, column): | ||
599 | otherCols = [] | ||
600 | for col in treeview.get_columns(): | ||
601 | if col != column: | ||
602 | otherCols.append(col) | ||
603 | adjwidth = treeview.allocation.width - sum(c.get_width() for c in otherCols) | ||
604 | adjwidth -= treeview.style_get_property("horizontal-separator") * 4 | ||
605 | if self.props.wrap_width == adjwidth or adjwidth <= 0: | ||
606 | adjwidth = self.props.wrap_width | ||
607 | return adjwidth | ||
608 | |||
609 | gobject.type_register(HobWarpCellRendererText) | ||
610 | |||
611 | class HobIconChecker(hic): | ||
612 | def set_hob_icon_to_stock_icon(self, file_path, stock_id=""): | ||
613 | try: | ||
614 | pixbuf = gtk.gdk.pixbuf_new_from_file(file_path) | ||
615 | except Exception as e: | ||
616 | return None | ||
617 | |||
618 | if stock_id and (gtk.icon_factory_lookup_default(stock_id) == None): | ||
619 | icon_factory = gtk.IconFactory() | ||
620 | icon_factory.add_default() | ||
621 | icon_factory.add(stock_id, gtk.IconSet(pixbuf)) | ||
622 | gtk.stock_add([(stock_id, '_label', 0, 0, '')]) | ||
623 | |||
624 | return icon_factory.lookup(stock_id) | ||
625 | |||
626 | return None | ||
627 | |||
628 | """ | ||
629 | For make hob icon consistently by request, and avoid icon view diff by system or gtk version, we use some 'hob icon' to replace the 'gtk icon'. | ||
630 | this function check the stock_id and make hob_id to replaced the gtk_id then return it or "" | ||
631 | """ | ||
632 | def check_stock_icon(self, stock_name=""): | ||
633 | HOB_CHECK_STOCK_NAME = { | ||
634 | ('hic-dialog-info', 'gtk-dialog-info', 'dialog-info') : self.ICON_INDI_INFO_FILE, | ||
635 | ('hic-ok', 'gtk-ok', 'ok') : self.ICON_INDI_TICK_FILE, | ||
636 | ('hic-dialog-error', 'gtk-dialog-error', 'dialog-error') : self.ICON_INDI_ERROR_FILE, | ||
637 | ('hic-dialog-warning', 'gtk-dialog-warning', 'dialog-warning') : self.ICON_INDI_ALERT_FILE, | ||
638 | ('hic-task-refresh', 'gtk-execute', 'execute') : self.ICON_INDI_REFRESH_FILE, | ||
639 | } | ||
640 | valid_stock_id = stock_name | ||
641 | if stock_name: | ||
642 | for names, path in HOB_CHECK_STOCK_NAME.iteritems(): | ||
643 | if stock_name in names: | ||
644 | valid_stock_id = names[0] | ||
645 | if not gtk.icon_factory_lookup_default(valid_stock_id): | ||
646 | self.set_hob_icon_to_stock_icon(path, valid_stock_id) | ||
647 | |||
648 | return valid_stock_id | ||
649 | |||
650 | class HobCellRendererController(gobject.GObject): | ||
651 | (MODE_CYCLE_RUNNING, MODE_ONE_SHORT) = range(2) | ||
652 | __gsignals__ = { | ||
653 | "run-timer-stopped" : (gobject.SIGNAL_RUN_LAST, | ||
654 | gobject.TYPE_NONE, | ||
655 | ()), | ||
656 | } | ||
657 | def __init__(self, runningmode=MODE_CYCLE_RUNNING, is_draw_row=False): | ||
658 | gobject.GObject.__init__(self) | ||
659 | self.timeout_id = None | ||
660 | self.current_angle_pos = 0.0 | ||
661 | self.step_angle = 0.0 | ||
662 | self.tree_headers_height = 0 | ||
663 | self.running_cell_areas = [] | ||
664 | self.running_mode = runningmode | ||
665 | self.is_queue_draw_row_area = is_draw_row | ||
666 | self.force_stop_enable = False | ||
667 | |||
668 | def is_active(self): | ||
669 | if self.timeout_id: | ||
670 | return True | ||
671 | else: | ||
672 | return False | ||
673 | |||
674 | def reset_run(self): | ||
675 | self.force_stop() | ||
676 | self.running_cell_areas = [] | ||
677 | self.current_angle_pos = 0.0 | ||
678 | self.step_angle = 0.0 | ||
679 | |||
680 | ''' time_iterval: (1~1000)ms, which will be as the basic interval count for timer | ||
681 | init_usrdata: the current data which related the progress-bar will be at | ||
682 | min_usrdata: the range of min of user data | ||
683 | max_usrdata: the range of max of user data | ||
684 | step: each step which you want to progress | ||
685 | Note: the init_usrdata should in the range of from min to max, and max should > min | ||
686 | step should < (max - min) | ||
687 | ''' | ||
688 | def start_run(self, time_iterval, init_usrdata, min_usrdata, max_usrdata, step, tree): | ||
689 | if (not time_iterval) or (not max_usrdata): | ||
690 | return | ||
691 | usr_range = (max_usrdata - min_usrdata) * 1.0 | ||
692 | self.current_angle_pos = (init_usrdata * 1.0) / usr_range | ||
693 | self.step_angle = (step * 1) / usr_range | ||
694 | self.timeout_id = gobject.timeout_add(int(time_iterval), | ||
695 | self.make_image_on_progressing_cb, tree) | ||
696 | self.tree_headers_height = self.get_treeview_headers_height(tree) | ||
697 | self.force_stop_enable = False | ||
698 | |||
699 | def force_stop(self): | ||
700 | self.emit("run-timer-stopped") | ||
701 | self.force_stop_enable = True | ||
702 | if self.timeout_id: | ||
703 | if gobject.source_remove(self.timeout_id): | ||
704 | self.timeout_id = None | ||
705 | |||
706 | def on_draw_pixbuf_cb(self, pixbuf, cr, x, y, img_width, img_height, do_refresh=True): | ||
707 | if pixbuf: | ||
708 | r = max(img_width/2, img_height/2) | ||
709 | cr.translate(x + r, y + r) | ||
710 | if do_refresh: | ||
711 | cr.rotate(2 * math.pi * self.current_angle_pos) | ||
712 | |||
713 | cr.set_source_pixbuf(pixbuf, -img_width/2, -img_height/2) | ||
714 | cr.paint() | ||
715 | |||
716 | def on_draw_fadeinout_cb(self, cr, color, x, y, width, height, do_fadeout=True): | ||
717 | if do_fadeout: | ||
718 | alpha = self.current_angle_pos * 0.8 | ||
719 | else: | ||
720 | alpha = (1.0 - self.current_angle_pos) * 0.8 | ||
721 | |||
722 | cr.set_source_rgba(color.red, color.green, color.blue, alpha) | ||
723 | cr.rectangle(x, y, width, height) | ||
724 | cr.fill() | ||
725 | |||
726 | def get_treeview_headers_height(self, tree): | ||
727 | if tree and (tree.get_property("headers-visible") == True): | ||
728 | height = tree.get_allocation().height - tree.get_bin_window().get_size()[1] | ||
729 | return height | ||
730 | |||
731 | return 0 | ||
732 | |||
733 | def make_image_on_progressing_cb(self, tree): | ||
734 | self.current_angle_pos += self.step_angle | ||
735 | if self.running_mode == self.MODE_CYCLE_RUNNING: | ||
736 | if (self.current_angle_pos >= 1): | ||
737 | self.current_angle_pos = 0 | ||
738 | else: | ||
739 | if self.current_angle_pos > 1: | ||
740 | self.force_stop() | ||
741 | return False | ||
742 | |||
743 | if self.is_queue_draw_row_area: | ||
744 | for path in self.running_cell_areas: | ||
745 | rect = tree.get_cell_area(path, tree.get_column(0)) | ||
746 | row_x, _, row_width, _ = tree.get_visible_rect() | ||
747 | tree.queue_draw_area(row_x, rect.y + self.tree_headers_height, row_width, rect.height) | ||
748 | else: | ||
749 | for rect in self.running_cell_areas: | ||
750 | tree.queue_draw_area(rect.x, rect.y + self.tree_headers_height, rect.width, rect.height) | ||
751 | |||
752 | return (not self.force_stop_enable) | ||
753 | |||
754 | def append_running_cell_area(self, cell_area): | ||
755 | if cell_area and (cell_area not in self.running_cell_areas): | ||
756 | self.running_cell_areas.append(cell_area) | ||
757 | |||
758 | def remove_running_cell_area(self, cell_area): | ||
759 | if cell_area in self.running_cell_areas: | ||
760 | self.running_cell_areas.remove(cell_area) | ||
761 | if not self.running_cell_areas: | ||
762 | self.reset_run() | ||
763 | |||
764 | gobject.type_register(HobCellRendererController) | ||
765 | |||
766 | class HobCellRendererPixbuf(gtk.CellRendererPixbuf): | ||
767 | def __init__(self): | ||
768 | gtk.CellRendererPixbuf.__init__(self) | ||
769 | self.control = HobCellRendererController() | ||
770 | # add icon checker for make the gtk-icon transfer to hob-icon | ||
771 | self.checker = HobIconChecker() | ||
772 | self.set_property("stock-size", gtk.ICON_SIZE_DND) | ||
773 | |||
774 | def get_pixbuf_from_stock_icon(self, widget, stock_id="", size=gtk.ICON_SIZE_DIALOG): | ||
775 | if widget and stock_id and gtk.icon_factory_lookup_default(stock_id): | ||
776 | return widget.render_icon(stock_id, size) | ||
777 | |||
778 | return None | ||
779 | |||
780 | def set_icon_name_to_id(self, new_name): | ||
781 | if new_name and type(new_name) == str: | ||
782 | # check the name is need to transfer to hob icon or not | ||
783 | name = self.checker.check_stock_icon(new_name) | ||
784 | if name.startswith("hic") or name.startswith("gtk"): | ||
785 | stock_id = name | ||
786 | else: | ||
787 | stock_id = 'gtk-' + name | ||
788 | |||
789 | return stock_id | ||
790 | |||
791 | ''' render cell exactly, "icon-name" is priority | ||
792 | if use the 'hic-task-refresh' will make the pix animation | ||
793 | if 'pix' will change the pixbuf for it from the pixbuf or image. | ||
794 | ''' | ||
795 | def do_render(self, window, tree, background_area,cell_area, expose_area, flags): | ||
796 | if (not self.control) or (not tree): | ||
797 | return | ||
798 | |||
799 | x, y, w, h = self.on_get_size(tree, cell_area) | ||
800 | x += cell_area.x | ||
801 | y += cell_area.y | ||
802 | w -= 2 * self.get_property("xpad") | ||
803 | h -= 2 * self.get_property("ypad") | ||
804 | |||
805 | stock_id = "" | ||
806 | if self.props.icon_name: | ||
807 | stock_id = self.set_icon_name_to_id(self.props.icon_name) | ||
808 | elif self.props.stock_id: | ||
809 | stock_id = self.props.stock_id | ||
810 | elif self.props.pixbuf: | ||
811 | pix = self.props.pixbuf | ||
812 | else: | ||
813 | return | ||
814 | |||
815 | if stock_id: | ||
816 | pix = self.get_pixbuf_from_stock_icon(tree, stock_id, self.props.stock_size) | ||
817 | if stock_id == 'hic-task-refresh': | ||
818 | self.control.append_running_cell_area(cell_area) | ||
819 | if self.control.is_active(): | ||
820 | self.control.on_draw_pixbuf_cb(pix, window.cairo_create(), x, y, w, h, True) | ||
821 | else: | ||
822 | self.control.start_run(200, 0, 0, 1000, 150, tree) | ||
823 | else: | ||
824 | self.control.remove_running_cell_area(cell_area) | ||
825 | self.control.on_draw_pixbuf_cb(pix, window.cairo_create(), x, y, w, h, False) | ||
826 | |||
827 | def on_get_size(self, widget, cell_area): | ||
828 | if self.props.icon_name or self.props.pixbuf or self.props.stock_id: | ||
829 | w, h = gtk.icon_size_lookup(self.props.stock_size) | ||
830 | calc_width = self.get_property("xpad") * 2 + w | ||
831 | calc_height = self.get_property("ypad") * 2 + h | ||
832 | x_offset = 0 | ||
833 | y_offset = 0 | ||
834 | if cell_area and w > 0 and h > 0: | ||
835 | x_offset = self.get_property("xalign") * (cell_area.width - calc_width - self.get_property("xpad")) | ||
836 | y_offset = self.get_property("yalign") * (cell_area.height - calc_height - self.get_property("ypad")) | ||
837 | |||
838 | return x_offset, y_offset, w, h | ||
839 | |||
840 | return 0, 0, 0, 0 | ||
841 | |||
842 | gobject.type_register(HobCellRendererPixbuf) | ||
843 | |||
844 | class HobCellRendererToggle(gtk.CellRendererToggle): | ||
845 | def __init__(self): | ||
846 | gtk.CellRendererToggle.__init__(self) | ||
847 | self.ctrl = HobCellRendererController(is_draw_row=True) | ||
848 | self.ctrl.running_mode = self.ctrl.MODE_ONE_SHORT | ||
849 | self.cell_attr = {"fadeout": False, "number_of_children": 0} | ||
850 | |||
851 | def do_render(self, window, widget, background_area, cell_area, expose_area, flags): | ||
852 | if (not self.ctrl) or (not widget): | ||
853 | return | ||
854 | |||
855 | if flags & gtk.CELL_RENDERER_SELECTED: | ||
856 | state = gtk.STATE_SELECTED | ||
857 | else: | ||
858 | state = gtk.STATE_NORMAL | ||
859 | |||
860 | if self.ctrl.is_active(): | ||
861 | path = widget.get_path_at_pos(cell_area.x + cell_area.width/2, cell_area.y + cell_area.height/2) | ||
862 | # sometimes the parameters of cell_area will be a negative number,such as pull up down the scroll bar | ||
863 | # it's over the tree container range, so the path will be bad | ||
864 | if not path: return | ||
865 | path = path[0] | ||
866 | if path in self.ctrl.running_cell_areas: | ||
867 | cr = window.cairo_create() | ||
868 | color = widget.get_style().base[state] | ||
869 | |||
870 | row_x, _, row_width, _ = widget.get_visible_rect() | ||
871 | border_y = self.get_property("ypad") | ||
872 | self.ctrl.on_draw_fadeinout_cb(cr, color, row_x, cell_area.y - border_y, row_width, \ | ||
873 | cell_area.height + border_y * 2, self.cell_attr["fadeout"]) | ||
874 | # draw number of a group | ||
875 | if self.cell_attr["number_of_children"]: | ||
876 | text = "%d pkg" % self.cell_attr["number_of_children"] | ||
877 | pangolayout = widget.create_pango_layout(text) | ||
878 | textw, texth = pangolayout.get_pixel_size() | ||
879 | x = cell_area.x + (cell_area.width/2) - (textw/2) | ||
880 | y = cell_area.y + (cell_area.height/2) - (texth/2) | ||
881 | |||
882 | widget.style.paint_layout(window, state, True, cell_area, widget, "checkbox", x, y, pangolayout) | ||
883 | else: | ||
884 | return gtk.CellRendererToggle.do_render(self, window, widget, background_area, cell_area, expose_area, flags) | ||
885 | |||
886 | '''delay: normally delay time is 1000ms | ||
887 | cell_list: whilch cells need to be render | ||
888 | ''' | ||
889 | def fadeout(self, tree, delay, cell_list=None): | ||
890 | if (delay < 200) or (not tree): | ||
891 | return | ||
892 | self.cell_attr["fadeout"] = True | ||
893 | self.ctrl.running_cell_areas = cell_list | ||
894 | self.ctrl.start_run(200, 0, 0, delay, (delay * 200 / 1000), tree) | ||
895 | |||
896 | def connect_render_state_changed(self, func, usrdata=None): | ||
897 | if not func: | ||
898 | return | ||
899 | if usrdata: | ||
900 | self.ctrl.connect("run-timer-stopped", func, self, usrdata) | ||
901 | else: | ||
902 | self.ctrl.connect("run-timer-stopped", func, self) | ||
903 | |||
904 | gobject.type_register(HobCellRendererToggle) | ||