Subversion Repositories basico

Rev

Rev 111 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

#!/usr/bin/python
# -*- coding: utf-8 -*-
# File: sapnoteview.py
# Author: Tomás Vírseda
# License: GPL v3
# Description: SAPNoteView GTK.TreeView widgets

from enum import IntEnum
import traceback as tb
from cgi import escape

import gi
gi.require_version('Gdk', '3.0')
gi.require_version('Gtk', '3.0')
from gi.repository import Gdk
from gi.repository import Gio
from gi.repository import Gtk
from gi.repository import Pango
from gi.repository.GdkPixbuf import Pixbuf
from gi.repository import Pango
from datetime import datetime
from dateutil import parser as dateparser

from .log import get_logger

#~ Enum(value='Column', names=<...>, *, module='...', qualname='...', type=<mixed-in class>, start=1)

class Column(IntEnum):
    rowtype = 0
    checkbox = 1
    icon = 2
    component = 3
    category = 4
    type = 5
    id = 6
    title = 7
    priority = 8
    language = 9
    released = 10


class SAPNoteView(Gtk.TreeView):
    def __init__(self, app):
        self.app = app
        LOG_FILE = self.app.get_file('LOG')
        LOG_NAME = self.__class__.__name__
        self.log = get_logger(LOG_NAME, LOG_FILE)
        self.get_services()
        self.toggled = 0
        self.selected = set()
        self.count = 0


        # Setup treeview and model
        Gtk.TreeView.__init__(self)
        self.model = Gtk.TreeStore(
            str,            # RowType@RowId
            bool,           # CheckBox
            Pixbuf,         # Icon
            str,            # Component key
            str,            # Category
            str,            # Type
            str,            # SAP Note Id
            str,            # Title
            str,            # Priority
            str,            # Language
            str             # Release date
        )
        self.set_model(self.model)

        # Setup columns
        # RowType
        self.renderer_rowtype = Gtk.CellRendererText()
        self.column_rowtype = Gtk.TreeViewColumn('', self.renderer_rowtype, text=Column.rowtype.value)
        self.column_rowtype.set_visible(False)
        self.column_rowtype.set_expand(False)
        self.column_rowtype.set_clickable(False)
        self.column_rowtype.set_sort_indicator(False)
        self.append_column(self.column_rowtype)

        # Checkbox
        self.renderer_checkbox = Gtk.CellRendererToggle()
        self.renderer_checkbox.connect("toggled", self.on_cell_toggled)
        self.column_checkbox = Gtk.TreeViewColumn('', self.renderer_checkbox, active=1)
        self.column_checkbox.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        self.column_checkbox.set_visible(False)
        self.column_checkbox.set_expand(False)
        self.column_checkbox.set_clickable(False)
        self.column_checkbox.set_sort_indicator(False)
        self.append_column(self.column_checkbox)
        self.column_checkbox.set_cell_data_func(self.renderer_checkbox, self.cell_data_func)

        # Icon
        self.renderer_icon = Gtk.CellRendererPixbuf()
        self.renderer_icon.set_alignment(0.0, 0.5)
        self.column_icon = Gtk.TreeViewColumn('', self.renderer_icon, pixbuf=2)
        self.column_icon.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        self.column_icon.set_visible(False)
        self.column_icon.set_expand(False)
        self.column_icon.set_clickable(False)
        self.column_icon.set_sort_indicator(False)
        self.append_column(self.column_icon)

        # Component key
        self.renderer_compkey = Gtk.CellRendererText()
        self.renderer_compkey.set_property('background', 'Lemon Chiffon')
        self.column_component = Gtk.TreeViewColumn('SAP Notes', self.renderer_compkey, markup=3)
        self.column_component.set_visible(True)
        self.column_component.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        self.column_component.set_clickable(True)
        self.column_component.set_sort_indicator(True)
        self.column_component.set_sort_column_id(3)
        self.column_component.set_sort_order(Gtk.SortType.ASCENDING)
        self.column_component.set_expand(True)
        self.append_column(self.column_component)
        expander_column = self.column_component

        # Category
        self.renderer_cat = Gtk.CellRendererText()
        self.column_cat = Gtk.TreeViewColumn('Category', self.renderer_cat, markup=4)
        self.column_cat.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        self.column_cat.set_visible(False)
        self.column_cat.set_expand(False)
        self.column_cat.set_clickable(True)
        self.column_cat.set_sort_indicator(True)
        self.column_cat.set_sort_column_id(4)
        self.column_cat.set_sort_order(Gtk.SortType.ASCENDING)
        self.append_column(self.column_cat)

        # Type
        self.renderer_sntype = Gtk.CellRendererText()
        self.column_sntype = Gtk.TreeViewColumn('Type', self.renderer_sntype, markup=5)
        self.column_sntype.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        self.column_sntype.set_visible(False)
        self.column_sntype.set_expand(False)
        self.column_sntype.set_clickable(True)
        self.column_sntype.set_sort_indicator(True)
        self.column_sntype.set_sort_column_id(5)
        self.column_sntype.set_sort_order(Gtk.SortType.ASCENDING)
        self.append_column(self.column_sntype)

        # SAP Note Id
        self.renderer_sid = Gtk.CellRendererText()
        self.renderer_sid.set_alignment(1.0, 0.5)
        self.column_sid = Gtk.TreeViewColumn('SAP Note', self.renderer_sid, markup=6)
        self.column_sid.set_visible(False)
        self.column_sid.set_expand(False)
        self.column_sid.set_clickable(True)
        self.column_sid.set_sort_indicator(True)
        self.column_sid.set_sort_column_id(6)
        self.column_sid.set_sort_order(Gtk.SortType.ASCENDING)
        self.append_column(self.column_sid)

        # Title
        self.renderer_title = Gtk.CellRendererText()
        self.renderer_title.set_property('ellipsize', Pango.EllipsizeMode.MIDDLE)
        self.column_title = Gtk.TreeViewColumn('Title', self.renderer_title, markup=7)
        self.column_title.set_visible(False)
        self.column_title.set_expand(True)
        self.column_title.set_clickable(True)
        self.column_title.set_sort_indicator(True)
        self.column_title.set_sort_column_id(7)
        self.column_title.set_sort_order(Gtk.SortType.ASCENDING)
        self.append_column(self.column_title)

        # Priority
        self.renderer_priority = Gtk.CellRendererText()
        self.column_priority = Gtk.TreeViewColumn('Priority', self.renderer_priority, markup=8)
        self.column_priority.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        self.column_priority.set_visible(True)
        self.column_priority.set_expand(False)
        self.column_priority.set_clickable(True)
        self.column_priority.set_sort_indicator(True)
        self.column_priority.set_sort_column_id(8)
        self.column_priority.set_sort_order(Gtk.SortType.ASCENDING)
        self.append_column(self.column_priority)

        # Language
        self.renderer_lang = Gtk.CellRendererText()
        self.column_lang = Gtk.TreeViewColumn('Language', self.renderer_lang, markup=9)
        self.column_lang.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        self.column_lang.set_visible(False)
        self.column_lang.set_expand(False)
        self.column_lang.set_clickable(True)
        self.column_lang.set_sort_indicator(True)
        self.column_lang.set_sort_column_id(9)
        self.column_lang.set_sort_order(Gtk.SortType.ASCENDING)
        self.append_column(self.column_lang)

        # Release date
        self.renderer_reldate = Gtk.CellRendererText()
        self.column_rel = Gtk.TreeViewColumn('Released on', self.renderer_reldate, markup=10)
        self.column_rel.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        self.column_rel.set_visible(False)
        self.column_rel.set_expand(False)
        self.column_rel.set_clickable(True)
        self.column_rel.set_sort_indicator(True)
        self.column_rel.set_sort_column_id(10)
        self.column_rel.set_sort_order(Gtk.SortType.ASCENDING)
        self.append_column(self.column_rel)

        # TreeView common
        #~ self.set_level_indentation(6)
        self.set_can_focus(True)
        self.set_headers_visible(False)
        self.set_enable_search(True)
        self.set_hover_selection(False)
        self.set_grid_lines(Gtk.TreeViewGridLines.NONE)
        self.set_search_entry(self.gui.get_widget('stySearchInfo'))
        self.set_search_column(3)
        #~ self.set_row_separator_func(self.row_separator_func)

        # Selection
        self.selection = self.get_selection()
        self.selection.set_select_function(self.select_function)
        self.selection.set_mode(Gtk.SelectionMode.SINGLE)

        # Font
        font_desc = Pango.FontDescription('Monospace 10')
        if font_desc:
            self.modify_font(font_desc)

        # Go live
        self.connect('button_press_event', self.cb_button_press)
        self.connect('row-activated', self.double_click)
        self.connect('cursor-changed', self.row_changed)

        # Custom method to be implemented.
        self.prepare()

        self.show_all()


    def get_services(self):
        self.gui = self.app.get_service('GUI')


    def cell_data_func(self, column, cell, store, iter, data=None):
        """Do not show checkbox when row type is distinct of sapnote"""

        #### This is code is valid:
        #### It allows to hide checkboxes other than sapnote
        #~ row = store[iter][0]
        #~ row_type, key = row.split('@')

        #~ if row_type != 'sapnote':
            #~ cell.set_visible(False)
        #~ else:
            #~ cell.set_visible(True)

        cell.set_visible(True)


    def select_function(self, selection, store, path, current):
        return True

        #~ This code works: only allow select rows of type 'sapnote'
        #~ rowtype = store[path][0]
        #~ if rowtype == 'sapnote':
            #~ return True
        #~ else:
            #~ return False


    #~ def row_separator_func(self, model, iter):
        #~ """ Call user function to determine if this node is separator """
        #~ if iter and model.iter_is_valid(iter):
            #~ rowtype = model.get_value(iter, 0)
            #~ if row<type == 'sapnote':
                #~ return True
            #~ else:
                #~ return False
            #~ self.log.debug("Row type: %s" % rowtype)
            #~ return False
        #~ return False


    def get_services(self):
        self.gui = self.app.get_service("GUI")
        self.cb = self.app.get_service('Callbacks')
        self.menu = self.app.get_service("Menus")
        self.sap = self.app.get_service('SAP')
        self.im = self.app.get_service('IM')
        self.settings = self.app.get_service('Settings')
        self.plugins = self.app.get_service('Plugins')
        self.db = self.app.get_service('DB')



    def row_changed(self, treeview):
        selected = set()
        selection = treeview.get_selection()
        model, treeiters = selection.get_selected_rows()
        try:
            row = model[treeiters[0]][0]
            row_type, sid = row.split('@')
            matches = self.db.get_notes_by_node(row_type, sid)
            visor = self.gui.get_widget('visor')
            visor.populate(matches)
        except IndexError as error:
            self.log.error(error)
            row_type = ""
        except Exception as error:
            self.log.error(error)
            self.log.error(tb.format_exc())
            row_type = ""

        return

        if row_type == 'sapnote':
            trvprops = self.gui.get_widget('trvprops')
            modelprops = trvprops.get_model()
            sapnote = self.db.get_sapnote_metadata(sid)

            modelprops.clear()
            modelprops.append(None, ["<big><b>SAP Note Id</b></big>", "<big>%s</big>" % sid])
            modelprops.append(None, ["<big><b>Title</b></big>", "<big>%s</big>" %  sapnote['title']])
            modelprops.append(None, ["<big><b>Version</b></big>", "<big>%s</big>" %  sapnote['version']])
            modelprops.append(None, ["<big><b>Component Key</b></big>", "<big>%s</big>" %  sapnote['componentkey']])
            modelprops.append(None, ["<big><b>Component Text</b></big>", "<big>%s</big>" %  sapnote['componenttxt']])
            modelprops.append(None, ["<big><b>Category</b></big>", "<big>%s</big>" %  sapnote['category']])
            modelprops.append(None, ["<big><b>Type</b></big>", "<big>%s</big>" %  sapnote['type']])
            modelprops.append(None, ["<big><b>Priority</b></big>", "<big>%s</big>" %  sapnote['priority']])
            modelprops.append(None, ["<big><b>Language</b></big>", "<big>%s</big>" %  sapnote['language']])
            modelprops.append(None, ["<big><b>Released</b></big>", "<big>%s</big>" %  sapnote['releaseon']])
            modelprops.append(None, ["<big><b>Downloaded</b></big>", "<big>%s</big>" %  str(sapnote['feedupdate'])])
            modelprops.append(None, ["<big><b>Bookmarked</b></big>", "<big>%s</big>" %  str(sapnote['bookmark'])])
        else:
            pass


    def double_click(self, treeview, row, col):
        model = treeview.get_model()
        row = model[row][0]
        row_type, sid = row.split('@')
        self.set_select_notes([sid])
        self.cb.actions_browse()


    def cb_button_press(self, treeview, event, data=None):
        if event.button == 3:
            x = int(event.x)
            y = int(event.y)
            time = event.time
            pthinfo = self.get_path_at_pos(x,y)
            if pthinfo is not None:
                path,col,cellx,celly = pthinfo
                self.grab_focus()
                self.set_cursor(path,col,0)

                model = treeview.get_model()
                treeiter = model.get_iter(path)
                for n in range(9):
                    self.log.debug("%d -> %s" % (n, model.get_value(treeiter, n)))
                rowtype, value = model.get_value(treeiter, 0).split('@')
                self.log.debug("Row type: %s ->   %s" % (rowtype, value))
                if rowtype == 'task':
                    task = model.get_value(treeiter, 6)
                    self.popup_menu = self.menu.create_popup_menu_by_task(task)
                elif rowtype == 'componentkey':
                    component = model.get_value(treeiter, 3)
                    self.popup_menu = self.menu.create_popup_menu_by_component(component)
                elif rowtype == 'sapnote':
                    sid = model.get_value(treeiter, 6)
                    self.popup_menu = self.menu.create_popup_menu_by_sapnote(sid)
                else:
                    return False

                if self.popup_menu is not None:
                    self.popup_menu.show_all()
                    self.popup_menu.popup(None, None, None, None, event.button, event.time)
                    return True # event has been handled
                else:
                    return False


    def populate(self, sapnotes):
        self.set_headers_visible(False)
        self.column_sid.set_visible(False)
        self.column_rel.set_visible(False)
        self.column_priority.set_visible(False)
        self.column_icon.set_visible(False)
        self.column_checkbox.set_visible(False)

        if self.view == 'components':
            self.populate_by_components(sapnotes)
        elif self.view == 'description':
            self.populate_by_component_descriptions(sapnotes)
        elif self.view == 'projects':
            self.populate_by_projects(sapnotes)
        elif self.view == 'tasks':
            self.populate_by_tasks(sapnotes)
        elif self.view == 'bookmarks':
            self.populate_by_bookmarks(sapnotes)
        elif self.view == 'tags':
            self.populate_by_tags(sapnotes)
        elif self.view == 'category':
            self.populate_by_category(sapnotes)
        elif self.view == 'chronologic':
            self.populate_by_chronologic(sapnotes)
        elif self.view == 'priority':
            self.populate_by_priority(sapnotes)
        elif self.view == 'type':
            self.populate_by_type(sapnotes)
        else:
            self.populate_by_components(sapnotes)
        # Save last populated list of sap notes
        total = self.db.get_total()
        notesid = set()
        for sapnote in sapnotes:
            notesid.add(sapnote)

        if len(notesid) < total:
            self.settings.set_config_value('Notes', ','.join(notesid))

        self.log.debug("View '%s' populated with %d SAP Notes" % (self.view, len(sapnotes)))


    def populate_by_bookmarks(self, sapnotes):
        self.model.clear()
        self.set_headers_visible(False)
        self.column_sid.set_visible(True)
        self.column_icon.set_visible(False)
        self.column_component.set_visible(True)
        self.column_component.set_expand(False)
        self.column_title.set_visible(True)
        self.column_title.set_expand(True)
        self.column_priority.set_expand(False)
        self.column_cat.set_visible(False)
        self.column_sid.set_expand(False)
        self.column_component.set_title('Component')
        icon_bookmark = self.im.get_icon('bookmark')

        for sid in sapnotes:
            try:
                bookmark = sapnotes[sid]['bookmark'] # True or False
            except:
                bookmark = False

            if bookmark:
                sapnote = self.get_node_sapnote_bookmark(sapnotes[sid], sid)
                self.model.append(None, sapnote)


    def get_node_project(self, project, icon):
        if project == '':
            title = "<span size='18000'><b>No project assigned</b></span>"
        else:
            title = "<span size='18000'><b>%s</b></span>" % project

        node = []
        node.append('project@%s' % project)
        node.append(0)
        node.append(icon)
        node.append(title) # Component
        node.append("") # Category
        node.append("") # Type
        node.append("") # Id
        node.append("") # Title
        node.append("") # Priority
        node.append("") # Lang
        node.append("") # Release on
        return node


    def get_node_task(self, task, icon):
        if task == '':
            title = "No task assigned"
        else:
            title = "%s" % task

        node = []
        node.append('task@%s' % task)
        node.append(0)
        node.append(icon)
        node.append(title) # Component
        node.append("") # Category
        node.append("") # Type
        node.append("") # Id
        node.append("") # Title
        node.append("") # Priority
        node.append("") # Lang
        node.append("") # Release on
        return node


    def populate_by_projects(self, sapnotes):
        self.model.clear()
        treepids = {}
        icon_noproject = self.im.get_icon('noproject')
        icon_project = self.im.get_icon('project')
        icon_sapnote = self.im.get_icon('fingerprint')
        icon_bookmark = self.im.get_icon('bookmark')
        self.column_component.set_title('Projects')

        if len(sapnotes) == 0:
            return

        node = self.get_node_project('', icon_noproject)
        pid = self.model.append(None, node)
        treepids['None'] = pid

        notlinked = 0
        for sid in sapnotes:
            try:
                projects = sapnotes[sid]['projects']
            except:
                projects = []


            if len(projects) == 0:
                #~ SAP Note not linked to any project
                sapnote = self.get_node_sapnote(sapnotes[sid], sid)
                self.model.append(treepids['None'], sapnote)
                notlinked += 1
            else:
                #~ SAP Note linked to projects
                for project in projects:
                    try:
                        pid = treepids[project]
                    except:
                        node = self.get_node_project(project, icon_project)
                        pid = self.model.append(None, node)
                        treepids[project] = pid

                    sapnote = self.get_node_sapnote(sapnotes[sid], sid)
                    self.model.append(pid, sapnote)

        if notlinked == 0:
            self.model.remove(treepids['None'])


    def populate_by_tags(self, sapnotes):
        pass


    def populate_by_tasks(self, sapnotes):
        self.model.clear()
        treepids = {}
        icon_notask = self.im.get_icon('notask', 32, 32)
        icon_task = self.im.get_icon('task', 32, 32)
        icon_sapnote = self.im.get_icon('fingerprint', 32, 32)
        icon_bookmark = self.im.get_icon('bookmark', 32, 32)
        self.column_component.set_title('Tasks')
        self.set_headers_visible(False)
        self.column_sid.set_visible(True)
        self.column_icon.set_visible(False)
        self.column_component.set_visible(True)
        self.column_component.set_expand(True)
        self.column_title.set_visible(False)
        self.column_title.set_expand(True)
        self.column_priority.set_expand(False)
        self.column_cat.set_visible(False)
        self.column_sid.set_expand(False)

        if len(sapnotes) == 0:
            return

        #~ "No task assigned" node creation
        node = self.get_node_task('', icon_notask)
        pid = self.model.append(None, node)
        treepids['None'] = pid

        scomp = set()
        dcomp = {}
        taskset = set()
        for sid in sapnotes:
            try:
                # setup components
                compkey = escape(sapnotes[sid]['componentkey'])
                comptxt = escape(sapnotes[sid]['componenttxt'])
                scomp.add(compkey)
                dcomp[compkey] = comptxt

                # setup tasks
                for task in sapnotes[sid]['tasks']:
                    taskset.add(task)
            except: pass

        lcomp = list(scomp)
        lcomp.sort()

        tasklist = []
        tasklist.extend(taskset)
        tasklist.sort()

        for task in tasklist:
            node = self.get_node_task(task, icon_task)
            pid = self.model.append(None, node)
            treepids[task] = pid

        return
        notask = 0
        for sid in sapnotes:
            #~ Get category
            compkey = escape(sapnotes[sid]['componentkey'])
            catname = escape(sapnotes[sid]['category'])


            #~ Get tasks for this sapnote
            try:
                tasks = sapnotes[sid]['tasks']
            except:
                sapnotes[sid]['tasks'] = tasks = []


            if len(tasks) == 0:
                # Components in no-task node
                cmpkey = "notask" + '-' + compkey
                comptxt = dcomp[compkey]
                try:
                    pid = treepids[cmpkey]
                except:
                    node = self.get_node_component(compkey, comptxt)
                    pid = self.model.append(treepids['None'], node)
                    treepids[cmpkey] = pid

                # Categories in no-task node
                catkey = "notask" + '-' + compkey + '-' + catname
                try:
                    cid = treepids[catkey]
                except:
                    node = self.get_node_category(sapnotes[sid])
                    cid = self.model.append(pid, node)
                    treepids[catkey] = cid

                #~ SAP Note not linked to any task
                sapnote = self.get_node_sapnote_task(sapnotes[sid], sid)
                self.model.append(cid, sapnote)
                notask += 1
            else:
                #~ SAP Note linked to tasks
                for task in sapnotes[sid]['tasks']:
                    # Components in task node
                    cmpkey = task + '-' + compkey
                    comptxt = dcomp[compkey]
                    pid = treepids[task]
                    sapnote = self.get_node_sapnote_task(sapnotes[sid], sid)
                    self.model.append(treepids[task], sapnote)

        #~ "No task assigned" node deletion if no tasks at all
        if notask == 0:
            self.model.remove(treepids['None'])


    def populate_by_priority(self, sapnotes):
        self.model.clear()
        treepids = {}

        self.set_headers_visible(False)
        self.column_sid.set_visible(False)
        self.column_icon.set_visible(False)
        self.column_component.set_visible(True)
        self.column_component.set_expand(False)
        self.column_title.set_visible(False)
        self.column_title.set_expand(False)
        self.column_priority.set_expand(False)
        self.column_cat.set_visible(False)
        self.column_sid.set_expand(False)

        if len(sapnotes) == 0:
            return

        scomp = set()
        dcomp = {}
        pset = set()

        for sid in sapnotes:
            try:
                priority = sapnotes[sid]['priority']
                pset.add(priority)
            except: pass

        plist = []
        plist.extend(pset)
        plist.sort()

        for priority in plist:
            node = self.get_node_priority(priority)
            pid = self.model.append(None, node)
            treepids[priority] = pid

        #~ for sid in sapnotes:
            #~ priority = sapnotes[sid]['priority']
            #~ pid = treepids[priority]
            #~ sapnote = self.get_node_sapnote(sapnotes[sid], sid)
            #~ self.model.append(pid, sapnote)


    def populate_by_type(self, sapnotes):
        self.model.clear()
        treepids = {}

        self.set_headers_visible(False)
        self.column_sid.set_visible(True)
        self.column_icon.set_visible(False)
        self.column_component.set_visible(True)
        self.column_component.set_expand(False)
        self.column_title.set_visible(True)
        self.column_title.set_expand(True)
        self.column_priority.set_expand(False)
        self.column_cat.set_visible(False)
        self.column_sid.set_expand(False)

        if len(sapnotes) == 0:
            return

        scomp = set()
        dcomp = {}
        pset = set()

        for sid in sapnotes:
            try:
                sntype = sapnotes[sid]['type']
                pset.add(sntype)
            except: pass

        plist = []
        plist.extend(pset)
        plist.sort()

        for sntype in plist:
            node = self.get_node_type(sntype)
            pid = self.model.append(None, node)
            treepids[sntype] = pid

        #~ for sid in sapnotes:
            #~ sntype = sapnotes[sid]['type']
            #~ pid = treepids[sntype]
            #~ sapnote = self.get_node_sapnote_type(sapnotes[sid], sid)
            #~ self.model.append(pid, sapnote)


    def populate_by_category(self, sapnotes):
        self.model.clear()
        treepids = {}
        icon_nocat = self.im.get_icon('notask')
        icon_cat = self.im.get_icon('category')
        icon_sapnote = self.im.get_icon('fingerprint')
        icon_bookmark = self.im.get_icon('bookmark')
        self.column_component.set_title('Categories')
        self.set_headers_visible(False)
        self.column_sid.set_visible(True)
        self.column_icon.set_visible(False)
        self.column_component.set_visible(True)
        self.column_component.set_expand(True)
        self.column_title.set_visible(False)
        self.column_title.set_expand(True)
        self.column_priority.set_expand(False)
        self.column_cat.set_visible(False)
        self.column_sid.set_expand(False)

        if len(sapnotes) == 0:
            return

        scomp = set()
        dcomp = {}
        catset = set()

        for sid in sapnotes:
            try:
                cat = sapnotes[sid]['category']
                catset.add(cat)
            except: pass

        catlist = []
        catlist.extend(catset)
        catlist.sort()

        for cat in catlist:
            node = self.get_node_category_view(cat)
            pid = self.model.append(None, node)
            treepids[cat] = pid

        #~ for sid in sapnotes:
            #~ category = sapnotes[sid]['category']
            #~ pid = treepids[category]
            #~ sapnote = self.get_node_sapnote_category(sapnotes[sid], sid)
            #~ self.model.append(pid, sapnote)


    def populate_by_chronologic(self, sapnotes):
        self.model.clear()
        treepids = {}

        self.column_component.set_title('Download date')
        self.set_headers_visible(False)
        self.column_sid.set_visible(True)
        self.column_icon.set_visible(False)
        self.column_component.set_visible(True)
        self.column_component.set_expand(True)
        self.column_title.set_visible(False)
        self.column_title.set_expand(True)
        self.column_priority.set_expand(False)
        self.column_cat.set_visible(False)
        self.column_sid.set_expand(False)

        if len(sapnotes) == 0:
            return

        years = set()
        months = set()
        days = set()
        for sid in sapnotes:
            try:
                downloaded = dateparser.parse(sapnotes[sid]['feedupdate'])
                year = "%d" % downloaded.year
                month = "%02d" % downloaded.month
                day = "%02d" % downloaded.day
                key_year    = year
                key_month   = year + month
                key_day     = year + month + day
                years.add(key_year)
                months.add(key_month)
                days.add(key_day)
            except:
                pass
        years = list(years)
        years.sort(reverse=True)
        months = list(months)
        months.sort(reverse=True)
        days = list(days)
        days.sort(reverse=True)

        for key_year in years:
            try:
                treepids[key_year]
            except:
                adate = key_year + '0101'
                downloaded = dateparser.parse(adate)
                node = self.get_node_date_year(downloaded, key_year)
                treepids[key_year] = self.model.append(None, node)

        for key_month in months:
            try:
                treepids[key_month]
            except:
                adate = key_month + '01'
                downloaded = dateparser.parse(adate)
                node = self.get_node_date_month(downloaded, key_month)
                key_year = key_month[0:4]
                treepids[key_month] = self.model.append(treepids[key_year], node)

        for key_day in days:
            try:
                treepids[key_day]
            except:
                downloaded = dateparser.parse(key_day)
                key_month = key_day[0:6]
                node = self.get_node_date_day(downloaded, key_day)
                treepids[key_day] = self.model.append(treepids[key_month], node)

        #~ for sid in sapnotes:
            #~ downloaded = dateparser.parse(sapnotes[sid]['feedupdate'])
            #~ year = "%d" % downloaded.year
            #~ month = "%02d" % downloaded.month
            #~ day = "%02d" % downloaded.day
            #~ key_year    = year
            #~ key_month   = year + month
            #~ key_day     = year + month + day
            #~ node = self.get_node_sapnote_chronologic(sapnotes[sid], sid)
            #~ self.log.debug("KeyDay %s for SAP Note %s" % (treepids[key_day], sid))
            #~ self.model.append(treepids[key_day], node)

        self.collapse_all()
        self.expand_all()


    def get_node_component(self, compkey, comptxt):
        icon = self.im.get_icon('component', 32, 32)
        node = []
        count = len(self.db.get_notes_by_node("componentkey", compkey))
        component = "%s" % (compkey)

        node.append('componentkey@%s' % compkey)
        node.append(0)
        node.append(icon)
        node.append(component) # Component
        node.append("") # Category
        node.append("") # Type
        node.append("") # %d SAP Notes" % count) # Id
        node.append("") # Title
        node.append("") # Priority
        node.append("") # Lang
        #~ node.append("") # Version
        node.append("") # Release on
        return node


    def get_node_category(self, sapnote):
        compkey = escape(sapnote['componentkey'])
        catname = escape(sapnote['category'])
        catkey = compkey + '-' + catname
        icon = self.im.get_icon('category', 32, 32)

        if len(catname) == 0:
            #~ catname = "<span size='12000'><b>No category assigned</b></span>"
            catname = "No category assigned"
        else:
            #~ category = "<span size='15000'><b>%s</b></span>" % catname
            category = "%s" % catname

        node = []
        node.append('category@%s' % catname)
        node.append(0)
        node.append(icon)
        node.append(category) # Component
        node.append("") # Category
        node.append("") # Type
        node.append("") # Id
        node.append("") # Title
        node.append("") # Priority
        node.append("") # Lang
        #~ node.append("") # Version
        node.append("") # Release on
        return node


    def get_node_date_year(self, date, token_date):
        title = "%s" % token_date

        node = []
        node.append('date-year@%s' % token_date)
        node.append(0)
        node.append(None)
        node.append(title) # Component
        node.append("") # Category
        node.append("") # Type
        node.append("") # Id
        node.append("") # Title
        node.append("") # Priority
        node.append("") # Lang
        #~ node.append("") # Version
        node.append("") # Release on
        return node


    def get_node_date_month(self, date, token_date):
        title = "%s" % date.strftime("%B")

        node = []
        node.append('date-month@%s' % token_date)
        node.append(0)
        node.append(None)
        node.append(title) # Component
        node.append("") # Category
        node.append("") # Type
        node.append("") # Id
        node.append("") # Title
        node.append("") # Priority
        node.append("") # Lang
        #~ node.append("") # Version
        node.append("") # Release on
        return node


    def get_node_date_day(self, date, token_date):
        title = "%s" % date.strftime("%d - %A")

        node = []
        node.append('date-day@%s' % token_date)
        node.append(0)
        node.append(None)
        node.append(title) # Component
        node.append("") # Category
        node.append("") # Type
        node.append("") # Id
        node.append("") # Title
        node.append("") # Priority
        node.append("") # Lang
        #~ node.append("") # Version
        node.append("") # Release on
        return node


    def get_node_priority(self, priority):
        icon = None # self.im.get_icon('category', 32, 32)

        if len(priority) == 0:
            title = "No priority assigned"
        else:
            title = "%s" % priority

        node = []
        node.append('priority@%s' % priority)
        node.append(0)
        node.append(icon)
        node.append(title) # Component # Category title
        node.append("") # Category
        node.append("") # Type
        node.append("") # Id
        node.append("") # Title
        node.append("") # Priority
        node.append("") # Lang
        #~ node.append("") # Version
        node.append("") # Release on
        return node


    def get_node_type(self, sntype):
        icon = self.im.get_icon('type', 32, 32)

        if len(sntype) == 0:
            title = "SAP Note type not found"
        else:
            title = "%s" % sntype

        node = []
        node.append('type@%s' % sntype)
        node.append(0)
        node.append(icon)
        node.append(title) # Component # Category title
        node.append("") # Category
        node.append("") # Type
        node.append("") # Id
        node.append("") # Title
        node.append("") # Priority
        node.append("") # Lang
        #~ node.append("") # Version
        node.append("") # Release on
        return node


    def get_node_component_desc(self, compkey, comptxt):
        icon = self.im.get_icon('component', 32, 32)
        node = []
        count = len(self.db.get_notes_by_node("componentkey", compkey))
        if len(comptxt) == 0:
            component = "%s" % (compkey)
        else:
            component = "%s" % (comptxt)
        node.append('componentkey@%s' % compkey)
        node.append(0)
        node.append(icon)
        node.append(component) # Component
        node.append("") # Category
        node.append("") # Type
        node.append("") # %d SAP Notes" % count) # Id
        node.append("") # Title
        node.append("") # Priority
        node.append("") # Lang
        #~ node.append("") # Version
        node.append("") # Release on
        return node



    def get_node_component_desc_bis(self, comptxt):
        icon = self.im.get_icon('component', 32, 32)

        if len(comptxt) == 0:
            title = "No component assigned"
        else:
            title = "%s" % comptxt

        node = []
        node.append('componenttxt@%s' % comptxt)
        node.append(0)
        node.append(icon)
        node.append(title) # Component # Category title
        node.append("") # Category
        node.append("") # Type
        node.append("") # Id
        node.append("") # Title
        node.append("") # Priority
        node.append("") # Lang
        #~ node.append("") # Version
        node.append("") # Release on
        return node


    def get_node_category_view(self, category=''):
        icon = None # self.im.get_icon('category', 32, 32)

        if len(category) == 0:
            catname = "No category assigned"
        else:
            catname = "%s" % category

        node = []
        node.append('category@%s' % category)
        node.append(0)
        node.append(icon)
        node.append(catname) # Component # Category title
        node.append("") # Category
        node.append("") # Type
        node.append("") # Id
        node.append("") # Title
        node.append("") # Priority
        node.append("") # Lang
        #~ node.append("") # Version
        node.append("") # Release on
        return node


    def get_node_sapnote_bookmark(self, sapnote, sid):
        compkey = escape(sapnote['componentkey'])
        icon_note = self.im.get_pixbuf_icon('fingerprint')
        icon_fav = self.im.get_icon('bookmark')
        compkey = escape(sapnote['componentkey'])
        compkey = escape(sapnote['componentkey'])
        catname = escape(sapnote['category'])
        priority = "%s" % escape(sapnote['priority'])
        released = dateparser.parse(sapnote['releaseon'])
        released = released.strftime("%Y.%m.%d")
        sid = "0"*(10 - len(sid)) + sid
        title = "<big>%s</big>" % (escape(sapnote['title']))

        # Create row-node
        node = []
        node.append('sapnote@%s' % sid)     # 0. # RowType
        node.append(0)                      # 1. # CheckBox
        node.append(icon_fav)               # 2. # Icon
        node.append("%s" % compkey)         # 3. # Component
        node.append("%s" % catname)         # 4. # Category
        node.append(sapnote['type'])        # 5. # Type
        node.append("<b>%s</b>" %sid)       # 6. # Sap Note ID
        node.append(title)                  # 7. # Title
        node.append(priority)               # 8. # Priority
        node.append(sapnote['language'])    # 9. # Lang
        node.append(released)               # 10. # Release date

        return node


    def get_node_sapnote_task(self, sapnote, sid):
        compkey = escape(sapnote['componentkey'])
        compkey = escape(sapnote['componentkey'])
        catname = escape(sapnote['category'])
        priority = "%s" % escape(sapnote['priority'])
        released = dateparser.parse(sapnote['releaseon'])
        released = released.strftime("%Y.%m.%d")
        sid = "0"*(10 - len(sid)) + sid
        title = "%s" % (escape(sapnote['title']))
        bookmarked = sapnote['bookmark']

        if bookmarked:
            icon = self.im.get_icon('bookmark', 32, 32)
        else:
            icon = self.im.get_icon('fingerprint', 32, 32)

        # Create row-node
        node = []
        node.append('sapnote@%s' % sid)     # 0. # RowType
        node.append(0)                      # 1. # CheckBox
        node.append(icon)                   # 2. # Icon
        node.append(title)                  # 3. # Component
        node.append("")                     # 4. # Category
        node.append(sapnote['type'])        # 5. # Type
        node.append("%s" %sid)              # 6. # Sap Note ID
        node.append("")                     # 7. # Title
        node.append(priority)               # 8. # Priority
        node.append(sapnote['language'])    # 9. # Lang
        node.append(released)               # 10. # Release date

        return node


    def get_node_sapnote_type(self, sapnote, sid):
        #~ compkey = escape(sapnote['componentkey'])
        #~ compkey = escape(sapnote['componentkey'])
        #~ catname = escape(sapnote['category'])
        sntype = "%s" % escape(sapnote['type'])
        #~ released = dateparser.parse(sapnote['releaseon'])
        #~ released = released.strftime("%Y.%m.%d")
        sid = "0"*(10 - len(sid)) + sid
        title = "%s" % (escape(sapnote['title']))
        bookmarked = sapnote['bookmark']

        if bookmarked:
            icon = self.im.get_icon('bookmark', 32, 32)
        else:
            icon = self.im.get_icon('fingerprint', 32, 32)

        # Create row-node
        node = []
        node.append('sapnote@%s' % sid)     # 0. # RowType
        node.append(0)                      # 1. # CheckBox
        node.append(icon)                   # 2. # Icon
        node.append(title)                  # 3. # Component
        node.append("")                     # 4. # Category
        node.append(sapnote['type'])        # 5. # Type
        node.append("%s" %sid)       # 6. # Sap Note ID
        node.append("")                     # 7. # Title
        node.append("")                     # 8. # Priority
        node.append(sapnote['language'])    # 9. # Lang
        node.append("")                     # 10. # Release date

        return node


    def get_node_sapnote_component_desc(self, sapnote, sid):
        compkey = escape(sapnote['componentkey'])
        compkey = escape(sapnote['componentkey'])
        catname = escape(sapnote['category'])
        priority = "%s" % escape(sapnote['priority'])
        released = dateparser.parse(sapnote['releaseon'])
        released = released.strftime("%Y.%m.%d")
        sid = "0"*(10 - len(sid)) + sid
        title = "%s" % (escape(sapnote['title']))
        bookmarked = sapnote['bookmark']

        if bookmarked:
            icon = self.im.get_icon('bookmark', 32, 32)
        else:
            icon = self.im.get_icon('fingerprint', 32, 32)

        # Create row-node
        node = []
        node.append('sapnote@%s' % sid)     # 0. # RowType
        node.append(0)                      # 1. # CheckBox
        node.append(icon)                   # 2. # Icon
        node.append(title)                  # 3. # Component
        node.append("")                     # 4. # Category
        node.append(sapnote['type'])        # 5. # Type
        node.append("%s" %sid)       # 6. # Sap Note ID
        node.append("")                     # 7. # Title
        node.append(priority)               # 8. # Priority
        node.append(sapnote['language'])    # 9. # Lang
        node.append(released)               # 10. # Release date

        return node


    def get_node_sapnote_category(self, sapnote, sid):
        compkey = escape(sapnote['componentkey'])
        compkey = escape(sapnote['componentkey'])
        catname = escape(sapnote['category'])
        priority = "%s" % escape(sapnote['priority'])
        released = dateparser.parse(sapnote['releaseon'])
        released = released.strftime("%Y.%m.%d")
        sid = "0"*(10 - len(sid)) + sid
        title = "<big>%s</big>" % (escape(sapnote['title']))
        bookmarked = sapnote['bookmark']

        if bookmarked:
            icon = self.im.get_icon('bookmark', 32, 32)
        else:
            icon = self.im.get_icon('fingerprint', 32, 32)

        # Create row-node
        node = []
        node.append('sapnote@%s' % sid)     # 0. # RowType
        node.append(0)                      # 1. # CheckBox
        node.append(icon)                   # 2. # Icon
        #~ node.append(title)                  # 3. # Component
        node.append(compkey)                  # 3. # Component
        self.log.debug("CompKey: %s" % compkey)
        node.append("")                     # 4. # Category
        node.append(sapnote['type'])        # 5. # Type
        node.append("<b>%s</b>" %sid)       # 6. # Sap Note ID
        node.append("")                     # 7. # Title
        node.append(priority)               # 8. # Priority
        node.append(sapnote['language'])    # 9. # Lang
        node.append(released)               # 10. # Release date

        return node


    def get_node_sapnote_chronologic(self, sapnote, sid):
        compkey = escape(sapnote['componentkey'])
        compkey = escape(sapnote['componentkey'])
        catname = escape(sapnote['category'])
        priority = "%s" % escape(sapnote['priority'])
        released = dateparser.parse(sapnote['releaseon'])
        released = released.strftime("%Y.%m.%d")
        sid = "0"*(10 - len(sid)) + sid
        title = "<big>%s</big>" % (escape(sapnote['title']))
        bookmarked = sapnote['bookmark']

        if bookmarked:
            icon = self.im.get_icon('bookmark', 32, 32)
        else:
            icon = self.im.get_icon('fingerprint', 32, 32)

        # Create row-node
        node = []
        node.append('sapnote@%s' % sid)     # 0. # RowType
        node.append(0)                      # 1. # CheckBox
        node.append(icon)                   # 2. # Icon
        node.append(title)                  # 3. # Component
        node.append("")                     # 4. # Category
        node.append(sapnote['type'])        # 5. # Type
        node.append("<b>%s</b>" %sid)       # 6. # Sap Note ID
        node.append("")                     # 7. # Title
        node.append(priority)               # 8. # Priority
        node.append(sapnote['language'])    # 9. # Lang
        node.append(released)               # 10. # Release date

        return node



    def get_node_sapnote(self, sapnote, sid):
        compkey = escape(sapnote['componentkey'])
        icon_note = self.im.get_icon('fingerprint', 32, 32)
        icon_fav = self.im.get_icon('bookmark', 32, 32)
        compkey = escape(sapnote['componentkey'])
        compkey = escape(sapnote['componentkey'])
        catname = escape(sapnote['category'])
        sid = "0"*(10 - len(sid)) + sid

        # Get bookmark
        try:
            bookmark = sapnote['bookmark']
        except Exception as error:
            bookmark = False

        # Get correct title
        title = "%s" % (escape(sapnote['title']))

        # Create row-node
        node = []
        node.append('sapnote@%s' % sid) # 0. # RowType
        node.append(0) # 1. # CheckBox

        # 2. # Icon
        if bookmark:
            node.append(icon_fav)
        else:
            node.append(icon_note)

        # 3. # Component
        if self.view == 'components':
            node.append(title)
            # 4. # Category
            node.append("")
        elif self.view == 'bookmarks':
            node.append("%s" % compkey)
            # 4. # Category
            node.append("%s" % catname)
            #~ node.append(catname)
        elif self.view == 'tasks':
            node.append("%s" % compkey)
            # 4. # Category
            node.append("%s" % catname)
            #~ node.append(catname)
        elif self.view == 'chronologic':
            node.append("")
            # 4. # Category
            node.append("")
            #~ node.append(catname)
        elif self.view == 'category':
            node.append("")
            # 4. # Category
            node.append("")
            #~ node.append(catname)
        elif self.view == 'description':
            node.append("")
            # 4. # Category
            node.append("")
            #~ node.append(catname)
        else:
            node.append(title)
            # 4. # Category
            node.append("")

        node.append(sapnote['type'])  # 5. # Type
        node.append("<b>%s</b>" %sid) # 6. # Sap Note ID

        # 7. # Title
        if self.view == 'bookmarks':
            node.append(title)
        elif self.view == 'tasks':
            node.append(title)
        elif self.view == 'chronologic':
            node.append(title)
        elif self.view == 'category':
            node.append(title)
        elif self.view == 'description':
            node.append(title)
        else:
            node.append("")

        # 8. # Priority
        priority = "%s" % escape(sapnote['priority'])
        node.append(priority)

        # 9. # Lang
        node.append(sapnote['language'])

        # 10. # Release date
        released = dateparser.parse(sapnote['releaseon'])
        node.append(released.strftime("%Y.%m.%d"))

        return node


    def populate_by_components(self, sapnotes, only_bookmarks=False):
        self.log.debug('Populating by components')
        self.model.clear()
        self.treepids = {}
        self.column_component.set_title('Components')
        self.column_icon.set_visible(False)
        self.column_component.set_visible(True)
        self.column_component.set_expand(True)
        self.column_title.set_visible(False)
        self.column_sid.set_visible(False)
        self.set_headers_visible(False)

        if len(sapnotes) == 0:
            return

        scomp = set()
        dcomp = {}

        for sid in sapnotes:
            compkey = escape(sapnotes[sid]['componentkey'])
            comptxt = escape(sapnotes[sid]['componenttxt'])
            scomp.add(compkey)
            dcomp[compkey] = comptxt
        lcomp = list(scomp)
        lcomp.sort()

        for compkey in lcomp:
            subkeys = compkey.split('-')
            ppid = None
            for i in range(1, len(subkeys)+1):
                key = ('-').join(subkeys[0:i])
                try:
                    ppid = self.treepids[key]
                except:
                    if i == len(subkeys):
                        title = dcomp[compkey]
                    else:
                        title = ""
                    node = self.get_node_component(key, title)
                    ppid = self.model.append(ppid, node)
                    self.treepids[key] = ppid


        for sid in sapnotes:
            #~ Gety component
            compkey = escape(sapnotes[sid]['componentkey'])
            pid = self.treepids[compkey]


    def populate_by_component_descriptions(self, sapnotes, only_bookmarks=False):
        self.log.debug('Populating by components')
        self.model.clear()
        self.treepids = {}
        self.column_component.set_title('Components')
        self.column_icon.set_visible(False)
        self.column_component.set_visible(True)
        self.column_component.set_expand(True)
        self.column_title.set_visible(False)
        self.column_sid.set_visible(False)
        self.set_headers_visible(False)

        if len(sapnotes) == 0:
            return

        scomp = set()
        dcomp = {}

        for sid in sapnotes:
            compkey = escape(sapnotes[sid]['componentkey'])
            comptxt = escape(sapnotes[sid]['componenttxt'])
            scomp.add(compkey)
            dcomp[compkey] = comptxt
        lcomp = list(scomp)
        lcomp.sort()

        for compkey in lcomp:
            subkeys = compkey.split('-')
            ppid = None
            for i in range(1, len(subkeys)+1):
                key = ('-').join(subkeys[0:i])
                try:
                    ppid = self.treepids[key]
                except:
                    if i == len(subkeys):
                        title = dcomp[compkey]
                    else:
                        title = ""
                    node = self.get_node_component_desc(key, title)
                    ppid = self.model.append(ppid, node)
                    self.treepids[key] = ppid


    def populate_by_component_descriptions_bis(self, sapnotes):
        self.model.clear()
        treepids = {}

        self.set_headers_visible(False)
        self.column_sid.set_visible(True)
        self.column_icon.set_visible(False)
        self.column_component.set_visible(True)
        self.column_component.set_expand(True)
        self.column_title.set_visible(False)
        self.column_title.set_expand(True)
        self.column_priority.set_expand(False)
        self.column_cat.set_visible(False)
        self.column_sid.set_expand(False)

        if len(sapnotes) == 0:
            return

        scomp = set()
        dcomp = {}
        pset = set()

        for sid in sapnotes:
            try:
                comptxt = escape(sapnotes[sid]['componenttxt'])
                pset.add(comptxt)
            except: pass

        plist = []
        plist.extend(pset)
        plist.sort()

        for comptxt in plist:
            node = self.get_node_component_desc(comptxt)
            pid = self.model.append(None, node)
            treepids[comptxt] = pid

        #~ for sid in sapnotes:
            #~ comptxt = escape(sapnotes[sid]['componenttxt'])
            #~ pid = treepids[comptxt]
            #~ sapnote = self.get_node_sapnote_component_desc(sapnotes[sid], sid)
            #~ self.model.append(pid, sapnote)


    def changed(self, *args):
        try:
            model, treeiters = self.selection.get_selected_rows()
            selected = set()
            if len(treeiters) > 0:
                for treeiter in treeiters:
                    if treeiter != None:
                        selected.add(model[treeiter][0])
        except Exception as error:
            self.log.error (self.get_traceback())


    def on_cell_toggled(self, widget, path):
        model = self.get_model()
        row = model[path][0]
        compkey = model[path][4]
        row_type, key = row.split('@')
        #~ rtype = model[path][0]
        self.log.debug("SAPNoteView Node type: %s" % row_type)
        model[path][1] = not model[path][1]
        toggled = model[path][1]

        matches = self.db.get_notes_by_node(row_type, key)
        self.select_sapnotes(toggled, matches)
        ### THIS CODE IS VALID
        ### It allows to hide/show checkboxes depending of SAPNoteView
        ### Node
        #~ if rtype != 'component' and rtype != 'category':
            #~ model[path][1] = not model[path][1]
            #~ self.count = 0
            #~ self.check_states()

        self.check_states()
        self.cb.check_task_link_button_status()
        #FIXME:
        #(basico:10248): Gtk-WARNING **: Failed to set text from markup
        # due to error parsing markup: Error en la lÝnea 1: La entidad
        # no termina con un punto y coma; probablemente utiliz¾ el
        # carßcter "&" sin la intenci¾n de indicar una entidad, escape
        #el signo "&" como &amp;


    def select_sapnotes(self, toggled, sapnotes):
        model = self.get_model()

        def traverse_treeview(model, path, iter, user_data=None):
            row = model.get_value(iter, 0)
            row_type, sid = row.split('@')
            if row_type == 'sapnote':
                if sid in sapnotes:
                    model.set_value(iter, 1, toggled)

            return False

        model.foreach(traverse_treeview, toggled)
        self.check_states()



    def check_states(self):
        lblSelectedNotes = self.gui.get_widget('lblSelectedNotes')
        self.selected = set()

        def traverse_treeview(model, path, iter, user_data=None):
            row = model.get_value(iter, 0)
            row_type, key = row.split('@')

            if row_type == 'component':
                compkey = key
            elif row_type == 'category':
                catkey = key

            toggled = model.get_value(iter, 1)
            if toggled:
                self.count += 1
                if row_type == 'sapnote':
                    self.selected.add(key)
            return False

        model = self.get_model()
        model.foreach(traverse_treeview)
        lblSelectedNotes.set_markup('<small><b>%d of %d</b></small>' % (len(self.selected), self.db.get_total()))
        actions = self.gui.get_widget('mnuBtnActions')
        if (len(self.selected)) > 0:
            actions.set_sensitive(True)
            self.cb.setup_menu_actions()
        else:
            actions.set_sensitive(False)

        self.cb.setup_menu_import()


    def set_select_notes(self, sapnotes):
        self.selected = set()
        for sapnote in sapnotes:
            self.selected.add(sapnote)
            self.log.debug("sapnote: %s" % sapnote)


    def get_selected_notes(self):
        bag = list(self.selected)
        bag.sort()
        self.log.debug("Selected SAP Notes: %d" % len(bag))
        n = 1
        for sid in bag:
            self.log.debug("\t%4d - SAP Note %10d" % (n, int(sid)))
            n += 1
        return bag


    def get_view(self):
        if self.view is None:
            return 'components'

        return self.view


    def set_view(self, view):
        settings = self.app.get_service('Settings')

        if view is None:
            view = settings.get_config_value('View')

        # Save view to settings
        if view not in ['settings', 'download']:
            self.view = view
            settings = self.app.get_service('Settings')
            settings.set_config_value('View', view)

        # Change icon
        iconview = self.gui.get_widget('imgViewCurrent')
        if view == 'settings':
            iconname = 'gtk-preferences'
        elif view == 'download':
            iconname = 'download'
        else:
            iconname = view

        icon = self.im.get_pixbuf_icon(iconname, 24, 24)
        iconview.set_from_pixbuf(icon)

        # Change label
        viewlabel = self.gui.get_widget('lblViewCurrent')
        name = "<b>%-s</b>" % view.capitalize()
        viewlabel.set_markup(name)


    def expand(self):
        switch = self.gui.get_widget('schExpandCollapse')
        switch.set_active(True)


    def collapse(self):
        switch = self.gui.get_widget('schExpandCollapse')
        switch.set_active(False)


    def expand_collapse(self, switch, active):
        if active:
            self.expand_all()
        else:
            self.collapse_all()
    #~ def expand_collapse(self, checkbutton):
        #~ active = checkbutton.get_active()
        #~ if active:
            #~ self.expand_all()
        #~ else:
            #~ self.collapse_all()


    def select_all_none(self, switch, active):
        model = self.get_model()

        def traverse_treeview(model, path, iter, user_data=None):
            row = model.get_value(iter, 0)
            row_type, sid = row.split('@')
            if row_type == 'sapnote':
                model.set_value(iter, 1, active)

            return False

        model.foreach(traverse_treeview, active)
        self.check_states()
    #~ def select_all_none(self, checkbutton):
        #~ active = checkbutton.get_active()

        #~ model = self.get_model()

        #~ def traverse_treeview(model, path, iter, user_data=None):
            #~ row = model.get_value(iter, 0)
            #~ row_type, sid = row.split('@')
            #~ if row_type == 'sapnote':
                #~ model.set_value(iter, 1, active)

            #~ return False

        #~ model.foreach(traverse_treeview, active)
        #~ self.check_states()


    def select_by_task(self, task_target, active):
        model = self.get_model()

        def traverse_treeview(model, path, iter, user_data=None):
            row = model.get_value(iter, 0)
            row_type, sid = row.split('@')
            if row_type == 'sapnote':
                sid = model.get_value(iter, 6)
                sapnote = self.db.get_sapnote_metadata(sid)
                tasks_source = sapnote['tasks']

                if task_target in tasks_source:
                    model.set_value(iter, 1, active)
                else:
                    if len(task_target) == 0 and len(tasks_source) == 0:
                        model.set_value(iter, 1, active)

            return False

        model.foreach(traverse_treeview, True)
        self.check_states()


