def __init__(self, pkg_name, pkg_version, data_manager):
        '''
        init docs
        '''
        TreeItem.__init__(self)
        self.pkg_name = pkg_name
        self.pkg_version = pkg_version
        self.data_manager = data_manager
        self.icon_pixbuf = None

        info = data_manager.get_item_pkg_info(self.pkg_name)
        self.alias_name = info[1]
        self.short_desc = info[2]
        self.star_level = get_star_level(5.0)
        self.star_buffer = DscStarBuffer(pkg_name)

        self.grade_star = 0

        button_pixbuf = app_theme.get_pixbuf(
            "button/uninstall_normal.png").get_pixbuf()
        (self.button_width, self.button_height
         ) = button_pixbuf.get_width(), button_pixbuf.get_height()
        self.button_status = BUTTON_NORMAL

        self.status = self.STATUS_NORMAL
        self.status_text = ""
        self.progress_buffer = ProgressBuffer()
    def __init__(self, pkg_name, pkg_version, data_manager):
        '''
        init docs
        '''
        TreeItem.__init__(self)
        self.pkg_name = pkg_name
        self.pkg_version = pkg_version
        self.data_manager = data_manager
        self.icon_pixbuf = None

        info = self.data_manager.get_item_pkg_info(self.pkg_name)
        self.alias_name = info[1]
        self.short_desc = info[2]
        self.star_level = get_star_level(5.0)
        self.star_buffer = DscStarBuffer(pkg_name)

        self.grade_star = 0

        self.status = self.STATUS_READY_DOWNLOAD
        self.status_text = _("Dependencies analyzing")
        self.progress_buffer = ProgressBuffer()

        button_pixbuf = app_theme.get_pixbuf(
            "button/start_normal.png").get_pixbuf()
        (self.button_width, self.button_height
         ) = button_pixbuf.get_width(), button_pixbuf.get_height()
        self.button_status = BUTTON_NORMAL

        ### TODO: is_installed status
        self.install_status = json.dumps([])
        self.desktops = []
 def __init__(self, trans_data):
     gobject.GObject.__init__(self)
     self.__updater_id = None
     
     # Init data.
     self.angle = 0
     self.status_icon = app_theme.get_pixbuf("transcoder/wait.png").get_pixbuf()
     self.status_icon_press = app_theme.get_pixbuf("transcoder/wait_press.png").get_pixbuf()
     self.stop_icon = app_theme.get_pixbuf("transcoder/stop.png").get_pixbuf()
     self.progress_ratio = 0.0
     self.skip_flag = False        
     self.trans_data = trans_data
     self.init_transcoder(trans_data)   
     self.__update()        
     self.progress_buffer = ProgressBuffer()
    def __init__(self, pkg_name, pkg_version, data_manager):
        '''
        init docs
        '''
        TreeItem.__init__(self)
        self.pkg_name = pkg_name
        self.pkg_version = pkg_version
        self.data_manager = data_manager
        self.icon_pixbuf = None
        
        info = data_manager.get_item_pkg_info(self.pkg_name)
        self.alias_name = info[1]
        self.short_desc = info[2]
        self.star_level = get_star_level(5.0)
        self.star_buffer = DscStarBuffer(pkg_name)

        self.grade_star = 0
        
        button_pixbuf = app_theme.get_pixbuf("button/uninstall_normal.png").get_pixbuf()
        (self.button_width, self.button_height) = button_pixbuf.get_width(), button_pixbuf.get_height()
        self.button_status = BUTTON_NORMAL
        
        self.status = self.STATUS_NORMAL
        self.status_text = ""
        self.progress_buffer = ProgressBuffer()
 def __init__(self, pkg_name, pkg_version, data_manager):
     '''
     init docs
     '''
     TreeItem.__init__(self)
     self.pkg_name = pkg_name
     self.pkg_version = pkg_version
     self.data_manager = data_manager
     self.icon_pixbuf = None
     
     (self.short_desc, star, self.alias_name) = data_manager.get_item_pkg_info(self.pkg_name)
     self.star_level = get_star_level(star)
     self.star_buffer = DscStarBuffer(pkg_name)
     
     self.grade_star = 0
     
     self.status = self.STATUS_READY_DOWNLOAD
     self.status_text = _("Analyzing dependencies")
     self.progress_buffer = ProgressBuffer()
     
     button_pixbuf = app_theme.get_pixbuf("button/start_normal.png").get_pixbuf()
     (self.button_width, self.button_height) = button_pixbuf.get_width(), button_pixbuf.get_height()
     self.button_status = BUTTON_NORMAL
     
     self.is_have_desktop_file = False
    def __init__(self, pkg_name, pkg_version, data_manager):
        '''
        init docs
        '''
        TreeItem.__init__(self)
        self.pkg_name = pkg_name
        self.pkg_version = pkg_version
        self.data_manager = data_manager
        self.icon_pixbuf = None

        info = self.data_manager.get_item_pkg_info(self.pkg_name)
        self.alias_name = info[1]
        self.short_desc = info[2]
        self.star_level = get_star_level(5.0)
        self.star_buffer = DscStarBuffer(pkg_name)

        self.grade_star = 0

        self.status = self.STATUS_READY_DOWNLOAD
        self.status_text = _("Dependencies analyzing")
        self.progress_buffer = ProgressBuffer()

        button_pixbuf = app_theme.get_pixbuf("button/start_normal.png").get_pixbuf()
        (self.button_width, self.button_height) = button_pixbuf.get_width(), button_pixbuf.get_height()
        self.button_status = BUTTON_NORMAL

        ### TODO: is_installed status
        self.install_status = json.dumps([])
        self.desktops = []
 def update(self, title="icon_pixbuf", name="Linux deepin", length="progress_buffer", _format="rmvb"):
     '''Update.'''
     # Update.
     self.title = title
     self.name = name
     self.length = length
     self.format = _format
     
     # Calculate item size.
     # self.title_padding_x = 10
     # self.title_padding_y = 5
     # (self.title_width, self.title_height) = get_content_size(self.title, DEFAULT_FONT_SIZE) #DEFAULT_FONT_SIZE
     # self.title_width = 20
     
     self.name_padding_x = 10
     self.name_padding_y = 5
     (self.name_width, self.name_height) = get_content_size(self.name, DEFAULT_FONT_SIZE) #DEFAULT_FONT_SIZE
     self.name_width = 80
     
     self.length_padding_x = 50
     self.length_padding_y = 5
     (self.length_width, self.length_height) = get_content_size(self.length, DEFAULT_FONT_SIZE) #DEFAULT_FONT_SIZE
     
     self.format_padding_x = 0
     self.format_padding_y = 5
     (self.format_width, self.format_height) = get_content_size(self.format, 8) #DEFAULT_FONT_SIZE        
     
     # ProgressBar buffer.
     self.progress_ratio = 0.0
     # self.progress_padding_x = 30
     self.progress_padding_x = 25
     self.progress_padding_y = 5
     self.progress_w, self.progress_h = 100, 10
     self.progress_buffer = ProgressBuffer()
     
     self.status_icon = None
     self.status_icon_press = None
     self.wait_icon = app_theme.get_pixbuf("transcoder/wait.png").get_pixbuf()
     self.wait_icon_press = app_theme.get_pixbuf("transcoder/wait_press.png").get_pixbuf()
     # stop conv.
     self.stop_icon = app_theme.get_pixbuf("transcoder/stop.png").get_pixbuf()        
     self.stop_icon_press = app_theme.get_pixbuf("transcoder/stop_press.png").get_pixbuf()
     # conv success.
     self.success_icon = app_theme.get_pixbuf("transcoder/success.png").get_pixbuf()
     self.success_icon_press = app_theme.get_pixbuf("transcoder/success_press.png").get_pixbuf()
     # staring conv.
     self.working_icon = app_theme.get_pixbuf("transcoder/working.png").get_pixbuf()
     self.working_icon_press = app_theme.get_pixbuf("transcoder/working_press.png").get_pixbuf()
     # error .
     self.error_icon = app_theme.get_pixbuf("transcoder/error.png").get_pixbuf()
     self.error_icon_press = app_theme.get_pixbuf("transcoder/error_press.png").get_pixbuf()
     # Init icon state.        
     self.set_status_icon("wait")        
     # set icon[position]->> x and y.
     self.status_icon_padding_x = 5
     self.status_icon_padding_y = 5        
     # get icon width and height .
     self.status_icon_w, self.status_icon_h = (self.status_icon.get_width(), self.status_icon.get_height())
    def __init__(self, image_object, finish_callback):
        '''
        init docs
        '''
        TreeItem.__init__(self)

        # Init sizes.
        self.item_height = 50
        self.info_width = -1
        self.progressbar_width = 100
        self.progressbar_padding_x = 10
        self.progressbar_height = 12
        self.check_button_padding_x = 10
        self.info_padding_x = 5

        self.icon_pixbuf = None
        self.image_object = image_object
        self.button_status = BUTTON_NORMAL

        # Init status.
        self.status = self.STATUS_WAIT_DOWNLOAD
        self.status_text = "等待下载"

        # Init buffers.
        self.progress_buffer = ProgressBuffer()

        self.stop_pixbuf = app_theme.get_pixbuf(
            "individuation/stop.png").get_pixbuf()
        self.stop_pixbuf_padding_x = 5
        self.block_width = 50
        self.download_task = TaskObject(image_object.big_url,
                                        image_object.get_save_path(),
                                        output_temp=True)

        self.download_task.connect("update", self.download_update)
        self.download_task.connect("finish", self.download_finish)
        self.download_task.connect("error", self.download_failed)
        self.download_task.connect("start", self.download_start)

        self.finish_callback = finish_callback

        self.start_download()
 def __init__(self, trans_data):
     gobject.GObject.__init__(self)
     self.__updater_id = None
     
     # Init data.
     self.angle = 0
     self.status_icon = app_theme.get_pixbuf("transcoder/wait.png").get_pixbuf()
     self.status_icon_press = app_theme.get_pixbuf("transcoder/wait_press.png").get_pixbuf()
     self.stop_icon = app_theme.get_pixbuf("transcoder/stop.png").get_pixbuf()
     self.progress_ratio = 0.0
     self.trans_data = trans_data
     self.init_transcoder(trans_data)   
     self.__update()        
     self.progress_buffer = ProgressBuffer()
    def __init__(self, image_object, finish_callback):
        '''
        init docs
        '''
        TreeItem.__init__(self)
        
        
        # Init sizes.
        self.item_height = 50        
        self.info_width = -1
        self.progressbar_width = 100
        self.progressbar_padding_x = 10
        self.progressbar_height = 12
        self.check_button_padding_x = 10
        self.info_padding_x = 5

        self.icon_pixbuf = None
        self.image_object = image_object        
        self.button_status = BUTTON_NORMAL
        
        # Init status.
        self.status = self.STATUS_WAIT_DOWNLOAD
        self.status_text = "等待下载"
        
        # Init buffers. 
        self.progress_buffer = ProgressBuffer()
        
        
        self.stop_pixbuf = app_theme.get_pixbuf("individuation/stop.png").get_pixbuf()
        self.stop_pixbuf_padding_x = 5
        self.block_width = 50
        self.download_task = TaskObject(image_object.big_url, image_object.get_save_path(), output_temp=True)
        
        self.download_task.connect("update", self.download_update)
        self.download_task.connect("finish", self.download_finish)
        self.download_task.connect("error",  self.download_failed)
        self.download_task.connect("start",  self.download_start)
        
        self.finish_callback = finish_callback
        
        self.start_download()
class InstallItem(TreeItem):
    '''
    class docs
    '''

    STATUS_READY_DOWNLOAD= 1
    STATUS_PARSE_DOWNLOAD_FAILED = 2
    STATUS_DOWNLOAD_FAILED = 3
    STATUS_WAIT_DOWNLOAD = 4
    STATUS_IN_DOWNLOAD = 5
    STATUS_STOP_DOWNLOAD = 6

    STATUS_WAIT_INSTALL = 7
    STATUS_STOP_WAIT_INSTALL = 8
    STATUS_IN_INSTALL = 9
    STATUS_INSTALL_FINISH = 10
    STATUS_INSTALL_FAILED = 11

    STATUS_PADDING_X = 15

    def __init__(self, pkg_name, pkg_version, data_manager):
        '''
        init docs
        '''
        TreeItem.__init__(self)
        self.pkg_name = pkg_name
        self.pkg_version = pkg_version
        self.data_manager = data_manager
        self.icon_pixbuf = None

        info = self.data_manager.get_item_pkg_info(self.pkg_name)
        self.alias_name = info[1]
        self.short_desc = info[2]
        self.star_level = get_star_level(5.0)
        self.star_buffer = DscStarBuffer(pkg_name)

        self.grade_star = 0

        self.status = self.STATUS_READY_DOWNLOAD
        self.status_text = _("Dependencies analyzing")
        self.progress_buffer = ProgressBuffer()

        button_pixbuf = app_theme.get_pixbuf("button/start_normal.png").get_pixbuf()
        (self.button_width, self.button_height) = button_pixbuf.get_width(), button_pixbuf.get_height()
        self.button_status = BUTTON_NORMAL

        ### TODO: is_installed status
        self.install_status = json.dumps([])
        self.desktops = []

    def handle_pkg_status(self, status, success):
        if success:
            self.install_status= str(status)
            try:
                self.desktops = json.loads(self.install_status)
                self.desktops = self.data_manager.get_pkg_desktop_info(self.desktops)
            except:
                pass
            self.emit_redraw_request()
        else:
            utils.global_logger.logerror("%s: get_pkg_installed handle_dbus_error" % self.pkg_name)
            utils.global_logger.logerror(status)

    def render_pkg_info(self, cr, rect):
        if self.row_index % 2 == 1:
            cr.set_source_rgba(1, 1, 1, 0.5)
            cr.rectangle(rect.x, rect.y, rect.width, rect.height)
            cr.fill()

        if self.icon_pixbuf == None:
            self.icon_pixbuf = gtk.gdk.pixbuf_new_from_file(get_icon_pixbuf_path(self.pkg_name))

        render_pkg_info(cr, rect, self.alias_name, self.pkg_name, self.icon_pixbuf, self.pkg_version, self.short_desc, ITEM_PKG_OFFSET_X)

    def render_pkg_status(self, cr, rect):
        if self.row_index % 2 == 1:
            cr.set_source_rgba(1, 1, 1, 0.5)
            cr.rectangle(rect.x, rect.y, rect.width, rect.height)
            cr.fill()

        if self.status == self.STATUS_READY_DOWNLOAD:
            draw_text(
                cr,
                self.status_text,
                rect.x + rect.width - ITEM_STATUS_TEXT_PADDING_RIGHT,
                rect.y,
                rect.width - ITEM_STAR_AREA_WIDTH - self.STATUS_PADDING_X,
                ITEM_HEIGHT,
                )
        elif self.status == self.STATUS_WAIT_DOWNLOAD:
            # Draw star.
            self.star_buffer.render(cr, gtk.gdk.Rectangle(rect.x, rect.y, ITEM_STAR_AREA_WIDTH, ITEM_HEIGHT))

            draw_text(
                cr,
                self.status_text,
                rect.x + rect.width - ITEM_STATUS_TEXT_PADDING_RIGHT,
                rect.y,
                rect.width - ITEM_STAR_AREA_WIDTH - self.STATUS_PADDING_X,
                ITEM_HEIGHT,
                )

            pixbuf = app_theme.get_pixbuf("button/stop.png").get_pixbuf()
            draw_pixbuf(
                cr,
                pixbuf,
                rect.x + rect.width - ITEM_CANCEL_BUTTON_PADDING_RIGHT,
                rect.y + (rect.height - pixbuf.get_height()) / 2,
                )
        elif self.status == self.STATUS_IN_DOWNLOAD:
            self.progress_buffer.render(
                cr,
                gtk.gdk.Rectangle(
                    rect.x,
                    rect.y + (ITEM_HEIGHT - PROGRESSBAR_HEIGHT) / 2,
                    ITEM_STAR_AREA_WIDTH,
                    PROGRESSBAR_HEIGHT))

            draw_text(
                cr,
                self.status_text,
                rect.x + rect.width - ITEM_STATUS_TEXT_PADDING_RIGHT,
                rect.y,
                rect.width - ITEM_STAR_AREA_WIDTH - self.STATUS_PADDING_X,
                ITEM_HEIGHT,
                )

            pixbuf = app_theme.get_pixbuf("button/stop.png").get_pixbuf()
            draw_pixbuf(
                cr,
                pixbuf,
                rect.x + rect.width - ITEM_CANCEL_BUTTON_PADDING_RIGHT,
                rect.y + (rect.height - pixbuf.get_height()) / 2,
                )
        elif self.status == self.STATUS_STOP_DOWNLOAD:
            # Draw star.
            self.star_buffer.render(cr, gtk.gdk.Rectangle(rect.x, rect.y, ITEM_STAR_AREA_WIDTH, ITEM_HEIGHT))

            draw_text(
                cr,
                self.status_text,
                rect.x + rect.width - ITEM_STATUS_TEXT_PADDING_RIGHT,
                rect.y,
                rect.width - ITEM_STAR_AREA_WIDTH - self.STATUS_PADDING_X,
                ITEM_HEIGHT,
                )
        elif self.status == self.STATUS_DOWNLOAD_FAILED:
            # Draw star.
            draw_text(
                cr,
                self.status_text,
                rect.x,
                rect.y,
                get_content_size(self.status_text)[0],
                ITEM_HEIGHT,
                text_color="#ff0000",
                )

            if self.button_status == BUTTON_NORMAL:
                status = "normal"
            elif self.button_status == BUTTON_HOVER:
                status = "hover"
            elif self.button_status == BUTTON_PRESS:
                status = "press"

            pixbuf = app_theme.get_pixbuf("button/install_%s.png" % (status, )).get_pixbuf()
            draw_pixbuf(
                cr,
                pixbuf,
                rect.x + rect.width - ITEM_BUTTON_PADDING_RIGHT - pixbuf.get_width(),
                rect.y + (rect.height - pixbuf.get_height()) / 2
                )

        elif self.status == self.STATUS_WAIT_INSTALL:
            self.progress_buffer.render(
                cr,
                gtk.gdk.Rectangle(
                    rect.x,
                    rect.y + (ITEM_HEIGHT - PROGRESSBAR_HEIGHT) / 2,
                    ITEM_STAR_AREA_WIDTH,
                    PROGRESSBAR_HEIGHT))

            draw_text(
                cr,
                self.status_text,
                rect.x + rect.width - ITEM_STATUS_TEXT_PADDING_RIGHT,
                rect.y,
                rect.width - ITEM_STAR_AREA_WIDTH - self.STATUS_PADDING_X,
                ITEM_HEIGHT,
                )

            pixbuf = app_theme.get_pixbuf("button/stop.png").get_pixbuf()
            draw_pixbuf(
                cr,
                pixbuf,
                rect.x + rect.width - ITEM_CANCEL_BUTTON_PADDING_RIGHT,
                rect.y + (rect.height - pixbuf.get_height()) / 2,
                )
        elif self.status == self.STATUS_STOP_WAIT_INSTALL:
            # Draw star.
            self.star_buffer.render(cr, gtk.gdk.Rectangle(rect.x, rect.y, ITEM_STAR_AREA_WIDTH, ITEM_HEIGHT))

            draw_text(
                cr,
                self.status_text,
                rect.x + rect.width - ITEM_STATUS_TEXT_PADDING_RIGHT,
                rect.y,
                rect.width - ITEM_STAR_AREA_WIDTH - self.STATUS_PADDING_X,
                ITEM_HEIGHT,
                )
        elif self.status == self.STATUS_IN_INSTALL:
            self.progress_buffer.render(
                cr,
                gtk.gdk.Rectangle(
                    rect.x,
                    rect.y + (ITEM_HEIGHT - PROGRESSBAR_HEIGHT) / 2,
                    ITEM_STAR_AREA_WIDTH,
                    PROGRESSBAR_HEIGHT))

            draw_text(
                cr,
                self.status_text,
                rect.x + rect.width - ITEM_STATUS_TEXT_PADDING_RIGHT,
                rect.y,
                rect.width - ITEM_STAR_AREA_WIDTH - self.STATUS_PADDING_X,
                ITEM_HEIGHT,
                )
        elif self.status == self.STATUS_INSTALL_FINISH:
            # Draw star.
            self.star_buffer.render(cr, gtk.gdk.Rectangle(rect.x, rect.y, ITEM_STAR_AREA_WIDTH, ITEM_HEIGHT))

            # Draw button.
            name = ""
            draw_str = ""
            if self.install_status == "uninstalled":
                name = "button/install"
            elif self.install_status == "unknown":
                draw_str = _("Installed")
            else:
                if self.desktops:
                    name = "button/start"
                else:
                    draw_str = _("Installed")

            if name:
                if self.button_status == BUTTON_NORMAL:
                    status = "normal"
                elif self.button_status == BUTTON_HOVER:
                    status = "hover"
                elif self.button_status == BUTTON_PRESS:
                    status = "press"

                pixbuf = app_theme.get_pixbuf("%s_%s.png" % (name, status)).get_pixbuf()
                draw_pixbuf(
                    cr,
                    pixbuf,
                    rect.x + rect.width - ITEM_BUTTON_PADDING_RIGHT - pixbuf.get_width(),
                    rect.y + (ITEM_HEIGHT - self.button_height) / 2,
                    )
            else:
                str_width, str_height = get_content_size(draw_str, 10)
                draw_text(
                    cr,
                    draw_str,
                    rect.x + rect.width - ITEM_BUTTON_PADDING_RIGHT - str_width,
                    rect.y + (rect.height - str_height) / 2,
                    str_width,
                    str_height,
                    alignment=pango.ALIGN_CENTER,
                    )
        elif self.status == self.STATUS_PARSE_DOWNLOAD_FAILED:
            # Draw star.
            self.star_buffer.render(cr, gtk.gdk.Rectangle(rect.x, rect.y, ITEM_STAR_AREA_WIDTH, ITEM_HEIGHT))

            draw_text(
                cr,
                self.status_text,
                rect.x + rect.width - ITEM_STATUS_TEXT_PADDING_RIGHT,
                rect.y,
                rect.width - ITEM_STAR_AREA_WIDTH - self.STATUS_PADDING_X,
                ITEM_HEIGHT,
                )

    def get_height(self):
        return ITEM_HEIGHT

    def get_column_widths(self):
        return [ITEM_INFO_AREA_WIDTH,
                ITEM_STAR_AREA_WIDTH + ITEM_BUTTON_AREA_WIDTH]

    def get_column_renders(self):
        return [self.render_pkg_info,
                self.render_pkg_status]

    def unselect(self):
        pass

    def select(self):
        pass

    def unhover(self, column, offset_x, offset_y):
        pass

    def hover(self, column, offset_x, offset_y):
        pass

    def motion_notify(self, column, offset_x, offset_y):
        if column == 0:
            if self.is_in_icon_area(column, offset_x, offset_y):
                global_event.emit("set-cursor", gtk.gdk.HAND2)
            elif self.is_in_name_area(column, offset_x, offset_y):
                global_event.emit("set-cursor", gtk.gdk.HAND2)
            else:
                global_event.emit("set-cursor", None)
        else:
            if self.status == self.STATUS_INSTALL_FINISH:
                if self.desktops:
                    if self.is_in_button_area(column, offset_x, offset_y):
                        self.button_status = BUTTON_HOVER

                        if self.redraw_request_callback:
                            self.redraw_request_callback(self)
                    elif self.button_status != BUTTON_NORMAL:
                        self.button_status = BUTTON_NORMAL

                        if self.redraw_request_callback:
                            self.redraw_request_callback(self)

                if self.is_in_star_area(column, offset_x, offset_y):
                    global_event.emit("set-cursor", gtk.gdk.HAND2)

                    times = offset_x / STAR_SIZE
                    self.grade_star = times * 2 + 2

                    self.grade_star = min(self.grade_star, 10)
                    self.star_buffer.star_level = self.grade_star

                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)
                else:
                    global_event.emit("set-cursor", None)

                    if self.star_buffer.star_level != self.star_level:
                        self.star_buffer.star_level = self.star_level

                        if self.redraw_request_callback:
                            self.redraw_request_callback(self)

            elif self.status == self.STATUS_DOWNLOAD_FAILED:
                if self.is_in_button_area(column, offset_x, offset_y):
                    self.button_status = BUTTON_HOVER
                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)
                elif self.button_status != BUTTON_NORMAL:
                    self.button_status = BUTTON_NORMAL
                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)

            elif self.status == self.STATUS_READY_DOWNLOAD:
                if self.is_in_star_area(column, offset_x, offset_y):
                    global_event.emit("set-cursor", gtk.gdk.HAND2)

                    times = offset_x / STAR_SIZE
                    self.grade_star = times * 2 + 2

                    self.grade_star = min(self.grade_star, 10)
                    self.star_buffer.star_level = self.grade_star

                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)
                else:
                    global_event.emit("set-cursor", None)

                    if self.star_buffer.star_level != self.star_level:
                        self.star_buffer.star_level = self.star_level

                        if self.redraw_request_callback:
                            self.redraw_request_callback(self)

    def button_press(self, column, offset_x, offset_y):
        if column == 0:
            if self.is_in_icon_area(column, offset_x, offset_y):
                global_event.emit("switch-to-detail-page", self.pkg_name)
                global_event.emit("set-cursor", None)
            elif self.is_in_name_area(column, offset_x, offset_y):
                global_event.emit("switch-to-detail-page", self.pkg_name)
                global_event.emit("set-cursor", None)
        else:
            if self.status == self.STATUS_WAIT_DOWNLOAD:
                if self.is_stop_button_can_click(column, offset_x, offset_y):
                    self.status = self.STATUS_STOP_DOWNLOAD
                    self.status_text = _("Download was interrupted")
                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)
                    global_event.emit("remove-wait-download", [self.pkg_name])
                    global_event.emit("request-stop-install-actions", [self.pkg_name])
                elif self.is_in_star_area(column, offset_x, offset_y):
                    global_event.emit("grade-pkg", self.pkg_name, self.grade_star)

            elif self.status == self.STATUS_IN_DOWNLOAD:
                if self.is_stop_button_can_click(column, offset_x, offset_y):
                    self.status = self.STATUS_STOP_DOWNLOAD
                    self.status_text = _("Download was interrupted")
                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)
                    global_event.emit("stop-download-pkg", [self.pkg_name])
                    global_event.emit("request-stop-install-actions", [self.pkg_name])

            elif self.status == self.STATUS_DOWNLOAD_FAILED:
                if self.is_in_button_area(column, offset_x, offset_y):
                    global_event.emit("install-pkg", [self.pkg_name])

            elif self.status == self.STATUS_WAIT_INSTALL:
                if self.is_stop_button_can_click(column, offset_x, offset_y):
                    self.status = self.STATUS_STOP_WAIT_INSTALL
                    self.status_text = _("Installation was interrupted")
                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)
                    global_event.emit("remove-wait-action", [(self.pkg_name, ACTION_INSTALL)])
                    global_event.emit("request-stop-install-actions", [self.pkg_name])

            elif self.status == self.STATUS_INSTALL_FINISH:
                if self.is_in_star_area(column, offset_x, offset_y):
                    global_event.emit("grade-pkg", self.pkg_name, self.grade_star)
                elif self.is_in_button_area(column, offset_x, offset_y):
                    if self.desktops:
                        global_event.emit("start-pkg", self.alias_name, self.desktops, self.get_offset_with_button(offset_x, offset_y))

    def get_offset_with_button(self, offset_x, offset_y):
        pixbuf = app_theme.get_pixbuf("button/start_normal.png").get_pixbuf()
        popup_x = self.get_column_widths()[1] - ITEM_BUTTON_PADDING_RIGHT - pixbuf.get_width() / 2
        popup_y = (ITEM_HEIGHT - pixbuf.get_height()) / 2
        return (offset_x, offset_y, popup_x, popup_y)

    def button_release(self, column, offset_x, offset_y):
        pass

    def single_click(self, column, offset_x, offset_y):
        pass

    def double_click(self, column, offset_x, offset_y):
        pass

    def is_in_star_area(self, column, offset_x, offset_y):
        return (column == 1
                and is_in_rect((offset_x, offset_y),
                               (0,
                                (ITEM_HEIGHT - STAR_SIZE) / 2,
                                ITEM_STAR_AREA_WIDTH,
                                STAR_SIZE)))

    def is_in_button_area(self, column, offset_x, offset_y):
        return (column == 1
                and is_in_rect((offset_x, offset_y),
                               (self.get_column_widths()[column] - ITEM_BUTTON_PADDING_RIGHT - self.button_width,
                                (ITEM_HEIGHT - self.button_height) / 2,
                                self.button_width,
                                self.button_height)))

    def is_stop_button_can_click(self, column, offset_x, offset_y):
        pixbuf = app_theme.get_pixbuf("button/stop.png").get_pixbuf()
        return (column == 1
                and is_in_rect((offset_x, offset_y),
                               (self.get_column_widths()[column] - ITEM_CANCEL_BUTTON_PADDING_RIGHT,
                                (ITEM_HEIGHT - pixbuf.get_height()) / 2,
                                pixbuf.get_width(),
                                pixbuf.get_height())))

    def download_ready(self):
        self.status = self.STATUS_READY_DOWNLOAD
        self.status_text = _("Dependencies analyzing")

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def download_wait(self):
        self.status = self.STATUS_WAIT_DOWNLOAD
        self.status_text = _("Waiting for download")

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def download_start(self):
        self.status = self.STATUS_IN_DOWNLOAD
        self.status_text = _("Downloading")

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def download_failed(self):
        self.status = self.STATUS_DOWNLOAD_FAILED
        self.status_text = _("Download failed")

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def download_update(self, percent, speed):
        self.status = self.STATUS_IN_DOWNLOAD
        self.progress_buffer.progress = percent
        self.status_text = "%s/s" % (format_file_size(speed))

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def download_finish(self):
        self.status = self.STATUS_WAIT_INSTALL
        self.progress_buffer.progress = 0
        self.status_text = _("Waiting for installation")

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def download_stop(self):
        pass

    def download_parse_failed(self):
        self.status = self.STATUS_PARSE_DOWNLOAD_FAILED
        self.status_text = _("Dependencies analysis failed")

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

        global_event.emit("request-clear-failed-action", self.pkg_name, ACTION_INSTALL)

    def action_start(self):
        self.status = self.STATUS_IN_INSTALL
        self.status_text = _("Installing")

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def action_update(self, percent):
        self.status = self.STATUS_IN_INSTALL
        self.status_text = _("Installing")
        self.progress_buffer.progress = percent

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def action_finish(self):
        self.status = self.STATUS_INSTALL_FINISH
        self.progress_buffer.progress = 100
        self.status_text = _("Installation completed")

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

        self.data_manager.get_pkg_installed(self.pkg_name, self.handle_pkg_status)

    def is_in_name_area(self, column, offset_x, offset_y):
        (name_width, name_height) = get_content_size(self.alias_name, NAME_SIZE)
        return (column == 0
                and is_in_rect((offset_x, offset_y),
                               (ITEM_PADDING_X + ITEM_PKG_OFFSET_X + ICON_SIZE + ITEM_PADDING_MIDDLE,
                                ITEM_PADDING_Y,
                                name_width,
                                NAME_SIZE)))

    def is_in_icon_area(self, column, offset_x, offset_y):
        return (column == 0
                and self.icon_pixbuf != None
                and is_in_rect((offset_x, offset_y),
                               (ITEM_PADDING_X + ITEM_PKG_OFFSET_X,
                                ITEM_PADDING_Y,
                                self.icon_pixbuf.get_width(),
                                self.icon_pixbuf.get_height())))

    def release_resource(self):
        '''
        Release item resource.

        If you have pixbuf in item, you should release memory resource like below code:

        >>> if self.pixbuf:
        >>>     del self.pixbuf
        >>>     self.pixbuf = None
        >>>
        >>> return True

        This is TreeView interface, you should implement it.

        @return: Return True if do release work, otherwise return False.

        When this function return True, TreeView will call function gc.collect() to release object to release memory.
        '''
        if self.icon_pixbuf:
            del self.icon_pixbuf
            self.icon_pixbuf = None

        return True
class InstallItem(TreeItem):
    '''
    class docs
    '''

    STATUS_READY_DOWNLOAD = 1
    STATUS_PARSE_DOWNLOAD_FAILED = 2
    STATUS_DOWNLOAD_FAILED = 3
    STATUS_WAIT_DOWNLOAD = 4
    STATUS_IN_DOWNLOAD = 5
    STATUS_STOP_DOWNLOAD = 6

    STATUS_WAIT_INSTALL = 7
    STATUS_STOP_WAIT_INSTALL = 8
    STATUS_IN_INSTALL = 9
    STATUS_INSTALL_FINISH = 10
    STATUS_INSTALL_FAILED = 11

    STATUS_PADDING_X = 15

    def __init__(self, pkg_name, pkg_version, data_manager):
        '''
        init docs
        '''
        TreeItem.__init__(self)
        self.pkg_name = pkg_name
        self.pkg_version = pkg_version
        self.data_manager = data_manager
        self.icon_pixbuf = None

        info = self.data_manager.get_item_pkg_info(self.pkg_name)
        self.alias_name = info[1]
        self.short_desc = info[2]
        self.star_level = get_star_level(5.0)
        self.star_buffer = DscStarBuffer(pkg_name)

        self.grade_star = 0

        self.status = self.STATUS_READY_DOWNLOAD
        self.status_text = _("Dependencies analyzing")
        self.progress_buffer = ProgressBuffer()

        button_pixbuf = app_theme.get_pixbuf(
            "button/start_normal.png").get_pixbuf()
        (self.button_width, self.button_height
         ) = button_pixbuf.get_width(), button_pixbuf.get_height()
        self.button_status = BUTTON_NORMAL

        ### TODO: is_installed status
        self.install_status = json.dumps([])
        self.desktops = []

    def handle_pkg_status(self, status, success):
        if success:
            self.install_status = str(status)
            try:
                self.desktops = json.loads(self.install_status)
                self.desktops = self.data_manager.get_pkg_desktop_info(
                    self.desktops)
            except:
                pass
            self.emit_redraw_request()
        else:
            utils.global_logger.error(
                "%s: get_pkg_installed handle_dbus_error" % self.pkg_name)
            utils.global_logger.error(status)

    def render_pkg_info(self, cr, rect):
        if self.row_index % 2 == 1:
            cr.set_source_rgba(1, 1, 1, 0.5)
            cr.rectangle(rect.x, rect.y, rect.width, rect.height)
            cr.fill()

        if self.icon_pixbuf == None:
            self.icon_pixbuf = gtk.gdk.pixbuf_new_from_file(
                get_icon_pixbuf_path(self.pkg_name))

        render_pkg_info(cr, rect, self.alias_name, self.pkg_name,
                        self.icon_pixbuf, self.pkg_version, self.short_desc,
                        ITEM_PKG_OFFSET_X)

    def render_pkg_status(self, cr, rect):
        if self.row_index % 2 == 1:
            cr.set_source_rgba(1, 1, 1, 0.5)
            cr.rectangle(rect.x, rect.y, rect.width, rect.height)
            cr.fill()

        if self.status == self.STATUS_READY_DOWNLOAD:
            draw_text(
                cr,
                self.status_text,
                rect.x + rect.width - ITEM_STATUS_TEXT_PADDING_RIGHT,
                rect.y,
                rect.width - ITEM_STAR_AREA_WIDTH - self.STATUS_PADDING_X,
                ITEM_HEIGHT,
            )
        elif self.status == self.STATUS_WAIT_DOWNLOAD:
            # Draw star.
            self.star_buffer.render(
                cr,
                gtk.gdk.Rectangle(rect.x, rect.y, ITEM_STAR_AREA_WIDTH,
                                  ITEM_HEIGHT))

            draw_text(
                cr,
                self.status_text,
                rect.x + rect.width - ITEM_STATUS_TEXT_PADDING_RIGHT,
                rect.y,
                rect.width - ITEM_STAR_AREA_WIDTH - self.STATUS_PADDING_X,
                ITEM_HEIGHT,
            )

            pixbuf = app_theme.get_pixbuf("button/stop.png").get_pixbuf()
            draw_pixbuf(
                cr,
                pixbuf,
                rect.x + rect.width - ITEM_CANCEL_BUTTON_PADDING_RIGHT,
                rect.y + (rect.height - pixbuf.get_height()) / 2,
            )
        elif self.status == self.STATUS_IN_DOWNLOAD:
            self.progress_buffer.render(
                cr,
                gtk.gdk.Rectangle(
                    rect.x, rect.y + (ITEM_HEIGHT - PROGRESSBAR_HEIGHT) / 2,
                    ITEM_STAR_AREA_WIDTH, PROGRESSBAR_HEIGHT))

            draw_text(
                cr,
                self.status_text,
                rect.x + rect.width - ITEM_STATUS_TEXT_PADDING_RIGHT,
                rect.y,
                rect.width - ITEM_STAR_AREA_WIDTH - self.STATUS_PADDING_X,
                ITEM_HEIGHT,
            )

            pixbuf = app_theme.get_pixbuf("button/stop.png").get_pixbuf()
            draw_pixbuf(
                cr,
                pixbuf,
                rect.x + rect.width - ITEM_CANCEL_BUTTON_PADDING_RIGHT,
                rect.y + (rect.height - pixbuf.get_height()) / 2,
            )
        elif self.status == self.STATUS_STOP_DOWNLOAD:
            # Draw star.
            self.star_buffer.render(
                cr,
                gtk.gdk.Rectangle(rect.x, rect.y, ITEM_STAR_AREA_WIDTH,
                                  ITEM_HEIGHT))

            draw_text(
                cr,
                self.status_text,
                rect.x + rect.width - ITEM_STATUS_TEXT_PADDING_RIGHT,
                rect.y,
                rect.width - ITEM_STAR_AREA_WIDTH - self.STATUS_PADDING_X,
                ITEM_HEIGHT,
            )
        elif self.status == self.STATUS_DOWNLOAD_FAILED:
            # Draw star.
            draw_text(
                cr,
                self.status_text,
                rect.x,
                rect.y,
                get_content_size(self.status_text)[0],
                ITEM_HEIGHT,
                text_color="#ff0000",
            )

            if self.button_status == BUTTON_NORMAL:
                status = "normal"
            elif self.button_status == BUTTON_HOVER:
                status = "hover"
            elif self.button_status == BUTTON_PRESS:
                status = "press"

            pixbuf = app_theme.get_pixbuf("button/install_%s.png" %
                                          (status, )).get_pixbuf()
            draw_pixbuf(
                cr, pixbuf, rect.x + rect.width - ITEM_BUTTON_PADDING_RIGHT -
                pixbuf.get_width(),
                rect.y + (rect.height - pixbuf.get_height()) / 2)

        elif self.status == self.STATUS_WAIT_INSTALL:
            self.progress_buffer.render(
                cr,
                gtk.gdk.Rectangle(
                    rect.x, rect.y + (ITEM_HEIGHT - PROGRESSBAR_HEIGHT) / 2,
                    ITEM_STAR_AREA_WIDTH, PROGRESSBAR_HEIGHT))

            draw_text(
                cr,
                self.status_text,
                rect.x + rect.width - ITEM_STATUS_TEXT_PADDING_RIGHT,
                rect.y,
                rect.width - ITEM_STAR_AREA_WIDTH - self.STATUS_PADDING_X,
                ITEM_HEIGHT,
            )

            pixbuf = app_theme.get_pixbuf("button/stop.png").get_pixbuf()
            draw_pixbuf(
                cr,
                pixbuf,
                rect.x + rect.width - ITEM_CANCEL_BUTTON_PADDING_RIGHT,
                rect.y + (rect.height - pixbuf.get_height()) / 2,
            )
        elif self.status == self.STATUS_STOP_WAIT_INSTALL:
            # Draw star.
            self.star_buffer.render(
                cr,
                gtk.gdk.Rectangle(rect.x, rect.y, ITEM_STAR_AREA_WIDTH,
                                  ITEM_HEIGHT))

            draw_text(
                cr,
                self.status_text,
                rect.x + rect.width - ITEM_STATUS_TEXT_PADDING_RIGHT,
                rect.y,
                rect.width - ITEM_STAR_AREA_WIDTH - self.STATUS_PADDING_X,
                ITEM_HEIGHT,
            )
        elif self.status == self.STATUS_IN_INSTALL:
            self.progress_buffer.render(
                cr,
                gtk.gdk.Rectangle(
                    rect.x, rect.y + (ITEM_HEIGHT - PROGRESSBAR_HEIGHT) / 2,
                    ITEM_STAR_AREA_WIDTH, PROGRESSBAR_HEIGHT))

            draw_text(
                cr,
                self.status_text,
                rect.x + rect.width - ITEM_STATUS_TEXT_PADDING_RIGHT,
                rect.y,
                rect.width - ITEM_STAR_AREA_WIDTH - self.STATUS_PADDING_X,
                ITEM_HEIGHT,
            )
        elif self.status == self.STATUS_INSTALL_FINISH:
            # Draw star.
            self.star_buffer.render(
                cr,
                gtk.gdk.Rectangle(rect.x, rect.y, ITEM_STAR_AREA_WIDTH,
                                  ITEM_HEIGHT))

            # Draw button.
            name = ""
            draw_str = ""
            if self.install_status == "uninstalled":
                name = "button/install"
            elif self.install_status == "unknown":
                draw_str = _("Installed")
            else:
                if self.desktops:
                    name = "button/start"
                else:
                    draw_str = _("Installed")

            if name:
                if self.button_status == BUTTON_NORMAL:
                    status = "normal"
                elif self.button_status == BUTTON_HOVER:
                    status = "hover"
                elif self.button_status == BUTTON_PRESS:
                    status = "press"

                pixbuf = app_theme.get_pixbuf("%s_%s.png" %
                                              (name, status)).get_pixbuf()
                draw_pixbuf(
                    cr,
                    pixbuf,
                    rect.x + rect.width - ITEM_BUTTON_PADDING_RIGHT -
                    pixbuf.get_width(),
                    rect.y + (ITEM_HEIGHT - self.button_height) / 2,
                )
            else:
                str_width, str_height = get_content_size(draw_str, 10)
                draw_text(
                    cr,
                    draw_str,
                    rect.x + rect.width - ITEM_BUTTON_PADDING_RIGHT -
                    str_width,
                    rect.y + (rect.height - str_height) / 2,
                    str_width,
                    str_height,
                    alignment=pango.ALIGN_CENTER,
                )
        elif self.status == self.STATUS_PARSE_DOWNLOAD_FAILED:
            # Draw star.
            self.star_buffer.render(
                cr,
                gtk.gdk.Rectangle(rect.x, rect.y, ITEM_STAR_AREA_WIDTH,
                                  ITEM_HEIGHT))

            draw_text(
                cr,
                self.status_text,
                rect.x + rect.width - ITEM_STATUS_TEXT_PADDING_RIGHT,
                rect.y,
                rect.width - ITEM_STAR_AREA_WIDTH - self.STATUS_PADDING_X,
                ITEM_HEIGHT,
            )

    def get_height(self):
        return ITEM_HEIGHT

    def get_column_widths(self):
        return [
            ITEM_INFO_AREA_WIDTH, ITEM_STAR_AREA_WIDTH + ITEM_BUTTON_AREA_WIDTH
        ]

    def get_column_renders(self):
        return [self.render_pkg_info, self.render_pkg_status]

    def unselect(self):
        pass

    def select(self):
        pass

    def unhover(self, column, offset_x, offset_y):
        pass

    def hover(self, column, offset_x, offset_y):
        pass

    def motion_notify(self, column, offset_x, offset_y):
        if column == 0:
            if self.is_in_icon_area(column, offset_x, offset_y):
                global_event.emit("set-cursor", gtk.gdk.HAND2)
            elif self.is_in_name_area(column, offset_x, offset_y):
                global_event.emit("set-cursor", gtk.gdk.HAND2)
            else:
                global_event.emit("set-cursor", None)
        else:
            if self.status == self.STATUS_INSTALL_FINISH:
                if self.desktops:
                    if self.is_in_button_area(column, offset_x, offset_y):
                        self.button_status = BUTTON_HOVER

                        if self.redraw_request_callback:
                            self.redraw_request_callback(self)
                    elif self.button_status != BUTTON_NORMAL:
                        self.button_status = BUTTON_NORMAL

                        if self.redraw_request_callback:
                            self.redraw_request_callback(self)

                if self.is_in_star_area(column, offset_x, offset_y):
                    global_event.emit("set-cursor", gtk.gdk.HAND2)

                    times = offset_x / STAR_SIZE
                    self.grade_star = times * 2 + 2

                    self.grade_star = min(self.grade_star, 10)
                    self.star_buffer.star_level = self.grade_star

                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)
                else:
                    global_event.emit("set-cursor", None)

                    if self.star_buffer.star_level != self.star_level:
                        self.star_buffer.star_level = self.star_level

                        if self.redraw_request_callback:
                            self.redraw_request_callback(self)

            elif self.status == self.STATUS_DOWNLOAD_FAILED:
                if self.is_in_button_area(column, offset_x, offset_y):
                    self.button_status = BUTTON_HOVER
                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)
                elif self.button_status != BUTTON_NORMAL:
                    self.button_status = BUTTON_NORMAL
                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)

            elif self.status == self.STATUS_READY_DOWNLOAD:
                if self.is_in_star_area(column, offset_x, offset_y):
                    global_event.emit("set-cursor", gtk.gdk.HAND2)

                    times = offset_x / STAR_SIZE
                    self.grade_star = times * 2 + 2

                    self.grade_star = min(self.grade_star, 10)
                    self.star_buffer.star_level = self.grade_star

                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)
                else:
                    global_event.emit("set-cursor", None)

                    if self.star_buffer.star_level != self.star_level:
                        self.star_buffer.star_level = self.star_level

                        if self.redraw_request_callback:
                            self.redraw_request_callback(self)

    def button_press(self, column, offset_x, offset_y):
        if column == 0:
            if self.is_in_icon_area(column, offset_x, offset_y):
                global_event.emit("switch-to-detail-page", self.pkg_name)
                global_event.emit("set-cursor", None)
            elif self.is_in_name_area(column, offset_x, offset_y):
                global_event.emit("switch-to-detail-page", self.pkg_name)
                global_event.emit("set-cursor", None)
        else:
            if self.status == self.STATUS_WAIT_DOWNLOAD:
                if self.is_stop_button_can_click(column, offset_x, offset_y):
                    self.status = self.STATUS_STOP_DOWNLOAD
                    self.status_text = _("Download was interrupted")
                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)
                    global_event.emit("remove-wait-download", [self.pkg_name])
                    global_event.emit("request-stop-install-actions",
                                      [self.pkg_name])
                elif self.is_in_star_area(column, offset_x, offset_y):
                    global_event.emit("grade-pkg", self.pkg_name,
                                      self.grade_star)

            elif self.status == self.STATUS_IN_DOWNLOAD:
                if self.is_stop_button_can_click(column, offset_x, offset_y):
                    self.status = self.STATUS_STOP_DOWNLOAD
                    self.status_text = _("Download was interrupted")
                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)
                    global_event.emit("stop-download-pkg", [self.pkg_name])
                    global_event.emit("request-stop-install-actions",
                                      [self.pkg_name])

            elif self.status == self.STATUS_DOWNLOAD_FAILED:
                if self.is_in_button_area(column, offset_x, offset_y):
                    global_event.emit("install-pkg", [self.pkg_name])

            elif self.status == self.STATUS_WAIT_INSTALL:
                if self.is_stop_button_can_click(column, offset_x, offset_y):
                    self.status = self.STATUS_STOP_WAIT_INSTALL
                    self.status_text = _("Installation was interrupted")
                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)
                    global_event.emit("remove-wait-action",
                                      [(self.pkg_name, ACTION_INSTALL)])
                    global_event.emit("request-stop-install-actions",
                                      [self.pkg_name])

            elif self.status == self.STATUS_INSTALL_FINISH:
                if self.is_in_star_area(column, offset_x, offset_y):
                    global_event.emit("grade-pkg", self.pkg_name,
                                      self.grade_star)
                elif self.is_in_button_area(column, offset_x, offset_y):
                    if self.desktops:
                        global_event.emit(
                            "start-pkg", self.alias_name, self.desktops,
                            self.get_offset_with_button(offset_x, offset_y))

    def get_offset_with_button(self, offset_x, offset_y):
        pixbuf = app_theme.get_pixbuf("button/start_normal.png").get_pixbuf()
        popup_x = self.get_column_widths(
        )[1] - ITEM_BUTTON_PADDING_RIGHT - pixbuf.get_width() / 2
        popup_y = (ITEM_HEIGHT - pixbuf.get_height()) / 2
        return (offset_x, offset_y, popup_x, popup_y)

    def button_release(self, column, offset_x, offset_y):
        pass

    def single_click(self, column, offset_x, offset_y):
        pass

    def double_click(self, column, offset_x, offset_y):
        pass

    def is_in_star_area(self, column, offset_x, offset_y):
        return (column == 1 and is_in_rect(
            (offset_x, offset_y),
            (0,
             (ITEM_HEIGHT - STAR_SIZE) / 2, ITEM_STAR_AREA_WIDTH, STAR_SIZE)))

    def is_in_button_area(self, column, offset_x, offset_y):
        return (column == 1 and is_in_rect(
            (offset_x, offset_y),
            (self.get_column_widths()[column] - ITEM_BUTTON_PADDING_RIGHT -
             self.button_width, (ITEM_HEIGHT - self.button_height) / 2,
             self.button_width, self.button_height)))

    def is_stop_button_can_click(self, column, offset_x, offset_y):
        pixbuf = app_theme.get_pixbuf("button/stop.png").get_pixbuf()
        return (column == 1 and is_in_rect(
            (offset_x, offset_y), (self.get_column_widths()[column] -
                                   ITEM_CANCEL_BUTTON_PADDING_RIGHT,
                                   (ITEM_HEIGHT - pixbuf.get_height()) / 2,
                                   pixbuf.get_width(), pixbuf.get_height())))

    def download_ready(self):
        self.status = self.STATUS_READY_DOWNLOAD
        self.status_text = _("Dependencies analyzing")

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def download_wait(self):
        self.status = self.STATUS_WAIT_DOWNLOAD
        self.status_text = _("Waiting for download")

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def download_start(self):
        self.status = self.STATUS_IN_DOWNLOAD
        self.status_text = _("Downloading")

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def download_failed(self):
        self.status = self.STATUS_DOWNLOAD_FAILED
        self.status_text = _("Download failed")

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def download_update(self, percent, speed):
        self.status = self.STATUS_IN_DOWNLOAD
        self.progress_buffer.progress = percent
        self.status_text = "%s/s" % (format_file_size(speed))

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def download_finish(self):
        self.status = self.STATUS_WAIT_INSTALL
        self.progress_buffer.progress = 0
        self.status_text = _("Waiting for installation")

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def download_stop(self):
        pass

    def download_parse_failed(self):
        self.status = self.STATUS_PARSE_DOWNLOAD_FAILED
        self.status_text = _("Dependencies analysis failed")

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

        global_event.emit("request-clear-failed-action", self.pkg_name,
                          ACTION_INSTALL)

    def action_start(self):
        self.status = self.STATUS_IN_INSTALL
        self.status_text = _("Installing")

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def action_update(self, percent):
        self.status = self.STATUS_IN_INSTALL
        self.status_text = _("Installing")
        self.progress_buffer.progress = percent

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def action_finish(self):
        self.status = self.STATUS_INSTALL_FINISH
        self.progress_buffer.progress = 100
        self.status_text = _("Installation completed")

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

        self.data_manager.get_pkg_installed(self.pkg_name,
                                            self.handle_pkg_status)

    def is_in_name_area(self, column, offset_x, offset_y):
        (name_width, name_height) = get_content_size(self.alias_name,
                                                     NAME_SIZE)
        return (column == 0 and is_in_rect(
            (offset_x, offset_y),
            (ITEM_PADDING_X + ITEM_PKG_OFFSET_X + ICON_SIZE +
             ITEM_PADDING_MIDDLE, ITEM_PADDING_Y, name_width, NAME_SIZE)))

    def is_in_icon_area(self, column, offset_x, offset_y):
        return (column == 0 and self.icon_pixbuf != None and is_in_rect(
            (offset_x, offset_y),
            (ITEM_PADDING_X + ITEM_PKG_OFFSET_X, ITEM_PADDING_Y,
             self.icon_pixbuf.get_width(), self.icon_pixbuf.get_height())))

    def release_resource(self):
        '''
        Release item resource.

        If you have pixbuf in item, you should release memory resource like below code:

        >>> if self.pixbuf:
        >>>     del self.pixbuf
        >>>     self.pixbuf = None
        >>>
        >>> return True

        This is TreeView interface, you should implement it.

        @return: Return True if do release work, otherwise return False.

        When this function return True, TreeView will call function gc.collect() to release object to release memory.
        '''
        if self.icon_pixbuf:
            del self.icon_pixbuf
            self.icon_pixbuf = None

        return True
class TranscoderJob(gobject.GObject):

    __gsignals__ = {
        "end": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
        "redraw-request": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
    }

    __stop = False
    __pause = False
    __is_alive = True
    priority = 10

    def __init__(self, trans_data):
        gobject.GObject.__init__(self)
        self.__updater_id = None

        # Init data.
        self.angle = 0
        self.status_icon = app_theme.get_pixbuf(
            "transcoder/wait.png").get_pixbuf()
        self.status_icon_press = app_theme.get_pixbuf(
            "transcoder/wait_press.png").get_pixbuf()
        self.stop_icon = app_theme.get_pixbuf(
            "transcoder/stop.png").get_pixbuf()
        self.progress_ratio = 0.0
        self.skip_flag = False
        self.trans_data = trans_data
        self.init_transcoder(trans_data)
        self.__update()
        self.progress_buffer = ProgressBuffer()

    def start(self):
        if self.skip_flag:
            self.finish_job()
        else:
            self.transcoder.start_transcode()
            self.__set_status_icon("working")
            self.__updater_id = gobject.timeout_add(500, self.update_progress)

    def update_progress(self):
        self.set_progress_ratio(self.transcoder.get_ratio())
        return True

    def __update(self):
        self.title = self.trans_data["song"].get_str("title")

        self.status_icon_padding_x = 10
        self.status_icon_padding_y = 5
        self.status_icon_w, self.status_icon_h = (
            self.status_icon.get_width(), self.status_icon.get_height())

        self.title_padding_x = 5
        self.title_padding_y = 5
        self.title_w, self.title_h = get_content_size(self.title,
                                                      DEFAULT_FONT_SIZE)

        self.progress_padding_x = 10
        self.progress_padding_y = 5
        self.progress_w, self.progress_h = 100, 10

        self.stop_icon_padding_x = 5
        self.stop_icon_padding_y = 5
        self.stop_icon_w, self.stop_icon_h = (self.stop_icon.get_width(),
                                              self.stop_icon.get_height())

        self.ext_padding_x = 5
        self.ext_padding_y = 5
        self.ext_w, self.ext_h = get_content_size(self.output_ext,
                                                  DEFAULT_FONT_SIZE)

    def init_transcoder(self, attr):
        self.raw_song = attr["song"]
        self.output_path = attr["output"]
        if os.path.exists(self.output_path) and attr["prompt"]:
            self.exists_prompt = True
        else:
            self.exists_prompt = False

        if self.raw_song.get_path() == self.output_path:
            self.skip_flag = True

        self.output_ext = FORMATS[attr["format"]]["extension"]
        self.transcoder = Transcoder()
        self.transcoder.set_format(attr["format"])
        self.transcoder.set_quality(attr["quality"])
        if self.raw_song.get_type() == "audiocd":
            self.transcoder.set_cd_input(self.raw_song.get("uri"))
        else:
            self.transcoder.set_input(attr["song"].get_path())
        self.transcoder.set_output(self.output_path)
        self.transcoder.end_cb = self.__end

    def __end(self):
        try:
            self.write_tags()
        except:
            pass

        self.finish_job()

    def finish_job(self):
        self.emit("end")
        self.__set_status_icon("success")
        self.set_progress_ratio(1.0)
        try:
            gobject.source_remove(self.__updater_id)
        except:
            pass

        if self.trans_data["to_playlist"]:
            tags = {"uri": utils.get_uri_from_path(self.output_path)}
            song = MediaDB.get_or_create_song(tags,
                                              "local",
                                              read_from_file=True)
            if song:
                Dispatcher.add_songs([song])

    def write_tags(self):
        tags = deepcopy(self.raw_song)
        tags["uri"] = utils.get_uri_from_path(self.output_path)
        s = Song()
        s.init_from_dict(tags)
        s.write_to_file()

    @property
    def is_running(self):
        return self.transcoder.running

    @property
    def is_finish(self):
        return self.transcoder.is_eos

    def force_stop(self):
        try:
            gobject.source_remove(self.__updater_id)
        except:
            pass

        if self.transcoder.running:
            self.transcoder.pause()
        try:
            os.unlink(self.output_path)
        except:
            pass

    def playpause(self):
        if self.transcoder.running:
            if self.transcoder.is_pause:
                self.transcoder.playing()
                self.__updater_id = gobject.timeout_add(
                    500, self.update_progress)
                return True
            else:
                try:
                    gobject.source_remove(self.__updater_id)
                except:
                    pass
                self.transcoder.pause()
                return True
        return False

    def set_progress_ratio(self, value):
        self.progress_ratio = value
        self.emit_redraw_request()

    def set_index(self, index):
        '''Update index.'''
        self.index = index

    def get_index(self):
        '''Get index.'''
        return self.index

    def __set_status_icon(self, name):
        self.status_icon = app_theme.get_pixbuf("transcoder/%s.png" %
                                                name).get_pixbuf()
        self.status_icon_press = app_theme.get_pixbuf(
            "transcoder/%s_press.png" % name).get_pixbuf()
        self.emit_redraw_request()

    def set_error_status(self):
        self.__set_status_icon("error")

    def emit_redraw_request(self):
        '''Emit redraw-request signal.'''
        self.emit("redraw-request")

    def render_icon(self, cr, rect, in_select, in_highlight):
        icon_x = rect.x + self.status_icon_padding_x
        icon_y = rect.y + (rect.height - self.status_icon_h) / 2
        if in_select:
            draw_pixbuf(cr, self.status_icon_press, icon_x, icon_y)
        else:
            draw_pixbuf(cr, self.status_icon, icon_x, icon_y)

    def render_title(self, cr, rect, in_select, in_highlight):
        rect.x += self.title_padding_x
        rect.width -= self.title_padding_x * 2
        render_item_text2(cr, self.title, rect, in_select, in_highlight)

    def render_progress(self, cr, rect, in_select, in_highlight):
        self.progress_buffer.progress = self.progress_ratio * 100
        progress_x = rect.x + self.progress_padding_x
        progress_y = rect.y + (rect.height - self.progress_h) / 2
        progress_rect = gtk.gdk.Rectangle(progress_x, progress_y,
                                          self.progress_w, self.progress_h)
        self.progress_buffer.render(cr, progress_rect)

    def render_stop(self, cr, rect, in_select, in_highlight):
        icon_x = rect.x + self.stop_icon_padding_x
        icon_y = rect.y + (rect.height - self.stop_icon_h) / 2
        draw_pixbuf(cr, self.stop_icon, icon_x, icon_y)

    def render_ext(self, cr, rect, in_select, in_highlight):
        rect.x += self.ext_padding_x
        rect.width -= self.ext_padding_x * 2
        render_item_text2(cr, self.output_ext.upper(), rect, in_select,
                          in_highlight)

    def get_ext_type(self):
        gio_file = gio.File(self.output_path)
        gio_file_info = gio_file.query_info(",".join([
            gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
            gio.FILE_ATTRIBUTE_STANDARD_TYPE,
            gio.FILE_ATTRIBUTE_STANDARD_NAME,
            gio.FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
            gio.FILE_ATTRIBUTE_STANDARD_SIZE,
            gio.FILE_ATTRIBUTE_STANDARD_ICON,
            gio.FILE_ATTRIBUTE_TIME_MODIFIED,
            gio.FILE_ATTRIBUTE_TIME_CHANGED,
        ]))

        info_attr = gio_file_info.get_attribute_as_string(
            gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE)
        return gio.content_type_get_description(info_attr)

    def get_column_sizes(self):
        return [
            (36, self.status_icon_h + self.status_icon_padding_y * 2),
            (135, self.title_h + self.title_padding_y * 2),
            (120, self.progress_h + self.progress_padding_y * 2),
            # (26, self.stop_icon_h + self.stop_icon_padding_y * 2),
            (50, self.ext_h + self.ext_padding_y),
        ]

    def get_renders(self):
        return [
            self.render_icon, self.render_title, self.render_progress,
            self.render_ext
        ]
