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
2020-08-05 20:10:36 +00:00
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
2022-01-05 22:15:56 +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
2020-07-08 22:00:33 +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 )
2020-07-08 22:00:33 +00:00
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 ) :
2022-11-09 22:24:07 +00:00
# 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
2022-11-09 22:24:07 +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
2020-07-08 22:00:33 +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 ) :
2022-01-19 21:38:58 +00:00
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
2022-03-02 21:44:08 +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
2020-07-08 22:00:33 +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
2020-07-08 22:00:33 +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 )
2022-11-09 22:24:07 +00:00
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 :
2022-11-09 22:24:07 +00:00
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
2022-11-09 22:24:07 +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 ) :
2020-07-08 22:00:33 +00:00
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 ) :
2020-07-08 22:00:33 +00:00
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 )
2020-07-08 22:00:33 +00:00
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 )
2022-11-09 22:24:07 +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 ( )
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 ) :
2020-07-08 22:00:33 +00:00
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
2020-07-08 22:00:33 +00:00
def PausePlaySearch ( self ) :
2016-09-21 19:54:04 +00:00
2020-07-08 22:00:33 +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 ) :
2020-07-08 22:00:33 +00:00
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
2020-07-08 22:00:33 +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 = [ ]
2021-09-08 21:41:52 +00:00
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 = [ ]
2022-07-27 21:18:33 +00:00
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 :
2022-07-27 21:18:33 +00:00
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
2022-07-27 21:18:33 +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 ) )
2021-09-08 21:41:52 +00:00
object_name = ' HydrusValid '
2020-02-12 22:50:37 +00:00
2022-07-27 21:18:33 +00:00
except ValueError as e :
2020-02-12 22:50:37 +00:00
2022-07-27 21:18:33 +00:00
output = ' Could not parse! {} ' . format ( e )
2021-09-08 21:41:52 +00:00
object_name = ' HydrusInvalid '
2020-02-12 22:50:37 +00:00
self . _result_preview . setPlainText ( output )
2021-09-08 21:41:52 +00:00
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