2016-09-21 19:54:04 +00:00
import ClientCaches
import ClientConstants as CC
2017-02-08 22:27:00 +00:00
import ClientData
2016-09-21 19:54:04 +00:00
import ClientGUICommon
2017-02-01 21:11:17 +00:00
import ClientGUIListBoxes
2017-03-29 19:39:34 +00:00
import ClientGUIMenus
2016-09-21 19:54:04 +00:00
import ClientSearch
import collections
import HydrusConstants as HC
import HydrusExceptions
2017-05-10 21:33:58 +00:00
import HydrusGlobals as HG
2016-09-21 19:54:04 +00:00
import HydrusTags
import itertools
import wx
ID_TIMER_DROPDOWN_HIDE = wx . NewId ( )
ID_TIMER_AC_LAG = wx . NewId ( )
# much of this is based on the excellent TexCtrlAutoComplete class by Edward Flick, Michele Petrazzo and Will Sadkin, just with plenty of simplification and integration into hydrus
class AutoCompleteDropdown ( wx . Panel ) :
def __init__ ( self , parent ) :
wx . Panel . __init__ ( self , parent )
self . _intercept_key_events = True
self . _last_search_text = ' '
self . _next_updatelist_is_probably_fast = False
tlp = self . GetTopLevelParent ( )
# There's a big bug in wx where FRAME_FLOAT_ON_PARENT Frames don't get passed their mouse events if their parent is a Dialog jej
# I think it is something to do with the initialisation order; if the frame is init'ed before the ShowModal call, but whatever.
# This turned out to be ugly when I added the manage tags frame, so I've set it to if the tlp has a parent, which basically means "not the main gui"
2018-01-03 22:37:30 +00:00
if tlp . GetParent ( ) is not None or HC . options [ ' always_embed_autocompletes ' ] :
2016-09-21 19:54:04 +00:00
self . _float_mode = False
else :
self . _float_mode = True
self . _text_ctrl = wx . TextCtrl ( self , style = wx . TE_PROCESS_ENTER )
2017-09-20 19:47:31 +00:00
self . _UpdateBackgroundColour ( )
2016-09-21 19:54:04 +00:00
self . _last_attempted_dropdown_width = 0
self . _last_attempted_dropdown_position = ( None , None )
if self . _float_mode :
self . _text_ctrl . Bind ( wx . EVT_SET_FOCUS , self . EventSetFocus )
self . _text_ctrl . Bind ( wx . EVT_KILL_FOCUS , self . EventKillFocus )
self . _text_ctrl . Bind ( wx . EVT_TEXT , self . EventText )
2017-04-19 20:58:30 +00:00
self . _text_ctrl . Bind ( wx . EVT_CHAR_HOOK , self . EventCharHook )
2016-09-21 19:54:04 +00:00
self . _text_ctrl . Bind ( wx . EVT_MOUSEWHEEL , self . EventMouseWheel )
vbox = wx . BoxSizer ( wx . VERTICAL )
2018-01-03 22:37:30 +00:00
vbox . Add ( self . _text_ctrl , CC . FLAGS_EXPAND_PERPENDICULAR )
2016-09-21 19:54:04 +00:00
#self._dropdown_window = wx.PopupWindow( self, flags = wx.BORDER_RAISED )
#self._dropdown_window = wx.PopupTransientWindow( self, style = wx.BORDER_RAISED )
#self._dropdown_window = wx.Window( self, style = wx.BORDER_RAISED )
#self._dropdown_window = wx.Panel( self )
if self . _float_mode :
self . _dropdown_window = wx . Frame ( self , style = wx . FRAME_TOOL_WINDOW | wx . FRAME_NO_TASKBAR | wx . FRAME_FLOAT_ON_PARENT | wx . BORDER_RAISED )
self . _dropdown_window . SetBackgroundColour ( wx . SystemSettings . GetColour ( wx . SYS_COLOUR_FRAMEBK ) )
self . _dropdown_window . SetSize ( ( 0 , 0 ) )
2017-05-31 21:50:53 +00:00
if self . _text_ctrl . IsShown ( ) :
2017-08-02 21:32:54 +00:00
self . _dropdown_window . SetPosition ( self . _text_ctrl . ClientToScreen ( ( 0 , 0 ) ) )
2017-05-31 21:50:53 +00:00
2016-09-21 19:54:04 +00:00
self . _dropdown_window . Show ( )
self . _dropdown_window . Bind ( wx . EVT_CLOSE , self . EventCloseDropdown )
self . _dropdown_hidden = True
self . _list_height = 250
else :
self . _dropdown_window = wx . Panel ( self )
self . _list_height = 125
self . _dropdown_list = self . _InitDropDownList ( )
2018-01-03 22:37:30 +00:00
if not self . _float_mode : vbox . Add ( self . _dropdown_window , CC . FLAGS_EXPAND_BOTH_WAYS )
2016-09-21 19:54:04 +00:00
self . SetSizer ( vbox )
2017-03-08 23:23:12 +00:00
self . _cache_text = None
2016-09-21 19:54:04 +00:00
self . _cached_results = [ ]
self . _initial_matches_fetched = False
2018-01-03 22:37:30 +00:00
self . _move_hide_timer = None
2016-09-21 19:54:04 +00:00
if self . _float_mode :
self . Bind ( wx . EVT_MOVE , self . EventMove )
self . Bind ( wx . EVT_SIZE , self . EventMove )
self . Bind ( wx . EVT_TIMER , self . TIMEREventDropdownHide , id = ID_TIMER_DROPDOWN_HIDE )
self . _move_hide_timer = wx . Timer ( self , id = ID_TIMER_DROPDOWN_HIDE )
self . _move_hide_timer . Start ( 1 , wx . TIMER_ONE_SHOT )
tlp . Bind ( wx . EVT_MOVE , self . EventMove )
parent = self
while True :
try :
parent = parent . GetParent ( )
if isinstance ( parent , wx . ScrolledWindow ) :
parent . Bind ( wx . EVT_SCROLLWIN , self . EventMove )
except :
break
self . Bind ( wx . EVT_TIMER , self . TIMEREventLag , id = ID_TIMER_AC_LAG )
2017-09-20 19:47:31 +00:00
HG . client_controller . sub ( self , ' _UpdateBackgroundColour ' , ' notify_new_colourset ' )
2016-09-21 19:54:04 +00:00
self . _lag_timer = wx . Timer ( self , id = ID_TIMER_AC_LAG )
wx . CallAfter ( self . _UpdateList )
def _BroadcastChoices ( self , predicates ) :
raise NotImplementedError ( )
def _BroadcastCurrentText ( self ) :
text = self . _text_ctrl . GetValue ( )
self . _BroadcastChoices ( { text } )
def _GenerateMatches ( self ) :
raise NotImplementedError ( )
def _HideDropdown ( self ) :
if not self . _dropdown_hidden :
self . _dropdown_window . SetSize ( ( 0 , 0 ) )
self . _dropdown_hidden = True
def _InitDropDownList ( self ) :
raise NotImplementedError ( )
def _ShouldShow ( self ) :
tlp_active = self . GetTopLevelParent ( ) . IsActive ( ) or self . _dropdown_window . IsActive ( )
if HC . PLATFORM_LINUX :
tlp = self . GetTopLevelParent ( )
if isinstance ( tlp , wx . Dialog ) :
visible = True
else :
# notebook on linux doesn't 'hide' things apparently, so isshownonscreen, which recursively tests parents' hide status, doesn't work!
2017-05-10 21:33:58 +00:00
gui = HG . client_controller . GetGUI ( )
2016-09-21 19:54:04 +00:00
current_page = gui . GetCurrentPage ( )
visible = ClientGUICommon . IsWXAncestor ( self , current_page )
else :
visible = self . _text_ctrl . IsShownOnScreen ( )
2017-04-19 20:58:30 +00:00
focus_remains_on_self_or_children = ClientGUICommon . WindowOrAnyTLPChildHasFocus ( self )
2016-09-21 19:54:04 +00:00
return tlp_active and visible and focus_remains_on_self_or_children
def _ShouldTakeResponsibilityForEnter ( self ) :
raise NotImplementedError ( )
def _ShowDropdown ( self ) :
( text_width , text_height ) = self . _text_ctrl . GetSize ( )
2017-05-31 21:50:53 +00:00
if self . _text_ctrl . IsShown ( ) :
2016-09-21 19:54:04 +00:00
2017-08-02 21:32:54 +00:00
desired_dropdown_position = self . _text_ctrl . ClientToScreen ( ( - 2 , text_height - 2 ) )
2016-09-21 19:54:04 +00:00
2017-05-31 21:50:53 +00:00
if self . _last_attempted_dropdown_position != desired_dropdown_position :
self . _dropdown_window . SetPosition ( desired_dropdown_position )
self . _last_attempted_dropdown_position = desired_dropdown_position
2016-09-21 19:54:04 +00:00
#
show_and_fit_needed = False
if self . _dropdown_hidden :
show_and_fit_needed = True
else :
if text_width != self . _last_attempted_dropdown_width :
show_and_fit_needed = True
if show_and_fit_needed :
self . _dropdown_window . Fit ( )
self . _dropdown_window . SetSize ( ( text_width , - 1 ) )
self . _dropdown_window . Layout ( )
self . _dropdown_hidden = False
self . _last_attempted_dropdown_width = text_width
def _TakeResponsibilityForEnter ( self ) :
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
if not self . _intercept_key_events :
colour = ClientData . GetLighterDarkerColour ( colour )
self . _text_ctrl . SetBackgroundColour ( colour )
self . _text_ctrl . Refresh ( )
2016-09-21 19:54:04 +00:00
def _UpdateList ( self ) :
pass
def BroadcastChoices ( self , predicates ) :
self . _BroadcastChoices ( predicates )
2018-01-03 22:37:30 +00:00
def CleanBeforeDestroy ( self ) :
if self . _move_hide_timer is not None :
self . _move_hide_timer . Stop ( )
self . _move_hide_timer = None
self . _lag_timer . Stop ( )
self . _lag_timer = None
2017-04-19 20:58:30 +00:00
def EventCharHook ( 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
2017-04-12 21:46:46 +00:00
( modifier , key ) = ClientData . ConvertKeyEventToSimpleTuple ( event )
2017-04-05 21:16:40 +00:00
if key in ( wx . WXK_INSERT , wx . WXK_NUMPAD_INSERT ) :
2016-09-21 19:54:04 +00:00
2017-09-20 19:47:31 +00:00
self . _intercept_key_events = not self . _intercept_key_events
2017-09-13 20:50:41 +00:00
2017-09-20 19:47:31 +00:00
self . _UpdateBackgroundColour ( )
2016-09-21 19:54:04 +00:00
2017-04-05 21:16:40 +00:00
elif key == wx . WXK_SPACE and event . RawControlDown ( ) : # this is control, not command on os x, for which command+space does some os stuff
2016-09-21 19:54:04 +00:00
self . _UpdateList ( )
self . _lag_timer . Stop ( )
elif self . _intercept_key_events :
2017-04-05 21:16:40 +00:00
if key in ( ord ( ' A ' ) , ord ( ' a ' ) ) and modifier == wx . ACCEL_CTRL :
2016-09-21 19:54:04 +00:00
event . Skip ( )
2017-04-05 21:16:40 +00:00
elif key in ( wx . WXK_RETURN , wx . WXK_NUMPAD_ENTER ) and self . _ShouldTakeResponsibilityForEnter ( ) :
2016-09-21 19:54:04 +00:00
self . _TakeResponsibilityForEnter ( )
2017-04-05 21:16:40 +00:00
elif key in ( wx . WXK_UP , wx . WXK_NUMPAD_UP , wx . WXK_DOWN , wx . WXK_NUMPAD_DOWN ) and self . _text_ctrl . GetValue ( ) == ' ' and len ( self . _dropdown_list ) == 0 :
2016-09-21 19:54:04 +00:00
2017-09-20 19:47:31 +00:00
if key in ( wx . WXK_UP , wx . WXK_NUMPAD_UP ) :
id = ClientCaches . MENU_EVENT_ID_TO_ACTION_CACHE . GetTemporaryId ( ' select_up ' )
elif key in ( wx . WXK_DOWN , wx . WXK_NUMPAD_DOWN ) :
id = ClientCaches . MENU_EVENT_ID_TO_ACTION_CACHE . GetTemporaryId ( ' select_down ' )
2016-09-21 19:54:04 +00:00
2018-01-03 22:37:30 +00:00
new_event = wx . CommandEvent ( commandEventType = wx . wxEVT_COMMAND_MENU_SELECTED , id = id )
2016-09-21 19:54:04 +00:00
2017-04-19 20:58:30 +00:00
self . ProcessEvent ( new_event )
2016-09-21 19:54:04 +00:00
2017-04-05 21:16:40 +00:00
elif key in ( wx . WXK_PAGEDOWN , wx . WXK_NUMPAD_PAGEDOWN , wx . WXK_PAGEUP , wx . WXK_NUMPAD_PAGEUP ) and self . _text_ctrl . GetValue ( ) == ' ' and len ( self . _dropdown_list ) == 0 :
2016-09-21 19:54:04 +00:00
2017-04-05 21:16:40 +00:00
if key in ( wx . WXK_PAGEUP , wx . WXK_NUMPAD_PAGEUP ) :
2016-09-21 19:54:04 +00:00
id = ClientCaches . MENU_EVENT_ID_TO_ACTION_CACHE . GetTemporaryId ( ' canvas_show_previous ' )
2017-04-05 21:16:40 +00:00
elif key in ( wx . WXK_PAGEDOWN , wx . WXK_NUMPAD_PAGEDOWN ) :
2016-09-21 19:54:04 +00:00
id = ClientCaches . MENU_EVENT_ID_TO_ACTION_CACHE . GetTemporaryId ( ' canvas_show_next ' )
2018-01-03 22:37:30 +00:00
new_event = wx . CommandEvent ( commandEventType = wx . wxEVT_COMMAND_MENU_SELECTED , id = id )
2016-09-21 19:54:04 +00:00
2017-04-19 20:58:30 +00:00
self . ProcessEvent ( new_event )
2016-09-21 19:54:04 +00:00
else :
2017-07-19 21:21:41 +00:00
# Don't say processevent here--it duplicates the event processing at higher levels, leading to 2 x F9, for instance
self . _dropdown_list . EventCharHook ( event ) # this typically skips the event, letting the text ctrl take it
2016-09-21 19:54:04 +00:00
else :
event . Skip ( )
2017-04-19 20:58:30 +00:00
def EventCloseDropdown ( self , event ) :
2017-05-10 21:33:58 +00:00
HG . client_controller . GetGUI ( ) . Close ( )
2017-04-19 20:58:30 +00:00
2016-09-21 19:54:04 +00:00
def EventKillFocus ( self , event ) :
2018-01-03 22:37:30 +00:00
if self . _move_hide_timer :
self . _move_hide_timer . Start ( 1 , wx . TIMER_ONE_SHOT )
2016-09-21 19:54:04 +00:00
event . Skip ( )
def EventMouseWheel ( self , event ) :
if self . _text_ctrl . GetValue ( ) == ' ' and len ( self . _dropdown_list ) == 0 :
if event . GetWheelRotation ( ) > 0 : id = ClientCaches . MENU_EVENT_ID_TO_ACTION_CACHE . GetTemporaryId ( ' select_up ' )
else : id = ClientCaches . MENU_EVENT_ID_TO_ACTION_CACHE . GetTemporaryId ( ' select_down ' )
2018-01-03 22:37:30 +00:00
new_event = wx . CommandEvent ( commandEventType = wx . wxEVT_COMMAND_MENU_SELECTED , id = id )
2016-09-21 19:54:04 +00:00
self . ProcessEvent ( new_event )
else :
if event . CmdDown ( ) :
2017-04-19 20:58:30 +00:00
key_event = wx . KeyEvent ( wx . EVT_CHAR_HOOK . typeId )
2016-09-21 19:54:04 +00:00
2017-04-19 20:58:30 +00:00
if event . GetWheelRotation ( ) > 0 :
key_event . m_keyCode = wx . WXK_UP
else :
key_event . m_keyCode = wx . WXK_DOWN
2016-09-21 19:54:04 +00:00
self . _dropdown_list . ProcessEvent ( key_event )
else :
# for some reason, the scrolledwindow list doesn't process scroll events properly when in a popupwindow
# so let's just tell it to scroll manually
( start_x , start_y ) = self . _dropdown_list . GetViewStart ( )
if event . GetWheelRotation ( ) > 0 : self . _dropdown_list . Scroll ( - 1 , start_y - 3 )
else : self . _dropdown_list . Scroll ( - 1 , start_y + 3 )
if event . GetWheelRotation ( ) > 0 : command_type = wx . wxEVT_SCROLLWIN_LINEUP
else : command_type = wx . wxEVT_SCROLLWIN_LINEDOWN
wx . PostEvent ( self , wx . ScrollWinEvent ( command_type ) )
def EventMove ( self , event ) :
2018-01-03 22:37:30 +00:00
self . _HideDropdown ( )
if self . _move_hide_timer :
2016-09-21 19:54:04 +00:00
self . _move_hide_timer . Start ( 250 , wx . TIMER_ONE_SHOT )
event . Skip ( )
def EventSetFocus ( self , event ) :
2018-01-03 22:37:30 +00:00
if self . _move_hide_timer :
self . _move_hide_timer . Start ( 1 , wx . TIMER_ONE_SHOT )
2016-09-21 19:54:04 +00:00
event . Skip ( )
def EventText ( self , event ) :
num_chars = len ( self . _text_ctrl . GetValue ( ) )
if num_chars == 0 :
self . _UpdateList ( )
elif HC . options [ ' fetch_ac_results_automatically ' ] :
( char_limit , long_wait , short_wait ) = HC . options [ ' ac_timings ' ]
self . _next_updatelist_is_probably_fast = self . _next_updatelist_is_probably_fast and num_chars > len ( self . _last_search_text )
if self . _next_updatelist_is_probably_fast : self . _UpdateList ( )
elif num_chars < char_limit : self . _lag_timer . Start ( long_wait , wx . TIMER_ONE_SHOT )
else : self . _lag_timer . Start ( short_wait , wx . TIMER_ONE_SHOT )
def RefreshList ( self ) :
2017-03-08 23:23:12 +00:00
self . _cache_text = None
2016-09-21 19:54:04 +00:00
self . _UpdateList ( )
def TIMEREventDropdownHide ( self , event ) :
try :
should_show = self . _ShouldShow ( )
if should_show :
self . _ShowDropdown ( )
else :
self . _HideDropdown ( )
2018-01-03 22:37:30 +00:00
if self . _move_hide_timer :
self . _move_hide_timer . Start ( 250 , wx . TIMER_ONE_SHOT )
2016-09-21 19:54:04 +00:00
except :
2018-01-03 22:37:30 +00:00
if self . _move_hide_timer :
self . _move_hide_timer . Stop ( )
2016-09-21 19:54:04 +00:00
raise
def TIMEREventLag ( self , event ) :
try :
self . _UpdateList ( )
except :
self . _lag_timer . Stop ( )
raise
class AutoCompleteDropdownTags ( AutoCompleteDropdown ) :
def __init__ ( self , parent , file_service_key , tag_service_key ) :
self . _file_service_key = file_service_key
self . _tag_service_key = tag_service_key
AutoCompleteDropdown . __init__ ( self , parent )
self . _current_matches = [ ]
2017-06-28 20:23:21 +00:00
file_service = HG . client_controller . services_manager . GetService ( self . _file_service_key )
2016-12-21 22:30:54 +00:00
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
2016-10-26 20:45:34 +00:00
self . _file_repo_button = ClientGUICommon . BetterButton ( self . _dropdown_window , file_service . GetName ( ) , self . FileButtonHit )
2016-09-21 19:54:04 +00:00
self . _file_repo_button . SetMinSize ( ( 20 , - 1 ) )
2016-10-26 20:45:34 +00:00
self . _tag_repo_button = ClientGUICommon . BetterButton ( self . _dropdown_window , tag_service . GetName ( ) , self . TagButtonHit )
2016-09-21 19:54:04 +00:00
self . _tag_repo_button . SetMinSize ( ( 20 , - 1 ) )
def _ChangeFileService ( self , file_service_key ) :
if file_service_key == CC . COMBINED_FILE_SERVICE_KEY and self . _tag_service_key == CC . COMBINED_TAG_SERVICE_KEY :
self . _ChangeTagService ( CC . LOCAL_TAG_SERVICE_KEY )
self . _file_service_key = file_service_key
2017-06-28 20:23:21 +00:00
file_service = HG . client_controller . services_manager . GetService ( self . _file_service_key )
2016-09-21 19:54:04 +00:00
name = file_service . GetName ( )
self . _file_repo_button . SetLabelText ( name )
wx . CallAfter ( self . RefreshList )
def _ChangeTagService ( self , tag_service_key ) :
if tag_service_key == CC . COMBINED_TAG_SERVICE_KEY and self . _file_service_key == CC . COMBINED_FILE_SERVICE_KEY :
self . _ChangeFileService ( CC . LOCAL_FILE_SERVICE_KEY )
self . _tag_service_key = tag_service_key
self . _dropdown_list . SetTagService ( self . _tag_service_key )
2017-06-28 20:23:21 +00:00
tag_service = tag_service = HG . client_controller . services_manager . GetService ( self . _tag_service_key )
2016-09-21 19:54:04 +00:00
name = tag_service . GetName ( )
self . _tag_repo_button . SetLabelText ( name )
2017-03-08 23:23:12 +00:00
self . _cache_text = None
2016-09-21 19:54:04 +00:00
wx . CallAfter ( self . RefreshList )
def _UpdateList ( self ) :
self . _last_search_text = self . _text_ctrl . GetValue ( )
matches = self . _GenerateMatches ( )
self . _initial_matches_fetched = True
self . _dropdown_list . SetPredicates ( matches )
self . _current_matches = matches
num_chars = len ( self . _text_ctrl . GetValue ( ) )
if num_chars == 0 :
self . _lag_timer . Start ( 5 * 60 * 1000 , wx . TIMER_ONE_SHOT )
2016-10-26 20:45:34 +00:00
def FileButtonHit ( self ) :
2017-06-28 20:23:21 +00:00
services_manager = HG . client_controller . services_manager
2016-10-26 20:45:34 +00:00
services = [ ]
2016-12-21 22:30:54 +00:00
2016-10-26 20:45:34 +00:00
services . append ( services_manager . GetService ( CC . LOCAL_FILE_SERVICE_KEY ) )
services . append ( services_manager . GetService ( CC . TRASH_SERVICE_KEY ) )
2016-12-21 22:30:54 +00:00
services . append ( services_manager . GetService ( CC . COMBINED_LOCAL_FILE_SERVICE_KEY ) )
2016-10-26 20:45:34 +00:00
services . extend ( services_manager . GetServices ( ( HC . FILE_REPOSITORY , ) ) )
2017-11-22 21:03:07 +00:00
advanced_mode = HG . client_controller . new_options . GetBoolean ( ' advanced_mode ' )
if advanced_mode :
services . append ( services_manager . GetService ( CC . COMBINED_FILE_SERVICE_KEY ) )
2016-10-26 20:45:34 +00:00
menu = wx . Menu ( )
2016-12-21 22:30:54 +00:00
for service in services :
2017-03-29 19:39:34 +00:00
ClientGUIMenus . AppendMenuItem ( self , menu , service . GetName ( ) , ' Change the current file domain to ' + service . GetName ( ) + ' . ' , self . _ChangeFileService , service . GetServiceKey ( ) )
2016-12-21 22:30:54 +00:00
2016-10-26 20:45:34 +00:00
2017-05-10 21:33:58 +00:00
HG . client_controller . PopupMenu ( self . _file_repo_button , menu )
2016-10-26 20:45:34 +00:00
def SetFileService ( self , file_service_key ) :
self . _ChangeFileService ( file_service_key )
def SetTagService ( self , tag_service_key ) :
self . _ChangeTagService ( tag_service_key )
def TagButtonHit ( self ) :
2016-09-21 19:54:04 +00:00
2017-06-28 20:23:21 +00:00
services_manager = HG . client_controller . services_manager
2016-09-21 19:54:04 +00:00
services = [ ]
2016-12-21 22:30:54 +00:00
2016-09-21 19:54:04 +00:00
services . append ( services_manager . GetService ( CC . LOCAL_TAG_SERVICE_KEY ) )
services . extend ( services_manager . GetServices ( ( HC . TAG_REPOSITORY , ) ) )
2016-12-21 22:30:54 +00:00
services . append ( services_manager . GetService ( CC . COMBINED_TAG_SERVICE_KEY ) )
2016-09-21 19:54:04 +00:00
menu = wx . Menu ( )
2016-12-21 22:30:54 +00:00
for service in services :
2017-03-29 19:39:34 +00:00
ClientGUIMenus . AppendMenuItem ( self , menu , service . GetName ( ) , ' Change the current tag domain to ' + service . GetName ( ) + ' . ' , self . _ChangeTagService , service . GetServiceKey ( ) )
2016-12-21 22:30:54 +00:00
2016-09-21 19:54:04 +00:00
2017-05-10 21:33:58 +00:00
HG . client_controller . PopupMenu ( self . _tag_repo_button , menu )
2016-09-21 19:54:04 +00:00
class AutoCompleteDropdownTagsRead ( AutoCompleteDropdownTags ) :
def __init__ ( self , parent , page_key , file_search_context , media_callable = None , synchronised = True , include_unusual_predicate_types = True ) :
file_service_key = file_search_context . GetFileServiceKey ( )
tag_service_key = file_search_context . GetTagServiceKey ( )
AutoCompleteDropdownTags . __init__ ( self , parent , file_service_key , tag_service_key )
self . _media_callable = media_callable
self . _page_key = page_key
self . _file_search_context = file_search_context
self . _include_current_tags = ClientGUICommon . OnOffButton ( self . _dropdown_window , self . _page_key , ' notify_include_current ' , on_label = ' include current tags ' , off_label = ' exclude current tags ' , start_on = file_search_context . IncludeCurrentTags ( ) )
2018-01-03 22:37:30 +00:00
self . _include_current_tags . SetToolTip ( ' select whether to include current tags in the search ' )
2016-09-21 19:54:04 +00:00
self . _include_pending_tags = ClientGUICommon . OnOffButton ( self . _dropdown_window , self . _page_key , ' notify_include_pending ' , on_label = ' include pending tags ' , off_label = ' exclude pending tags ' , start_on = file_search_context . IncludePendingTags ( ) )
2018-01-03 22:37:30 +00:00
self . _include_pending_tags . SetToolTip ( ' select whether to include pending tags in the search ' )
2016-09-21 19:54:04 +00:00
self . _synchronised = ClientGUICommon . OnOffButton ( self . _dropdown_window , self . _page_key , ' notify_search_immediately ' , on_label = ' searching immediately ' , off_label = ' waiting -- tag counts may be inaccurate ' , start_on = synchronised )
2018-01-03 22:37:30 +00:00
self . _synchronised . SetToolTip ( ' select whether to renew the search as soon as a new predicate is entered ' )
2016-09-21 19:54:04 +00:00
self . _include_unusual_predicate_types = include_unusual_predicate_types
button_hbox_1 = wx . BoxSizer ( wx . HORIZONTAL )
2018-01-03 22:37:30 +00:00
button_hbox_1 . Add ( self . _include_current_tags , CC . FLAGS_EXPAND_BOTH_WAYS )
button_hbox_1 . Add ( self . _include_pending_tags , CC . FLAGS_EXPAND_BOTH_WAYS )
2016-09-21 19:54:04 +00:00
button_hbox_2 = wx . BoxSizer ( wx . HORIZONTAL )
2018-01-03 22:37:30 +00:00
button_hbox_2 . Add ( self . _file_repo_button , CC . FLAGS_EXPAND_BOTH_WAYS )
button_hbox_2 . Add ( self . _tag_repo_button , CC . FLAGS_EXPAND_BOTH_WAYS )
2016-09-21 19:54:04 +00:00
vbox = wx . BoxSizer ( wx . VERTICAL )
2018-01-03 22:37:30 +00:00
vbox . Add ( button_hbox_1 , CC . FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox . Add ( self . _synchronised , CC . FLAGS_EXPAND_PERPENDICULAR )
vbox . Add ( button_hbox_2 , CC . FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox . Add ( self . _dropdown_list , CC . FLAGS_EXPAND_BOTH_WAYS )
2016-09-21 19:54:04 +00:00
self . _dropdown_window . SetSizer ( vbox )
2017-05-10 21:33:58 +00:00
HG . client_controller . sub ( self , ' SetSynchronisedWait ' , ' synchronised_wait_switch ' )
2016-09-21 19:54:04 +00:00
2017-05-10 21:33:58 +00:00
HG . client_controller . sub ( self , ' IncludeCurrent ' , ' notify_include_current ' )
HG . client_controller . sub ( self , ' IncludePending ' , ' notify_include_pending ' )
2016-09-21 19:54:04 +00:00
def _BroadcastChoices ( self , predicates ) :
if self . _text_ctrl . GetValue ( ) != ' ' :
self . _text_ctrl . SetValue ( ' ' )
2017-05-10 21:33:58 +00:00
HG . client_controller . pub ( ' enter_predicates ' , self . _page_key , predicates )
2016-09-21 19:54:04 +00:00
def _BroadcastCurrentText ( self ) :
2017-03-08 23:23:12 +00:00
( inclusive , search_text , explicit_wildcard , cache_text , entry_predicate ) = self . _ParseSearchText ( )
2016-09-21 19:54:04 +00:00
try :
HydrusTags . CheckTagNotEmpty ( search_text )
except HydrusExceptions . SizeException :
return
self . _BroadcastChoices ( { entry_predicate } )
def _ChangeFileService ( self , file_service_key ) :
AutoCompleteDropdownTags . _ChangeFileService ( self , file_service_key )
self . _file_search_context . SetFileServiceKey ( file_service_key )
2017-05-10 21:33:58 +00:00
HG . client_controller . pub ( ' change_file_service ' , self . _page_key , file_service_key )
2016-09-21 19:54:04 +00:00
2017-05-10 21:33:58 +00:00
HG . client_controller . pub ( ' refresh_query ' , self . _page_key )
2016-09-21 19:54:04 +00:00
def _ChangeTagService ( self , tag_service_key ) :
AutoCompleteDropdownTags . _ChangeTagService ( self , tag_service_key )
self . _file_search_context . SetTagServiceKey ( tag_service_key )
2017-05-10 21:33:58 +00:00
HG . client_controller . pub ( ' change_tag_service ' , self . _page_key , tag_service_key )
2016-09-21 19:54:04 +00:00
2017-05-10 21:33:58 +00:00
HG . client_controller . pub ( ' refresh_query ' , self . _page_key )
2016-09-21 19:54:04 +00:00
def _InitDropDownList ( self ) :
2017-03-22 22:38:15 +00:00
return ClientGUIListBoxes . ListBoxTagsACRead ( self . _dropdown_window , self . BroadcastChoices , self . _tag_service_key , min_height = self . _list_height )
2016-09-21 19:54:04 +00:00
def _ParseSearchText ( self ) :
raw_entry = self . _text_ctrl . GetValue ( )
if raw_entry . startswith ( ' - ' ) :
inclusive = False
2017-03-08 23:23:12 +00:00
entry_text = raw_entry [ 1 : ]
2016-09-21 19:54:04 +00:00
else :
inclusive = True
2017-03-08 23:23:12 +00:00
entry_text = raw_entry
2016-09-21 19:54:04 +00:00
2017-03-08 23:23:12 +00:00
tag = HydrusTags . CleanTag ( entry_text )
2017-02-01 21:11:17 +00:00
2017-03-08 23:23:12 +00:00
explicit_wildcard = ' * ' in entry_text
2016-09-21 19:54:04 +00:00
2017-03-08 23:23:12 +00:00
search_text = ClientSearch . ConvertEntryTextToSearchText ( entry_text )
2016-09-21 19:54:04 +00:00
2017-03-08 23:23:12 +00:00
if explicit_wildcard :
cache_text = None
2016-09-21 19:54:04 +00:00
2017-03-08 23:23:12 +00:00
entry_predicate = ClientSearch . Predicate ( HC . PREDICATE_TYPE_WILDCARD , search_text , inclusive )
2016-09-21 19:54:04 +00:00
else :
2017-03-08 23:23:12 +00:00
cache_text = search_text [ : - 1 ] # take off the trailing '*' for the cache text
2017-05-10 21:33:58 +00:00
siblings_manager = HG . client_controller . GetManager ( ' tag_siblings ' )
2017-03-08 23:23:12 +00:00
sibling = siblings_manager . GetSibling ( self . _tag_service_key , tag )
if sibling is None :
entry_predicate = ClientSearch . Predicate ( HC . PREDICATE_TYPE_TAG , tag , inclusive )
else :
entry_predicate = ClientSearch . Predicate ( HC . PREDICATE_TYPE_TAG , sibling , inclusive )
2016-09-21 19:54:04 +00:00
2017-03-08 23:23:12 +00:00
return ( inclusive , search_text , explicit_wildcard , cache_text , entry_predicate )
2016-09-21 19:54:04 +00:00
def _GenerateMatches ( self ) :
self . _next_updatelist_is_probably_fast = False
num_autocomplete_chars = HC . options [ ' num_autocomplete_chars ' ]
2017-03-08 23:23:12 +00:00
( inclusive , search_text , explicit_wildcard , cache_text , entry_predicate ) = self . _ParseSearchText ( )
2016-09-21 19:54:04 +00:00
2017-03-08 23:23:12 +00:00
if search_text in ( ' ' , ' : ' , ' * ' ) :
2016-09-21 19:54:04 +00:00
2017-03-08 23:23:12 +00:00
input_just_changed = self . _cache_text is not None
2016-09-21 19:54:04 +00:00
2017-05-10 21:33:58 +00:00
db_not_going_to_hang_if_we_hit_it = not HG . client_controller . DBCurrentlyDoingJob ( )
2016-09-21 19:54:04 +00:00
if input_just_changed or db_not_going_to_hang_if_we_hit_it or not self . _initial_matches_fetched :
2017-03-08 23:23:12 +00:00
self . _cache_text = None
2016-09-21 19:54:04 +00:00
2017-03-08 23:23:12 +00:00
if self . _file_service_key == CC . COMBINED_FILE_SERVICE_KEY :
search_service_key = self . _tag_service_key
else :
search_service_key = self . _file_service_key
2016-09-21 19:54:04 +00:00
2017-05-10 21:33:58 +00:00
self . _cached_results = HG . client_controller . Read ( ' file_system_predicates ' , search_service_key )
2016-09-21 19:54:04 +00:00
matches = self . _cached_results
else :
2017-02-08 22:27:00 +00:00
( namespace , half_complete_subtag ) = HydrusTags . SplitTag ( search_text )
2017-05-10 21:33:58 +00:00
siblings_manager = HG . client_controller . GetManager ( ' tag_siblings ' )
2016-09-21 19:54:04 +00:00
2017-03-08 23:23:12 +00:00
if False and half_complete_subtag == ' ' :
2016-09-21 19:54:04 +00:00
2017-03-08 23:23:12 +00:00
self . _cache_text = None
2016-09-21 19:54:04 +00:00
matches = [ ] # a query like 'namespace:'
else :
fetch_from_db = True
if self . _media_callable is not None :
media = self . _media_callable ( )
can_fetch_from_media = media is not None and len ( media ) > 0
if can_fetch_from_media and self . _synchronised . IsOn ( ) :
fetch_from_db = False
if fetch_from_db :
2017-03-08 23:23:12 +00:00
# if user searches 'blah', then we include 'blah (23)' for 'series:blah (10)', 'blah (13)'
# if they search for 'series:blah', then we don't!
add_namespaceless = ' : ' not in namespace
2016-09-21 19:54:04 +00:00
include_current = self . _file_search_context . IncludeCurrentTags ( )
include_pending = self . _file_search_context . IncludePendingTags ( )
2017-03-08 23:23:12 +00:00
small_and_specific_search = cache_text is not None and len ( cache_text ) < num_autocomplete_chars
if small_and_specific_search :
2016-09-21 19:54:04 +00:00
2017-05-10 21:33:58 +00:00
predicates = HG . client_controller . Read ( ' autocomplete_predicates ' , file_service_key = self . _file_service_key , tag_service_key = self . _tag_service_key , search_text = cache_text , exact_match = True , inclusive = inclusive , include_current = include_current , include_pending = include_pending , add_namespaceless = add_namespaceless , collapse_siblings = True )
2016-09-21 19:54:04 +00:00
else :
2017-03-08 23:23:12 +00:00
cache_invalid_for_this_search = cache_text is None or self . _cache_text is None or not cache_text . startswith ( self . _cache_text )
if cache_invalid_for_this_search :
2016-09-21 19:54:04 +00:00
2017-03-08 23:23:12 +00:00
self . _cache_text = cache_text
2016-09-21 19:54:04 +00:00
2017-05-10 21:33:58 +00:00
self . _cached_results = HG . client_controller . Read ( ' autocomplete_predicates ' , file_service_key = self . _file_service_key , tag_service_key = self . _tag_service_key , search_text = search_text , inclusive = inclusive , include_current = include_current , include_pending = include_pending , add_namespaceless = add_namespaceless , collapse_siblings = True )
2016-09-21 19:54:04 +00:00
predicates = self . _cached_results
self . _next_updatelist_is_probably_fast = True
else :
# it is possible that media will change between calls to this, so don't cache it
# it's also quick as hell, so who cares
tags_managers = [ ]
for m in media :
if m . IsCollection ( ) : tags_managers . extend ( m . GetSingletonsTagsManagers ( ) )
else : tags_managers . append ( m . GetTagsManager ( ) )
tags_to_do = set ( )
current_tags_to_count = collections . Counter ( )
pending_tags_to_count = collections . Counter ( )
if self . _file_search_context . IncludeCurrentTags ( ) :
lists_of_current_tags = [ list ( tags_manager . GetCurrent ( self . _tag_service_key ) ) for tags_manager in tags_managers ]
current_tags_flat_iterable = itertools . chain . from_iterable ( lists_of_current_tags )
2017-03-08 23:23:12 +00:00
current_tags_flat = ClientSearch . FilterTagsBySearchText ( self . _tag_service_key , search_text , current_tags_flat_iterable )
2016-09-21 19:54:04 +00:00
current_tags_to_count . update ( current_tags_flat )
tags_to_do . update ( current_tags_to_count . keys ( ) )
if self . _file_search_context . IncludePendingTags ( ) :
lists_of_pending_tags = [ list ( tags_manager . GetPending ( self . _tag_service_key ) ) for tags_manager in tags_managers ]
pending_tags_flat_iterable = itertools . chain . from_iterable ( lists_of_pending_tags )
2017-03-08 23:23:12 +00:00
pending_tags_flat = ClientSearch . FilterTagsBySearchText ( self . _tag_service_key , search_text , pending_tags_flat_iterable )
2016-09-21 19:54:04 +00:00
pending_tags_to_count . update ( pending_tags_flat )
tags_to_do . update ( pending_tags_to_count . keys ( ) )
predicates = [ ClientSearch . Predicate ( HC . PREDICATE_TYPE_TAG , tag , inclusive , current_tags_to_count [ tag ] , pending_tags_to_count [ tag ] ) for tag in tags_to_do ]
if self . _tag_service_key != CC . COMBINED_TAG_SERVICE_KEY :
predicates = siblings_manager . CollapsePredicates ( self . _tag_service_key , predicates )
2017-03-08 23:23:12 +00:00
if namespace == ' ' :
2017-02-08 22:27:00 +00:00
predicates = ClientData . MergePredicates ( predicates , add_namespaceless = True )
2016-09-21 19:54:04 +00:00
self . _next_updatelist_is_probably_fast = True
2017-03-08 23:23:12 +00:00
matches = ClientSearch . FilterPredicatesBySearchText ( self . _tag_service_key , search_text , predicates )
2016-09-21 19:54:04 +00:00
matches = ClientSearch . SortPredicates ( matches )
if self . _include_unusual_predicate_types :
2017-03-08 23:23:12 +00:00
if explicit_wildcard :
2016-09-21 19:54:04 +00:00
2017-03-08 23:23:12 +00:00
matches . insert ( 0 , ClientSearch . Predicate ( HC . PREDICATE_TYPE_WILDCARD , search_text , inclusive ) )
else :
2016-09-21 19:54:04 +00:00
2017-03-08 23:23:12 +00:00
if namespace != ' ' and half_complete_subtag in ( ' ' , ' * ' ) :
2016-09-21 19:54:04 +00:00
2017-03-08 23:23:12 +00:00
matches . insert ( 0 , ClientSearch . Predicate ( HC . PREDICATE_TYPE_NAMESPACE , namespace , inclusive ) )
2016-09-21 19:54:04 +00:00
for match in matches :
if match . GetInclusive ( ) != inclusive : match . SetInclusive ( inclusive )
try :
index = matches . index ( entry_predicate )
predicate = matches [ index ]
del matches [ index ]
matches . insert ( 0 , predicate )
except :
pass
return matches
def _ShouldTakeResponsibilityForEnter ( self ) :
# when the user has quickly typed something in and the results are not yet in
return self . _text_ctrl . GetValue ( ) != ' ' and self . _last_search_text == ' '
def _TakeResponsibilityForEnter ( self ) :
self . _BroadcastCurrentText ( )
def GetFileSearchContext ( self ) :
return self . _file_search_context
def IncludeCurrent ( self , page_key , value ) :
if page_key == self . _page_key :
self . _file_search_context . SetIncludeCurrentTags ( value )
2017-01-18 22:52:39 +00:00
wx . CallAfter ( self . RefreshList )
2017-05-10 21:33:58 +00:00
HG . client_controller . pub ( ' refresh_query ' , self . _page_key )
2017-01-18 22:52:39 +00:00
2016-09-21 19:54:04 +00:00
def IncludePending ( self , page_key , value ) :
if page_key == self . _page_key :
self . _file_search_context . SetIncludePendingTags ( value )
2017-01-18 22:52:39 +00:00
wx . CallAfter ( self . RefreshList )
2017-05-10 21:33:58 +00:00
HG . client_controller . pub ( ' refresh_query ' , self . _page_key )
2017-01-18 22:52:39 +00:00
2016-09-21 19:54:04 +00:00
def SetSynchronisedWait ( self , page_key ) :
if page_key == self . _page_key : self . _synchronised . EventButton ( None )
class AutoCompleteDropdownTagsWrite ( AutoCompleteDropdownTags ) :
def __init__ ( self , parent , chosen_tag_callable , expand_parents , file_service_key , tag_service_key , null_entry_callable = None ) :
self . _chosen_tag_callable = chosen_tag_callable
self . _expand_parents = expand_parents
self . _null_entry_callable = null_entry_callable
if tag_service_key != CC . COMBINED_TAG_SERVICE_KEY and HC . options [ ' show_all_tags_in_autocomplete ' ] :
file_service_key = CC . COMBINED_FILE_SERVICE_KEY
2017-05-10 21:33:58 +00:00
if tag_service_key == CC . LOCAL_TAG_SERVICE_KEY :
file_service_key = CC . LOCAL_FILE_SERVICE_KEY
2016-09-21 19:54:04 +00:00
AutoCompleteDropdownTags . __init__ ( self , parent , file_service_key , tag_service_key )
vbox = wx . BoxSizer ( wx . VERTICAL )
hbox = wx . BoxSizer ( wx . HORIZONTAL )
2018-01-03 22:37:30 +00:00
hbox . Add ( self . _file_repo_button , CC . FLAGS_EXPAND_BOTH_WAYS )
hbox . Add ( self . _tag_repo_button , CC . FLAGS_EXPAND_BOTH_WAYS )
2016-09-21 19:54:04 +00:00
2018-01-03 22:37:30 +00:00
vbox . Add ( hbox , CC . FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox . Add ( self . _dropdown_list , CC . FLAGS_EXPAND_BOTH_WAYS )
2016-09-21 19:54:04 +00:00
self . _dropdown_window . SetSizer ( vbox )
def _BroadcastChoices ( self , predicates ) :
if self . _text_ctrl . GetValue ( ) != ' ' :
self . _text_ctrl . SetValue ( ' ' )
tags = { predicate . GetValue ( ) for predicate in predicates }
if len ( tags ) > 0 :
self . _chosen_tag_callable ( tags )
def _ParseSearchText ( self ) :
raw_entry = self . _text_ctrl . GetValue ( )
2017-02-01 21:11:17 +00:00
tag = HydrusTags . CleanTag ( raw_entry )
2017-03-08 23:23:12 +00:00
search_text = ClientSearch . ConvertEntryTextToSearchText ( raw_entry )
if ClientSearch . IsComplexWildcard ( search_text ) :
cache_text = None
else :
cache_text = search_text [ : - 1 ] # take off the trailing '*' for the cache text
2016-09-21 19:54:04 +00:00
2017-02-01 21:11:17 +00:00
entry_predicate = ClientSearch . Predicate ( HC . PREDICATE_TYPE_TAG , tag )
2016-09-21 19:54:04 +00:00
2017-05-10 21:33:58 +00:00
siblings_manager = HG . client_controller . GetManager ( ' tag_siblings ' )
2016-09-21 19:54:04 +00:00
2017-02-01 21:11:17 +00:00
sibling = siblings_manager . GetSibling ( self . _tag_service_key , tag )
2016-09-21 19:54:04 +00:00
if sibling is not None :
sibling_predicate = ClientSearch . Predicate ( HC . PREDICATE_TYPE_TAG , sibling )
else :
sibling_predicate = None
2017-03-08 23:23:12 +00:00
return ( search_text , cache_text , entry_predicate , sibling_predicate )
2016-09-21 19:54:04 +00:00
def _BroadcastCurrentText ( self ) :
2017-03-08 23:23:12 +00:00
( search_text , cache_text , entry_predicate , sibling_predicate ) = self . _ParseSearchText ( )
2016-09-21 19:54:04 +00:00
try :
HydrusTags . CheckTagNotEmpty ( search_text )
except HydrusExceptions . SizeException :
return
self . _BroadcastChoices ( { entry_predicate } )
def _GenerateMatches ( self ) :
self . _next_updatelist_is_probably_fast = False
num_autocomplete_chars = HC . options [ ' num_autocomplete_chars ' ]
2017-03-08 23:23:12 +00:00
( search_text , cache_text , entry_predicate , sibling_predicate ) = self . _ParseSearchText ( )
2016-09-21 19:54:04 +00:00
2017-03-08 23:23:12 +00:00
if search_text in ( ' ' , ' : ' , ' * ' ) :
2016-09-21 19:54:04 +00:00
2017-03-08 23:23:12 +00:00
self . _cache_text = None
2016-09-21 19:54:04 +00:00
matches = [ ]
else :
must_do_a_search = False
2017-03-08 23:23:12 +00:00
small_and_specific_search = cache_text is not None and len ( cache_text ) < num_autocomplete_chars
2017-02-08 22:27:00 +00:00
2017-03-08 23:23:12 +00:00
if small_and_specific_search :
2016-09-21 19:54:04 +00:00
2017-05-10 21:33:58 +00:00
predicates = HG . client_controller . Read ( ' autocomplete_predicates ' , file_service_key = self . _file_service_key , tag_service_key = self . _tag_service_key , search_text = cache_text , exact_match = True , add_namespaceless = False , collapse_siblings = False )
2016-09-21 19:54:04 +00:00
else :
2017-03-08 23:23:12 +00:00
cache_invalid_for_this_search = cache_text is None or self . _cache_text is None or not cache_text . startswith ( self . _cache_text )
if must_do_a_search or cache_invalid_for_this_search :
2016-09-21 19:54:04 +00:00
2017-03-08 23:23:12 +00:00
self . _cache_text = cache_text
2016-09-21 19:54:04 +00:00
2017-05-10 21:33:58 +00:00
self . _cached_results = HG . client_controller . Read ( ' autocomplete_predicates ' , file_service_key = self . _file_service_key , tag_service_key = self . _tag_service_key , search_text = search_text , add_namespaceless = False , collapse_siblings = False )
2016-09-21 19:54:04 +00:00
predicates = self . _cached_results
self . _next_updatelist_is_probably_fast = True
2017-03-08 23:23:12 +00:00
matches = ClientSearch . FilterPredicatesBySearchText ( self . _tag_service_key , search_text , predicates )
2016-09-21 19:54:04 +00:00
matches = ClientSearch . SortPredicates ( matches )
self . _PutAtTopOfMatches ( matches , entry_predicate )
if sibling_predicate is not None :
self . _PutAtTopOfMatches ( matches , sibling_predicate )
if self . _expand_parents :
2017-05-10 21:33:58 +00:00
parents_manager = HG . client_controller . GetManager ( ' tag_parents ' )
2016-09-21 19:54:04 +00:00
matches = parents_manager . ExpandPredicates ( self . _tag_service_key , matches )
return matches
def _InitDropDownList ( self ) :
2017-03-22 22:38:15 +00:00
return ClientGUIListBoxes . ListBoxTagsACWrite ( self . _dropdown_window , self . BroadcastChoices , self . _tag_service_key , min_height = self . _list_height )
2016-09-21 19:54:04 +00:00
def _PutAtTopOfMatches ( self , matches , predicate ) :
try :
index = matches . index ( predicate )
predicate = matches [ index ]
matches . remove ( predicate )
except ValueError :
pass
matches . insert ( 0 , predicate )
def _ShouldTakeResponsibilityForEnter ( self ) :
# when the user has quickly typed something in and the results are not yet in
p1 = self . _text_ctrl . GetValue ( ) != ' ' and self . _last_search_text == ' '
# when the text ctrl is empty and we want to push a None to the parent dialog
p2 = self . _text_ctrl . GetValue ( ) == ' '
return p1 or p2
def _TakeResponsibilityForEnter ( self ) :
if self . _text_ctrl . GetValue ( ) == ' ' :
if self . _null_entry_callable is not None :
self . _null_entry_callable ( )
else :
self . _BroadcastCurrentText ( )
2016-12-21 22:30:54 +00:00