class MediaItem(gobject.GObject):
    '''List item.'''    
    __gsignals__ = {
        "redraw-request" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
    }    
    def __init__(self):
        '''Init list item.'''
        gobject.GObject.__init__(self)
        self.update()
        self.index = None
        self.path = None
        
    def get_path(self):    
        return self.path
    
    def get_name(self):    
        return self.name
    
    def set_name(self, name):    
        self.name = name
        self.emit_redraw_request()
        
    def set_text(self, text):
        if text == "Done Transcoding":
            self.set_status_icon("success")
        elif text == "show_error":    
            self.set_status_icon("error")
        elif text == "":    
            pass
        
    def set_fraction(self, value):
        if 0.0 <= value <= 1.0:
            self.progress_ratio = value
            self.emit_redraw_request()
        
    def set_format(self, _format):        
        self.format = _format
        self.emit_redraw_request()
        
    def set_status_icon(self, name):
        if name == "wait":
            self.status_icon = self.wait_icon
            self.status_icon_press = self.wait_icon_press
        elif name == "stop":
            self.status_icon =  self.stop_icon
            self.status_icon_press = self.stop_icon_press
        elif name == "success":
            self.status_icon =  self.success_icon
            self.status_icon_press = self.success_icon_press
        elif name == "working":
            self.status_icon = self.working_icon
            self.status_icon_press = self. working_icon_press
        elif name == "error":
            self.status_icon = self.error_icon
            self.status_icon_press = self. error_icon_press
        
        self.emit_redraw_request()
        
    def set_index(self, index):
        '''Update index.'''
        self.index = index
        
    def get_index(self):
        '''Get index.'''
        return self.index
        
    def emit_redraw_request(self):
        '''Emit redraw-request signal.'''
        self.emit("redraw-request")
        
    def update(self, title="icon_pixbuf", name="Linux deepin", length="progress_buffer", _format="rmvb"):
        '''Update.'''
        # Update.
        self.title = title
        self.name = name
        self.length = length
        self.format = _format
        
        # Calculate item size.
        # self.title_padding_x = 10
        # self.title_padding_y = 5
        # (self.title_width, self.title_height) = get_content_size(self.title, DEFAULT_FONT_SIZE) #DEFAULT_FONT_SIZE
        # self.title_width = 20
        
        self.name_padding_x = 10
        self.name_padding_y = 5
        (self.name_width, self.name_height) = get_content_size(self.name, DEFAULT_FONT_SIZE) #DEFAULT_FONT_SIZE
        self.name_width = 80
        
        self.length_padding_x = 50
        self.length_padding_y = 5
        (self.length_width, self.length_height) = get_content_size(self.length, DEFAULT_FONT_SIZE) #DEFAULT_FONT_SIZE
        
        self.format_padding_x = 0
        self.format_padding_y = 5
        (self.format_width, self.format_height) = get_content_size(self.format, 8) #DEFAULT_FONT_SIZE        
        
        # ProgressBar buffer.
        self.progress_ratio = 0.0
        # self.progress_padding_x = 30
        self.progress_padding_x = 25
        self.progress_padding_y = 5
        self.progress_w, self.progress_h = 100, 10
        self.progress_buffer = ProgressBuffer()
        
        self.status_icon = None
        self.status_icon_press = None
        self.wait_icon = app_theme.get_pixbuf("transcoder/wait.png").get_pixbuf()
        self.wait_icon_press = app_theme.get_pixbuf("transcoder/wait_press.png").get_pixbuf()
        # stop conv.
        self.stop_icon = app_theme.get_pixbuf("transcoder/stop.png").get_pixbuf()        
        self.stop_icon_press = app_theme.get_pixbuf("transcoder/stop_press.png").get_pixbuf()
        # conv success.
        self.success_icon = app_theme.get_pixbuf("transcoder/success.png").get_pixbuf()
        self.success_icon_press = app_theme.get_pixbuf("transcoder/success_press.png").get_pixbuf()
        # staring conv.
        self.working_icon = app_theme.get_pixbuf("transcoder/working.png").get_pixbuf()
        self.working_icon_press = app_theme.get_pixbuf("transcoder/working_press.png").get_pixbuf()
        # error .
        self.error_icon = app_theme.get_pixbuf("transcoder/error.png").get_pixbuf()
        self.error_icon_press = app_theme.get_pixbuf("transcoder/error_press.png").get_pixbuf()
        # Init icon state.        
        self.set_status_icon("wait")        
        # set icon[position]->> x and y.
        self.status_icon_padding_x = 5
        self.status_icon_padding_y = 5        
        # get icon width and height .
        self.status_icon_w, self.status_icon_h = (self.status_icon.get_width(), self.status_icon.get_height())
        
    def render_title(self, cr, rect, in_selection, in_highlight):
        '''Render title.'''
        rect.x += self.status_icon_padding_x # title_padding_x
        icon_x = rect.x + self.status_icon_padding_x # self.status_icon_padding_x
        icon_y = rect.y + (rect.height - self.status_icon_h) / 2
        if in_selection:
            draw_pixbuf(cr, self.status_icon_press, icon_x, icon_y)
        else:    
            draw_pixbuf(cr, self.status_icon, icon_x, icon_y)
        
    def render_name(self, cr, rect, in_selection, in_highlight):            
        rect.x += self.name_padding_x
        render_item_text(cr, self.name, rect, in_selection, in_highlight)
        
    def render_length(self, cr, rect, in_selection, in_highlight):
        '''Render length.'''
        rect.width -= self.length_padding_x
        self.progress_buffer.progress = self.progress_ratio * 100
        progress_x = rect.x + self.progress_padding_x
        progress_y = rect.y + (rect.height - self.progress_h) / 2
        progress_rect = gtk.gdk.Rectangle(progress_x, progress_y, self.progress_w, self.progress_h)
        self.progress_buffer.render(cr, progress_rect)        
        
    def render_format(self, cr, rect, in_selection, in_highlight):
        rect.x += self.format_padding_x
        render_item_text(cr, self.format, rect, in_selection, in_highlight)
        
    def get_column_sizes(self):
        '''Get sizes.'''
        return [(self.status_icon_w + self.status_icon_padding_x * 2, 
                 self.status_icon_h + self.status_icon_padding_y * 2),
                (self.name_width + self.name_padding_x * 2 + 30,                 
                 self.name_height + self.name_padding_y * 2),                
                (self.length_width + self.length_padding_x + 12,                 
                 self.length_height + self.length_padding_y * 2),
                # (self.format_width + self.format_padding_x * 2,  
                (self.format_width + 20,  
                 self.format_height + self.format_padding_y * 2),                                                
                ]    
    
    def get_renders(self):
        '''Get render callbacks.'''
        return [self.render_title,
                self.render_name,
                self.render_length,
                self.render_format]
class TaskItem(TreeItem):
    '''
    class docs
    '''
    
    STATUS_WAIT_DOWNLOAD = 2
    STATUS_IN_DOWNLOAD = 3
    STATUS_STOP = 4
    STATUS_FINISH = 5
    
    def __init__(self, image_object, finish_callback):
        '''
        init docs
        '''
        TreeItem.__init__(self)
        
        
        # Init sizes.
        self.item_height = 50        
        self.info_width = -1
        self.progressbar_width = 100
        self.progressbar_padding_x = 10
        self.progressbar_height = 12
        self.check_button_padding_x = 10
        self.info_padding_x = 5

        self.icon_pixbuf = None
        self.image_object = image_object        
        self.button_status = BUTTON_NORMAL
        
        # Init status.
        self.status = self.STATUS_WAIT_DOWNLOAD
        self.status_text = "等待下载"
        
        # Init buffers. 
        self.progress_buffer = ProgressBuffer()
        
        
        self.stop_pixbuf = app_theme.get_pixbuf("individuation/stop.png").get_pixbuf()
        self.stop_pixbuf_padding_x = 5
        self.block_width = 50
        self.download_task = TaskObject(image_object.big_url, image_object.get_save_path(), output_temp=True)
        
        self.download_task.connect("update", self.download_update)
        self.download_task.connect("finish", self.download_finish)
        self.download_task.connect("error",  self.download_failed)
        self.download_task.connect("start",  self.download_start)
        
        self.finish_callback = finish_callback
        
        self.start_download()
        
    def start_download(self):    
        fetch_service.add_missions([self.download_task])
        
        
    def render_info(self, cr, rect):
        if self.is_hover:
            draw_single_mask(cr, rect.x, rect.y, rect.width, rect.height, "globalItemHover")
        
        if self.icon_pixbuf is None:
            self.icon_pixbuf = cache_manager.get_small_pixbuf(self.image_object, 37, 38)
            
        icon_width = self.icon_pixbuf.get_width()
        icon_height = self.icon_pixbuf.get_height()
        icon_x = rect.x + self.info_padding_x
        icon_y = rect.y + (rect.height - icon_height) / 2
        
        
        # Draw shadow.
        drop_shadow_padding = 3
        drop_shadow_radious = 3
        draw_shadow(
            cr,
            icon_x,
            icon_y,
            icon_width + drop_shadow_padding,
            icon_height + drop_shadow_padding,
            drop_shadow_radious,
            app_theme.get_shadow_color("window_shadow")
            )

        outside_shadow_padding = 2
        outside_shadow_radious = 3
        draw_shadow(
            cr,
            icon_x - outside_shadow_padding,
            icon_y - outside_shadow_padding,
            icon_width + outside_shadow_padding * 2,
            icon_height + outside_shadow_padding * 2,
            outside_shadow_radious,
            app_theme.get_shadow_color("window_shadow")
            )
        
        # Draw wallpaper.

        draw_pixbuf(cr, self.icon_pixbuf, icon_x, icon_y)
        
            
        rect.x = icon_x + self.icon_pixbuf.get_width() + self.info_padding_x
        rect.width -= self.info_padding_x * 2 - self.icon_pixbuf.get_width()
        _width, _height = get_content_size(self.image_object.get_display_name())
        draw_text(cr, "<b>%s</b>" % self.image_object.get_display_name(), rect.x, icon_y, rect.width, _height,
                  text_size=10)                   
        
        rect.y = icon_y + icon_width - _height
        _width, _height = get_content_size(self.status_text)
        draw_text(cr, self.status_text, rect.x, rect.y, rect.width, _height)
        
    def render_progressbar(self, cr, rect):     
        if self.is_hover:
            draw_single_mask(cr, rect.x, rect.y, rect.width, rect.height, "globalItemHover")
       
        self.progress_buffer.render(cr, 
                                    gtk.gdk.Rectangle(rect.x + (rect.width - self.progressbar_width) / 2,
                                                      rect.y + (rect.height - self.progressbar_height)/ 2,
                                                      self.progressbar_width, self.progressbar_height))
        
    def render_stop(self, cr, rect):    
        if self.is_hover:
            draw_single_mask(cr, rect.x, rect.y, rect.width, rect.height, "globalItemHover")
        
        icon_x = rect.x + (rect.width - self.stop_pixbuf.get_width()) / 2
        icon_y = rect.y + (rect.height - self.stop_pixbuf.get_height()) / 2
        draw_pixbuf(cr, self.stop_pixbuf, icon_x, icon_y)
        
    def render_block(self, cr, rect):    
        if self.is_hover:
            draw_single_mask(cr, rect.x, rect.y, rect.width, rect.height, "globalItemHover")
            
    def get_column_widths(self):
        return [ 10,
                self.info_width, 
                self.progressbar_width + self.progressbar_padding_x * 2,
                self.stop_pixbuf.get_width() + self.stop_pixbuf_padding_x * 2,
                self.block_width
                ]
    
    
    def get_column_renders(self):
        return [
            self.render_block,
            self.render_info,
            self.render_progressbar,
            self.render_stop, 
            self.render_block,
            ]
    
    def get_height(self):
        return self.item_height
    
    def emit_request_redraw(self):
        if self.redraw_request_callback:
            self.redraw_request_callback(self)
            
    def unselect(self):
        pass
    
    def select(self):
        pass
    
    def unhover(self, column, offset_x, offset_y):
        self.is_hover = False
        self.emit_request_redraw()

    def hover(self, column, offset_x, offset_y):
        self.is_hover = True
        self.emit_request_redraw()
    
    def motion_notify(self, column, offset_x, offset_y):
        if column == 0:
            if self.check_button_buffer.motion_button(offset_x, offset_y):
                self.emit_request_redraw()
    
    def button_press(self, column, offset_x, offset_y):
        if column == 0:
            if self.check_button_buffer.press_button(offset_x, offset_y):
                self.emit_request_redraw()
                
    def button_release(self, column, offset_x, offset_y):
        if column == 0 and self.check_button_buffer.release_button(offset_x, offset_y):
            self.emit_request_redraw()
                    
    def single_click(self, column, offset_x, offset_y):
        pass

    def double_click(self, column, offset_x, offset_y):
        pass        
    
    @post_gui        
    def download_update(self, obj, data):
        self.progress_buffer.progress = data.progress
        speed = parse_bytes(data.speed)
        remaining = parse_time(data.remaining)
        filesize = parse_bytes(data.filesize)
        downloaded = parse_bytes(data.downloaded)
        self.status_text = "%s/s - %s, 共%s,  还有 %s " % (speed, downloaded, filesize, remaining)
        self.emit_request_redraw()

    @post_gui        
    def download_finish(self, obj, data):
        event_manager.emit("download-finish", obj)
        self.progress_buffer.progress = 100
        self.status_text = "下载完成"
        self.emit_request_redraw()
        
        if self.finish_callback:
            self.finish_callback(self)
            
    @post_gui        
    def download_failed(self, obj, data):
        self.status_text = data
        self.emit_request_redraw()
        
    @post_gui    
    def download_start(self, obj, data):
        event_manager.emit("download-start", obj)
        self.status_text = "开始下载"
        self.emit_request_redraw()

    def download_stop(self):
        pass
            
            
    def release_resource(self):
        '''
        Release item resource.

        If you have pixbuf in item, you should release memory resource like below code:

        >>> if self.pixbuf:
        >>>     del self.pixbuf
        >>>     self.pixbuf = None
        >>>
        >>> return True

        This is TreeView interface, you should implement it.
        
        @return: Return True if do release work, otherwise return False.
        
        When this function return True, TreeView will call function gc.collect() to release object to release memory.
        '''
        if self.icon_pixbuf:
            del self.icon_pixbuf
            self.icon_pixbuf = None

        return True    
