Subversion Repositories basico

Rev

Rev 230 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

#!/usr/bin/python
# -*- coding: utf-8 -*-
# File: sapnoteviewvisor.py
# Author: Tomás Vírseda
# License: GPL v3
# Description: SAPNoteViewVisor widget

import os
from os.path import sep as SEP
from cgi import escape
import glob
import json

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.GdkPixbuf import Pixbuf
from gi.repository import Pango

from .env import LPATH
from .service import Service
from .widgets.collections import CollectionsMgtView
from .widgets.annotation import AnnotationWidget
from .widgets.importwdg import ImportWidget

class SAPNoteViewVisorToolbar(Gtk.HBox, Service):
    def __init__(self, app):
        Gtk.Box.__init__(self, app)
        self.app = app
        self.get_services()
        self.set_homogeneous(False)
        self.tool_bar = Gtk.Toolbar()
        self.pack_start(self.tool_bar, False, True, 0)
        self.tool_bar.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR)
        self.tool_bar.set_property('margin-bottom', 0)

        # Toggle Views button
        tool = self.gui.add_widget('gtk_toogletoolbutton_menu_view', Gtk.ToggleToolButton.new())
        tool.set_icon_name('gtk-select-color')
        tool.connect('toggled', self.cb.gui_toggle_menu_view)
        tool.set_active(False)
        tool.set_hexpand(False)
        self.tool_bar.insert(tool, -1)

        # View combobox button/popover
        tool = Gtk.ToolItem.new()
        lhbox = Gtk.HBox()
        menuviews = self.gui.add_widget('gtk_button_menu_views', Gtk.Button())
        hbox = Gtk.HBox()
        label = self.gui.add_widget('gtk_label_current_view', Gtk.Label())
        label.set_xalign(0.0)
        image = self.gui.add_widget('gtk_image_current_view', Gtk.Image())
        hbox.pack_start(image, False, False, 3)
        hbox.pack_start(label, True, True, 3)
        menuviews.add(hbox)
        lhbox.pack_start(menuviews, True, True, 3)
        lhbox.show_all()
        tool.add(lhbox)
        tool.set_expand(False)
        self.tool_bar.insert(tool, -1)

        ### Popover menuviews
        popover = self.gui.add_widget('gtk_popover_button_menu_views', Gtk.Popover.new(menuviews))
        menuviews.connect('clicked', self.cb.gui_show_popover, popover)
        box = Gtk.Box(spacing = 0, orientation="vertical")
        popover.add(box)


        box.pack_start(self.uif.create_menuview_button('annotation'), False, False, 0)
        separator = Gtk.Separator(orientation = Gtk.Orientation.HORIZONTAL)
        box.pack_start(separator, False, False, 0)
        box.pack_start(self.uif.create_menuview_button('collection'), False, False, 0)
        box.pack_start(self.uif.create_menuview_button('component'), False, False, 0)
        box.pack_start(self.uif.create_menuview_button('description'), False, False, 0)
        box.pack_start(self.uif.create_menuview_button('bookmarks'), False, False, 0)
        box.pack_start(self.uif.create_menuview_button('category'), False, False, 0)
        box.pack_start(self.uif.create_menuview_button('chronologic'), False, False, 0)
        box.pack_start(self.uif.create_menuview_button('priority'), False, False, 0)
        box.pack_start(self.uif.create_menuview_button('type'), False, False, 0)

        # Import button
        tool = Gtk.ToolButton()
        tool.set_icon_name('basico-add')
        popover = self.gui.add_widget('gtk_popover_toolbutton_import', Gtk.Popover.new(tool))
        tool.connect('clicked', self.cb.gui_show_popover, popover)
        self.tool_bar.insert(tool, -1)

        ## Popover body
        box = Gtk.VBox(spacing = 0, orientation="vertical")
        box.set_property('margin', 3)
        widget_import = self.gui.add_widget('widget_import', ImportWidget(self.app))
        box.pack_start(widget_import, True, True, 6)
        popover.add(box)

        # Annotation button
        tool = Gtk.ToolButton()
        tool.set_icon_name('basico-annotation')
        popover = self.gui.add_widget('gtk_popover_annotation', Gtk.Popover.new(tool))
        # ~ tool.connect('clicked', self.cb.gui_show_popover, popover)
        tool.connect('clicked', self.cb.gui_annotation_widget_show)
        self.tool_bar.insert(tool, -1)

        ## Popover body
        box = Gtk.VBox(spacing = 0, orientation="vertical")
        box.set_property('margin', 3)
        widget_import = self.gui.add_widget('widget_annotation', AnnotationWidget(self.app))
        box.pack_start(widget_import, True, True, 6)
        popover.add(box)


        # Filter entry
        tool = Gtk.ToolItem.new()

        hbox = Gtk.HBox()
        entry = Gtk.Entry()
        entry.connect('activate', self.cb.gui_filter_visor)
        self.gui.add_widget('gtk_entry_filter_visor', entry)

        icon = self.im.get_pixbuf_icon('basico-find')
        entry.set_icon_from_pixbuf(Gtk.EntryIconPosition.PRIMARY, icon)
        entry.set_icon_sensitive(Gtk.EntryIconPosition.PRIMARY, True)
        entry.set_icon_tooltip_markup (Gtk.EntryIconPosition.PRIMARY, "Search in the whole database")

        icon = self.im.get_pixbuf_icon('basico-filter')
        entry.set_icon_from_pixbuf(Gtk.EntryIconPosition.SECONDARY, icon)
        entry.set_icon_sensitive(Gtk.EntryIconPosition.SECONDARY, True)
        entry.set_icon_tooltip_markup (Gtk.EntryIconPosition.SECONDARY, "Click here to filter results")
        entry.set_placeholder_text("Filter results...")

        def on_icon_pressed(entry, icon_pos, event):
            if icon_pos == Gtk.EntryIconPosition.PRIMARY:
                self.cb.action_search(entry)
            elif icon_pos == Gtk.EntryIconPosition.SECONDARY:
                self.cb.gui_filter_visor(entry)

        entry.connect('changed', self.cb.gui_filter_visor)
        entry.connect("icon-press", on_icon_pressed)
        hbox.pack_start(entry, True, True, 0)
        tool.add(hbox)
        tool.set_expand(True)
        self.tool_bar.insert(tool, -1)

        # ~ Separator
        tool = Gtk.SeparatorToolItem.new()
        tool.set_draw(False)
        tool.set_expand(True)
        self.tool_bar.insert(tool, -1)

        # Button Total SAP Notes
        tool = Gtk.ToolItem()
        tool.set_expand(False)
        button = self.gui.add_widget('gtk_button_total_notes', Gtk.Button())
        button.set_relief(Gtk.ReliefStyle.NONE)

        popover = self.gui.add_widget('gtk_popover_button_total_notes', Gtk.Popover.new(button))
        label = self.gui.add_widget('gtk_label_total_notes', Gtk.Label())
        hbox = Gtk.HBox()
        hbox.pack_start(label, False, False, 0)
        button.add(hbox)
        button.connect('clicked', self.cb.gui_show_popover, popover)
        tool.add(button)
        self.tool_bar.insert(tool, -1)

        ## Popover body
        box = self.build_popover(popover)
        popover.add(box)

        # Fullscreen toggle button
        tool = Gtk.ToolItem()
        tool.set_expand(False)
        icon = self.im.get_new_image_icon('basico-fullscreen', 24, 24)
        box = self.gui.add_widget('gtk_box_container_icon_fullscreen', Gtk.Box())
        box.pack_start(icon, False, False, 0)
        button = Gtk.ToggleButton()
        button.set_relief(Gtk.ReliefStyle.NONE)
        button.connect('toggled', self.cb.gui_toggle_fullscreen)
        button.add(box)
        tool.add(button)
        self.tool_bar.insert(tool, -1)

        # Toolbar initial settings
        self.set_visible(True)
        self.set_no_show_all(False)
        self.tool_bar.set_hexpand(True)


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


    def build_popover(self, popover):
        box = Gtk.Box(spacing = 3, orientation="vertical")
        sid = '0000000000'

        def get_popover_button(text, icon_name):
            button = Gtk.Button()
            button.set_relief(Gtk.ReliefStyle.NONE)
            hbox = Gtk.HBox()
            icon = self.im.get_new_image_icon(icon_name, 24, 24)
            lbltext = Gtk.Label()
            lbltext.set_xalign(0.0)
            lbltext.set_markup('%s' % text)
            hbox.pack_start(icon, False, False, 3)
            hbox.pack_start(lbltext, True, True, 3)
            button.add(hbox)
            return button

        # Popover button "Bookmark"
        button = get_popover_button("(Un)bookmark SAP Notes", 'basico-bookmarks')
        button.connect('clicked', self.cb.switch_bookmark_current_set, popover)
        box.pack_start(button, False, False, 0)

        # Separator
        separator = Gtk.Separator(orientation = Gtk.Orientation.HORIZONTAL)
        box.pack_start(separator, True, True, 0)

        # Popover button Collection Management
        button = get_popover_button("Manage collections", 'basico-collection')
        box.pack_start(button, False, False, 0)
        self.popcollections = self.gui.add_widget('gtk_popover_button_manage_collections_selected_notes', Gtk.Popover.new(button))
        self.popcollections.set_position(Gtk.PositionType.RIGHT)
        button.connect('clicked', self.cb.gui_show_popover, self.popcollections)
        self.popcollections.add(CollectionsMgtView(self.app, sid, self.popcollections))

        # Separator
        separator = Gtk.Separator(orientation = Gtk.Orientation.HORIZONTAL)
        box.pack_start(separator, True, True, 0)


        # Popover button "Delete SAP Notes"
        button = get_popover_button("Delete SAP Notes", 'basico-delete')
        button.connect('clicked', self.cb.sapnote_delete, sid)
        box.pack_start(button, False, False, 0)

        return box

