import ClientCaches import ClientConstants as CC import ClientData import ClientGUICommon import ClientGUIMenus import ClientSearch import ClientTags import collections import HydrusConstants as HC import HydrusData import HydrusExceptions import HydrusGlobals as HG import HydrusTags import os import wx ( ListBoxEvent, EVT_LIST_BOX ) = wx.lib.newevent.NewCommandEvent() class AddEditDeleteListBox( wx.Panel ): def __init__( self, parent, height_num_chars, data_to_pretty_callable, add_callable, edit_callable ): self._data_to_pretty_callable = data_to_pretty_callable self._add_callable = add_callable self._edit_callable = edit_callable wx.Panel.__init__( self, parent ) self._listbox = wx.ListBox( self, style = wx.LB_EXTENDED ) self._add_button = ClientGUICommon.BetterButton( self, 'add', self._Add ) self._edit_button = ClientGUICommon.BetterButton( self, 'edit', self._Edit ) self._delete_button = ClientGUICommon.BetterButton( self, 'delete', self._Delete ) # vbox = wx.BoxSizer( wx.VERTICAL ) buttons_hbox = wx.BoxSizer( wx.HORIZONTAL ) buttons_hbox.Add( self._add_button, CC.FLAGS_EXPAND_BOTH_WAYS ) buttons_hbox.Add( self._edit_button, CC.FLAGS_EXPAND_BOTH_WAYS ) buttons_hbox.Add( self._delete_button, CC.FLAGS_EXPAND_BOTH_WAYS ) vbox.Add( self._listbox, CC.FLAGS_EXPAND_BOTH_WAYS ) vbox.Add( buttons_hbox, CC.FLAGS_EXPAND_PERPENDICULAR ) self.SetSizer( vbox ) # ( width, height ) = ClientData.ConvertTextToPixels( self._listbox, ( 20, height_num_chars ) ) self._listbox.SetInitialSize( ( width, height ) ) # self._listbox.Bind( wx.EVT_LISTBOX, self.EventSelection ) self._listbox.Bind( wx.EVT_LISTBOX_DCLICK, self.EventEdit ) def _Add( self ): ( result, data ) = self._add_callable() if result: self._AddData( data ) def _AddData( self, data ): pretty_data = self._data_to_pretty_callable( data ) self._listbox.Append( pretty_data, data ) def _Delete( self ): indices = list( self._listbox.GetSelections() ) if len( indices ) == 0: return indices.sort( reverse = True ) import ClientGUIDialogs with ClientGUIDialogs.DialogYesNo( self, 'Remove all selected?' ) as dlg_yn: if dlg_yn.ShowModal() == wx.ID_YES: for i in indices: self._listbox.Delete( i ) wx.QueueEvent( self.GetEventHandler(), ListBoxEvent( -1 ) ) def _Edit( self ): for i in range( self._listbox.GetCount() ): if not self._listbox.IsSelected( i ): continue data = self._listbox.GetClientData( i ) ( result, new_data ) = self._edit_callable( data ) if result: self._listbox.Delete( i ) pretty_new_data = self._data_to_pretty_callable( new_data ) self._listbox.Insert( pretty_new_data, i, new_data ) else: break wx.QueueEvent( self.GetEventHandler(), ListBoxEvent( -1 ) ) def AddDatas( self, datas ): for data in datas: self._AddData( data ) wx.QueueEvent( self.GetEventHandler(), ListBoxEvent( -1 ) ) def Bind( self, event, handler ): self._listbox.Bind( event, handler ) def EventEdit( self, event ): self._Edit() def EventSelection( self, event ): if len( self._listbox.GetSelections() ) == 0: self._edit_button.Disable() self._delete_button.Disable() else: self._edit_button.Enable() self._delete_button.Enable() event.Skip() def GetCount( self ): return self._listbox.GetCount() def GetData( self, only_selected = False ): datas = [] for i in range( self._listbox.GetCount() ): data = self._listbox.GetClientData( i ) datas.append( data ) return datas class QueueListBox( wx.Panel ): def __init__( self, parent, data_to_pretty_callable, add_callable = None, edit_callable = None ): self._data_to_pretty_callable = data_to_pretty_callable self._add_callable = add_callable self._edit_callable = edit_callable wx.Panel.__init__( self, parent ) self._listbox = wx.ListBox( self, style = wx.LB_EXTENDED ) self._up_button = ClientGUICommon.BetterButton( self, u'\u2191', self._Up ) self._delete_button = ClientGUICommon.BetterButton( self, 'X', self._Delete ) self._down_button = ClientGUICommon.BetterButton( self, u'\u2193', self._Down ) self._add_button = ClientGUICommon.BetterButton( self, 'add', self._Add ) self._edit_button = ClientGUICommon.BetterButton( self, 'edit', self._Edit ) if self._add_callable is None: self._add_button.Hide() if self._edit_callable is None: self._edit_button.Hide() # vbox = wx.BoxSizer( wx.VERTICAL ) buttons_vbox = wx.BoxSizer( wx.VERTICAL ) buttons_vbox.Add( self._up_button, CC.FLAGS_VCENTER ) buttons_vbox.Add( self._delete_button, CC.FLAGS_VCENTER ) buttons_vbox.Add( self._down_button, CC.FLAGS_VCENTER ) hbox = wx.BoxSizer( wx.HORIZONTAL ) hbox.Add( self._listbox, CC.FLAGS_EXPAND_BOTH_WAYS ) hbox.Add( buttons_vbox, CC.FLAGS_VCENTER ) buttons_hbox = wx.BoxSizer( wx.HORIZONTAL ) buttons_hbox.Add( self._add_button, CC.FLAGS_EXPAND_BOTH_WAYS ) buttons_hbox.Add( self._edit_button, CC.FLAGS_EXPAND_BOTH_WAYS ) vbox.Add( hbox, CC.FLAGS_EXPAND_BOTH_WAYS ) vbox.Add( buttons_hbox, CC.FLAGS_EXPAND_PERPENDICULAR ) self.SetSizer( vbox ) # self._listbox.Bind( wx.EVT_LISTBOX, self.EventSelection ) self._listbox.Bind( wx.EVT_LISTBOX_DCLICK, self.EventEdit ) def _Add( self ): ( result, data ) = self._add_callable() if result: self._AddData( data ) def _AddData( self, data ): pretty_data = self._data_to_pretty_callable( data ) self._listbox.Append( pretty_data, data ) def _Delete( self ): indices = list( self._listbox.GetSelections() ) if len( indices ) == 0: return indices.sort( reverse = True ) import ClientGUIDialogs with ClientGUIDialogs.DialogYesNo( self, 'Remove all selected?' ) as dlg_yn: if dlg_yn.ShowModal() == wx.ID_YES: for i in indices: self._listbox.Delete( i ) wx.QueueEvent( self.GetEventHandler(), ListBoxEvent( -1 ) ) def _Down( self ): indices = list( self._listbox.GetSelections() ) indices.sort( reverse = True ) for i in indices: if i < self._listbox.GetCount() - 1: if not self._listbox.IsSelected( i + 1 ): # is the one below not selected? self._SwapRows( i, i + 1 ) wx.QueueEvent( self.GetEventHandler(), ListBoxEvent( -1 ) ) def _Edit( self ): for i in range( self._listbox.GetCount() ): if not self._listbox.IsSelected( i ): continue data = self._listbox.GetClientData( i ) ( result, new_data ) = self._edit_callable( data ) if result: self._listbox.Delete( i ) pretty_new_data = self._data_to_pretty_callable( new_data ) self._listbox.Insert( pretty_new_data, i, new_data ) else: break wx.QueueEvent( self.GetEventHandler(), ListBoxEvent( -1 ) ) def _SwapRows( self, index_a, index_b ): a_was_selected = self._listbox.IsSelected( index_a ) b_was_selected = self._listbox.IsSelected( index_b ) data_a = self._listbox.GetClientData( index_a ) data_b = self._listbox.GetClientData( index_b ) pretty_data_a = self._data_to_pretty_callable( data_a ) pretty_data_b = self._data_to_pretty_callable( data_b ) self._listbox.Delete( index_a ) self._listbox.Insert( pretty_data_b, index_a, data_b ) self._listbox.Delete( index_b ) self._listbox.Insert( pretty_data_a, index_b, data_a ) if b_was_selected: self._listbox.Select( index_a ) if a_was_selected: self._listbox.Select( index_b ) def _Up( self ): indices = self._listbox.GetSelections() for i in indices: if i > 0: if not self._listbox.IsSelected( i - 1 ): # is the one above not selected? self._SwapRows( i, i - 1 ) wx.QueueEvent( self.GetEventHandler(), ListBoxEvent( -1 ) ) def AddDatas( self, datas ): for data in datas: self._AddData( data ) wx.QueueEvent( self.GetEventHandler(), ListBoxEvent( -1 ) ) def Bind( self, event, handler ): self._listbox.Bind( event, handler ) def EventEdit( self, event ): self._Edit() def EventSelection( self, event ): if len( self._listbox.GetSelections() ) == 0: self._up_button.Disable() self._delete_button.Disable() self._down_button.Disable() self._edit_button.Disable() else: self._up_button.Enable() self._delete_button.Enable() self._down_button.Enable() self._edit_button.Enable() event.Skip() def GetCount( self ): return self._listbox.GetCount() def GetData( self, only_selected = False ): datas = [] for i in range( self._listbox.GetCount() ): data = self._listbox.GetClientData( i ) datas.append( data ) return datas def Pop( self ): if self._listbox.GetCount() == 0: return None data = self._listbox.GetClientData( 0 ) self._listbox.Delete( 0 ) return data class ListBox( wx.ScrolledWindow ): TEXT_X_PADDING = 3 def __init__( self, parent, min_height = 150 ): wx.ScrolledWindow.__init__( self, parent, style = wx.VSCROLL | wx.BORDER_DOUBLE ) self._background_colour = wx.Colour( 255, 255, 255 ) self._terms = set() self._ordered_terms = [] self._selected_terms = set() self._terms_to_texts = {} self._last_hit_index = None self._last_view_start = None self._dirty = True self._client_bmp = wx.Bitmap( 20, 20, 24 ) dc = wx.MemoryDC( self._client_bmp ) dc.SetFont( wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT ) ) ( text_x, self._text_y ) = dc.GetTextExtent( 'abcdefghijklmnopqrstuvwxyz' ) self._num_rows_per_page = 0 self.SetScrollRate( 0, self._text_y ) self.SetMinSize( ( 50, min_height ) ) self.Bind( wx.EVT_PAINT, self.EventPaint ) self.Bind( wx.EVT_SIZE, self.EventResize ) self.Bind( wx.EVT_ERASE_BACKGROUND, self.EventEraseBackground ) self.Bind( wx.EVT_LEFT_DOWN, self.EventMouseSelect ) self.Bind( wx.EVT_LEFT_DCLICK, self.EventDClick ) self.Bind( wx.EVT_CHAR_HOOK, self.EventCharHook ) def __len__( self ): return len( self._ordered_terms ) def _Activate( self ): pass def _DeleteActivate( self ): pass def _AppendTerm( self, term ): was_selected_before = term in self._selected_terms if term in self._terms: self._RemoveTerm( term ) self._terms.add( term ) self._ordered_terms.append( term ) self._terms_to_texts[ term ] = self._GetTextFromTerm( term ) if was_selected_before: self._selected_terms.add( term ) def _Clear( self ): self._terms = set() self._ordered_terms = [] self._selected_terms = set() self._terms_to_texts = {} self._last_hit_index = None self._last_view_start = None self._dirty = True def _DataHasChanged( self ): self._SetVirtualSize() self._SetDirty() wx.QueueEvent( self.GetEventHandler(), ListBoxEvent( -1 ) ) def _Deselect( self, index ): term = self._GetTerm( index ) self._selected_terms.discard( term ) def _DeselectAll( self ): self._selected_terms = set() def _GetIndexUnderMouse( self, mouse_event ): ( xUnit, yUnit ) = self.GetScrollPixelsPerUnit() ( x_scroll, y_scroll ) = self.GetViewStart() y_offset = y_scroll * yUnit y = mouse_event.GetY() + y_offset row_index = ( y / self._text_y ) if row_index >= len( self._ordered_terms ): return None return row_index def _GetSelectedIncludeExcludePredicates( self ): include_predicates = [] exclude_predicates = [] for term in self._selected_terms: if isinstance( term, ClientSearch.Predicate ): predicate_type = term.GetType() if predicate_type in ( HC.PREDICATE_TYPE_TAG, HC.PREDICATE_TYPE_NAMESPACE, HC.PREDICATE_TYPE_WILDCARD ): value = term.GetValue() include_predicates.append( ClientSearch.Predicate( predicate_type, value ) ) exclude_predicates.append( ClientSearch.Predicate( predicate_type, value, False ) ) else: include_predicates.append( term ) else: s = term include_predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, term ) ) exclude_predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, term, False ) ) return ( include_predicates, exclude_predicates ) def _GetTerm( self, index ): if index < 0 or index > len( self._ordered_terms ) - 1: raise HydrusExceptions.DataMissing( 'No term for index ' + str( index ) ) return self._ordered_terms[ index ] def _GetTextColour( self, term ): return ( 0, 111, 250 ) def _GetSafeHitIndex( self, hit_index, direction = None ): if hit_index is not None: if hit_index == -1 or hit_index > len( self._ordered_terms ): hit_index = len( self._ordered_terms ) - 1 elif hit_index == len( self._ordered_terms ) or hit_index < -1: hit_index = 0 return hit_index def _GetSimplifiedTextFromTerm( self, term ): return self._GetTextFromTerm( term ) def _GetTextFromTerm( self, term ): raise NotImplementedError() def _HandleClick( self, event ): hit_index = self._GetIndexUnderMouse( event ) shift = event.ShiftDown() ctrl = event.CmdDown() self._Hit( shift, ctrl, hit_index ) def _Hit( self, shift, ctrl, hit_index ): hit_index = self._GetSafeHitIndex( hit_index ) to_select = set() to_deselect = set() deselect_all = False if shift: if hit_index is not None: if self._last_hit_index is not None: lower = min( hit_index, self._last_hit_index ) upper = max( hit_index, self._last_hit_index ) to_select = range( lower, upper + 1 ) else: to_select.add( hit_index ) elif ctrl: if hit_index is not None: if self._IsSelected( hit_index ): to_deselect.add( hit_index ) else: to_select.add( hit_index ) else: if hit_index is None: deselect_all = True else: if not self._IsSelected( hit_index ): deselect_all = True to_select.add( hit_index ) if deselect_all: self._DeselectAll() for index in to_select: self._Select( index ) for index in to_deselect: self._Deselect( index ) self._last_hit_index = hit_index if self._last_hit_index is not None: y = self._text_y * self._last_hit_index ( start_x, start_y ) = self.GetViewStart() ( x_unit, y_unit ) = self.GetScrollPixelsPerUnit() ( width, height ) = self.GetClientSize() if y < start_y * y_unit: y_to_scroll_to = y / y_unit #self.Scroll( -1, y_to_scroll_to ) wx.QueueEvent( self.GetEventHandler(), wx.ScrollWinEvent( wx.wxEVT_SCROLLWIN_THUMBRELEASE, pos = y_to_scroll_to ) ) elif y > ( start_y * y_unit ) + height - self._text_y: y_to_scroll_to = ( y - height ) / y_unit #self.Scroll( -1, y_to_scroll_to + 2 ) wx.QueueEvent( self.GetEventHandler(), wx.ScrollWinEvent( wx.wxEVT_SCROLLWIN_THUMBRELEASE, pos = y_to_scroll_to + 2 ) ) self._SetDirty() def _IsSelected( self, index ): try: term = self._GetTerm( index ) except HydrusExceptions.DataMissing: return False return term in self._selected_terms def _Redraw( self, dc ): ( xUnit, yUnit ) = self.GetScrollPixelsPerUnit() ( x_scroll, y_scroll ) = self.GetViewStart() self._last_view_start = self.GetViewStart() y_offset = y_scroll * yUnit ( my_width, my_height ) = self.GetClientSize() first_visible_index = y_offset / self._text_y last_visible_index = ( y_offset + my_height ) / self._text_y if ( y_offset + my_height ) % self._text_y != 0: last_visible_index += 1 last_visible_index = min( last_visible_index, len( self._ordered_terms ) - 1 ) dc.SetFont( wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT ) ) dc.SetBackground( wx.Brush( self._background_colour ) ) dc.Clear() for ( i, current_index ) in enumerate( range( first_visible_index, last_visible_index + 1 ) ): term = self._GetTerm( current_index ) text = self._terms_to_texts[ term ] ( r, g, b ) = self._GetTextColour( term ) text_colour = wx.Colour( r, g, b ) if term in self._selected_terms: dc.SetBrush( wx.Brush( text_colour ) ) dc.SetPen( wx.TRANSPARENT_PEN ) dc.DrawRectangle( 0, i * self._text_y, my_width, self._text_y ) text_colour = self._background_colour dc.SetTextForeground( text_colour ) ( x, y ) = ( self.TEXT_X_PADDING, i * self._text_y ) dc.DrawText( text, x, y ) self._dirty = False def _RefreshTexts( self ): self._terms_to_texts = { term : self._GetTextFromTerm( term ) for term in self._terms } self._SetDirty() def _RemoveSelectedTerms( self ): for term in list( self._selected_terms ): self._RemoveTerm( term ) def _RemoveTerm( self, term ): if term in self._terms: self._terms.discard( term ) self._ordered_terms.remove( term ) self._selected_terms.discard( term ) del self._terms_to_texts[ term ] def _Select( self, index ): term = self._GetTerm( index ) self._selected_terms.add( term ) def _SelectAll( self ): self._selected_terms = set( self._terms ) def _SetDirty( self ): self._dirty = True self.Refresh() def _SetVirtualSize( self ): ( my_x, my_y ) = self.GetClientSize() ideal_virtual_size = ( my_x, max( self._text_y * len( self._ordered_terms ), my_y ) ) if ideal_virtual_size != self.GetVirtualSize(): self.SetVirtualSize( ideal_virtual_size ) def _SortByText( self ): def lexicographic_key( term ): return self._terms_to_texts[ term ] self._ordered_terms.sort( key = lexicographic_key ) def EventCharHook( self, event ): shift = event.ShiftDown() ctrl = event.CmdDown() key_code = event.GetKeyCode() if ClientGUICommon.WindowHasFocus( self ) and key_code in CC.DELETE_KEYS: self._DeleteActivate() elif key_code in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ): self._Activate() else: if ctrl and key_code in ( ord( 'A' ), ord( 'a' ) ): self._SelectAll() self._SetDirty() else: hit_index = None if len( self._ordered_terms ) > 1: roll_up = False roll_down = False if key_code in ( wx.WXK_HOME, wx.WXK_NUMPAD_HOME ): hit_index = 0 elif key_code in ( wx.WXK_END, wx.WXK_NUMPAD_END ): hit_index = len( self._ordered_terms ) - 1 roll_up = True elif self._last_hit_index is not None: if key_code in ( wx.WXK_UP, wx.WXK_NUMPAD_UP ): hit_index = self._last_hit_index - 1 roll_up = True elif key_code in ( wx.WXK_DOWN, wx.WXK_NUMPAD_DOWN ): hit_index = self._last_hit_index + 1 roll_down = True elif key_code in ( wx.WXK_PAGEUP, wx.WXK_NUMPAD_PAGEUP ): hit_index = max( 0, self._last_hit_index - self._num_rows_per_page ) roll_up = True elif key_code in ( wx.WXK_PAGEDOWN, wx.WXK_NUMPAD_PAGEDOWN ): hit_index = min( len( self._ordered_terms ) - 1, self._last_hit_index + self._num_rows_per_page ) roll_down = True if hit_index is None: event.Skip() else: if roll_up: hit_index = self._GetSafeHitIndex( hit_index, -1 ) if roll_down: hit_index = self._GetSafeHitIndex( hit_index, 1 ) self._Hit( shift, ctrl, hit_index ) def EventDClick( self, event ): self._Activate() def EventEraseBackground( self, event ): pass def EventMouseSelect( self, event ): self._HandleClick( event ) event.Skip() def EventPaint( self, event ): ( my_x, my_y ) = self.GetClientSize() if ( my_x, my_y ) != self._client_bmp.GetSize(): self._client_bmp = wx.Bitmap( my_x, my_y, 24 ) self._dirty = True dc = wx.BufferedPaintDC( self, self._client_bmp ) if self._dirty or self._last_view_start != self.GetViewStart(): self._Redraw( dc ) def EventResize( self, event ): ( my_x, my_y ) = self.GetClientSize() self._num_rows_per_page = my_y / self._text_y self._SetVirtualSize() self._SetDirty() def GetClientData( self, index = None ): if index is None: return set( self._terms ) else: return self._GetTerm( index ) def GetIdealHeight( self ): return self._text_y * len( self._ordered_terms ) + 20 def MoveSelectionDown( self ): if len( self._ordered_terms ) > 1 and self._last_hit_index is not None: self._Hit( False, False, self._last_hit_index + 1 ) def MoveSelectionUp( self ): if len( self._ordered_terms ) > 1 and self._last_hit_index is not None: self._Hit( False, False, self._last_hit_index - 1 ) class ListBoxTags( ListBox ): has_counts = False can_spawn_new_windows = True def __init__( self, *args, **kwargs ): ListBox.__init__( self, *args, **kwargs ) self._get_current_predicates_callable = None self._UpdateBackgroundColour() self.Bind( wx.EVT_RIGHT_DOWN, self.EventMouseRightClick ) self.Bind( wx.EVT_MIDDLE_DOWN, self.EventMouseMiddleClick ) HG.client_controller.sub( self, 'SiblingsHaveChanged', 'notify_new_siblings_gui' ) HG.client_controller.sub( self, '_UpdateBackgroundColour', 'notify_new_colourset' ) def _GetNamespaceColours( self ): return HC.options[ 'namespace_colours' ] def _GetAllTagsForClipboard( self, with_counts = False ): texts = list( self._terms_to_texts.values() ) texts.sort() return texts def _GetNamespaceFromTerm( self, term ): raise NotImplementedError() def _GetTextColour( self, term ): namespace_colours = self._GetNamespaceColours() namespace = self._GetNamespaceFromTerm( term ) if namespace in namespace_colours: ( r, g, b ) = namespace_colours[ namespace ] else: ( r, g, b ) = namespace_colours[ None ] return ( r, g, b ) def _NewSearchPage( self ): predicates = [] for term in self._selected_terms: if isinstance( term, ClientSearch.Predicate ): predicates.append( term ) else: predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, term ) ) predicates = HG.client_controller.GetGUI().FlushOutPredicates( predicates ) if len( predicates ) > 0: HG.client_controller.pub( 'new_page_query', CC.LOCAL_FILE_SERVICE_KEY, initial_predicates = predicates ) def _ProcessMenuCopyEvent( self, command ): if command in ( 'copy_terms', 'copy_sub_terms' ): texts = [] for term in self._selected_terms: if isinstance( term, ClientSearch.Predicate ): text = term.GetUnicode( with_count = False ) else: text = HydrusData.ToUnicode( term ) if command == 'copy_sub_terms': ( namespace_gumpf, text ) = HydrusTags.SplitTag( text ) texts.append( text ) texts.sort() text = os.linesep.join( texts ) elif command == 'copy_all_tags': text = os.linesep.join( self._GetAllTagsForClipboard( with_counts = False ) ) elif command == 'copy_all_tags_with_counts': text = os.linesep.join( self._GetAllTagsForClipboard( with_counts = True ) ) HG.client_controller.pub( 'clipboard', 'text', text ) def _ProcessMenuPredicateEvent( self, command ): pass def _ProcessMenuTagEvent( self, command ): import ClientGUIDialogsManage if command == 'censorship': ( tag, ) = self._selected_terms with ClientGUIDialogsManage.DialogManageTagCensorship( self, tag ) as dlg: dlg.ShowModal() elif command == 'parent': with ClientGUIDialogsManage.DialogManageTagParents( self, self._selected_terms ) as dlg: dlg.ShowModal() elif command == 'sibling': with ClientGUIDialogsManage.DialogManageTagSiblings( self, self._selected_terms ) as dlg: dlg.ShowModal() def _UpdateBackgroundColour( self ): new_options = HG.client_controller.new_options self._background_colour = new_options.GetColour( CC.COLOUR_TAGS_BOX ) self.Refresh() def EventMouseMiddleClick( self, event ): self._HandleClick( event ) if self.can_spawn_new_windows: self._NewSearchPage() def EventMouseRightClick( self, event ): self._HandleClick( event ) if len( self._ordered_terms ) > 0: menu = wx.Menu() if len( self._selected_terms ) > 0: if len( self._selected_terms ) == 1: ( term, ) = self._selected_terms if isinstance( term, ClientSearch.Predicate ): if term.GetType() == HC.PREDICATE_TYPE_TAG: selection_string = '"' + term.GetValue() + '"' else: selection_string = '"' + term.GetUnicode( with_count = False ) + '"' else: selection_string = '"' + HydrusData.ToUnicode( term ) + '"' else: selection_string = 'selected' if self._get_current_predicates_callable is not None: current_predicates = self._get_current_predicates_callable() ( include_predicates, exclude_predicates ) = self._GetSelectedIncludeExcludePredicates() if current_predicates is not None: if True in ( include_predicate in current_predicates for include_predicate in include_predicates ): ClientGUIMenus.AppendMenuItem( self, menu, 'discard ' + selection_string + ' from current search', 'Remove the selected predicates from the current search.', self._ProcessMenuPredicateEvent, 'remove_include_predicates' ) if True in ( include_predicate not in current_predicates for include_predicate in include_predicates ): ClientGUIMenus.AppendMenuItem( self, menu, 'require ' + selection_string + ' for current search', 'Add the selected predicates from the current search.', self._ProcessMenuPredicateEvent, 'add_include_predicates' ) if True in ( exclude_predicate in current_predicates for exclude_predicate in exclude_predicates ): ClientGUIMenus.AppendMenuItem( self, menu, 'permit ' + selection_string + ' for current search', 'Stop disallowing the selected predicates from the current search.', self._ProcessMenuPredicateEvent, 'remove_exclude_predicates' ) if True in ( exclude_predicate not in current_predicates for exclude_predicate in exclude_predicates ): ClientGUIMenus.AppendMenuItem( self, menu, 'exclude ' + selection_string + ' from current search', 'Disallow the selected predicates for the current search.', self._ProcessMenuPredicateEvent, 'add_exclude_predicates' ) ClientGUIMenus.AppendSeparator( menu ) if self.can_spawn_new_windows: ClientGUIMenus.AppendMenuItem( self, menu, 'open a new search page for ' + selection_string, 'Open a new search page starting with the selected predicates.', self._NewSearchPage ) ClientGUIMenus.AppendSeparator( menu ) ClientGUIMenus.AppendMenuItem( self, menu, 'copy ' + selection_string, 'Copy the selected predicates to your clipboard.', self._ProcessMenuCopyEvent, 'copy_terms' ) if len( self._selected_terms ) == 1: ( namespace, subtag ) = HydrusTags.SplitTag( selection_string ) if namespace != '': sub_selection_string = '"' + subtag ClientGUIMenus.AppendMenuItem( self, menu, 'copy ' + sub_selection_string, 'Copy the selected sub-predicates to your clipboard.', self._ProcessMenuCopyEvent, 'copy_sub_terms' ) else: ClientGUIMenus.AppendMenuItem( self, menu, 'copy selected subtags', 'Copy the selected sub-predicates to your clipboard.', self._ProcessMenuCopyEvent, 'copy_sub_terms' ) ClientGUIMenus.AppendSeparator( menu ) if len( self._ordered_terms ) > len( self._selected_terms ): ClientGUIMenus.AppendMenuItem( self, menu, 'copy all tags', 'Copy all the predicates in this list to your clipboard.', self._ProcessMenuCopyEvent, 'copy_all_tags' ) if self.has_counts: ClientGUIMenus.AppendMenuItem( self, menu, 'copy all tags with counts', 'Copy all the predicates in this list, with their counts, to your clipboard.', self._ProcessMenuCopyEvent, 'copy_all_tags_with_counts' ) if self.can_spawn_new_windows and len( self._selected_terms ) > 0: term_types = [ type( term ) for term in self._selected_terms ] if str in term_types or unicode in term_types: ClientGUIMenus.AppendSeparator( menu ) if len( self._selected_terms ) == 1: ( tag, ) = self._selected_terms text = tag else: text = 'selection' if len( self._selected_terms ) == 1: ClientGUIMenus.AppendMenuItem( self, menu, 'censor ' + text, 'Hide this tag from view in future.', self._ProcessMenuTagEvent, 'censorship' ) ClientGUIMenus.AppendMenuItem( self, menu, 'add parents to ' + text, 'Add a parent to this tag.', self._ProcessMenuTagEvent, 'parent' ) ClientGUIMenus.AppendMenuItem( self, menu, 'add siblings to ' + text, 'Add a sibling to this tag.', self._ProcessMenuTagEvent, 'sibling' ) HG.client_controller.PopupMenu( self, menu ) event.Skip() def GetSelectedTags( self ): return set( self._selected_terms ) def SiblingsHaveChanged( self ): pass class ListBoxTagsPredicates( ListBoxTags ): has_counts = True def _GetWithParentIndices( self, index ): indices = [ index ] index += 1 while index < len( self._ordered_terms ): term = self._GetTerm( index ) if term.GetType() == HC.PREDICATE_TYPE_PARENT: indices.append( index ) else: break index += 1 return indices def _Deselect( self, index ): to_deselect = self._GetWithParentIndices( index ) for index in to_deselect: ListBoxTags._Deselect( self, index ) def _GetAllTagsForClipboard( self, with_counts = False ): return [ term.GetUnicode( with_counts ) for term in self._terms ] def _GetNamespaceFromTerm( self, term ): predicate = term namespace = predicate.GetNamespace() return namespace def _GetSafeHitIndex( self, hit_index, direction = None ): hit_index = ListBox._GetSafeHitIndex( self, hit_index ) if direction is not None and hit_index is not None: hit_term = self._GetTerm( hit_index ) while hit_term.GetType() == HC.PREDICATE_TYPE_PARENT: hit_index += direction if hit_index >= len( self._ordered_terms ): hit_index = 0 hit_term = self._GetTerm( hit_index ) return hit_index def _GetSimplifiedTextFromTerm( self, term ): predicate = term return predicate.GetUnicode( with_counts = False ) def _GetTextFromTerm( self, term ): predicate = term return predicate.GetUnicode() def _HasPredicate( self, predicate ): return predicate in self._terms def _Hit( self, shift, ctrl, hit_index ): hit_index = self._GetSafeHitIndex( hit_index ) if hit_index is not None and hit_index > 0: # this realigns the hit index in the up direction, so if user clicks on parent, they get the upper child while self._GetTerm( hit_index ).GetType() == HC.PREDICATE_TYPE_PARENT: hit_index -= 1 ListBoxTags._Hit( self, shift, ctrl, hit_index ) def _Select( self, index ): to_select = self._GetWithParentIndices( index ) for index in to_select: ListBoxTags._Select( self, index ) def GetPredicates( self ): return set( self._terms ) class ListBoxTagsActiveSearchPredicates( ListBoxTagsPredicates ): has_counts = False def __init__( self, parent, page_key, initial_predicates = None ): if initial_predicates is None: initial_predicates = [] ListBoxTagsPredicates.__init__( self, parent, min_height = 100 ) self._page_key = page_key self._get_current_predicates_callable = self.GetPredicates if len( initial_predicates ) > 0: for predicate in initial_predicates: self._AppendTerm( predicate ) self._DataHasChanged() HG.client_controller.sub( self, 'EnterPredicates', 'enter_predicates' ) def _Activate( self ): if len( self._selected_terms ) > 0: self._EnterPredicates( set( self._selected_terms ) ) def _DeleteActivate( self ): self._Activate() def _EnterPredicates( self, predicates, permit_add = True, permit_remove = True ): if len( predicates ) == 0: return predicates_to_be_added = set() predicates_to_be_removed = set() for predicate in predicates: predicate = predicate.GetCountlessCopy() if self._HasPredicate( predicate ): if permit_remove: predicates_to_be_removed.add( predicate ) else: if permit_add: predicates_to_be_added.add( predicate ) inverse_predicate = predicate.GetInverseCopy() if self._HasPredicate( inverse_predicate ): predicates_to_be_removed.add( inverse_predicate ) for predicate in predicates_to_be_added: self._AppendTerm( predicate ) for predicate in predicates_to_be_removed: self._RemoveTerm( predicate ) self._SortByText() self._DataHasChanged() HG.client_controller.pub( 'refresh_query', self._page_key ) def _GetTextFromTerm( self, term ): predicate = term return predicate.GetUnicode( render_for_user = True ) def _ProcessMenuPredicateEvent( self, command ): ( include_predicates, exclude_predicates ) = self._GetSelectedIncludeExcludePredicates() if command == 'add_include_predicates': self._EnterPredicates( include_predicates, permit_remove = False ) elif command == 'remove_include_predicates': self._EnterPredicates( include_predicates, permit_add = False ) elif command == 'add_exclude_predicates': self._EnterPredicates( exclude_predicates, permit_remove = False ) elif command == 'remove_exclude_predicates': self._EnterPredicates( exclude_predicates, permit_add = False ) 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 ) class ListBoxTagsAC( ListBoxTagsPredicates ): def __init__( self, parent, callable, service_key, **kwargs ): ListBoxTagsPredicates.__init__( self, parent, **kwargs ) self._callable = callable self._service_key = service_key self._predicates = {} def _Activate( self ): predicates = [ term for term in self._selected_terms if term.GetType() != HC.PREDICATE_TYPE_PARENT ] predicates = HG.client_controller.GetGUI().FlushOutPredicates( predicates ) if len( predicates ) > 0: self._callable( predicates ) 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() for predicate in predicates: self._AppendTerm( predicate ) self._DataHasChanged() if len( predicates ) > 0: hit_index = 0 if len( predicates ) > 1: if HG.client_controller.new_options.GetBoolean( 'ac_select_first_with_count' ): for ( index, predicate ) in enumerate( predicates ): if predicate.GetCount() != 0: hit_index = index break self._Hit( False, False, hit_index ) def SetTagService( self, service_key ): self._service_key = service_key class ListBoxTagsACRead( ListBoxTagsAC ): def _GetTextFromTerm( self, term ): predicate = term return predicate.GetUnicode( render_for_user = True ) class ListBoxTagsACWrite( ListBoxTagsAC ): def _GetTextFromTerm( self, term ): predicate = term return predicate.GetUnicode( sibling_service_key = self._service_key ) class ListBoxTagsCensorship( ListBoxTags ): def _Activate( self ): if len( self._selected_terms ) > 0: tags = set( self._selected_terms ) for tag in tags: self._RemoveTerm( tag ) self._ordered_terms.sort() self._DataHasChanged() def _GetNamespaceFromTerm( self, term ): tag = term if tag == ':': return None else: ( namespace, subtag ) = HydrusTags.SplitTag( tag ) return namespace def _GetTextFromTerm( self, term ): tag_slice = term return ClientData.ConvertTagSliceToString( tag_slice ) def AddTags( self, tags ): for tag in tags: self._AppendTerm( tag ) self._ordered_terms.sort() self._DataHasChanged() def EnterTags( self, tags ): for tag in tags: if tag in self._terms: self._RemoveTerm( tag ) else: self._AppendTerm( tag ) self._ordered_terms.sort() self._DataHasChanged() def RemoveTags( self, tags ): for tag in tags: self._RemoveTerm( tag ) self._ordered_terms.sort() self._DataHasChanged() class ListBoxTagsColourOptions( ListBoxTags ): PROTECTED_TERMS = ( None, '' ) can_spawn_new_windows = False def __init__( self, parent, initial_namespace_colours ): ListBoxTags.__init__( self, parent ) for ( namespace, colour ) in initial_namespace_colours.items(): colour = tuple( colour ) # tuple to convert from list, for oooold users who have list colours self._AppendTerm( ( namespace, colour ) ) self._SortByText() self._DataHasChanged() def _Activate( self ): namespaces = [ namespace for ( namespace, colour ) in self._selected_terms ] if len( namespaces ) > 0: import ClientGUIDialogs with ClientGUIDialogs.DialogYesNo( self, 'Delete all selected colours?' ) as dlg: if dlg.ShowModal() == wx.ID_YES: self._RemoveNamespaces( namespaces ) def _DeleteActivate( self ): self._Activate() def _GetTextFromTerm( self, term ): ( namespace, colour ) = term if namespace is None: namespace_string = 'default namespace:tag' elif namespace == '': namespace_string = 'unnamespaced tag' else: namespace_string = namespace + ':tag' return namespace_string def _GetNamespaceColours( self ): return dict( self._terms ) def _GetNamespaceFromTerm( self, term ): ( namespace, colour ) = term return namespace def _RemoveNamespaces( self, namespaces ): namespaces = [ namespace for namespace in namespaces if namespace not in self.PROTECTED_TERMS ] removees = [ ( existing_namespace, existing_colour ) for ( existing_namespace, existing_colour ) in self._terms if existing_namespace in namespaces ] for removee in removees: self._RemoveTerm( removee ) self._DataHasChanged() def SetNamespaceColour( self, namespace, colour ): ( r, g, b, a ) = colour.Get() colour_tuple = ( r, g, b ) for ( existing_namespace, existing_colour ) in self._terms: if existing_namespace == namespace: self._RemoveTerm( ( existing_namespace, existing_colour ) ) break self._AppendTerm( ( namespace, colour_tuple ) ) self._SortByText() self._DataHasChanged() def GetNamespaceColours( self ): return self._GetNamespaceColours() def GetSelectedNamespaceColours( self ): namespace_colours = dict( self._selected_terms ) return namespace_colours class ListBoxTagsStrings( ListBoxTags ): def __init__( self, parent, service_key = None, show_sibling_text = True, sort_tags = True ): ListBoxTags.__init__( self, parent ) if service_key is not None: service_key = CC.COMBINED_TAG_SERVICE_KEY self._service_key = service_key self._show_sibling_text = show_sibling_text self._sort_tags = sort_tags def _GetNamespaceFromTerm( self, term ): tag = term ( namespace, subtag ) = HydrusTags.SplitTag( tag ) return namespace def _GetSimplifiedTextFromTerm( self, term ): tag = term return HydrusData.ToUnicode( tag ) def _GetTextFromTerm( self, term ): siblings_manager = HG.client_controller.GetManager( 'tag_siblings' ) tag = term tag_string = ClientTags.RenderTag( tag, True ) if self._show_sibling_text: sibling = siblings_manager.GetSibling( self._service_key, tag ) if sibling is not None: tag_string += ' (will display as ' + ClientTags.RenderTag( sibling, True ) + ')' return tag_string def _RecalcTags( self ): self._RefreshTexts() if self._sort_tags: self._SortByText() self._DataHasChanged() def GetTags( self ): return set( self._terms ) def SetTags( self, tags ): self._Clear() for tag in tags: self._AppendTerm( tag ) self._RecalcTags() def SiblingsHaveChanged( self ): self._RecalcTags() class ListBoxTagsStringsAddRemove( ListBoxTagsStrings ): def __init__( self, parent, service_key = None, removed_callable = None, show_sibling_text = True ): ListBoxTagsStrings.__init__( self, parent, service_key = service_key, show_sibling_text = show_sibling_text ) self._removed_callable = removed_callable def _Activate( self ): if len( self._selected_terms ) > 0: tags = set( self._selected_terms ) self._RemoveTags( tags ) def _RemoveTags( self, tags ): for tag in tags: self._RemoveTerm( tag ) self._RecalcTags() if self._removed_callable is not None: self._removed_callable( tags ) def AddTags( self, tags ): for tag in tags: self._AppendTerm( tag ) self._RecalcTags() def Clear( self ): self._Clear() self._RecalcTags() def EnterTags( self, tags ): removed = set() for tag in tags: if tag in self._terms: self._RemoveTerm( tag ) removed.add( tag ) else: self._AppendTerm( tag ) self._RecalcTags() if len( removed ) > 0 and self._removed_callable is not None: self._removed_callable( removed ) def EventCharHook( self, event ): ( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event ) if key in CC.DELETE_KEYS: self._Activate() else: event.Skip() def RemoveTags( self, tags ): self._RemoveTags( tags ) class ListBoxTagsSelection( ListBoxTags ): render_for_user = True has_counts = True def __init__( self, parent, include_counts = True, collapse_siblings = False ): ListBoxTags.__init__( self, parent, min_height = 200 ) self._sort = HC.options[ 'default_tag_sort' ] if not include_counts and self._sort in ( CC.SORT_BY_INCIDENCE_ASC, CC.SORT_BY_INCIDENCE_DESC, CC.SORT_BY_INCIDENCE_NAMESPACE_ASC, CC.SORT_BY_INCIDENCE_NAMESPACE_DESC ): self._sort = CC.SORT_BY_LEXICOGRAPHIC_ASC self._last_media = set() self._tag_service_key = CC.COMBINED_TAG_SERVICE_KEY self._include_counts = include_counts self._collapse_siblings = collapse_siblings self._current_tags_to_count = collections.Counter() self._deleted_tags_to_count = collections.Counter() self._pending_tags_to_count = collections.Counter() self._petitioned_tags_to_count = collections.Counter() self._show_current = True self._show_deleted = False self._show_pending = True self._show_petitioned = True def _GetAllTagsForClipboard( self, with_counts = False ): if with_counts: return [ self._terms_to_texts[ term ] for term in self._ordered_terms ] else: return self._ordered_terms def _GetNamespaceFromTerm( self, term ): tag = term ( namespace, subtag ) = HydrusTags.SplitTag( tag ) return namespace def _GetSimplifiedTextFromTerm( self, term ): tag = term return HydrusData.ToUnicode( tag ) def _GetTextFromTerm( self, term ): tag = term tag_string = ClientTags.RenderTag( tag, self.render_for_user ) if self._include_counts: if self._show_current and tag in self._current_tags_to_count: tag_string += ' (' + HydrusData.ConvertIntToPrettyString( self._current_tags_to_count[ tag ] ) + ')' if self._show_pending and tag in self._pending_tags_to_count: tag_string += ' (+' + HydrusData.ConvertIntToPrettyString( self._pending_tags_to_count[ tag ] ) + ')' if self._show_petitioned and tag in self._petitioned_tags_to_count: tag_string += ' (-' + HydrusData.ConvertIntToPrettyString( self._petitioned_tags_to_count[ tag ] ) + ')' if self._show_deleted and tag in self._deleted_tags_to_count: tag_string += ' (X' + HydrusData.ConvertIntToPrettyString( self._deleted_tags_to_count[ tag ] ) + ')' else: if self._show_pending and tag in self._pending_tags_to_count: tag_string += ' (+)' if self._show_petitioned and tag in self._petitioned_tags_to_count: tag_string += ' (-)' if self._show_deleted and tag in self._deleted_tags_to_count: tag_string += ' (X)' if not self._collapse_siblings: siblings_manager = HG.client_controller.GetManager( 'tag_siblings' ) sibling = siblings_manager.GetSibling( self._tag_service_key, tag ) if sibling is not None: sibling = ClientTags.RenderTag( sibling, self.render_for_user ) tag_string += ' (will display as ' + sibling + ')' return tag_string def _RecalcStrings( self, limit_to_these_tags = None ): previous_selected_terms = set( self._selected_terms ) if limit_to_these_tags is None: self._Clear() nonzero_tags = set() if self._show_current: nonzero_tags.update( ( tag for ( tag, count ) in self._current_tags_to_count.items() if count > 0 ) ) if self._show_deleted: nonzero_tags.update( ( tag for ( tag, count ) in self._deleted_tags_to_count.items() if count > 0 ) ) if self._show_pending: nonzero_tags.update( ( tag for ( tag, count ) in self._pending_tags_to_count.items() if count > 0 ) ) if self._show_petitioned: nonzero_tags.update( ( tag for ( tag, count ) in self._petitioned_tags_to_count.items() if count > 0 ) ) for tag in nonzero_tags: self._AppendTerm( tag ) else: if not isinstance( limit_to_these_tags, set ): limit_to_these_tags = set( limit_to_these_tags ) for tag in limit_to_these_tags: self._RemoveTerm( tag ) nonzero_tags = set() if self._show_current: nonzero_tags.update( ( tag for ( tag, count ) in self._current_tags_to_count.items() if count > 0 and tag in limit_to_these_tags ) ) if self._show_deleted: nonzero_tags.update( ( tag for ( tag, count ) in self._deleted_tags_to_count.items() if count > 0 and tag in limit_to_these_tags ) ) if self._show_pending: nonzero_tags.update( ( tag for ( tag, count ) in self._pending_tags_to_count.items() if count > 0 and tag in limit_to_these_tags ) ) if self._show_petitioned: nonzero_tags.update( ( tag for ( tag, count ) in self._petitioned_tags_to_count.items() if count > 0 and tag in limit_to_these_tags ) ) for tag in nonzero_tags: self._AppendTerm( tag ) for term in previous_selected_terms: if term in self._terms: self._selected_terms.add( term ) self._SortTags() def _SortTags( self ): def lexicographic_key( term ): tag = self._terms_to_texts[ term ] ( namespace, subtag ) = HydrusTags.SplitTag( tag ) comparable_subtag = HydrusTags.ConvertTagToSortable( subtag ) # 'cat' < 'character:rei' # 'page:3' < 'page:20' # '1' < 'series:eva' # note that 'test' < ( 1, '' ) but u'test' > ( 1, '' ) wew if namespace == '': return ( comparable_subtag, comparable_subtag ) else: return ( namespace, comparable_subtag ) def incidence_key( term ): return tags_to_count[ term ] def namespace_key( term ): tag = term ( namespace, subtag ) = HydrusTags.SplitTag( tag ) if namespace == '': namespace = '{' # '{' is above 'z' in ascii, so this works for most situations return namespace def namespace_lexicographic_key( term ): tag = term # '{' is above 'z' in ascii, so this works for most situations ( namespace, subtag ) = HydrusTags.SplitTag( tag ) if namespace == '': return ( '{', HydrusTags.ConvertTagToSortable( subtag ) ) else: return ( namespace, HydrusTags.ConvertTagToSortable( subtag ) ) if self._sort in ( CC.SORT_BY_INCIDENCE_ASC, CC.SORT_BY_INCIDENCE_DESC, CC.SORT_BY_INCIDENCE_NAMESPACE_ASC, CC.SORT_BY_INCIDENCE_NAMESPACE_DESC ): tags_to_count = collections.Counter() if self._show_current: tags_to_count.update( self._current_tags_to_count ) if self._show_deleted: tags_to_count.update( self._deleted_tags_to_count ) if self._show_pending: tags_to_count.update( self._pending_tags_to_count ) if self._show_petitioned: tags_to_count.update( self._petitioned_tags_to_count ) # let's establish a-z here for equal incidence values later if self._sort in ( CC.SORT_BY_INCIDENCE_ASC, CC.SORT_BY_INCIDENCE_NAMESPACE_ASC ): self._ordered_terms.sort( key = lexicographic_key, reverse = True ) reverse = False elif self._sort in ( CC.SORT_BY_INCIDENCE_DESC, CC.SORT_BY_INCIDENCE_NAMESPACE_DESC ): self._ordered_terms.sort( key = lexicographic_key ) reverse = True self._ordered_terms.sort( key = incidence_key, reverse = reverse ) if self._sort in ( CC.SORT_BY_INCIDENCE_NAMESPACE_ASC, CC.SORT_BY_INCIDENCE_NAMESPACE_DESC ): # python list sort is stable, so lets now sort again if self._sort == CC.SORT_BY_INCIDENCE_NAMESPACE_ASC: reverse = True elif self._sort == CC.SORT_BY_INCIDENCE_NAMESPACE_DESC: reverse = False self._ordered_terms.sort( key = namespace_key, reverse = reverse ) else: if self._sort in ( CC.SORT_BY_LEXICOGRAPHIC_DESC, CC.SORT_BY_LEXICOGRAPHIC_NAMESPACE_DESC ): reverse = True elif self._sort in ( CC.SORT_BY_LEXICOGRAPHIC_ASC, CC.SORT_BY_LEXICOGRAPHIC_NAMESPACE_ASC ): reverse = False if self._sort in ( CC.SORT_BY_LEXICOGRAPHIC_NAMESPACE_ASC, CC.SORT_BY_LEXICOGRAPHIC_NAMESPACE_DESC ): key = namespace_lexicographic_key elif self._sort in ( CC.SORT_BY_LEXICOGRAPHIC_ASC, CC.SORT_BY_LEXICOGRAPHIC_DESC ): key = lexicographic_key self._ordered_terms.sort( key = key, reverse = reverse ) self._DataHasChanged() def ChangeTagService( self, service_key ): self._tag_service_key = service_key self.SetTagsByMedia( self._last_media, force_reload = True ) def SetSort( self, sort ): self._sort = sort self._SortTags() def SetShow( self, show_type, value ): if show_type == 'current': self._show_current = value elif show_type == 'deleted': self._show_deleted = value elif show_type == 'pending': self._show_pending = value elif show_type == 'petitioned': self._show_petitioned = value self._RecalcStrings() def IncrementTagsByMedia( self, media ): media = set( media ) media = media.difference( self._last_media ) ( current_tags_to_count, deleted_tags_to_count, pending_tags_to_count, petitioned_tags_to_count ) = ClientData.GetMediasTagCount( media, tag_service_key = self._tag_service_key, collapse_siblings = self._collapse_siblings ) self._current_tags_to_count.update( current_tags_to_count ) self._deleted_tags_to_count.update( deleted_tags_to_count ) self._pending_tags_to_count.update( pending_tags_to_count ) self._petitioned_tags_to_count.update( petitioned_tags_to_count ) tags_changed = set() if self._show_current: tags_changed.update( current_tags_to_count.keys() ) if self._show_deleted: tags_changed.update( deleted_tags_to_count.keys() ) if self._show_pending: tags_changed.update( pending_tags_to_count.keys() ) if self._show_petitioned: tags_changed.update( petitioned_tags_to_count.keys() ) if len( tags_changed ) > 0: self._RecalcStrings( tags_changed ) self._last_media.update( media ) def SetTagsByMedia( self, media, force_reload = False ): media = set( media ) if force_reload: ( current_tags_to_count, deleted_tags_to_count, pending_tags_to_count, petitioned_tags_to_count ) = ClientData.GetMediasTagCount( media, tag_service_key = self._tag_service_key, collapse_siblings = self._collapse_siblings ) self._current_tags_to_count = current_tags_to_count self._deleted_tags_to_count = deleted_tags_to_count self._pending_tags_to_count = pending_tags_to_count self._petitioned_tags_to_count = petitioned_tags_to_count self._RecalcStrings() else: removees = self._last_media.difference( media ) adds = media.difference( self._last_media ) ( current_tags_to_count, deleted_tags_to_count, pending_tags_to_count, petitioned_tags_to_count ) = ClientData.GetMediasTagCount( removees, tag_service_key = self._tag_service_key, collapse_siblings = self._collapse_siblings ) self._current_tags_to_count.subtract( current_tags_to_count ) self._deleted_tags_to_count.subtract( deleted_tags_to_count ) self._pending_tags_to_count.subtract( pending_tags_to_count ) self._petitioned_tags_to_count.subtract( petitioned_tags_to_count ) ( current_tags_to_count, deleted_tags_to_count, pending_tags_to_count, petitioned_tags_to_count ) = ClientData.GetMediasTagCount( adds, tag_service_key = self._tag_service_key, collapse_siblings = self._collapse_siblings ) self._current_tags_to_count.update( current_tags_to_count ) self._deleted_tags_to_count.update( deleted_tags_to_count ) self._pending_tags_to_count.update( pending_tags_to_count ) self._petitioned_tags_to_count.update( petitioned_tags_to_count ) for counter in ( self._current_tags_to_count, self._deleted_tags_to_count, self._pending_tags_to_count, self._petitioned_tags_to_count ): tags = counter.keys() for tag in tags: if counter[ tag ] == 0: del counter[ tag ] if len( removees ) == 0: tags_changed = set() if self._show_current: tags_changed.update( current_tags_to_count.keys() ) if self._show_deleted: tags_changed.update( deleted_tags_to_count.keys() ) if self._show_pending: tags_changed.update( pending_tags_to_count.keys() ) if self._show_petitioned: tags_changed.update( petitioned_tags_to_count.keys() ) if len( tags_changed ) > 0: self._RecalcStrings( tags_changed ) else: self._RecalcStrings() self._last_media = media self._DataHasChanged() def SiblingsHaveChanged( self ): self.SetTagsByMedia( self._last_media, force_reload = True ) class ListBoxTagsSelectionHoverFrame( ListBoxTagsSelection ): def __init__( self, parent, canvas_key ): ListBoxTagsSelection.__init__( self, parent, include_counts = False, collapse_siblings = True ) self._canvas_key = canvas_key def _Activate( self ): HG.client_controller.pub( 'canvas_manage_tags', self._canvas_key ) class ListBoxTagsSelectionManagementPanel( ListBoxTagsSelection ): def __init__( self, parent, page_key, predicates_callable = None ): ListBoxTagsSelection.__init__( self, parent, include_counts = True, collapse_siblings = True ) self._page_key = page_key self._get_current_predicates_callable = predicates_callable HG.client_controller.sub( self, 'IncrementTagsByMediaPubsub', 'increment_tags_selection' ) HG.client_controller.sub( self, 'SetTagsByMediaPubsub', 'new_tags_selection' ) HG.client_controller.sub( self, 'ChangeTagServicePubsub', 'change_tag_service' ) def _Activate( self ): predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, term ) for term in self._selected_terms ] if len( predicates ) > 0: HG.client_controller.pub( 'enter_predicates', self._page_key, predicates ) def _ProcessMenuPredicateEvent( self, command ): ( include_predicates, exclude_predicates ) = self._GetSelectedIncludeExcludePredicates() if command == 'add_include_predicates': HG.client_controller.pub( 'enter_predicates', self._page_key, include_predicates, permit_remove = False ) elif command == 'remove_include_predicates': HG.client_controller.pub( 'enter_predicates', self._page_key, include_predicates, permit_add = False ) elif command == 'add_exclude_predicates': HG.client_controller.pub( 'enter_predicates', self._page_key, exclude_predicates, permit_remove = False ) elif command == 'remove_exclude_predicates': HG.client_controller.pub( 'enter_predicates', self._page_key, exclude_predicates, permit_add = False ) def ChangeTagServicePubsub( self, page_key, service_key ): if page_key == self._page_key: self.ChangeTagService( service_key ) def IncrementTagsByMediaPubsub( self, page_key, media ): if page_key == self._page_key: self.IncrementTagsByMedia( media ) def SetTagsByMediaPubsub( self, page_key, media, force_reload = False ): if page_key == self._page_key: self.SetTagsByMedia( media, force_reload = force_reload ) class ListBoxTagsSelectionTagsDialog( ListBoxTagsSelection ): render_for_user = False def __init__( self, parent, add_func, delete_func ): ListBoxTagsSelection.__init__( self, parent, include_counts = True, collapse_siblings = False ) self._add_func = add_func self._delete_func = delete_func def _Activate( self ): if len( self._selected_terms ) > 0: self._add_func( set( self._selected_terms ) ) def _DeleteActivate( self ): if len( self._selected_terms ) > 0: self._delete_func( set( self._selected_terms ) )