class DeviceIconView(ScrolledWindow):
    def __init__(self, items=None):
        ScrolledWindow.__init__(self, 0, 0)

        self.device_iconview = IconView()
        self.device_iconview.connect("right-click-item",
                                     self.__on_right_click_item)
        self.device_iconview.draw_mask = self.draw_mask

        if items:
            self.device_iconview.add_items(items)

        self.device_scrolledwindow = ScrolledWindow()
        self.device_scrolledwindow.add_child(self.device_iconview)

        self.add_child(self.device_scrolledwindow)

        event_manager.add_callback("text", self.__on_text)
        event_manager.add_callback("hide-text", self.__on_hide_text)

    def __on_text(self, name, obj, argv):
        disable(self, False)
        text(self, argv)

    def __on_hide_text(self, name, obj, argv):
        disable(self, True)

    def __do_remove_item(self, item):
        item.do_remove()
        self.device_iconview.delete_items([item])

    def __on_right_click_item(self, widget, item, x, y):
        menu_items = [(None, _("Pair"), lambda: item.do_pair())]
        if item.is_paired:
            menu_items = []
            menu_items.extend(item.menu_items)
            menu_items.append(None)
            menu_items.append((None, _("Remove Device"),
                               lambda: self.__do_remove_item(item)))

        Menu(menu_items, True).show((int(x), int(y)))

    def clear(self):
        self.device_iconview.clear()

    def draw_mask(self, cr, x, y, w, h):
        cr.set_source_rgb(*color_hex_to_cairo("#FFFFFF"))
        cr.rectangle(x, y, w, h)
        cr.fill()

        cr.set_source_rgb(*color_hex_to_cairo("#AEAEAE"))
        cr.rectangle(x, y, w, h)
        cr.stroke()

    def add_items(self, items, clear=False):
        if clear:
            self.device_iconview.clear()

        self.device_iconview.add_items(items)
class DeviceIconView(ScrolledWindow):
    def __init__(self, items=None):
        ScrolledWindow.__init__(self, 0, 0)

        self.device_iconview = IconView()
        self.device_iconview.connect("right-click-item", self.__on_right_click_item)
        self.device_iconview.draw_mask = self.draw_mask

        if items:
            self.device_iconview.add_items(items)

        self.device_scrolledwindow = ScrolledWindow()
        self.device_scrolledwindow.add_child(self.device_iconview)

        self.add_child(self.device_scrolledwindow)

        event_manager.add_callback("text", self.__on_text)
        event_manager.add_callback("hide-text", self.__on_hide_text)
        
    def __on_text(self, name, obj, argv):
        disable(self, False)
        text(self, argv)

    def __on_hide_text(self, name, obj, argv):
        disable(self, True)

    def __do_remove_item(self, item):
        item.do_remove()
        self.device_iconview.delete_items([item])

    def __on_right_click_item(self, widget, item, x, y):
        menu_items = [(None, _("Pair"), lambda : item.do_pair())]
        if item.is_paired:
            menu_items = []
            menu_items.extend(item.menu_items)
            menu_items.append(None)
            menu_items.append((None, _("Remove Device"), lambda : self.__do_remove_item(item)))

        Menu(menu_items, True).show((int(x), int(y)))
        
    def clear(self):
        self.device_iconview.clear()

    def draw_mask(self, cr, x, y, w, h):
        cr.set_source_rgb(*color_hex_to_cairo("#FFFFFF"))
        cr.rectangle(x, y, w, h)
        cr.fill()

        cr.set_source_rgb(*color_hex_to_cairo("#AEAEAE"))
        cr.rectangle(x, y, w, h)
        cr.stroke()

    def add_items(self, items, clear=False):
        if clear:
            self.device_iconview.clear()

        self.device_iconview.add_items(items)
 def get_icon_view(self, padding_x=0, padding_y=10):    
     ''' Draggable IconView '''
     icon_view = IconView(padding_x, padding_y)
     targets = [("text/deepin-songs", gtk.TARGET_SAME_APP, 1), ("text/uri-list", 0, 2)]
     icon_view.drag_source_set(gtk.gdk.BUTTON1_MASK, targets, gtk.gdk.ACTION_COPY)
     icon_view.connect("drag-data-get", self.__on_drag_data_get) 
     icon_view.connect("double-click-item", self.__on_double_click_item)
     icon_view.connect("single-click-item", self.__on_single_click_item)
     icon_view.connect("right-click-item", self.__on_right_click_item)
     
     icon_view.draw_mask  = self.on_iconview_draw_mask
     scrolled_window = ScrolledWindow()
     scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
     scrolled_window.add_child(icon_view)
     return icon_view, scrolled_window
Exemple #4
0
 def get_icon_view(self, padding_x=0, padding_y=10):    
     ''' Draggable IconView '''
     icon_view = IconView(padding_x, padding_y)
     targets = [("text/deepin-songs", gtk.TARGET_SAME_APP, 1), ("text/uri-list", 0, 2)]
     icon_view.drag_source_set(gtk.gdk.BUTTON1_MASK, targets, gtk.gdk.ACTION_COPY)
     icon_view.connect("drag-data-get", self.__on_drag_data_get) 
     icon_view.connect("double-click-item", self.__on_double_click_item)
     icon_view.connect("single-click-item", self.__on_single_click_item)
     icon_view.connect("right-click-item", self.__on_right_click_item)
     
     icon_view.draw_mask  = self.on_iconview_draw_mask
     scrolled_window = ScrolledWindow()
     scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
     scrolled_window.add_child(icon_view)
     return icon_view, scrolled_window
