hydrus/hydrus/client/gui/search/ClientGUIACDropdown.py

3015 lines
106 KiB
Python
Raw Normal View History

2016-09-21 19:54:04 +00:00
import collections
import itertools
2020-02-12 22:50:37 +00:00
import os
2020-03-11 21:52:11 +00:00
import typing
2020-04-22 21:00:35 +00:00
2019-11-14 03:56:30 +00:00
from qtpy import QtCore as QC
from qtpy import QtWidgets as QW
2020-04-22 21:00:35 +00:00
from hydrus.core import HydrusConstants as HC
from hydrus.core import HydrusData
from hydrus.core import HydrusExceptions
from hydrus.core import HydrusGlobals as HG
from hydrus.core import HydrusTags
from hydrus.core import HydrusText
2020-07-29 20:52:44 +00:00
2021-01-27 22:14:03 +00:00
from hydrus.client import ClientApplicationCommand as CAC
2020-04-22 21:00:35 +00:00
from hydrus.client import ClientConstants as CC
2022-01-19 21:28:59 +00:00
from hydrus.client import ClientLocation
2020-04-22 21:00:35 +00:00
from hydrus.client import ClientSearch
2022-07-13 21:35:17 +00:00
from hydrus.client import ClientSearchParseSystemPredicates
2020-04-22 21:00:35 +00:00
from hydrus.client import ClientThreading
from hydrus.client.gui import ClientGUICore as CGC
from hydrus.client.gui import ClientGUIFunctions
from hydrus.client.gui import ClientGUIMenus
from hydrus.client.gui import ClientGUIScrolledPanels
from hydrus.client.gui import ClientGUIShortcuts
2020-04-29 21:44:12 +00:00
from hydrus.client.gui import ClientGUITopLevelWindowsPanels
2020-04-22 21:00:35 +00:00
from hydrus.client.gui import QtPorting as QP
2020-07-15 20:52:09 +00:00
from hydrus.client.gui.lists import ClientGUIListBoxes
2021-06-02 21:59:19 +00:00
from hydrus.client.gui.pages import ClientGUIResultsSortCollect
2022-03-23 20:57:10 +00:00
from hydrus.client.gui.search import ClientGUILocation
2020-11-25 22:22:47 +00:00
from hydrus.client.gui.search import ClientGUISearch
2021-03-17 21:59:28 +00:00
from hydrus.client.gui.widgets import ClientGUICommon
from hydrus.client.metadata import ClientTags
2020-07-29 20:52:44 +00:00
2020-04-22 21:00:35 +00:00
from hydrus.external import LogicExpressionQueryParser
2018-01-31 22:58:15 +00:00
2019-07-17 22:10:19 +00:00
def AppendLoadingPredicate( predicates ):
2020-03-11 21:52:11 +00:00
predicates.append( ClientSearch.Predicate( predicate_type = ClientSearch.PREDICATE_TYPE_LABEL, value = 'loading results\u2026' ) )
2019-07-17 22:10:19 +00:00
2020-04-22 21:00:35 +00:00
def InsertOtherPredicatesForRead( predicates: list, parsed_autocomplete_text: ClientSearch.ParsedAutocompleteText, include_unusual_predicate_types: bool, under_construction_or_predicate: typing.Optional[ ClientSearch.Predicate ] ):
2019-07-17 22:10:19 +00:00
2020-04-22 21:00:35 +00:00
if include_unusual_predicate_types:
2019-07-17 22:10:19 +00:00
2020-04-22 21:00:35 +00:00
non_tag_predicates = list( parsed_autocomplete_text.GetNonTagFileSearchPredicates() )
2019-07-17 22:10:19 +00:00
2020-04-22 21:00:35 +00:00
non_tag_predicates.reverse()
2019-07-17 22:10:19 +00:00
2020-04-22 21:00:35 +00:00
for predicate in non_tag_predicates:
2019-07-17 22:10:19 +00:00
2020-04-22 21:00:35 +00:00
PutAtTopOfMatches( predicates, predicate )
2019-07-17 22:10:19 +00:00
2019-09-05 00:05:32 +00:00
if under_construction_or_predicate is not None:
2020-04-22 21:00:35 +00:00
PutAtTopOfMatches( predicates, under_construction_or_predicate )
2019-07-17 22:10:19 +00:00
2020-04-22 21:00:35 +00:00
def InsertTagPredicates( predicates: list, tag_service_key: bytes, parsed_autocomplete_text: ClientSearch.ParsedAutocompleteText, insert_if_does_not_exist: bool = True ):
2019-07-17 22:10:19 +00:00
2020-04-22 21:00:35 +00:00
if parsed_autocomplete_text.IsTagSearch():
2019-07-17 22:10:19 +00:00
2020-04-22 21:00:35 +00:00
tag_predicate = parsed_autocomplete_text.GetImmediateFileSearchPredicate()
2019-07-17 22:10:19 +00:00
2020-09-09 20:59:19 +00:00
actual_tag = tag_predicate.GetValue()
2019-07-17 22:10:19 +00:00
2020-09-09 20:59:19 +00:00
ideal_predicate = None
other_matching_predicates = []
for predicate in predicates:
# this works due to __hash__
if predicate == tag_predicate:
ideal_predicate = predicate.GetIdealPredicate()
continue
matchable_search_texts = predicate.GetMatchableSearchTexts()
if len( matchable_search_texts ) <= 1:
continue
if actual_tag in matchable_search_texts:
other_matching_predicates.append( predicate )
for predicate in other_matching_predicates:
2019-07-17 22:10:19 +00:00
2020-04-22 21:00:35 +00:00
PutAtTopOfMatches( predicates, predicate, insert_if_does_not_exist = insert_if_does_not_exist )
2019-07-17 22:10:19 +00:00
2020-04-22 21:00:35 +00:00
PutAtTopOfMatches( predicates, tag_predicate, insert_if_does_not_exist = insert_if_does_not_exist )
2020-09-09 20:59:19 +00:00
if ideal_predicate is not None:
PutAtTopOfMatches( predicates, ideal_predicate, insert_if_does_not_exist = insert_if_does_not_exist )
2019-07-17 22:10:19 +00:00
2020-05-20 21:36:02 +00:00
def ReadFetch(
win: QW.QWidget,
job_key: ClientThreading.JobKey,
results_callable,
parsed_autocomplete_text: ClientSearch.ParsedAutocompleteText,
qt_media_callable,
file_search_context: ClientSearch.FileSearchContext,
synchronised,
include_unusual_predicate_types,
results_cache: ClientSearch.PredicateResultsCache,
under_construction_or_predicate,
force_system_everything
):
2019-01-30 22:14:54 +00:00
2022-07-20 19:17:03 +00:00
tag_context = file_search_context.GetTagContext()
2020-03-11 21:52:11 +00:00
2022-07-20 19:17:03 +00:00
tag_service_key = tag_context.service_key
2020-03-11 21:52:11 +00:00
2020-05-20 21:36:02 +00:00
if not parsed_autocomplete_text.IsAcceptableForTagSearches():
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
if parsed_autocomplete_text.IsEmpty():
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
cache_valid = isinstance( results_cache, ClientSearch.PredicateResultsCacheSystem )
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
we_need_results = not cache_valid
2019-08-15 00:40:48 +00:00
2019-01-30 22:14:54 +00:00
db_not_going_to_hang_if_we_hit_it = not HG.client_controller.DBCurrentlyDoingJob()
2020-04-22 21:00:35 +00:00
if we_need_results or db_not_going_to_hang_if_we_hit_it:
2019-01-30 22:14:54 +00:00
predicates = HG.client_controller.Read( 'file_system_predicates', file_search_context, force_system_everything = force_system_everything )
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
results_cache = ClientSearch.PredicateResultsCacheSystem( predicates )
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
matches = predicates
2019-08-15 00:40:48 +00:00
else:
2020-04-22 21:00:35 +00:00
matches = results_cache.GetPredicates()
2019-08-15 00:40:48 +00:00
2019-01-30 22:14:54 +00:00
else:
2020-04-22 21:00:35 +00:00
# if the user inputs '-' or 'creator:' or similar, let's go to an empty list
2019-01-30 22:14:54 +00:00
matches = []
else:
2020-04-22 21:00:35 +00:00
fetch_from_db = True
2019-01-30 22:14:54 +00:00
2021-10-06 20:59:30 +00:00
if synchronised and qt_media_callable is not None and not file_search_context.GetSystemPredicates().HasSystemLimit():
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
try:
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
media = HG.client_controller.CallBlockingToQt( win, qt_media_callable )
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
except HydrusExceptions.QtDeadWindowException:
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
return
2019-07-24 21:39:02 +00:00
2020-04-22 21:00:35 +00:00
if job_key.IsCancelled():
return
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
media_available_and_good = media is not None and len( media ) > 0
if media_available_and_good:
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
fetch_from_db = False
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
strict_search_text = parsed_autocomplete_text.GetSearchText( False )
autocomplete_search_text = parsed_autocomplete_text.GetSearchText( True )
if fetch_from_db:
is_explicit_wildcard = parsed_autocomplete_text.IsExplicitWildcard()
2020-12-23 23:07:58 +00:00
small_exact_match_search = ShouldDoExactSearch( parsed_autocomplete_text )
2020-04-22 21:00:35 +00:00
matches = []
if small_exact_match_search:
2019-01-30 22:14:54 +00:00
2020-12-16 22:29:51 +00:00
if not results_cache.CanServeTagResults( parsed_autocomplete_text, True ):
2019-01-30 22:14:54 +00:00
2022-09-28 17:15:23 +00:00
predicates = HG.client_controller.Read( 'autocomplete_predicates', ClientTags.TAG_DISPLAY_ACTUAL, file_search_context, search_text = strict_search_text, exact_match = True, inclusive = parsed_autocomplete_text.inclusive, job_key = job_key )
2019-01-30 22:14:54 +00:00
2020-05-20 21:36:02 +00:00
results_cache = ClientSearch.PredicateResultsCacheTag( predicates, strict_search_text, True )
2019-01-30 22:14:54 +00:00
2020-05-20 21:36:02 +00:00
matches = results_cache.FilterPredicates( tag_service_key, strict_search_text )
2019-01-30 22:14:54 +00:00
else:
2020-04-22 21:00:35 +00:00
if is_explicit_wildcard:
cache_valid = False
else:
2020-12-16 22:29:51 +00:00
cache_valid = results_cache.CanServeTagResults( parsed_autocomplete_text, False )
2020-04-22 21:00:35 +00:00
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
if cache_valid:
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
matches = results_cache.FilterPredicates( tag_service_key, autocomplete_search_text )
else:
2020-05-20 21:36:02 +00:00
search_namespaces_into_full_tags = parsed_autocomplete_text.GetTagAutocompleteOptions().SearchNamespacesIntoFullTags()
2022-09-28 17:15:23 +00:00
predicates = HG.client_controller.Read( 'autocomplete_predicates', ClientTags.TAG_DISPLAY_ACTUAL, file_search_context, search_text = autocomplete_search_text, inclusive = parsed_autocomplete_text.inclusive, job_key = job_key, search_namespaces_into_full_tags = search_namespaces_into_full_tags )
2020-04-22 21:00:35 +00:00
2020-09-09 20:59:19 +00:00
if job_key.IsCancelled():
return
2020-04-22 21:00:35 +00:00
if is_explicit_wildcard:
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
matches = ClientSearch.FilterPredicatesBySearchText( tag_service_key, autocomplete_search_text, predicates )
2019-01-30 22:14:54 +00:00
else:
2020-04-22 21:00:35 +00:00
results_cache = ClientSearch.PredicateResultsCacheTag( predicates, strict_search_text, False )
matches = results_cache.FilterPredicates( tag_service_key, autocomplete_search_text )
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
if job_key.IsCancelled():
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
return
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
else:
2020-09-09 20:59:19 +00:00
if not isinstance( results_cache, ClientSearch.PredicateResultsCacheMedia ):
# it is possible that media will change between calls to this, so don't cache it
2020-04-22 21:00:35 +00:00
2020-09-09 20:59:19 +00:00
tags_managers = []
for m in media:
2019-01-30 22:14:54 +00:00
2020-09-09 20:59:19 +00:00
if m.IsCollection():
tags_managers.extend( m.GetSingletonsTagsManagers() )
else:
tags_managers.append( m.GetTagsManager() )
2019-01-30 22:14:54 +00:00
2020-09-09 20:59:19 +00:00
if job_key.IsCancelled():
2019-01-30 22:14:54 +00:00
2020-09-09 20:59:19 +00:00
return
2019-01-30 22:14:54 +00:00
2020-09-09 20:59:19 +00:00
current_tags_to_count = collections.Counter()
pending_tags_to_count = collections.Counter()
2019-07-24 21:39:02 +00:00
2022-07-20 19:17:03 +00:00
include_current_tags = tag_context.include_current_tags
include_pending_tags = tag_context.include_pending_tags
2019-07-24 21:39:02 +00:00
2020-09-09 20:59:19 +00:00
for group_of_tags_managers in HydrusData.SplitListIntoChunks( tags_managers, 1000 ):
2019-01-30 22:14:54 +00:00
2020-09-09 20:59:19 +00:00
if include_current_tags:
2020-10-21 22:22:10 +00:00
current_tags_to_count.update( itertools.chain.from_iterable( tags_manager.GetCurrent( tag_service_key, ClientTags.TAG_DISPLAY_ACTUAL ) for tags_manager in group_of_tags_managers ) )
2020-09-09 20:59:19 +00:00
2019-01-30 22:14:54 +00:00
2020-09-09 20:59:19 +00:00
if include_pending_tags:
2020-10-21 22:22:10 +00:00
pending_tags_to_count.update( itertools.chain.from_iterable( [ tags_manager.GetPending( tag_service_key, ClientTags.TAG_DISPLAY_ACTUAL ) for tags_manager in group_of_tags_managers ] ) )
2020-09-09 20:59:19 +00:00
2020-04-22 21:00:35 +00:00
2020-09-09 20:59:19 +00:00
if job_key.IsCancelled():
return
2020-04-22 21:00:35 +00:00
2019-01-30 22:14:54 +00:00
2020-09-09 20:59:19 +00:00
tags_to_do = set()
tags_to_do.update( current_tags_to_count.keys() )
tags_to_do.update( pending_tags_to_count.keys() )
tags_to_count = { tag : ( current_tags_to_count[ tag ], pending_tags_to_count[ tag ] ) for tag in tags_to_do }
2019-07-24 21:39:02 +00:00
if job_key.IsCancelled():
return
2022-07-20 19:17:03 +00:00
predicates = HG.client_controller.Read( 'media_predicates', tag_context, tags_to_count, parsed_autocomplete_text.inclusive, job_key = job_key )
2020-09-09 20:59:19 +00:00
results_cache = ClientSearch.PredicateResultsCacheMedia( predicates )
2020-04-22 21:00:35 +00:00
if job_key.IsCancelled():
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
return
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
2020-09-09 20:59:19 +00:00
predicates = results_cache.FilterPredicates( tag_service_key, autocomplete_search_text )
2020-08-27 01:00:42 +00:00
2020-04-22 21:00:35 +00:00
if job_key.IsCancelled():
return
2019-07-24 21:39:02 +00:00
2019-01-30 22:14:54 +00:00
2022-09-28 17:15:23 +00:00
predicates = ClientSearch.MergePredicates( predicates )
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
matches = predicates
matches = ClientSearch.SortPredicates( matches )
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
if not parsed_autocomplete_text.inclusive:
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
for match in matches:
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
match.SetInclusive( False )
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
InsertTagPredicates( matches, tag_service_key, parsed_autocomplete_text, insert_if_does_not_exist = False )
InsertOtherPredicatesForRead( matches, parsed_autocomplete_text, include_unusual_predicate_types, under_construction_or_predicate )
2019-04-10 22:50:53 +00:00
2019-01-30 22:14:54 +00:00
if job_key.IsCancelled():
return
2021-06-23 21:11:38 +00:00
HG.client_controller.CallAfterQtSafe( win, 'read a/c fetch', results_callable, job_key, parsed_autocomplete_text, results_cache, matches )
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
def PutAtTopOfMatches( matches: list, predicate: ClientSearch.Predicate, insert_if_does_not_exist: bool = True ):
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
# we have to be careful here to preserve autocomplete counts!
# if it already exists, we move it up, do not replace with the test pred param
if predicate in matches:
2019-01-30 22:14:54 +00:00
index = matches.index( predicate )
2020-04-22 21:00:35 +00:00
predicate_to_insert = matches[ index ]
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
del matches[ index ]
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
matches.insert( 0, predicate_to_insert )
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
else:
if insert_if_does_not_exist:
matches.insert( 0, predicate )
2019-01-30 22:14:54 +00:00
2020-12-23 23:07:58 +00:00
def ShouldDoExactSearch( parsed_autocomplete_text: ClientSearch.ParsedAutocompleteText ):
2019-07-17 22:10:19 +00:00
2020-12-23 23:07:58 +00:00
if parsed_autocomplete_text.IsExplicitWildcard():
2019-07-17 22:10:19 +00:00
return False
2020-12-23 23:07:58 +00:00
strict_search_text = parsed_autocomplete_text.GetSearchText( False )
2019-07-17 22:10:19 +00:00
2020-12-23 23:07:58 +00:00
exact_match_character_threshold = parsed_autocomplete_text.GetTagAutocompleteOptions().GetExactMatchCharacterThreshold()
if exact_match_character_threshold is None:
2019-07-17 22:10:19 +00:00
return False
2020-12-23 23:07:58 +00:00
if ':' in strict_search_text:
2019-07-17 22:10:19 +00:00
2020-12-23 23:07:58 +00:00
( namespace, test_text ) = HydrusTags.SplitTag( strict_search_text )
2019-07-17 22:10:19 +00:00
else:
2020-12-23 23:07:58 +00:00
test_text = strict_search_text
if len( test_text ) == 0:
return False
2019-07-17 22:10:19 +00:00
2020-12-23 23:07:58 +00:00
return len( test_text ) <= exact_match_character_threshold
2019-07-17 22:10:19 +00:00
2022-01-12 22:14:50 +00:00
def WriteFetch( win, job_key, results_callable, parsed_autocomplete_text: ClientSearch.ParsedAutocompleteText, file_search_context: ClientSearch.FileSearchContext, results_cache: ClientSearch.PredicateResultsCache ):
2022-07-20 19:17:03 +00:00
tag_context = file_search_context.GetTagContext()
2019-01-30 22:14:54 +00:00
2022-07-20 19:17:03 +00:00
display_tag_service_key = tag_context.display_service_key
2020-03-11 21:52:11 +00:00
2020-05-20 21:36:02 +00:00
if not parsed_autocomplete_text.IsAcceptableForTagSearches():
2019-01-30 22:14:54 +00:00
matches = []
else:
2020-04-22 21:00:35 +00:00
is_explicit_wildcard = parsed_autocomplete_text.IsExplicitWildcard()
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
strict_search_text = parsed_autocomplete_text.GetSearchText( False )
autocomplete_search_text = parsed_autocomplete_text.GetSearchText( True )
2020-12-23 23:07:58 +00:00
small_exact_match_search = ShouldDoExactSearch( parsed_autocomplete_text )
2020-04-22 21:00:35 +00:00
if small_exact_match_search:
2019-01-30 22:14:54 +00:00
2020-12-16 22:29:51 +00:00
if not results_cache.CanServeTagResults( parsed_autocomplete_text, True ):
2020-04-22 21:00:35 +00:00
2022-09-28 17:15:23 +00:00
predicates = HG.client_controller.Read( 'autocomplete_predicates', ClientTags.TAG_DISPLAY_STORAGE, file_search_context, search_text = strict_search_text, exact_match = True, job_key = job_key )
2020-04-22 21:00:35 +00:00
2020-05-20 21:36:02 +00:00
results_cache = ClientSearch.PredicateResultsCacheTag( predicates, strict_search_text, True )
2020-04-22 21:00:35 +00:00
2019-01-30 22:14:54 +00:00
2020-06-11 12:01:08 +00:00
matches = results_cache.FilterPredicates( display_tag_service_key, strict_search_text )
2020-05-20 21:36:02 +00:00
2019-01-30 22:14:54 +00:00
else:
2020-04-22 21:00:35 +00:00
if is_explicit_wildcard:
cache_valid = False
else:
2020-12-16 22:29:51 +00:00
cache_valid = results_cache.CanServeTagResults( parsed_autocomplete_text, False )
2020-04-22 21:00:35 +00:00
2019-08-15 00:40:48 +00:00
2020-04-22 21:00:35 +00:00
if cache_valid:
2019-01-30 22:14:54 +00:00
2020-06-11 12:01:08 +00:00
matches = results_cache.FilterPredicates( display_tag_service_key, autocomplete_search_text )
2019-01-30 22:14:54 +00:00
2019-08-15 00:40:48 +00:00
else:
2020-05-20 21:36:02 +00:00
search_namespaces_into_full_tags = parsed_autocomplete_text.GetTagAutocompleteOptions().SearchNamespacesIntoFullTags()
2022-09-28 17:15:23 +00:00
predicates = HG.client_controller.Read( 'autocomplete_predicates', ClientTags.TAG_DISPLAY_STORAGE, file_search_context, search_text = autocomplete_search_text, job_key = job_key, search_namespaces_into_full_tags = search_namespaces_into_full_tags )
2019-08-15 00:40:48 +00:00
2020-04-22 21:00:35 +00:00
if is_explicit_wildcard:
2020-06-11 12:01:08 +00:00
matches = ClientSearch.FilterPredicatesBySearchText( display_tag_service_key, autocomplete_search_text, predicates )
2019-08-15 00:40:48 +00:00
2020-04-22 21:00:35 +00:00
else:
2019-08-15 00:40:48 +00:00
2020-04-22 21:00:35 +00:00
results_cache = ClientSearch.PredicateResultsCacheTag( predicates, strict_search_text, False )
2020-06-11 12:01:08 +00:00
matches = results_cache.FilterPredicates( display_tag_service_key, autocomplete_search_text )
2019-08-15 00:40:48 +00:00
2019-01-30 22:14:54 +00:00
2020-12-02 22:04:38 +00:00
if not is_explicit_wildcard:
# this lets us get sibling data for tags that do not exist with count in the domain
# we always do this, because results cache will not have current text input data
2022-09-28 17:15:23 +00:00
input_text_predicates = HG.client_controller.Read( 'autocomplete_predicates', ClientTags.TAG_DISPLAY_STORAGE, file_search_context, search_text = strict_search_text, exact_match = True, zero_count_ok = True, job_key = job_key )
2020-12-02 22:04:38 +00:00
for input_text_predicate in input_text_predicates:
if ( input_text_predicate.HasIdealSibling() or input_text_predicate.HasParentPredicates() ) and input_text_predicate not in matches:
matches.append( input_text_predicate )
2020-04-22 21:00:35 +00:00
matches = ClientSearch.SortPredicates( matches )
2020-06-11 12:01:08 +00:00
InsertTagPredicates( matches, display_tag_service_key, parsed_autocomplete_text )
2019-07-17 22:10:19 +00:00
2021-06-23 21:11:38 +00:00
HG.client_controller.CallAfterQtSafe( win, 'write a/c fetch', results_callable, job_key, parsed_autocomplete_text, results_cache, matches )
2019-01-30 22:14:54 +00:00
2021-02-24 22:35:18 +00:00
class ListBoxTagsPredicatesAC( ClientGUIListBoxes.ListBoxTagsPredicates ):
2020-04-29 21:44:12 +00:00
def __init__( self, parent, callable, service_key, float_mode, **kwargs ):
ClientGUIListBoxes.ListBoxTagsPredicates.__init__( self, parent, **kwargs )
self._callable = callable
self._service_key = service_key
self._float_mode = float_mode
self._predicates = {}
2021-11-10 21:53:57 +00:00
def _Activate( self, ctrl_down, shift_down ) -> bool:
2020-04-29 21:44:12 +00:00
2021-02-11 01:59:52 +00:00
predicates = self._GetPredicatesFromTerms( self._selected_terms )
2020-04-29 21:44:12 +00:00
if self._float_mode:
2022-10-12 20:18:22 +00:00
widget = self.window()
2021-12-08 22:40:59 +00:00
2020-04-29 21:44:12 +00:00
else:
widget = self
predicates = ClientGUISearch.FleshOutPredicates( widget, predicates )
if len( predicates ) > 0:
self._callable( predicates, shift_down )
2020-11-18 22:15:21 +00:00
return True
return False
2020-04-29 21:44:12 +00:00
2021-02-24 22:35:18 +00:00
def _GenerateTermFromPredicate( self, predicate: ClientSearch.Predicate ):
term = ClientGUIListBoxes.ListBoxTagsPredicates._GenerateTermFromPredicate( self, predicate )
if predicate.GetType() == ClientSearch.PREDICATE_TYPE_OR_CONTAINER:
term.SetORUnderConstruction( True )
return term
2020-04-29 21:44:12 +00:00
def SetPredicates( self, predicates ):
# need to do a clever compare, since normal predicate compare doesn't take count into account
they_are_the_same = True
if len( predicates ) == len( self._predicates ):
for index in range( len( predicates ) ):
p_1 = predicates[ index ]
p_2 = self._predicates[ index ]
if p_1 != p_2 or p_1.GetCount() != p_2.GetCount():
they_are_the_same = False
break
else:
they_are_the_same = False
if not they_are_the_same:
# important to make own copy, as same object originals can be altered (e.g. set non-inclusive) in cache, and we need to notice that change just above
self._predicates = [ predicate.GetCopy() for predicate in predicates ]
self._Clear()
2021-02-11 01:59:52 +00:00
terms = [ self._GenerateTermFromPredicate( predicate ) for predicate in predicates ]
self._AppendTerms( terms )
2020-04-29 21:44:12 +00:00
self._DataHasChanged()
if len( predicates ) > 0:
2021-02-11 01:59:52 +00:00
logical_index = 0
2020-04-29 21:44:12 +00:00
if len( predicates ) > 1:
skip_ors = True
2022-01-12 22:14:50 +00:00
some_preds_have_count = True in ( predicate.GetCount().HasNonZeroCount() for predicate in predicates )
2020-05-13 19:03:16 +00:00
skip_countless = HG.client_controller.new_options.GetBoolean( 'ac_select_first_with_count' ) and some_preds_have_count
2020-04-29 21:44:12 +00:00
for ( index, predicate ) in enumerate( predicates ):
# now only apply this to simple tags, not wildcards and system tags
if skip_ors and predicate.GetType() == ClientSearch.PREDICATE_TYPE_OR_CONTAINER:
continue
2022-01-12 22:14:50 +00:00
if skip_countless and predicate.GetType() in ( ClientSearch.PREDICATE_TYPE_PARENT, ClientSearch.PREDICATE_TYPE_TAG ) and predicate.GetCount().HasZeroCount():
2020-04-29 21:44:12 +00:00
continue
2021-02-11 01:59:52 +00:00
logical_index = index
2020-04-29 21:44:12 +00:00
break
2021-02-11 01:59:52 +00:00
self._Hit( False, False, logical_index )
2020-04-29 21:44:12 +00:00
2021-02-24 22:35:18 +00:00
def SetTagServiceKey( self, service_key: bytes ):
2020-04-29 21:44:12 +00:00
self._service_key = service_key
2021-02-24 22:35:18 +00:00
class ListBoxTagsStringsAC( ClientGUIListBoxes.ListBoxTagsStrings ):
2020-04-29 21:44:12 +00:00
2021-02-24 22:35:18 +00:00
def __init__( self, parent, callable, service_key, float_mode, **kwargs ):
2020-04-29 21:44:12 +00:00
2021-02-24 22:35:18 +00:00
ClientGUIListBoxes.ListBoxTagsStrings.__init__( self, parent, service_key = service_key, sort_tags = False, **kwargs )
2020-04-29 21:44:12 +00:00
2021-02-24 22:35:18 +00:00
self._callable = callable
self._float_mode = float_mode
2021-11-10 21:53:57 +00:00
def _Activate( self, ctrl_down, shift_down ) -> bool:
2021-02-24 22:35:18 +00:00
predicates = self._GetPredicatesFromTerms( self._selected_terms )
if self._float_mode:
2021-02-11 01:59:52 +00:00
2022-10-12 20:18:22 +00:00
widget = self.window()
2021-02-24 22:35:18 +00:00
else:
widget = self
2021-02-11 01:59:52 +00:00
2021-02-24 22:35:18 +00:00
predicates = ClientGUISearch.FleshOutPredicates( widget, predicates )
2020-04-29 21:44:12 +00:00
2021-02-24 22:35:18 +00:00
if len( predicates ) > 0:
self._callable( predicates, shift_down )
return True
2020-04-29 21:44:12 +00:00
2021-02-24 22:35:18 +00:00
return False
2020-04-29 21:44:12 +00:00
2022-10-12 20:18:22 +00:00
2022-05-04 21:40:27 +00:00
class AutoCompleteDropdown( QW.QWidget, CAC.ApplicationCommandProcessorMixin ):
2019-11-14 03:56:30 +00:00
2022-05-25 21:30:53 +00:00
movePageLeft = QC.Signal()
movePageRight = QC.Signal()
2019-11-14 03:56:30 +00:00
showNext = QC.Signal()
showPrevious = QC.Signal()
2016-09-21 19:54:04 +00:00
def __init__( self, parent ):
2022-05-04 21:40:27 +00:00
CAC.ApplicationCommandProcessorMixin.__init__( self )
2019-11-14 03:56:30 +00:00
QW.QWidget.__init__( self, parent )
2016-09-21 19:54:04 +00:00
2021-01-27 22:14:03 +00:00
self._can_intercept_unusual_key_events = True
2016-09-21 19:54:04 +00:00
2019-11-14 03:56:30 +00:00
if self.window() == HG.client_controller.gui:
2016-09-21 19:54:04 +00:00
2019-11-14 03:56:30 +00:00
use_float_mode = HG.client_controller.new_options.GetBoolean( 'autocomplete_float_main_gui' )
2016-09-21 19:54:04 +00:00
else:
2022-10-12 20:18:22 +00:00
use_float_mode = False
2016-09-21 19:54:04 +00:00
2019-11-14 03:56:30 +00:00
self._float_mode = use_float_mode
2022-10-12 20:18:22 +00:00
self._temporary_focus_widget = None
2019-11-14 03:56:30 +00:00
2020-03-11 21:52:11 +00:00
self._text_input_panel = QW.QWidget( self )
self._text_ctrl = QW.QLineEdit( self._text_input_panel )
2016-09-21 19:54:04 +00:00
self.setFocusProxy( self._text_ctrl )
2017-09-20 19:47:31 +00:00
self._UpdateBackgroundColour()
2016-09-21 19:54:04 +00:00
self._last_attempted_dropdown_width = 0
2019-11-14 03:56:30 +00:00
self._text_ctrl_widget_event_filter = QP.WidgetEventFilter( self._text_ctrl )
self._text_ctrl.textChanged.connect( self.EventText )
2019-11-28 01:11:46 +00:00
2019-11-14 03:56:30 +00:00
self._text_ctrl_widget_event_filter.EVT_KEY_DOWN( self.keyPressFilter )
2016-09-21 19:54:04 +00:00
2020-02-19 21:48:36 +00:00
self._text_ctrl.installEventFilter( self )
2016-09-21 19:54:04 +00:00
2020-03-18 21:35:57 +00:00
self._main_vbox = QP.VBoxLayout( margin = 0 )
self._SetupTopListBox()
2019-06-26 21:27:18 +00:00
2019-11-14 03:56:30 +00:00
self._text_input_hbox = QP.HBoxLayout()
2019-06-26 21:27:18 +00:00
2020-07-29 20:52:44 +00:00
QP.AddToLayout( self._text_input_hbox, self._text_ctrl, CC.FLAGS_CENTER_PERPENDICULAR_EXPAND_DEPTH )
2016-09-21 19:54:04 +00:00
2020-03-11 21:52:11 +00:00
self._text_input_panel.setLayout( self._text_input_hbox )
2020-03-18 21:35:57 +00:00
QP.AddToLayout( self._main_vbox, self._text_input_panel, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
2016-09-21 19:54:04 +00:00
if self._float_mode:
2022-10-12 20:18:22 +00:00
# needs to have bigger parent in order to draw fully, otherwise it is clipped by our little panel box
p = self.parentWidget()
2016-09-21 19:54:04 +00:00
2022-10-12 20:18:22 +00:00
# we don't want the .window() since that clusters all these a/cs as children of it. not beautiful, and page deletion won't delete them
# let's try and chase page
while not ( p is None or p == self.window() or isinstance( p.parentWidget(), QW.QTabWidget ) ):
p = p.parentWidget()
2019-12-05 05:29:32 +00:00
2022-10-12 20:18:22 +00:00
parent_to_use = p
self._dropdown_window = QW.QFrame( parent_to_use )
2016-09-21 19:54:04 +00:00
2019-11-14 03:56:30 +00:00
self._dropdown_window.setFrameStyle( QW.QFrame.Panel | QW.QFrame.Raised )
self._dropdown_window.setLineWidth( 2 )
2016-09-21 19:54:04 +00:00
2019-11-14 03:56:30 +00:00
self._dropdown_hidden = True
2016-09-21 19:54:04 +00:00
2020-04-01 21:51:42 +00:00
self._force_dropdown_hide = False
2022-10-12 20:18:22 +00:00
# We need this, or else if the QSS does not define a Widget background color (the default), these 'raised' windows are transparent lmao
self._dropdown_window.setAutoFillBackground( True )
self._dropdown_window.hide()
2016-09-21 19:54:04 +00:00
else:
2020-03-25 21:15:57 +00:00
self._dropdown_window = QW.QWidget( self )
2016-09-21 19:54:04 +00:00
2022-10-12 20:18:22 +00:00
QP.AddToLayout( self._main_vbox, self._dropdown_window, CC.FLAGS_EXPAND_BOTH_WAYS )
2020-02-19 21:48:36 +00:00
2019-11-14 03:56:30 +00:00
self._dropdown_notebook = QW.QTabWidget( self._dropdown_window )
2018-03-28 21:55:58 +00:00
#
self._search_results_list = self._InitSearchResultsList()
2019-11-14 03:56:30 +00:00
self._dropdown_notebook.setCurrentIndex( self._dropdown_notebook.addTab( self._search_results_list, 'results' ) )
2018-03-28 21:55:58 +00:00
#
2016-09-21 19:54:04 +00:00
2020-03-18 21:35:57 +00:00
self.setLayout( self._main_vbox )
2016-09-21 19:54:04 +00:00
2020-05-20 21:36:02 +00:00
self._current_list_parsed_autocomplete_text = self._GetParsedAutocompleteText()
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
self._results_cache: ClientSearch.PredicateResultsCache = ClientSearch.PredicateResultsCacheInit()
2016-09-21 19:54:04 +00:00
2019-01-30 22:14:54 +00:00
self._current_fetch_job_key = None
2020-04-01 21:51:42 +00:00
self._schedule_results_refresh_job = None
2018-01-03 22:37:30 +00:00
2021-01-27 22:14:03 +00:00
self._my_shortcut_handler = ClientGUIShortcuts.ShortcutsHandler( self, [ 'tags_autocomplete' ], alternate_filter_target = self._text_ctrl )
2016-09-21 19:54:04 +00:00
if self._float_mode:
2019-11-14 03:56:30 +00:00
self._widget_event_filter = QP.WidgetEventFilter( self )
self._widget_event_filter.EVT_MOVE( self.EventMove )
self._widget_event_filter.EVT_SIZE( self.EventMove )
2016-09-21 19:54:04 +00:00
parent = self
2019-11-14 03:56:30 +00:00
self._scroll_event_filters = []
2016-09-21 19:54:04 +00:00
while True:
try:
2019-11-14 03:56:30 +00:00
parent = parent.parentWidget()
2016-09-21 19:54:04 +00:00
2022-10-12 20:18:22 +00:00
if parent is None or parent == self.window():
2019-11-14 03:56:30 +00:00
2022-10-12 20:18:22 +00:00
break
2019-11-14 03:56:30 +00:00
2022-10-12 20:18:22 +00:00
if isinstance( parent, QW.QScrollArea ):
2016-09-21 19:54:04 +00:00
2022-10-12 20:18:22 +00:00
parent.verticalScrollBar().valueChanged.connect( self.ParentWasScrolled )
2016-09-21 19:54:04 +00:00
except:
break
2017-09-20 19:47:31 +00:00
HG.client_controller.sub( self, '_UpdateBackgroundColour', 'notify_new_colourset' )
2018-06-27 19:27:05 +00:00
HG.client_controller.sub( self, 'DoDropdownHideShow', 'notify_page_change' )
2017-09-20 19:47:31 +00:00
2020-04-01 21:51:42 +00:00
self._ScheduleResultsRefresh( 0.0 )
2016-09-21 19:54:04 +00:00
2021-06-23 21:11:38 +00:00
HG.client_controller.CallLaterQtSafe( self, 0.05, 'hide/show dropdown', self._DropdownHideShow )
2022-04-20 20:18:56 +00:00
# trying a second go to see if that improves some positioning
HG.client_controller.CallLaterQtSafe( self, 0.25, 'hide/show dropdown', self._DropdownHideShow )
2016-09-21 19:54:04 +00:00
2019-04-03 22:45:57 +00:00
def _BroadcastChoices( self, predicates, shift_down ):
2016-09-21 19:54:04 +00:00
raise NotImplementedError()
2020-04-01 21:51:42 +00:00
def _CancelSearchResultsFetchJob( self ):
2019-06-19 22:08:48 +00:00
if self._current_fetch_job_key is not None:
self._current_fetch_job_key.Cancel()
2019-08-07 22:59:53 +00:00
self._current_fetch_job_key = None
2019-06-19 22:08:48 +00:00
2020-04-01 21:51:42 +00:00
def _ClearInput( self ):
2018-02-14 21:47:18 +00:00
2020-04-01 21:51:42 +00:00
self._CancelSearchResultsFetchJob()
2018-02-14 21:47:18 +00:00
2020-04-01 21:51:42 +00:00
self._text_ctrl.blockSignals( True )
2019-07-17 22:10:19 +00:00
2020-12-23 23:07:58 +00:00
self._text_ctrl.clear()
2018-05-23 21:05:06 +00:00
2020-05-20 21:36:02 +00:00
self._SetResultsToList( [], self._GetParsedAutocompleteText() )
2020-04-22 21:00:35 +00:00
2020-04-01 21:51:42 +00:00
self._text_ctrl.blockSignals( False )
2019-04-10 22:50:53 +00:00
2020-04-01 21:51:42 +00:00
self._ScheduleResultsRefresh( 0.0 )
2018-05-23 21:05:06 +00:00
2020-05-20 21:36:02 +00:00
def _GetParsedAutocompleteText( self ) -> ClientSearch.ParsedAutocompleteText:
raise NotImplementedError()
2018-02-28 22:30:36 +00:00
def _DropdownHideShow( self ):
if not self._float_mode:
return
try:
if self._ShouldShow():
self._ShowDropdown()
else:
self._HideDropdown()
except:
raise
2019-04-10 22:50:53 +00:00
def _HandleEscape( self ):
2020-03-18 21:35:57 +00:00
if self._text_ctrl.text() != '':
self._ClearInput()
return True
elif self._float_mode:
2019-04-10 22:50:53 +00:00
2019-11-14 03:56:30 +00:00
self.parentWidget().setFocus( QC.Qt.OtherFocusReason )
2019-04-10 22:50:53 +00:00
return True
else:
return False
2016-09-21 19:54:04 +00:00
def _HideDropdown( self ):
if not self._dropdown_hidden:
2019-11-14 03:56:30 +00:00
self._dropdown_window.hide()
2016-09-21 19:54:04 +00:00
self._dropdown_hidden = True
2018-03-28 21:55:58 +00:00
def _InitSearchResultsList( self ):
raise NotImplementedError()
2021-07-28 21:12:00 +00:00
def _RestoreTextCtrlFocus( self ):
# if an event came from clicking the dropdown or stop or something, we want to put focus back on textctrl
current_focus_widget = QW.QApplication.focusWidget()
2021-07-28 21:12:00 +00:00
if ClientGUIFunctions.IsQtAncestor( current_focus_widget, self ):
ClientGUIFunctions.SetFocusLater( self._text_ctrl )
2021-07-28 21:12:00 +00:00
2020-04-01 21:51:42 +00:00
def _ScheduleResultsRefresh( self, delay ):
2018-02-14 21:47:18 +00:00
2020-04-01 21:51:42 +00:00
if self._schedule_results_refresh_job is not None:
2018-02-14 21:47:18 +00:00
2020-04-01 21:51:42 +00:00
self._schedule_results_refresh_job.Cancel()
2018-02-14 21:47:18 +00:00
2021-06-23 21:11:38 +00:00
self._schedule_results_refresh_job = HG.client_controller.CallLaterQtSafe( self, delay, 'a/c results refresh', self._UpdateSearchResults )
2020-04-01 21:51:42 +00:00
2018-02-14 21:47:18 +00:00
2020-03-18 21:35:57 +00:00
def _SetupTopListBox( self ):
pass
2018-02-14 21:47:18 +00:00
def _SetListDirty( self ):
2020-04-22 21:00:35 +00:00
self._results_cache = ClientSearch.PredicateResultsCacheInit()
2018-02-14 21:47:18 +00:00
2020-04-01 21:51:42 +00:00
self._ScheduleResultsRefresh( 0.0 )
2018-02-14 21:47:18 +00:00
2020-04-22 21:00:35 +00:00
def _SetResultsToList( self, results, parsed_autocomplete_text ):
2019-01-30 22:14:54 +00:00
raise NotImplementedError()
2016-09-21 19:54:04 +00:00
def _ShouldShow( self ):
2020-04-01 21:51:42 +00:00
if self._force_dropdown_hide:
return False
2020-02-19 21:48:36 +00:00
current_active_window = QW.QApplication.activeWindow()
2021-06-09 20:28:09 +00:00
i_am_active_and_focused = self.window() == current_active_window and self._text_ctrl.hasFocus() and not self.visibleRegion().isEmpty()
2016-09-21 19:54:04 +00:00
2020-02-19 21:48:36 +00:00
dropdown_is_active = self._dropdown_window == current_active_window
2016-09-21 19:54:04 +00:00
2020-02-12 22:50:37 +00:00
focus_or_active_good = i_am_active_and_focused or dropdown_is_active
2016-09-21 19:54:04 +00:00
2020-02-12 22:50:37 +00:00
visible = self.isVisible()
return focus_or_active_good and visible
2016-09-21 19:54:04 +00:00
def _ShouldTakeResponsibilityForEnter( self ):
raise NotImplementedError()
def _ShowDropdown( self ):
2020-03-11 21:52:11 +00:00
text_panel_size = self._text_input_panel.size()
2020-02-26 22:28:52 +00:00
2020-03-11 21:52:11 +00:00
text_input_width = text_panel_size.width()
2016-09-21 19:54:04 +00:00
2020-03-11 21:52:11 +00:00
if self._text_input_panel.isVisible():
2016-09-21 19:54:04 +00:00
2022-10-12 20:18:22 +00:00
desired_dropdown_position = self.mapTo( self._dropdown_window.parent(), self._text_input_panel.geometry().bottomLeft() )
2016-09-21 19:54:04 +00:00
2022-04-20 20:18:56 +00:00
if self.pos() != desired_dropdown_position:
2017-05-31 21:50:53 +00:00
2019-11-14 03:56:30 +00:00
self._dropdown_window.move( desired_dropdown_position )
2017-05-31 21:50:53 +00:00
2016-09-21 19:54:04 +00:00
2022-10-12 20:18:22 +00:00
self._dropdown_window.raise_()
2016-09-21 19:54:04 +00:00
#
if self._dropdown_hidden:
2019-11-14 03:56:30 +00:00
self._dropdown_window.show()
2016-09-21 19:54:04 +00:00
2018-03-28 21:55:58 +00:00
self._dropdown_hidden = False
2016-09-21 19:54:04 +00:00
2020-03-11 21:52:11 +00:00
if text_input_width != self._last_attempted_dropdown_width:
2018-03-28 21:55:58 +00:00
2020-03-11 21:52:11 +00:00
self._dropdown_window.setFixedWidth( text_input_width )
2016-09-21 19:54:04 +00:00
2020-03-11 21:52:11 +00:00
self._last_attempted_dropdown_width = text_input_width
2016-09-21 19:54:04 +00:00
2022-10-12 20:18:22 +00:00
self._dropdown_window.adjustSize()
2016-09-21 19:54:04 +00:00
2020-04-01 21:51:42 +00:00
def _StartSearchResultsFetchJob( self, job_key ):
2019-01-30 22:14:54 +00:00
raise NotImplementedError()
2019-04-03 22:45:57 +00:00
def _TakeResponsibilityForEnter( self, shift_down ):
2016-09-21 19:54:04 +00:00
raise NotImplementedError()
2017-09-20 19:47:31 +00:00
def _UpdateBackgroundColour( self ):
2017-12-06 22:06:56 +00:00
colour = HG.client_controller.new_options.GetColour( CC.COLOUR_AUTOCOMPLETE_BACKGROUND )
2017-09-20 19:47:31 +00:00
2021-01-27 22:14:03 +00:00
if not self._can_intercept_unusual_key_events:
2017-09-20 19:47:31 +00:00
2020-05-20 21:36:02 +00:00
colour = ClientGUIFunctions.GetLighterDarkerColour( colour )
2017-09-20 19:47:31 +00:00
2019-11-14 03:56:30 +00:00
QP.SetBackgroundColour( self._text_ctrl, colour )
2017-09-20 19:47:31 +00:00
2019-11-14 03:56:30 +00:00
self._text_ctrl.update()
2017-09-20 19:47:31 +00:00
2020-04-01 21:51:42 +00:00
def _UpdateSearchResults( self ):
2016-09-21 19:54:04 +00:00
2020-04-01 21:51:42 +00:00
self._schedule_results_refresh_job = None
2019-01-30 22:14:54 +00:00
2020-04-01 21:51:42 +00:00
self._CancelSearchResultsFetchJob()
2019-01-30 22:14:54 +00:00
self._current_fetch_job_key = ClientThreading.JobKey( cancellable = True )
2020-04-01 21:51:42 +00:00
self._StartSearchResultsFetchJob( self._current_fetch_job_key )
2016-09-21 19:54:04 +00:00
2019-04-10 22:50:53 +00:00
def BroadcastChoices( self, predicates, shift_down = False ):
2019-04-03 22:45:57 +00:00
self._BroadcastChoices( predicates, shift_down )
2016-09-21 19:54:04 +00:00
2021-07-28 21:12:00 +00:00
self._RestoreTextCtrlFocus()
2016-09-21 19:54:04 +00:00
2019-06-19 22:08:48 +00:00
def CancelCurrentResultsFetchJob( self ):
2020-04-01 21:51:42 +00:00
self._CancelSearchResultsFetchJob()
2019-06-19 22:08:48 +00:00
2018-06-27 19:27:05 +00:00
def DoDropdownHideShow( self ):
self._DropdownHideShow()
2019-11-14 03:56:30 +00:00
def keyPressFilter( self, event ):
2016-09-21 19:54:04 +00:00
2017-05-10 21:33:58 +00:00
HG.client_controller.ResetIdleTimer()
2016-09-21 19:54:04 +00:00
2018-05-16 20:09:50 +00:00
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
2017-04-05 21:16:40 +00:00
2021-01-27 22:14:03 +00:00
if self._can_intercept_unusual_key_events:
2016-09-21 19:54:04 +00:00
2018-03-28 21:55:58 +00:00
send_input_to_current_list = False
2019-11-14 03:56:30 +00:00
current_results_list = self._dropdown_notebook.currentWidget()
2018-03-28 21:55:58 +00:00
2019-11-14 03:56:30 +00:00
if key in ( ord( 'A' ), ord( 'a' ) ) and modifier == QC.Qt.ControlModifier:
2016-09-21 19:54:04 +00:00
2019-11-14 03:56:30 +00:00
return True # was: event.ignore()
2016-09-21 19:54:04 +00:00
2019-11-14 03:56:30 +00:00
elif key in ( QC.Qt.Key_Return, QC.Qt.Key_Enter ) and self._ShouldTakeResponsibilityForEnter():
2016-09-21 19:54:04 +00:00
2019-11-14 03:56:30 +00:00
shift_down = modifier == QC.Qt.ShiftModifier
2019-04-03 22:45:57 +00:00
self._TakeResponsibilityForEnter( shift_down )
2016-09-21 19:54:04 +00:00
2020-03-18 21:35:57 +00:00
elif key == QC.Qt.Key_Escape:
escape_caught = self._HandleEscape()
if not escape_caught:
send_input_to_current_list = True
2016-09-21 19:54:04 +00:00
else:
2018-03-28 21:55:58 +00:00
send_input_to_current_list = True
if send_input_to_current_list:
2019-11-14 03:56:30 +00:00
current_results_list.keyPressEvent( event ) # ultimately, this typically ignores the event, letting the text ctrl take it
return not event.isAccepted()
2016-09-21 19:54:04 +00:00
else:
2019-11-14 03:56:30 +00:00
return True # was: event.ignore()
2016-09-21 19:54:04 +00:00
2017-04-19 20:58:30 +00:00
def EventCloseDropdown( self, event ):
2019-11-14 03:56:30 +00:00
HG.client_controller.gui.close()
2017-04-19 20:58:30 +00:00
2019-12-05 05:29:32 +00:00
return True
2017-04-19 20:58:30 +00:00
2020-02-19 21:48:36 +00:00
def eventFilter( self, watched, event ):
2018-03-28 21:55:58 +00:00
2020-02-19 21:48:36 +00:00
if watched == self._text_ctrl:
2016-09-21 19:54:04 +00:00
2020-02-19 21:48:36 +00:00
if event.type() == QC.QEvent.Wheel:
2018-01-31 22:58:15 +00:00
2020-02-19 21:48:36 +00:00
current_results_list = self._dropdown_notebook.currentWidget()
2016-09-21 19:54:04 +00:00
2020-02-19 21:48:36 +00:00
if self._text_ctrl.text() == '' and len( current_results_list ) == 0:
if event.angleDelta().y() > 0:
2022-05-25 21:30:53 +00:00
self.movePageLeft.emit()
2020-02-19 21:48:36 +00:00
else:
2022-05-25 21:30:53 +00:00
self.movePageRight.emit()
2020-02-19 21:48:36 +00:00
2017-04-19 20:58:30 +00:00
2020-02-19 21:48:36 +00:00
event.accept()
return True
2017-04-19 20:58:30 +00:00
2022-10-12 20:18:22 +00:00
elif event.modifiers() & QC.Qt.ControlModifier:
2017-04-19 20:58:30 +00:00
2022-10-12 20:18:22 +00:00
if event.angleDelta().y() > 0:
2020-02-19 21:48:36 +00:00
2022-10-12 20:18:22 +00:00
current_results_list.MoveSelectionUp()
2020-02-19 21:48:36 +00:00
2022-10-12 20:18:22 +00:00
else:
2020-02-19 21:48:36 +00:00
2022-10-12 20:18:22 +00:00
current_results_list.MoveSelectionDown()
2020-02-19 21:48:36 +00:00
2017-04-19 20:58:30 +00:00
2022-10-12 20:18:22 +00:00
event.accept()
return True
elif self._float_mode and not self._dropdown_hidden:
# it is annoying to scroll on this lad when float is around, so swallow it here
event.accept()
return True
2016-09-21 19:54:04 +00:00
2020-02-19 21:48:36 +00:00
elif self._float_mode:
2016-09-21 19:54:04 +00:00
2022-10-12 20:18:22 +00:00
# I could probably wangle this garbagewith setFocusProxy on all the children of the dropdown, assuming that wouldn't break anything, but this seems to work ok nonetheless
if event.type() == QC.QEvent.FocusIn:
2020-02-19 21:48:36 +00:00
self._DropdownHideShow()
return False
2022-10-12 20:18:22 +00:00
elif event.type() == QC.QEvent.FocusOut:
current_focus_widget = QW.QApplication.focusWidget()
if current_focus_widget is not None and ClientGUIFunctions.IsQtAncestor( current_focus_widget, self._dropdown_window ):
self._temporary_focus_widget = current_focus_widget
self._temporary_focus_widget.installEventFilter( self )
else:
self._DropdownHideShow()
return False
2016-09-21 19:54:04 +00:00
2020-02-19 21:48:36 +00:00
2022-10-12 20:18:22 +00:00
elif self._temporary_focus_widget is not None and watched == self._temporary_focus_widget:
2020-02-19 21:48:36 +00:00
2022-10-12 20:18:22 +00:00
if self._float_mode and event.type() == QC.QEvent.FocusOut:
self._temporary_focus_widget.removeEventFilter( self )
self._temporary_focus_widget = None
2020-02-19 21:48:36 +00:00
2022-10-12 20:18:22 +00:00
current_focus_widget = QW.QApplication.focusWidget()
if current_focus_widget is None:
# happens sometimes when moving tabs in the tags dropdown list
ClientGUIFunctions.SetFocusLater( self._text_ctrl )
elif ClientGUIFunctions.IsQtAncestor( current_focus_widget, self._dropdown_window ):
self._temporary_focus_widget = current_focus_widget
self._temporary_focus_widget.installEventFilter( self )
else:
self._DropdownHideShow()
2020-02-19 21:48:36 +00:00
return False
2016-09-21 19:54:04 +00:00
2020-02-19 21:48:36 +00:00
return False
2020-02-12 22:50:37 +00:00
2016-09-21 19:54:04 +00:00
def EventMove( self, event ):
2020-03-18 21:35:57 +00:00
self._DropdownHideShow()
2016-09-21 19:54:04 +00:00
2019-11-14 03:56:30 +00:00
return True # was: event.ignore()
2016-09-21 19:54:04 +00:00
2019-11-14 03:56:30 +00:00
def EventText( self, new_text ):
2016-09-21 19:54:04 +00:00
2019-11-14 03:56:30 +00:00
num_chars = len( self._text_ctrl.text() )
2016-09-21 19:54:04 +00:00
if num_chars == 0:
2020-04-01 21:51:42 +00:00
self._ScheduleResultsRefresh( 0.0 )
2016-09-21 19:54:04 +00:00
2018-03-28 21:55:58 +00:00
else:
2016-09-21 19:54:04 +00:00
2020-12-23 23:07:58 +00:00
parsed_autocomplete_text = self._GetParsedAutocompleteText()
if parsed_autocomplete_text.GetTagAutocompleteOptions().FetchResultsAutomatically():
2018-02-07 23:40:33 +00:00
2020-04-01 21:51:42 +00:00
self._ScheduleResultsRefresh( 0.0 )
2016-09-21 19:54:04 +00:00
2018-03-28 21:55:58 +00:00
2019-11-14 03:56:30 +00:00
if self._dropdown_notebook.currentWidget() != self._search_results_list:
2016-09-21 19:54:04 +00:00
2018-03-28 21:55:58 +00:00
self.MoveNotebookPageFocus( index = 0 )
2018-01-03 22:37:30 +00:00
2016-09-21 19:54:04 +00:00
2018-03-28 21:55:58 +00:00
def MoveNotebookPageFocus( self, index = None, direction = None ):
new_index = None
if index is not None:
new_index = index
elif direction is not None:
2019-11-14 03:56:30 +00:00
current_index = self._dropdown_notebook.currentIndex()
2018-03-28 21:55:58 +00:00
2019-11-14 03:56:30 +00:00
if current_index is not None and current_index != -1:
2018-03-28 21:55:58 +00:00
2019-11-14 03:56:30 +00:00
number_of_pages = self._dropdown_notebook.count()
2018-03-28 21:55:58 +00:00
new_index = ( current_index + direction ) % number_of_pages # does wraparound
if new_index is not None:
2019-11-14 03:56:30 +00:00
self._dropdown_notebook.setCurrentIndex( new_index )
2018-03-28 21:55:58 +00:00
self.setFocus( QC.Qt.OtherFocusReason )
2018-03-28 21:55:58 +00:00
2022-10-12 20:18:22 +00:00
def ParentWasScrolled( self ):
self._DropdownHideShow()
2021-01-27 22:14:03 +00:00
def ProcessApplicationCommand( self, command: CAC.ApplicationCommand ):
command_processed = True
if command.IsSimpleCommand():
2021-08-25 21:59:05 +00:00
action = command.GetSimpleAction()
2021-01-27 22:14:03 +00:00
if action == CAC.SIMPLE_AUTOCOMPLETE_IME_MODE:
self._can_intercept_unusual_key_events = not self._can_intercept_unusual_key_events
self._UpdateBackgroundColour()
elif self._can_intercept_unusual_key_events:
current_results_list = self._dropdown_notebook.currentWidget()
current_list_is_empty = len( current_results_list ) == 0
input_is_empty = self._text_ctrl.text() == ''
everything_is_empty = input_is_empty and current_list_is_empty
if action == CAC.SIMPLE_AUTOCOMPLETE_FORCE_FETCH:
self._ScheduleResultsRefresh( 0.0 )
elif input_is_empty and action in ( CAC.SIMPLE_AUTOCOMPLETE_IF_EMPTY_TAB_LEFT, CAC.SIMPLE_AUTOCOMPLETE_IF_EMPTY_TAB_RIGHT ):
if action == CAC.SIMPLE_AUTOCOMPLETE_IF_EMPTY_TAB_LEFT:
direction = -1
else:
direction = 1
self.MoveNotebookPageFocus( direction = direction )
elif everything_is_empty and action == CAC.SIMPLE_AUTOCOMPLETE_IF_EMPTY_PAGE_LEFT:
2022-05-25 21:30:53 +00:00
self.movePageLeft.emit()
2021-01-27 22:14:03 +00:00
elif everything_is_empty and action == CAC.SIMPLE_AUTOCOMPLETE_IF_EMPTY_PAGE_RIGHT:
2022-05-25 21:30:53 +00:00
self.movePageRight.emit()
2021-01-27 22:14:03 +00:00
elif everything_is_empty and action == CAC.SIMPLE_AUTOCOMPLETE_IF_EMPTY_MEDIA_PREVIOUS:
self.showPrevious.emit()
elif everything_is_empty and action == CAC.SIMPLE_AUTOCOMPLETE_IF_EMPTY_MEDIA_NEXT:
self.showNext.emit()
else:
command_processed = False
else:
command_processed = False
else:
command_processed = False
return command_processed
2020-04-22 21:00:35 +00:00
def SetFetchedResults( self, job_key: ClientThreading.JobKey, parsed_autocomplete_text: ClientSearch.ParsedAutocompleteText, results_cache: ClientSearch.PredicateResultsCache, results: list ):
2018-03-28 21:55:58 +00:00
2019-01-30 22:14:54 +00:00
if self._current_fetch_job_key is not None and self._current_fetch_job_key.GetKey() == job_key.GetKey():
2020-04-01 21:51:42 +00:00
self._CancelSearchResultsFetchJob()
2019-07-17 22:10:19 +00:00
2020-04-22 21:00:35 +00:00
self._results_cache = results_cache
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
self._SetResultsToList( results, parsed_autocomplete_text )
2019-01-30 22:14:54 +00:00
2018-03-28 21:55:58 +00:00
2020-04-01 21:51:42 +00:00
def SetForceDropdownHide( self, value ):
self._force_dropdown_hide = value
self._DropdownHideShow()
2016-09-21 19:54:04 +00:00
class AutoCompleteDropdownTags( AutoCompleteDropdown ):
locationChanged = QC.Signal( ClientLocation.LocationContext )
2020-03-25 21:15:57 +00:00
tagServiceChanged = QC.Signal( bytes )
2022-01-19 21:28:59 +00:00
def __init__( self, parent, location_context: ClientLocation.LocationContext, tag_service_key ):
2016-09-21 19:54:04 +00:00
2022-01-19 21:28:59 +00:00
location_context.FixMissingServices( HG.client_controller.services_manager.FilterValidServiceKeys )
2020-09-02 21:10:41 +00:00
if not HG.client_controller.services_manager.ServiceExists( tag_service_key ):
tag_service_key = CC.COMBINED_TAG_SERVICE_KEY
2016-09-21 19:54:04 +00:00
self._tag_service_key = tag_service_key
AutoCompleteDropdown.__init__( self, parent )
2017-06-28 20:23:21 +00:00
tag_service = HG.client_controller.services_manager.GetService( self._tag_service_key )
2016-09-21 19:54:04 +00:00
2022-03-23 20:57:10 +00:00
self._location_context_button = ClientGUILocation.LocationSearchContextButton( self._dropdown_window, location_context )
self._location_context_button.setMinimumWidth( 20 )
2016-09-21 19:54:04 +00:00
2022-07-20 19:17:03 +00:00
tag_context = ClientSearch.TagContext( service_key = self._tag_service_key )
self._tag_context_button = ClientGUISearch.TagContextButton( self._dropdown_window, tag_context )
self._tag_context_button.setMinimumWidth( 20 )
2016-09-21 19:54:04 +00:00
2019-01-30 22:14:54 +00:00
self._favourites_list = self._InitFavouritesList()
self.RefreshFavouriteTags()
2019-11-14 03:56:30 +00:00
self._dropdown_notebook.addTab( self._favourites_list, 'favourites' )
2019-01-30 22:14:54 +00:00
#
2022-03-23 20:57:10 +00:00
self._location_context_button.locationChanged.connect( self._LocationContextJustChanged )
2022-07-20 19:17:03 +00:00
self._tag_context_button.valueChanged.connect( self._TagContextJustChanged )
2022-03-23 20:57:10 +00:00
2019-01-30 22:14:54 +00:00
HG.client_controller.sub( self, 'RefreshFavouriteTags', 'notify_new_favourite_tags' )
2021-02-11 01:59:52 +00:00
HG.client_controller.sub( self, 'NotifyNewServices', 'notify_new_services' )
2019-01-30 22:14:54 +00:00
2016-09-21 19:54:04 +00:00
2022-10-26 20:43:00 +00:00
def _BroadcastChoices( self, predicates, shift_down ):
raise NotImplementedError()
2020-05-20 21:36:02 +00:00
def _GetCurrentBroadcastTextPredicate( self ) -> typing.Optional[ ClientSearch.Predicate ]:
raise NotImplementedError()
2020-04-22 21:00:35 +00:00
def _GetParsedAutocompleteText( self ) -> ClientSearch.ParsedAutocompleteText:
collapse_search_characters = True
2020-05-20 21:36:02 +00:00
tag_autocomplete_options = HG.client_controller.tag_display_manager.GetTagAutocompleteOptions( self._tag_service_key )
2020-04-22 21:00:35 +00:00
2020-05-20 21:36:02 +00:00
parsed_autocomplete_text = ClientSearch.ParsedAutocompleteText( self._text_ctrl.text(), tag_autocomplete_options, collapse_search_characters )
2016-09-21 19:54:04 +00:00
2020-05-20 21:36:02 +00:00
return parsed_autocomplete_text
2016-09-21 19:54:04 +00:00
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
def _InitFavouritesList( self ):
raise NotImplementedError()
2016-09-21 19:54:04 +00:00
2020-04-22 21:00:35 +00:00
2022-10-26 20:43:00 +00:00
def _InitSearchResultsList( self ):
raise NotImplementedError()
2022-03-23 20:57:10 +00:00
def _LocationContextJustChanged( self, location_context: ClientLocation.LocationContext ):
2020-03-11 21:52:11 +00:00
2022-03-23 20:57:10 +00:00
self._RestoreTextCtrlFocus()
2020-03-11 21:52:11 +00:00
2022-06-29 20:52:53 +00:00
if location_context.IsAllKnownFiles() and self._tag_service_key == CC.COMBINED_TAG_SERVICE_KEY:
2022-11-23 21:01:41 +00:00
top_local_tag_service_key = HG.client_controller.services_manager.GetDefaultLocalTagService().GetServiceKey()
2022-06-29 20:52:53 +00:00
self._SetTagService( top_local_tag_service_key )
2022-03-23 20:57:10 +00:00
self.locationChanged.emit( location_context )
2020-03-11 21:52:11 +00:00
self._SetListDirty()
2022-03-23 20:57:10 +00:00
def _SetLocationContext( self, location_context: ClientLocation.LocationContext ):
2020-03-11 21:52:11 +00:00
2022-03-23 20:57:10 +00:00
location_context.FixMissingServices( HG.client_controller.services_manager.FilterValidServiceKeys )
2020-03-11 21:52:11 +00:00
2022-07-13 21:35:17 +00:00
if location_context == self._location_context_button.GetValue():
return
2022-03-23 20:57:10 +00:00
if location_context.IsAllKnownFiles() and self._tag_service_key == CC.COMBINED_TAG_SERVICE_KEY:
local_tag_services = HG.client_controller.services_manager.GetServices( ( HC.LOCAL_TAG, ) )
self._SetTagService( local_tag_services[0].GetServiceKey() )
2016-10-26 20:45:34 +00:00
2022-03-23 20:57:10 +00:00
self._location_context_button.SetValue( location_context )
2016-12-21 22:30:54 +00:00
2022-03-23 20:57:10 +00:00
def _SetResultsToList( self, results, parsed_autocomplete_text: ClientSearch.ParsedAutocompleteText ):
2016-10-26 20:45:34 +00:00
2022-03-23 20:57:10 +00:00
self._search_results_list.SetPredicates( results )
2022-01-19 21:28:59 +00:00
2022-03-23 20:57:10 +00:00
self._current_list_parsed_autocomplete_text = parsed_autocomplete_text
2016-10-26 20:45:34 +00:00
2022-03-23 20:57:10 +00:00
2022-07-13 21:35:17 +00:00
def _SetTagService( self, tag_service_key ):
if not HG.client_controller.services_manager.ServiceExists( tag_service_key ):
tag_service_key = CC.COMBINED_TAG_SERVICE_KEY
if tag_service_key == CC.COMBINED_TAG_SERVICE_KEY and self._location_context_button.GetValue().IsAllKnownFiles():
default_location_context = HG.client_controller.new_options.GetDefaultLocalLocationContext()
self._SetLocationContext( default_location_context )
if tag_service_key == self._tag_service_key:
return False
2022-08-17 20:54:59 +00:00
tag_context = self._tag_context_button.GetValue().Duplicate()
tag_context.service_key = tag_service_key
self._tag_context_button.SetValue( tag_context )
return True
2022-10-26 20:43:00 +00:00
def _ShouldTakeResponsibilityForEnter( self ):
raise NotImplementedError()
def _StartSearchResultsFetchJob( self, job_key ):
raise NotImplementedError()
2022-08-17 20:54:59 +00:00
def _TagContextJustChanged( self, tag_context: ClientSearch.TagContext ):
self._RestoreTextCtrlFocus()
tag_service_key = tag_context.service_key
if not HG.client_controller.services_manager.ServiceExists( tag_service_key ):
tag_service_key = CC.COMBINED_TAG_SERVICE_KEY
if tag_service_key == CC.COMBINED_TAG_SERVICE_KEY and self._location_context_button.GetValue().IsAllKnownFiles():
default_location_context = HG.client_controller.new_options.GetDefaultLocalLocationContext()
self._SetLocationContext( default_location_context )
if tag_service_key == self._tag_service_key:
return False
2022-07-13 21:35:17 +00:00
self._tag_service_key = tag_service_key
self._search_results_list.SetTagServiceKey( self._tag_service_key )
self._favourites_list.SetTagServiceKey( self._tag_service_key )
self.tagServiceChanged.emit( self._tag_service_key )
self._SetListDirty()
return True
2022-10-26 20:43:00 +00:00
def _TakeResponsibilityForEnter( self, shift_down ):
raise NotImplementedError()
2023-01-04 22:22:08 +00:00
def GetLocationContext( self ) -> ClientLocation.LocationContext:
return self._location_context_button.GetValue()
2021-02-11 01:59:52 +00:00
def NotifyNewServices( self ):
2022-03-23 20:57:10 +00:00
self._SetLocationContext( self._location_context_button.GetValue() )
self._SetTagService( self._tag_service_key )
2021-02-11 01:59:52 +00:00
2019-01-30 22:14:54 +00:00
def RefreshFavouriteTags( self ):
2020-05-13 19:03:16 +00:00
favourite_tags = sorted( HG.client_controller.new_options.GetStringList( 'favourite_tags' ) )
2019-01-30 22:14:54 +00:00
2022-01-12 22:14:50 +00:00
predicates = [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_TAG, value = tag ) for tag in favourite_tags ]
2019-01-30 22:14:54 +00:00
self._favourites_list.SetPredicates( predicates )
2022-03-23 20:57:10 +00:00
def SetLocationContext( self, location_context: ClientLocation.LocationContext ):
2016-10-26 20:45:34 +00:00
2022-03-23 20:57:10 +00:00
self._SetLocationContext( location_context )
2016-10-26 20:45:34 +00:00
2020-04-22 21:00:35 +00:00
def SetStubPredicates( self, job_key, stub_predicates, parsed_autocomplete_text ):
2019-07-17 22:10:19 +00:00
if self._current_fetch_job_key is not None and self._current_fetch_job_key.GetKey() == job_key.GetKey():
2020-04-22 21:00:35 +00:00
self._SetResultsToList( stub_predicates, parsed_autocomplete_text )
2019-07-17 22:10:19 +00:00
2021-02-24 22:35:18 +00:00
def SetTagServiceKey( self, tag_service_key ):
2016-10-26 20:45:34 +00:00
2022-03-23 20:57:10 +00:00
self._SetTagService( tag_service_key )
2016-10-26 20:45:34 +00:00
2016-09-21 19:54:04 +00:00
class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
2020-03-25 21:15:57 +00:00
searchChanged = QC.Signal( ClientSearch.FileSearchContext )
searchCancelled = QC.Signal()
2020-11-25 22:22:47 +00:00
def __init__( self, parent: QW.QWidget, page_key, file_search_context: ClientSearch.FileSearchContext, media_sort_widget: typing.Optional[ ClientGUIResultsSortCollect.MediaSortControl ] = None, media_collect_widget: typing.Optional[ ClientGUIResultsSortCollect.MediaCollectControl ] = None, media_callable = None, synchronised = True, include_unusual_predicate_types = True, allow_all_known_files = True, force_system_everything = False, hide_favourites_edit_actions = False ):
2020-03-11 21:52:11 +00:00
2020-03-18 21:35:57 +00:00
self._page_key = page_key
2016-09-21 19:54:04 +00:00
# make a dupe here so we know that any direct changes we make to this guy will not affect other copies around
file_search_context = file_search_context.Duplicate()
2020-09-02 21:10:41 +00:00
self._under_construction_or_predicate = None
2022-01-19 21:28:59 +00:00
location_context = file_search_context.GetLocationContext()
2022-07-20 19:17:03 +00:00
tag_context = file_search_context.GetTagContext()
2016-09-21 19:54:04 +00:00
2020-03-18 21:35:57 +00:00
self._include_unusual_predicate_types = include_unusual_predicate_types
self._force_system_everything = force_system_everything
self._hide_favourites_edit_actions = hide_favourites_edit_actions
2020-03-11 21:52:11 +00:00
self._media_sort_widget = media_sort_widget
self._media_collect_widget = media_collect_widget
2016-09-21 19:54:04 +00:00
self._media_callable = media_callable
2020-03-11 21:52:11 +00:00
2016-09-21 19:54:04 +00:00
self._file_search_context = file_search_context
2022-07-20 19:17:03 +00:00
AutoCompleteDropdownTags.__init__( self, parent, location_context, tag_context.service_key )
2020-09-02 21:10:41 +00:00
2020-03-11 21:52:11 +00:00
self._predicates_listbox.SetPredicates( self._file_search_context.GetPredicates() )
2022-03-23 20:57:10 +00:00
self._location_context_button.SetAllKnownFilesAllowed( allow_all_known_files, True )
2020-03-11 21:52:11 +00:00
#
self._favourite_searches_button = ClientGUICommon.BetterBitmapButton( self._text_input_panel, CC.global_pixmaps().star, self._FavouriteSearchesMenu )
self._favourite_searches_button.setToolTip( 'Load or save a favourite search.' )
2020-03-25 21:15:57 +00:00
self._cancel_search_button = ClientGUICommon.BetterBitmapButton( self._text_input_panel, CC.global_pixmaps().stop, self.searchCancelled.emit )
self._cancel_search_button.hide()
2020-07-29 20:52:44 +00:00
QP.AddToLayout( self._text_input_hbox, self._favourite_searches_button, CC.FLAGS_CENTER_PERPENDICULAR )
QP.AddToLayout( self._text_input_hbox, self._cancel_search_button, CC.FLAGS_CENTER_PERPENDICULAR )
2020-03-25 21:15:57 +00:00
#
2020-03-11 21:52:11 +00:00
2022-07-20 19:17:03 +00:00
self._include_current_tags = ClientGUICommon.OnOffButton( self._dropdown_window, on_label = 'include current tags', off_label = 'exclude current tags', start_on = tag_context.include_current_tags )
2019-11-14 03:56:30 +00:00
self._include_current_tags.setToolTip( 'select whether to include current tags in the search' )
2022-07-20 19:17:03 +00:00
self._include_pending_tags = ClientGUICommon.OnOffButton( self._dropdown_window, on_label = 'include pending tags', off_label = 'exclude pending tags', start_on = tag_context.include_pending_tags )
2019-11-14 03:56:30 +00:00
self._include_pending_tags.setToolTip( 'select whether to include pending tags in the search' )
2016-09-21 19:54:04 +00:00
self._search_pause_play = ClientGUICommon.OnOffButton( self._dropdown_window, on_label = 'searching immediately', off_label = 'search paused', start_on = synchronised )
self._search_pause_play.setToolTip( 'select whether to renew the search as soon as a new predicate is entered' )
2016-09-21 19:54:04 +00:00
2022-04-13 21:39:26 +00:00
self._or_basic = ClientGUICommon.BetterButton( self._dropdown_window, 'OR', self._CreateNewOR )
self._or_basic.setToolTip( 'Create a new empty OR predicate in the dialog.' )
if not HG.client_controller.new_options.GetBoolean( 'advanced_mode' ):
self._or_basic.hide()
self._or_advanced = ClientGUICommon.BetterButton( self._dropdown_window, 'OR*', self._AdvancedORInput )
self._or_advanced.setToolTip( 'Advanced OR input.' )
2019-08-15 00:40:48 +00:00
if not HG.client_controller.new_options.GetBoolean( 'advanced_mode' ):
2019-11-14 03:56:30 +00:00
self._or_advanced.hide()
2019-08-15 00:40:48 +00:00
2020-03-11 21:52:11 +00:00
self._or_cancel = ClientGUICommon.BetterBitmapButton( self._dropdown_window, CC.global_pixmaps().delete, self._CancelORConstruction )
2019-11-14 03:56:30 +00:00
self._or_cancel.setToolTip( 'Cancel OR Predicate construction.' )
self._or_cancel.hide()
2016-09-21 19:54:04 +00:00
2020-03-11 21:52:11 +00:00
self._or_rewind = ClientGUICommon.BetterBitmapButton( self._dropdown_window, CC.global_pixmaps().previous, self._RewindORConstruction )
2019-11-14 03:56:30 +00:00
self._or_rewind.setToolTip( 'Rewind OR Predicate construction.' )
self._or_rewind.hide()
2019-04-03 22:45:57 +00:00
2019-11-14 03:56:30 +00:00
button_hbox_1 = QP.HBoxLayout()
2016-09-21 19:54:04 +00:00
2019-11-14 03:56:30 +00:00
QP.AddToLayout( button_hbox_1, self._include_current_tags, CC.FLAGS_EXPAND_BOTH_WAYS )
QP.AddToLayout( button_hbox_1, self._include_pending_tags, CC.FLAGS_EXPAND_BOTH_WAYS )
2016-09-21 19:54:04 +00:00
2019-11-14 03:56:30 +00:00
sync_button_hbox = QP.HBoxLayout()
2019-04-10 22:50:53 +00:00
QP.AddToLayout( sync_button_hbox, self._search_pause_play, CC.FLAGS_EXPAND_BOTH_WAYS )
2022-04-13 21:39:26 +00:00
QP.AddToLayout( sync_button_hbox, self._or_basic, CC.FLAGS_CENTER_PERPENDICULAR )
2020-07-29 20:52:44 +00:00
QP.AddToLayout( sync_button_hbox, self._or_advanced, CC.FLAGS_CENTER_PERPENDICULAR )
QP.AddToLayout( sync_button_hbox, self._or_cancel, CC.FLAGS_CENTER_PERPENDICULAR )
QP.AddToLayout( sync_button_hbox, self._or_rewind, CC.FLAGS_CENTER_PERPENDICULAR )
2019-04-10 22:50:53 +00:00
2019-11-14 03:56:30 +00:00
button_hbox_2 = QP.HBoxLayout()
2016-09-21 19:54:04 +00:00
2022-03-23 20:57:10 +00:00
QP.AddToLayout( button_hbox_2, self._location_context_button, CC.FLAGS_EXPAND_BOTH_WAYS )
2022-07-20 19:17:03 +00:00
QP.AddToLayout( button_hbox_2, self._tag_context_button, CC.FLAGS_EXPAND_BOTH_WAYS )
2016-09-21 19:54:04 +00:00
2019-11-14 03:56:30 +00:00
vbox = QP.VBoxLayout()
2016-09-21 19:54:04 +00:00
2019-11-14 03:56:30 +00:00
QP.AddToLayout( vbox, button_hbox_1, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
QP.AddToLayout( vbox, sync_button_hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
QP.AddToLayout( vbox, button_hbox_2, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
QP.AddToLayout( vbox, self._dropdown_notebook, CC.FLAGS_EXPAND_BOTH_WAYS )
2016-09-21 19:54:04 +00:00
2019-11-14 03:56:30 +00:00
self._dropdown_window.setLayout( vbox )
2016-09-21 19:54:04 +00:00
2020-03-25 21:15:57 +00:00
self._predicates_listbox.listBoxChanged.connect( self._SignalNewSearchState )
2022-05-04 21:40:27 +00:00
self._include_current_tags.valueChanged.connect( self._IncludeCurrentChanged )
self._include_pending_tags.valueChanged.connect( self._IncludePendingChanged )
self._search_pause_play.valueChanged.connect( self._SynchronisedChanged )
2020-05-20 21:36:02 +00:00
2016-09-21 19:54:04 +00:00
2019-08-15 00:40:48 +00:00
def _AdvancedORInput( self ):
title = 'enter advanced OR predicates'
2020-04-29 21:44:12 +00:00
with ClientGUITopLevelWindowsPanels.DialogEdit( self, title ) as dlg:
2019-08-15 00:40:48 +00:00
2020-02-12 22:50:37 +00:00
panel = EditAdvancedORPredicates( dlg )
2019-08-15 00:40:48 +00:00
dlg.SetPanel( panel )
2019-11-14 03:56:30 +00:00
if dlg.exec() == QW.QDialog.Accepted:
2019-08-15 00:40:48 +00:00
predicates = panel.GetValue()
shift_down = False
if len( predicates ) > 0:
self._BroadcastChoices( predicates, shift_down )
ClientGUIFunctions.SetFocusLater( self._text_ctrl )
2022-10-12 20:18:22 +00:00
2019-08-15 00:40:48 +00:00
2019-04-03 22:45:57 +00:00
def _BroadcastChoices( self, predicates, shift_down ):
2016-09-21 19:54:04 +00:00
2019-04-10 22:50:53 +00:00
or_pred_in_broadcast = self._under_construction_or_predicate is not None and self._under_construction_or_predicate in predicates
2019-04-03 22:45:57 +00:00
if shift_down:
if self._under_construction_or_predicate is None:
2022-01-12 22:14:50 +00:00
self._under_construction_or_predicate = ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_OR_CONTAINER, value = predicates )
2019-04-03 22:45:57 +00:00
else:
2019-04-10 22:50:53 +00:00
if or_pred_in_broadcast:
predicates.remove( self._under_construction_or_predicate )
2019-04-03 22:45:57 +00:00
or_preds = list( self._under_construction_or_predicate.GetValue() )
or_preds.extend( [ predicate for predicate in predicates if predicate not in or_preds ] )
2022-01-12 22:14:50 +00:00
self._under_construction_or_predicate = ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_OR_CONTAINER, value = or_preds )
2019-04-03 22:45:57 +00:00
else:
2019-04-10 22:50:53 +00:00
if or_pred_in_broadcast:
2019-04-03 22:45:57 +00:00
2019-04-10 22:50:53 +00:00
or_preds = list( self._under_construction_or_predicate.GetValue() )
if len( or_preds ) == 1:
predicates.remove( self._under_construction_or_predicate )
predicates.extend( or_preds )
2019-04-03 22:45:57 +00:00
2020-07-15 20:52:09 +00:00
elif self._under_construction_or_predicate is not None:
or_preds = list( self._under_construction_or_predicate.GetValue() )
or_preds.extend( [ predicate for predicate in predicates if predicate not in or_preds ] )
2022-01-12 22:14:50 +00:00
predicates = { ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_OR_CONTAINER, value = or_preds ) }
2020-07-15 20:52:09 +00:00
2019-04-03 22:45:57 +00:00
2019-04-10 22:50:53 +00:00
self._under_construction_or_predicate = None
2020-03-25 21:15:57 +00:00
self._predicates_listbox.EnterPredicates( self._page_key, predicates )
2019-04-03 22:45:57 +00:00
2016-09-21 19:54:04 +00:00
2019-04-10 22:50:53 +00:00
self._UpdateORButtons()
2018-05-23 21:05:06 +00:00
self._ClearInput()
2016-09-21 19:54:04 +00:00
2022-04-13 21:39:26 +00:00
def _CancelORConstruction( self ):
2020-03-25 21:15:57 +00:00
2022-04-13 21:39:26 +00:00
self._under_construction_or_predicate = None
2021-10-06 20:59:30 +00:00
2022-04-13 21:39:26 +00:00
self._UpdateORButtons()
2020-03-25 21:15:57 +00:00
2022-04-13 21:39:26 +00:00
self._ClearInput()
2020-03-25 21:15:57 +00:00
2022-04-13 21:39:26 +00:00
def _CreateNewOR( self ):
2019-04-10 22:50:53 +00:00
2022-04-13 21:39:26 +00:00
predicates = { ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_OR_CONTAINER, value = [ ] ) }
2019-04-10 22:50:53 +00:00
2022-04-13 21:39:26 +00:00
try:
predicates = ClientGUISearch.EditPredicates( self, predicates )
except HydrusExceptions.CancelledException:
ClientGUIFunctions.SetFocusLater( self._text_ctrl )
2022-10-12 20:18:22 +00:00
2022-04-13 21:39:26 +00:00
return
2019-04-10 22:50:53 +00:00
2022-04-13 21:39:26 +00:00
shift_down = False
self._BroadcastChoices( predicates, shift_down )
2019-04-10 22:50:53 +00:00
ClientGUIFunctions.SetFocusLater( self._text_ctrl )
2022-10-12 20:18:22 +00:00
2019-04-10 22:50:53 +00:00
2020-03-11 21:52:11 +00:00
def _FavouriteSearchesMenu( self ):
menu = QW.QMenu()
if not self._hide_favourites_edit_actions:
ClientGUIMenus.AppendMenuItem( menu, 'manage favourite searches', 'Open a dialog to edit your favourite searches.', self._ManageFavouriteSearches )
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( menu, 'save this search', 'Save this search for later.', self._SaveFavouriteSearch )
folders_to_names = HG.client_controller.favourite_search_manager.GetFoldersToNames()
if len( folders_to_names ) > 0:
ClientGUIMenus.AppendSeparator( menu )
folder_names = list( folders_to_names.keys() )
if None in folder_names:
folder_names.remove( None )
folder_names.sort()
folder_names.insert( 0, None )
2020-05-13 19:03:16 +00:00
else:
folder_names.sort()
2020-03-11 21:52:11 +00:00
for folder_name in folder_names:
if folder_name is None:
menu_to_use = menu
else:
menu_to_use = QW.QMenu( menu )
ClientGUIMenus.AppendMenu( menu, menu_to_use, folder_name )
2020-05-13 19:03:16 +00:00
names = sorted( folders_to_names[ folder_name ] )
2020-03-11 21:52:11 +00:00
for name in names:
ClientGUIMenus.AppendMenuItem( menu_to_use, name, 'Load the {} search.'.format( name ), self._LoadFavouriteSearch, folder_name, name )
CGC.core().PopupMenu( self, menu )
2016-09-21 19:54:04 +00:00
2020-04-22 21:00:35 +00:00
def _GetCurrentBroadcastTextPredicate( self ) -> typing.Optional[ ClientSearch.Predicate ]:
parsed_autocomplete_text = self._GetParsedAutocompleteText()
2020-05-20 21:36:02 +00:00
if parsed_autocomplete_text.IsAcceptableForFileSearches():
2020-04-22 21:00:35 +00:00
return parsed_autocomplete_text.GetImmediateFileSearchPredicate()
else:
return None
2019-04-10 22:50:53 +00:00
def _HandleEscape( self ):
2019-11-14 03:56:30 +00:00
if self._under_construction_or_predicate is not None and self._text_ctrl.text() == '':
2019-04-10 22:50:53 +00:00
self._CancelORConstruction()
return True
else:
return AutoCompleteDropdown._HandleEscape( self )
2022-05-04 21:40:27 +00:00
def _IncludeCurrentChanged( self, value ):
self._file_search_context.SetIncludeCurrentTags( value )
self._SetListDirty()
self._SignalNewSearchState()
self._RestoreTextCtrlFocus()
def _IncludePendingChanged( self, value ):
self._file_search_context.SetIncludePendingTags( value )
self._SetListDirty()
self._SignalNewSearchState()
self._RestoreTextCtrlFocus()
2018-03-28 21:55:58 +00:00
def _InitFavouritesList( self ):
height_num_chars = HG.client_controller.new_options.GetInteger( 'ac_read_list_height_num_chars' )
2021-02-24 22:35:18 +00:00
favs_list = ListBoxTagsPredicatesAC( self._dropdown_notebook, self.BroadcastChoices, self._float_mode, self._tag_service_key, tag_display_type = ClientTags.TAG_DISPLAY_ACTUAL, height_num_chars = height_num_chars )
2016-09-21 19:54:04 +00:00
2018-03-28 21:55:58 +00:00
return favs_list
def _InitSearchResultsList( self ):
height_num_chars = HG.client_controller.new_options.GetInteger( 'ac_read_list_height_num_chars' )
2019-11-14 03:56:30 +00:00
2021-02-24 22:35:18 +00:00
return ListBoxTagsPredicatesAC( self._dropdown_notebook, self.BroadcastChoices, self._tag_service_key, self._float_mode, tag_display_type = ClientTags.TAG_DISPLAY_ACTUAL, height_num_chars = height_num_chars )
2016-09-21 19:54:04 +00:00
2022-03-23 20:57:10 +00:00
def _LocationContextJustChanged( self, location_context: ClientLocation.LocationContext ):
AutoCompleteDropdownTags._LocationContextJustChanged( self, location_context )
self._file_search_context.SetLocationContext( location_context )
self._SignalNewSearchState()
2020-03-11 21:52:11 +00:00
def _LoadFavouriteSearch( self, folder_name, name ):
( file_search_context, synchronised, media_sort, media_collect ) = HG.client_controller.favourite_search_manager.GetFavouriteSearch( folder_name, name )
2020-05-20 21:36:02 +00:00
self.blockSignals( True )
2020-03-11 21:52:11 +00:00
self.SetFileSearchContext( file_search_context )
if media_sort is not None and self._media_sort_widget is not None:
self._media_sort_widget.SetSort( media_sort )
if media_collect is not None and self._media_collect_widget is not None:
self._media_collect_widget.SetCollect( media_collect )
self._search_pause_play.SetOnOff( synchronised )
2020-03-11 21:52:11 +00:00
2020-05-20 21:36:02 +00:00
self.blockSignals( False )
2022-03-23 20:57:10 +00:00
self.locationChanged.emit( self._location_context_button.GetValue() )
2022-03-09 22:18:23 +00:00
self.tagServiceChanged.emit( self._tag_service_key )
2022-08-17 20:54:59 +00:00
2020-05-20 21:36:02 +00:00
self._SignalNewSearchState()
2020-03-11 21:52:11 +00:00
def _ManageFavouriteSearches( self, favourite_search_row_to_save = None ):
2020-11-25 22:22:47 +00:00
from hydrus.client.gui.search import ClientGUISearchPanels
2020-03-11 21:52:11 +00:00
favourite_searches_rows = HG.client_controller.favourite_search_manager.GetFavouriteSearchRows()
title = 'edit favourite searches'
2020-04-29 21:44:12 +00:00
with ClientGUITopLevelWindowsPanels.DialogEdit( self, title ) as dlg:
2020-03-11 21:52:11 +00:00
panel = ClientGUISearchPanels.EditFavouriteSearchesPanel( dlg, favourite_searches_rows, initial_search_row_to_edit = favourite_search_row_to_save )
dlg.SetPanel( panel )
if dlg.exec() == QW.QDialog.Accepted:
edited_favourite_searches_rows = panel.GetValue()
HG.client_controller.favourite_search_manager.SetFavouriteSearchRows( edited_favourite_searches_rows )
def _RestoreTextCtrlFocus( self ):
# if an event came from clicking the dropdown or stop or something, we want to put focus back on textctrl
current_focus_widget = QW.QApplication.focusWidget()
if current_focus_widget != self._favourite_searches_button:
AutoCompleteDropdownTags._RestoreTextCtrlFocus( self )
2019-04-10 22:50:53 +00:00
def _RewindORConstruction( self ):
2019-04-03 22:45:57 +00:00
2019-04-10 22:50:53 +00:00
if self._under_construction_or_predicate is not None:
2019-04-03 22:45:57 +00:00
2019-04-10 22:50:53 +00:00
or_preds = self._under_construction_or_predicate.GetValue()
2019-04-03 22:45:57 +00:00
2019-04-10 22:50:53 +00:00
if len( or_preds ) <= 1:
2019-04-03 22:45:57 +00:00
2019-04-10 22:50:53 +00:00
self._CancelORConstruction()
2019-04-03 22:45:57 +00:00
2019-04-10 22:50:53 +00:00
return
2019-04-03 22:45:57 +00:00
2019-04-10 22:50:53 +00:00
or_preds = or_preds[:-1]
2019-04-03 22:45:57 +00:00
2022-01-12 22:14:50 +00:00
self._under_construction_or_predicate = ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_OR_CONTAINER, value = or_preds )
2019-04-03 22:45:57 +00:00
2019-04-10 22:50:53 +00:00
self._UpdateORButtons()
self._ClearInput()
2019-04-03 22:45:57 +00:00
2020-03-11 21:52:11 +00:00
def _SaveFavouriteSearch( self ):
foldername = None
name = 'new favourite search'
file_search_context = self.GetFileSearchContext()
synchronised = self.IsSynchronised()
if self._media_sort_widget is None:
media_sort = None
else:
media_sort = self._media_sort_widget.GetSort()
if self._media_collect_widget is None:
media_collect = None
else:
media_collect = self._media_collect_widget.GetValue()
search_row = ( foldername, name, file_search_context, synchronised, media_sort, media_collect )
self._ManageFavouriteSearches( favourite_search_row_to_save = search_row )
2020-03-18 21:35:57 +00:00
def _SetupTopListBox( self ):
self._predicates_listbox = ListBoxTagsActiveSearchPredicates( self, self._page_key )
2021-04-14 21:54:17 +00:00
QP.AddToLayout( self._main_vbox, self._predicates_listbox, CC.FLAGS_EXPAND_BOTH_WAYS )
2020-03-18 21:35:57 +00:00
2022-04-13 21:39:26 +00:00
def _SignalNewSearchState( self ):
self._file_search_context.SetPredicates( self._predicates_listbox.GetPredicates() )
file_search_context = self._file_search_context.Duplicate()
self.searchChanged.emit( file_search_context )
2022-05-04 21:40:27 +00:00
def _SynchronisedChanged( self, value ):
self._SignalNewSearchState()
self._RestoreTextCtrlFocus()
if not self._search_pause_play.IsOn() and not self._file_search_context.GetSystemPredicates().HasSystemLimit():
# update if user goes from sync to non-sync
self._SetListDirty()
2020-04-01 21:51:42 +00:00
def _StartSearchResultsFetchJob( self, job_key ):
2016-09-21 19:54:04 +00:00
2020-04-22 21:00:35 +00:00
parsed_autocomplete_text = self._GetParsedAutocompleteText()
2016-09-21 19:54:04 +00:00
2019-07-17 22:10:19 +00:00
stub_predicates = []
2020-04-22 21:00:35 +00:00
InsertOtherPredicatesForRead( stub_predicates, parsed_autocomplete_text, self._include_unusual_predicate_types, self._under_construction_or_predicate )
2019-07-17 22:10:19 +00:00
AppendLoadingPredicate( stub_predicates )
2021-06-23 21:11:38 +00:00
HG.client_controller.CallLaterQtSafe( self, 0.2, 'set stub predicates', self.SetStubPredicates, job_key, stub_predicates, parsed_autocomplete_text )
2020-04-01 21:51:42 +00:00
2021-10-06 20:59:30 +00:00
fsc = self.GetFileSearchContext()
2020-04-01 21:51:42 +00:00
if self._under_construction_or_predicate is None:
under_construction_or_predicate = None
else:
under_construction_or_predicate = self._under_construction_or_predicate.Duplicate()
2019-07-17 22:10:19 +00:00
2021-10-06 20:59:30 +00:00
HG.client_controller.CallToThread( ReadFetch, self, job_key, self.SetFetchedResults, parsed_autocomplete_text, self._media_callable, fsc, self._search_pause_play.IsOn(), self._include_unusual_predicate_types, self._results_cache, under_construction_or_predicate, self._force_system_everything )
2016-09-21 19:54:04 +00:00
def _ShouldTakeResponsibilityForEnter( self ):
2019-11-14 03:56:30 +00:00
looking_at_search_results = self._dropdown_notebook.currentWidget() == self._search_results_list
2019-08-07 22:59:53 +00:00
2020-04-22 21:00:35 +00:00
something_to_broadcast = self._GetCurrentBroadcastTextPredicate() is not None
parsed_autocomplete_text = self._GetParsedAutocompleteText()
2019-01-30 22:14:54 +00:00
2019-08-15 00:40:48 +00:00
# the list has results, but they are out of sync with what we have currently entered
2016-09-21 19:54:04 +00:00
# when the user has quickly typed something in and the results are not yet in
2020-04-22 21:00:35 +00:00
results_desynced_with_text = parsed_autocomplete_text != self._current_list_parsed_autocomplete_text
2016-09-21 19:54:04 +00:00
2019-08-15 00:40:48 +00:00
p1 = looking_at_search_results and something_to_broadcast and results_desynced_with_text
2019-01-30 22:14:54 +00:00
return p1
2016-09-21 19:54:04 +00:00
2022-08-17 20:54:59 +00:00
def _TagContextJustChanged( self, tag_context: ClientSearch.TagContext ):
it_changed = AutoCompleteDropdownTags._TagContextJustChanged( self, tag_context )
if it_changed:
self._file_search_context.SetTagServiceKey( self._tag_service_key )
self._SignalNewSearchState()
2019-04-03 22:45:57 +00:00
def _TakeResponsibilityForEnter( self, shift_down ):
2016-09-21 19:54:04 +00:00
2020-04-22 21:00:35 +00:00
current_broadcast_predicate = self._GetCurrentBroadcastTextPredicate()
if current_broadcast_predicate is not None:
self._BroadcastChoices( { current_broadcast_predicate }, shift_down )
2016-09-21 19:54:04 +00:00
2019-04-10 22:50:53 +00:00
def _UpdateORButtons( self ):
if self._under_construction_or_predicate is None:
2019-11-14 03:56:30 +00:00
if self._or_cancel.isVisible():
2019-04-10 22:50:53 +00:00
2019-11-14 03:56:30 +00:00
self._or_cancel.hide()
2019-04-10 22:50:53 +00:00
2019-11-14 03:56:30 +00:00
if self._or_rewind.isVisible():
2019-04-10 22:50:53 +00:00
2019-11-14 03:56:30 +00:00
self._or_rewind.hide()
2019-04-10 22:50:53 +00:00
else:
or_preds = self._under_construction_or_predicate.GetValue()
if len( or_preds ) > 1:
2019-11-14 03:56:30 +00:00
if not self._or_rewind.isVisible():
2019-04-10 22:50:53 +00:00
2019-11-14 03:56:30 +00:00
self._or_rewind.show()
2019-04-10 22:50:53 +00:00
else:
2019-11-14 03:56:30 +00:00
if self._or_rewind.isVisible():
2019-04-10 22:50:53 +00:00
2019-11-14 03:56:30 +00:00
self._or_rewind.hide()
2019-04-10 22:50:53 +00:00
2019-11-14 03:56:30 +00:00
if not self._or_cancel.isVisible():
2019-04-10 22:50:53 +00:00
2019-11-14 03:56:30 +00:00
self._or_cancel.show()
2019-04-10 22:50:53 +00:00
2020-03-11 21:52:11 +00:00
def GetFileSearchContext( self ) -> ClientSearch.FileSearchContext:
fsc = self._file_search_context.Duplicate()
fsc.SetPredicates( self._predicates_listbox.GetPredicates() )
2016-09-21 19:54:04 +00:00
2020-03-11 21:52:11 +00:00
return fsc
2016-09-21 19:54:04 +00:00
2020-03-18 21:35:57 +00:00
def GetPredicates( self ) -> typing.Set[ ClientSearch.Predicate ]:
return self._predicates_listbox.GetPredicates()
2019-04-24 22:18:50 +00:00
def IsSynchronised( self ):
return self._search_pause_play.IsOn()
2021-01-27 22:14:03 +00:00
def ProcessApplicationCommand( self, command: CAC.ApplicationCommand ):
command_processed = True
if self._can_intercept_unusual_key_events and command.IsSimpleCommand():
2021-08-25 21:59:05 +00:00
action = command.GetSimpleAction()
2021-01-27 22:14:03 +00:00
if action == CAC.SIMPLE_SYNCHRONISED_WAIT_SWITCH:
self.PausePlaySearch()
else:
command_processed = False
else:
command_processed = False
if not command_processed:
command_processed = AutoCompleteDropdownTags.ProcessApplicationCommand( self, command )
return command_processed
2020-04-22 21:00:35 +00:00
def SetFetchedResults( self, job_key: ClientThreading.JobKey, parsed_autocomplete_text: ClientSearch.ParsedAutocompleteText, results_cache: ClientSearch.PredicateResultsCache, results: list ):
2019-01-30 22:14:54 +00:00
if self._current_fetch_job_key is not None and self._current_fetch_job_key.GetKey() == job_key.GetKey():
2020-04-22 21:00:35 +00:00
AutoCompleteDropdownTags.SetFetchedResults( self, job_key, parsed_autocomplete_text, results_cache, results )
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
if parsed_autocomplete_text.IsEmpty():
2019-01-30 22:14:54 +00:00
# refresh system preds after five mins
2020-04-01 21:51:42 +00:00
self._ScheduleResultsRefresh( 300 )
2019-01-30 22:14:54 +00:00
2020-03-11 21:52:11 +00:00
def SetFileSearchContext( self, file_search_context: ClientSearch.FileSearchContext ):
self._ClearInput()
self._CancelORConstruction()
self._file_search_context = file_search_context.Duplicate()
2021-10-06 20:59:30 +00:00
self._predicates_listbox.SetPredicates( self._file_search_context.GetPredicates() )
2022-03-23 20:57:10 +00:00
self._SetLocationContext( self._file_search_context.GetLocationContext() )
2022-07-20 19:17:03 +00:00
self._SetTagService( self._file_search_context.GetTagContext().service_key )
2020-03-11 21:52:11 +00:00
2022-08-03 20:59:51 +00:00
self._include_current_tags.SetOnOff( self._file_search_context.GetTagContext().include_current_tags )
self._include_pending_tags.SetOnOff( self._file_search_context.GetTagContext().include_pending_tags )
2020-05-20 21:36:02 +00:00
self._SignalNewSearchState()
2022-05-04 21:40:27 +00:00
def SetIncludeCurrent( self, value: bool ):
2020-05-20 21:36:02 +00:00
2022-05-04 21:40:27 +00:00
self._include_current_tags.SetOnOff( value )
2021-07-28 21:12:00 +00:00
2020-05-20 21:36:02 +00:00
2022-05-04 21:40:27 +00:00
def SetIncludePending( self, value: bool ):
2020-05-20 21:36:02 +00:00
2022-05-04 21:40:27 +00:00
self._include_pending_tags.SetOnOff( value )
2021-07-28 21:12:00 +00:00
2020-05-20 21:36:02 +00:00
2022-05-04 21:40:27 +00:00
def SetSynchronised( self, value: bool ):
2020-05-20 21:36:02 +00:00
2022-05-04 21:40:27 +00:00
self._search_pause_play.SetOnOff( value )
2021-10-06 20:59:30 +00:00
2020-03-11 21:52:11 +00:00
def PausePlaySearch( self ):
2016-09-21 19:54:04 +00:00
self._search_pause_play.Flip()
2020-03-25 21:15:57 +00:00
2021-07-28 21:12:00 +00:00
self._RestoreTextCtrlFocus()
2020-03-25 21:15:57 +00:00
def ShowCancelSearchButton( self, show ):
if self._cancel_search_button.isVisible() != show:
2019-04-24 22:18:50 +00:00
2020-03-25 21:15:57 +00:00
self._cancel_search_button.setVisible( show )
2019-04-24 22:18:50 +00:00
2016-09-21 19:54:04 +00:00
2020-03-18 21:35:57 +00:00
class ListBoxTagsActiveSearchPredicates( ClientGUIListBoxes.ListBoxTagsPredicates ):
def __init__( self, parent: AutoCompleteDropdownTagsRead, page_key, initial_predicates = None ):
if initial_predicates is None:
initial_predicates = []
ClientGUIListBoxes.ListBoxTagsPredicates.__init__( self, parent, height_num_chars = 6 )
self._my_ac_parent = parent
self._page_key = page_key
if len( initial_predicates ) > 0:
2021-02-11 01:59:52 +00:00
terms = [ self._GenerateTermFromPredicate( predicate ) for predicate in initial_predicates ]
self._AppendTerms( terms )
2020-03-18 21:35:57 +00:00
2021-02-11 01:59:52 +00:00
self._Sort()
2020-11-25 22:22:47 +00:00
2020-03-18 21:35:57 +00:00
self._DataHasChanged()
HG.client_controller.sub( self, 'EnterPredicates', 'enter_predicates' )
2021-11-10 21:53:57 +00:00
def _Activate( self, ctrl_down, shift_down ) -> bool:
2020-03-18 21:35:57 +00:00
2021-02-11 01:59:52 +00:00
predicates = self._GetPredicatesFromTerms( self._selected_terms )
if len( predicates ) > 0:
2020-03-18 21:35:57 +00:00
2020-11-18 22:15:21 +00:00
if shift_down:
2021-02-11 01:59:52 +00:00
self._EditPredicates( set( predicates ) )
2020-11-18 22:15:21 +00:00
2021-11-10 21:53:57 +00:00
elif ctrl_down:
( predicates, or_predicate, inverse_predicates, namespace_predicate, inverse_namespace_predicate ) = self._GetSelectedPredicatesAndInverseCopies()
self._EnterPredicates( inverse_predicates )
2020-11-18 22:15:21 +00:00
else:
2021-02-11 01:59:52 +00:00
self._EnterPredicates( set( predicates ) )
2020-11-18 22:15:21 +00:00
return True
2020-03-18 21:35:57 +00:00
2020-11-18 22:15:21 +00:00
return False
2020-03-18 21:35:57 +00:00
2020-11-25 22:22:47 +00:00
def _AddEditMenu( self, menu: QW.QMenu ):
2022-09-28 17:15:23 +00:00
( editable_predicates, only_invertible_predicates, non_editable_predicates ) = ClientGUISearch.GetEditablePredicates( self._GetPredicatesFromTerms( self._selected_terms ) )
2020-11-25 22:22:47 +00:00
if len( editable_predicates ) > 0:
2022-10-05 21:00:47 +00:00
editable_and_invertible_predicates = list( editable_predicates )
editable_and_invertible_predicates.extend( only_invertible_predicates )
2020-11-25 22:22:47 +00:00
ClientGUIMenus.AppendSeparator( menu )
2022-10-05 21:00:47 +00:00
if len( editable_and_invertible_predicates ) == 1:
2020-11-25 22:22:47 +00:00
2022-10-05 21:00:47 +00:00
desc = list( editable_and_invertible_predicates )[0].ToString()
2020-11-25 22:22:47 +00:00
else:
2022-10-05 21:00:47 +00:00
desc = '{} search terms'.format( HydrusData.ToHumanInt( len( editable_and_invertible_predicates ) ) )
2020-11-25 22:22:47 +00:00
label = 'edit {}'.format( desc )
2022-10-05 21:00:47 +00:00
ClientGUIMenus.AppendMenuItem( menu, label, 'Edit these predicates and refresh the search. Not all predicates are editable.', self._EditPredicates, editable_and_invertible_predicates )
2020-11-25 22:22:47 +00:00
2020-04-29 21:44:12 +00:00
def _CanProvideCurrentPagePredicates( self ):
return True
2020-03-18 21:35:57 +00:00
def _DeleteActivate( self ):
2021-11-10 21:53:57 +00:00
ctrl_down = False
2020-11-18 22:15:21 +00:00
shift_down = False
2021-11-10 21:53:57 +00:00
self._Activate( ctrl_down, shift_down )
2020-11-18 22:15:21 +00:00
def _EditPredicates( self, predicates ):
original_predicates = set( predicates )
try:
edited_predicates = set( ClientGUISearch.EditPredicates( self, predicates ) )
except HydrusExceptions.CancelledException:
return
non_edited_predicates = original_predicates.intersection( edited_predicates )
predicates_to_add = edited_predicates.difference( non_edited_predicates )
predicates_to_remove = original_predicates.difference( non_edited_predicates )
if len( predicates_to_add ) + len( predicates_to_remove ) == 0:
return
2021-02-11 01:59:52 +00:00
terms_to_remove = [ self._GenerateTermFromPredicate( predicate ) for predicate in predicates_to_remove ]
2020-11-18 22:15:21 +00:00
2021-02-11 01:59:52 +00:00
self._RemoveTerms( terms_to_remove )
terms_to_add = [ self._GenerateTermFromPredicate( predicate ) for predicate in predicates_to_add ]
self._AppendTerms( terms_to_add )
2020-11-18 22:15:21 +00:00
2021-02-11 01:59:52 +00:00
self._selected_terms.update( terms_to_add )
2020-11-25 22:22:47 +00:00
2021-02-11 01:59:52 +00:00
self._Sort()
2020-11-18 22:15:21 +00:00
self._DataHasChanged()
2020-03-18 21:35:57 +00:00
def _EnterPredicates( self, predicates, permit_add = True, permit_remove = True ):
if len( predicates ) == 0:
return
2021-02-11 01:59:52 +00:00
terms_to_be_added = set()
terms_to_be_removed = set()
2020-03-18 21:35:57 +00:00
for predicate in predicates:
predicate = predicate.GetCountlessCopy()
2021-02-11 01:59:52 +00:00
term = self._GenerateTermFromPredicate( predicate )
if term in self._terms_to_logical_indices:
2020-03-18 21:35:57 +00:00
if permit_remove:
2021-02-11 01:59:52 +00:00
terms_to_be_removed.add( term )
2020-03-18 21:35:57 +00:00
else:
if permit_add:
2021-02-11 01:59:52 +00:00
terms_to_be_added.add( term )
2020-03-18 21:35:57 +00:00
2021-02-24 22:35:18 +00:00
m_e_preds = self._GetMutuallyExclusivePredicates( predicate )
terms_to_be_removed.update( ( self._GenerateTermFromPredicate( pred ) for pred in m_e_preds ) )
2020-03-18 21:35:57 +00:00
2021-02-11 01:59:52 +00:00
self._AppendTerms( terms_to_be_added )
2020-03-18 21:35:57 +00:00
2021-02-11 01:59:52 +00:00
self._RemoveTerms( terms_to_be_removed )
2020-03-18 21:35:57 +00:00
2021-02-11 01:59:52 +00:00
self._Sort()
2020-03-18 21:35:57 +00:00
self._DataHasChanged()
2022-01-19 21:28:59 +00:00
def _GetCurrentLocationContext( self ):
2020-03-18 21:35:57 +00:00
2022-01-19 21:28:59 +00:00
return self._my_ac_parent.GetFileSearchContext().GetLocationContext()
2020-03-18 21:35:57 +00:00
def _GetCurrentPagePredicates( self ) -> typing.Set[ ClientSearch.Predicate ]:
return self.GetPredicates()
2020-07-29 20:52:44 +00:00
def _HasCounts( self ):
return False
2020-03-18 21:35:57 +00:00
def _ProcessMenuPredicateEvent( self, command ):
2021-11-10 21:53:57 +00:00
( predicates, or_predicate, inverse_predicates, namespace_predicate, inverse_namespace_predicate ) = self._GetSelectedPredicatesAndInverseCopies()
2020-03-18 21:35:57 +00:00
2020-04-29 21:44:12 +00:00
if command == 'add_predicates':
2020-03-18 21:35:57 +00:00
2020-04-29 21:44:12 +00:00
self._EnterPredicates( predicates, permit_remove = False )
2020-03-18 21:35:57 +00:00
2020-11-25 22:22:47 +00:00
elif command == 'add_or_predicate':
if or_predicate is not None:
self._EnterPredicates( ( or_predicate, ), permit_remove = False )
2020-04-29 21:44:12 +00:00
elif command == 'remove_predicates':
2020-03-18 21:35:57 +00:00
2020-04-29 21:44:12 +00:00
self._EnterPredicates( predicates, permit_add = False )
2020-03-18 21:35:57 +00:00
2020-04-29 21:44:12 +00:00
elif command == 'add_inverse_predicates':
2020-03-18 21:35:57 +00:00
2020-04-29 21:44:12 +00:00
self._EnterPredicates( inverse_predicates, permit_remove = False )
2020-03-18 21:35:57 +00:00
2021-11-10 21:53:57 +00:00
elif command == 'add_namespace_predicate':
self._EnterPredicates( ( namespace_predicate, ), permit_remove = False )
elif command == 'add_inverse_namespace_predicate':
self._EnterPredicates( ( inverse_namespace_predicate, ), permit_remove = False )
2020-03-18 21:35:57 +00:00
def EnterPredicates( self, page_key, predicates, permit_add = True, permit_remove = True ):
if page_key == self._page_key:
self._EnterPredicates( predicates, permit_add = permit_add, permit_remove = permit_remove )
def SetPredicates( self, predicates ):
self._Clear()
2021-02-11 01:59:52 +00:00
terms = [ self._GenerateTermFromPredicate( predicate ) for predicate in predicates ]
self._AppendTerms( terms )
2020-03-18 21:35:57 +00:00
2021-02-11 01:59:52 +00:00
self._Sort()
2020-11-25 22:22:47 +00:00
2020-03-18 21:35:57 +00:00
self._DataHasChanged()
2016-09-21 19:54:04 +00:00
class AutoCompleteDropdownTagsWrite( AutoCompleteDropdownTags ):
2022-05-25 21:30:53 +00:00
nullEntered = QC.Signal()
def __init__( self, parent, chosen_tag_callable, location_context, tag_service_key, tag_service_key_changed_callable = None, show_paste_button = False ):
2016-09-21 19:54:04 +00:00
2020-06-11 12:01:08 +00:00
self._display_tag_service_key = tag_service_key
2016-09-21 19:54:04 +00:00
self._chosen_tag_callable = chosen_tag_callable
2018-11-21 22:22:36 +00:00
self._tag_service_key_changed_callable = tag_service_key_changed_callable
2016-09-21 19:54:04 +00:00
2019-09-18 22:40:39 +00:00
service = HG.client_controller.services_manager.GetService( tag_service_key )
2016-09-21 19:54:04 +00:00
2020-05-20 21:36:02 +00:00
tag_autocomplete_options = HG.client_controller.tag_display_manager.GetTagAutocompleteOptions( tag_service_key )
2022-03-23 20:57:10 +00:00
( location_context, tag_service_key ) = tag_autocomplete_options.GetWriteAutocompleteSearchDomain( location_context )
2017-05-10 21:33:58 +00:00
2022-01-19 21:28:59 +00:00
AutoCompleteDropdownTags.__init__( self, parent, location_context, tag_service_key )
2016-09-21 19:54:04 +00:00
2022-03-23 20:57:10 +00:00
self._location_context_button.SetAllKnownFilesAllowed( True, False )
2020-03-11 21:52:11 +00:00
self._paste_button = ClientGUICommon.BetterBitmapButton( self._text_input_panel, CC.global_pixmaps().paste, self._Paste )
2019-11-14 03:56:30 +00:00
self._paste_button.setToolTip( 'Paste from the clipboard and quick-enter as if you had typed. This can take multiple newline-separated tags.' )
2019-06-26 21:27:18 +00:00
if not show_paste_button:
2019-11-14 03:56:30 +00:00
self._paste_button.hide()
2019-06-26 21:27:18 +00:00
2020-07-29 20:52:44 +00:00
QP.AddToLayout( self._text_input_hbox, self._paste_button, CC.FLAGS_CENTER_PERPENDICULAR )
2019-06-26 21:27:18 +00:00
2019-11-14 03:56:30 +00:00
vbox = QP.VBoxLayout()
2016-09-21 19:54:04 +00:00
2019-11-14 03:56:30 +00:00
hbox = QP.HBoxLayout()
2016-09-21 19:54:04 +00:00
2022-03-23 20:57:10 +00:00
QP.AddToLayout( hbox, self._location_context_button, CC.FLAGS_EXPAND_BOTH_WAYS )
2022-07-20 19:17:03 +00:00
QP.AddToLayout( hbox, self._tag_context_button, CC.FLAGS_EXPAND_BOTH_WAYS )
2016-09-21 19:54:04 +00:00
2019-11-14 03:56:30 +00:00
QP.AddToLayout( vbox, hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
QP.AddToLayout( vbox, self._dropdown_notebook, CC.FLAGS_EXPAND_BOTH_WAYS )
2016-09-21 19:54:04 +00:00
2019-11-14 03:56:30 +00:00
self._dropdown_window.setLayout( vbox )
2016-09-21 19:54:04 +00:00
2019-04-03 22:45:57 +00:00
def _BroadcastChoices( self, predicates, shift_down ):
2016-09-21 19:54:04 +00:00
2020-05-20 21:36:02 +00:00
tags = { predicate.GetValue() for predicate in predicates }
2016-09-21 19:54:04 +00:00
if len( tags ) > 0:
self._chosen_tag_callable( tags )
2018-05-23 21:05:06 +00:00
self._ClearInput()
2016-09-21 19:54:04 +00:00
2022-08-17 20:54:59 +00:00
def _TagContextJustChanged( self, tag_context: ClientSearch.TagContext ):
2016-09-21 19:54:04 +00:00
2022-08-17 20:54:59 +00:00
it_changed = AutoCompleteDropdownTags._TagContextJustChanged( self, tag_context )
2016-09-21 19:54:04 +00:00
2022-08-17 20:54:59 +00:00
if it_changed and self._tag_service_key_changed_callable is not None:
2016-09-21 19:54:04 +00:00
2022-08-17 20:54:59 +00:00
self._tag_service_key_changed_callable( self._tag_service_key )
2016-09-21 19:54:04 +00:00
2020-04-22 21:00:35 +00:00
def _GetCurrentBroadcastTextPredicate( self ) -> typing.Optional[ ClientSearch.Predicate ]:
2018-11-21 22:22:36 +00:00
2020-04-22 21:00:35 +00:00
parsed_autocomplete_text = self._GetParsedAutocompleteText()
2018-11-21 22:22:36 +00:00
2020-04-22 21:00:35 +00:00
if parsed_autocomplete_text.IsTagSearch():
2018-11-21 22:22:36 +00:00
2020-04-22 21:00:35 +00:00
return parsed_autocomplete_text.GetImmediateFileSearchPredicate()
else:
return None
2018-11-21 22:22:36 +00:00
2020-04-22 21:00:35 +00:00
def _GetParsedAutocompleteText( self ) -> ClientSearch.ParsedAutocompleteText:
parsed_autocomplete_text = AutoCompleteDropdownTags._GetParsedAutocompleteText( self )
parsed_autocomplete_text.SetInclusive( True )
return parsed_autocomplete_text
2018-03-28 21:55:58 +00:00
def _InitFavouritesList( self ):
height_num_chars = HG.client_controller.new_options.GetInteger( 'ac_write_list_height_num_chars' )
2021-02-24 22:35:18 +00:00
favs_list = ListBoxTagsStringsAC( self._dropdown_notebook, self.BroadcastChoices, self._display_tag_service_key, self._float_mode, tag_display_type = ClientTags.TAG_DISPLAY_STORAGE, height_num_chars = height_num_chars )
2022-08-24 21:06:25 +00:00
favs_list.SetExtraParentRowsAllowed( HG.client_controller.new_options.GetBoolean( 'expand_parents_on_storage_autocomplete_taglists' ) )
favs_list.SetParentDecoratorsAllowed( HG.client_controller.new_options.GetBoolean( 'show_parent_decorators_on_storage_autocomplete_taglists' ) )
favs_list.SetSiblingDecoratorsAllowed( HG.client_controller.new_options.GetBoolean( 'show_sibling_decorators_on_storage_autocomplete_taglists' ) )
2018-03-28 21:55:58 +00:00
return favs_list
def _InitSearchResultsList( self ):
2016-09-21 19:54:04 +00:00
height_num_chars = HG.client_controller.new_options.GetInteger( 'ac_write_list_height_num_chars' )
2019-11-14 03:56:30 +00:00
2021-02-24 22:35:18 +00:00
preds_list = ListBoxTagsPredicatesAC( self._dropdown_notebook, self.BroadcastChoices, self._display_tag_service_key, self._float_mode, tag_display_type = ClientTags.TAG_DISPLAY_STORAGE, height_num_chars = height_num_chars )
2022-08-24 21:06:25 +00:00
preds_list.SetExtraParentRowsAllowed( HG.client_controller.new_options.GetBoolean( 'expand_parents_on_storage_autocomplete_taglists' ) )
preds_list.SetParentDecoratorsAllowed( HG.client_controller.new_options.GetBoolean( 'show_parent_decorators_on_storage_autocomplete_taglists' ) )
preds_list.SetSiblingDecoratorsAllowed( HG.client_controller.new_options.GetBoolean( 'show_sibling_decorators_on_storage_autocomplete_taglists' ) )
2021-02-24 22:35:18 +00:00
return preds_list
2016-09-21 19:54:04 +00:00
2019-06-26 21:27:18 +00:00
def _Paste( self ):
try:
raw_text = HG.client_controller.GetClipboardText()
except HydrusExceptions.DataMissing as e:
2019-11-14 03:56:30 +00:00
QW.QMessageBox.critical( self, 'Error', str(e) )
2019-06-26 21:27:18 +00:00
return
try:
tags = [ text for text in HydrusText.DeserialiseNewlinedTexts( raw_text ) ]
tags = HydrusTags.CleanTags( tags )
2022-01-12 22:14:50 +00:00
entry_predicates = [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_TAG, value = tag ) for tag in tags ]
2019-06-26 21:27:18 +00:00
if len( entry_predicates ) > 0:
shift_down = False
self._BroadcastChoices( entry_predicates, shift_down )
except:
2019-11-14 03:56:30 +00:00
QW.QMessageBox.critical( self, 'Error', 'I could not understand what was in the clipboard' )
2019-07-17 22:10:19 +00:00
2019-06-26 21:27:18 +00:00
raise
2019-07-17 22:10:19 +00:00
2019-06-26 21:27:18 +00:00
2019-01-30 22:14:54 +00:00
def _ShouldTakeResponsibilityForEnter( self ):
2016-09-21 19:54:04 +00:00
2020-04-22 21:00:35 +00:00
parsed_autocomplete_text = self._GetParsedAutocompleteText()
2016-09-21 19:54:04 +00:00
2019-11-14 03:56:30 +00:00
looking_at_search_results = self._dropdown_notebook.currentWidget() == self._search_results_list
2019-08-07 22:59:53 +00:00
2020-04-22 21:00:35 +00:00
sitting_on_empty = parsed_autocomplete_text.IsEmpty()
2016-09-21 19:54:04 +00:00
2020-04-22 21:00:35 +00:00
something_to_broadcast = self._GetCurrentBroadcastTextPredicate() is not None
2019-08-07 22:59:53 +00:00
2019-08-15 00:40:48 +00:00
# the list has results, but they are out of sync with what we have currently entered
2016-09-21 19:54:04 +00:00
# when the user has quickly typed something in and the results are not yet in
2020-04-22 21:00:35 +00:00
results_desynced_with_text = parsed_autocomplete_text != self._current_list_parsed_autocomplete_text
2016-09-21 19:54:04 +00:00
2019-08-15 00:40:48 +00:00
p1 = something_to_broadcast and results_desynced_with_text
2016-09-21 19:54:04 +00:00
2019-08-15 00:40:48 +00:00
# when the text ctrl is empty and we want to push a None to the parent dialog
p2 = sitting_on_empty
2016-09-21 19:54:04 +00:00
2019-08-15 00:40:48 +00:00
return looking_at_search_results and ( p1 or p2 )
2016-09-21 19:54:04 +00:00
2020-04-01 21:51:42 +00:00
def _StartSearchResultsFetchJob( self, job_key ):
2019-01-30 22:14:54 +00:00
2020-04-22 21:00:35 +00:00
parsed_autocomplete_text = self._GetParsedAutocompleteText()
2019-01-30 22:14:54 +00:00
2019-07-17 22:10:19 +00:00
stub_predicates = []
2020-06-11 12:01:08 +00:00
InsertTagPredicates( stub_predicates, self._display_tag_service_key, parsed_autocomplete_text )
2020-04-22 21:00:35 +00:00
2019-07-17 22:10:19 +00:00
AppendLoadingPredicate( stub_predicates )
2021-06-23 21:11:38 +00:00
HG.client_controller.CallLaterQtSafe( self, 0.2, 'set stub predicates', self.SetStubPredicates, job_key, stub_predicates, parsed_autocomplete_text )
2019-07-17 22:10:19 +00:00
2022-07-20 19:17:03 +00:00
tag_context = ClientSearch.TagContext( service_key = self._tag_service_key, display_service_key = self._display_tag_service_key )
2020-08-27 01:00:42 +00:00
2022-07-20 19:17:03 +00:00
file_search_context = ClientSearch.FileSearchContext( location_context = self._location_context_button.GetValue(), tag_context = tag_context )
2022-01-12 22:14:50 +00:00
HG.client_controller.CallToThread( WriteFetch, self, job_key, self.SetFetchedResults, parsed_autocomplete_text, file_search_context, self._results_cache )
2019-01-30 22:14:54 +00:00
2019-04-03 22:45:57 +00:00
def _TakeResponsibilityForEnter( self, shift_down ):
2016-09-21 19:54:04 +00:00
2020-04-22 21:00:35 +00:00
parsed_autocomplete_text = self._GetParsedAutocompleteText()
if parsed_autocomplete_text.IsEmpty() and self._dropdown_notebook.currentWidget() == self._search_results_list:
2016-09-21 19:54:04 +00:00
2022-05-25 21:30:53 +00:00
self.nullEntered.emit()
2016-09-21 19:54:04 +00:00
else:
2020-04-22 21:00:35 +00:00
current_broadcast_predicate = self._GetCurrentBroadcastTextPredicate()
if current_broadcast_predicate is not None:
self._BroadcastChoices( { current_broadcast_predicate }, shift_down )
2016-09-21 19:54:04 +00:00
2016-12-21 22:30:54 +00:00
2018-04-05 01:22:26 +00:00
def RefreshFavouriteTags( self ):
2020-05-13 19:03:16 +00:00
favourite_tags = sorted( HG.client_controller.new_options.GetStringList( 'favourite_tags' ) )
2018-04-05 01:22:26 +00:00
2021-02-24 22:35:18 +00:00
self._favourites_list.SetTags( favourite_tags )
2018-04-05 01:22:26 +00:00
2020-09-09 20:59:19 +00:00
def SetDisplayTagServiceKey( self, service_key ):
self._display_tag_service_key = service_key
2020-02-12 22:50:37 +00:00
class EditAdvancedORPredicates( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent, initial_string = None ):
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
self._input_text = QW.QLineEdit( self )
self._result_preview = QW.QPlainTextEdit()
self._result_preview.setReadOnly( True )
( width, height ) = ClientGUIFunctions.ConvertTextToPixels( self._result_preview, ( 64, 6 ) )
self._result_preview.setMinimumWidth( width )
self._result_preview.setMinimumHeight( height )
self._current_predicates = []
#
if initial_string is not None:
self._input_text.setText( initial_string )
#
rows = []
rows.append( ( 'Input: ', self._input_text ) )
rows.append( ( 'Result preview: ', self._result_preview ) )
gridbox = ClientGUICommon.WrapInGrid( self, rows )
vbox = QP.VBoxLayout()
summary = 'Enter a complicated tag search here as text, such as \'( blue eyes and blonde hair ) or ( green eyes and red hair )\', and this should turn it into hydrus-compatible search predicates.'
summary += os.linesep * 2
2022-07-13 21:35:17 +00:00
summary += 'Accepted operators: not (!, -), and (&&), or (||), implies (=>), xor, xnor (iff, <=>), nand, nor. Many system predicates are also supported.'
2020-02-12 22:50:37 +00:00
summary += os.linesep * 2
2020-04-29 21:44:12 +00:00
summary += 'Parentheses work the usual way. \\ can be used to escape characters (e.g. to search for tags including parentheses)'
2020-02-12 22:50:37 +00:00
st = ClientGUICommon.BetterStaticText( self, summary )
st.setWordWrap( True )
QP.AddToLayout( vbox, st, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, gridbox, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
self.widget().setLayout( vbox )
self._UpdateText()
self._input_text.textChanged.connect( self.EventUpdateText )
2022-07-13 21:35:17 +00:00
ClientGUIFunctions.SetFocusLater( self._input_text )
2020-02-12 22:50:37 +00:00
def _UpdateText( self ):
text = self._input_text.text()
self._current_predicates = []
object_name = ''
2020-02-12 22:50:37 +00:00
output = ''
if len( text ) > 0:
try:
# this makes a list of sets, each set representing a list of AND preds
result = LogicExpressionQueryParser.parse_logic_expression_query( text )
for s in result:
2022-07-13 21:35:17 +00:00
tag_preds = []
system_preds = []
negated_system_pred_strings = []
2022-07-13 21:35:17 +00:00
system_pred_strings = []
2020-02-12 22:50:37 +00:00
for tag_string in s:
if tag_string.startswith( '-system:' ):
negated_system_pred_strings.append( tag_string )
continue
2022-07-13 21:35:17 +00:00
if tag_string.startswith( 'system:' ):
system_pred_strings.append( tag_string )
continue
2020-02-12 22:50:37 +00:00
if tag_string.startswith( '-' ):
inclusive = False
tag_string = tag_string[1:]
else:
inclusive = True
2022-07-13 21:35:17 +00:00
try:
tag_string = HydrusTags.CleanTag( tag_string )
HydrusTags.CheckTagNotEmpty( tag_string )
except Exception as e:
raise ValueError( str( e ) )
2020-02-12 22:50:37 +00:00
if '*' in tag_string:
( namespace, subtag ) = HydrusTags.SplitTag( tag_string )
2020-07-22 20:59:16 +00:00
if len( namespace ) > 0 and subtag == '*':
2020-02-12 22:50:37 +00:00
2022-01-12 22:14:50 +00:00
row_pred = ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_NAMESPACE, value = namespace, inclusive = inclusive )
2020-02-12 22:50:37 +00:00
else:
2022-01-12 22:14:50 +00:00
row_pred = ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, value = tag_string, inclusive = inclusive )
2020-02-12 22:50:37 +00:00
else:
2022-01-12 22:14:50 +00:00
row_pred = ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_TAG, value = tag_string, inclusive = inclusive )
2020-02-12 22:50:37 +00:00
2022-07-13 21:35:17 +00:00
tag_preds.append( row_pred )
2020-02-12 22:50:37 +00:00
if len( negated_system_pred_strings ) > 0:
raise ValueError( 'Sorry, that would make negated system tags, which are not supported yet! Try to rephrase or negate the system tag yourself.' )
2022-07-13 21:35:17 +00:00
if len( system_pred_strings ) > 0:
try:
system_preds = ClientSearchParseSystemPredicates.ParseSystemPredicateStringsToPredicates( system_pred_strings )
except Exception as e:
raise ValueError( str( e ) )
row_preds = tag_preds + system_preds
2020-02-12 22:50:37 +00:00
if len( row_preds ) == 1:
self._current_predicates.append( row_preds[0] )
else:
2022-01-12 22:14:50 +00:00
self._current_predicates.append( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_OR_CONTAINER, value = row_preds ) )
2020-02-12 22:50:37 +00:00
output = os.linesep.join( ( pred.ToString() for pred in self._current_predicates ) )
object_name = 'HydrusValid'
2020-02-12 22:50:37 +00:00
except ValueError as e:
2020-02-12 22:50:37 +00:00
output = 'Could not parse! {}'.format( e )
object_name = 'HydrusInvalid'
2020-02-12 22:50:37 +00:00
self._result_preview.setPlainText( output )
self._result_preview.setObjectName( object_name )
self._result_preview.style().polish( self._result_preview )
2020-02-12 22:50:37 +00:00
def EventUpdateText( self, text ):
self._UpdateText()
def GetValue( self ):
self._UpdateText()
if len( self._current_predicates ) == 0:
raise HydrusExceptions.VetoException( 'Please enter a string that parses into a set of search rules.' )
return self._current_predicates