class SAPNoteViewMenu(SAPNoteView):
    def prepare(self):
        self.column_rowtype.set_visible(False)
        self.column_checkbox.set_visible(False)
        self.column_icon.set_visible(False)
        self.column_component.set_visible(True)
        self.column_cat.set_visible(False)
        self.column_sntype.set_visible(False)
        self.column_sid.set_visible(False)
        self.column_priority.set_visible(True)
        self.column_lang.set_visible(False)
        self.column_rel.set_visible(False)

        # TreeView common
        self.set_can_focus(True)
        self.set_headers_visible(False)
        self.set_enable_search(True)
        self.set_hover_selection(False)
        self.set_grid_lines(Gtk.TreeViewGridLines.NONE)
        self.set_level_indentation(0)
        #~ self.set_search_entry(self.gui.get_widget('stySearchInfo'))
        #~ self.set_search_column(3)


class SAPNoteViewVisorBis(SAPNoteView):
    def prepare(self):
        self.renderer_sid.set_property('background', 'Light Steel Blue')
        self.renderer_title.set_property('background', 'Gold')
        self.set_headers_visible(False)
        self.column_checkbox.set_visible(True)
        self.column_sid.set_visible(True)
        self.column_icon.set_visible(False)
        self.column_component.set_visible(False)
        self.column_component.set_expand(False)
        self.column_title.set_visible(True)
        self.column_title.set_expand(True)
        self.column_priority.set_expand(False)
        self.column_cat.set_visible(False)
        self.column_priority.set_visible(False)
        self.column_sid.set_expand(False)
        self.column_component.set_title('Component')


    def get_services(self):
        self.gui = self.app.get_service("GUI")
        self.cb = self.app.get_service('Callbacks')
        self.menu = self.app.get_service("Menus")
        self.sap = self.app.get_service('SAP')
        self.im = self.app.get_service('IM')
        self.settings = self.app.get_service('Settings')
        self.plugins = self.app.get_service('Plugins')
        self.db = self.app.get_service('DB')


    def check_states(self):
        lblSelectedNotes = self.gui.get_widget('lblSelectedNotes')
        self.selected = set()

        def traverse_treeview(model, path, iter, user_data=None):
            row = model.get_value(iter, 0)
            row_type, key = row.split('@')
            if row_type == 'component':
                compkey = key #component[8:ce]
            elif row_type == 'category':
                catkey = key # category[9:ce]

            toggled = model.get_value(iter, 1)
            if toggled:
                self.count += 1
                if row_type == 'sapnote':
                    self.selected.add(key)
            return False

        model = self.get_model()
        model.foreach(traverse_treeview)
        lblSelectedNotes.set_markup('<small><b>%d of %d</b></small>' % (len(self.selected), self.db.get_total()))
        actions = self.gui.get_widget('mnuBtnActions')
        if (len(self.selected)) > 0:
            actions.set_sensitive(True)
            self.cb.setup_menu_actions()
        else:
            actions.set_sensitive(False)

        self.cb.setup_menu_import()


    def select_sapnotes(self, toggled, sapnotes):
        model = self.get_model()

        def traverse_treeview(model, path, iter, user_data=None):
            row = model.get_value(iter, 0)
            row_type, sid = row.split('@')
            if row_type == 'sapnote':
                if sid in sapnotes:
                    model.set_value(iter, 1, toggled)

            return False

        model.foreach(traverse_treeview, toggled)
        self.check_states()


    def select_function(self, selection, store, path, current):
        return True


    def row_changed(self, treeview):
        return

        selected = set()
        selection = treeview.get_selection()
        model, treeiters = selection.get_selected_rows()
        try:
            row = model[treeiters[0]][0]
            row_type, sid = row.split('@')
            self.log.debug("Row Type: %s - Id: %s" % (row_type, sid))
            matches = self.db.get_notes_by_node(row_type, sid)
        except IndexError as error:
            self.log.error(error)
            row_type = ""
            raise
        except Exception as error:
            self.log.error(error)
            self.log.error(tb.format_exc())
            row_type = ""

        return


    def double_click(self, treeview, row, col):
        model = treeview.get_model()
        row = model[row][0]
        row_type, sid = row.split('@')
        self.set_select_notes([sid])
        self.cb.actions_browse()


    def cell_data_func(self, column, cell, store, iter, data=None):
        """Do not show checkbox when row type is distinct of sapnote"""

        #### This is code is valid:
        #### It allows to hide checkboxes other than sapnote
        #~ row = store[iter][0]
        #~ row_type, key = row.split('@')

        #~ if row_type != 'sapnote':
            #~ cell.set_visible(False)
        #~ else:
            #~ cell.set_visible(True)

        cell.set_visible(True)


    def on_cell_toggled(self, widget, path):
        model = self.get_model()
        row = model[path][0]
        compkey = model[path][4]
        row_type, key = row.split('@')
        #~ rtype = model[path][0]
        self.log.debug("SAPNoteView Node type: %s" % row_type)
        model[path][1] = not model[path][1]
        toggled = model[path][1]

        matches = self.db.get_notes_by_node(row_type, key)
        self.select_sapnotes(toggled, matches)
        ### THIS CODE IS VALID
        ### It allows to hide/show checkboxes depending of SAPNoteView
        ### Node
        #~ if rtype != 'component' and rtype != 'category':
            #~ model[path][1] = not model[path][1]
            #~ self.count = 0
            #~ self.check_states()

        self.check_states()
        #~ self.cb.check_task_link_button_status()
        #FIXME:
        #(basico:10248): Gtk-WARNING **: Failed to set text from markup
        # due to error parsing markup: Error en la lÝnea 1: La entidad
        # no termina con un punto y coma; probablemente utiliz¾ el
        # carßcter "&" sin la intenci¾n de indicar una entidad, escape
        #el signo "&" como &amp;


    def cb_button_press(self, treeview, event, data=None):
        if event.button == 3:
            x = int(event.x)
            y = int(event.y)
            time = event.time
            pthinfo = self.get_path_at_pos(x,y)
            if pthinfo is not None:
                path,col,cellx,celly = pthinfo
                self.grab_focus()
                self.set_cursor(path,col,0)

                model = treeview.get_model()
                treeiter = model.get_iter(path)
                for n in range(9):
                    self.log.debug("%d -> %s" % (n, model.get_value(treeiter, n)))
                rowtype, value = model.get_value(treeiter, 0).split('@')
                self.log.debug("Row type: %s ->   %s" % (rowtype, value))
                if rowtype == 'task':
                    task = model.get_value(treeiter, 6)
                    self.popup_menu = self.menu.create_popup_menu_by_task(task)
                elif rowtype == 'componentkey':
                    comptitle = model.get_value(treeiter, 3)
                    self.log.debug(comptitle)
                    token = "</b></big>"
                    pos = comptitle.find(token)
                    component = comptitle[8:pos]
                    self.popup_menu = self.menu.create_popup_menu_by_component(component)
                elif rowtype == 'sapnote':
                    sid = model.get_value(treeiter, 6)
                    self.popup_menu = self.menu.create_popup_menu_by_sapnote(sid)
                else:
                    return False

                if self.popup_menu is not None:
                    self.popup_menu.show_all()
                    self.popup_menu.popup(None, None, None, None, event.button, event.time)
                    return True # event has been handled
                else:
                    return False


    def get_node_sapnote(self, sapnote, sid):
        compkey = escape(sapnote['componentkey'])
        # FIXME: im.get_pixbuf take a long time loading icons
        icon_note = None #self.im.get_pixbuf_icon('fingerprint')
        icon_fav = None #self.im.get_icon('bookmark')
        compkey = escape(sapnote['componentkey'])
        compkey = escape(sapnote['componentkey'])
        catname = escape(sapnote['category'])
        priority = "%s" % escape(sapnote['priority'])
        released = dateparser.parse(sapnote['releaseon'])
        released = released.strftime("%Y.%m.%d")
        snsid = "0"*(10 - len(str(int(sid)))) + '<b>%s</b>' % str(int(sid))
        title = "%s" % (escape(sapnote['title']))

        # Create row-node
        node = []
        node.append('sapnote@%s' % sid)     # 0. # RowType
        node.append(0)                      # 1. # CheckBox
        node.append(icon_fav)               # 2. # Icon
        node.append("%s" % compkey)         # 3. # Component
        node.append("%s" % catname)         # 4. # Category
        node.append(sapnote['type'])        # 5. # Type
        node.append("%s" % snsid)       # 6. # Sap Note ID
        node.append(title)                  # 7. # Title
        node.append(priority)               # 8. # Priority
        node.append(sapnote['language'])    # 9. # Lang
        node.append(released)               # 10. # Release date

        return node


    def populate(self, bag):
        self.model.clear()
        self.hide()
        icon_bookmark = self.im.get_icon('bookmark')
        flowbox = Gtk.FlowBox()
        flowbox.set_valign(Gtk.Align.START)
        flowbox.set_max_children_per_line(30)
        flowbox.set_selection_mode(Gtk.SelectionMode.SINGLE)
        sapnotes = self.db.get_notes()
        for sid in bag:
            sapnote = self.get_node_sapnote(sapnotes[sid], sid)
            label = Gtk.Label("%s" % sid)
            flowbox.add(label)
            #~ self.model.append(None, sapnote)
        lblcount = self.gui.get_widget("lblSAPNoteViewCount")
        lblcount.set_markup("<span size=\"30000\">%d</span>" % len(bag))

        self.show_all()


    def select_by_component(self, component_target, active):
        model = self.get_model()
        self.log.debug("Component target: %s" % component_target)

        def traverse_treeview(model, path, iter, user_data=None):
            row = model.get_value(iter, 0)
            row_type, sid = row.split('@')
            if row_type == 'sapnote':
                sapnote = self.db.get_sapnote_metadata(sid)
                component_source = sapnote['componentkey']
                self.log.debug("Component source: %s - Component target: %s" % (component_source, component_target))
                if component_target in component_source:
                    model.set_value(iter, 1, active)

            return False

        model.foreach(traverse_treeview, True)
        self.check_states()


class SAPNoteViewVisor(Gtk.Box):
    def __init__(self, app):
        self.app = app
        LOG_FILE = self.app.get_file('LOG')
        LOG_NAME = self.__class__.__name__
        self.log = get_logger(LOG_NAME, LOG_FILE)
        self.get_services()
        self.toggled = 0
        self.selected = set()
        self.count = 0
        Gtk.Box.__init__(self)


    def get_services(self):
        self.gui = self.app.get_service("GUI")
        self.cb = self.app.get_service('Callbacks')
        self.menu = self.app.get_service("Menus")
        self.sap = self.app.get_service('SAP')
        self.im = self.app.get_service('IM')
        self.settings = self.app.get_service('Settings')
        self.plugins = self.app.get_service('Plugins')
        self.db = self.app.get_service('DB')
        self.uif = self.app.get_service("UIF")



    def prepare(self):
        self.set_hexpand(True)
        self.set_property('fill', True)


    def get_node_sapnote(self, sid):
        sapnotes = self.db.get_notes()
        sapnote = sapnotes[sid]
        compkey = escape(sapnote['componentkey'])
        icon_note = self.im.get_icon('fingerprint', 32, 32)
        icon_fav = self.im.get_icon('bookmark', 32, 32)
        compkey = escape(sapnote['componentkey'])
        compkey = escape(sapnote['componentkey'])
        catname = escape(sapnote['category'])
        sid = "0"*(10 - len(sid)) + sid

        return sapnote


    def show_infobox(self, button, revealer):
        revealed = revealer.get_reveal_child()
        revealer.set_reveal_child(not revealed)


    def get_row(self, sid):
        sapnote = self.get_node_sapnote(sid)

        # Container
        container = Gtk.VBox()

        # SAP Note Infobox
        infobox = Gtk.Revealer()
        infobox.set_reveal_child(False)
        label = Gtk.Label(sid)
        label.set_selectable(True)
        infobox.add(label)

        # row widget
        row = Gtk.HBox()
        #~ row.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse("Gold"))
        row.set_hexpand(True)

        # row items widgets
        checkbox = Gtk.CheckButton()
        row.pack_start(checkbox, False, False, 6)

        label = Gtk.Label()
        label.set_markup('<big>❤</big>')
        bookmark = Gtk.ToggleButton()
        bookmark.set_relief(Gtk.ReliefStyle.NONE)
        bookmark.get_style_context().add_class(Gtk.STYLE_CLASS_DESTRUCTIVE_ACTION)
        bookmark.add(label)
        row.pack_start(bookmark, False, False, 6)

        lblsid = Gtk.Label()
        title = escape("%s - %s" % (sid, sapnote['title']))
        lblsid.set_markup("%s" % title)
        lblsid.set_justify(Gtk.Justification.LEFT)
        lblsid.set_xalign(0.0)
        lblsid.set_selectable(True)
        lblsid.set_hexpand(False)
        lblsid.set_property('ellipsize', Pango.EllipsizeMode.MIDDLE)
        lblsid.modify_font(Pango.FontDescription('Monospace 10'))
        row.pack_start(lblsid, True, True, 6)

        button = Gtk.Button()
        button.set_relief(Gtk.ReliefStyle.NONE)
        button.set_alignment (0.0, 0.0)
        button.set_hexpand(False)
        button.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION)
        label = Gtk.Label()
        label.set_markup('<big>❓</big>')
        button.add(label)
        button.connect('clicked', self.show_infobox, infobox)
        row.pack_start(button, False, False, 6)

        button = Gtk.Button()
        button.set_relief(Gtk.ReliefStyle.NONE)
        button.set_alignment (0.0, 0.0)
        button.set_hexpand(False)
        button.get_style_context().add_class(Gtk.STYLE_CLASS_DESTRUCTIVE_ACTION)
        label = Gtk.Label()
        label.set_markup('<big>🔗</big>')
        button.add(label)
        button.connect('clicked', self.show_infobox, infobox)
        row.pack_start(button, False, False, 6)

        button = Gtk.Button()
        button.set_relief(Gtk.ReliefStyle.NONE)
        button.set_alignment (0.0, 0.0)
        button.set_hexpand(False)
        label = Gtk.Label()
        label.set_markup('<big>✍</big>')
        button.add(label)
        button.connect('clicked', self.show_infobox, infobox)
        row.pack_start(button, False, False, 6)

        app = self.gui.get_app()
        actions_menu = Gio.Menu()
        actions_menu.append_item(self.uif.create_item('Import Basico Package', 'app.actions-import-basico', 'document-open'))
        app.add_action(self.uif.create_action("actions-import-basico"))
        actions_menu.append_item(self.uif.create_item('Import JSON file', 'app.actions-import-json', 'document-open'))
        app.add_action(self.uif.create_action("actions-import-json"))
        actions_menu.append_item(self.uif.create_item('Import SAP Notes from SAP Launchpad', 'app.actions-import-launchpad', 'download'))
        app.add_action(self.uif.create_action("actions-import-launchpad"))
        btnactions = Gtk.MenuButton()
        btnactions.set_always_show_image(True)
        btnactions.set_property("use-popover", True)
        btnactions.set_menu_model(actions_menu)
        row.pack_start(btnactions, False, False, 6)

        container.pack_start(row, False, False, 0)
        container.pack_start(infobox, False, False, 0)
        container.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse("White"))

        return container


    def populate(self, bag):
        flowbox = Gtk.FlowBox()
        flowbox.set_valign(Gtk.Align.START)
        flowbox.set_max_children_per_line(1)
        flowbox.set_selection_mode(Gtk.SelectionMode.NONE)

        self.hide()
        icon_bookmark = self.im.get_icon('bookmark')

        sapnotes = self.db.get_notes()

        lbag = []
        for sid in bag:
            lbag.append(sid)
        lbag.sort()

        for sid in lbag:
            row = SAPNoteViewRow(self.app, sid)
            flowbox.add(row)

        lblcount = self.gui.get_widget("lblSAPNoteViewCount")
        lblcount.set_markup("<span size=\"30000\">%d</span>" % len(bag))
        self.gui.swap_widget(self, flowbox)
        self.show_all()