Exemple #5
0
class SecondCategoryItem(TreeItem):
    '''
    class docs
    '''
    def __init__(self, first_category_name, second_category_name,
                 data_manager):
        '''
        init docs
        '''
        TreeItem.__init__(self)
        self.first_category_name = first_category_name
        self.second_category_name = second_category_name
        self.data_manager = data_manager

    def render_name(self, cr, rect):
        text_color = "#333333"
        if self.is_select:
            cr.set_source_rgba(*color_hex_to_cairo(
                app_theme.get_color("sidebar_select").get_color()))
            cr.rectangle(rect.x, rect.y, rect.width, rect.height)
            cr.fill()

            text_color = "#FFFFFF"
        elif self.is_hover:
            cr.set_source_rgba(*color_hex_to_cairo(
                app_theme.get_color("sidebar_hover").get_color()))
            cr.rectangle(rect.x, rect.y, rect.width, rect.height)
            cr.fill()

        draw_text(
            cr,
            get_category_name(self.second_category_name),
            rect.x + SECOND_CATEGORY_PADDING_X,
            rect.y,
            rect.width,
            rect.height,
            text_size=SECOND_CATEGORY_ITEM_NAME_SIZE,
            text_color=text_color,
        )

    def get_height(self):
        return SECOND_CATEGORY_ITEM_HEIGHT

    def get_column_widths(self):
        return [CATEGORY_ITEM_NAME_WIDTH]

    def get_column_renders(self):
        return [self.render_name]

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

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

    def select(self):
        self.is_select = True
        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def unselect(self):
        self.is_select = False
        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def scrolled_window_vscrollbar_handler(self, widget, state):
        if state == "bottom":
            current_item_number = len(self.pkg_icon_view.items)
            all_pkgs_number = len(self.all_pkg_names)
            start = current_item_number
            if current_item_number < all_pkgs_number and (
                    current_item_number + LOAD_ITEMS_NUMBER) < all_pkgs_number:
                end = current_item_number + LOAD_ITEMS_NUMBER
            elif current_item_number < all_pkgs_number and (
                    current_item_number +
                    LOAD_ITEMS_NUMBER) >= all_pkgs_number:
                end = all_pkgs_number
            else:
                return
            self.load_new_items(self.all_pkg_names[start:end])

    def load_new_items(self, pkg_names):
        items = []
        for pkg_name in pkg_names:
            info = self.data_manager.get_item_pkg_info(pkg_name)
            items.append(
                PkgIconItem(info[0], info[1], info[2], self.data_manager))
        self.pkg_icon_view.add_items(items)
        global_event.emit("show-pkg-view", self.page_box)

    def handle_pkg_install_status_request(self, status, items):
        for (index, state) in enumerate(status):
            items[index].is_installed = state
            items[index].emit_redraw_request()

    def button_press(self, column, offset_x, offset_y):
        # init Loading widget
        loading_box = LoadingBox()

        global_event.emit("show-pkg-view", loading_box)

        self.page_box = gtk.VBox()

        self.all_pkg_names = self.data_manager.get_second_category_packages(
            self.second_category_name)
        self.message_bar = MessageBar(18)
        self.message_bar.set_message(
            _("%s > %s : %s applications") % (
                get_category_name(self.first_category_name),
                get_category_name(self.second_category_name),
                len(self.all_pkg_names),
            ))

        self.pkg_icon_view = IconView()
        self.pkg_icon_view.connect("motion-notify-item", tooltip_aciton)
        if len(self.all_pkg_names) > LOAD_ITEMS_NUMBER:
            self.load_new_items(self.all_pkg_names[:LOAD_ITEMS_NUMBER])
        else:
            self.load_new_items(self.all_pkg_names)

        self.pkg_icon_scrolled_window = ScrolledWindow()
        self.pkg_icon_scrolled_window.connect(
            "vscrollbar-state-changed",
            self.scrolled_window_vscrollbar_handler)
        self.pkg_icon_scrolled_window.add_child(self.pkg_icon_view)
        self.pkg_icon_view.draw_mask = self.draw_mask
        self.pkg_icon_view.draw_row_mask = self.draw_row_mask

        self.page_box.pack_start(self.message_bar, False, False)
        self.page_box.pack_start(self.pkg_icon_scrolled_window, True, True)

        global_event.emit("update-current-status-pkg-page", self.pkg_icon_view)

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

    def draw_mask(self, cr, x, y, w, h):
        '''
        Draw mask interface.

        @param cr: Cairo context.
        @param x: X coordiante of draw area.
        @param y: Y coordiante of draw area.
        @param w: Width of draw area.
        @param h: Height of draw area.
        '''
        draw_vlinear(cr, x, y, w, h, [
            (0, ("#FFFFFF", 0.9)),
            (1, ("#FFFFFF", 0.9)),
        ])
