Skip to content

Instantly share code, notes, and snippets.

@introt
Forked from muguu/find_tagged_pages_wip.py
Created September 28, 2021 18:39
Show Gist options
  • Save introt/eb649fd39cd80abaec7e204b270bc9ff to your computer and use it in GitHub Desktop.
Save introt/eb649fd39cd80abaec7e204b270bc9ff to your computer and use it in GitHub Desktop.
Plugin for ZimWiki >=0.67 (Work in progress)
# -*- coding: utf-8 -*-
# Plugin for ZimWiki: Find tagged pages.
# Copyright 2018 Murat Güven <muratg@online.de>
# Copyright ZimWiki: 2009-2018 Jaap Karssenberg <jaap.karssenberg@gmail.com>
# V1.251 for Zim >= 0.67
# Change Log
# V1.251 Rework on search
# V1.25 Bug fixed when removing last common tag, list was reset
# V1.241 Added n_page to common tags list
# V1.24 Code Cleaning. Bug fixed when removing last selected tag, completion was not reset
# V1.231 GUI Rework, Button hide
# V1.22 Code cleaning and improved "add pages as link" function using zim functions to show text style and links directly
# without reloading
# V1.21 Added tag list and speed up of selection of tags.
# V1.20 Updated to support ZimWiki >=0.67
# V1.19 Little bug fix when selecting the tree title of pages (All common pages of ...) throws a value error. Code cleaning
# V1.18 Using zim widgets
# V1.17 Renamed to find tagged pages
# V1.16: ESC to close plugin window added
# V1.15: If cursor is at empty line, don't go back to previous line / word and lookup a tag.
# Also changed search beaviour. Now looks for word which is right to the cursor instead of left
# V1.14: Now getting word at cursor position without the need to mark the text in order to look it up
# V1.13: Little bug fix
# V1.12: Added functionality to lookup a tag which is selected.
import gtk
import time
import datetime
import sys
from zim.plugins import PluginClass, extends, WindowExtension
from zim.notebook import Path
from zim.search import *
from zim.signals import ConnectorMixin
from zim.actions import action
from zim.gui.widgets import Window, ScrolledWindow, VPaned, HPaned, InputEntry, Button, BrowserTreeView
from zim.gui import searchdialog
ALTSHIFTQ = '<alt><shift>q'
ENTRY_LABEL_WIDTH_PIX = 50
BUTTON_HIGHT_PIX = 27
BUTTON_WIDTH_PIX = 80
H_SPACING_PIX = 3
TABLE_PADDING_LEFT_PIX = 10
TABLE_PADDING_TOP_PIX = 10
TABLE_COL_SPACINGS = 10
TABLE_ROW_SPACINGS = 2
ENTRY_HIGHT_PIX = 25
ENTRY_WIDTH_PIX = 180
PADDING_TOP_PIX = 10
CHECK_BOX_PADDING_LEFT_PIX = ENTRY_LABEL_WIDTH_PIX + 1
SIZE_X = H_SPACING_PIX + ENTRY_LABEL_WIDTH_PIX + \
H_SPACING_PIX + ENTRY_WIDTH_PIX + \
H_SPACING_PIX + BUTTON_WIDTH_PIX \
+ H_SPACING_PIX + BUTTON_WIDTH_PIX + 8 * H_SPACING_PIX
TAG_ENTRY_WIDTH_PIX = (SIZE_X - 2 * TABLE_PADDING_LEFT_PIX - 3 * TABLE_COL_SPACINGS) / 2
SELECTED_TAGS_WIN_WIDTH = (TAG_ENTRY_WIDTH_PIX + 2 * TABLE_PADDING_LEFT_PIX + TABLE_COL_SPACINGS ) * 2
SELECTED_TAGS_WIN_HEIGHT = ENTRY_HIGHT_PIX * 4 + 20
COMMON_TAGS_WIN_WIDTH = 200
COMMON_TAGS_WIN_HEIGHT = 200
class FindTaggedPagesPlugin(PluginClass):
plugin_info = {
'name': _('Find tagged pages'), # T: plugin name
'description': _('''\
This plugin helps finding pages or tags by narrowing down to selected and common tags.
In addition you can use an open search filter.
V1.251 (wip)
'''), # T: plugin description
'author': 'Murat Güven',
'help': 'Plugins:Find tagged pages',
}
plugin_preferences = (
# T: label for plugin preferences dialog
('add_pages_separator', 'string', _('Add pages as link function: Tag separator to use'), '||'),
('add_pages_atsign', 'bool', _('Add pages as link function: Paste tags as tags (with @)'), False),
# T: plugin preference
)
@extends('MainWindow')
class FindTaggedPagesMainWindowExtension(WindowExtension, ConnectorMixin):
uimanager_xml = '''
<ui>
<menubar name='menubar'>
<menu action='search_menu'>
<placeholder name='plugin_items'>
<menuitem action='find_tagged_pages'/>
</placeholder>
</menu>
</menubar>
<toolbar name='toolbar'>
<placeholder name='tools'>
<toolitem action='find_tagged_pages'/>
</placeholder>
</toolbar>
</ui>
'''
accel_key = ALTSHIFTQ
@action(_('Find tagged pages'), stock='gtk-find', accelerator=accel_key, readonly=True) # T: menu item
def find_tagged_pages(self):
FindTaggedPagesDialog(self.window, self.plugin).__init__
class FindTaggedPagesDialog:
def __init__(self, window, plugin):
self.ui = window.ui
self.index = window.ui.notebook.index
self.notebook_pages = self.ui.notebook.pages
self.notebook_tags = self.ui.notebook.tags
self.window = window
self.plugin = plugin
self.main_entry = InputEntry()
self.filter_entry = InputEntry()
self.progress_bar = gtk.ProgressBar()
self.progress_bar_fraction = self.notebook_pages.n_list_pages() / 5000.0
self.n_pages = 1
self.table_selected_tags = gtk.Table()
self.table_selected_tags.set_col_spacings(TABLE_COL_SPACINGS)
self.table_selected_tags.set_row_spacings(TABLE_ROW_SPACINGS)
self.search_win = Window()
self.search_win.set_title("Find tagged pages")
self.vbox_main = gtk.VBox()
self.vbox_main.set_size_request(COMMON_TAGS_WIN_HEIGHT + SELECTED_TAGS_WIN_WIDTH + 60, 600)
self.hpaned_main = HPaned()
self.vpaned_right = VPaned()
self.vbox_common_tags = gtk.VBox(False, H_SPACING_PIX)
self.vbox_selected_tags_group = gtk.VBox(False, H_SPACING_PIX)
self.vbox_pages = gtk.VBox(False, H_SPACING_PIX)
# homogeneous=False, spacing=0
self.hbox_tag_entry = gtk.HBox(False, H_SPACING_PIX)
self.hbox_search = gtk.HBox(False, H_SPACING_PIX)
self.hbox_check_buttons = gtk.HBox(False, H_SPACING_PIX)
self.vbox_selected_tags = gtk.VBox(False, H_SPACING_PIX)
# xalign=0.0, yalign=0.0, xscale=0.0, yscale=0.0
self.halign_tag_entry = gtk.Alignment(0, 0, 0, 0)
self.halign_search = gtk.Alignment(0, 0, 0, 0)
self.halign_check_buttons = gtk.Alignment(0, 0, 0, 0)
self.halign_table_selected_tags = gtk.Alignment(0, 0, 0, 0)
self.halign_label_selected_tags = gtk.Alignment(0, 0, 0, 0)
self.search_scrolled_win_buttons = gtk.ScrolledWindow()
self.search_scrolled_win_selected_tags = gtk.ScrolledWindow()
self.search_scrolled_win_selected_tags.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
self.search_scrolled_win_selected_tags.set_border_width(10)
self.search_scrolled_win_selected_tags.set_shadow_type(gtk.SHADOW_NONE)
self.search_scrolled_win_common_tags_tree = gtk.ScrolledWindow()
self.search_scrolled_win_common_tags_tree.set_border_width(10)
self.search_scrolled_win_common_tags_tree.set_shadow_type(gtk.SHADOW_ETCHED_IN)
self.search_scrolled_win_pages = gtk.ScrolledWindow()
self.search_scrolled_win_pages.set_border_width(10)
self.search_scrolled_win_pages.set_shadow_type(gtk.SHADOW_IN)
self.search_help_text = (
'For advanced search you can use operators like\n'
'+ AND and &&,\n'
'OR or ||, \n'
'- NOT \n'
'Keywords:\n'
'Content: or Name: or NameSpace:\n'
'Links: or LinksFrom: or LinksTo:\n'
'See the help page for the general search function for more details.'
) # T: help text for the search dialog
self.tag_help_text = (
'Enter and select a tag from a list of your tags.\n'
'The tag entered here will be shown below in selected tags.\n\n'
'The available list of your tags will show all tags\n'
'which have common pages with the selected tags\n'
)
#self.search_win.set_geometry_hints(min_width=SIZE_X)
self.vpaned_right.set_position(SELECTED_TAGS_WIN_HEIGHT)
self.hpaned_main.set_position(COMMON_TAGS_WIN_WIDTH)
self.search_win.add(self.vbox_main)
self.vbox_main.pack_start(self.hpaned_main, True)
self.frame_common_tags = gtk.Frame(label="Common Tags")
self.frame_selected_tags = gtk.Frame(label="Selected Tags")
self.frame_pages = gtk.Frame(label="Pages of Selected Tags")
self.vbox_frame_common_tags = gtk.HBox(20)
self.vbox_frame_right = gtk.HBox(20)
self.vbox_frame_right.pack_start(self.vpaned_right, True, True, 10)
self.vbox_frame_common_tags.pack_start(self.frame_common_tags, True, True, 10)
self.vbox_common_tags.pack_start(self.halign_tag_entry, False, False, 5)
self.vbox_common_tags.pack_start(self.search_scrolled_win_common_tags_tree, True, H_SPACING_PIX)
self.frame_common_tags.add(self.vbox_common_tags)
self.frame_selected_tags.add(self.vbox_selected_tags)
self.hpaned_main.add1(self.vbox_frame_common_tags)
self.hpaned_main.add2(self.vbox_frame_right)
self.vpaned_right.add1(self.frame_selected_tags)
self.table_selected_tags.set_homogeneous(False)
# Populate Buttons Window_________________________________________________________________________________
# padding_top, padding_bottom, padding_left, padding_right
self.halign_tag_entry.set_padding(PADDING_TOP_PIX, 0, 10, 10)
self.halign_search.set_padding(0, 0, 0, 0)
self.halign_check_buttons.set_padding(PADDING_TOP_PIX, 0, CHECK_BOX_PADDING_LEFT_PIX, 0)
self.halign_tag_entry.add(self.hbox_tag_entry)
self.halign_search.add(self.hbox_search)
self.halign_check_buttons.add(self.hbox_check_buttons)
# Add Page add button__________________________________________________________________________________________
self.hbox_add_pages = gtk.HBox()
self.button_add_page = Button("Add pages as link")
self.hbox_add_pages.pack_start(self.button_add_page, False, False, 10)
# _________________________________________________________________________________________________________
self.vbox_pages.pack_start(self.halign_search, False, False, H_SPACING_PIX)
self.vbox_pages.pack_start(self.halign_check_buttons, False, False, H_SPACING_PIX)
self.vbox_pages.pack_start(self.search_scrolled_win_pages, True, True)
self.vbox_pages.pack_end(self.hbox_add_pages, False, False, 10)
# Add Main Entry field ___________________________________________________________________________________
main_entry_label = gtk.Label("Tag:")
self.main_entry.set_tooltip_text(self.tag_help_text)
self.main_entry.set_icon_from_stock(1, "gtk-clear")
self.hbox_tag_entry.add(main_entry_label)
self.hbox_tag_entry.add(self.main_entry)
# ________________________________________________________________________________________________________
# Add Remove all Button____________________________________________________________________________________
self.button_remove_all = Button("Remove all selected tags")
#self.button_remove_all.set_size_request(BUTTON_WIDTH_PIX, BUTTON_HIGHT_PIX)
#self.hbox1_buttons.add(self.button_remove_all)
# _________________________________________________________________________________________________________
# Add Filter Entry field___________________________________________________________________________________
self.filter_entry.set_tooltip_text(self.search_help_text)
filter_entry_label = gtk.Label("Filter:")
filter_entry_label.set_size_request(ENTRY_LABEL_WIDTH_PIX, ENTRY_HIGHT_PIX)
self.filter_entry.set_icon_from_stock(1, "gtk-clear")
self.filter_entry.set_size_request(ENTRY_WIDTH_PIX, ENTRY_HIGHT_PIX)
self.hbox_search.add(filter_entry_label)
self.hbox_search.add(self.filter_entry)
# _________________________________________________________________________________________________________
# Add search / progress bar ______________________________________________________________________________
self.button_search = Button("Find")
self.button_search.set_size_request(BUTTON_WIDTH_PIX, BUTTON_HIGHT_PIX)
self.button_cancel_search = Button("Stopp")
self.button_cancel_search.set_size_request(BUTTON_WIDTH_PIX, BUTTON_HIGHT_PIX)
self.progress_bar.set_size_request(BUTTON_WIDTH_PIX, BUTTON_HIGHT_PIX)
self.hbox_search.add(self.button_search)
self.hbox_search.add(self.button_cancel_search)
self.hbox_search.add(self.progress_bar)
# _________________________________________________________________________________________________________
# Add Check boxes__________________________________________________________________________________________
self.checkbutton_inter = gtk.CheckButton("Common pages")
self.checkbutton_inter.set_active(is_active=1)
self.checkbutton_union = gtk.CheckButton("Unique pages")
self.hbox_check_buttons.add(self.checkbutton_inter)
self.hbox_check_buttons.add(self.checkbutton_union)
# _________________________________________________________________________________________________________
self.halign_label_selected_tags.set_padding(5, 0, TABLE_PADDING_LEFT_PIX, 0)
self.hbox_remove_button = gtk.HBox()
self.hbox_remove_button.pack_start(self.button_remove_all, False, False, 10)
self.halign_table_selected_tags.add(self.table_selected_tags)
self.halign_table_selected_tags.set_padding(TABLE_PADDING_TOP_PIX, 0, TABLE_PADDING_LEFT_PIX, 0)
self.frame_pages.add(self.vbox_pages)
self.vpaned_right.add2(self.frame_pages)
self.search_scrolled_win_selected_tags.add_with_viewport(self.halign_table_selected_tags)
self.vbox_selected_tags.pack_start(self.search_scrolled_win_selected_tags, True, True)
self.vbox_selected_tags.pack_start(self.hbox_remove_button, False, False, 10)
self.search_scrolled_win_selected_tags.set_size_request(SELECTED_TAGS_WIN_WIDTH, SELECTED_TAGS_WIN_HEIGHT)
FTP = FindTaggedPages(
self.window, self.plugin, self.main_entry,
self.search_scrolled_win_selected_tags,
self.progress_bar, self.progress_bar_fraction,
self.table_selected_tags,
self.filter_entry, self.button_cancel_search, self.button_search,
self.search_scrolled_win_common_tags_tree,
self.button_remove_all, self.button_add_page
)
FTP.show()
self.main_entry.set_completion(FTP.completion)
self.search_scrolled_win_pages.add(FTP.pages_tree_view)
self.search_scrolled_win_common_tags_tree.add(FTP.common_tags_tree_view)
self.search_win.show_all()
self.button_cancel_search.hide_all()
self.progress_bar.hide_all()
self.button_remove_all.hide_all()
self.button_add_page.hide_all()
self.search_win.connect("key_press_event", FTP.get_key_event)
self.main_entry.connect("icon-press", FTP.clear_entry)
self.main_entry.connect('activate', FTP.main_entry_return)
self.filter_entry.connect("icon-press", FTP.clear_filter_entry)
self.filter_entry.connect('activate', FTP.search_filter)
self.button_remove_all.connect("clicked", FTP.button_remove_all_clicked)
#self.button_show_all.connect("clicked", FTP.button_show_all_clicked)
self.button_search.connect("clicked", FTP.search_filter)
self.button_cancel_search.connect("clicked", FTP.search_cancel)
self.button_add_page.connect("clicked", FTP.button_add_page_clicked)
self.checkbutton_inter.connect("toggled", FTP.checkbutton_inter_toggled)
self.checkbutton_union.connect("toggled", FTP.checkbutton_union_toggled)
class FindTaggedPages():
# TODO: Common Tags Tree gets reset if more is selected than possible
# TODO: Show tags of page, when pages is selected
def __init__(self, window, plugin, main_entry, search_scrolled_win_selected_tags,
progress_bar, progress_bar_fraction, table_selected_tags,
filter_entry, button_cancel_search, button_search,
search_scrolled_win_common_tags_tree, button_remove_all, button_add_page):
self.PIXBUF_COL = 0
self.N_PAGE = 1
self.TAG_COL = 2
self.PAGE_COL = 0
self.ui = window.ui
self.index = window.ui.notebook.index
self.window = window
self.plugin = plugin
self.main_entry = main_entry
self.search_scrolled_win_selected_tags = search_scrolled_win_selected_tags
self.filter_entry = filter_entry
self.progress_bar = progress_bar
self.progress_bar_fraction = progress_bar_fraction
self.table_selected_tags = table_selected_tags
self.search_scrolled_win_common_tags_tree = search_scrolled_win_common_tags_tree
self.button_cancel_search = button_cancel_search
self.button_search = button_search
self.button_remove_all = button_remove_all
self.button_add_page = button_add_page
self.completion = gtk.EntryCompletion()
self.all_tags = set()
self.tags_and_pages_store = {}
self.do_store_tags_and_pages()
self.common_tag_list_store = gtk.ListStore(str, str, str)
self.n_all_tags = self.ui.notebook.tags.n_list_all_tags()
self.tag_list_store = gtk.ListStore(str)
self.pages_tree_store = gtk.TreeStore(str, int, object)
self.pages_tree_view = BrowserTreeView(self.pages_tree_store)
self.pages_column = gtk.TreeViewColumn("Pages")
self.score_column = gtk.TreeViewColumn("Score")
self.renderer_text = gtk.CellRendererText()
self.common_tags_tree_view = BrowserTreeView(self.common_tag_list_store)
self.common_tags_tree_view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_HORIZONTAL)
self.common_tags_tree_selection = self.common_tags_tree_view.get_selection()
self.common_tags_tree_selection.set_mode(gtk.SELECTION_SINGLE)
self.common_tags_column = gtk.TreeViewColumn("Common Tags")
self.renderer_pixbuf = gtk.CellRendererPixbuf()
self.renderer_text_common = gtk.CellRendererText()
self.renderer_text_n_page = gtk.CellRendererText()
self.checkbutton_inter_state = True
self.checkbutton_union_state = False
self.intersect_pages_list = []
self.query = None
self.selection = SearchSelection(self.ui.notebook)
self.filter_hit_store = gtk.ListStore(str, int, object)
self.cancelled = False
self.pages_tree_selection = self.pages_tree_view.get_selection()
self.tag_matrix_selected = {
1: [0, 1, 0, 1, 0, False, False, 0, 0, 0],
2: [1, 2, 0, 1, 0, False, False, 0, 0, 0]
}
'''
tag_matrix_selected primarily needed to fill the table with
entries. I don't want to have the table re-ordered if a tag
is removed from selected tags.
table:
0/1: x1 = left_attach, x2 = right_attach
2/3: y1 = top_attach, y2 = bottom_attach
4: entry = store entry object to find when deleted or to delete all
5: status_occupied: 0 = empty, 1: cell occupied ( in table)
6: status_enabled: 0 = disabled (greyed out), 1: enabled
7: entrystyle = store entry style object
8: pages: all pages for a tag
9: the tag
'''
self.LEFT_ATT_X1 = 0
self.RIGHT_ATT_X2 = 1
self.TOP_ATT_Y1 = 2
self.BOT_ATT_Y2 = 3
self.ENTRY_OBJ = 4
self.CEL_OCCUPIED = 5
self.CEL_STATUS = 6
self.ENTRY_STYLE = 7
self.ALL_PAGES = 8
self.TAG = 9
def show(self):
self.pages_tree_view.append_column(self.pages_column)
self.pages_column.pack_start(self.renderer_text, False)
self.pages_column.add_attribute(self.renderer_text, "text", 0)
self.pages_column.set_sort_column_id(0)
self.pages_column.set_resizable(True)
self.pages_column.set_max_width(SIZE_X-60)
self.pages_tree_view.append_column(self.score_column)
self.score_column.pack_start(self.renderer_text, False)
self.score_column.add_attribute(self.renderer_text, "text", 1)
self.score_column.set_sort_column_id(1)
self.pages_tree_store.set_sort_column_id(1, gtk.SORT_DESCENDING)
self.common_tags_tree_view.append_column(self.common_tags_column)
self.common_tags_column.pack_start(self.renderer_pixbuf, False)
self.common_tags_column.pack_start(self.renderer_text_n_page, True)
self.common_tags_column.pack_start(self.renderer_text_common, True)
self.common_tags_column.set_attributes(self.renderer_pixbuf, stock_id = self.PIXBUF_COL)
self.common_tags_column.set_attributes(self.renderer_text_n_page, text = self.N_PAGE)
self.common_tags_column.set_attributes(self.renderer_text_common, text = self.TAG_COL)
self.common_tags_column.set_sort_column_id(self.TAG_COL)
self.common_tags_column.set_min_width(50)
self.set_entry_completion(self.all_tags, 'all')
self.reset_common_tags_tree()
self.completion.set_inline_completion(True)
self.completion.set_inline_selection(True)
self.completion.set_text_column(0)
self.completion.set_popup_single_match(False)
self.completion.connect('match-selected', self.match_selected)
self.pages_tree_view.connect("row_activated", self.page_selected)
self.common_tags_tree_view.connect("row_activated", self.common_tag_selected)
self.auto_fill_tag()
########### REVIEW
def auto_fill_tag(self):
# get the text buffer from the note
self.buffer = self.window.pageview.view.get_buffer()
word = self.get_word_at_cursor_pos(self.buffer)
tags = self.all_tags
if word:
word = word.strip('@')
if word in self.tags_and_pages_store:
self.show_tag(word)
def page_selected(self, tree_view, path, column):
page = self.pages_tree_store[path][self.PAGE_COL]
try:
page_name_index = self.intersect_pages_list[1].index(page)
page_path = Path(self.intersect_pages_list[0][page_name_index])
if page_path:
self.ui.open_page(page_path)
search_text = self.filter_entry.get_text()
self.window.pageview.find(search_text)
except:
logger.info('Exception during page opening')
pass
def common_tag_selected(self, tree_view, path, column):
#start_time = self.time_code(True)
tag = self.common_tag_list_store[path][self.TAG_COL]
print "tag selected: ", tag
self.show_tag(tag)
#func_and_call = "method: ** " + str(sys._getframe().f_code.co_name) + "** <called by> ** " + str(sys._getframe().f_back.f_code.co_name) + "**"
#self.time_code(False, func_and_call, start_time)
def checkbutton_inter_toggled(self, widget):
if self.checkbutton_inter_state:
self.checkbutton_inter_state = False
else:
self.checkbutton_inter_state = True
# TODO: handle error when no tags are shown
self.show_pages()
def checkbutton_union_toggled(self, widget):
if self.checkbutton_union_state:
self.checkbutton_union_state = False
else:
self.checkbutton_union_state = True
# TODO: handle error when no tags are shown
self.show_pages()
def button_remove_all_clicked(self, widget):
self.remove_all_selected_tag_boxes()
self.init_data()
def init_data(self):
self.main_entry.set_text('')
self.filter_entry.set_text('')
self.clear_pages()
self.reset_common_tags_tree()
self.button_remove_all.hide_all()
self.button_add_page.hide_all()
self.set_entry_completion(self.all_tags, 'all') # to fill the completion list with all tags again
def button_add_page_clicked(self, widget):
# get the windows, buffer and cursor position
page_links = []
main_window = self.window.pageview.view
zim_buffer = self.window.pageview.view.get_buffer()
cursor = zim_buffer.get_iter_at_mark(zim_buffer.get_insert())
# add text to the page
current_style = zim_buffer.get_textstyle()
zim_buffer.set_textstyle('h5')
zim_buffer.insert(cursor, "Pages with following tag(s) in common\n")
zim_buffer.set_textstyle(current_style)
# get the selected tags and add them to the page
tags = self.get_active_tags()
atsign=""
if self.plugin.preferences['add_pages_atsign']:
atsign = "@"
separator = self.plugin.preferences['add_pages_separator']
tags_n = len(tags)
for tag in sorted(tags):
tags_n -= 1
if tags_n == 0: # Don't add a separator for the last tag (or if only one tag)
separator = ""
zim_buffer.insert(cursor, atsign + tag + " " + separator + " ")
zim_buffer.insert(cursor, "\n")
# get the pages to be linked and put them into the page
intersect_pages, union_pages = self.get_common_pages_of_active_tags()
for page in sorted(intersect_pages):
page_links.append(page.name)
self.window.pageview.insert_links(page_links)
def remove_all_selected_tag_boxes(self):
for key in self.tag_matrix_selected.keys():
tag_box = self.tag_matrix_selected[key][self.ENTRY_OBJ]
if tag_box:
self.table_selected_tags.remove(tag_box)
self.tag_matrix_selected[key][self.ENTRY_OBJ:] = [0, False, False, 0, 0, 0]
def reset_common_tags_tree(self):
self.common_tag_list_store.clear()
self.all_tags = set()
for tag in self.tags_and_pages_store:
#for tag in self.ui.notebook.tags.list_all_tags():
self.all_tags.add(tag)
#n_tagged_pages = self.ui.notebook.tags.n_list_pages(tag)
n_tagged_pages = len(self.tags_and_pages_store[tag])
self.common_tag_list_store.append([gtk.STOCK_CONVERT, "[" + str(n_tagged_pages) + "p]" , tag ])
self.set_common_tag_column_title()
def main_entry_return(self, entry):
tag_exists = False
tag_entered = unicode(self.main_entry.get_text())
#print "tag entered", tag_entered
#if tag_entered == '':
# return
if tag_entered in self.tags_and_pages_store:
#print "tag found"
self.show_tag(tag_entered)
def search_filter(self, widget):
if not self.get_tag_box_occupied_status(1, self.tag_matrix_selected):
self.main_entry.set_text('Select tag first!')
return
self.switch_progress_bar("on")
self.clear_pages()
self.filter_hit_store.clear()
#filter = unicode(self.filter_entry.get_text().strip())
search_txt, self.n_pages = self.get_filter()
query = Query(search_txt)
#query = Query(self.filter_entry.get_text())
self.selection.search(query, callback=self.search_callback)
# TODO: handle error when no tags are shown
self.show_pages()
self.switch_progress_bar("off")
self.cancelled = False
def get_filter(self):
self.filt = []
n_pages = 1
for selected_tag in self.tag_matrix_selected:
if self.tag_matrix_selected[selected_tag][self.CEL_OCCUPIED]:
tag = "@" + self.tag_matrix_selected[selected_tag][self.TAG]
#n_pages = n_pages + len(self.tags_and_pages_store[self.tag_matrix_selected[selected_tag][self.TAG]])
self.filt.append(tag)
search_txt = self.filter_entry.get_text()
if search_txt:
self.filt.append(search_txt)
if self.checkbutton_inter_state:
afilter = " ".join(x for x in self.filt if "@" in x)
print ">>>>>>>>> self.filter: ", self.filt
print ">>>>>>>>> afilter: ", afilter
if self.checkbutton_union_state:
afilter = " ".join(x for x in self.filt if "@" in x)
print ">>>>>>>>> self.filter: ", self.filt
print ">>>>>>>>> afilter: ", afilter
#merged_filter = afilter + " ".join(x for x in self.filt if "@" not in x)
merged_filter = afilter + " " + search_txt
print ">>>>>>>>> filter merged: ", merged_filter
return merged_filter, n_pages
def switch_progress_bar(self, switch):
if switch == "on":
self.progress_bar.set_fraction(0)
self.progress_bar.show()
self.button_cancel_search.show_all()
self.button_search.hide_all()
if switch == "off":
self.progress_bar.hide_all()
self.button_cancel_search.hide_all()
self.button_search.show_all()
self.progress_bar.set_fraction(0)
def set_progress_bar(self):
value = self.progress_bar.get_fraction() + self.progress_bar_fraction
percent = value * 100
percent = str(int(percent))
if not value > 1:
self.progress_bar.set_fraction(value)
self.progress_bar.set_text(percent + "%")
def search_cancel(self, widget):
self.cancelled = True
def search_callback(self, results, path):
if results is not None:
score = results.scores.get(path)
if score: # so, only if there is a path.name
self.filter_hit_store.append((path.name, score, path))
self.set_progress_bar()
while gtk.events_pending():
gtk.main_iteration(block=False)
return not self.cancelled
def match_selected(self, completion, model, iter):
#print "tag selected"
tag_selected = unicode(model[iter][0])
if tag_selected:
self.show_tag(tag_selected)
def get_key_event(self, widget, event):
if event.keyval == gtk.gdk.keyval_from_name('Escape'):
widget.destroy()
def show_tag(self, tag):
#start_time = self.time_code(True)
# Delete text doesn't work when tag is selected from popup :(
#self.main_entry.set_text('')
self.clear_entry(None, None, None)
self.place_tag(tag, tag_toggle=False)
self.button_remove_all.show_all()
self.button_add_page.show_all()
self.update_common_tags()
#func_and_call = "method: **" + str(sys._getframe().f_code.co_name) + "** <called by> **" + str(sys._getframe().f_back.f_code.co_name) + "**"
#self.time_code(False, func_and_call, start_time)
def place_tag(self, tag, tag_toggle):
#start_time = self.time_code(True)
tag_to_place = unicode(tag)
free_cell_key = self.get_free_cell_key(self.tag_matrix_selected)
if not free_cell_key: # no free cell available
# start a new pair (equals new line)
# I need to add +1 to y1 and y2 for new index
# If I need to start a new line then x1=0, x2=1 for column 1 and
# x1=1, x2=2 for column 2
last_key = len(self.tag_matrix_selected)
#n_all_tags = self.n_all_tags
# if show all is pressed more than once, but there should be a better way to handle this
if (last_key == self.n_all_tags) & tag_toggle:
return
# get y1, y2 from last self.tag_matrix_selected element
y1 = self.tag_matrix_selected[last_key][self.TOP_ATT_Y1] + 1 # add a new line to self.table_selected_tags
y2 = self.tag_matrix_selected[last_key][self.BOT_ATT_Y2] + 1 # just for readability. same as y2 = y1+1
self.tag_matrix_selected[last_key + 1] = [0, 1, y1, y2, 0, False, False, 0, 0, 0] # new key
self.tag_matrix_selected[last_key + 2] = [1, 2, y1, y2, 0, False, False, 0, 0, 0] # new key
#free_cell_key = self.get_free_cell_key(self.tag_matrix_selected) # now as we have 2 new free cells, get the 1. free cell key
free_cell_key = last_key + 1
x1, x2, y1, y2 = self.get_cell_pos(free_cell_key, self.tag_matrix_selected)
current_key = free_cell_key
tag_box = InputEntry()
tag_box_style = tag_box.get_style() # to get the current style of the entry
tag_box.set_property("editable", False) # Text in entry shall not be editable
tag_box.set_icon_from_stock(0, "gtk-convert")
tag_box.set_icon_tooltip_text(0, "Remove tag from selected tags")
n_tagged_pages = len(self.tags_and_pages_store[tag_to_place])
tag_box.set_text("[" + str(n_tagged_pages) + "p] " + tag_to_place) # put the tag into the entry field
tag_box.set_tooltip_text(str(n_tagged_pages) + " pages with @" + tag_to_place)
tag_box.set_size_request(TAG_ENTRY_WIDTH_PIX, ENTRY_HIGHT_PIX)
tag_box.connect("icon-press", self.handle_tag_box_icon, tag_to_place, current_key)
#tag_shadow_box = gtk.EventBox()
#tag_shadow_box.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color("grey"))
#tag_box_size = tag_box.get_size_request()
#tag_shadow_box.set_size_request(tag_box_size[0],tag_box_size[1]+5)
#tag_shadow_box.add(tag_box)
##self.table_selected_tags.attach(tag_shadow_box, x1, x2, y1, y2, xoptions=gtk.EXPAND, yoptions=gtk.EXPAND)
self.table_selected_tags.attach(tag_box, x1, x2, y1, y2, xoptions=gtk.EXPAND, yoptions=gtk.EXPAND)
self.tag_matrix_selected[current_key][self.ENTRY_OBJ] = tag_box # put the entry into the dict to find it
self.tag_matrix_selected[current_key][self.CEL_OCCUPIED] = True # table cell is occupied
self.tag_matrix_selected[current_key][self.CEL_STATUS] = tag_toggle # toggle entry style
self.tag_matrix_selected[current_key][self.ENTRY_STYLE] = tag_box_style # style for each entry (if needed)
self.tag_matrix_selected[current_key][self.ALL_PAGES] = self.get_pages_for_tag(tag_to_place) # store all pages for this tag
self.tag_matrix_selected[current_key][self.TAG] = tag_to_place
# TODO: update pages in taq_self.tag_matrix_selected on signal, otherwise, data gets outdated
self.toggle_tag_box_status(tag_box, self.tag_matrix_selected)
self.search_scrolled_win_selected_tags.show_all()
#func_and_call = "method: **" + str(sys._getframe().f_code.co_name) + "** <called by> **" + str(sys._getframe().f_back.f_code.co_name) + "**"
#self.time_code(False, func_and_call, start_time)
def remove_tag(self, tag, tag_box_key, tag_box):
self.main_entry.set_text('') # just in case
# TODO: element should be deleted from dict. Now after re-design
self.tag_matrix_selected[tag_box_key][self.CEL_OCCUPIED] = False # set flag to free within tag_matrix_selected
self.table_selected_tags.remove(tag_box)
#print "no of selected tabs: ", len(self.table_selected_tags)
if len(self.table_selected_tags) == 0:
self.init_data()
else:
self.update_common_tags()
def update_common_tags(self):
#start_time = self.time_code(True)
#self.remove_all_common_tag_boxes()
#print "common tag list store len", len(self.common_tag_list_store)
if len(self.common_tag_list_store) == 1:
#print "last common tag removed"
self.common_tag_list_store.clear()
self.set_common_tag_column_title()
return
self.common_tag_list_store.clear()
common_tags = self.get_common_tags_for_active_tags() # now get new common tags of selected tags
if common_tags:
for common_tag in sorted(common_tags, key=lambda tag: tag):
#n_tagged_pages = self.ui.notebook.tags.n_list_pages(common_tag)
n_tagged_pages = len(self.tags_and_pages_store[common_tag])
self.common_tag_list_store.append([gtk.STOCK_CONVERT, "[" + str(n_tagged_pages) + "p]", common_tag])
else:
self.reset_common_tags_tree()
self.set_common_tag_column_title()
self.show_pages()
self.set_entry_completion(common_tags, 'common')
#func_and_call = "method: **" + str(sys._getframe().f_code.co_name) + "** <called by> **" + str(sys._getframe().f_back.f_code.co_name) + "**"
#self.time_code(False, func_and_call, start_time)
return common_tags # todo: rework. What if no common_tags?
def set_common_tag_column_title(self):
common_tag_n = len(self.common_tag_list_store)
common_tags_column_title = "Common tags (" + str(common_tag_n) + ")"
self.common_tags_column.set_title(common_tags_column_title)
def set_entry_completion(self, tags_list, scope):
tags_list_sorted = sorted(tags_list, key=lambda tag: tag)
if scope == 'all':
self.tag_list_store.clear()
for tag in tags_list_sorted:
self.tag_list_store.append([tag])
self.completion.set_model(self.tag_list_store)
return
if scope == 'common':
common_tag_list_store = gtk.ListStore(str)
for tag in tags_list_sorted:
common_tag_list_store.append([tag])
self.completion.set_model(common_tag_list_store)
return
def handle_tag_box_icon(self, tag_box, icon_pos, event, tag, key):
self.remove_tag(tag, key, tag_box)
def show_pages(self):
#start_time = #self.time_code(True)
intersect_pages, union_pages = self.get_common_pages_of_active_tags()
filter_len = len(self.filter_hit_store)
if self.filter_entry.get_text() and filter_len:
intersect_pages_node_text = "All common pages of selected tags [" + str(len(intersect_pages)) + "p] " +\
"with filter [" + str(len(self.filter_hit_store)) + "p]"
union_pages_node_text = "All unique pages of selected tags [" + str(len(intersect_pages)) + "p] " +\
"with filter [" + str(len(self.filter_hit_store)) + "p]"
else:
intersect_pages_node_text = "All common pages of selected tags [" + str(len(intersect_pages)) + "p]"
union_pages_node_text = "All unique pages of selected tags [" + str(len(union_pages)) + "p]"
self.get_pages(intersect_pages, intersect_pages_node_text, union_pages, union_pages_node_text)
#func_and_call = "method: " + str(sys._getframe().f_code.co_name) + "<called by:>" + str(sys._getframe().f_back.f_code.co_name)
#self.time_code(False, #func_and_call, start_time)
def clear_pages(self):
self.pages_tree_store.clear()
def get_pages(self, intersect_pages, intersect_pages_node_text, union_pages, union_pages_node_text):
#start_time = #self.time_code(True)
self.clear_pages()
self.get_page_nodes(intersect_pages, union_pages)
if self.checkbutton_inter_state:
# first place the node text
parent_intersection = self.pages_tree_store.append(None, (intersect_pages_node_text, 0, None))
for page in intersect_pages:
if self.filter_entry.get_text() and self.filter_hit_store:
for hit in self.filter_hit_store:
if hit[0] == page.name:
self.pages_tree_store.append(parent_intersection, (page.basename, (hit[1] + 1), page))
else:
self.pages_tree_store.append(parent_intersection, (page.basename, 1, page))
self.pages_tree_view.expand_all()
if self.checkbutton_union_state:
# first place the node text
parent_union = self.pages_tree_store.append(None, (union_pages_node_text, 0, None))
for page in union_pages:
if self.filter_entry.get_text() and self.filter_hit_store:
for hit in self.filter_hit_store:
if hit[0] == page.name:
#print ("Score: " + str(hit[1]))
#print page.name
self.pages_tree_store.append(parent_union, (page.basename, (hit[1] + 1), page))
else:
self.pages_tree_store.append(parent_union, (page.basename, 1, page))
self.pages_tree_view.expand_all()
#func_and_call = "method: " + str(sys._getframe().f_code.co_name) + "<called by:>" + str(sys._getframe().f_back.f_code.co_name)
#self.time_code(False, #func_and_call, start_time)
def get_page_nodes(self, intersect_pages, union_pages):
page_roots_list = []
intersect_page_namespace_set = set()
for l in range(0, 6): self.intersect_pages_list.append([])
if self.checkbutton_inter_state:
self.intersect_pages_list[0] = [p.name for p in intersect_pages]
self.intersect_pages_list[1] = [p.basename for p in intersect_pages]
def clear_entry(self, entry, iconpos, event):
self.main_entry.set_text('')
def clear_filter_entry(self, entry, iconpos, event):
if entry.get_text():
entry.set_text('')
self.clear_pages()
self.filter_hit_store.clear()
self.show_pages()
def toggle_tag_box_status(self, tag_box, matrix):
tag_box_key = self.get_tag_box_key(tag_box, matrix)
tag_box_status = self.get_tag_box_enabled_status(tag_box_key, matrix)
tag_box_style = self.get_tag_box_style(tag_box_key, matrix)
if tag_box_status:
# disable
tag_box.modify_text(gtk.STATE_NORMAL, gtk.gdk.color_parse("grey"))
matrix[tag_box_key][self.CEL_STATUS] = False # flag to disabled
else:
# enable
tag_box.modify_text(gtk.STATE_NORMAL, tag_box_style.text[gtk.STATE_NORMAL])
matrix[tag_box_key][self.CEL_STATUS] = True # flag to enabled
return
def get_common_tags_for_active_tags(self):
#start_time = self.time_code(True)
common_tags_list = []
common_tags_set = set()
active_tags = self.get_active_tags()
for tag in active_tags:
common_tags_list.append(self.get_common_tags_for_tag(tag))
if common_tags_list:
common_tags_set = set.intersection(*common_tags_list)
#func_and_call = "method: **" + str(sys._getframe().f_code.co_name) + "** <called by> **" + str(sys._getframe().f_back.f_code.co_name) + "**"
#self.time_code(False, func_and_call, start_time)
return common_tags_set
def do_store_tags_and_pages(self):
for tag in self.ui.notebook.tags.list_all_tags():
#for tag in self.all_tags:
all_pages = set(self.ui.notebook.tags.list_pages(tag))
self.tags_and_pages_store[tag.name] = all_pages
self.all_tags.add(tag.name)
def get_common_tags_for_tag(self, tag):
#start_time = self.time_code(True)
common_tags_set = set()
# now run through all tags except the selected tag
for each_tag in self.all_tags:
if each_tag == tag:
continue
# find intersection (overlapping) of pages between the selected tag and all other tags
intersection_pages_set = (self.tags_and_pages_store[tag] & self.tags_and_pages_store[each_tag])
# if both tags, the selected and the current from the loop have at least one page in common then:
if intersection_pages_set:
# as the current tag has at least one page in common with the selected tag, add this tag into the list to show
common_tags_set.add(each_tag)
#func_and_call = "method: **" + str(sys._getframe().f_code.co_name) + "** <called by> **" + str(sys._getframe().f_back.f_code.co_name) + "**"
#self.time_code(False, func_and_call, start_time)
return common_tags_set
def get_common_pages_of_active_tags(self):
#start_time = #self.time_code(True)
active_tags = self.get_active_tags()
tagged_pages_list_of_active_tags = []
is_tagged_pages_of_active_tags = set()
u_tagged_pages_of_active_tags = set()
for active_tag in active_tags:
tagged_pages_set_of_active_tag = self.tags_and_pages_store[active_tag]
# Don't add empty entries as this would lead to Null on intersection
if tagged_pages_set_of_active_tag:
tagged_pages_list_of_active_tags.append(tagged_pages_set_of_active_tag)
if tagged_pages_list_of_active_tags:
is_tagged_pages_of_active_tags = set.intersection(*tagged_pages_list_of_active_tags)
u_tagged_pages_of_active_tags = set.union(*tagged_pages_list_of_active_tags)
#func_and_call = "method: " + str(sys._getframe().f_code.co_name) + "<called by:>" + str(sys._getframe().f_back.f_code.co_name)
#self.time_code(False, #func_and_call, start_time)
return is_tagged_pages_of_active_tags, u_tagged_pages_of_active_tags
def get_tag_box_style(self, key, matrix):
return matrix[key][self.ENTRY_STYLE]
def get_tag_box_enabled_status(self, key, matrix):
return matrix[key][self.CEL_STATUS]
def get_tag_box_occupied_status(self, key, matrix):
return matrix[key][self.CEL_OCCUPIED]
def get_tag_box(self, key, matrix):
return matrix[key][self.ENTRY_OBJ]
def get_tag_box_key(self, tag_box, matrix):
for key in matrix.keys():
if matrix[key][self.ENTRY_OBJ] == tag_box: # look for the cell pos of the deleted entry
return key
return False
def get_cell_pos(self, key, matrix):
x1 = matrix[key][self.LEFT_ATT_X1]
x2 = matrix[key][self.RIGHT_ATT_X2]
y1 = matrix[key][self.TOP_ATT_Y1]
y2 = matrix[key][self.BOT_ATT_Y2]
return x1, x2, y1, y2
def mark_cell_free(self, tag_box, matrix):
key = self.get_tag_box_key(tag_box, matrix)
if key:
matrix[key][self.CEL_OCCUPIED] = False # mark status of cell pos as 0 = empty
def get_free_cell_key(self, matrix):
for key in matrix.keys():
if not matrix[key][self.CEL_OCCUPIED]:
return key
return False
def get_active_tags(self):
#start_time = self.time_code(True)
active_tags = []
for key in self.tag_matrix_selected.keys():
# the tag can be de-selected again
if self.tag_matrix_selected[key][self.CEL_OCCUPIED] and self.tag_matrix_selected[key][self.CEL_STATUS]:
active_tags.append(self.tag_matrix_selected[key][self.TAG])
#func_and_call = "method: **" + str(sys._getframe().f_code.co_name) + "** <called by> **" + str(sys._getframe().f_back.f_code.co_name) + "**"
#self.time_code(False, func_and_call, start_time)
return active_tags
def get_key_for_selected_tag(self, tag):
for key in self.tag_matrix_selected.keys():
if self.tag_matrix_selected[key][self.TAG] == tag:
return key
return False
def get_pages_for_tag(self, tag):
return self.ui.notebook.tags.list_pages(tag)
def get_word_at_cursor_pos(self, buffer):
try:
# preserve cursor position
cursor_orig = buffer.get_iter_at_mark(buffer.get_insert())
cursor = buffer.get_iter_at_mark(buffer.get_insert())
# no need to look up if cursor is at empty line
if cursor.ends_line() and cursor.starts_line():
return
cursor.forward_word_end()
buffer.place_cursor(cursor)
word_end = buffer.get_iter_at_mark(buffer.get_insert())
cursor.backward_word_start()
buffer.place_cursor(cursor)
word_start = buffer.get_iter_at_mark(buffer.get_insert())
# just to not have changed anything, move cursor to start position
buffer.place_cursor(cursor_orig)
word = word_end.get_text(word_start)
except ValueError:
return False
return word
def time_code(self, start, func_str=None, start_time=None):
'''
call with stop = False returns start time.
call with stop = True and start=returned time)
'''
if start:
return time.time()
else:
end_time = time.time()
total = str(end_time - start_time)
main_window = self.window.pageview.view
zim_buffer = self.window.pageview.view.get_buffer()
cursor = zim_buffer.get_iter_at_mark(zim_buffer.get_insert())
# add text to the page
zim_buffer.insert(cursor, '%s> time elapsed: %s \n' % (str(func_str), total))
#print "start: ", start_time
#print "end: ", end_time
#print "%s time elapsed: %s" % (func_str, end_time - start_time)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment