Skip to content

Instantly share code, notes, and snippets.

@muguu
Last active September 28, 2021 18:39
Show Gist options
  • Save muguu/675d229ef59ec26c8e49780c688c3ae0 to your computer and use it in GitHub Desktop.
Save muguu/675d229ef59ec26c8e49780c688c3ae0 to your computer and use it in GitHub Desktop.
Plugin for ZimWiki >0.61 and < V0.66
# -*- coding: utf-8 -*-
# Plugin for ZimWiki: Search tagged pages.
# Copyright 2015 Murat Güven <[email protected]>
# Copyright ZimWiki: 2009-2013 Jaap Karssenberg <[email protected]>
# V1.15
# Change Log
# 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 gtk.gdk
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
ALTSHIFTQ = '<alt><shift>q'
ENTRY_LABEL_WIDTH_PIX = 50
BUTTON_HIGHT_PIX = 25
BUTTON_WIDTH_PIX = 80
H_SPACING_PIX = 3
TABLE_PADDING_LEFT_PIX = 10
TABLE_PADDING_TOP_PIX = 0
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
class SearchTaggedPagesPlugin(PluginClass):
plugin_info = {
'name': _('Search 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.15
'''), # T: plugin description
'author': 'Murat Güven',
'help': 'Plugins:Search 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 SearchTaggedPagesMainWindowExtension(WindowExtension, ConnectorMixin):
uimanager_xml = '''
<ui>
<menubar name='menubar'>
<menu action='search_menu'>
<placeholder name='plugin_items'>
<menuitem action='search_tagged_pages'/>
</placeholder>
</menu>
</menubar>
<toolbar name='toolbar'>
<placeholder name='tools'>
<toolitem action='search_tagged_pages'/>
</placeholder>
</toolbar>
</ui>
'''
accel_key = ALTSHIFTQ
@action(_('Search tagged pages'), stock='gtk-find', accelerator=accel_key, readonly=True) # T: menu item
def search_tagged_pages(self):
self.ui = self.window.ui
self.index = self.window.ui.notebook.index
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.table_common_tags = gtk.Table()
self.table_common_tags.set_col_spacings(TABLE_COL_SPACINGS)
self.table_common_tags.set_row_spacings(TABLE_ROW_SPACINGS)
self.search_win = gtk.Window()
self.search_win.set_title("Search tagged pages")
self.v_paned_main = gtk.VPaned()
self.v_paned_top = gtk.VPaned()
self.v_paned_bottom = gtk.VPaned()
# homogeneous=False, spacing=0
self.hbox1_buttons = gtk.HBox(False, H_SPACING_PIX)
self.hbox2_buttons = gtk.HBox(False, H_SPACING_PIX)
self.hbox3_buttons = gtk.HBox(False, H_SPACING_PIX)
self.vbox_buttons = gtk.VBox(False, H_SPACING_PIX)
self.vbox_selected_tags = gtk.VBox(False, H_SPACING_PIX)
self.vbox_common_tags = gtk.VBox(False, H_SPACING_PIX)
# xalign=0.0, yalign=0.0, xscale=0.0, yscale=0.0
self.halign1_buttons = gtk.Alignment(0, 0, 0, 0)
self.halign2_buttons = gtk.Alignment(0, 0, 0, 0)
self.halign3_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.halign_label_common_tags = gtk.Alignment(0, 0, 0, 0)
self.halign_table_common_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_common_tags = gtk.ScrolledWindow()
self.search_scrolled_win_pages = gtk.ScrolledWindow()
self.main_entry = gtk.Entry()
self.filter_entry = gtk.Entry()
self.completion = gtk.EntryCompletion()
self.tag_list_store = gtk.ListStore(str)
self.pages_tree_store = gtk.TreeStore(str, int)
self.pages_tree_view = gtk.TreeView(self.pages_tree_store)
self.pages_column = gtk.TreeViewColumn("Pages")
self.score_column = gtk.TreeViewColumn("Score")
self.cell_text = gtk.CellRendererText()
self.progress_bar = gtk.ProgressBar()
self.progress_bar_fraction = self.index.n_list_all_pages() / 100000.0
self.checkbutton_inter_state = 1
self.checkbutton_union_state = 0
self.intersect_pages_list = []
self.query = Query
self.selection = SearchSelection(self.ui.notebook)
self.filter_hit_store = gtk.ListStore(str, int)
self.cancelled = False
self.tree_selection = self.pages_tree_view.get_selection()
self.tag_matrix_common = {
1: [0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
2: [1, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
}
# tag_matrix_common primarily needed to fill the window with
# dummy entries, otherwise 2 elements (equals one line) were
# enough
# 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: show tag in completion: 0: no, 1: yes
# 9: pages: all pages for a tag
# 10: the tag
# 11: # of tags with common pages
self.tag_matrix_selected = {
1: [0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
2: [1, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
}
# tag_matrix_common primarily needed to fill the window with
# dummy entries, otherwise 2 elements (equals one line) were
# enough
# 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: show tag in completion: 0: no, 1: yes
# 9: pages: all pages for a tag
# 10: the tag
# 11: # of tags with common pages
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_default_size(SIZE_X, 800)
self.search_win.set_geometry_hints(min_width=SIZE_X)
self.v_paned_main.set_position(250)
self.search_win.add(self.v_paned_main)
self.v_paned_top.set_position(100)
self.v_paned_bottom.set_position(180)
self.v_paned_top.add1(self.vbox_buttons)
# self.v_paned_top.add1(self.search_scrolled_win_buttons)
self.v_paned_top.add2(self.search_scrolled_win_selected_tags)
self.v_paned_main.add1(self.v_paned_top)
self.table_selected_tags.set_homogeneous(False)
# Populate Buttons Window_________________________________________________________________________________
# padding_top, padding_bottom, padding_left, padding_right
self.halign1_buttons.set_padding(PADDING_TOP_PIX, 0, 0, 0)
self.halign2_buttons.set_padding(0, 0, 0, 0)
self.halign3_buttons.set_padding(PADDING_TOP_PIX, 0, CHECK_BOX_PADDING_LEFT_PIX, 0)
self.halign1_buttons.add(self.hbox1_buttons)
self.halign2_buttons.add(self.hbox2_buttons)
self.halign3_buttons.add(self.hbox3_buttons)
self.vbox_buttons.pack_start(self.halign1_buttons, False, False, 0)
self.vbox_buttons.pack_start(self.halign2_buttons, False, False, 0)
self.vbox_buttons.pack_start(self.halign3_buttons, False, False, 0)
# Add Main Entry field ___________________________________________________________________________________
main_entry_label = gtk.Label("Tag:")
main_entry_label.set_size_request(ENTRY_LABEL_WIDTH_PIX, ENTRY_HIGHT_PIX)
self.main_entry.set_tooltip_text(self.tag_help_text)
self.main_entry.set_icon_from_stock(1, "gtk-clear")
self.main_entry.set_size_request(ENTRY_WIDTH_PIX, ENTRY_HIGHT_PIX)
self.hbox1_buttons.add(main_entry_label)
self.hbox1_buttons.add(self.main_entry)
# ________________________________________________________________________________________________________
# Add Show All Button_____________________________________________________________________________________
self.button_show_all = gtk.Button("Show all")
self.button_show_all.set_size_request(BUTTON_WIDTH_PIX, BUTTON_HIGHT_PIX)
self.hbox1_buttons.add(self.button_show_all)
# _________________________________________________________________________________________________________
# Add Remove all Button____________________________________________________________________________________
self.button_remove_all = gtk.Button("Remove all")
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.hbox2_buttons.add(filter_entry_label)
self.hbox2_buttons.add(self.filter_entry)
# _________________________________________________________________________________________________________
# Add search / progress bar ______________________________________________________________________________
self.button_search = gtk.Button("Search")
self.button_search.set_size_request(BUTTON_WIDTH_PIX, BUTTON_HIGHT_PIX)
self.button_cancel_search = gtk.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.hbox2_buttons.add(self.button_search)
self.hbox2_buttons.add(self.button_cancel_search)
self.hbox2_buttons.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.hbox3_buttons.add(self.checkbutton_inter)
self.hbox3_buttons.add(self.checkbutton_union)
# _________________________________________________________________________________________________________
# Add Page add button__________________________________________________________________________________________
self.button_add_page = gtk.Button("Add pages as link")
self.button_add_page.set_size_request(BUTTON_WIDTH_PIX + 50, BUTTON_HIGHT_PIX)
self.hbox3_buttons.add(self.button_add_page)
# _________________________________________________________________________________________________________
self.search_scrolled_win_buttons.add_with_viewport(self.vbox_buttons)
# Add selected tags text___________________________________________________________________________________
self.separator = gtk.HSeparator()
selected_tags_label = gtk.Label("Selected tags: ")
self.separator2 = gtk.HSeparator()
common_tags_label = gtk.Label("Common tags of selected tags: ")
self.halign_label_selected_tags.add(selected_tags_label)
self.halign_label_selected_tags.set_padding(5, 0, TABLE_PADDING_LEFT_PIX, 0)
self.halign_label_common_tags.add(common_tags_label)
self.halign_label_common_tags.set_padding(5, 0, TABLE_PADDING_LEFT_PIX, 0)
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.halign_table_common_tags.add(self.table_common_tags)
self.halign_table_common_tags.set_padding(TABLE_PADDING_TOP_PIX, 0, TABLE_PADDING_LEFT_PIX, 0)
self.vbox_selected_tags.pack_start(self.halign_label_selected_tags, False, False, 0)
self.vbox_selected_tags.pack_start(self.separator, False, False, 0)
self.vbox_selected_tags.pack_start(self.halign_table_selected_tags, False, False, 0)
self.vbox_common_tags.pack_start(self.halign_label_common_tags, False, False, 0)
self.vbox_common_tags.pack_start(self.separator2, False, False, 0)
self.vbox_common_tags.pack_start(self.halign_table_common_tags, False, False, 0)
self.search_scrolled_win_selected_tags.add_with_viewport(self.vbox_selected_tags)
self.search_scrolled_win_common_tags.add_with_viewport(self.vbox_common_tags)
self.search_scrolled_win_pages.add(self.pages_tree_view)
self.v_paned_main.add2(self.v_paned_bottom)
self.v_paned_bottom.add1(self.search_scrolled_win_common_tags)
self.v_paned_bottom.add2(self.search_scrolled_win_pages)
self.pages_tree_view.append_column(self.pages_column)
self.pages_column.pack_start(self.cell_text, False)
self.pages_column.add_attribute(self.cell_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.cell_text, False)
self.score_column.add_attribute(self.cell_text, "text", 1)
self.score_column.set_sort_column_id(1)
self.pages_tree_store.set_sort_column_id(1, gtk.SORT_DESCENDING)
self.set_entry_completion(self.index.list_all_tags(), 'all')
self.completion.set_inline_completion(True)
self.completion.set_inline_selection(True)
self.main_entry.set_completion(self.completion)
self.completion.set_text_column(0)
self.completion.set_popup_single_match(False)
self.search_win.show_all()
self.progress_bar.hide_all()
self.button_cancel_search.hide_all()
self.main_entry.connect("icon-press", self.clear_entry)
self.filter_entry.connect("icon-press", self.clear_filter_entry)
self.main_entry.connect('activate', self.main_entry_return)
self.filter_entry.connect('activate', self.search_filter)
self.completion.connect('match-selected', self.match_selected)
self.button_remove_all.connect("clicked", self.button_remove_all_clicked)
self.button_show_all.connect("clicked", self.button_show_all_clicked)
self.button_search.connect("clicked", self.search_filter)
self.button_cancel_search.connect("clicked", self.search_cancel)
self.button_add_page.connect("clicked", self.button_add_page_clicked)
self.checkbutton_inter.connect("toggled", self.checkbutton_inter_toggled)
self.checkbutton_union.connect("toggled", self.checkbutton_union_toggled)
self.tree_selection.connect("changed", self.page_selected, self.tree_selection)
self.auto_fill_tag()
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.index.list_all_tags()
if word:
for tag in tags:
if word == tag.name.decode('utf-8'):
word = word.strip('@')
self.main_entry.append_text(word)
self.main_entry_return(None)
break
def page_selected(self, widget, tree_selection):
# This function displays the page in Zim when selected in result list
(model, pathlist) = tree_selection.get_selected_rows()
for path in pathlist:
tree_iter = model.get_iter(path)
page = model.get_value(tree_iter, 0)
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)
def checkbutton_inter_toggled(self, widget):
if self.checkbutton_inter_state:
self.checkbutton_inter_state = 0
else:
self.checkbutton_inter_state = 1
# 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 = 0
else:
self.checkbutton_union_state = 1
# TODO: handle error when no tags are shown
self.show_pages()
def button_remove_all_clicked(self, widget):
self.main_entry.set_text('')
self.filter_entry.set_text('')
self.clear_pages()
self.remove_all_entries()
self.set_entry_completion(self.index.list_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
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, "== Pages with following tag(s) in common ==\n")
# 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_link = "[[" + page.name + "]]" + "\n"
zim_buffer.insert(cursor, page_link)
def remove_all_entries(self):
self.remove_all_entries_selected_tags()
self.remove_all_entries_common_tags()
def remove_all_entries_selected_tags(self):
for key in self.tag_matrix_selected.keys():
entry = self.tag_matrix_selected[key][4]
if entry:
self.table_selected_tags.remove(entry)
self.tag_matrix_selected[key][4:] = [0, 0, 0, 0, 0, 0, 0, 0]
def remove_all_entries_common_tags(self):
for key in self.tag_matrix_common.keys():
entry = self.tag_matrix_common[key][4]
if entry:
self.table_common_tags.remove(entry)
self.tag_matrix_common[key][4:] = [0, 0, 0, 0, 0, 0, 0, 0]
def button_show_all_clicked(self, widget):
self.main_entry.set_text('') # delete the text in the main_entry
if self.get_entry_occupied_status(1, self.tag_matrix_selected):
self.main_entry.set_text('Tag already selected!')
return
# Show all tags as 'disabled'
self.set_entry_completion(self.index.list_all_tags(), 'all')
self.show_all_tag_entries()
def show_all_tag_entries(self):
for tag in self.tag_list_store:
self.show_tag(tag[0], self.search_scrolled_win_common_tags, self.tag_matrix_common, self.table_common_tags,
None, None, tag_toggle=1)
def main_entry_return(self, entry):
tag_exists = False
tag_entered = unicode(self.main_entry.get_text())
if tag_entered == '':
return
for tag in self.tag_list_store:
if tag[0] == tag_entered:
tag_exists = True
break
if not tag_exists:
return
self.place_tag(tag_entered)
def search_filter(self, widget):
if not self.get_entry_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())
query = self.query(filter)
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 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):
while gtk.events_pending():
gtk.main_iteration(block=False)
if results is not None:
if results.scores.get(path): # so, only if there is a path.name
self.filter_hit_store.append((path.name, results.scores.get(path)))
self.set_progress_bar()
return not self.cancelled
def match_selected(self, completion, model, iter):
tag_selected = unicode(model[iter][0])
if tag_selected:
self.place_tag(tag_selected)
def place_tag(self, tag):
# place / toggle tag into top window
self.main_entry.set_text('') # Delete text doesn't work when tag is selected from popup :(
key_common = self.get_key_for_common_tag(tag)
if not key_common: # so it has not been placed yet
self.show_tag(tag, self.search_scrolled_win_selected_tags,
self.tag_matrix_selected, self.table_selected_tags,
None, None, tag_toggle=0) # 0 as it will be toggled to 1
else: # needs to be toggled to selected tags
entry = self.get_entry(key_common, self.tag_matrix_common)
self.toggle_tag(tag, entry, self.tag_matrix_common, self.table_common_tags)
self.update_common_tags()
def show_tag(self, tag, window, matrix, table, left_info, right_info, tag_toggle):
tag_to_place = unicode(tag)
free_cell_key = self.get_free_cell_key(matrix)
if free_cell_key == False: # 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(matrix)
n_all_tags = self.index.n_list_all_tags()
# if show all is pressed more than once, but there should be a better way to handle this
if (last_key == n_all_tags) & (tag_toggle == 1):
return
# get y1, y2 from last matrix element
y1 = matrix[last_key][2] + 1 # add a new line to table
y2 = matrix[last_key][3] + 1 # just for readability. same as y2 = y1+1
matrix[last_key + 1] = [0, 1, y1, y2, 0, 0, 0, 0, 0, 0, 0, 0] # new key
matrix[last_key + 2] = [1, 2, y1, y2, 0, 0, 0, 0, 0, 0, 0, 0] # new key
free_cell_key = self.get_free_cell_key(matrix) # now as we have 2 new free cells, get the 1. free cell key
x1, x2, y1, y2 = self.get_cell_pos(free_cell_key, matrix)
current_key = free_cell_key
entry = gtk.Entry()
entry_style = entry.get_style() # to get the current style of the entry
entry.set_property("editable", False) # Text in entry shall not be editable
entry.set_icon_from_stock(0, "gtk-convert")
entry.set_icon_tooltip_text(0, "Toggle tag from filter")
# entry.set_icon_from_stock(1, "gtk-close") ###### Removed for later implementation
# entry.set_icon_tooltip_text(1, "Remove tag from filter") ###### Removed for later implementation
n_tagged_pages = self.index.n_list_tagged_pages(tag_to_place)
entry.set_text("[" + str(n_tagged_pages) + "p] " + tag_to_place) # put the tag into the entry field
entry.set_tooltip_text(str(n_tagged_pages) + " pages with @" + tag_to_place)
entry.set_size_request(TAG_ENTRY_WIDTH_PIX, ENTRY_HIGHT_PIX)
entry.connect("icon-press", self.handle_entry_icon, tag_to_place, current_key, matrix, table)
table.attach(entry, x1, x2, y1, y2, xoptions=gtk.EXPAND, yoptions=gtk.EXPAND)
matrix[current_key][4] = entry # put the entry into the dict to find it
matrix[current_key][5] = 1 # table cell is occupied
matrix[current_key][6] = tag_toggle # toggle entry style
matrix[current_key][7] = entry_style # style for each entry (if needed)
matrix[current_key][9] = self.get_pages_for_tag(tag_to_place) # store all pages for this tag
matrix[current_key][10] = tag_to_place
# TODO: update pages in taq_matrix on signal, otherwise, data gets outdated
self.toggle_entry_status(entry, matrix)
window.show_all()
def toggle_tag(self, tag, entry, matrix, table):
self.main_entry.set_text('') # just in case
entry_key = self.get_entry_key(entry, matrix)
entry_status = self.get_entry_enabled_status(entry_key, matrix)
if entry_status: # so the tag is enabled and in the top window
# disable
matrix[entry_key][5] = 0 # set flag to free within tag_matrix_selected
# and move from top to bottom window
table.remove(entry)
self.show_tag(tag, self.search_scrolled_win_common_tags, self.tag_matrix_common, self.table_common_tags,
None, None, tag_toggle=1)
else: # the tag is disabled and in the bottom window
# enable
matrix[entry_key][5] = 0 # flag to free, as it will be moved up to selected
table.remove(entry)
self.show_tag(tag, self.search_scrolled_win_selected_tags, self.tag_matrix_selected,
self.table_selected_tags, None, None, tag_toggle=0)
return
def update_common_tags(self):
common_tags = self.get_common_tags_for_active_tags() # now get new common tags of selected tags
self.remove_all_entries_common_tags()
for common_tag in sorted(common_tags, key=lambda tag: tag.name):
self.show_tag(common_tag.name, self.search_scrolled_win_common_tags, self.tag_matrix_common,
self.table_common_tags, None, None, tag_toggle=1) # 0 as it will be toggled to 1
self.show_pages()
self.set_entry_completion(common_tags, 'common')
return common_tags
def set_entry_completion(self, tags_list, scope):
if scope == 'all':
self.tag_list_store.clear()
for tag in tags_list:
self.tag_list_store.append([tag.name])
self.completion.set_model(self.tag_list_store)
return
if scope == 'common':
tags_list_sorted = sorted(tags_list, key=lambda tag: tag.name)
common_tag_list_store = gtk.ListStore(str)
for tag in tags_list_sorted:
common_tag_list_store.append([tag.name])
self.completion.set_model(common_tag_list_store)
return
def handle_entry_icon(self, entry, icon_pos, event, tag, key, matrix, table):
if icon_pos == 0:
self.handle_left_entry_icon(tag, key, entry, matrix, table)
# if icon_pos == 1:
# self.handle_right_entry_icon(entry, matrix, table)
def handle_right_entry_icon(self, entry, matrix, table):
# not used yet
table.remove(entry)
self.mark_cell_free(entry, matrix)
def handle_left_entry_icon(self, tag, key, entry, matrix, table):
self.toggle_tag(tag, entry, matrix, table)
self.update_common_tags()
def show_pages(self):
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.place_pages(intersect_pages, intersect_pages_node_text, union_pages, union_pages_node_text)
def clear_pages(self):
self.pages_tree_store.clear()
def place_pages(self, intersect_pages, intersect_pages_node_text, union_pages, union_pages_node_text):
self.clear_pages()
self.place_pages_with_nodes(intersect_pages, union_pages)
if self.checkbutton_inter_state:
parent_intersection = self.pages_tree_store.append(None, (intersect_pages_node_text, 0))
for page in intersect_pages:
# for page in self.intersect_pages_list[1]:
# self.pages_tree_store.append(intersection, [page])
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)))
else:
self.pages_tree_store.append(parent_intersection, (page.basename, 1))
self.pages_tree_view.expand_all()
if self.checkbutton_union_state:
parent_union = self.pages_tree_store.append(None, (union_pages_node_text, 0))
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)))
else:
self.pages_tree_store.append(parent_union, (page.basename, 1))
self.pages_tree_view.expand_all()
def place_pages_with_nodes(self, intersect_pages, union_pages):
page_roots_list = []
intersect_page_namespace_set = set()
self.intersect_pages_list.append([])
self.intersect_pages_list.append([])
self.intersect_pages_list.append([])
self.intersect_pages_list.append([])
self.intersect_pages_list.append([])
self.intersect_pages_list.append([])
self.pages_tree_view.append_column(self.pages_column)
if self.checkbutton_inter_state:
for page in intersect_pages:
self.intersect_pages_list[0].append(page.name)
self.intersect_pages_list[1].append(page.basename)
# following not implemented yet
# self.intersect_pages_list[2].append(page.parts)
# self.intersect_pages_list[3].append(page.namespace)
# self.intersect_pages_list[4].append(page.parent.name)
# self.intersect_pages_list[5].append(page.isroot)
# http://cbio.ufs.ac.za/live_docs/nbn_tut/trees.html
# intersect_page_namespace_set.add(page.namespace)
def clear_entry(self, entry, iconpos, event):
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_entry_status(self, entry, matrix):
entry_key = self.get_entry_key(entry, matrix)
entry_status = self.get_entry_enabled_status(entry_key, matrix)
entry_style = self.get_entry_style(entry_key, matrix)
if entry_status:
# disable
entry.modify_text(gtk.STATE_NORMAL, gtk.gdk.color_parse("grey"))
matrix[entry_key][6] = 0 # flag to disabled
else:
# enable
entry.modify_text(gtk.STATE_NORMAL, entry_style.text[gtk.STATE_NORMAL])
matrix[entry_key][6] = 1 # flag to enabled
return
def get_common_tags_for_active_tags(self):
common_tags_list = []
common_tags_set = set()
active_tags = self.get_active_tags()
for each_active_tag in active_tags:
common_tags_set_tmp = self.get_common_tags_for_tag(each_active_tag)
common_tags_list.append(common_tags_set_tmp)
if common_tags_list:
common_tags_set = set.intersection(*common_tags_list)
return common_tags_set
def get_common_tags_for_tags(self):
selected_tags_list = []
common_tags_list = set()
tags = self.index.list_all_tags()
active_tags = self.get_active_tags()
# this is to get the tag name and index to lookup from db
for tag in tags:
for active_tag in active_tags:
if tag.name == active_tag:
selected_tags_list.append(tag)
intersecting_tags_name = self.index.list_intersecting_tags(selected_tags_list)
for each_tag in intersecting_tags_name:
common_tags_list.add(each_tag)
return common_tags_list
def get_common_tags_for_tag(self, tag):
common_tags_set = set()
tagged_pages_names_each_tag_set = set()
tagged_pages_names_selected_tag_set = set()
all_tags = self.index.list_all_tags()
tagged_pages_list_of_selected_tag = self.index.list_tagged_pages(tag)
# Put all pages of the selected tag into a set
for page in tagged_pages_list_of_selected_tag:
tagged_pages_names_selected_tag_set.add(page.name)
# now run through all tags except the selected tag
for each_tag in all_tags:
if each_tag.name == tag:
continue
tagged_pages_list_of_each_tag = self.index.list_tagged_pages(each_tag)
# put all pages of the current tag into another set
for page in tagged_pages_list_of_each_tag:
tagged_pages_names_each_tag_set.add(page.name)
# now do a intersection between both lists.
# Result is a list set with unique list of pages which both tags have in common
intersection_pages_set = (tagged_pages_names_selected_tag_set & tagged_pages_names_each_tag_set)
# 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)
# just clear the temporary set to run over with the next tag
tagged_pages_names_each_tag_set.clear()
return common_tags_set
def get_common_pages_of_active_tags(self):
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 = set(self.index.list_tagged_pages(active_tag))
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)
return is_tagged_pages_of_active_tags, u_tagged_pages_of_active_tags
def get_entry_style(self, key, matrix):
return matrix[key][7]
def get_entry_enabled_status(self, key, matrix):
return matrix[key][6]
def get_entry_occupied_status(self, key, matrix):
return matrix[key][5]
def get_entry(self, key, matrix):
return matrix[key][4]
def get_entry_key(self, entry, matrix):
for key in matrix.keys():
if matrix[key][4] == entry: # look for the cell pos of the deleted entry
return key
return False
def get_cell_pos(self, key, matrix):
x1 = matrix[key][0]
x2 = matrix[key][1]
y1 = matrix[key][2]
y2 = matrix[key][3]
return x1, x2, y1, y2
def mark_cell_free(self, entry, matrix):
key = self.get_entry_key(entry, matrix)
if key:
matrix[key][5] = 0 # mark status of cell pos as 0 = empty
def get_free_cell_key(self, matrix):
for key in matrix.keys():
if matrix[key][5] == 0:
return key
return False
def get_active_tags(self):
active_tags = []
for key in self.tag_matrix_selected.keys():
# the tag can be de-selected again
if self.tag_matrix_selected[key][5] and self.tag_matrix_selected[key][6]:
active_tags.append(self.tag_matrix_selected[key][10])
return active_tags
def get_n_common_tags_shown(self):
common_tags_shown = []
for key in self.tag_matrix_common.keys():
# the tag can be de-selected again
if self.tag_matrix_common[key][5] and not self.tag_matrix_common[key][6]:
common_tags_shown.append(self.tag_matrix_common[key][10])
return len(common_tags_shown)
def get_key_for_common_tag(self, tag):
for key in self.tag_matrix_common.keys():
if self.tag_matrix_common[key][10] == tag:
return key
return False
def get_pages_for_tag(self, tag):
return self.index.list_tagged_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:
dummy = 0
return False
return word
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment