Guest User

Untitled

a guest
Jan 16th, 2018
305
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 37.83 KB | None | 0 0
  1. # -*- coding: utf-8 -*-
  2.  
  3. # Plugin for ZimWiki: Find tagged pages.
  4. # Copyright 2018 Murat Güven <muratg@online.de>
  5. # Copyright ZimWiki: 2009-2013 Jaap Karssenberg <jaap.karssenberg@gmail.com>
  6.  
  7. # V1.20
  8.  
  9. # Change Log
  10. # V1.20 Update to support ZimWiki >067
  11. # V1.19 Little bug fix when selecting the tree title of pages (All common pages of ...) throws a value error. Code cleaning
  12. # V1.18 Using zim widgets
  13. # V1.17 Renamed to find tagged pages
  14. # V1.16: ESC to close plugin window added
  15. # V1.15: If cursor is at empty line, don't go back to previous line / word and lookup a tag.
  16. # Also changed search beaviour. Now looks for word which is right to the cursor instead of left
  17. # V1.14: Now getting word at cursor position without the need to mark the text in order to look it up
  18. # V1.13: Little bug fix
  19. # V1.12: Added functionality to lookup a tag which is selected.
  20.  
  21.  
  22. import gtk
  23. import gtk.gdk
  24.  
  25. from zim.plugins import PluginClass, extends, WindowExtension
  26. from zim.notebook import Path
  27. from zim.search import *
  28. from zim.signals import ConnectorMixin
  29. from zim.actions import action
  30. from zim.gui.widgets import Window, ScrolledWindow, VPaned, InputEntry
  31.  
  32.  
  33. ALTSHIFTQ = '<alt><shift>q'
  34.  
  35. ENTRY_LABEL_WIDTH_PIX = 50
  36. BUTTON_HIGHT_PIX = 25
  37. BUTTON_WIDTH_PIX = 80
  38. H_SPACING_PIX = 3
  39. TABLE_PADDING_LEFT_PIX = 10
  40. TABLE_PADDING_TOP_PIX = 0
  41. TABLE_COL_SPACINGS = 10
  42. TABLE_ROW_SPACINGS = 2
  43. ENTRY_HIGHT_PIX = 25
  44. ENTRY_WIDTH_PIX = 180
  45. PADDING_TOP_PIX = 10
  46. CHECK_BOX_PADDING_LEFT_PIX = ENTRY_LABEL_WIDTH_PIX + 1
  47. SIZE_X = H_SPACING_PIX + ENTRY_LABEL_WIDTH_PIX + \
  48. H_SPACING_PIX + ENTRY_WIDTH_PIX + \
  49. H_SPACING_PIX + BUTTON_WIDTH_PIX \
  50. + H_SPACING_PIX + BUTTON_WIDTH_PIX + 8 * H_SPACING_PIX
  51.  
  52. TAG_ENTRY_WIDTH_PIX = (SIZE_X - 2 * TABLE_PADDING_LEFT_PIX - 3 * TABLE_COL_SPACINGS) / 2
  53.  
  54.  
  55. class FindTaggedPagesPlugin(PluginClass):
  56. plugin_info = {
  57. 'name': _('Find tagged pages'), # T: plugin name
  58. 'description': _('''\
  59. This plugin helps finding pages or tags by narrowing down to selected and common tags.
  60.  
  61. In addition you can use an open search filter.
  62.  
  63. V1.20
  64. '''), # T: plugin description
  65. 'author': 'Murat Güven',
  66. 'help': 'Plugins:Find tagged pages',
  67. }
  68.  
  69. plugin_preferences = (
  70. # T: label for plugin preferences dialog
  71. ('add_pages_separator', 'string', _('Add pages as link function: Tag separator to use'), '||'),
  72. ('add_pages_atsign', 'bool', _('Add pages as link function: Paste tags as tags (with @)'), False),
  73. # T: plugin preference
  74. )
  75.  
  76.  
  77. @extends('MainWindow')
  78. class FindTaggedPagesMainWindowExtension(WindowExtension, ConnectorMixin):
  79. uimanager_xml = '''
  80. <ui>
  81. <menubar name='menubar'>
  82. <menu action='search_menu'>
  83. <placeholder name='plugin_items'>
  84. <menuitem action='find_tagged_pages'/>
  85. </placeholder>
  86. </menu>
  87. </menubar>
  88. <toolbar name='toolbar'>
  89. <placeholder name='tools'>
  90. <toolitem action='find_tagged_pages'/>
  91. </placeholder>
  92. </toolbar>
  93. </ui>
  94. '''
  95.  
  96. accel_key = ALTSHIFTQ
  97.  
  98. @action(_('Find tagged pages'), stock='gtk-find', accelerator=accel_key, readonly=True) # T: menu item
  99. def find_tagged_pages(self):
  100. self.ui = self.window.ui
  101. self.index = self.window.ui.notebook.index
  102. self.table_selected_tags = gtk.Table()
  103. self.table_selected_tags.set_col_spacings(TABLE_COL_SPACINGS)
  104. self.table_selected_tags.set_row_spacings(TABLE_ROW_SPACINGS)
  105. self.table_common_tags = gtk.Table()
  106. self.table_common_tags.set_col_spacings(TABLE_COL_SPACINGS)
  107. self.table_common_tags.set_row_spacings(TABLE_ROW_SPACINGS)
  108. #self.search_win = gtk.Window()
  109. self.search_win = Window()
  110. self.search_win.set_title("Find tagged pages")
  111. #self.v_paned_main = gtk.VPaned()
  112. self.v_paned_main = VPaned()
  113. #self.v_paned_top = gtk.VPaned()
  114. self.v_paned_top = VPaned()
  115. #self.v_paned_bottom = gtk.VPaned()
  116. self.v_paned_bottom = VPaned()
  117. # homogeneous=False, spacing=0
  118. self.hbox1_buttons = gtk.HBox(False, H_SPACING_PIX)
  119. self.hbox2_buttons = gtk.HBox(False, H_SPACING_PIX)
  120. self.hbox3_buttons = gtk.HBox(False, H_SPACING_PIX)
  121. self.vbox_buttons = gtk.VBox(False, H_SPACING_PIX)
  122. self.vbox_selected_tags = gtk.VBox(False, H_SPACING_PIX)
  123. self.vbox_common_tags = gtk.VBox(False, H_SPACING_PIX)
  124. # xalign=0.0, yalign=0.0, xscale=0.0, yscale=0.0
  125. self.halign1_buttons = gtk.Alignment(0, 0, 0, 0)
  126. self.halign2_buttons = gtk.Alignment(0, 0, 0, 0)
  127. self.halign3_buttons = gtk.Alignment(0, 0, 0, 0)
  128. self.halign_table_selected_tags = gtk.Alignment(0, 0, 0, 0)
  129. self.halign_label_selected_tags = gtk.Alignment(0, 0, 0, 0)
  130. self.halign_label_common_tags = gtk.Alignment(0, 0, 0, 0)
  131. self.halign_table_common_tags = gtk.Alignment(0, 0, 0, 0)
  132. self.search_scrolled_win_buttons = gtk.ScrolledWindow()
  133. #self.search_scrolled_win_buttons = ScrolledWindow()
  134. self.search_scrolled_win_selected_tags = gtk.ScrolledWindow()
  135. #self.search_scrolled_win_selected_tags = ScrolledWindow()
  136. self.search_scrolled_win_common_tags = gtk.ScrolledWindow()
  137. #self.search_scrolled_win_common_tags = ScrolledWindow()
  138. self.search_scrolled_win_pages = gtk.ScrolledWindow()
  139. #self.search_scrolled_win_pages = ScrolledWindow()
  140.  
  141. #self.main_entry = gtk.Entry()
  142. self.main_entry = InputEntry()
  143. #self.filter_entry = gtk.Entry()
  144. self.filter_entry = InputEntry()
  145. self.completion = gtk.EntryCompletion()
  146. self.tag_list_store = gtk.ListStore(str)
  147. self.pages_tree_store = gtk.TreeStore(str, int)
  148. self.pages_tree_view = gtk.TreeView(self.pages_tree_store)
  149. self.pages_column = gtk.TreeViewColumn("Pages")
  150. self.score_column = gtk.TreeViewColumn("Score")
  151. self.cell_text = gtk.CellRendererText()
  152. self.progress_bar = gtk.ProgressBar()
  153. self.progress_bar_fraction = self.ui.notebook.pages.n_list_pages() / 100000.0
  154. self.checkbutton_inter_state = True
  155. self.checkbutton_union_state = False
  156. self.intersect_pages_list = []
  157. self.query = Query
  158. self.selection = SearchSelection(self.ui.notebook)
  159. self.filter_hit_store = gtk.ListStore(str, int)
  160. self.cancelled = False
  161. self.tree_selection = self.pages_tree_view.get_selection()
  162.  
  163. self.tag_matrix_common = {
  164. 1: [0, 1, 0, 1, 0, False, False, 0, False, 0, 0, 0],
  165. 2: [1, 2, 0, 1, 0, False, False, 0, False, 0, 0, 0]
  166. }
  167. '''
  168. tag_matrix_common primarily needed to fill the window with
  169. dummy entries, otherwise 2 elements (equals one line) were
  170. enough
  171.  
  172. table:
  173. 0/1: x1 = left_attach, x2 = right_attach
  174. 2/3: y1 = top_attach, y2 = bottom_attach
  175. 4: entry = store entry object to find when deleted or to delete all
  176. 5: status_occupied: 0 = empty, 1: cell occupied ( in table)
  177. 6: status_enabled: 0 = disabled (greyed out), 1: enabled
  178. 7: entrystyle = store entry style object
  179. 8: show tag in completion: 0: no, 1: yes
  180. 9: pages: all pages for a tag
  181. 10: the tag
  182. 11: # of tags with common pages
  183. '''
  184. self.tag_matrix_selected = {
  185. 1: [0, 1, 0, 1, 0, False, False, 0, False, 0, 0, 0],
  186. 2: [1, 2, 0, 1, 0, False, False, 0, False, 0, 0, 0]
  187. }
  188. '''
  189. tag_matrix_selected primarily needed to fill the window with
  190. dummy entries, otherwise 2 elements (equals one line) were
  191. enough
  192.  
  193. table:
  194. 0/1: x1 = left_attach, x2 = right_attach
  195. 2/3: y1 = top_attach, y2 = bottom_attach
  196. 4: entry = store entry object to find when deleted or to delete all
  197. 5: status_occupied: 0 = empty, 1: cell occupied ( in table)
  198. 6: status_enabled: 0 = disabled (greyed out), 1: enabled
  199. 7: entrystyle = store entry style object
  200. 8: show tag in completion: 0: no, 1: yes
  201. 9: pages: all pages for a tag
  202. 10: the tag
  203. 11: # of tags with common pages
  204. '''
  205. self.LEFT_ATT_X1 = 0
  206. self.RIGHT_ATT_X2 = 1
  207. self.TOP_ATT_Y1 = 2
  208. self.BOT_ATT_Y2 = 3
  209.  
  210. self.ENTRY_OBJ = 4
  211. self.CEL_OCCUPIED = 5
  212. self.CEL_STATUS = 6
  213. self.ENTRY_STYLE = 7
  214. self.SHOW_TAG = 8
  215. self.ALL_PAGES = 9
  216. self.TAG = 10
  217. self.TAG_NO = 11
  218.  
  219. self.search_help_text = (
  220. 'For advanced search you can use operators like\n'
  221. '+ AND and &&,\n'
  222. 'OR or ||, \n'
  223. '- NOT \n'
  224. 'Keywords:\n'
  225. 'Content: or Name: or NameSpace:\n'
  226. 'Links: or LinksFrom: or LinksTo:\n'
  227. 'See the help page for the general search function for more details.'
  228. ) # T: help text for the search dialog
  229.  
  230. self.tag_help_text = (
  231. 'Enter and select a tag from a list of your tags.\n'
  232. 'The tag entered here will be shown below in selected tags.\n\n'
  233. 'The available list of your tags will show all tags\n'
  234. 'which have common pages with the selected tags\n'
  235. )
  236. self.search_win.set_default_size(SIZE_X, 800)
  237. self.search_win.set_geometry_hints(min_width=SIZE_X)
  238.  
  239. self.v_paned_main.set_position(250)
  240. self.search_win.add(self.v_paned_main)
  241. self.v_paned_top.set_position(100)
  242. self.v_paned_bottom.set_position(180)
  243.  
  244. self.v_paned_top.add1(self.vbox_buttons)
  245. # self.v_paned_top.add1(self.search_scrolled_win_buttons)
  246. self.v_paned_top.add2(self.search_scrolled_win_selected_tags)
  247.  
  248. self.v_paned_main.add1(self.v_paned_top)
  249.  
  250. self.table_selected_tags.set_homogeneous(False)
  251.  
  252. # Populate Buttons Window_________________________________________________________________________________
  253. # padding_top, padding_bottom, padding_left, padding_right
  254. self.halign1_buttons.set_padding(PADDING_TOP_PIX, 0, 0, 0)
  255. self.halign2_buttons.set_padding(0, 0, 0, 0)
  256. self.halign3_buttons.set_padding(PADDING_TOP_PIX, 0, CHECK_BOX_PADDING_LEFT_PIX, 0)
  257.  
  258. self.halign1_buttons.add(self.hbox1_buttons)
  259. self.halign2_buttons.add(self.hbox2_buttons)
  260. self.halign3_buttons.add(self.hbox3_buttons)
  261.  
  262.  
  263. self.vbox_buttons.pack_start(self.halign1_buttons, False, False, 0)
  264. self.vbox_buttons.pack_start(self.halign2_buttons, False, False, 0)
  265. self.vbox_buttons.pack_start(self.halign3_buttons, False, False, 0)
  266.  
  267.  
  268. # Add Main Entry field ___________________________________________________________________________________
  269. main_entry_label = gtk.Label("Tag:")
  270. main_entry_label.set_size_request(ENTRY_LABEL_WIDTH_PIX, ENTRY_HIGHT_PIX)
  271. self.main_entry.set_tooltip_text(self.tag_help_text)
  272. self.main_entry.set_icon_from_stock(1, "gtk-clear")
  273. self.main_entry.set_size_request(ENTRY_WIDTH_PIX, ENTRY_HIGHT_PIX)
  274. self.hbox1_buttons.add(main_entry_label)
  275. self.hbox1_buttons.add(self.main_entry)
  276. # ________________________________________________________________________________________________________
  277. # Add Show All Button_____________________________________________________________________________________
  278. self.button_show_all = gtk.Button("Show all")
  279. self.button_show_all.set_size_request(BUTTON_WIDTH_PIX, BUTTON_HIGHT_PIX)
  280. self.hbox1_buttons.add(self.button_show_all)
  281. # _________________________________________________________________________________________________________
  282. # Add Remove all Button____________________________________________________________________________________
  283. self.button_remove_all = gtk.Button("Remove all")
  284. self.button_remove_all.set_size_request(BUTTON_WIDTH_PIX, BUTTON_HIGHT_PIX)
  285. self.hbox1_buttons.add(self.button_remove_all)
  286. # _________________________________________________________________________________________________________
  287. # Add Filter Entry field___________________________________________________________________________________
  288. self.filter_entry.set_tooltip_text(self.search_help_text)
  289. filter_entry_label = gtk.Label("Filter:")
  290. filter_entry_label.set_size_request(ENTRY_LABEL_WIDTH_PIX, ENTRY_HIGHT_PIX)
  291. self.filter_entry.set_icon_from_stock(1, "gtk-clear")
  292. self.filter_entry.set_size_request(ENTRY_WIDTH_PIX, ENTRY_HIGHT_PIX)
  293. self.hbox2_buttons.add(filter_entry_label)
  294. self.hbox2_buttons.add(self.filter_entry)
  295. # _________________________________________________________________________________________________________
  296. # Add search / progress bar ______________________________________________________________________________
  297. self.button_search = gtk.Button("Find")
  298. self.button_search.set_size_request(BUTTON_WIDTH_PIX, BUTTON_HIGHT_PIX)
  299. self.button_cancel_search = gtk.Button("Stopp")
  300. self.button_cancel_search.set_size_request(BUTTON_WIDTH_PIX, BUTTON_HIGHT_PIX)
  301. self.progress_bar.set_size_request(BUTTON_WIDTH_PIX, BUTTON_HIGHT_PIX)
  302. self.hbox2_buttons.add(self.button_search)
  303. self.hbox2_buttons.add(self.button_cancel_search)
  304. self.hbox2_buttons.add(self.progress_bar)
  305. # _________________________________________________________________________________________________________
  306. # Add Check boxes__________________________________________________________________________________________
  307. self.checkbutton_inter = gtk.CheckButton("Common pages")
  308. self.checkbutton_inter.set_active(is_active=1)
  309. self.checkbutton_union = gtk.CheckButton("Unique pages")
  310.  
  311. self.hbox3_buttons.add(self.checkbutton_inter)
  312. self.hbox3_buttons.add(self.checkbutton_union)
  313. # _________________________________________________________________________________________________________
  314.  
  315. # Add Page add button__________________________________________________________________________________________
  316. self.button_add_page = gtk.Button("Add pages as link")
  317. self.button_add_page.set_size_request(BUTTON_WIDTH_PIX + 50, BUTTON_HIGHT_PIX)
  318. self.hbox3_buttons.add(self.button_add_page)
  319. # _________________________________________________________________________________________________________
  320.  
  321.  
  322. self.search_scrolled_win_buttons.add_with_viewport(self.vbox_buttons)
  323.  
  324. # Add selected tags text___________________________________________________________________________________
  325. self.separator = gtk.HSeparator()
  326. selected_tags_label = gtk.Label("Selected tags: ")
  327. self.separator2 = gtk.HSeparator()
  328. common_tags_label = gtk.Label("Common tags of selected tags: ")
  329.  
  330. self.halign_label_selected_tags.add(selected_tags_label)
  331. self.halign_label_selected_tags.set_padding(5, 0, TABLE_PADDING_LEFT_PIX, 0)
  332. self.halign_label_common_tags.add(common_tags_label)
  333. self.halign_label_common_tags.set_padding(5, 0, TABLE_PADDING_LEFT_PIX, 0)
  334.  
  335. self.halign_table_selected_tags.add(self.table_selected_tags)
  336. self.halign_table_selected_tags.set_padding(TABLE_PADDING_TOP_PIX, 0, TABLE_PADDING_LEFT_PIX, 0)
  337. self.halign_table_common_tags.add(self.table_common_tags)
  338. self.halign_table_common_tags.set_padding(TABLE_PADDING_TOP_PIX, 0, TABLE_PADDING_LEFT_PIX, 0)
  339.  
  340. self.vbox_selected_tags.pack_start(self.halign_label_selected_tags, False, False, 0)
  341. self.vbox_selected_tags.pack_start(self.separator, False, False, 0)
  342. self.vbox_selected_tags.pack_start(self.halign_table_selected_tags, False, False, 0)
  343.  
  344. self.vbox_common_tags.pack_start(self.halign_label_common_tags, False, False, 0)
  345. self.vbox_common_tags.pack_start(self.separator2, False, False, 0)
  346. self.vbox_common_tags.pack_start(self.halign_table_common_tags, False, False, 0)
  347.  
  348. self.search_scrolled_win_selected_tags.add_with_viewport(self.vbox_selected_tags)
  349. self.search_scrolled_win_common_tags.add_with_viewport(self.vbox_common_tags)
  350.  
  351. self.search_scrolled_win_pages.add(self.pages_tree_view)
  352. self.v_paned_main.add2(self.v_paned_bottom)
  353. self.v_paned_bottom.add1(self.search_scrolled_win_common_tags)
  354. self.v_paned_bottom.add2(self.search_scrolled_win_pages)
  355.  
  356. self.pages_tree_view.append_column(self.pages_column)
  357. self.pages_column.pack_start(self.cell_text, False)
  358. self.pages_column.add_attribute(self.cell_text, "text", 0)
  359. self.pages_column.set_sort_column_id(0)
  360. self.pages_column.set_resizable(True)
  361. self.pages_column.set_max_width(SIZE_X-60)
  362.  
  363. self.pages_tree_view.append_column(self.score_column)
  364. self.score_column.pack_start(self.cell_text, False)
  365. self.score_column.add_attribute(self.cell_text, "text", 1)
  366. self.score_column.set_sort_column_id(1)
  367. self.pages_tree_store.set_sort_column_id(1, gtk.SORT_DESCENDING)
  368.  
  369. self.set_entry_completion(self.ui.notebook.tags.list_all_tags(), 'all')
  370. self.completion.set_inline_completion(True)
  371. self.completion.set_inline_selection(True)
  372. self.main_entry.set_completion(self.completion)
  373. self.completion.set_text_column(0)
  374. self.completion.set_popup_single_match(False)
  375.  
  376. self.search_win.show_all()
  377. self.progress_bar.hide_all()
  378. self.button_cancel_search.hide_all()
  379.  
  380. self.search_win.connect("key_press_event", self.get_key_event)
  381.  
  382. self.main_entry.connect("icon-press", self.clear_entry)
  383. self.filter_entry.connect("icon-press", self.clear_filter_entry)
  384. self.main_entry.connect('activate', self.main_entry_return)
  385. self.filter_entry.connect('activate', self.search_filter)
  386. self.completion.connect('match-selected', self.match_selected)
  387.  
  388. self.button_remove_all.connect("clicked", self.button_remove_all_clicked)
  389. self.button_show_all.connect("clicked", self.button_show_all_clicked)
  390. self.button_search.connect("clicked", self.search_filter)
  391. self.button_cancel_search.connect("clicked", self.search_cancel)
  392. self.button_add_page.connect("clicked", self.button_add_page_clicked)
  393.  
  394. self.checkbutton_inter.connect("toggled", self.checkbutton_inter_toggled)
  395. self.checkbutton_union.connect("toggled", self.checkbutton_union_toggled)
  396.  
  397. self.tree_selection.connect("changed", self.page_selected, self.tree_selection)
  398.  
  399. self.auto_fill_tag()
  400.  
  401. def auto_fill_tag(self):
  402. # get the text buffer from the note
  403. self.buffer = self.window.pageview.view.get_buffer()
  404. word = self.get_word_at_cursor_pos(self.buffer)
  405. tags = self.ui.notebook.tags.list_all_tags()
  406.  
  407. if word:
  408. for tag in tags:
  409. if word == tag.name.decode('utf-8'):
  410. word = word.strip('@')
  411. self.main_entry.append_text(word)
  412. self.main_entry_return(None)
  413. break
  414.  
  415. def page_selected(self, widget, tree_selection):
  416. # This function displays the page in Zim when selected in result list
  417. (model, pathlist) = tree_selection.get_selected_rows()
  418. for path in pathlist:
  419. tree_iter = model.get_iter(path)
  420. page = model.get_value(tree_iter, 0)
  421. try:
  422. page_name_index = self.intersect_pages_list[1].index(page)
  423. page_path = Path(self.intersect_pages_list[0][page_name_index])
  424. if page_path:
  425. self.ui.open_page(page_path)
  426. except:
  427. pass
  428.  
  429. def checkbutton_inter_toggled(self, widget):
  430. if self.checkbutton_inter_state:
  431. self.checkbutton_inter_state = False
  432. else:
  433. self.checkbutton_inter_state = True
  434. # TODO: handle error when no tags are shown
  435. self.show_pages()
  436.  
  437. def checkbutton_union_toggled(self, widget):
  438. if self.checkbutton_union_state:
  439. self.checkbutton_union_state = False
  440. else:
  441. self.checkbutton_union_state = True
  442. # TODO: handle error when no tags are shown
  443. self.show_pages()
  444.  
  445. def button_remove_all_clicked(self, widget):
  446. self.main_entry.set_text('')
  447. self.filter_entry.set_text('')
  448. self.clear_pages()
  449. self.remove_all_entries()
  450. self.set_entry_completion(self.ui.notebook.tags.list_all_tags(), 'all') # to fill the completion list with all tags again
  451.  
  452. def button_add_page_clicked(self, widget):
  453. # get the windows, buffer and cursor position
  454. main_window = self.window.pageview.view
  455. zim_buffer = self.window.pageview.view.get_buffer()
  456. cursor = zim_buffer.get_iter_at_mark(zim_buffer.get_insert())
  457.  
  458. # add text to the page
  459. zim_buffer.insert(cursor, "== Pages with following tag(s) in common ==\n")
  460. # get the selected tags and add them to the page
  461. tags = self.get_active_tags()
  462. atsign=""
  463. if self.plugin.preferences['add_pages_atsign']:
  464. atsign = "@"
  465. separator = self.plugin.preferences['add_pages_separator']
  466. tags_n = len(tags)
  467. for tag in sorted(tags):
  468. tags_n -= 1
  469. if tags_n == 0: # Don't add a separator for the last tag (or if only one tag)
  470. separator = ""
  471. zim_buffer.insert(cursor, atsign + tag + " " + separator + " ")
  472. zim_buffer.insert(cursor, "\n")
  473. # get the pages to be linked and put them into the page
  474. intersect_pages, union_pages = self.get_common_pages_of_active_tags()
  475. for page in sorted(intersect_pages):
  476. page_link = "[[" + page.name + "]]" + "\n"
  477. zim_buffer.insert(cursor, page_link)
  478.  
  479. def remove_all_entries(self):
  480. self.remove_all_entries_selected_tags()
  481. self.remove_all_entries_common_tags()
  482.  
  483. def remove_all_entries_selected_tags(self):
  484. for key in self.tag_matrix_selected.keys():
  485. entry = self.tag_matrix_selected[key][self.ENTRY_OBJ]
  486. if entry:
  487. self.table_selected_tags.remove(entry)
  488. self.tag_matrix_selected[key][self.ENTRY_OBJ:] = [0, 0, 0, 0, 0, 0, 0, 0]
  489.  
  490. def remove_all_entries_common_tags(self):
  491. for key in self.tag_matrix_common.keys():
  492. entry = self.tag_matrix_common[key][self.ENTRY_OBJ]
  493. if entry:
  494. self.table_common_tags.remove(entry)
  495. self.tag_matrix_common[key][self.ENTRY_OBJ:] = [0, 0, 0, 0, 0, 0, 0, 0]
  496.  
  497. def button_show_all_clicked(self, widget):
  498. self.main_entry.set_text('') # delete the text in the main_entry
  499.  
  500. if self.get_entry_occupied_status(1, self.tag_matrix_selected):
  501. self.main_entry.set_text('Tag already selected!')
  502. return
  503. # Show all tags as 'disabled'
  504. self.set_entry_completion(self.ui.notebook.tags.list_all_tags(), 'all')
  505. self.show_all_tag_entries()
  506.  
  507. def show_all_tag_entries(self):
  508. for tag in self.tag_list_store:
  509. self.show_tag(tag[0], self.search_scrolled_win_common_tags, self.tag_matrix_common, self.table_common_tags,
  510. None, None, tag_toggle=True)
  511.  
  512. def main_entry_return(self, entry):
  513. tag_exists = False
  514. tag_entered = unicode(self.main_entry.get_text())
  515. if tag_entered == '':
  516. return
  517. for tag in self.tag_list_store:
  518. if tag[0] == tag_entered:
  519. tag_exists = True
  520. break
  521. if not tag_exists:
  522. return
  523. self.place_tag(tag_entered)
  524.  
  525. def search_filter(self, widget):
  526. if not self.get_entry_occupied_status(1, self.tag_matrix_selected):
  527. self.main_entry.set_text('Select tag first!')
  528. return
  529. self.switch_progress_bar("on")
  530. self.clear_pages()
  531. self.filter_hit_store.clear()
  532. filter = unicode(self.filter_entry.get_text().strip())
  533. query = self.query(filter)
  534. self.selection.search(query, callback=self.search_callback)
  535. # TODO: handle error when no tags are shown
  536. self.show_pages()
  537. self.switch_progress_bar("off")
  538. self.cancelled = False
  539.  
  540. def switch_progress_bar(self, switch):
  541. if switch == "on":
  542. self.progress_bar.set_fraction(0)
  543. self.progress_bar.show()
  544. self.button_cancel_search.show_all()
  545. self.button_search.hide_all()
  546. if switch == "off":
  547. self.progress_bar.hide_all()
  548. self.button_cancel_search.hide_all()
  549. self.button_search.show_all()
  550. self.progress_bar.set_fraction(0)
  551.  
  552. def set_progress_bar(self):
  553. value = self.progress_bar.get_fraction() + self.progress_bar_fraction
  554. percent = value * 100
  555. percent = str(int(percent))
  556. if not value > 1:
  557. self.progress_bar.set_fraction(value)
  558. self.progress_bar.set_text(percent + "%")
  559.  
  560. def search_cancel(self, widget):
  561. self.cancelled = True
  562.  
  563. def search_callback(self, results, path):
  564. while gtk.events_pending():
  565. gtk.main_iteration(block=False)
  566. if results is not None:
  567. if results.scores.get(path): # so, only if there is a path.name
  568. self.filter_hit_store.append((path.name, results.scores.get(path)))
  569. self.set_progress_bar()
  570. return not self.cancelled
  571.  
  572. def match_selected(self, completion, model, iter):
  573. tag_selected = unicode(model[iter][0])
  574. if tag_selected:
  575. self.place_tag(tag_selected)
  576.  
  577. def get_key_event(self, widget, event):
  578. if event.keyval == gtk.gdk.keyval_from_name('Escape'):
  579. self.search_win.destroy()
  580.  
  581. def place_tag(self, tag):
  582. # place / toggle tag into top window
  583. self.main_entry.set_text('') # Delete text doesn't work when tag is selected from popup :(
  584. key_common = self.get_key_for_common_tag(tag)
  585.  
  586. if not key_common: # so it has not been placed yet
  587. self.show_tag(tag, self.search_scrolled_win_selected_tags,
  588. self.tag_matrix_selected, self.table_selected_tags,
  589. None, None, tag_toggle=False) # 0 as it will be toggled to 1
  590. else: # needs to be toggled to selected tags
  591. entry = self.get_entry(key_common, self.tag_matrix_common)
  592. self.toggle_tag(tag, entry, self.tag_matrix_common, self.table_common_tags)
  593. self.update_common_tags()
  594.  
  595. def show_tag(self, tag, window, matrix, table, left_info, right_info, tag_toggle):
  596. tag_to_place = unicode(tag)
  597. free_cell_key = self.get_free_cell_key(matrix)
  598. if free_cell_key == False: # no free cell available
  599. # start a new pair (equals new line)
  600. # I need to add +1 to y1 and y2 for new index
  601. # If I need to start a new line then x1=0, x2=1 for column 1 and
  602. # x1=1, x2=2 for column 2
  603. last_key = len(matrix)
  604. n_all_tags = self.ui.notebook.tags.n_list_all_tags()
  605. # if show all is pressed more than once, but there should be a better way to handle this
  606. if (last_key == n_all_tags) & (tag_toggle == True):
  607. return
  608. # get y1, y2 from last matrix element
  609. y1 = matrix[last_key][self.TOP_ATT_Y1] + 1 # add a new line to table
  610. y2 = matrix[last_key][self.BOT_ATT_Y2] + 1 # just for readability. same as y2 = y1+1
  611. matrix[last_key + 1] = [0, 1, y1, y2, 0, 0, 0, 0, 0, 0, 0, 0] # new key
  612. matrix[last_key + 2] = [1, 2, y1, y2, 0, 0, 0, 0, 0, 0, 0, 0] # new key
  613. free_cell_key = self.get_free_cell_key(matrix) # now as we have 2 new free cells, get the 1. free cell key
  614. x1, x2, y1, y2 = self.get_cell_pos(free_cell_key, matrix)
  615. current_key = free_cell_key
  616. entry = gtk.Entry()
  617. entry_style = entry.get_style() # to get the current style of the entry
  618. entry.set_property("editable", False) # Text in entry shall not be editable
  619. entry.set_icon_from_stock(0, "gtk-convert")
  620. entry.set_icon_tooltip_text(0, "Toggle tag from filter")
  621. # entry.set_icon_from_stock(1, "gtk-close") ###### Removed for later implementation
  622. # entry.set_icon_tooltip_text(1, "Remove tag from filter") ###### Removed for later implementation
  623. n_tagged_pages = self.ui.notebook.tags.n_list_pages(tag_to_place)
  624.  
  625. entry.set_text("[" + str(n_tagged_pages) + "p] " + tag_to_place) # put the tag into the entry field
  626. entry.set_tooltip_text(str(n_tagged_pages) + " pages with @" + tag_to_place)
  627. entry.set_size_request(TAG_ENTRY_WIDTH_PIX, ENTRY_HIGHT_PIX)
  628. entry.connect("icon-press", self.handle_entry_icon, tag_to_place, current_key, matrix, table)
  629. table.attach(entry, x1, x2, y1, y2, xoptions=gtk.EXPAND, yoptions=gtk.EXPAND)
  630. matrix[current_key][self.ENTRY_OBJ] = entry # put the entry into the dict to find it
  631. matrix[current_key][self.CEL_OCCUPIED] = True # table cell is occupied
  632. matrix[current_key][self.CEL_STATUS] = tag_toggle # toggle entry style
  633. matrix[current_key][self.ENTRY_STYLE] = entry_style # style for each entry (if needed)
  634. matrix[current_key][self.ALL_PAGES] = self.get_pages_for_tag(tag_to_place) # store all pages for this tag
  635. matrix[current_key][self.TAG] = tag_to_place
  636.  
  637. # TODO: update pages in taq_matrix on signal, otherwise, data gets outdated
  638. self.toggle_entry_status(entry, matrix)
  639. window.show_all()
  640.  
  641. def toggle_tag(self, tag, entry, matrix, table):
  642. self.main_entry.set_text('') # just in case
  643. entry_key = self.get_entry_key(entry, matrix)
  644. entry_status = self.get_entry_enabled_status(entry_key, matrix)
  645.  
  646. if entry_status: # so the tag is enabled and in the top window
  647. # disable
  648. matrix[entry_key][self.CEL_OCCUPIED] = False # set flag to free within tag_matrix_selected
  649. # and move from top to bottom window
  650. table.remove(entry)
  651. self.show_tag(tag, self.search_scrolled_win_common_tags, self.tag_matrix_common, self.table_common_tags,
  652. None, None, tag_toggle=True)
  653. else: # the tag is disabled and in the bottom window
  654. # enable
  655. matrix[entry_key][self.CEL_OCCUPIED] = False # flag to free, as it will be moved up to selected
  656. table.remove(entry)
  657. self.show_tag(tag, self.search_scrolled_win_selected_tags, self.tag_matrix_selected,
  658. self.table_selected_tags, None, None, tag_toggle=False)
  659. return
  660.  
  661. def update_common_tags(self):
  662. common_tags = self.get_common_tags_for_active_tags() # now get new common tags of selected tags
  663. self.remove_all_entries_common_tags()
  664. for common_tag in sorted(common_tags, key=lambda tag: tag.name):
  665. self.show_tag(common_tag.name, self.search_scrolled_win_common_tags, self.tag_matrix_common,
  666. self.table_common_tags, None, None, tag_toggle=True) # 0 as it will be toggled to 1
  667. self.show_pages()
  668. self.set_entry_completion(common_tags, 'common')
  669. return common_tags
  670.  
  671. def set_entry_completion(self, tags_list, scope):
  672. if scope == 'all':
  673. self.tag_list_store.clear()
  674. for tag in tags_list:
  675. self.tag_list_store.append([tag.name])
  676. self.completion.set_model(self.tag_list_store)
  677. return
  678. if scope == 'common':
  679. tags_list_sorted = sorted(tags_list, key=lambda tag: tag.name)
  680. common_tag_list_store = gtk.ListStore(str)
  681. for tag in tags_list_sorted:
  682. common_tag_list_store.append([tag.name])
  683. self.completion.set_model(common_tag_list_store)
  684. return
  685.  
  686. def handle_entry_icon(self, entry, icon_pos, event, tag, key, matrix, table):
  687. if icon_pos == 0:
  688. self.handle_left_entry_icon(tag, key, entry, matrix, table)
  689. # if icon_pos == 1:
  690. # self.handle_right_entry_icon(entry, matrix, table)
  691.  
  692. def handle_right_entry_icon(self, entry, matrix, table):
  693. # not used yet
  694. table.remove(entry)
  695. self.mark_cell_free(entry, matrix)
  696.  
  697. def handle_left_entry_icon(self, tag, key, entry, matrix, table):
  698. self.toggle_tag(tag, entry, matrix, table)
  699. self.update_common_tags()
  700.  
  701. def show_pages(self):
  702. intersect_pages, union_pages = self.get_common_pages_of_active_tags()
  703. filter_len = len(self.filter_hit_store)
  704. if self.filter_entry.get_text() and filter_len:
  705. intersect_pages_node_text = "All common pages of selected tags [" + str(len(intersect_pages)) + "p] " +\
  706. "with filter [" + str(len(self.filter_hit_store)) + "p]"
  707. union_pages_node_text = "All unique pages of selected tags [" + str(len(intersect_pages)) + "p] " +\
  708. "with filter [" + str(len(self.filter_hit_store)) + "p]"
  709. else:
  710. intersect_pages_node_text = "All common pages of selected tags [" + str(len(intersect_pages)) + "p]"
  711. union_pages_node_text = "All unique pages of selected tags [" + str(len(union_pages)) + "p]"
  712. self.place_pages(intersect_pages, intersect_pages_node_text, union_pages, union_pages_node_text)
  713.  
  714. def clear_pages(self):
  715. self.pages_tree_store.clear()
  716.  
  717. def place_pages(self, intersect_pages, intersect_pages_node_text, union_pages, union_pages_node_text):
  718. self.clear_pages()
  719. self.place_pages_with_nodes(intersect_pages, union_pages)
  720. if self.checkbutton_inter_state:
  721. parent_intersection = self.pages_tree_store.append(None, (intersect_pages_node_text, 0))
  722. for page in intersect_pages:
  723. # for page in self.intersect_pages_list[1]:
  724. # self.pages_tree_store.append(intersection, [page])
  725. if self.filter_entry.get_text() and self.filter_hit_store:
  726. for hit in self.filter_hit_store:
  727. if hit[0] == page.name:
  728. self.pages_tree_store.append(parent_intersection, (page.basename, (hit[1] + 1)))
  729. else:
  730. self.pages_tree_store.append(parent_intersection, (page.basename, 1))
  731. self.pages_tree_view.expand_all()
  732. if self.checkbutton_union_state:
  733. parent_union = self.pages_tree_store.append(None, (union_pages_node_text, 0))
  734. for page in union_pages:
  735. if self.filter_entry.get_text() and self.filter_hit_store:
  736. for hit in self.filter_hit_store:
  737. if hit[0] == page.name:
  738. print ("Score: " + str(hit[1]))
  739. print page.name
  740. self.pages_tree_store.append(parent_union, (page.basename, (hit[1] + 1)))
  741. else:
  742. self.pages_tree_store.append(parent_union, (page.basename, 1))
  743. self.pages_tree_view.expand_all()
  744.  
  745.  
  746. def place_pages_with_nodes(self, intersect_pages, union_pages):
  747. page_roots_list = []
  748. intersect_page_namespace_set = set()
  749.  
  750. for l in range(0, 6): self.intersect_pages_list.append([])
  751.  
  752. self.pages_tree_view.append_column(self.pages_column)
  753. if self.checkbutton_inter_state:
  754. for page in intersect_pages:
  755. self.intersect_pages_list[0].append(page.name)
  756. self.intersect_pages_list[1].append(page.basename)
  757. # following not implemented yet
  758. # self.intersect_pages_list[2].append(page.parts)
  759. # self.intersect_pages_list[3].append(page.namespace)
  760. # self.intersect_pages_list[4].append(page.parent.name)
  761. # self.intersect_pages_list[5].append(page.isroot)
  762. # intersect_page_namespace_set.add(page.namespace)
  763.  
  764. def clear_entry(self, entry, iconpos, event):
  765. entry.set_text('')
  766.  
  767. def clear_filter_entry(self, entry, iconpos, event):
  768. if entry.get_text():
  769. entry.set_text('')
  770. self.clear_pages()
  771. self.filter_hit_store.clear()
  772. self.show_pages()
  773.  
  774. def toggle_entry_status(self, entry, matrix):
  775. entry_key = self.get_entry_key(entry, matrix)
  776. entry_status = self.get_entry_enabled_status(entry_key, matrix)
  777. entry_style = self.get_entry_style(entry_key, matrix)
  778. if entry_status:
  779. # disable
  780. entry.modify_text(gtk.STATE_NORMAL, gtk.gdk.color_parse("grey"))
  781. matrix[entry_key][self.CEL_STATUS] = False # flag to disabled
  782. else:
  783. # enable
  784. entry.modify_text(gtk.STATE_NORMAL, entry_style.text[gtk.STATE_NORMAL])
  785. matrix[entry_key][self.CEL_STATUS] = True # flag to enabled
  786. return
  787.  
  788. def get_common_tags_for_active_tags(self):
  789. common_tags_list = []
  790. common_tags_set = set()
  791. active_tags = self.get_active_tags()
  792. for each_active_tag in active_tags:
  793. common_tags_set_tmp = self.get_common_tags_for_tag(each_active_tag)
  794. common_tags_list.append(common_tags_set_tmp)
  795. if common_tags_list:
  796. common_tags_set = set.intersection(*common_tags_list)
  797. return common_tags_set
  798.  
  799. def get_common_tags_for_tags(self):
  800. selected_tags_list = []
  801. common_tags_list = set()
  802. tags = self.ui.notebook.tags.list_all_tags()
  803. active_tags = self.get_active_tags()
  804. # this is to get the tag name and index to lookup from db
  805. for tag in tags:
  806. for active_tag in active_tags:
  807. if tag.name == active_tag:
  808. selected_tags_list.append(tag)
  809.  
  810. intersecting_tags_name = self.ui.notebook.tags.list_intersecting_tags(selected_tags_list)
  811.  
  812. for each_tag in intersecting_tags_name:
  813. common_tags_list.add(each_tag)
  814. return common_tags_list
  815.  
  816. def get_common_tags_for_tag(self, tag):
  817. common_tags_set = set()
  818. tagged_pages_names_each_tag_set = set()
  819. tagged_pages_names_selected_tag_set = set()
  820.  
  821. all_tags = self.ui.notebook.tags.list_all_tags()
  822. tagged_pages_list_of_selected_tag = self.ui.notebook.tags.list_pages(tag)
  823.  
  824. # Put all pages of the selected tag into a set
  825. for page in tagged_pages_list_of_selected_tag:
  826. tagged_pages_names_selected_tag_set.add(page.name)
  827. # now run through all tags except the selected tag
  828. for each_tag in all_tags:
  829. if each_tag.name == tag:
  830. continue
  831. tagged_pages_list_of_each_tag = self.ui.notebook.tags.list_pages(each_tag)
  832. # put all pages of the current tag into another set
  833. for page in tagged_pages_list_of_each_tag:
  834. tagged_pages_names_each_tag_set.add(page.name)
  835. # now do a intersection between both lists.
  836. # Result is a list set with unique list of pages which both tags have in common
  837. intersection_pages_set = (tagged_pages_names_selected_tag_set & tagged_pages_names_each_tag_set)
  838. # if both tags, the selected and the current from the loop have at least one page in common then:
  839. if intersection_pages_set:
  840. # as the current tag has at least one page in common with the selected tag, add this tag into the list to show
  841. common_tags_set.add(each_tag)
  842. # just clear the temporary set to run over with the next tag
  843. tagged_pages_names_each_tag_set.clear()
  844. return common_tags_set
  845.  
  846. def get_common_pages_of_active_tags(self):
  847. active_tags = self.get_active_tags()
  848.  
  849. tagged_pages_list_of_active_tags = []
  850. is_tagged_pages_of_active_tags = set()
  851. u_tagged_pages_of_active_tags = set()
  852.  
  853. for active_tag in active_tags:
  854. tagged_pages_set_of_active_tag = set(self.ui.notebook.tags.list_pages(active_tag))
  855. if tagged_pages_set_of_active_tag:
  856. tagged_pages_list_of_active_tags.append(tagged_pages_set_of_active_tag)
  857. if tagged_pages_list_of_active_tags:
  858. is_tagged_pages_of_active_tags = set.intersection(*tagged_pages_list_of_active_tags)
  859. u_tagged_pages_of_active_tags = set.union(*tagged_pages_list_of_active_tags)
  860.  
  861. return is_tagged_pages_of_active_tags, u_tagged_pages_of_active_tags
  862.  
  863. def get_entry_style(self, key, matrix):
  864. return matrix[key][self.ENTRY_STYLE]
  865.  
  866. def get_entry_enabled_status(self, key, matrix):
  867. return matrix[key][self.CEL_STATUS]
  868.  
  869. def get_entry_occupied_status(self, key, matrix):
  870. return matrix[key][self.CEL_OCCUPIED]
  871.  
  872. def get_entry(self, key, matrix):
  873. return matrix[key][self.ENTRY_OBJ]
  874.  
  875. def get_entry_key(self, entry, matrix):
  876. for key in matrix.keys():
  877. if matrix[key][self.ENTRY_OBJ] == entry: # look for the cell pos of the deleted entry
  878. return key
  879. return False
  880.  
  881. def get_cell_pos(self, key, matrix):
  882. x1 = matrix[key][self.LEFT_ATT_X1]
  883. x2 = matrix[key][self.RIGHT_ATT_X2]
  884. y1 = matrix[key][self.TOP_ATT_Y1]
  885. y2 = matrix[key][self.BOT_ATT_Y2]
  886. return x1, x2, y1, y2
  887.  
  888. def mark_cell_free(self, entry, matrix):
  889. key = self.get_entry_key(entry, matrix)
  890. if key:
  891. matrix[key][self.CEL_OCCUPIED] = False # mark status of cell pos as 0 = empty
  892.  
  893. def get_free_cell_key(self, matrix):
  894. for key in matrix.keys():
  895. if not matrix[key][self.CEL_OCCUPIED]:
  896. return key
  897. return False
  898.  
  899. def get_active_tags(self):
  900. active_tags = []
  901.  
  902. for key in self.tag_matrix_selected.keys():
  903. # the tag can be de-selected again
  904. if self.tag_matrix_selected[key][self.CEL_OCCUPIED] and self.tag_matrix_selected[key][self.CEL_STATUS]:
  905. active_tags.append(self.tag_matrix_selected[key][self.TAG])
  906. return active_tags
  907.  
  908. def get_n_common_tags_shown(self):
  909. common_tags_shown = []
  910.  
  911. for key in self.tag_matrix_common.keys():
  912. # the tag can be de-selected again
  913. if self.tag_matrix_common[key][self.CEL_OCCUPIED] and not self.tag_matrix_common[key][self.CEL_STATUS]:
  914. common_tags_shown.append(self.tag_matrix_common[key][self.TAG])
  915. return len(common_tags_shown)
  916.  
  917. def get_key_for_common_tag(self, tag):
  918. for key in self.tag_matrix_common.keys():
  919. if self.tag_matrix_common[key][self.TAG] == tag:
  920. return key
  921. return False
  922.  
  923. def get_pages_for_tag(self, tag):
  924. return self.ui.notebook.tags.list_pages(tag)
  925.  
  926. def get_word_at_cursor_pos(self, buffer):
  927.  
  928. try:
  929. # preserve cursor position
  930. cursor_orig = buffer.get_iter_at_mark(buffer.get_insert())
  931.  
  932. cursor = buffer.get_iter_at_mark(buffer.get_insert())
  933.  
  934. # no need to look up if cursor is at empty line
  935. if cursor.ends_line() and cursor.starts_line():
  936. return
  937.  
  938. cursor.forward_word_end()
  939. buffer.place_cursor(cursor)
  940. word_end = buffer.get_iter_at_mark(buffer.get_insert())
  941.  
  942. cursor.backward_word_start()
  943. buffer.place_cursor(cursor)
  944. word_start = buffer.get_iter_at_mark(buffer.get_insert())
  945.  
  946. # just to not have changed anything, move cursor to start position
  947. buffer.place_cursor(cursor_orig)
  948.  
  949. word = word_end.get_text(word_start)
  950.  
  951. except ValueError:
  952. return False
  953. return word
Add Comment
Please, Sign In to add comment