5326847ef8
In package selection page, treeview cell data callback function not cover the 'else' case, so parent item will render the pervious value. (Bitbake rev: 98694c1dbc276cc151f393db67bfd43442da28ba) Signed-off-by: Liming An <limingx.l.an@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
1128 lines
44 KiB
Python
1128 lines
44 KiB
Python
# BitBake Graphical GTK User Interface
|
|
#
|
|
# Copyright (C) 2011-2012 Intel Corporation
|
|
#
|
|
# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
|
|
# Authored by Shane Wang <shane.wang@intel.com>
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License version 2 as
|
|
# published by the Free Software Foundation.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along
|
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
import gtk
|
|
import gobject
|
|
import os
|
|
import os.path
|
|
import sys
|
|
import pango, pangocairo
|
|
import math
|
|
|
|
from bb.ui.crumbs.hobcolor import HobColors
|
|
from bb.ui.crumbs.persistenttooltip import PersistentTooltip
|
|
|
|
class hwc:
|
|
|
|
MAIN_WIN_WIDTH = 1024
|
|
MAIN_WIN_HEIGHT = 700
|
|
|
|
class hic:
|
|
|
|
HOB_ICON_BASE_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), ("ui/icons/"))
|
|
|
|
ICON_RCIPE_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('recipe/recipe_display.png'))
|
|
ICON_RCIPE_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('recipe/recipe_hover.png'))
|
|
ICON_PACKAGES_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('packages/packages_display.png'))
|
|
ICON_PACKAGES_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('packages/packages_hover.png'))
|
|
ICON_LAYERS_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('layers/layers_display.png'))
|
|
ICON_LAYERS_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('layers/layers_hover.png'))
|
|
ICON_TEMPLATES_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('templates/templates_display.png'))
|
|
ICON_TEMPLATES_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('templates/templates_hover.png'))
|
|
ICON_IMAGES_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('images/images_display.png'))
|
|
ICON_IMAGES_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('images/images_hover.png'))
|
|
ICON_SETTINGS_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('settings/settings_display.png'))
|
|
ICON_SETTINGS_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('settings/settings_hover.png'))
|
|
ICON_INFO_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('info/info_display.png'))
|
|
ICON_INFO_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('info/info_hover.png'))
|
|
ICON_INDI_CONFIRM_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/confirmation.png'))
|
|
ICON_INDI_ERROR_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/denied.png'))
|
|
ICON_INDI_REMOVE_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/remove.png'))
|
|
ICON_INDI_REMOVE_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/remove-hover.png'))
|
|
ICON_INDI_ADD_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/add.png'))
|
|
ICON_INDI_ADD_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/add-hover.png'))
|
|
ICON_INDI_REFRESH_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/refresh.png'))
|
|
ICON_INDI_ALERT_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/alert.png'))
|
|
ICON_INDI_TICK_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/tick.png'))
|
|
ICON_INDI_INFO_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/info.png'))
|
|
|
|
class hcc:
|
|
|
|
SUPPORTED_IMAGE_TYPES = {
|
|
"jffs2" : ["jffs2"],
|
|
"sum.jffs2" : ["sum.jffs2"],
|
|
"cramfs" : ["cramfs"],
|
|
"ext2" : ["ext2"],
|
|
"ext2.gz" : ["ext2.gz"],
|
|
"ext2.bz2" : ["ext2.bz2"],
|
|
"ext3" : ["ext3"],
|
|
"ext3.gz" : ["ext3.gz"],
|
|
"ext2.lzma" : ["ext2.lzma"],
|
|
"btrfs" : ["btrfs"],
|
|
"live" : ["hddimg", "iso"],
|
|
"squashfs" : ["squashfs"],
|
|
"squashfs-lzma" : ["squashfs-lzma"],
|
|
"ubi" : ["ubi"],
|
|
"tar" : ["tar"],
|
|
"tar.gz" : ["tar.gz"],
|
|
"tar.bz2" : ["tar.bz2"],
|
|
"tar.xz" : ["tar.xz"],
|
|
"cpio" : ["cpio"],
|
|
"cpio.gz" : ["cpio.gz"],
|
|
"cpio.xz" : ["cpio.xz"],
|
|
"vmdk" : ["vmdk"],
|
|
"cpio.lzma" : ["cpio.lzma"],
|
|
}
|
|
|
|
class HobViewTable (gtk.VBox):
|
|
"""
|
|
A VBox to contain the table for different recipe views and package view
|
|
"""
|
|
__gsignals__ = {
|
|
"toggled" : (gobject.SIGNAL_RUN_LAST,
|
|
gobject.TYPE_NONE,
|
|
(gobject.TYPE_PYOBJECT,
|
|
gobject.TYPE_STRING,
|
|
gobject.TYPE_INT,
|
|
gobject.TYPE_PYOBJECT,)),
|
|
"row-activated" : (gobject.SIGNAL_RUN_LAST,
|
|
gobject.TYPE_NONE,
|
|
(gobject.TYPE_PYOBJECT,
|
|
gobject.TYPE_PYOBJECT,)),
|
|
"cell-fadeinout-stopped" : (gobject.SIGNAL_RUN_LAST,
|
|
gobject.TYPE_NONE,
|
|
(gobject.TYPE_PYOBJECT,
|
|
gobject.TYPE_PYOBJECT,
|
|
gobject.TYPE_PYOBJECT,)),
|
|
}
|
|
|
|
def __init__(self, columns):
|
|
gtk.VBox.__init__(self, False, 6)
|
|
self.table_tree = gtk.TreeView()
|
|
self.table_tree.set_headers_visible(True)
|
|
self.table_tree.set_headers_clickable(True)
|
|
self.table_tree.set_enable_search(True)
|
|
self.table_tree.set_rules_hint(True)
|
|
self.table_tree.get_selection().set_mode(gtk.SELECTION_SINGLE)
|
|
self.toggle_columns = []
|
|
self.table_tree.connect("row-activated", self.row_activated_cb)
|
|
|
|
for i, column in enumerate(columns):
|
|
col = gtk.TreeViewColumn(column['col_name'])
|
|
col.set_clickable(True)
|
|
col.set_resizable(True)
|
|
col.set_sort_column_id(column['col_id'])
|
|
if 'col_min' in column.keys():
|
|
col.set_min_width(column['col_min'])
|
|
if 'col_max' in column.keys():
|
|
col.set_max_width(column['col_max'])
|
|
if 'expand' in column.keys():
|
|
col.set_expand(True)
|
|
self.table_tree.append_column(col)
|
|
|
|
if (not 'col_style' in column.keys()) or column['col_style'] == 'text':
|
|
cell = gtk.CellRendererText()
|
|
col.pack_start(cell, True)
|
|
col.set_attributes(cell, text=column['col_id'])
|
|
elif column['col_style'] == 'check toggle':
|
|
cell = HobCellRendererToggle()
|
|
cell.set_property('activatable', True)
|
|
cell.connect("toggled", self.toggled_cb, i, self.table_tree)
|
|
cell.connect_render_state_changed(self.stop_cell_fadeinout_cb, self.table_tree)
|
|
self.toggle_id = i
|
|
col.pack_end(cell, True)
|
|
col.set_attributes(cell, active=column['col_id'])
|
|
self.toggle_columns.append(column['col_name'])
|
|
elif column['col_style'] == 'radio toggle':
|
|
cell = gtk.CellRendererToggle()
|
|
cell.set_property('activatable', True)
|
|
cell.set_radio(True)
|
|
cell.connect("toggled", self.toggled_cb, i, self.table_tree)
|
|
self.toggle_id = i
|
|
col.pack_end(cell, True)
|
|
col.set_attributes(cell, active=column['col_id'])
|
|
self.toggle_columns.append(column['col_name'])
|
|
elif column['col_style'] == 'binb':
|
|
cell = gtk.CellRendererText()
|
|
col.pack_start(cell, True)
|
|
col.set_cell_data_func(cell, self.display_binb_cb, column['col_id'])
|
|
|
|
scroll = gtk.ScrolledWindow()
|
|
scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
|
|
scroll.add(self.table_tree)
|
|
self.pack_start(scroll, True, True, 0)
|
|
|
|
def display_binb_cb(self, col, cell, model, it, col_id):
|
|
binb = model.get_value(it, col_id)
|
|
# Just display the first item
|
|
if binb:
|
|
bin = binb.split(', ')
|
|
cell.set_property('text', bin[0])
|
|
else:
|
|
cell.set_property('text', "")
|
|
return True
|
|
|
|
def set_model(self, tree_model):
|
|
self.table_tree.set_model(tree_model)
|
|
|
|
def set_search_entry(self, search_column_id, entry):
|
|
self.table_tree.set_search_column(search_column_id)
|
|
self.table_tree.set_search_entry(entry)
|
|
|
|
def toggle_default(self):
|
|
model = self.table_tree.get_model()
|
|
if not model:
|
|
return
|
|
iter = model.get_iter_first()
|
|
if iter:
|
|
rowpath = model.get_path(iter)
|
|
model[rowpath][self.toggle_id] = True
|
|
|
|
def toggled_cb(self, cell, path, columnid, tree):
|
|
self.emit("toggled", cell, path, columnid, tree)
|
|
|
|
def row_activated_cb(self, tree, path, view_column):
|
|
if not view_column.get_title() in self.toggle_columns:
|
|
self.emit("row-activated", tree.get_model(), path)
|
|
|
|
def stop_cell_fadeinout_cb(self, ctrl, cell, tree):
|
|
self.emit("cell-fadeinout-stopped", ctrl, cell, tree)
|
|
|
|
"""
|
|
A method to calculate a softened value for the colour of widget when in the
|
|
provided state.
|
|
|
|
widget: the widget whose style to use
|
|
state: the state of the widget to use the style for
|
|
|
|
Returns a string value representing the softened colour
|
|
"""
|
|
def soften_color(widget, state=gtk.STATE_NORMAL):
|
|
# this colour munging routine is heavily inspired bu gdu_util_get_mix_color()
|
|
# from gnome-disk-utility:
|
|
# http://git.gnome.org/browse/gnome-disk-utility/tree/src/gdu-gtk/gdu-gtk.c?h=gnome-3-0
|
|
blend = 0.7
|
|
style = widget.get_style()
|
|
color = style.text[state]
|
|
color.red = color.red * blend + style.base[state].red * (1.0 - blend)
|
|
color.green = color.green * blend + style.base[state].green * (1.0 - blend)
|
|
color.blue = color.blue * blend + style.base[state].blue * (1.0 - blend)
|
|
return color.to_string()
|
|
|
|
class HobButton(gtk.Button):
|
|
"""
|
|
A gtk.Button subclass which follows the visual design of Hob for primary
|
|
action buttons
|
|
|
|
label: the text to display as the button's label
|
|
"""
|
|
def __init__(self, label):
|
|
gtk.Button.__init__(self, label)
|
|
HobButton.style_button(self)
|
|
|
|
@staticmethod
|
|
def style_button(button):
|
|
style = button.get_style()
|
|
button_color = gtk.gdk.Color(HobColors.ORANGE)
|
|
button.modify_bg(gtk.STATE_NORMAL, button_color)
|
|
button.modify_bg(gtk.STATE_PRELIGHT, button_color)
|
|
button.modify_bg(gtk.STATE_SELECTED, button_color)
|
|
|
|
button.set_flags(gtk.CAN_DEFAULT)
|
|
button.grab_default()
|
|
|
|
label = "<span size='x-large'><b>%s</b></span>" % gobject.markup_escape_text(button.get_label())
|
|
button.set_label(label)
|
|
button.child.set_use_markup(True)
|
|
|
|
class HobAltButton(gtk.Button):
|
|
"""
|
|
A gtk.Button subclass which has no relief, and so is more discrete
|
|
"""
|
|
def __init__(self, label):
|
|
gtk.Button.__init__(self, label)
|
|
HobAltButton.style_button(self)
|
|
|
|
"""
|
|
A callback for the state-changed event to ensure the text is displayed
|
|
differently when the widget is not sensitive
|
|
"""
|
|
@staticmethod
|
|
def desensitise_on_state_change_cb(button, state):
|
|
if not button.get_property("sensitive"):
|
|
HobAltButton.set_text(button, False)
|
|
else:
|
|
HobAltButton.set_text(button, True)
|
|
|
|
"""
|
|
Set the button label with an appropriate colour for the current widget state
|
|
"""
|
|
@staticmethod
|
|
def set_text(button, sensitive=True):
|
|
if sensitive:
|
|
colour = HobColors.PALE_BLUE
|
|
else:
|
|
colour = HobColors.LIGHT_GRAY
|
|
button.set_label("<span size='large' color='%s'><b>%s</b></span>" % (colour, gobject.markup_escape_text(button.text)))
|
|
button.child.set_use_markup(True)
|
|
|
|
@staticmethod
|
|
def style_button(button):
|
|
button.text = button.get_label()
|
|
button.connect("state-changed", HobAltButton.desensitise_on_state_change_cb)
|
|
HobAltButton.set_text(button)
|
|
button.child.set_use_markup(True)
|
|
button.set_relief(gtk.RELIEF_NONE)
|
|
|
|
class HobImageButton(gtk.Button):
|
|
"""
|
|
A gtk.Button with an icon and two rows of text, the second of which is
|
|
displayed in a blended colour.
|
|
|
|
primary_text: the main button label
|
|
secondary_text: optional second line of text
|
|
icon_path: path to the icon file to display on the button
|
|
"""
|
|
def __init__(self, primary_text, secondary_text="", icon_path="", hover_icon_path=""):
|
|
gtk.Button.__init__(self)
|
|
self.set_relief(gtk.RELIEF_NONE)
|
|
|
|
self.icon_path = icon_path
|
|
self.hover_icon_path = hover_icon_path
|
|
|
|
hbox = gtk.HBox(False, 10)
|
|
hbox.show()
|
|
self.add(hbox)
|
|
self.icon = gtk.Image()
|
|
self.icon.set_from_file(self.icon_path)
|
|
self.icon.set_alignment(0.5, 0.0)
|
|
self.icon.show()
|
|
if self.hover_icon_path and len(self.hover_icon_path):
|
|
self.connect("enter-notify-event", self.set_hover_icon_cb)
|
|
self.connect("leave-notify-event", self.set_icon_cb)
|
|
hbox.pack_start(self.icon, False, False, 0)
|
|
label = gtk.Label()
|
|
label.set_alignment(0.0, 0.5)
|
|
colour = soften_color(label)
|
|
mark = "<span size='x-large'>%s</span>\n<span size='medium' fgcolor='%s' weight='ultralight'>%s</span>" % (primary_text, colour, secondary_text)
|
|
label.set_markup(mark)
|
|
label.show()
|
|
hbox.pack_start(label, True, True, 0)
|
|
|
|
def set_hover_icon_cb(self, widget, event):
|
|
self.icon.set_from_file(self.hover_icon_path)
|
|
|
|
def set_icon_cb(self, widget, event):
|
|
self.icon.set_from_file(self.icon_path)
|
|
|
|
class HobInfoButton(gtk.EventBox):
|
|
"""
|
|
This class implements a button-like widget per the Hob visual and UX designs
|
|
which will display a persistent tooltip, with the contents of tip_markup, when
|
|
clicked.
|
|
|
|
tip_markup: the Pango Markup to be displayed in the persistent tooltip
|
|
"""
|
|
def __init__(self, tip_markup, parent=None):
|
|
gtk.EventBox.__init__(self)
|
|
self.image = gtk.Image()
|
|
self.image.set_from_file(hic.ICON_INFO_DISPLAY_FILE)
|
|
self.image.show()
|
|
self.add(self.image)
|
|
|
|
self.set_events(gtk.gdk.BUTTON_RELEASE |
|
|
gtk.gdk.ENTER_NOTIFY_MASK |
|
|
gtk.gdk.LEAVE_NOTIFY_MASK)
|
|
|
|
self.ptip = PersistentTooltip(tip_markup)
|
|
|
|
if parent:
|
|
self.ptip.set_parent(parent)
|
|
self.ptip.set_transient_for(parent)
|
|
self.ptip.set_destroy_with_parent(True)
|
|
|
|
self.connect("button-release-event", self.button_release_cb)
|
|
self.connect("enter-notify-event", self.mouse_in_cb)
|
|
self.connect("leave-notify-event", self.mouse_out_cb)
|
|
|
|
"""
|
|
When the mouse click is released emulate a button-click and show the associated
|
|
PersistentTooltip
|
|
"""
|
|
def button_release_cb(self, widget, event):
|
|
self.ptip.show()
|
|
|
|
"""
|
|
Change to the prelight image when the mouse enters the widget
|
|
"""
|
|
def mouse_in_cb(self, widget, event):
|
|
self.image.set_from_file(hic.ICON_INFO_HOVER_FILE)
|
|
|
|
"""
|
|
Change to the stock image when the mouse enters the widget
|
|
"""
|
|
def mouse_out_cb(self, widget, event):
|
|
self.image.set_from_file(hic.ICON_INFO_DISPLAY_FILE)
|
|
|
|
class HobTabBar(gtk.DrawingArea):
|
|
__gsignals__ = {
|
|
"blank-area-changed" : (gobject.SIGNAL_RUN_LAST,
|
|
gobject.TYPE_NONE,
|
|
(gobject.TYPE_INT,
|
|
gobject.TYPE_INT,
|
|
gobject.TYPE_INT,
|
|
gobject.TYPE_INT,)),
|
|
|
|
"tab-switched" : (gobject.SIGNAL_RUN_LAST,
|
|
gobject.TYPE_NONE,
|
|
(gobject.TYPE_INT,)),
|
|
}
|
|
|
|
def __init__(self):
|
|
gtk.DrawingArea.__init__(self)
|
|
self.children = []
|
|
|
|
self.tab_width = 140
|
|
self.tab_height = 52
|
|
self.tab_x = 10
|
|
self.tab_y = 0
|
|
|
|
self.width = 500
|
|
self.height = 53
|
|
self.tab_w_ratio = 140 * 1.0/500
|
|
self.tab_h_ratio = 52 * 1.0/53
|
|
self.set_size_request(self.width, self.height)
|
|
|
|
self.current_child = None
|
|
self.font = self.get_style().font_desc
|
|
self.font.set_size(pango.SCALE * 13)
|
|
self.update_children_text_layout_and_bg_color()
|
|
|
|
self.blank_rectangle = None
|
|
self.tab_pressed = False
|
|
|
|
self.set_property('can-focus', True)
|
|
self.set_events(gtk.gdk.EXPOSURE_MASK | gtk.gdk.POINTER_MOTION_MASK |
|
|
gtk.gdk.BUTTON1_MOTION_MASK | gtk.gdk.BUTTON_PRESS_MASK |
|
|
gtk.gdk.BUTTON_RELEASE_MASK)
|
|
|
|
self.connect("expose-event", self.on_draw)
|
|
self.connect("button-press-event", self.button_pressed_cb)
|
|
self.connect("button-release-event", self.button_released_cb)
|
|
self.connect("query-tooltip", self.query_tooltip_cb)
|
|
self.show_all()
|
|
|
|
def button_released_cb(self, widget, event):
|
|
self.tab_pressed = False
|
|
self.queue_draw()
|
|
|
|
def button_pressed_cb(self, widget, event):
|
|
if event.type == gtk.gdk._2BUTTON_PRESS:
|
|
return
|
|
|
|
result = False
|
|
if self.is_focus() or event.type == gtk.gdk.BUTTON_PRESS:
|
|
x, y = event.get_coords()
|
|
# check which tab be clicked
|
|
for child in self.children:
|
|
if (child["x"] < x) and (x < child["x"] + self.tab_width) \
|
|
and (child["y"] < y) and (y < child["y"] + self.tab_height):
|
|
self.current_child = child
|
|
result = True
|
|
self.grab_focus()
|
|
break
|
|
|
|
# check the blank area is focus in or not
|
|
if (self.blank_rectangle) and (self.blank_rectangle.x > 0) and (self.blank_rectangle.y > 0):
|
|
if (self.blank_rectangle.x < x) and (x < self.blank_rectangle.x + self.blank_rectangle.width) \
|
|
and (self.blank_rectangle.y < y) and (y < self.blank_rectangle.y + self.blank_rectangle.height):
|
|
self.grab_focus()
|
|
|
|
if result == True:
|
|
page = self.current_child["toggled_page"]
|
|
self.emit("tab-switched", page)
|
|
self.tab_pressed = True
|
|
self.queue_draw()
|
|
|
|
def update_children_size(self):
|
|
# calculate the size of tabs
|
|
self.tab_width = int(self.width * self.tab_w_ratio)
|
|
self.tab_height = int(self.height * self.tab_h_ratio)
|
|
for i, child in enumerate(self.children):
|
|
child["x"] = self.tab_x + i * self.tab_width
|
|
child["y"] = self.tab_y
|
|
|
|
if self.blank_rectangle:
|
|
self.resize_blank_rectangle()
|
|
|
|
def resize_blank_rectangle(self):
|
|
width = self.width - self.tab_width * len(self.children) - self.tab_x
|
|
x = self.tab_x + self.tab_width * len(self.children)
|
|
hpadding = vpadding = 5
|
|
self.blank_rectangle = self.set_blank_size(x + hpadding, self.tab_y + vpadding,
|
|
width - 2 * hpadding, self.tab_height - 2 * vpadding)
|
|
|
|
def update_children_text_layout_and_bg_color(self):
|
|
style = self.get_style().copy()
|
|
color = style.base[gtk.STATE_NORMAL]
|
|
for child in self.children:
|
|
pangolayout = self.create_pango_layout(child["title"])
|
|
pangolayout.set_font_description(self.font)
|
|
child["title_layout"] = pangolayout
|
|
child["r"] = color.red
|
|
child["g"] = color.green
|
|
child["b"] = color.blue
|
|
|
|
def append_tab_child(self, title, page, tooltip=""):
|
|
num = len(self.children) + 1
|
|
self.tab_width = self.tab_width * len(self.children) / num
|
|
|
|
i = 0
|
|
for i, child in enumerate(self.children):
|
|
child["x"] = self.tab_x + i * self.tab_width
|
|
i += 1
|
|
|
|
x = self.tab_x + i * self.tab_width
|
|
y = self.tab_y
|
|
pangolayout = self.create_pango_layout(title)
|
|
pangolayout.set_font_description(self.font)
|
|
color = self.style.base[gtk.STATE_NORMAL]
|
|
new_one = {
|
|
"x" : x,
|
|
"y" : y,
|
|
"r" : color.red,
|
|
"g" : color.green,
|
|
"b" : color.blue,
|
|
"title_layout" : pangolayout,
|
|
"toggled_page" : page,
|
|
"title" : title,
|
|
"indicator_show" : False,
|
|
"indicator_number" : 0,
|
|
"tooltip_markup" : tooltip,
|
|
}
|
|
self.children.append(new_one)
|
|
if tooltip and (not self.props.has_tooltip):
|
|
self.props.has_tooltip = True
|
|
# set the default current child
|
|
if not self.current_child:
|
|
self.current_child = new_one
|
|
|
|
def on_draw(self, widget, event):
|
|
cr = widget.window.cairo_create()
|
|
|
|
self.width = self.allocation.width
|
|
self.height = self.allocation.height
|
|
|
|
self.update_children_size()
|
|
|
|
self.draw_background(cr)
|
|
self.draw_toggled_tab(cr)
|
|
|
|
for child in self.children:
|
|
if child["indicator_show"] == True:
|
|
self.draw_indicator(cr, child)
|
|
|
|
self.draw_tab_text(cr)
|
|
|
|
def draw_background(self, cr):
|
|
style = self.get_style()
|
|
|
|
if self.is_focus():
|
|
cr.set_source_color(style.base[gtk.STATE_SELECTED])
|
|
else:
|
|
cr.set_source_color(style.base[gtk.STATE_NORMAL])
|
|
|
|
y = 6
|
|
h = self.height - 6 - 1
|
|
gap = 1
|
|
|
|
w = self.children[0]["x"]
|
|
cr.set_source_color(gtk.gdk.color_parse(HobColors.GRAY))
|
|
cr.rectangle(0, y, w - gap, h) # start rectangle
|
|
cr.fill()
|
|
|
|
cr.set_source_color(style.base[gtk.STATE_NORMAL])
|
|
cr.rectangle(w - gap, y, w, h) #first gap
|
|
cr.fill()
|
|
|
|
w = self.tab_width
|
|
for child in self.children:
|
|
x = child["x"]
|
|
cr.set_source_color(gtk.gdk.color_parse(HobColors.GRAY))
|
|
cr.rectangle(x, y, w - gap, h) # tab rectangle
|
|
cr.fill()
|
|
cr.set_source_color(style.base[gtk.STATE_NORMAL])
|
|
cr.rectangle(x + w - gap, y, w, h) # gap
|
|
cr.fill()
|
|
|
|
cr.set_source_color(gtk.gdk.color_parse(HobColors.GRAY))
|
|
cr.rectangle(x + w, y, self.width - x - w, h) # last rectangle
|
|
cr.fill()
|
|
|
|
def draw_tab_text(self, cr):
|
|
style = self.get_style()
|
|
|
|
for child in self.children:
|
|
pangolayout = child["title_layout"]
|
|
if pangolayout:
|
|
fontw, fonth = pangolayout.get_pixel_size()
|
|
# center pos
|
|
off_x = (self.tab_width - fontw) / 2
|
|
off_y = (self.tab_height - fonth) / 2
|
|
x = child["x"] + off_x
|
|
y = child["y"] + off_y
|
|
if not child == self.current_child:
|
|
self.window.draw_layout(self.style.fg_gc[gtk.STATE_NORMAL], int(x), int(y), pangolayout, gtk.gdk.Color(HobColors.WHITE))
|
|
else:
|
|
self.window.draw_layout(self.style.fg_gc[gtk.STATE_NORMAL], int(x), int(y), pangolayout)
|
|
|
|
def draw_toggled_tab(self, cr):
|
|
if not self.current_child:
|
|
return
|
|
x = self.current_child["x"]
|
|
y = self.current_child["y"]
|
|
width = self.tab_width
|
|
height = self.tab_height
|
|
style = self.get_style()
|
|
color = style.base[gtk.STATE_NORMAL]
|
|
|
|
r = height / 10
|
|
if self.tab_pressed == True:
|
|
for xoff, yoff, c1, c2 in [(1, 0, HobColors.SLIGHT_DARK, HobColors.DARK), (2, 0, HobColors.GRAY, HobColors.LIGHT_GRAY)]:
|
|
cr.set_source_color(gtk.gdk.color_parse(c1))
|
|
cr.move_to(x + xoff, y + height + yoff)
|
|
cr.line_to(x + xoff, r + yoff)
|
|
cr.arc(x + r + xoff, y + r + yoff, r, math.pi, 1.5*math.pi)
|
|
cr.move_to(x + r + xoff, y + yoff)
|
|
cr.line_to(x + width - r + xoff, y + yoff)
|
|
cr.arc(x + width - r + xoff, y + r + yoff, r, 1.5*math.pi, 2*math.pi)
|
|
cr.stroke()
|
|
cr.set_source_color(gtk.gdk.color_parse(c2))
|
|
cr.move_to(x + width + xoff, r + yoff)
|
|
cr.line_to(x + width + xoff, y + height + yoff)
|
|
cr.line_to(x + xoff, y + height + yoff)
|
|
cr.stroke()
|
|
x = x + 2
|
|
y = y + 2
|
|
cr.set_source_rgba(color.red, color.green, color.blue, 1)
|
|
cr.move_to(x + r, y)
|
|
cr.line_to(x + width - r , y)
|
|
cr.arc(x + width - r, y + r, r, 1.5*math.pi, 2*math.pi)
|
|
cr.move_to(x + width, r)
|
|
cr.line_to(x + width, y + height)
|
|
cr.line_to(x, y + height)
|
|
cr.line_to(x, r)
|
|
cr.arc(x + r, y + r, r, math.pi, 1.5*math.pi)
|
|
cr.fill()
|
|
|
|
def draw_indicator(self, cr, child):
|
|
text = ("%d" % child["indicator_number"])
|
|
layout = self.create_pango_layout(text)
|
|
layout.set_font_description(self.font)
|
|
textw, texth = layout.get_pixel_size()
|
|
# draw the back round area
|
|
tab_x = child["x"]
|
|
tab_y = child["y"]
|
|
dest_w = int(32 * self.tab_w_ratio)
|
|
dest_h = int(32 * self.tab_h_ratio)
|
|
if dest_h < self.tab_height:
|
|
dest_w = dest_h
|
|
# x position is offset(tab_width*3/4 - icon_width/2) + start_pos(tab_x)
|
|
x = tab_x + self.tab_width * 3/4 - dest_w/2
|
|
y = tab_y + self.tab_height/2 - dest_h/2
|
|
|
|
r = min(dest_w, dest_h)/2
|
|
if not child == self.current_child:
|
|
color = cr.set_source_color(gtk.gdk.color_parse(HobColors.DEEP_RED))
|
|
else:
|
|
color = cr.set_source_color(gtk.gdk.color_parse(HobColors.GRAY))
|
|
# check round back area can contain the text or not
|
|
back_round_can_contain_width = float(2 * r * 0.707)
|
|
if float(textw) > back_round_can_contain_width:
|
|
xoff = (textw - int(back_round_can_contain_width)) / 2
|
|
cr.move_to(x + r - xoff, y + r + r)
|
|
cr.arc((x + r - xoff), (y + r), r, 0.5*math.pi, 1.5*math.pi)
|
|
cr.fill() # left half round
|
|
cr.rectangle((x + r - xoff), y, 2 * xoff, 2 * r)
|
|
cr.fill() # center rectangle
|
|
cr.arc((x + r + xoff), (y + r), r, 1.5*math.pi, 0.5*math.pi)
|
|
cr.fill() # right half round
|
|
else:
|
|
cr.arc((x + r), (y + r), r, 0, 2*math.pi)
|
|
cr.fill()
|
|
# draw the number text
|
|
x = x + (dest_w/2)-(textw/2)
|
|
y = y + (dest_h/2) - (texth/2)
|
|
cr.move_to(x, y)
|
|
self.window.draw_layout(self.style.fg_gc[gtk.STATE_NORMAL], int(x), int(y), layout, gtk.gdk.Color(HobColors.WHITE))
|
|
|
|
def show_indicator_icon(self, child, number):
|
|
child["indicator_show"] = True
|
|
child["indicator_number"] = number
|
|
self.queue_draw()
|
|
|
|
def hide_indicator_icon(self, child):
|
|
child["indicator_show"] = False
|
|
self.queue_draw()
|
|
|
|
def set_blank_size(self, x, y, w, h):
|
|
if not self.blank_rectangle or self.blank_rectangle.x != x or self.blank_rectangle.width != w:
|
|
self.emit("blank-area-changed", x, y, w, h)
|
|
|
|
return gtk.gdk.Rectangle(x, y, w, h)
|
|
|
|
def query_tooltip_cb(self, widget, x, y, keyboardtip, tooltip):
|
|
if keyboardtip or (not tooltip):
|
|
return False
|
|
# check which tab be clicked
|
|
for child in self.children:
|
|
if (child["x"] < x) and (x < child["x"] + self.tab_width) \
|
|
and (child["y"] < y) and (y < child["y"] + self.tab_height):
|
|
tooltip.set_markup(child["tooltip_markup"])
|
|
return True
|
|
|
|
return False
|
|
|
|
class HobNotebook(gtk.VBox):
|
|
|
|
def __init__(self):
|
|
gtk.VBox.__init__(self, False, 0)
|
|
|
|
self.notebook = gtk.Notebook()
|
|
self.notebook.set_property('homogeneous', True)
|
|
self.notebook.set_property('show-tabs', False)
|
|
|
|
self.tabbar = HobTabBar()
|
|
self.tabbar.connect("tab-switched", self.tab_switched_cb)
|
|
self.notebook.connect("page-added", self.page_added_cb)
|
|
self.notebook.connect("page-removed", self.page_removed_cb)
|
|
|
|
self.search = None
|
|
self.search_name = ""
|
|
|
|
self.tb = gtk.Table(1, 100, False)
|
|
self.hbox= gtk.HBox(False, 0)
|
|
self.hbox.pack_start(self.tabbar, True, True)
|
|
self.tb.attach(self.hbox, 0, 100, 0, 1)
|
|
|
|
self.pack_start(self.tb, False, False)
|
|
self.pack_start(self.notebook)
|
|
|
|
self.show_all()
|
|
|
|
def append_page(self, child, tab_label):
|
|
self.notebook.set_current_page(self.notebook.append_page(child, tab_label))
|
|
|
|
def set_entry(self, name="Search:"):
|
|
for child in self.tb.get_children():
|
|
if child:
|
|
self.tb.remove(child)
|
|
|
|
hbox_entry = gtk.HBox(False, 0)
|
|
hbox_entry.show()
|
|
|
|
self.search = gtk.Entry()
|
|
self.search_name = name
|
|
style = self.search.get_style()
|
|
style.text[gtk.STATE_NORMAL] = self.get_colormap().alloc_color(HobColors.GRAY, False, False)
|
|
self.search.set_style(style)
|
|
self.search.set_text(name)
|
|
self.search.set_editable(False)
|
|
self.search.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, gtk.STOCK_CLEAR)
|
|
self.search.connect("icon-release", self.set_search_entry_clear_cb)
|
|
self.search.show()
|
|
self.align = gtk.Alignment(xalign=1.0, yalign=0.7)
|
|
self.align.add(self.search)
|
|
self.align.show()
|
|
hbox_entry.pack_end(self.align, False, False)
|
|
self.tabbar.resize_blank_rectangle()
|
|
|
|
self.tb.attach(hbox_entry, 75, 100, 0, 1, xpadding=5)
|
|
self.tb.attach(self.hbox, 0, 100, 0, 1)
|
|
|
|
self.tabbar.connect("blank-area-changed", self.blank_area_resize_cb)
|
|
self.search.connect("focus-in-event", self.set_search_entry_editable_cb)
|
|
self.search.connect("focus-out-event", self.set_search_entry_reset_cb)
|
|
|
|
self.tb.show()
|
|
|
|
def show_indicator_icon(self, title, number):
|
|
for child in self.tabbar.children:
|
|
if child["toggled_page"] == -1:
|
|
continue
|
|
if child["title"] == title:
|
|
self.tabbar.show_indicator_icon(child, number)
|
|
|
|
def hide_indicator_icon(self, title):
|
|
for child in self.tabbar.children:
|
|
if child["toggled_page"] == -1:
|
|
continue
|
|
if child["title"] == title:
|
|
self.tabbar.hide_indicator_icon(child)
|
|
|
|
def tab_switched_cb(self, widget, page):
|
|
self.notebook.set_current_page(page)
|
|
|
|
def page_added_cb(self, notebook, notebook_child, page):
|
|
if not notebook:
|
|
return
|
|
title = notebook.get_tab_label_text(notebook_child)
|
|
label = notebook.get_tab_label(notebook_child)
|
|
tooltip_markup = label.get_tooltip_markup()
|
|
if not title:
|
|
return
|
|
for child in self.tabbar.children:
|
|
if child["title"] == title:
|
|
child["toggled_page"] = page
|
|
return
|
|
self.tabbar.append_tab_child(title, page, tooltip_markup)
|
|
|
|
def page_removed_cb(self, notebook, notebook_child, page, title=""):
|
|
for child in self.tabbar.children:
|
|
if child["title"] == title:
|
|
child["toggled_page"] = -1
|
|
|
|
def blank_area_resize_cb(self, widget, request_x, request_y, request_width, request_height):
|
|
self.search.set_size_request(request_width, request_height)
|
|
|
|
def set_search_entry_editable_cb(self, search, event):
|
|
search.set_editable(True)
|
|
search.set_text("")
|
|
style = self.search.get_style()
|
|
style.text[gtk.STATE_NORMAL] = self.get_colormap().alloc_color(HobColors.BLACK, False, False)
|
|
search.set_style(style)
|
|
|
|
def reset_entry(self, entry):
|
|
style = entry.get_style()
|
|
style.text[gtk.STATE_NORMAL] = self.get_colormap().alloc_color(HobColors.GRAY, False, False)
|
|
entry.set_style(style)
|
|
entry.set_text(self.search_name)
|
|
entry.set_editable(False)
|
|
|
|
def set_search_entry_reset_cb(self, search, event):
|
|
self.reset_entry(search)
|
|
|
|
def set_search_entry_clear_cb(self, search, icon_pos, event):
|
|
self.reset_entry(search)
|
|
|
|
class HobWarpCellRendererText(gtk.CellRendererText):
|
|
def __init__(self, col_number):
|
|
gtk.CellRendererText.__init__(self)
|
|
self.set_property("wrap-mode", pango.WRAP_WORD_CHAR)
|
|
self.set_property("wrap-width", 300) # default value wrap width is 300
|
|
self.col_n = col_number
|
|
|
|
def do_render(self, window, widget, background_area, cell_area, expose_area, flags):
|
|
if widget:
|
|
self.props.wrap_width = self.get_resized_wrap_width(widget, widget.get_column(self.col_n))
|
|
return gtk.CellRendererText.do_render(self, window, widget, background_area, cell_area, expose_area, flags)
|
|
|
|
def get_resized_wrap_width(self, treeview, column):
|
|
otherCols = []
|
|
for col in treeview.get_columns():
|
|
if col != column:
|
|
otherCols.append(col)
|
|
adjwidth = treeview.allocation.width - sum(c.get_width() for c in otherCols)
|
|
adjwidth -= treeview.style_get_property("horizontal-separator") * 4
|
|
if self.props.wrap_width == adjwidth or adjwidth <= 0:
|
|
adjwidth = self.props.wrap_width
|
|
return adjwidth
|
|
|
|
gobject.type_register(HobWarpCellRendererText)
|
|
|
|
class HobIconChecker(hic):
|
|
def set_hob_icon_to_stock_icon(self, file_path, stock_id=""):
|
|
try:
|
|
pixbuf = gtk.gdk.pixbuf_new_from_file(file_path)
|
|
except Exception, e:
|
|
return None
|
|
|
|
if stock_id and (gtk.icon_factory_lookup_default(stock_id) == None):
|
|
icon_factory = gtk.IconFactory()
|
|
icon_factory.add_default()
|
|
icon_factory.add(stock_id, gtk.IconSet(pixbuf))
|
|
gtk.stock_add([(stock_id, '_label', 0, 0, '')])
|
|
|
|
return icon_factory.lookup(stock_id)
|
|
|
|
return None
|
|
|
|
"""
|
|
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'.
|
|
this function check the stock_id and make hob_id to replaced the gtk_id then return it or ""
|
|
"""
|
|
def check_stock_icon(self, stock_name=""):
|
|
HOB_CHECK_STOCK_NAME = {
|
|
('hic-dialog-info', 'gtk-dialog-info', 'dialog-info') : self.ICON_INDI_INFO_FILE,
|
|
('hic-ok', 'gtk-ok', 'ok') : self.ICON_INDI_TICK_FILE,
|
|
('hic-dialog-error', 'gtk-dialog-error', 'dialog-error') : self.ICON_INDI_ERROR_FILE,
|
|
('hic-dialog-warning', 'gtk-dialog-warning', 'dialog-warning') : self.ICON_INDI_ALERT_FILE,
|
|
('hic-task-refresh', 'gtk-execute', 'execute') : self.ICON_INDI_REFRESH_FILE,
|
|
}
|
|
valid_stock_id = stock_name
|
|
if stock_name:
|
|
for names, path in HOB_CHECK_STOCK_NAME.iteritems():
|
|
if stock_name in names:
|
|
valid_stock_id = names[0]
|
|
if not gtk.icon_factory_lookup_default(valid_stock_id):
|
|
self.set_hob_icon_to_stock_icon(path, valid_stock_id)
|
|
|
|
return valid_stock_id
|
|
|
|
class HobCellRendererController(gobject.GObject):
|
|
(MODE_CYCLE_RUNNING, MODE_ONE_SHORT) = range(2)
|
|
__gsignals__ = {
|
|
"run-timer-stopped" : (gobject.SIGNAL_RUN_LAST,
|
|
gobject.TYPE_NONE,
|
|
()),
|
|
}
|
|
def __init__(self, runningmode=MODE_CYCLE_RUNNING, is_draw_row=False):
|
|
gobject.GObject.__init__(self)
|
|
self.timeout_id = None
|
|
self.current_angle_pos = 0.0
|
|
self.step_angle = 0.0
|
|
self.tree_headers_height = 0
|
|
self.running_cell_areas = []
|
|
self.running_mode = runningmode
|
|
self.is_queue_draw_row_area = is_draw_row
|
|
self.force_stop_enable = False
|
|
|
|
def is_active(self):
|
|
if self.timeout_id:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def reset_run(self):
|
|
self.force_stop()
|
|
self.running_cell_areas = []
|
|
self.current_angle_pos = 0.0
|
|
self.step_angle = 0.0
|
|
|
|
''' time_iterval: (1~1000)ms, which will be as the basic interval count for timer
|
|
init_usrdata: the current data which related the progress-bar will be at
|
|
min_usrdata: the range of min of user data
|
|
max_usrdata: the range of max of user data
|
|
step: each step which you want to progress
|
|
Note: the init_usrdata should in the range of from min to max, and max should > min
|
|
step should < (max - min)
|
|
'''
|
|
def start_run(self, time_iterval, init_usrdata, min_usrdata, max_usrdata, step, tree):
|
|
if (not time_iterval) or (not max_usrdata):
|
|
return
|
|
usr_range = (max_usrdata - min_usrdata) * 1.0
|
|
self.current_angle_pos = (init_usrdata * 1.0) / usr_range
|
|
self.step_angle = (step * 1) / usr_range
|
|
self.timeout_id = gobject.timeout_add(int(time_iterval),
|
|
self.make_image_on_progressing_cb, tree)
|
|
self.tree_headers_height = self.get_treeview_headers_height(tree)
|
|
self.force_stop_enable = False
|
|
|
|
def force_stop(self):
|
|
self.emit("run-timer-stopped")
|
|
self.force_stop_enable = True
|
|
if self.timeout_id:
|
|
if gobject.source_remove(self.timeout_id):
|
|
self.timeout_id = None
|
|
|
|
def on_draw_pixbuf_cb(self, pixbuf, cr, x, y, img_width, img_height, do_refresh=True):
|
|
if pixbuf:
|
|
r = max(img_width/2, img_height/2)
|
|
cr.translate(x + r, y + r)
|
|
if do_refresh:
|
|
cr.rotate(2 * math.pi * self.current_angle_pos)
|
|
|
|
cr.set_source_pixbuf(pixbuf, -img_width/2, -img_height/2)
|
|
cr.paint()
|
|
|
|
def on_draw_fadeinout_cb(self, cr, color, x, y, width, height, do_fadeout=True):
|
|
if do_fadeout:
|
|
alpha = self.current_angle_pos * 0.8
|
|
else:
|
|
alpha = (1.0 - self.current_angle_pos) * 0.8
|
|
|
|
cr.set_source_rgba(color.red, color.green, color.blue, alpha)
|
|
cr.rectangle(x, y, width, height)
|
|
cr.fill()
|
|
|
|
def get_treeview_headers_height(self, tree):
|
|
if tree and (tree.get_property("headers-visible") == True):
|
|
height = tree.get_allocation().height - tree.get_bin_window().get_size()[1]
|
|
return height
|
|
|
|
return 0
|
|
|
|
def make_image_on_progressing_cb(self, tree):
|
|
self.current_angle_pos += self.step_angle
|
|
if self.running_mode == self.MODE_CYCLE_RUNNING:
|
|
if (self.current_angle_pos >= 1):
|
|
self.current_angle_pos = self.step_angle
|
|
else:
|
|
if self.current_angle_pos > 1:
|
|
self.force_stop()
|
|
return False
|
|
|
|
if self.is_queue_draw_row_area:
|
|
for path in self.running_cell_areas:
|
|
rect = tree.get_cell_area(path, tree.get_column(0))
|
|
row_x, _, row_width, _ = tree.get_visible_rect()
|
|
tree.queue_draw_area(row_x, rect.y + self.tree_headers_height, row_width, rect.height)
|
|
else:
|
|
for rect in self.running_cell_areas:
|
|
tree.queue_draw_area(rect.x, rect.y + self.tree_headers_height, rect.width, rect.height)
|
|
|
|
return (not self.force_stop_enable)
|
|
|
|
def append_running_cell_area(self, cell_area):
|
|
if cell_area and (cell_area not in self.running_cell_areas):
|
|
self.running_cell_areas.append(cell_area)
|
|
|
|
def remove_running_cell_area(self, cell_area):
|
|
if cell_area in self.running_cell_areas:
|
|
self.running_cell_areas.remove(cell_area)
|
|
if not self.running_cell_areas:
|
|
self.reset_run()
|
|
|
|
gobject.type_register(HobCellRendererController)
|
|
|
|
class HobCellRendererPixbuf(gtk.CellRendererPixbuf):
|
|
def __init__(self):
|
|
gtk.CellRendererPixbuf.__init__(self)
|
|
self.control = HobCellRendererController()
|
|
# add icon checker for make the gtk-icon transfer to hob-icon
|
|
self.checker = HobIconChecker()
|
|
self.set_property("stock-size", gtk.ICON_SIZE_DND)
|
|
|
|
def get_pixbuf_from_stock_icon(self, widget, stock_id="", size=gtk.ICON_SIZE_DIALOG):
|
|
if widget and stock_id and gtk.icon_factory_lookup_default(stock_id):
|
|
return widget.render_icon(stock_id, size)
|
|
|
|
return None
|
|
|
|
def set_icon_name_to_id(self, new_name):
|
|
if new_name and type(new_name) == str:
|
|
# check the name is need to transfer to hob icon or not
|
|
name = self.checker.check_stock_icon(new_name)
|
|
if name.startswith("hic") or name.startswith("gtk"):
|
|
stock_id = name
|
|
else:
|
|
stock_id = 'gtk-' + name
|
|
|
|
return stock_id
|
|
|
|
''' render cell exactly, "icon-name" is priority
|
|
if use the 'hic-task-refresh' will make the pix animation
|
|
if 'pix' will change the pixbuf for it from the pixbuf or image.
|
|
'''
|
|
def do_render(self, window, tree, background_area,cell_area, expose_area, flags):
|
|
if (not self.control) or (not tree):
|
|
return
|
|
|
|
x, y, w, h = self.on_get_size(tree, cell_area)
|
|
x += cell_area.x
|
|
y += cell_area.y
|
|
w -= 2 * self.get_property("xpad")
|
|
h -= 2 * self.get_property("ypad")
|
|
|
|
stock_id = ""
|
|
if self.props.icon_name:
|
|
stock_id = self.set_icon_name_to_id(self.props.icon_name)
|
|
elif self.props.stock_id:
|
|
stock_id = self.props.stock_id
|
|
elif self.props.pixbuf:
|
|
pix = self.props.pixbuf
|
|
else:
|
|
return
|
|
|
|
if stock_id:
|
|
pix = self.get_pixbuf_from_stock_icon(tree, stock_id, self.props.stock_size)
|
|
if stock_id == 'hic-task-refresh':
|
|
self.control.append_running_cell_area(cell_area)
|
|
if self.control.is_active():
|
|
self.control.on_draw_pixbuf_cb(pix, window.cairo_create(), x, y, w, h, True)
|
|
else:
|
|
self.control.start_run(200, 0, 0, 1000, 200, tree)
|
|
else:
|
|
self.control.remove_running_cell_area(cell_area)
|
|
self.control.on_draw_pixbuf_cb(pix, window.cairo_create(), x, y, w, h, False)
|
|
|
|
def on_get_size(self, widget, cell_area):
|
|
if self.props.icon_name or self.props.pixbuf or self.props.stock_id:
|
|
w, h = gtk.icon_size_lookup(self.props.stock_size)
|
|
calc_width = self.get_property("xpad") * 2 + w
|
|
calc_height = self.get_property("ypad") * 2 + h
|
|
x_offset = 0
|
|
y_offset = 0
|
|
if cell_area and w > 0 and h > 0:
|
|
x_offset = self.get_property("xalign") * (cell_area.width - calc_width - self.get_property("xpad"))
|
|
y_offset = self.get_property("yalign") * (cell_area.height - calc_height - self.get_property("ypad"))
|
|
|
|
return x_offset, y_offset, w, h
|
|
|
|
return 0, 0, 0, 0
|
|
|
|
gobject.type_register(HobCellRendererPixbuf)
|
|
|
|
class HobCellRendererToggle(gtk.CellRendererToggle):
|
|
def __init__(self):
|
|
gtk.CellRendererToggle.__init__(self)
|
|
self.ctrl = HobCellRendererController(is_draw_row=True)
|
|
self.ctrl.running_mode = self.ctrl.MODE_ONE_SHORT
|
|
self.cell_attr = {"fadeout": False}
|
|
|
|
def do_render(self, window, widget, background_area, cell_area, expose_area, flags):
|
|
if (not self.ctrl) or (not widget):
|
|
return
|
|
if self.ctrl.is_active():
|
|
path = widget.get_path_at_pos(cell_area.x + cell_area.width/2, cell_area.y + cell_area.height/2)
|
|
# sometimes the parameters of cell_area will be a negative number,such as pull up down the scroll bar
|
|
# it's over the tree container range, so the path will be bad
|
|
if not path: return
|
|
path = path[0]
|
|
if path in self.ctrl.running_cell_areas:
|
|
cr = window.cairo_create()
|
|
color = gtk.gdk.Color(HobColors.WHITE)
|
|
|
|
row_x, _, row_width, _ = widget.get_visible_rect()
|
|
border_y = self.get_property("ypad")
|
|
self.ctrl.on_draw_fadeinout_cb(cr, color, row_x, cell_area.y - border_y, row_width, \
|
|
cell_area.height + border_y * 2, self.cell_attr["fadeout"])
|
|
|
|
return gtk.CellRendererToggle.do_render(self, window, widget, background_area, cell_area, expose_area, flags)
|
|
|
|
'''delay: normally delay time is 1000ms
|
|
cell_list: whilch cells need to be render
|
|
'''
|
|
def fadeout(self, tree, delay, cell_list=None):
|
|
if (delay < 200) or (not tree):
|
|
return
|
|
self.cell_attr["fadeout"] = True
|
|
self.ctrl.running_cell_areas = cell_list
|
|
self.ctrl.start_run(200, 0, 0, delay, (delay * 200 / 1000), tree)
|
|
|
|
def connect_render_state_changed(self, func, usrdata=None):
|
|
if not func:
|
|
return
|
|
if usrdata:
|
|
self.ctrl.connect("run-timer-stopped", func, self, usrdata)
|
|
else:
|
|
self.ctrl.connect("run-timer-stopped", func, self)
|
|
|
|
gobject.type_register(HobCellRendererToggle)
|