class TranscoderJob(gobject.GObject):
    
    __gsignals__ = {
        "end"            : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
        "redraw-request" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
        }
    
    __stop     = False
    __pause    = False
    __is_alive = True
    priority   = 10
    
    def __init__(self, trans_data):
        gobject.GObject.__init__(self)
        self.__updater_id = None
        
        # Init data.
        self.angle = 0
        self.status_icon = app_theme.get_pixbuf("transcoder/wait.png").get_pixbuf()
        self.status_icon_press = app_theme.get_pixbuf("transcoder/wait_press.png").get_pixbuf()
        self.stop_icon = app_theme.get_pixbuf("transcoder/stop.png").get_pixbuf()
        self.progress_ratio = 0.0
        self.skip_flag = False        
        self.trans_data = trans_data
        self.init_transcoder(trans_data)   
        self.__update()        
        self.progress_buffer = ProgressBuffer()

    def start(self):    
        if self.skip_flag:
            self.finish_job()
        else:    
            self.transcoder.start_transcode()
            self.__set_status_icon("working")
            self.__updater_id = gobject.timeout_add(500, self.update_progress)
        
    def update_progress(self):    
        self.set_progress_ratio(self.transcoder.get_ratio())
        return True
    
    def __update(self):    
        self.title = self.trans_data["song"].get_str("title")
        
        self.status_icon_padding_x = 10
        self.status_icon_padding_y = 5
        self.status_icon_w, self.status_icon_h = (self.status_icon.get_width(), self.status_icon.get_height())
        
        self.title_padding_x = 5
        self.title_padding_y = 5
        self.title_w, self.title_h = get_content_size(self.title, DEFAULT_FONT_SIZE)
        
        self.progress_padding_x = 10
        self.progress_padding_y = 5
        self.progress_w, self.progress_h = 100, 10
        
        self.stop_icon_padding_x = 5
        self.stop_icon_padding_y = 5
        self.stop_icon_w, self.stop_icon_h = (self.stop_icon.get_width(), self.stop_icon.get_height())
        
        self.ext_padding_x = 5
        self.ext_padding_y = 5
        self.ext_w, self.ext_h = get_content_size(self.output_ext, DEFAULT_FONT_SIZE)
        
    def init_transcoder(self, attr):
        self.raw_song = attr["song"]
        self.output_path = attr["output"]
        if os.path.exists(self.output_path) and attr["prompt"]:
            self.exists_prompt = True
        else:    
            self.exists_prompt = False
            
        if self.raw_song.get_path() == self.output_path:
            self.skip_flag = True
                        
        self.output_ext = FORMATS[attr["format"]]["extension"]
        self.transcoder = Transcoder()        
        self.transcoder.set_format(attr["format"])
        self.transcoder.set_quality(attr["quality"])
        if self.raw_song.get_type() == "audiocd":
            self.transcoder.set_cd_input(self.raw_song.get("uri"))
        else:    
            self.transcoder.set_input(attr["song"].get_path())
        self.transcoder.set_output(self.output_path)
        self.transcoder.end_cb = self.__end
        
    def __end(self):    
        try:
            self.write_tags()
        except:    
            pass
        
        self.finish_job()
                
    def finish_job(self):            
        self.emit("end")
        self.__set_status_icon("success")
        self.set_progress_ratio(1.0)
        try:
            gobject.source_remove(self.__updater_id)
        except: pass
        
        if self.trans_data["to_playlist"]:
            tags = {"uri" : utils.get_uri_from_path(self.output_path)}
            song = MediaDB.get_or_create_song(tags, "local", read_from_file=True)
            if song:
                Dispatcher.add_songs([song])
                
    def write_tags(self):    
        tags = deepcopy(self.raw_song)
        tags["uri"] = utils.get_uri_from_path(self.output_path)
        s = Song()
        s.init_from_dict(tags)
        s.write_to_file()
        
    @property    
    def is_running(self):    
        return self.transcoder.running
        
    @property
    def is_finish(self):
        return self.transcoder.is_eos
    
    def force_stop(self):    
        try:
            gobject.source_remove(self.__updater_id)
        except: pass    
        
        if self.transcoder.running:
            self.transcoder.pause()
        try:    
            os.unlink(self.output_path)
        except: pass
        
    def playpause(self):    
        if self.transcoder.running:
            if self.transcoder.is_pause:
                self.transcoder.playing()
                self.__updater_id = gobject.timeout_add(500, self.update_progress)
                return True
            else:    
                try:
                    gobject.source_remove(self.__updater_id)
                except:    
                    pass
                self.transcoder.pause()
                return True
        return False    
        
    def set_progress_ratio(self, value):    
        self.progress_ratio = value
        self.emit_redraw_request()
        
    def set_index(self, index):
        '''Update index.'''
        self.index = index
        
    def get_index(self):
        '''Get index.'''
        return self.index
    
    def __set_status_icon(self, name):
        self.status_icon = app_theme.get_pixbuf("transcoder/%s.png" % name).get_pixbuf()
        self.status_icon_press = app_theme.get_pixbuf("transcoder/%s_press.png" % name).get_pixbuf()
        self.emit_redraw_request()
        
    def set_error_status(self):    
        self.__set_status_icon("error")
        
    def emit_redraw_request(self):
        '''Emit redraw-request signal.'''
        self.emit("redraw-request")
        
    def render_icon(self, cr, rect, in_select, in_highlight):    
        icon_x = rect.x + self.status_icon_padding_x
        icon_y = rect.y + (rect.height - self.status_icon_h) / 2
        if in_select:
            draw_pixbuf(cr, self.status_icon_press, icon_x, icon_y)
        else:    
            draw_pixbuf(cr, self.status_icon, icon_x, icon_y)
    
    def render_title(self, cr, rect, in_select, in_highlight):
        rect.x += self.title_padding_x
        rect.width -= self.title_padding_x * 2
        render_item_text2(cr, self.title, rect, in_select, in_highlight)
    
    def render_progress(self, cr, rect, in_select, in_highlight):
        self.progress_buffer.progress = self.progress_ratio * 100
        progress_x = rect.x + self.progress_padding_x
        progress_y = rect.y + (rect.height - self.progress_h) / 2
        progress_rect = gtk.gdk.Rectangle(progress_x, progress_y, self.progress_w, self.progress_h)
        self.progress_buffer.render(cr, progress_rect)        
    
    def render_stop(self, cr, rect, in_select, in_highlight):
        icon_x = rect.x + self.stop_icon_padding_x
        icon_y = rect.y + (rect.height - self.stop_icon_h) / 2
        draw_pixbuf(cr, self.stop_icon, icon_x, icon_y)
        
    def render_ext(self, cr, rect, in_select, in_highlight):    
        rect.x += self.ext_padding_x
        rect.width -= self.ext_padding_x * 2
        render_item_text2(cr, self.output_ext.upper(), rect, in_select, in_highlight)
        
    def get_ext_type(self):    
        gio_file = gio.File(self.output_path)
        gio_file_info = gio_file.query_info(",".join([gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
                                                      gio.FILE_ATTRIBUTE_STANDARD_TYPE, 
                                                      gio.FILE_ATTRIBUTE_STANDARD_NAME,
                                                      gio.FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
                                                      gio.FILE_ATTRIBUTE_STANDARD_SIZE,
                                                      gio.FILE_ATTRIBUTE_STANDARD_ICON,
                                                      gio.FILE_ATTRIBUTE_TIME_MODIFIED,
                                                      gio.FILE_ATTRIBUTE_TIME_CHANGED,]))
        
        info_attr = gio_file_info.get_attribute_as_string(gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE)                
        return gio.content_type_get_description(info_attr)
    
    def get_column_sizes(self):
        return [
            (36, self.status_icon_h + self.status_icon_padding_y * 2),
            (135, self.title_h + self.title_padding_y * 2),
            (120, self.progress_h + self.progress_padding_y * 2),
            # (26, self.stop_icon_h + self.stop_icon_padding_y * 2),
            (50, self.ext_h + self.ext_padding_y),
            ]
    
    def get_renders(self):
        return [ self.render_icon, self.render_title, self.render_progress, self.render_ext]
class UninstallItem(TreeItem):
    '''
    class docs
    '''

    STATUS_NORMAL = 1
    STATUS_CONFIRM = 2
    STATUS_WAIT_ACTION = 3
    STATUS_IN_ACTION = 4

    STATUS_PADDING_X = 15

    def __init__(self, pkg_name, pkg_version, data_manager):
        '''
        init docs
        '''
        TreeItem.__init__(self)
        self.pkg_name = pkg_name
        self.pkg_version = pkg_version
        self.data_manager = data_manager
        self.icon_pixbuf = None

        info = data_manager.get_item_pkg_info(self.pkg_name)
        self.alias_name = info[1]
        self.short_desc = info[2]
        self.star_level = get_star_level(5.0)
        self.star_buffer = DscStarBuffer(pkg_name)

        self.grade_star = 0

        button_pixbuf = app_theme.get_pixbuf(
            "button/uninstall_normal.png").get_pixbuf()
        (self.button_width, self.button_height
         ) = button_pixbuf.get_width(), button_pixbuf.get_height()
        self.button_status = BUTTON_NORMAL

        self.status = self.STATUS_NORMAL
        self.status_text = ""
        self.progress_buffer = ProgressBuffer()
        #self.action_wait()

    def render_pkg_info(self, cr, rect):
        if self.row_index % 2 == 1:
            cr.set_source_rgba(1, 1, 1, 0.5)
            cr.rectangle(rect.x, rect.y, rect.width, rect.height)
            cr.fill()

        if self.icon_pixbuf == None:
            self.icon_pixbuf = gtk.gdk.pixbuf_new_from_file(
                get_icon_pixbuf_path(self.pkg_name))

        render_pkg_info(cr, rect, self.alias_name, self.pkg_name,
                        self.icon_pixbuf, self.pkg_version, self.short_desc,
                        ITEM_PKG_OFFSET_X)

    def render_pkg_status(self, cr, rect):
        if self.row_index % 2 == 1:
            cr.set_source_rgba(1, 1, 1, 0.5)
            cr.rectangle(rect.x, rect.y, rect.width, rect.height)
            cr.fill()

        if self.status == self.STATUS_CONFIRM:
            # Draw star.
            self.star_buffer.render(
                cr,
                gtk.gdk.Rectangle(rect.x, rect.y, ITEM_STAR_AREA_WIDTH,
                                  ITEM_HEIGHT))

            confirm_pixbuf = app_theme.get_pixbuf(
                "button/uninstall_confirm.png").get_pixbuf()
            cancel_pixbuf = app_theme.get_pixbuf(
                "button/uninstall_cancel.png").get_pixbuf()

            draw_pixbuf(
                cr,
                confirm_pixbuf,
                rect.x + rect.width - ITEM_CONFIRM_BUTTON_PADDING_RIGHT,
                rect.y + (ITEM_HEIGHT - confirm_pixbuf.get_height()) / 2,
            )

            draw_pixbuf(
                cr,
                cancel_pixbuf,
                rect.x + rect.width - ITEM_CANCEL_BUTTON_PADDING_RIGHT,
                rect.y + (ITEM_HEIGHT - cancel_pixbuf.get_height()) / 2,
            )
        elif self.status == self.STATUS_IN_ACTION:
            self.progress_buffer.render(
                cr,
                gtk.gdk.Rectangle(
                    rect.x, rect.y + (ITEM_HEIGHT - PROGRESSBAR_HEIGHT) / 2,
                    ITEM_STAR_AREA_WIDTH, PROGRESSBAR_HEIGHT))

            draw_text(
                cr,
                self.status_text,
                rect.x + rect.width - ITEM_STATUS_TEXT_PADDING_RIGHT,
                rect.y,
                rect.width - ITEM_STAR_AREA_WIDTH,
                ITEM_HEIGHT,
            )
        elif self.status == self.STATUS_WAIT_ACTION:
            # Draw star.
            self.star_buffer.render(
                cr,
                gtk.gdk.Rectangle(rect.x, rect.y, ITEM_STAR_AREA_WIDTH,
                                  ITEM_HEIGHT))

            draw_text(
                cr,
                self.status_text,
                rect.x + rect.width - ITEM_STATUS_TEXT_PADDING_RIGHT,
                rect.y,
                rect.width - ITEM_STAR_AREA_WIDTH,
                ITEM_HEIGHT,
            )

            cancel_pixbuf = app_theme.get_pixbuf(
                "button/uninstall_cancel.png").get_pixbuf()

            draw_pixbuf(
                cr,
                cancel_pixbuf,
                rect.x + rect.width - 50,
                rect.y + (ITEM_HEIGHT - cancel_pixbuf.get_height()) / 2,
            )
        elif self.status == self.STATUS_NORMAL:
            # Draw star.
            self.star_buffer.render(
                cr,
                gtk.gdk.Rectangle(rect.x, rect.y, ITEM_STAR_AREA_WIDTH,
                                  ITEM_HEIGHT))

            # Draw button.
            if self.button_status == BUTTON_NORMAL:
                pixbuf = app_theme.get_pixbuf(
                    "button/uninstall_normal.png").get_pixbuf()
            elif self.button_status == BUTTON_HOVER:
                pixbuf = app_theme.get_pixbuf(
                    "button/uninstall_hover.png").get_pixbuf()
            elif self.button_status == BUTTON_PRESS:
                pixbuf = app_theme.get_pixbuf(
                    "button/uninstall_press.png").get_pixbuf()
            draw_pixbuf(
                cr,
                pixbuf,
                rect.x + rect.width - ITEM_BUTTON_PADDING_RIGHT -
                pixbuf.get_width(),
                rect.y + (ITEM_HEIGHT - self.button_height) / 2,
            )

    def get_height(self):
        return ITEM_HEIGHT

    def get_column_widths(self):
        return [
            ITEM_INFO_AREA_WIDTH, ITEM_STAR_AREA_WIDTH + ITEM_BUTTON_AREA_WIDTH
        ]

    def get_column_renders(self):
        return [self.render_pkg_info, self.render_pkg_status]

    def unselect(self):
        pass

    def select(self):
        pass

    def unhover(self, column, offset_x, offset_y):
        pass

    def hover(self, column, offset_x, offset_y):
        pass

    def motion_notify(self, column, offset_x, offset_y):
        if column == 0:
            if self.is_in_icon_area(column, offset_x, offset_y):
                global_event.emit("set-cursor", gtk.gdk.HAND2)
            elif self.is_in_name_area(column, offset_x, offset_y):
                global_event.emit("set-cursor", gtk.gdk.HAND2)
            else:
                global_event.emit("set-cursor", None)
        else:
            if self.status == self.STATUS_NORMAL:
                if self.is_in_button_area(column, offset_x, offset_y):
                    self.button_status = BUTTON_HOVER

                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)
                elif self.button_status != BUTTON_NORMAL:
                    self.button_status = BUTTON_NORMAL

                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)

                if self.is_in_star_area(column, offset_x, offset_y):
                    global_event.emit("set-cursor", gtk.gdk.HAND2)

                    times = offset_x / STAR_SIZE
                    self.grade_star = times * 2 + 2

                    self.grade_star = min(self.grade_star, 10)
                    self.star_buffer.star_level = self.grade_star

                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)
                else:
                    global_event.emit("set-cursor", None)

                    if self.star_buffer.star_level != self.star_level:
                        self.star_buffer.star_level = self.star_level

                        if self.redraw_request_callback:
                            self.redraw_request_callback(self)

    def button_press(self, column, offset_x, offset_y):
        if column == 0:
            if self.is_in_icon_area(column, offset_x, offset_y):
                global_event.emit("switch-to-detail-page", self.pkg_name)
                global_event.emit("set-cursor", None)
            elif self.is_in_name_area(column, offset_x, offset_y):
                global_event.emit("switch-to-detail-page", self.pkg_name)
                global_event.emit("set-cursor", None)
        else:
            if self.status == self.STATUS_NORMAL:
                if self.is_in_button_area(column, offset_x, offset_y):
                    self.status = self.STATUS_CONFIRM

                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)
                elif self.is_in_star_area(column, offset_x, offset_y):
                    global_event.emit("grade-pkg", self.pkg_name,
                                      self.grade_star)
            elif self.status == self.STATUS_CONFIRM:
                if self.is_confirm_button_area(column, offset_x, offset_y):
                    self.status = self.STATUS_WAIT_ACTION
                    self.status_text = _("Waiting for uninstall")

                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)

                    global_event.emit("uninstall-pkg", self.pkg_name,
                                      get_purg_flag())
                elif self.is_cancel_button_area(column, offset_x, offset_y):
                    self.status = self.STATUS_NORMAL

                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)
            elif self.status == self.STATUS_WAIT_ACTION:
                if self.is_cancel_button_area(column, offset_x, offset_y):
                    self.status = self.STATUS_NORMAL

                    global_event.emit("remove-wait-action",
                                      [(self.pkg_name, ACTION_UNINSTALL)])

                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)

    def button_release(self, column, offset_x, offset_y):
        pass

    def single_click(self, column, offset_x, offset_y):
        pass

    def double_click(self, column, offset_x, offset_y):
        pass

    def is_in_star_area(self, column, offset_x, offset_y):
        return (column == 1 and is_in_rect(
            (offset_x, offset_y),
            (0,
             (ITEM_HEIGHT - STAR_SIZE) / 2, ITEM_STAR_AREA_WIDTH, STAR_SIZE)))

    def is_in_button_area(self, column, offset_x, offset_y):
        return (column == 1 and is_in_rect(
            (offset_x, offset_y),
            (self.get_column_widths()[column] - ITEM_BUTTON_PADDING_RIGHT -
             self.button_width, (ITEM_HEIGHT - self.button_height) / 2,
             self.button_width, self.button_height)))

    def is_confirm_button_area(self, column, offset_x, offset_y):
        confirm_pixbuf = app_theme.get_pixbuf(
            "button/uninstall_confirm.png").get_pixbuf()
        return (column == 1 and is_in_rect(
            (offset_x, offset_y),
            (self.get_column_widths()[column] -
             ITEM_CONFIRM_BUTTON_PADDING_RIGHT,
             (ITEM_HEIGHT - confirm_pixbuf.get_height()) / 2,
             confirm_pixbuf.get_width(), confirm_pixbuf.get_height())))

    def is_cancel_button_area(self, column, offset_x, offset_y):
        cancel_pixbuf = app_theme.get_pixbuf(
            "button/uninstall_cancel.png").get_pixbuf()
        return (column == 1 and is_in_rect(
            (offset_x, offset_y),
            (self.get_column_widths()[column] -
             ITEM_CANCEL_BUTTON_PADDING_RIGHT,
             (ITEM_HEIGHT - cancel_pixbuf.get_height()) / 2,
             cancel_pixbuf.get_width(), cancel_pixbuf.get_height())))

    def is_in_name_area(self, column, offset_x, offset_y):
        (name_width, name_height) = get_content_size(self.alias_name,
                                                     NAME_SIZE)
        return (column == 0 and is_in_rect(
            (offset_x, offset_y),
            (ITEM_PADDING_X + ITEM_PKG_OFFSET_X + ICON_SIZE +
             ITEM_PADDING_MIDDLE, ITEM_PADDING_Y, name_width, NAME_SIZE)))

    def is_in_icon_area(self, column, offset_x, offset_y):
        return (column == 0 and self.icon_pixbuf != None and is_in_rect(
            (offset_x, offset_y),
            (ITEM_PADDING_X + ITEM_PKG_OFFSET_X, ITEM_PADDING_Y,
             self.icon_pixbuf.get_width(), self.icon_pixbuf.get_height())))

    def action_wait(self):
        self.status = self.STATUS_WAIT_ACTION
        self.status_text = _("Waiting for uninstall")

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def action_start(self):
        self.status = self.STATUS_IN_ACTION
        self.status_text = _("Uninstalling")

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def action_update(self, percent):
        self.progress_buffer.progress = percent
        self.status_text = _("Uninstalling")

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def action_finish(self):
        self.progress_buffer.progress = 100
        self.status_text = _("Uninstallation completed")

        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def release_resource(self):
        '''
        Release item resource.

        If you have pixbuf in item, you should release memory resource like below code:

        >>> if self.pixbuf:
        >>>     del self.pixbuf
        >>>     self.pixbuf = None
        >>>
        >>> return True

        This is TreeView interface, you should implement it.

        @return: Return True if do release work, otherwise return False.

        When this function return True, TreeView will call function gc.collect() to release object to release memory.
        '''
        if self.icon_pixbuf:
            del self.icon_pixbuf
            self.icon_pixbuf = None

        return True