class SAPNoteViewRow(Gtk.HBox):
    def __init__(self, app, sid):
        Gtk.HBox.__init__(self)
        self.app = app
        LOG_FILE = self.app.get_file('LOG')
        LOG_NAME = self.__class__.__name__
        self.log = get_logger(LOG_NAME, LOG_FILE)
        self.get_services()
        self.setup_row(sid)


    def get_services(self):
        self.gui = self.app.get_service("GUI")
        self.cb = self.app.get_service('Callbacks')
        self.menu = self.app.get_service("Menus")
        self.sap = self.app.get_service('SAP')
        self.im = self.app.get_service('IM')
        self.settings = self.app.get_service('Settings')
        self.plugins = self.app.get_service('Plugins')
        self.db = self.app.get_service('DB')
        self.uif = self.app.get_service("UIF")


    def setup_row(self, sid):
        sapnote = self.get_node_sapnote(sid)

        self.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse("White"))
        self.set_hexpand(True)

        # row items widgets
        checkbox = Gtk.CheckButton()
        self.pack_start(checkbox, False, False, 6)

        label = Gtk.Label()
        label.set_markup('<big>❤</big>')
        self.bookmark = Gtk.ToggleButton()
        self.bookmark.set_relief(Gtk.ReliefStyle.NONE)
        self.bookmark.get_style_context().add_class(Gtk.STYLE_CLASS_DESTRUCTIVE_ACTION)
        if sapnote['bookmark']:
            self.bookmark.set_active(True)
            #~ self.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse("Light Coral"))
        self.bookmark.connect('toggled', self.cb.toggle_bookmark, [sid])
        self.bookmark.add(label)
        self.pack_start(self.bookmark, False, False, 6)

        lblsid = Gtk.Label()
        title = escape("%s - %s" % (sid, sapnote['title']))
        lblsid.set_markup("%s" % title)
        lblsid.set_justify(Gtk.Justification.LEFT)
        lblsid.set_xalign(0.0)
        lblsid.set_selectable(True)
        lblsid.set_hexpand(False)
        lblsid.set_property('ellipsize', Pango.EllipsizeMode.MIDDLE)
        lblsid.modify_font(Pango.FontDescription('Monospace 10'))
        self.pack_start(lblsid, True, True, 6)

        button = Gtk.Button()
        button.set_relief(Gtk.ReliefStyle.NONE)
        button.set_alignment (0.0, 0.0)
        button.set_hexpand(False)
        button.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION)
        label = Gtk.Label()
        label.set_markup('<big>❓</big>')
        button.add(label)
        self.pack_start(button, False, False, 6)

        button = Gtk.Button()
        button.set_relief(Gtk.ReliefStyle.NONE)
        button.set_alignment (0.0, 0.0)
        button.set_hexpand(False)
        button.get_style_context().add_class(Gtk.STYLE_CLASS_DESTRUCTIVE_ACTION)
        label = Gtk.Label()
        label.set_markup('<big>🔗</big>')
        button.add(label)
        self.pack_start(button, False, False, 6)

        button = Gtk.Button()
        button.set_relief(Gtk.ReliefStyle.NONE)
        button.set_alignment (0.0, 0.0)
        button.set_hexpand(False)
        label = Gtk.Label()
        label.set_markup('<big>✍</big>')
        button.add(label)
        self.pack_start(button, False, False, 6)

        app = self.gui.get_app()
        actions_menu = Gio.Menu()
        actions_menu.append_item(self.uif.create_item('Import Basico Package', 'app.actions-import-basico', 'document-open'))
        app.add_action(self.uif.create_action("actions-import-basico"))
        actions_menu.append_item(self.uif.create_item('Import JSON file', 'app.actions-import-json', 'document-open'))
        app.add_action(self.uif.create_action("actions-import-json"))
        actions_menu.append_item(self.uif.create_item('Import SAP Notes from SAP Launchpad', 'app.actions-import-launchpad', 'download'))
        app.add_action(self.uif.create_action("actions-import-launchpad"))
        btnactions = Gtk.MenuButton()
        btnactions.set_always_show_image(True)
        btnactions.set_property("use-popover", True)
        btnactions.set_menu_model(actions_menu)
        self.pack_start(btnactions, False, False, 6)

        self.show_all()


    def get_node_sapnote(self, sid):
        sapnotes = self.db.get_notes()
        return sapnotes[sid]