Exemple #6
0
class CategoryItem(TreeItem):
    '''
    class docs
    '''
    def __init__(self, index, first_category_name, data_manager):
        '''
        init docs
        '''
        TreeItem.__init__(self)
        self.index = index
        self.first_category_name = first_category_name
        self.data_manager = data_manager

    def render_name(self, cr, rect):
        text_color = "#333333"
        font_image_color = app_theme.get_color("sidebar_select").get_color()
        if self.is_select:
            cr.set_source_rgba(*color_hex_to_cairo(
                app_theme.get_color("sidebar_select").get_color()))
            cr.rectangle(rect.x, rect.y, rect.width, rect.height)
            cr.fill()

            text_color = "#FFFFFF"
            font_image_color = "#FFFFFF"
        elif self.is_hover:
            cr.set_source_rgba(*color_hex_to_cairo(
                app_theme.get_color("sidebar_hover").get_color()))
            cr.rectangle(rect.x, rect.y, rect.width, rect.height)
            cr.fill()

        pixbuf = app_theme.get_pixbuf("category/%s.png" %
                                      (self.index)).get_pixbuf()

        category_img_path = os.path.join(get_parent_dir(__file__, 2), "image",
                                         "category",
                                         "%s.png" % (self.index + 1, ))
        surface = cairo.ImageSurface.create_from_png(category_img_path)
        cr.set_source_rgb(*color_hex_to_cairo(font_image_color))
        cr.mask_surface(surface, rect.x + 14, rect.y + (rect.height - 24) / 2)

        draw_text(
            cr,
            get_category_name(self.first_category_name),
            rect.x + pixbuf.get_width() + 22,
            rect.y,
            rect.width,
            rect.height,
            text_size=CATEGORY_ITEM_NAME_SIZE,
            text_color=text_color,
        )

        if self.is_hover:
            if self.is_expand:
                pixbuf = app_theme.get_pixbuf("sidebar/close.png").get_pixbuf()
            else:
                pixbuf = app_theme.get_pixbuf("sidebar/open.png").get_pixbuf()

            draw_pixbuf(cr, pixbuf,
                        rect.x + rect.width - CATEGORY_ITEM_EXPAND_PADDING_X,
                        rect.y + (rect.height - pixbuf.get_height()) / 2)

    def get_height(self):
        return CATEGORY_ITEM_HEIGHT

    def get_column_widths(self):
        return [CATEGORY_ITEM_NAME_WIDTH]

    def get_column_renders(self):
        return [self.render_name]

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

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

    def select(self):
        self.is_select = True
        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def unselect(self):
        self.is_select = False
        if self.redraw_request_callback:
            self.redraw_request_callback(self)

    def is_in_expand_button_area(self, column, offset_x, offset_y):
        pixbuf = app_theme.get_pixbuf("sidebar/close.png").get_pixbuf()

        return is_in_rect(
            (offset_x, offset_y),
            (CATEGORY_VIEW_WIDTH - CATEGORY_ITEM_EXPAND_PADDING_X,
             (self.get_height() - pixbuf.get_height()) / 2, pixbuf.get_width(),
             pixbuf.get_height()))

    def motion_notify(self, column, offset_x, offset_y):
        if self.is_in_expand_button_area(column, offset_x, offset_y):
            global_event.emit("set-cursor", gtk.gdk.HAND2)
        else:
            global_event.emit("set-cursor", None)

    def button_press(self, column, offset_x, offset_y):
        if self.is_in_expand_button_area(column, offset_x, offset_y):
            if self.is_expand:
                self.unexpand()
            else:
                self.expand()

    def scrolled_window_vscrollbar_handler(self, widget, state):
        if state == "bottom":
            current_item_number = len(self.pkg_icon_view.items)
            all_pkgs_number = len(self.all_pkg_names)
            start = current_item_number
            if current_item_number < all_pkgs_number and (
                    current_item_number + LOAD_ITEMS_NUMBER) < all_pkgs_number:
                end = current_item_number + LOAD_ITEMS_NUMBER
            elif current_item_number < all_pkgs_number and (
                    current_item_number +
                    LOAD_ITEMS_NUMBER) >= all_pkgs_number:
                end = all_pkgs_number
            else:
                return
            self.load_new_items(self.all_pkg_names[start:end])

    def load_new_items(self, pkg_names):
        items = []
        for pkg_name in pkg_names:
            info = self.data_manager.get_item_pkg_info(pkg_name)
            items.append(
                PkgIconItem(info[0], info[1], info[2], self.data_manager))
        self.pkg_icon_view.add_items(items)
        global_event.emit("show-pkg-view", self.page_box)

    def handle_pkg_install_status_request(self, status, items):
        for (index, state) in enumerate(status):
            items[index].is_installed = state
            items[index].emit_redraw_request()

    def single_click(self, column, offset_x, offset_y):
        # init Loading widget
        loading_box = LoadingBox()
        global_event.emit("show-pkg-view", loading_box)

        self.page_box = gtk.VBox()

        self.all_pkg_names = []
        self.all_desktop_infos = {}
        self.all_pkg_names = self.data_manager.get_first_category_packages(
            self.first_category_name)

        self.message_bar = MessageBar(18)
        self.message_bar.set_message(
            _("%s: %s applications") % (
                get_category_name(self.first_category_name),
                len(self.all_pkg_names),
            ))

        self.pkg_icon_view = IconView()
        self.pkg_icon_view.connect("motion-notify-item", tooltip_aciton)
        if len(self.all_pkg_names) > LOAD_ITEMS_NUMBER:
            self.load_new_items(self.all_pkg_names[:LOAD_ITEMS_NUMBER])
        else:
            self.load_new_items(self.all_pkg_names)

        self.pkg_icon_view.draw_mask = self.draw_mask
        self.pkg_icon_view.draw_row_mask = self.draw_row_mask
        self.pkg_icon_scrolled_window = ScrolledWindow()
        self.pkg_icon_scrolled_window.connect(
            "vscrollbar-state-changed",
            self.scrolled_window_vscrollbar_handler)
        self.pkg_icon_scrolled_window.add_child(self.pkg_icon_view)

        self.page_box.pack_start(self.message_bar, False, False)
        self.page_box.pack_start(self.pkg_icon_scrolled_window, True, True)
        global_event.emit("update-current-status-pkg-page", self.pkg_icon_view)

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

    def draw_mask(self, cr, x, y, w, h):
        '''
        Draw mask interface.

        @param cr: Cairo context.
        @param x: X coordiante of draw area.
        @param y: Y coordiante of draw area.
        @param w: Width of draw area.
        @param h: Height of draw area.
        '''
        draw_vlinear(cr, x, y, w, h, [
            (0, ("#FFFFFF", 0.9)),
            (1, ("#FFFFFF", 0.9)),
        ])

    def double_click(self, column, offset_x, offset_y):
        if self.is_expand:
            self.unexpand()
        else:
            self.expand()

    def add_child_item(self):
        items = []
        second_category_names = self.data_manager.get_second_category(
            self.first_category_name)
        for second_category_name in second_category_names:
            items.append(
                SecondCategoryItem(self.first_category_name,
                                   second_category_name, self.data_manager))

        self.child_items = items
        self.add_items_callback(self.child_items, self.row_index + 1)

    def delete_chlid_item(self):
        self.delete_items_callback(self.child_items)

    def expand(self):
        self.is_expand = True

        self.add_child_item()

        if self.redraw_request_callback:
            self.redraw_request_callback(self)
        global_event.emit("category-expand", self)

    def unexpand(self):
        self.is_expand = False

        self.delete_chlid_item()

        if self.redraw_request_callback:
            self.redraw_request_callback(self)
class SecondCategoryItem(TreeItem):
    '''
    class docs
    '''
	
    def __init__(self, first_category_name, second_category_name, data_manager):
        '''
        init docs
        '''
        TreeItem.__init__(self)
        self.first_category_name = first_category_name
        self.second_category_name = second_category_name
        self.data_manager = data_manager
    
    def render_name(self, cr, rect):
        text_color = "#333333"
        if self.is_select:
            cr.set_source_rgba(*color_hex_to_cairo(app_theme.get_color("sidebar_select").get_color()))
            cr.rectangle(rect.x, rect.y, rect.width, rect.height)
            cr.fill()
            
            text_color = "#FFFFFF"
        elif self.is_hover:
            cr.set_source_rgba(*color_hex_to_cairo(app_theme.get_color("sidebar_hover").get_color()))
            cr.rectangle(rect.x, rect.y, rect.width, rect.height)
            cr.fill()
        
        draw_text(cr, 
                  get_category_name(self.second_category_name),
                  rect.x + SECOND_CATEGORY_PADDING_X,
                  rect.y,
                  rect.width,
                  rect.height,
                  text_size=SECOND_CATEGORY_ITEM_NAME_SIZE,
                  text_color=text_color,
                  )
        
    def get_height(self):
        return SECOND_CATEGORY_ITEM_HEIGHT
    
    def get_column_widths(self):
        return [CATEGORY_ITEM_NAME_WIDTH]
        
    def get_column_renders(self):    
        return [self.render_name]
        
    def hover(self, column, offset_x, offset_y):
        self.is_hover = True
        if self.redraw_request_callback:
            self.redraw_request_callback(self)
        
    def unhover(self, column, offset_x, offset_y):
        self.is_hover = False
        if self.redraw_request_callback:
            self.redraw_request_callback(self)
    
    def select(self):
        self.is_select = True
        if self.redraw_request_callback:
            self.redraw_request_callback(self)
        
    def unselect(self):
        self.is_select = False
        if self.redraw_request_callback:
            self.redraw_request_callback(self)
    
    def scrolled_window_vscrollbar_handler(self, widget, state):
        if state == "bottom":
            current_item_number = len(self.pkg_icon_view.items)
            all_pkgs_number = len(self.all_pkg_names)
            start = current_item_number
            if current_item_number < all_pkgs_number and (current_item_number+LOAD_ITEMS_NUMBER) < all_pkgs_number:
                end = current_item_number+LOAD_ITEMS_NUMBER
            elif current_item_number < all_pkgs_number and (current_item_number+LOAD_ITEMS_NUMBER) >= all_pkgs_number:
                end = all_pkgs_number
            else:
                return
            self.load_new_items(self.all_pkg_names[start:end])

    def load_new_items(self, pkg_names):
        items = []
        for pkg_name in pkg_names:
            info = self.data_manager.get_item_pkg_info(pkg_name)
            items.append(PkgIconItem(info[0], info[1], info[2], self.data_manager))
        self.pkg_icon_view.add_items(items)
        global_event.emit("show-pkg-view", self.page_box)

    def handle_pkg_install_status_request(self, status, items):
        for (index, state) in enumerate(status):
            items[index].is_installed = state
            items[index].emit_redraw_request()

    def button_press(self, column, offset_x, offset_y):
        # init Loading widget
        loading_box = LoadingBox()

        global_event.emit("show-pkg-view", loading_box)

        self.page_box = gtk.VBox()    
            
        self.all_pkg_names = self.data_manager.get_second_category_packages(self.second_category_name)
        self.message_bar = MessageBar(18)
        self.message_bar.set_message(_("%s > %s : %s applications") % (
                get_category_name(self.first_category_name), 
                get_category_name(self.second_category_name), 
                len(self.all_pkg_names),
                ))
        
        self.pkg_icon_view = IconView() 
        self.pkg_icon_view.connect("motion-notify-item", tooltip_aciton)
        if len(self.all_pkg_names) > LOAD_ITEMS_NUMBER:
            self.load_new_items(self.all_pkg_names[:LOAD_ITEMS_NUMBER])
        else:
            self.load_new_items(self.all_pkg_names)

        self.pkg_icon_scrolled_window = ScrolledWindow()
        self.pkg_icon_scrolled_window.connect("vscrollbar-state-changed", self.scrolled_window_vscrollbar_handler)
        self.pkg_icon_scrolled_window.add_child(self.pkg_icon_view)
        self.pkg_icon_view.draw_mask = self.draw_mask
        self.pkg_icon_view.draw_row_mask = self.draw_row_mask
        
        self.page_box.pack_start(self.message_bar, False, False)
        self.page_box.pack_start(self.pkg_icon_scrolled_window, True, True)
        
        global_event.emit("update-current-status-pkg-page", self.pkg_icon_view)
        
    def draw_row_mask(self, cr, rect, row):
        if row % 2 == 1:
            cr.set_source_rgba(1, 1, 1, 0.5)
            cr.rectangle(rect.x, rect.y, rect.width, rect.height)
            cr.fill()
    
    def draw_mask(self, cr, x, y, w, h):
        '''
        Draw mask interface.
        
        @param cr: Cairo context.
        @param x: X coordiante of draw area.
        @param y: Y coordiante of draw area.
        @param w: Width of draw area.
        @param h: Height of draw area.
        '''
        draw_vlinear(cr, x, y, w, h,
                     [(0, ("#FFFFFF", 0.9)),
                      (1, ("#FFFFFF", 0.9)),]
                     )
class CategoryItem(TreeItem):
    '''
    class docs
    '''
	
    def __init__(self, index, first_category_name, data_manager):
        '''
        init docs
        '''
        TreeItem.__init__(self)
        self.index = index
        self.first_category_name = first_category_name
        self.data_manager = data_manager
    
    def render_name(self, cr, rect):
        text_color = "#333333"
        font_image_color = app_theme.get_color("sidebar_select").get_color()
        if self.is_select:
            cr.set_source_rgba(*color_hex_to_cairo(app_theme.get_color("sidebar_select").get_color()))
            cr.rectangle(rect.x, rect.y, rect.width, rect.height)
            cr.fill()
            
            text_color = "#FFFFFF"
            font_image_color = "#FFFFFF"
        elif self.is_hover:
            cr.set_source_rgba(*color_hex_to_cairo(app_theme.get_color("sidebar_hover").get_color()))
            cr.rectangle(rect.x, rect.y, rect.width, rect.height)
            cr.fill()
        
        pixbuf = app_theme.get_pixbuf("category/%s.png" % (self.index)).get_pixbuf()

        category_img_path = os.path.join(get_parent_dir(__file__, 2), "image", "category", "%s.png" % (self.index+1, ))
        surface = cairo.ImageSurface.create_from_png(category_img_path)
        cr.set_source_rgb(*color_hex_to_cairo(font_image_color))
        cr.mask_surface(surface, rect.x+14, rect.y+(rect.height-24)/2)

        draw_text(cr, 
                  get_category_name(self.first_category_name),
                  rect.x + pixbuf.get_width() + 22, 
                  rect.y,
                  rect.width,
                  rect.height,
                  text_size=CATEGORY_ITEM_NAME_SIZE,
                  text_color=text_color,
                  )
        
        if self.is_hover:
            if self.is_expand:
                pixbuf = app_theme.get_pixbuf("sidebar/close.png").get_pixbuf()
            else:
                pixbuf = app_theme.get_pixbuf("sidebar/open.png").get_pixbuf()
                
            draw_pixbuf(
                cr,
                pixbuf,
                rect.x + rect.width - CATEGORY_ITEM_EXPAND_PADDING_X,
                rect.y + (rect.height - pixbuf.get_height()) / 2)
        
    def get_height(self):
        return CATEGORY_ITEM_HEIGHT
    
    def get_column_widths(self):
        return [CATEGORY_ITEM_NAME_WIDTH]
        
    def get_column_renders(self):    
        return [self.render_name]
        
    def hover(self, column, offset_x, offset_y):
        self.is_hover = True
        if self.redraw_request_callback:
            self.redraw_request_callback(self)
        
    def unhover(self, column, offset_x, offset_y):
        self.is_hover = False
        if self.redraw_request_callback:
            self.redraw_request_callback(self)
    
    def select(self):
        self.is_select = True
        if self.redraw_request_callback:
            self.redraw_request_callback(self)
        
    def unselect(self):
        self.is_select = False
        if self.redraw_request_callback:
            self.redraw_request_callback(self)
    
    def is_in_expand_button_area(self, column, offset_x, offset_y):
        pixbuf = app_theme.get_pixbuf("sidebar/close.png").get_pixbuf()
        
        return is_in_rect((offset_x, offset_y),
                          (CATEGORY_VIEW_WIDTH - CATEGORY_ITEM_EXPAND_PADDING_X,
                           (self.get_height() - pixbuf.get_height()) / 2,
                           pixbuf.get_width(),
                           pixbuf.get_height()))
    
    def motion_notify(self, column, offset_x, offset_y):
        if self.is_in_expand_button_area(column, offset_x, offset_y):
            global_event.emit("set-cursor", gtk.gdk.HAND2)    
        else:
            global_event.emit("set-cursor", None)    
            
    def button_press(self, column, offset_x, offset_y):
        if self.is_in_expand_button_area(column, offset_x, offset_y):
            if self.is_expand:
                self.unexpand()
            else:
                self.expand()

    def scrolled_window_vscrollbar_handler(self, widget, state):
        if state == "bottom":
            current_item_number = len(self.pkg_icon_view.items)
            all_pkgs_number = len(self.all_pkg_names)
            start = current_item_number
            if current_item_number < all_pkgs_number and (current_item_number+LOAD_ITEMS_NUMBER) < all_pkgs_number:
                end = current_item_number+LOAD_ITEMS_NUMBER
            elif current_item_number < all_pkgs_number and (current_item_number+LOAD_ITEMS_NUMBER) >= all_pkgs_number:
                end = all_pkgs_number
            else:
                return
            self.load_new_items(self.all_pkg_names[start:end])
            
    def load_new_items(self, pkg_names):
        items = []
        for pkg_name in pkg_names:
            info = self.data_manager.get_item_pkg_info(pkg_name)
            items.append(PkgIconItem(info[0], info[1], info[2], self.data_manager))
        self.pkg_icon_view.add_items(items)
        global_event.emit("show-pkg-view", self.page_box)

    def handle_pkg_install_status_request(self, status, items):
        for (index, state) in enumerate(status):
            items[index].is_installed = state
            items[index].emit_redraw_request()

    def single_click(self, column, offset_x, offset_y):
        # init Loading widget
        loading_box = LoadingBox()
        global_event.emit("show-pkg-view", loading_box)

        self.page_box = gtk.VBox()    

        self.all_pkg_names = []
        self.all_desktop_infos = {}
        self.all_pkg_names = self.data_manager.get_first_category_packages(self.first_category_name)
            
        self.message_bar = MessageBar(18)
        self.message_bar.set_message(_("%s: %s applications") % (
                    get_category_name(self.first_category_name), 
                    len(self.all_pkg_names),
                    ))
        
        self.pkg_icon_view = IconView() 
        self.pkg_icon_view.connect("motion-notify-item", tooltip_aciton)
        if len(self.all_pkg_names) > LOAD_ITEMS_NUMBER:
            self.load_new_items(self.all_pkg_names[:LOAD_ITEMS_NUMBER])
        else:
            self.load_new_items(self.all_pkg_names)

        self.pkg_icon_view.draw_mask = self.draw_mask
        self.pkg_icon_view.draw_row_mask = self.draw_row_mask
        self.pkg_icon_scrolled_window = ScrolledWindow()
        self.pkg_icon_scrolled_window.connect("vscrollbar-state-changed", self.scrolled_window_vscrollbar_handler)
        self.pkg_icon_scrolled_window.add_child(self.pkg_icon_view)
        
        self.page_box.pack_start(self.message_bar, False, False)
        self.page_box.pack_start(self.pkg_icon_scrolled_window, True, True)
        global_event.emit("update-current-status-pkg-page", self.pkg_icon_view)
        
    def draw_row_mask(self, cr, rect, row):
        if row % 2 == 1:
            cr.set_source_rgba(1, 1, 1, 0.5)
            cr.rectangle(rect.x, rect.y, rect.width, rect.height)
            cr.fill()
    
    def draw_mask(self, cr, x, y, w, h):
        '''
        Draw mask interface.
        
        @param cr: Cairo context.
        @param x: X coordiante of draw area.
        @param y: Y coordiante of draw area.
        @param w: Width of draw area.
        @param h: Height of draw area.
        '''
        draw_vlinear(cr, x, y, w, h,
                     [(0, ("#FFFFFF", 0.9)),
                      (1, ("#FFFFFF", 0.9)),]
                     )
        
    def double_click(self, column, offset_x, offset_y):
        if self.is_expand:
            self.unexpand()
        else:
            self.expand()
        
    def add_child_item(self):
        items = []
        second_category_names = self.data_manager.get_second_category(self.first_category_name)
        for second_category_name in second_category_names:
            items.append(SecondCategoryItem(self.first_category_name, second_category_name, self.data_manager))
            
        self.child_items = items
        self.add_items_callback(self.child_items, self.row_index + 1)
        
    def delete_chlid_item(self):
        self.delete_items_callback(self.child_items)
    
    def expand(self):
        self.is_expand = True
        
        self.add_child_item()
            
        if self.redraw_request_callback:
            self.redraw_request_callback(self)
        global_event.emit("category-expand", self)
    
    def unexpand(self):
        self.is_expand = False
        
        self.delete_chlid_item()
    
        if self.redraw_request_callback:
            self.redraw_request_callback(self)
class Browser(gtk.VBox, SignalContainer):
    
    def __init__(self, db_query):
        
        gtk.VBox.__init__(self)
        SignalContainer.__init__(self)
        
        self.__db_query = db_query
        self._tree = {}
        self.__selected_tag = {"album": [], "artist": [], "genre": []}
        self.view_mode = ICON_VIEW_MODE
        self.__search_flag = False
        self.__song_cache_items = []
        self.__cover_cache_items = []
        
        # init widget.
        self.entry_box = SearchEntry("")
        self.entry_box.set_size(155, 22)
        self.entry_box.entry.connect("changed", self.__search_cb)
        
        # upper box.
        self.back_button = self.__create_simple_button("back", self.__switch_to_filter_view)
        self.back_button.set_no_show_all(True)
        back_align = gtk.Alignment()
        back_align.set(0.5, 0.5, 0, 0)
        back_align.set_padding(0, 0, 0, 10)
        back_align.add(self.back_button)
        
        # path control
        self.path_combo_box = ComboMenuButton()
        self.path_combo_box.connect("list-actived", lambda w: self.update_path_list_view())
        self.path_combo_box.connect("combo-actived", lambda w, k : self.update_path_filter_view(k))
        path_combo_align = gtk.Alignment()
        path_combo_align.set_padding(0, 0, 10, 0)
        path_combo_align.add(self.path_combo_box)
        self.path_combo_box.set_no_show_all(True)
        
        upper_box = gtk.HBox(spacing=5)
        upper_box.pack_start(path_combo_align, False, False)
        upper_box.pack_start(create_right_align(), True, True)
        upper_box.pack_start(back_align, False, False)
        entry_box_align = gtk.Alignment()
        entry_box_align.set(0.5, 0.5, 0, 0)
        entry_box_align.set_padding(1, 0, 0, 0)
        entry_box_align.add(self.entry_box)
        upper_box.pack_start(entry_box_align, False, False)
        
        upper_box_align = gtk.Alignment()
        upper_box_align.set_padding(0, 10, 0, 10)
        upper_box_align.set(0.5, 0.5, 1, 1)
        upper_box_align.connect("expose-event", self.expose_upper_box_mask)
        upper_box_align.add(upper_box)
        
        self.categorybar_status = "artist"
        self.filter_categorybar = OptionBar(
            [(app_theme.get_pixbuf("filter/artist_normal.png"), app_theme.get_pixbuf("filter/artist_press.png"),
              _("By artist"), lambda : self.reload_filter_view("artist", True)),
             (app_theme.get_pixbuf("filter/album_normal.png"), app_theme.get_pixbuf("filter/album_press.png"),
              _("By album"), lambda : self.reload_filter_view("album", True)),
             (app_theme.get_pixbuf("filter/genre_normal.png"), app_theme.get_pixbuf("filter/genre_press.png"),
              _("By genre"), lambda : self.reload_filter_view("genre", True)),]
                                            )
        
        # Song path bar.
        self.__current_path = None
        self.current_icon_item = None
        self.reload_path_flag = True
        self.path_categorybar = SongPathBar(_("Local"))
        self.path_categorybar.set_size_request(-1, 205)
        
        # Song import bar.
        self.import_categorybar = SongImportBar()
        self.import_categorybar.reload_items(
            [
             (_("Scan Home dir"), lambda : ImportFolderJob([os.path.expanduser("~")])),                
             (_("Select dir to scan"), lambda : ImportFolderJob()),
             (_("Refresh library"), lambda : ReloadDBJob())]
            )
        
        # iconview.
        self.filter_view = IconView(10, 10)
        targets = [("text/deepin-songs", gtk.TARGET_SAME_APP, 1), ("text/uri-list", 0, 2)]
        self.filter_view.drag_source_set(gtk.gdk.BUTTON1_MASK, targets, gtk.gdk.ACTION_COPY)
        self.filter_view.connect("drag-data-get", self.__on_drag_data_get) 
        self.filter_view.connect("double-click-item", self.__on_double_click_item)
        self.filter_view.connect("single-click-item", self.__on_single_click_item)
        self.filter_view.draw_mask  = self.draw_filter_view_mask
        self.filter_scrolled_window = ScrolledWindow()
        self.filter_scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
        self.filter_scrolled_window.add_child(self.filter_view)
        
        # songs_view
        self.songs_view = MultiDragSongView()
        self.songs_view.add_titles([_("Title"), _("Artist"), _("Album"), _("Added time")])
        self.songs_scrolled_window = ScrolledWindow(0, 0)
        self.songs_scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
        self.songs_scrolled_window.add_child(self.songs_view)
        
        # left_vbox
        align = gtk.Alignment()
        align.set(0, 1, 0, 0)
        left_box = gtk.VBox(spacing=10)
        left_box.set_size_request(140, -1)
        left_box.pack_start(self.filter_categorybar, False, False)
        left_box.pack_start(self.create_separator_box(), False, False)
        left_box.pack_start(self.path_categorybar, False, False)
        left_box.pack_start(self.create_separator_box(), False, False)
        left_box.pack_start(self.import_categorybar, False, False)
        left_box.pack_start(align, True, True)
        left_box.connect("expose-event", self.expose_left_box_mask)
        
        # right_box.
        self.right_box = gtk.VBox()
        self.right_box.add(self.filter_scrolled_window)
        
        # swith_box
        right_box_align = gtk.Alignment()
        right_box_align.set_padding(0, 0, 0, 2)
        right_box_align.set(1, 1, 1, 1)
        right_box_align.add(self.right_box)
        browser_box = gtk.VBox()
        browser_box.pack_start(upper_box_align,  False, False)
        browser_box.pack_start(right_box_align, True, True)
        
        body_box = gtk.HBox()
        body_box.pack_start(left_box, False, False)
        body_box.pack_start(browser_box, True, True)
        self.pack_start(body_box, True, True)
        
        self.reload_flag = False
        Dispatcher.connect("reload-browser", self.reload_browser)
        gobject.timeout_add(5000, self.interval_reload_browser)
        
    def __create_simple_button(self, name, callback):    
        button = ImageButton(
            app_theme.get_pixbuf("filter/%s_normal.png" % name),
            app_theme.get_pixbuf("filter/%s_hover.png" % name),
            app_theme.get_pixbuf("filter/%s_press.png" % name),
            )
        if callback:
            button.connect("clicked", callback)
        return button    
    
    def expose_left_box_mask(self, widget, event):
        cr = widget.window.cairo_create()
        rect = widget.allocation
        draw_alpha_mask(cr, rect.x, rect.y, rect.width, rect.height, "layoutRight")
        return False
    
    def expose_upper_box_mask(self, widget, event):
        cr = widget.window.cairo_create()
        rect = widget.allocation
        draw_alpha_mask(cr, rect.x, rect.y, rect.width - 2, rect.height, "layoutLast")
        return False
    
    def draw_filter_view_mask(self, cr, x, y, width, height):
        draw_alpha_mask(cr, x, y, width, height, "layoutLast")
    
    def create_separator_box(self, padding_x=0, padding_y=0):
        separator_box = HSeparator(
            app_theme.get_shadow_color("hSeparator").get_color_info(),
            padding_x, padding_y)
        return separator_box
    
    def __switch_to_filter_view(self, widget):
        self.back_button.set_no_show_all(True)
        self.back_button.hide()
        self.switch_box(self.right_box, self.filter_scrolled_window)
        self.view_mode = ICON_VIEW_MODE
        
    def reload_filter_view(self, tag="artist", switch=False, use_path=False):    
        self.entry_box.entry.set_text("")
        self.back_button.set_no_show_all(True)
        self.back_button.hide()
        self.categorybar_status = tag
        self.filter_view.clear()
            
        if not use_path:    
            self.path_categorybar.set_index(-1)    
        
        _dict = self.get_infos_from_db(tag)
        keys = _dict.keys()
        keys.sort()
        items = []
        all_nb = len(self.__db_query.get_all_songs())
        items.append(IconItem(("deepin-all-songs", "deepin-all-songs", all_nb, tag)))

        for key in keys:
            value, nb = _dict[key] 
            items.append(IconItem((key, value, nb, tag)))
        self.filter_view.add_items(items)    
        
        if switch:
            self.switch_box(self.right_box, self.filter_scrolled_window)
            self.view_mode = ICON_VIEW_MODE
            if not use_path:
                self.change_combo_box_status(True)
            
    def get_infos_from_db(self, tag, values=None):
        genres = []
        artists = []
        extened = False
        return self.__db_query.get_info(tag, genres, artists, values, extened)
    
    def get_attr_infos_from_db(self, info_type="###ALL###", song_dir=None):
        return self.__db_query.get_attr_infos(info_type, song_dir)
    
    def change_combo_box_status(self, hide=False):    
        if not hide:
            self.path_combo_box.set_no_show_all(False)
            self.path_combo_box.show_all()
        else:    
            self.path_combo_box.hide_all()
            self.path_combo_box.set_no_show_all(True)
    
    def update_path_songs(self, key):
        self.back_button.set_no_show_all(True)
        self.back_button.hide()
        self.filter_categorybar.set_index(-1)        
        self.__current_path = key        
        self.change_combo_box_status()        
        
        if self.path_combo_box.get_combo_active():
            self.update_path_filter_view(self.path_combo_box.current_status)
        else:    
            songs = self.__db_query.get_attr_songs(key)
            self.update_songs_view(songs)
            
    def update_path_list_view(self):        
        self.back_button.set_no_show_all(True)
        self.back_button.hide()
        if self.__current_path == "###ALL###":
            songs = self.__db_query.get_all_songs()
        else:    
            songs = self.__db_query.get_attr_songs(self.__current_path)
        self.update_songs_view(songs)    
            
    def update_path_filter_view(self, name):
        self.back_button.set_no_show_all(True)
        self.back_button.hide()
        self.entry_box.entry.set_text("")
        
        self.filter_view.clear()
        if self.__current_path == "###ALL###":
            self.reload_filter_view(name, True, True)
        else:    
            attr_infos = self.get_attr_infos_from_db(name, self.__current_path)
            items = []
            for info in attr_infos:
                key, value, nb, tag = info
                items.append(IconItem((key, value, nb, tag)))
            self.filter_view.add_items(items)    
            
            if self.view_mode != ICON_VIEW_MODE:
                self.switch_box(self.right_box, self.filter_scrolled_window)
                self.view_mode = ICON_VIEW_MODE
                
    def reload_song_path(self):
        path_infos = self.get_attr_infos_from_db()
        if path_infos:
            self.path_categorybar.update_items([(name[0], self.update_path_songs, name[1]) for name in path_infos])
    
    def connect_to_db(self):    
        self.autoconnect(self.__db_query, "added", self.__added_song_cb)
        self.autoconnect(self.__db_query, "removed", self.__removed_song_cb)
        self.autoconnect(self.__db_query, "update-tag", self.__update_tag_view)
        self.autoconnect(self.__db_query, "full-update", self.__full_update)
        self.autoconnect(self.__db_query, "quick-update", self.__quick_update)
        self.__db_query.set_query("")                
        
    def __added_song_cb(self, db_query, songs):
        self.reload_song_path()        
        self.reload_flag = True

    def __removed_song_cb(self, db_query, songs):
        self.reload_song_path()        
        self.reload_flag = True
            
    def reload_browser(self,  obj, infos):
        if infos:
            self.reload_flag = True
            
    def interval_reload_browser(self):        
        if self.reload_flag and not self.__search_flag:
            self.reload_all_items()
            self.reload_flag = False    
        return True    
                    
    def reload_all_items(self):                
        if self.view_mode == ICON_VIEW_MODE:
            if self.path_categorybar.get_index() == -1:
                self.reload_filter_view(self.categorybar_status)
            else:    
                self.update_path_filter_view(self.path_combo_box.current_status)
        else:        
            if self.path_categorybar.get_index() == -1:
                if self.current_icon_item:
                    self.update_category_songs_view(self.current_icon_item)
            else:        
                if self.current_icon_item:
                    self.update_path_songs_view(self.current_icon_item)
    
    def __update_tag_view(self, db_query, tag, values):
        if values:
            self.reload_flag = True
    
    def __quick_update(self, db_query, songs):
        pass
        
    def __full_update(self, db_query):    
        self.reload_filter_view()
        self.reload_song_path()
    
    def __get_selected_songs(self, tag="artist"):
        artists = []
        albums = []
        genres = []
        
        if tag == "artist":
            artists = self.__selected_tag["artist"]
        elif tag == "album":    
            albums = self.__selected_tag["album"]
        elif tag == "genre":    
            genres = self.__selected_tag["genre"]
            
        return self.__db_query.get_songs(genres, artists, albums)    
    
    def __on_drag_data_get(self, widget, context, selection, info, timestamp):
        item = self.filter_view.highlight_item
        if not item:
            return 
        
        if item.key_name == "deepin-all-songs":
            songs = self.__db_query.get_all_songs()
        else:    
            del self.__selected_tag[item.tag]
            self.__selected_tag[item.tag] = [item.key_name]
            songs = self.__get_selected_songs(item.tag)
        if not songs:
            return 
        songs = list(songs)
        songs.sort()
        list_uris = list([ song.get("uri") for song in songs])
        selection.set("text/deepin-songs", 8, "\n".join(list_uris))
        selection.set_uris(list_uris)
    
    def __on_double_click_item(self, widget,  item, x, y):
        self.current_icon_item = item
        self.entry_box.entry.set_text("")
        self.back_button.set_no_show_all(False)
        self.back_button.show()
        if self.path_categorybar.get_index() == -1:
            self.update_category_songs_view(item)
        else:    
            self.update_path_songs_view(item)
        
    def update_category_songs_view(self, item):    
        if item.key_name == "deepin-all-songs":
            songs = self.__db_query.get_all_songs()
        else:    
            self.__selected_tag[item.tag] = [item.key_name]
            songs = self.__get_selected_songs(item.tag)
        self.update_songs_view(songs, item.tag)    
        
    def update_path_songs_view(self, item):    
        songs = self.__db_query.get_attr_songs(self.__current_path, item.tag, item.key_name)
        if songs:
            self.update_songs_view(songs, item.tag)
        
    def update_songs_view(self, items, sort_key="title"):    
        self.songs_view.clear()
        self.entry_box.entry.set_text("")
        self.songs_view.add_songs(items)
        self.songs_view.set_sort_keyword(sort_key)
        if self.view_mode != LIST_VIEW_MODE:
            self.switch_box(self.right_box, self.songs_scrolled_window)
            self.view_mode= LIST_VIEW_MODE
        
    def __on_single_click_item(self, widget, item, x, y):    
        if item.pointer_in_play_rect(x, y):
            if item.key_name == "deepin-all-songs":
                songs = self.__db_query.get_all_songs()
            else:    
                del self.__selected_tag[item.tag]
                self.__selected_tag[item.tag] = [item.key_name]
                songs = self.__get_selected_songs(item.tag)
            if not songs:
                return 
            songs = list(songs)
            songs.sort()
            if not songs:
                return 
            Dispatcher.play_and_add_song(songs)
        
        
    def __search_cb(self, widget, text):    
        if self.view_mode == LIST_VIEW_MODE:
            if not self.__search_flag:
                self.__song_cache_items = self.songs_view.items[:]
                
            # Clear song_view select status    
            self.songs_view.select_rows = []    
            if text != "":
                self.__search_flag = True
                results = filter(lambda item: text.lower().replace(" ", "") in item.get_song().get("search", ""), self.__song_cache_items)
                self.songs_view.items = results
                self.songs_view.update_item_index()
                self.songs_view.update_vadjustment()
            else:    
                self.__search_flag = False
                self.songs_view.items = self.__song_cache_items
                self.songs_view.update_item_index()
                self.songs_view.update_vadjustment()
            self.songs_view.queue_draw()    
        elif self.view_mode == ICON_VIEW_MODE:    
            if not self.__search_flag:
                self.__cover_cache_items = self.filter_view.items[:]
            if text != "":    
                self.__search_flag = True
                results = filter(lambda item: text.lower().replace(" ", "") in item.retrieve, self.__cover_cache_items)
                self.filter_view.items = results
            else:    
                self.__search_flag = False
                self.filter_view.items = self.__cover_cache_items
            self.filter_view.queue_draw()    
        
    def switch_box(self, parent, child):    
        switch_tab(parent, child)