class UninstallItem(TreeItem):
    '''
    class docs
    '''
    
    STATUS_NORMAL = 1
    STATUS_CONFIRM = 2
    STATUS_WAIT_ACTION = 3
    STATUS_IN_ACTION = 4
    
    STATUS_PADDING_X = 15
	
    def __init__(self, pkg_name, pkg_version, data_manager):
        '''
        init docs
        '''
        TreeItem.__init__(self)
        self.pkg_name = pkg_name
        self.pkg_version = pkg_version
        self.data_manager = data_manager
        self.icon_pixbuf = None
        
        (self.short_desc, star, self.alias_name) = data_manager.get_item_pkg_info(self.pkg_name)
        self.star_level = get_star_level(star)
        self.star_buffer = DscStarBuffer(pkg_name)

        self.grade_star = 0
        
        button_pixbuf = app_theme.get_pixbuf("button/uninstall_normal.png").get_pixbuf()
        (self.button_width, self.button_height) = button_pixbuf.get_width(), button_pixbuf.get_height()
        self.button_status = BUTTON_NORMAL
        
        self.status = self.STATUS_NORMAL
        self.status_text = ""
        self.progress_buffer = ProgressBuffer()
        #self.action_wait()

    def render_pkg_info(self, cr, rect):
        if self.row_index % 2 == 1:
            cr.set_source_rgba(1, 1, 1, 0.5)
            cr.rectangle(rect.x, rect.y, rect.width, rect.height)
            cr.fill()
        
        if self.icon_pixbuf == None:
            self.icon_pixbuf = gtk.gdk.pixbuf_new_from_file(get_icon_pixbuf_path(self.pkg_name))        
            
        render_pkg_info(cr, rect, self.alias_name, self.pkg_name, self.icon_pixbuf, self.pkg_version, self.short_desc, ITEM_PKG_OFFSET_X)
        
    def render_pkg_status(self, cr, rect):
        if self.row_index % 2 == 1:
            cr.set_source_rgba(1, 1, 1, 0.5)
            cr.rectangle(rect.x, rect.y, rect.width, rect.height)
            cr.fill()
        
        if self.status == self.STATUS_CONFIRM:
            # Draw star.
            self.star_buffer.render(cr, gtk.gdk.Rectangle(rect.x, rect.y, ITEM_STAR_AREA_WIDTH, ITEM_HEIGHT))
            
            confirm_pixbuf = app_theme.get_pixbuf("button/uninstall_confirm.png").get_pixbuf()
            cancel_pixbuf = app_theme.get_pixbuf("button/uninstall_cancel.png").get_pixbuf()
            
            draw_pixbuf(
                cr,
                confirm_pixbuf,
                rect.x + rect.width - ITEM_CONFIRM_BUTTON_PADDING_RIGHT,
                rect.y + (ITEM_HEIGHT - confirm_pixbuf.get_height()) / 2,
                )

            draw_pixbuf(
                cr,
                cancel_pixbuf,
                rect.x + rect.width - ITEM_CANCEL_BUTTON_PADDING_RIGHT,
                rect.y + (ITEM_HEIGHT - cancel_pixbuf.get_height()) / 2,
                )
        elif self.status == self.STATUS_IN_ACTION:
            self.progress_buffer.render(
                cr, 
                gtk.gdk.Rectangle(
                    rect.x, 
                    rect.y + (ITEM_HEIGHT - PROGRESSBAR_HEIGHT) / 2, 
                    ITEM_STAR_AREA_WIDTH, 
                    PROGRESSBAR_HEIGHT))
            
            draw_text(
                cr,
                self.status_text,
                rect.x + rect.width - ITEM_STATUS_TEXT_PADDING_RIGHT,
                rect.y,
                rect.width - ITEM_STAR_AREA_WIDTH,
                ITEM_HEIGHT,
                )
        elif self.status == self.STATUS_WAIT_ACTION:
            # Draw star.
            self.star_buffer.render(cr, gtk.gdk.Rectangle(rect.x, rect.y, ITEM_STAR_AREA_WIDTH, ITEM_HEIGHT))
            
            draw_text(
                cr,
                self.status_text,
                rect.x + rect.width - ITEM_STATUS_TEXT_PADDING_RIGHT,
                rect.y,
                rect.width - ITEM_STAR_AREA_WIDTH,
                ITEM_HEIGHT,
                )
            
            cancel_pixbuf = app_theme.get_pixbuf("button/uninstall_cancel.png").get_pixbuf()
            
            draw_pixbuf(
                cr,
                cancel_pixbuf,
                rect.x + rect.width - 50,
                rect.y + (ITEM_HEIGHT - cancel_pixbuf.get_height()) / 2,
                )
        elif self.status == self.STATUS_NORMAL:
            # Draw star.
            self.star_buffer.render(cr, gtk.gdk.Rectangle(rect.x, rect.y, ITEM_STAR_AREA_WIDTH, ITEM_HEIGHT))
            
            # Draw button.
            if self.button_status == BUTTON_NORMAL:
                pixbuf = app_theme.get_pixbuf("button/uninstall_normal.png").get_pixbuf()
            elif self.button_status == BUTTON_HOVER:
                pixbuf = app_theme.get_pixbuf("button/uninstall_hover.png").get_pixbuf()
            elif self.button_status == BUTTON_PRESS:
                pixbuf = app_theme.get_pixbuf("button/uninstall_press.png").get_pixbuf()
            draw_pixbuf(
                cr,
                pixbuf,
                rect.x + rect.width - ITEM_BUTTON_PADDING_RIGHT - pixbuf.get_width(),
                rect.y + (ITEM_HEIGHT - self.button_height) / 2,
                )
        
    def get_height(self):
        return ITEM_HEIGHT
    
    def get_column_widths(self):
        return [ITEM_INFO_AREA_WIDTH,
                ITEM_STAR_AREA_WIDTH + ITEM_BUTTON_AREA_WIDTH]
    
    def get_column_renders(self):
        return [self.render_pkg_info,
                self.render_pkg_status]
    
    def unselect(self):
        pass
    
    def select(self):
        pass
    
    def unhover(self, column, offset_x, offset_y):
        pass

    def hover(self, column, offset_x, offset_y):
        pass
    
    def motion_notify(self, column, offset_x, offset_y):
        if column == 0:
            if self.is_in_icon_area(column, offset_x, offset_y):
                global_event.emit("set-cursor", gtk.gdk.HAND2)    
            elif self.is_in_name_area(column, offset_x, offset_y):
                global_event.emit("set-cursor", gtk.gdk.HAND2)    
            else:
                global_event.emit("set-cursor", None)
        else:
            if self.status == self.STATUS_NORMAL:
                if self.is_in_button_area(column, offset_x, offset_y):
                    self.button_status = BUTTON_HOVER
                    
                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)
                elif self.button_status != BUTTON_NORMAL:
                    self.button_status = BUTTON_NORMAL
                    
                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)
            
                if self.is_in_star_area(column, offset_x, offset_y):
                    global_event.emit("set-cursor", gtk.gdk.HAND2)
                    
                    times = offset_x / STAR_SIZE 
                    self.grade_star = times * 2 + 2
                        
                    self.grade_star = min(self.grade_star, 10)    
                    self.star_buffer.star_level = self.grade_star
                    
                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)
                else:
                    global_event.emit("set-cursor", None)
                    
                    if self.star_buffer.star_level != self.star_level:
                        self.star_buffer.star_level = self.star_level
                        
                        if self.redraw_request_callback:
                            self.redraw_request_callback(self)
                    
    def button_press(self, column, offset_x, offset_y):
        if column == 0:
            if self.is_in_icon_area(column, offset_x, offset_y):
                global_event.emit("switch-to-detail-page", self.pkg_name)    
                global_event.emit("set-cursor", None)    
            elif self.is_in_name_area(column, offset_x, offset_y):
                global_event.emit("switch-to-detail-page", self.pkg_name)    
                global_event.emit("set-cursor", None)    
        else:
            if self.status == self.STATUS_NORMAL:
                if self.is_in_button_area(column, offset_x, offset_y):
                    self.status = self.STATUS_CONFIRM
                
                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)            
                elif self.is_in_star_area(column, offset_x, offset_y):
                    global_event.emit("grade-pkg", self.pkg_name, self.grade_star)
            elif self.status == self.STATUS_CONFIRM:
                if self.is_confirm_button_area(column, offset_x, offset_y):
                    self.status = self.STATUS_WAIT_ACTION
                    self.status_text = _("Waiting for uninstall")
                    
                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)
                        
                    global_event.emit("uninstall-pkg", self.pkg_name, get_purg_flag())
                elif self.is_cancel_button_area(column, offset_x, offset_y):
                    self.status = self.STATUS_NORMAL
                    
                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)
            elif self.status == self.STATUS_WAIT_ACTION:
                if self.is_cancel_button_area(column, offset_x, offset_y):
                    self.status = self.STATUS_NORMAL
                    
                    global_event.emit("remove-wait-action", [(str((self.pkg_name, ACTION_UNINSTALL)))])
                    
                    if self.redraw_request_callback:
                        self.redraw_request_callback(self)

    def button_release(self, column, offset_x, offset_y):
        pass
                    
    def single_click(self, column, offset_x, offset_y):
        pass        

    def double_click(self, column, offset_x, offset_y):
        pass        
    
    def is_in_star_area(self, column, offset_x, offset_y):
        return (column == 1 
                and is_in_rect((offset_x, offset_y), 
                               (0,
                                (ITEM_HEIGHT - STAR_SIZE) / 2,
                                ITEM_STAR_AREA_WIDTH,
                                STAR_SIZE)))
    
    def is_in_button_area(self, column, offset_x, offset_y):
        return (column == 1
                and is_in_rect((offset_x, offset_y), 
                               (self.get_column_widths()[column] - ITEM_BUTTON_PADDING_RIGHT - self.button_width,
                                (ITEM_HEIGHT - self.button_height) / 2,
                                self.button_width,
                                self.button_height)))
    
    def is_confirm_button_area(self, column, offset_x, offset_y):
        confirm_pixbuf = app_theme.get_pixbuf("button/uninstall_confirm.png").get_pixbuf()
        return (column == 1
                and is_in_rect((offset_x, offset_y),
                               (self.get_column_widths()[column] - ITEM_CONFIRM_BUTTON_PADDING_RIGHT,
                                (ITEM_HEIGHT - confirm_pixbuf.get_height()) / 2,
                                confirm_pixbuf.get_width(),
                                confirm_pixbuf.get_height())))

    def is_cancel_button_area(self, column, offset_x, offset_y):
        cancel_pixbuf = app_theme.get_pixbuf("button/uninstall_cancel.png").get_pixbuf()
        return (column == 1
                and is_in_rect((offset_x, offset_y),
                               (self.get_column_widths()[column] - ITEM_CANCEL_BUTTON_PADDING_RIGHT,
                                (ITEM_HEIGHT - cancel_pixbuf.get_height()) / 2,
                                cancel_pixbuf.get_width(),
                                cancel_pixbuf.get_height())))
    
    def is_in_name_area(self, column, offset_x, offset_y):
        (name_width, name_height) = get_content_size(self.alias_name, NAME_SIZE)
        return (column == 0
                and is_in_rect((offset_x, offset_y),
                               (ITEM_PADDING_X + ITEM_PKG_OFFSET_X + ICON_SIZE + ITEM_PADDING_MIDDLE,
                                ITEM_PADDING_Y,
                                name_width,
                                NAME_SIZE)))
    
    def is_in_icon_area(self, column, offset_x, offset_y):
        return (column == 0
                and self.icon_pixbuf != None
                and is_in_rect((offset_x, offset_y),
                               (ITEM_PADDING_X + ITEM_PKG_OFFSET_X,
                                ITEM_PADDING_Y,
                                self.icon_pixbuf.get_width(),
                                self.icon_pixbuf.get_height())))
    
    def action_wait(self):
        self.status = self.STATUS_WAIT_ACTION
        self.status_text = _("Waiting for uninstall")

        if self.redraw_request_callback:
            self.redraw_request_callback(self)
    
    def action_start(self):
        self.status = self.STATUS_IN_ACTION
        self.status_text = _("Uninstalling")
    
        if self.redraw_request_callback:
            self.redraw_request_callback(self)
                
    def action_update(self, percent):
        self.progress_buffer.progress = percent
        self.status_text = _("Uninstalling")
        
        if self.redraw_request_callback:
            self.redraw_request_callback(self)
            
    def action_finish(self):
        self.progress_buffer.progress = 100
        self.status_text = _("Uninstall successful")
        
        if self.redraw_request_callback:
            self.redraw_request_callback(self)
    
    def release_resource(self):
        '''
        Release item resource.

        If you have pixbuf in item, you should release memory resource like below code:

        >>> if self.pixbuf:
        >>>     del self.pixbuf
        >>>     self.pixbuf = None
        >>>
        >>> return True

        This is TreeView interface, you should implement it.
        
        @return: Return True if do release work, otherwise return False.
        
        When this function return True, TreeView will call function gc.collect() to release object to release memory.
        '''
        if self.icon_pixbuf:
            del self.icon_pixbuf
            self.icon_pixbuf = None

        return True    
class TaskItem(TreeItem):
    '''
    class docs
    '''

    STATUS_WAIT_DOWNLOAD = 2
    STATUS_IN_DOWNLOAD = 3
    STATUS_STOP = 4
    STATUS_FINISH = 5

    def __init__(self, image_object, finish_callback):
        '''
        init docs
        '''
        TreeItem.__init__(self)

        # Init sizes.
        self.item_height = 50
        self.info_width = -1
        self.progressbar_width = 100
        self.progressbar_padding_x = 10
        self.progressbar_height = 12
        self.check_button_padding_x = 10
        self.info_padding_x = 5

        self.icon_pixbuf = None
        self.image_object = image_object
        self.button_status = BUTTON_NORMAL

        # Init status.
        self.status = self.STATUS_WAIT_DOWNLOAD
        self.status_text = "等待下载"

        # Init buffers.
        self.progress_buffer = ProgressBuffer()

        self.stop_pixbuf = app_theme.get_pixbuf(
            "individuation/stop.png").get_pixbuf()
        self.stop_pixbuf_padding_x = 5
        self.block_width = 50
        self.download_task = TaskObject(image_object.big_url,
                                        image_object.get_save_path(),
                                        output_temp=True)

        self.download_task.connect("update", self.download_update)
        self.download_task.connect("finish", self.download_finish)
        self.download_task.connect("error", self.download_failed)
        self.download_task.connect("start", self.download_start)

        self.finish_callback = finish_callback

        self.start_download()

    def start_download(self):
        fetch_service.add_missions([self.download_task])

    def render_info(self, cr, rect):
        if self.is_hover:
            draw_single_mask(cr, rect.x, rect.y, rect.width, rect.height,
                             "globalItemHover")

        if self.icon_pixbuf is None:
            self.icon_pixbuf = cache_manager.get_small_pixbuf(
                self.image_object, 37, 38)

        icon_width = self.icon_pixbuf.get_width()
        icon_height = self.icon_pixbuf.get_height()
        icon_x = rect.x + self.info_padding_x
        icon_y = rect.y + (rect.height - icon_height) / 2

        # Draw shadow.
        drop_shadow_padding = 3
        drop_shadow_radious = 3
        draw_shadow(cr, icon_x, icon_y, icon_width + drop_shadow_padding,
                    icon_height + drop_shadow_padding, drop_shadow_radious,
                    app_theme.get_shadow_color("window_shadow"))

        outside_shadow_padding = 2
        outside_shadow_radious = 3
        draw_shadow(cr, icon_x - outside_shadow_padding,
                    icon_y - outside_shadow_padding,
                    icon_width + outside_shadow_padding * 2,
                    icon_height + outside_shadow_padding * 2,
                    outside_shadow_radious,
                    app_theme.get_shadow_color("window_shadow"))

        # Draw wallpaper.

        draw_pixbuf(cr, self.icon_pixbuf, icon_x, icon_y)

        rect.x = icon_x + self.icon_pixbuf.get_width() + self.info_padding_x
        rect.width -= self.info_padding_x * 2 - self.icon_pixbuf.get_width()
        _width, _height = get_content_size(
            self.image_object.get_display_name())
        draw_text(cr,
                  "<b>%s</b>" % self.image_object.get_display_name(),
                  rect.x,
                  icon_y,
                  rect.width,
                  _height,
                  text_size=10)

        rect.y = icon_y + icon_width - _height
        _width, _height = get_content_size(self.status_text)
        draw_text(cr, self.status_text, rect.x, rect.y, rect.width, _height)

    def render_progressbar(self, cr, rect):
        if self.is_hover:
            draw_single_mask(cr, rect.x, rect.y, rect.width, rect.height,
                             "globalItemHover")

        self.progress_buffer.render(
            cr,
            gtk.gdk.Rectangle(
                rect.x + (rect.width - self.progressbar_width) / 2,
                rect.y + (rect.height - self.progressbar_height) / 2,
                self.progressbar_width, self.progressbar_height))

    def render_stop(self, cr, rect):
        if self.is_hover:
            draw_single_mask(cr, rect.x, rect.y, rect.width, rect.height,
                             "globalItemHover")

        icon_x = rect.x + (rect.width - self.stop_pixbuf.get_width()) / 2
        icon_y = rect.y + (rect.height - self.stop_pixbuf.get_height()) / 2
        draw_pixbuf(cr, self.stop_pixbuf, icon_x, icon_y)

    def render_block(self, cr, rect):
        if self.is_hover:
            draw_single_mask(cr, rect.x, rect.y, rect.width, rect.height,
                             "globalItemHover")

    def get_column_widths(self):
        return [
            10, self.info_width,
            self.progressbar_width + self.progressbar_padding_x * 2,
            self.stop_pixbuf.get_width() + self.stop_pixbuf_padding_x * 2,
            self.block_width
        ]

    def get_column_renders(self):
        return [
            self.render_block,
            self.render_info,
            self.render_progressbar,
            self.render_stop,
            self.render_block,
        ]

    def get_height(self):
        return self.item_height

    def emit_request_redraw(self):
        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def unselect(self):
        pass

    def select(self):
        pass

    def unhover(self, column, offset_x, offset_y):
        self.is_hover = False
        self.emit_request_redraw()

    def hover(self, column, offset_x, offset_y):
        self.is_hover = True
        self.emit_request_redraw()

    def motion_notify(self, column, offset_x, offset_y):
        if column == 0:
            if self.check_button_buffer.motion_button(offset_x, offset_y):
                self.emit_request_redraw()

    def button_press(self, column, offset_x, offset_y):
        if column == 0:
            if self.check_button_buffer.press_button(offset_x, offset_y):
                self.emit_request_redraw()

    def button_release(self, column, offset_x, offset_y):
        if column == 0 and self.check_button_buffer.release_button(
                offset_x, offset_y):
            self.emit_request_redraw()

    def single_click(self, column, offset_x, offset_y):
        pass

    def double_click(self, column, offset_x, offset_y):
        pass

    @post_gui
    def download_update(self, obj, data):
        self.progress_buffer.progress = data.progress
        speed = parse_bytes(data.speed)
        remaining = parse_time(data.remaining)
        filesize = parse_bytes(data.filesize)
        downloaded = parse_bytes(data.downloaded)
        self.status_text = "%s/s - %s, 共%s,  还有 %s " % (speed, downloaded,
                                                        filesize, remaining)
        self.emit_request_redraw()

    @post_gui
    def download_finish(self, obj, data):
        event_manager.emit("download-finish", obj)
        self.progress_buffer.progress = 100
        self.status_text = "下载完成"
        self.emit_request_redraw()

        if self.finish_callback:
            self.finish_callback(self)

    @post_gui
    def download_failed(self, obj, data):
        self.status_text = data
        self.emit_request_redraw()

    @post_gui
    def download_start(self, obj, data):
        event_manager.emit("download-start", obj)
        self.status_text = "开始下载"
        self.emit_request_redraw()

    def download_stop(self):
        pass

    def release_resource(self):
        '''
        Release item resource.

        If you have pixbuf in item, you should release memory resource like below code:

        >>> if self.pixbuf:
        >>>     del self.pixbuf
        >>>     self.pixbuf = None
        >>>
        >>> return True

        This is TreeView interface, you should implement it.
        
        @return: Return True if do release work, otherwise return False.
        
        When this function return True, TreeView will call function gc.collect() to release object to release memory.
        '''
        if self.icon_pixbuf:
            del self.icon_pixbuf
            self.icon_pixbuf = None

        return True