class SAPNoteViewVisor(Gtk.Box, Service):
    def __init__(self, app):
        Gtk.Box.__init__(self, app)
        self.app = app
        self.bag = []
        self.get_services()
        self.setup()


    def get_services(self):
        self.gui = self.app.get_service("GUI")
        self.cb = self.app.get_service('Callbacks')
        self.sap = self.app.get_service('SAP')
        self.im = self.app.get_service('IM')
        self.settings = self.app.get_service('Settings')
        self.db = self.app.get_service('DB')
        self.uif = self.app.get_service("UIF")
        self.utils = self.app.get_service("Utils")
        self.annot = self.app.get_service('Annotation')

    def get_treeview(self):
        return self.treeview


    def setup(self):
        scr = Gtk.ScrolledWindow()
        scr.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        scr.set_shadow_type(Gtk.ShadowType.IN)
        viewport = Gtk.Viewport()
        self.treeview = Gtk.TreeView()
        viewport.add(self.treeview)
        scr.add(viewport)
        self.pack_start(scr, True, True, 6)

        # Setup model
        self.model = Gtk.TreeStore(
            int,        # key
            Pixbuf,     # Icon
            int,        # checkbox
            str,        # sid
            str,        # title
            str,        # component
            str,        # category
            str,        # type
            str,        # priority
            str,        # last update
            str,        # Annotation Id (extra key)
            str,        # Timestamp
        )

        # Setup columns
        def get_column_header_widget(title, icon_name=None, width=24, height=24):
            hbox = Gtk.HBox()
            icon = self.im.get_new_image_icon(icon_name, width, height)
            label = Gtk.Label()
            label.set_markup("<b>%s</b>" % title)
            label.modify_font(Pango.FontDescription('Monospace 10'))
            hbox.pack_start(icon, False, False, 3)
            hbox.pack_start(label, True, True, 3)
            hbox.show_all()
            return hbox

        # SAP Note key
        self.renderer_key = Gtk.CellRendererText()
        self.renderer_key.set_property('height', 32)
        self.column_key = Gtk.TreeViewColumn('Key', self.renderer_key, text=0)
        self.column_key.set_visible(False)
        self.column_key.set_expand(False)
        self.column_key.set_clickable(False)
        self.column_key.set_sort_indicator(False)
        self.treeview.append_column(self.column_key)

        # Icon
        self.renderer_icon = Gtk.CellRendererPixbuf()
        self.renderer_icon.set_alignment(0.0, 0.5)
        self.column_icon = Gtk.TreeViewColumn('Bookmark', self.renderer_icon, pixbuf=1)
        widget = get_column_header_widget('', 'basico-bookmarks')
        self.column_icon.set_widget(widget)
        self.column_icon.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        self.column_icon.set_visible(True)
        self.column_icon.set_expand(False)
        self.column_icon.set_clickable(False)
        self.column_icon.set_sort_indicator(False)
        self.treeview.append_column(self.column_icon)

        # SAP Note Checkbox
        self.renderer_checkbox = Gtk.CellRendererToggle()
        self.renderer_checkbox.connect("toggled", self.toggle_checkbox)
        self.column_checkbox = Gtk.TreeViewColumn('', self.renderer_checkbox, active=2)
        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.column_checkbox.set_property('spacing', 50)
        self.treeview.append_column(self.column_checkbox)

        # SAP Note Id
        self.renderer_sid = Gtk.CellRendererText()
        self.renderer_sid.set_property('xalign', 1.0)
        self.renderer_sid.set_property('height', 36)
        self.renderer_sid.set_property('background', '#F0E3E3')
        self.column_sid = Gtk.TreeViewColumn('SAP Note Id', self.renderer_sid, markup=3)
        widget = get_column_header_widget('SAP Note Id', 'basico-sid')
        self.column_sid.set_widget(widget)
        self.column_sid.set_visible(True)
        self.column_sid.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        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(0)
        self.column_sid.set_sort_order(Gtk.SortType.ASCENDING)
        self.model.set_sort_column_id(0, Gtk.SortType.ASCENDING)
        self.treeview.append_column(self.column_sid)

        # SAP Note title
        self.renderer_title = Gtk.CellRendererText()
        self.renderer_title.set_property('background', '#FFFEEA')
        self.renderer_title.set_property('ellipsize', Pango.EllipsizeMode.MIDDLE)
        self.column_title = Gtk.TreeViewColumn('Title', self.renderer_title, markup=4)
        widget = get_column_header_widget('Title', 'basico-tag')
        self.column_title.set_widget(widget)
        self.column_title.set_visible(True)
        self.column_title.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        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(4)
        self.treeview.append_column(self.column_title)

        # SAP Note Component
        self.renderer_component = Gtk.CellRendererText()
        self.renderer_component.set_property('background', '#E3E3F0')
        self.column_component = Gtk.TreeViewColumn('Component', self.renderer_component, markup=5)
        widget = get_column_header_widget('Component', 'basico-component')
        self.column_component.set_widget(widget)
        self.column_component.set_visible(True)
        self.column_component.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        self.column_component.set_expand(False)
        self.column_component.set_clickable(True)
        self.column_component.set_sort_indicator(True)
        self.column_component.set_sort_column_id(5)
        self.treeview.append_column(self.column_component)

        # SAP Note Category
        self.renderer_category = Gtk.CellRendererText()
        self.renderer_category.set_property('background', '#E3F1E3')
        self.column_category = Gtk.TreeViewColumn('Category', self.renderer_category, markup=6)
        widget = get_column_header_widget('Category', 'basico-category')
        self.column_category.set_widget(widget)
        self.column_category.set_visible(False)
        self.column_category.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        self.column_category.set_expand(False)
        self.column_category.set_clickable(True)
        self.column_category.set_sort_indicator(True)
        self.column_category.set_sort_column_id(6)
        self.treeview.append_column(self.column_category)

        # SAP Note Type
        self.renderer_type = Gtk.CellRendererText()
        self.renderer_type.set_property('background', '#e4f1f1')
        self.column_type = Gtk.TreeViewColumn('Type', self.renderer_type, markup=7)
        self.column_type.set_visible(True)
        self.column_type.set_expand(False)
        self.column_type.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        self.column_type.set_clickable(True)
        self.column_type.set_sort_indicator(True)
        self.column_type.set_sort_column_id(7)
        self.treeview.append_column(self.column_type)

        # SAP Note Priority
        self.renderer_priority = Gtk.CellRendererText()
        self.column_priority = Gtk.TreeViewColumn('Priority', self.renderer_priority, markup=8)
        self.column_priority.set_visible(False)
        self.column_priority.set_expand(True)
        self.column_priority.set_clickable(True)
        self.column_priority.set_sort_indicator(True)
        self.column_priority.set_sort_column_id(8)
        self.treeview.append_column(self.column_priority)

        # SAP Note UpdatedOn
        self.renderer_updated = Gtk.CellRendererText()
        self.renderer_updated.set_property('background', '#FFE6D1')
        self.column_updated = Gtk.TreeViewColumn('Updated On', self.renderer_updated, markup=9)
        self.column_updated.set_visible(True)
        self.column_updated.set_expand(False)
        self.column_updated.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        self.column_updated.set_clickable(True)
        self.column_updated.set_sort_indicator(True)
        self.column_updated.set_sort_column_id(11)
        self.treeview.append_column(self.column_updated)

        # Annotation Id
        self.renderer_annotation = Gtk.CellRendererText()
        self.column_annotation = Gtk.TreeViewColumn('Annotation Id', self.renderer_annotation, markup=10)
        self.column_annotation.set_visible(False)
        self.column_annotation.set_expand(False)
        self.column_annotation.set_clickable(False)
        self.column_annotation.set_sort_indicator(False)
        self.treeview.append_column(self.column_annotation)

        # Timestamp
        self.renderer_timestamp = Gtk.CellRendererText()
        self.column_timestamp = Gtk.TreeViewColumn('Annotation Id', self.renderer_timestamp, text=11)
        self.column_timestamp.set_visible(False)
        self.column_timestamp.set_expand(False)
        self.column_timestamp.set_clickable(False)
        self.column_timestamp.set_sort_indicator(False)
        self.treeview.append_column(self.column_timestamp)

        # Treeview properties
        self.treeview.set_can_focus(False)
        self.treeview.set_enable_tree_lines(True)
        self.treeview.set_headers_visible(True)
        self.treeview.set_enable_search(True)
        self.treeview.set_hover_selection(False)
        self.treeview.set_grid_lines(Gtk.TreeViewGridLines.NONE)
        self.treeview.set_enable_tree_lines(True)
        self.treeview.set_level_indentation(10)
        self.treeview.modify_font(Pango.FontDescription('Monospace 10'))
        self.treeview.connect('button_press_event', self.right_click)

        # DOC: In order to have a Gtk.Widged with sorting and filtering
        # capabilities, you have to filter the model first, and use this
        # new model to create the sorted model. Then, attach the sorted
        # model to the treeview...

        # Treeview filtering:
        self.visible_filter = self.model.filter_new()
        self.visible_filter.set_visible_func(self.visible_function)
        # ~ treeview.set_search_equal_func(self.search_function)
        # https://stackoverflow.com/questions/23355866/user-search-collapsed-rows-in-a-gtk-treeview

        # TreeView sorting
        self.sorted_model = Gtk.TreeModelSort(model=self.visible_filter)
        self.sorted_model.set_sort_func(0, self.sort_function, None)

        # Selection
        self.selection = self.treeview.get_selection()
        self.selection.set_mode(Gtk.SelectionMode.SINGLE)
        self.selection.connect('changed', self.row_changed)

        # Set model (filtered and sorted)
        self.treeview.set_model(self.sorted_model)

        self.show_all()


    def sort_function(self, model, row1, row2, user_data):
        sort_column = 0

        value1 = model.get_value(row1, sort_column)
        value2 = model.get_value(row2, sort_column)

        if value1 < value2:
            return -1
        elif value1 == value2:
            return 0
        else:
            return 1


    def visible_function(self, model, itr, data):
        entry = self.gui.get_widget('gtk_entry_filter_visor')
        text = entry.get_text()
        sid = str(model.get(itr, 3)[0])
        if sid.startswith('<'):
            sid = sid[3:-4]
        title = model.get(itr, 4)[0]
        component = str(model.get(itr, 5)[0])
        category = model.get(itr, 6)[0]
        rtype = model.get(itr, 7)[0]
        string = ' '.join(['SAP Note %s' % sid, title, component, category, rtype])
        match = text.upper() in string.upper()
        # ~ self.debug("%s in %s? %s" % (text, string, match))

        return match


    def update_total_sapnotes_count(self, count=0):
        statusbar = self.gui.get_widget('widget_statusbar')
        lblnotescount = self.gui.get_widget('gtk_label_total_notes')
        total = self.db.get_total()

        lblnotescount.set_markup("<b>%d/<big>%d</big></b>" % (count, total))
        statusbar.message("<b>View populated with %d SAP Notes</b>" % count)


    def get_visible_filter(self):
        return self.visible_filter


    def row_changed(self, selection):
        try:
            model, treeiter = selection.get_selected()
            if treeiter is not None:
                component = model[treeiter][5]
                if component == 'Annotation':
                    aid = model[treeiter][10]
                    is_valid = self.annot.is_valid(aid)
                    if is_valid:
                        self.cb.action_annotation_edit(aid)
                else:
                    aid = None
                    self.uif.set_widget_visibility('gtk_vbox_container_annotations', False)
        except Exception as error:
            self.debug(error)
            self.print_traceback()


    def toggle_checkbox(self, cell, path):
        self.model[path][2] = not self.model[path][2]


    def get_node(self, key, icon, checkbox, sid, title, component, category='', sntype='', priority='', updated='', aid='', timestamp=''):
        node = []
        node.append(key)
        node.append(icon)
        node.append(checkbox)
        node.append(sid)
        node.append(title)
        node.append(component)
        node.append(category)
        node.append(sntype)
        node.append(priority)
        node.append(updated)
        node.append(aid) # Extra key for annotations id (aid)
        node.append(timestamp)
        return node


    def refresh(self):
        self.populate()


    def get_bag(self):
        return self.bag


    def populate(self, bag=None, cid=None):
        icon_annotation = self.im.get_pixbuf_icon('basico-annotation', 32, 32)
        icon_content = self.im.get_pixbuf_icon('basico-logviewer', 32, 32)
        icon_link = self.im.get_pixbuf_icon('basico-browser', 32, 32)
        icon_empty = self.im.get_pixbuf_icon('basico-empty', 32, 32)
        icon_sapnote = self.im.get_pixbuf_icon('basico-sapnote', 32, 32)
        icon_bookmark = self.im.get_pixbuf_icon('basico-bookmarks', 32, 32)
        self.column_sid.set_visible(True)

        if bag is None:
            bag = self.bag
        else:
            self.bag = bag


        self.model.clear()

        for sid in self.bag:
            metadata = self.db.get_sapnote_metadata(sid)
            if metadata is not None:
                bookmark = metadata['bookmark']
                title = escape(metadata['title'])
                sid = str(int(metadata['id']))
                if bookmark:
                    icon = icon_bookmark
                    title = "<b>%s</b>" % title
                    sid = "<b>%s</b>" % sid
                else:
                    icon = icon_sapnote

                timestamp = metadata['releaseon']
                timestamp = timestamp.replace('-', '')
                timestamp = timestamp.replace(':', '')
                timestamp = timestamp.replace('T', '_')

                node = self.get_node(   int(metadata['id']),
                                        icon,
                                        bookmark,
                                        '<b>%s</b>' % sid,
                                        title,
                                        escape(metadata['componentkey']),
                                        escape(metadata['category']),
                                        escape(metadata['type']),
                                        escape(metadata['priority']),
                                        self.utils.fuzzy_date_from_timestamp(timestamp),
                                        '',
                                        timestamp
                                    )
                pid = self.model.append(None, node)

                # Load annotations
                files = self.annot.get_by_sid(metadata['id'])
                for fname in files:
                    with open(fname, 'r') as fa:
                        annotation = json.load(fa)
                        self.debug("Populating annotation: %s" % annotation['aid'])
                        node = self.get_node(   0,
                                                icon_annotation,
                                                False,
                                                '',
                                                annotation['title'],
                                                annotation['component'],
                                                '',
                                                annotation['type'],
                                                '',
                                                self.utils.fuzzy_date_from_timestamp(annotation['timestamp']),
                                                annotation['aid'],
                                                annotation['timestamp']
                                            )
                        self.model.append(pid, node)
        self.treeview.set_model(self.sorted_model)
        self.update_total_sapnotes_count(len(self.model))
        self.show_widgets()
        stack = self.gui.get_widget('gtk_stack_main')
        stack.set_visible_child_name('visor')
        # ~ self.annot.get_all()


    def populate_annotations(self, annotations=None):
        icon_annotation = self.im.get_pixbuf_icon('basico-annotation', 32, 32)
        icon_content = self.im.get_pixbuf_icon('basico-logviewer', 32, 32)
        icon_link = self.im.get_pixbuf_icon('basico-browser', 32, 32)
        icon_empty = self.im.get_pixbuf_icon('basico-empty', 32, 32)
        icon_bookmark = self.im.get_pixbuf_icon('basico-bookmarks', 32, 32)
        icon_sapnote = self.im.get_pixbuf_icon('basico-sapnote', 32, 32)
        self.column_sid.set_visible(False)

        self.model.clear()

        if annotations is None:
            annotations = self.annot.get_all()

        snpids = {}

        for fname in annotations:
            with open(fname, 'r') as fa:
                annotation = json.load(fa)
                sid = self.annot.get_sid(annotation['aid'])
                # ~ if sid != '0000000000':
                    # ~ try:
                        # ~ pid = snpids[self.db.normalize_sid(sid)]
                    # ~ except:
                        # ~ metadata = self.db.get_sapnote_metadata(sid)
                        # ~ if metadata is not None:
                            # ~ bookmark = metadata['bookmark']
                            # ~ title = escape(metadata['title'])
                            # ~ sid = str(int(metadata['id']))
                            # ~ if bookmark:
                                # ~ icon = icon_bookmark
                                # ~ title = "<b>%s</b>" % title
                                # ~ sid = "<b>%s</b>" % sid
                            # ~ else:
                                # ~ icon = icon_sapnote

                            # ~ timestamp = metadata['releaseon']
                            # ~ timestamp = timestamp.replace('-', '')
                            # ~ timestamp = timestamp.replace(':', '')
                            # ~ timestamp = timestamp.replace('T', '_')
                            # ~ node = self.get_node(   int(metadata['id']),
                                                # ~ icon,
                                                # ~ bookmark,
                                                # ~ '<b>%s</b>' % sid,
                                                # ~ title,
                                                # ~ escape(metadata['componentkey']),
                                                # ~ escape(metadata['category']),
                                                # ~ escape(metadata['type']),
                                                # ~ escape(metadata['priority']),
                                                # ~ self.utils.fuzzy_date_from_timestamp(timestamp),
                                                # ~ '',
                                                # ~ timestamp
                                                # ~ )
                            # ~ pid = self.model.append(None, node)
                            # ~ snpids[self.db.normalize_sid(sid)] = pid
                # ~ else:
                    # ~ pid = None
                node = self.get_node(   0,
                                        icon_annotation,
                                        False,
                                        str(int(sid)),
                                        annotation['title'],
                                        annotation['component'],
                                        '',
                                        annotation['type'],
                                        '',
                                        self.utils.fuzzy_date_from_timestamp(annotation['timestamp']),
                                        annotation['aid'],
                                        annotation['timestamp']
                                    )
                self.model.append(None, node)
        self.treeview.set_model(self.sorted_model)
        self.treeview.expand_all()
        self.update_total_sapnotes_count(len(self.model))
        self.show_widgets()
        stack = self.gui.get_widget('gtk_stack_main')
        stack.set_visible_child_name('visor')


    def show_widgets(self):
        button = self.gui.get_widget('gtk_button_total_notes')
        button.set_no_show_all(False)
        button.show_all()


    def right_click(self, treeview, event, data=None):
        if event.button == 3:
            rect = Gdk.Rectangle()
            rect.x = x = int(event.x)
            rect.y = y = int(event.y)
            pthinfo = self.treeview.get_path_at_pos(x,y)
            if pthinfo is not None:
                path,col,cellx,celly = pthinfo
                model = treeview.get_model()
                treeiter = model.get_iter(path)
                component = model[treeiter][5]
                sid = model[treeiter][0]
                sid = "0"*(10 - len(str(sid))) + str(sid)
                toolbar = self.gui.get_widget('visortoolbar')
                popover = self.gui.add_widget('gtk_popover_visor_row', Gtk.Popover.new(treeview))
                popover.set_position(Gtk.PositionType.TOP)
                popover.set_pointing_to(rect)
                box = self.build_popover(sid, popover, component)
                popover.add(box)
                self.cb.gui_show_popover(None, popover)


    def build_popover(self, sid, popover, component):
        box = Gtk.Box(spacing = 3, orientation="vertical")

        def get_popover_button(text, icon_name):
            button = Gtk.Button()
            button.set_relief(Gtk.ReliefStyle.NONE)
            hbox = Gtk.HBox()
            icon = self.im.get_new_image_icon(icon_name, 24, 24)
            lbltext = Gtk.Label()
            lbltext.set_xalign(0.0)
            lbltext.set_markup('%s' % text)
            hbox.pack_start(icon, False, False, 3)
            hbox.pack_start(lbltext, True, True, 3)
            button.add(hbox)
            return button

        if component == 'Annotation':
            # Popover button "Delete annotation"
            button = get_popover_button("Delete annotation", 'basico-delete')
            button.show_all()
            button.connect('clicked', self.cb.action_annotation_delete)
            box.pack_start(button, False, False, 0)

            # Popover button "Duplicate annotation"
            button = get_popover_button("Duplicate annotation", 'basico-duplicate')
            button.show_all()
            button.connect('clicked', self.cb.action_annotation_duplicate)
            box.pack_start(button, False, False, 0)

        else:
            # Popover button "Add an annotation"
            button = get_popover_button("Add an annotation", 'basico-annotation')
            button.show_all()
            button.connect('clicked', self.cb.gui_annotation_widget_show, sid, 'create')
            box.pack_start(button, False, False, 0)

            # Popover button "Open SAP Note"
            button = get_popover_button("See SAP Note", 'basico-browse')
            button.connect('clicked', self.cb.sapnote_browse, sid)
            box.pack_start(button, False, False, 0)

            # Popover button "Bookmark"
            button = get_popover_button("(Un)bookmark SAP Note", 'basico-bookmarks')
            button.connect('clicked', self.cb.switch_bookmark, [sid], popover)
            box.pack_start(button, False, False, 0)

            # Separator
            separator = Gtk.Separator(orientation = Gtk.Orientation.HORIZONTAL)
            box.pack_start(separator, True, True, 0)

            # Popover button Collection Management
            button = get_popover_button("Manage collections", 'basico-collection')
            box.pack_start(button, False, False, 0)
            self.popcollections = self.gui.add_widget('gtk_popover_button_manage_collections_single_note', Gtk.Popover.new(button))
            self.popcollections.set_position(Gtk.PositionType.RIGHT)
            button.connect('clicked', self.cb.gui_show_popover, self.popcollections)
            self.popcollections.add(CollectionsMgtView(self.app, sid, self.popcollections))

            # Separator
            separator = Gtk.Separator(orientation = Gtk.Orientation.HORIZONTAL)
            box.pack_start(separator, True, True, 0)


            # Popover button "Delete SAP Note"
            button = get_popover_button("Delete SAP Note", 'basico-delete')
            button.connect('clicked', self.cb.sapnote_delete, sid)
            box.pack_start(button, False, False, 0)

        return box