382 lines
11 KiB
Python
382 lines
11 KiB
Python
from hydrus.client.gui.QLocator import QAbstractLocatorSearchProvider, QCalculatorSearchProvider, QLocatorSearchResult
|
|
from hydrus.core import HydrusGlobals as HG
|
|
from qtpy import QtWidgets as QW
|
|
from html import escape
|
|
|
|
|
|
def highlight_result_text( result_text: str, query_text: str ):
|
|
|
|
result_text = escape( result_text )
|
|
|
|
if query_text:
|
|
|
|
result_text = result_text.replace( escape( query_text ), '<b>' + escape( query_text ) + '</b>' )
|
|
|
|
return result_text
|
|
|
|
|
|
# Subclass for customizing icon paths
|
|
class CalculatorSearchProvider( QCalculatorSearchProvider ):
|
|
|
|
def __init__( self, parent = None ):
|
|
|
|
super().__init__( parent )
|
|
|
|
|
|
def titleIconPath( self ):
|
|
|
|
return str()
|
|
|
|
|
|
def selectedIconPath( self ):
|
|
|
|
return str()
|
|
|
|
|
|
def iconPath( self ):
|
|
|
|
return str()
|
|
|
|
|
|
class PagesSearchProvider( QAbstractLocatorSearchProvider ):
|
|
|
|
def __init__( self, parent = None ):
|
|
|
|
super().__init__( parent )
|
|
|
|
self.result_id_counter = 0
|
|
self.result_ids_to_pages = {}
|
|
|
|
|
|
def title( self ):
|
|
|
|
return "Pages"
|
|
|
|
|
|
# How many preallocated result widgets should be created (so that we don't have to recreate the entire result list on each search)
|
|
# Should be larger than the average expected result count
|
|
def suggestedReservedItemCount( self ):
|
|
|
|
return 32
|
|
|
|
|
|
# Called when the user activates a result
|
|
def resultSelected( self, resultID: int ):
|
|
|
|
page = self.result_ids_to_pages.get( resultID, None )
|
|
|
|
if page:
|
|
|
|
HG.client_controller.gui._notebook.ShowPage( page )
|
|
|
|
self.result_ids_to_pages = {}
|
|
|
|
|
|
# Should generate a list of QLocatorSearchResults
|
|
def processQuery( self, query: str, context, jobID: int ):
|
|
|
|
self.result_ids_to_pages = {}
|
|
|
|
if not HG.client_controller.gui or not HG.client_controller.gui._notebook:
|
|
|
|
return
|
|
|
|
|
|
tab_widget = HG.client_controller.gui._notebook
|
|
|
|
# helper function to traverse tab tree and generate entries
|
|
def get_child_tabs( tab_widget: QW.QTabWidget, parent_name: str ) -> list:
|
|
|
|
result = []
|
|
|
|
for i in range( tab_widget.count() ):
|
|
|
|
widget = tab_widget.widget(i)
|
|
|
|
if isinstance( widget, QW.QTabWidget ): # page of pages
|
|
|
|
result.extend( get_child_tabs( widget, widget.GetName() ) )
|
|
|
|
else:
|
|
|
|
selectable_media_page = widget
|
|
|
|
label = selectable_media_page.GetNameForMenu()
|
|
|
|
if not query in label:
|
|
|
|
continue
|
|
|
|
|
|
primary_text = highlight_result_text( label, query )
|
|
secondary_text = 'top level page' if not parent_name else "child of '" + escape( parent_name ) + "'"
|
|
|
|
result.append( QLocatorSearchResult( self.result_id_counter, 'thumbnails.png', 'thumbnails.png', True, [ primary_text, secondary_text ] ) )
|
|
|
|
self.result_ids_to_pages[ self.result_id_counter ] = selectable_media_page
|
|
|
|
self.result_id_counter += 1
|
|
|
|
return result
|
|
|
|
tab_data = get_child_tabs( tab_widget, '' )
|
|
|
|
if tab_data:
|
|
|
|
self.resultsAvailable.emit( jobID, tab_data )
|
|
|
|
|
|
# When this is called, it means that the Locator/LocatorWidget is done with these jobs and no results will be activated either
|
|
# So if any still-in-progress search can be stopped and any resources associated with these jobs can be freed
|
|
def stopJobs( self, jobs: list ):
|
|
|
|
self.result_ids_to_pages = {}
|
|
|
|
|
|
# Should the title item be visible in the result list
|
|
def hideTitle( self ):
|
|
|
|
return False
|
|
|
|
|
|
def titleIconPath( self ):
|
|
|
|
return str() #TODO fill this in
|
|
|
|
|
|
class MainMenuSearchProvider( QAbstractLocatorSearchProvider ):
|
|
|
|
def __init__( self, parent = None ):
|
|
|
|
super().__init__( parent )
|
|
|
|
self.result_id_counter = 0
|
|
self.result_ids_to_actions = {}
|
|
|
|
|
|
def title( self ):
|
|
|
|
return "Main Menu"
|
|
|
|
|
|
def suggestedReservedItemCount( self ):
|
|
|
|
return 128
|
|
|
|
|
|
def resultSelected( self, resultID: int ):
|
|
|
|
action = self.result_ids_to_actions.get( resultID, None )
|
|
|
|
if action:
|
|
|
|
action.trigger()
|
|
|
|
self.result_ids_to_actions = {}
|
|
|
|
|
|
def processQuery( self, query: str, context, jobID: int ):
|
|
|
|
if not HG.client_controller.new_options.GetBoolean( 'advanced_mode' ):
|
|
|
|
return
|
|
|
|
|
|
if len( query ) < 3:
|
|
|
|
return
|
|
|
|
self.result_ids_to_pages = {}
|
|
|
|
if not HG.client_controller.gui or not HG.client_controller.gui._menubar:
|
|
|
|
return
|
|
|
|
menubar = HG.client_controller.gui._menubar
|
|
|
|
# helper function to traverse menu and generate entries
|
|
# TODO: need to filter out menu items not suitable for display in locator
|
|
# (probably best to mark them when they are created and just check a property here)
|
|
# TODO: need special icon or secondary text for toggle-able items to see toggle state
|
|
def get_menu_items( menu: QW.QWidget, parent_name: str ) -> list:
|
|
|
|
result = []
|
|
|
|
for action in menu.actions():
|
|
|
|
actionText = action.text().replace( "&", "" )
|
|
if action.menu():
|
|
|
|
new_parent_name = parent_name + " | " + actionText if parent_name else actionText
|
|
|
|
result.extend( get_menu_items( action.menu(), new_parent_name ) )
|
|
|
|
else:
|
|
|
|
if not query in action.text() and not query in actionText:
|
|
|
|
continue
|
|
|
|
|
|
primary_text = highlight_result_text( actionText, query )
|
|
secondary_text = escape( parent_name )
|
|
|
|
normal_png = 'lightning.png'
|
|
toggled = False
|
|
toggled_png = 'lightning.png'
|
|
|
|
if action.isCheckable():
|
|
|
|
toggled = action.isChecked()
|
|
|
|
normal_png = 'lightning_unchecked.png'
|
|
toggled_png = 'lightning_checked.png'
|
|
|
|
|
|
result.append( QLocatorSearchResult( self.result_id_counter, normal_png, normal_png, True, [ primary_text, secondary_text ], toggled, toggled_png, toggled_png ) )
|
|
|
|
self.result_ids_to_actions[ self.result_id_counter ] = action
|
|
|
|
self.result_id_counter += 1
|
|
|
|
return result
|
|
|
|
menu_data = get_menu_items( menubar, '' )
|
|
|
|
if menu_data:
|
|
|
|
self.resultsAvailable.emit( jobID, menu_data )
|
|
|
|
|
|
def stopJobs( self, jobs ):
|
|
|
|
self.result_ids_to_actions = {}
|
|
|
|
|
|
def hideTitle( self ):
|
|
|
|
return False
|
|
|
|
|
|
def titleIconPath( self ):
|
|
|
|
return str() #TODO fill this in
|
|
|
|
|
|
class MediaMenuSearchProvider( QAbstractLocatorSearchProvider ):
|
|
|
|
def __init__( self, parent = None ):
|
|
|
|
super().__init__( parent )
|
|
|
|
self.result_id_counter = 0
|
|
self.result_ids_to_actions = {}
|
|
self.menu = None
|
|
|
|
|
|
def title( self ):
|
|
|
|
return "Media"
|
|
|
|
|
|
def suggestedReservedItemCount( self ):
|
|
|
|
return 64
|
|
|
|
|
|
def resultSelected( self, resultID: int ):
|
|
|
|
action = self.result_ids_to_actions.get( resultID, None )
|
|
|
|
if action:
|
|
|
|
action.trigger()
|
|
|
|
self.result_ids_to_actions = {}
|
|
self.menu = None
|
|
|
|
|
|
def processQuery( self, query: str, context, jobID: int ):
|
|
|
|
if not HG.client_controller.new_options.GetBoolean( 'advanced_mode' ):
|
|
|
|
return
|
|
|
|
|
|
if len( query ) < 3:
|
|
|
|
return
|
|
|
|
self.result_ids_to_pages = {}
|
|
self.menu = None
|
|
|
|
if not HG.client_controller.gui or not HG.client_controller.gui._notebook:
|
|
|
|
return
|
|
|
|
media_page = HG.client_controller.gui._notebook.GetCurrentMediaPage()
|
|
|
|
if not media_page or not media_page._media_panel:
|
|
|
|
return
|
|
|
|
self.menu = media_page._media_panel.ShowMenu( True )
|
|
|
|
# helper function to traverse menu and generate entries
|
|
# TODO: need to filter out menu items not suitable for display in locator
|
|
# (probably best to mark them when they are created and just check a property here)
|
|
# TODO: need special icon or secondary text for toggle-able items to see toggle state
|
|
def get_menu_items( menu: QW.QWidget, parent_name: str ) -> list:
|
|
|
|
result = []
|
|
|
|
for action in menu.actions():
|
|
|
|
actionText = action.text().replace( "&", "" )
|
|
if action.menu():
|
|
|
|
new_parent_name = parent_name + " | " + actionText if parent_name else actionText
|
|
|
|
result.extend( get_menu_items( action.menu(), new_parent_name ) )
|
|
|
|
else:
|
|
|
|
if not query in action.text() and not query in actionText:
|
|
|
|
continue
|
|
|
|
primary_text = highlight_result_text( actionText, query )
|
|
secondary_text = escape( parent_name )
|
|
|
|
result.append( QLocatorSearchResult( self.result_id_counter, 'images.png', 'images.png', True, [ primary_text, secondary_text ] ) )
|
|
|
|
self.result_ids_to_actions[ self.result_id_counter ] = action
|
|
|
|
self.result_id_counter += 1
|
|
|
|
return result
|
|
|
|
menu_data = get_menu_items( self.menu, '' )
|
|
|
|
if menu_data:
|
|
|
|
self.resultsAvailable.emit( jobID, menu_data )
|
|
|
|
|
|
def stopJobs( self, jobs ):
|
|
|
|
self.result_ids_to_actions = {}
|
|
self.menu = {}
|
|
|
|
|
|
def hideTitle( self ):
|
|
|
|
return False
|
|
|
|
|
|
def titleIconPath( self ):
|
|
|
|
return str() #TODO fill this in
|
|
|
|
# TODO: provider for page tab right click menu actions?
|
|
|