hydrus/include/ClientGUICommon.py

4000 lines
136 KiB
Python
Raw Normal View History

2013-02-19 00:11:43 +00:00
import collections
import HydrusConstants as HC
import ClientConstants as CC
import ClientGUIMixins
2013-05-29 20:19:54 +00:00
import itertools
2013-02-19 00:11:43 +00:00
import os
import random
2013-08-14 20:21:49 +00:00
import sys
2013-02-19 00:11:43 +00:00
import time
import traceback
import wx
2013-03-15 02:38:12 +00:00
import wx.combo
2013-02-19 00:11:43 +00:00
import wx.richtext
from wx.lib.mixins.listctrl import ListCtrlAutoWidthMixin
from wx.lib.mixins.listctrl import ColumnSorterMixin
ID_TIMER_ANIMATED = wx.NewId()
ID_TIMER_SLIDESHOW = wx.NewId()
ID_TIMER_MEDIA_INFO_DISPLAY = wx.NewId()
2013-03-15 02:38:12 +00:00
ID_TIMER_DROPDOWN_HIDE = wx.NewId()
ID_TIMER_AC_LAG = wx.NewId()
2013-02-19 00:11:43 +00:00
# Zooms
ZOOMINS = [ 0.01, 0.05, 0.1, 0.15, 0.2, 0.3, 0.5, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.5, 2.0, 3.0, 5.0, 10.0, 20.0 ]
ZOOMOUTS = [ 20.0, 10.0, 5.0, 3.0, 2.0, 1.5, 1.2, 1.1, 1.0, 0.9, 0.8, 0.7, 0.5, 0.3, 0.2, 0.15, 0.1, 0.05, 0.01 ]
# Sizer Flags
FLAGS_NONE = wx.SizerFlags( 0 )
FLAGS_SMALL_INDENT = wx.SizerFlags( 0 ).Border( wx.ALL, 2 )
FLAGS_EXPAND_PERPENDICULAR = wx.SizerFlags( 0 ).Border( wx.ALL, 2 ).Expand()
FLAGS_EXPAND_BOTH_WAYS = wx.SizerFlags( 2 ).Border( wx.ALL, 2 ).Expand()
2013-03-15 02:38:12 +00:00
FLAGS_EXPAND_SIZER_PERPENDICULAR = wx.SizerFlags( 0 ).Expand()
FLAGS_EXPAND_SIZER_BOTH_WAYS = wx.SizerFlags( 2 ).Expand()
2013-02-19 00:11:43 +00:00
FLAGS_BUTTON_SIZERS = wx.SizerFlags( 0 ).Align( wx.ALIGN_RIGHT )
FLAGS_LONE_BUTTON = wx.SizerFlags( 0 ).Border( wx.ALL, 2 ).Align( wx.ALIGN_RIGHT )
FLAGS_MIXED = wx.SizerFlags( 0 ).Border( wx.ALL, 2 ).Align( wx.ALIGN_CENTER_VERTICAL )
2013-03-15 02:38:12 +00:00
class AnimatedStaticTextTimestamp( wx.StaticText ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def __init__( self, parent, prefix, rendering_function, timestamp, suffix ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._prefix = prefix
self._rendering_function = rendering_function
self._timestamp = timestamp
self._suffix = suffix
2013-02-19 00:11:43 +00:00
2013-07-31 21:26:38 +00:00
self._last_tick = HC.GetNow()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
wx.StaticText.__init__( self, parent, label = self._prefix + self._rendering_function( self._timestamp ) + self._suffix )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
HC.pubsub.sub( self, 'Tick', 'animated_tick' )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def Tick( self ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
update = False
2013-02-19 00:11:43 +00:00
2013-07-31 21:26:38 +00:00
now = HC.GetNow()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
difference = abs( now - self._timestamp )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if difference < 3600: update = True
elif difference < 3600 * 24 and now - self._last_tick > 60: update = True
elif now - self._last_tick > 3600: update = True
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if update:
self.SetLabel( self._prefix + self._rendering_function( self._timestamp ) + self._suffix )
wx.PostEvent( self.GetEventHandler(), wx.SizeEvent() )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
# 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.TextCtrl ):
def __init__( self, parent ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
wx.TextCtrl.__init__( self, parent, style=wx.TE_PROCESS_ENTER )
2013-02-19 00:11:43 +00:00
2013-03-23 17:57:29 +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 )
2013-04-17 21:48:18 +00:00
self._dropdown_window = wx.Frame( self, style = wx.FRAME_TOOL_WINDOW | wx.FRAME_NO_TASKBAR | wx.FRAME_FLOAT_ON_PARENT | wx.BORDER_RAISED )
2013-03-23 17:57:29 +00:00
2013-03-15 02:38:12 +00:00
self._dropdown_window.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
2013-02-19 00:11:43 +00:00
2013-03-23 17:57:29 +00:00
self._dropdown_list = self._InitDropDownList()
2013-03-15 02:38:12 +00:00
self._first_letters = ''
self._cached_results = self._InitCachedResults()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.Bind( wx.EVT_SET_FOCUS, self.EventSetFocus )
self.Bind( wx.EVT_KILL_FOCUS, self.EventKillFocus )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.Bind( wx.EVT_TEXT, self.EventText, self )
self.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown, self )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.Bind( wx.EVT_MOVE, self.EventMove )
self.Bind( wx.EVT_SIZE, self.EventMove )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.Bind( wx.EVT_MOUSEWHEEL, self.EventMouseWheel )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.Bind( wx.EVT_TIMER, self.EventDropdownHideTimer, id = ID_TIMER_DROPDOWN_HIDE )
self.Bind( wx.EVT_TIMER, self.EventLagTimer, id = ID_TIMER_AC_LAG )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._move_hide_timer = wx.Timer( self, id = ID_TIMER_DROPDOWN_HIDE )
self._lag_timer = wx.Timer( self, id = ID_TIMER_AC_LAG )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
tlp = self.GetTopLevelParent()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
tlp.Bind( wx.EVT_MOVE, self.EventMove )
2013-02-19 00:11:43 +00:00
2013-09-11 21:28:19 +00:00
parent = self
while True:
try:
parent = parent.GetParent()
if issubclass( type( parent ), wx.ScrolledWindow ):
parent.Bind( wx.EVT_SCROLLWIN, self.EventMove )
except: break
2013-08-28 21:31:52 +00:00
wx.CallAfter( self._UpdateList )
2013-03-15 02:38:12 +00:00
def _BroadcastChoice( self, predicate ): pass
def BroadcastChoice( self, predicate ):
2013-02-19 00:11:43 +00:00
2013-08-28 21:31:52 +00:00
if self.GetValue() != '':
self.SetValue( '' )
2013-02-19 00:11:43 +00:00
2013-08-28 21:31:52 +00:00
self._BroadcastChoice( predicate )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def _HideDropdown( self ): self._dropdown_window.Show( False )
def _ShowDropdownIfFocussed( self ):
2013-02-19 00:11:43 +00:00
2013-09-11 21:28:19 +00:00
if self.GetTopLevelParent().IsActive() and wx.Window.FindFocus() == self:
2013-03-15 02:38:12 +00:00
( my_width, my_height ) = self.GetSize()
2013-09-11 21:28:19 +00:00
if not self._dropdown_window.IsShown():
self._dropdown_window.Fit()
self._dropdown_window.SetSize( ( my_width, -1 ) )
self._dropdown_window.Layout()
2013-03-15 02:38:12 +00:00
self._dropdown_window.SetPosition( self.ClientToScreenXY( -2, my_height - 2 ) )
self._dropdown_window.Show()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def _UpdateList( self ): pass
def EventDropdownHideTimer( self, event ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
try: self._ShowDropdownIfFocussed()
except: pass
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def EventKeyDown( self, event ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if event.KeyCode in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ) and self.GetValue() == '' and len( self._dropdown_list ) == 0: self._BroadcastChoice( None )
elif event.KeyCode == wx.WXK_ESCAPE: self.GetTopLevelParent().SetFocus()
elif event.KeyCode in ( wx.WXK_UP, wx.WXK_NUMPAD_UP, wx.WXK_DOWN, wx.WXK_NUMPAD_DOWN ) and self.GetValue() == '' and len( self._dropdown_list ) == 0:
if event.KeyCode in ( wx.WXK_UP, wx.WXK_NUMPAD_UP ): id = CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'select_up' )
elif event.KeyCode in ( wx.WXK_DOWN, wx.WXK_NUMPAD_DOWN ): id = CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'select_down' )
new_event = wx.CommandEvent( commandType = wx.wxEVT_COMMAND_MENU_SELECTED, winid = id )
self.ProcessEvent( new_event )
else: self._dropdown_list.ProcessEvent( event )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def EventKillFocus( self, event ):
2013-02-19 00:11:43 +00:00
2013-03-23 17:57:29 +00:00
new_window = event.GetWindow()
if new_window == self._dropdown_window or new_window in self._dropdown_window.GetChildren(): pass
else: self._HideDropdown()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
event.Skip()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def EventLagTimer( self, event ): self._UpdateList()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def EventMouseWheel( self, event ):
if self.GetValue() == '' and len( self._dropdown_list ) == 0:
if event.GetWheelRotation() > 0: id = CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'select_up' )
else: id = CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'select_down' )
new_event = wx.CommandEvent( commandType = wx.wxEVT_COMMAND_MENU_SELECTED, winid = id )
self.ProcessEvent( new_event )
else:
if event.CmdDown():
key_event = wx.KeyEvent( wx.EVT_KEY_DOWN.typeId )
if event.GetWheelRotation() > 0: key_event.m_keyCode = wx.WXK_UP
else: key_event.m_keyCode = wx.WXK_DOWN
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 )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def EventMove( self, event ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
try:
2013-03-23 17:57:29 +00:00
try: self._HideDropdown()
except: pass
2013-03-15 02:38:12 +00:00
2013-09-11 21:28:19 +00:00
lag = 250
2013-03-15 02:38:12 +00:00
self._move_hide_timer.Start( lag, wx.TIMER_ONE_SHOT )
except wx.PyDeadObjectError: pass
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
event.Skip()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def EventSetFocus( self, event ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._ShowDropdownIfFocussed()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
event.Skip()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def EventText( self, event ):
2013-02-19 00:11:43 +00:00
2013-08-28 21:31:52 +00:00
if len( self.GetValue() ) == 0: self._UpdateList()
else: self._lag_timer.Start( 150, wx.TIMER_ONE_SHOT )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
class AutoCompleteDropdownContacts( AutoCompleteDropdown ):
def __init__( self, parent, compose_key, identity ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
AutoCompleteDropdown.__init__( self, parent )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._compose_key = compose_key
self._identity = identity
2013-02-19 00:11:43 +00:00
vbox = wx.BoxSizer( wx.VERTICAL )
2013-03-15 02:38:12 +00:00
vbox.AddF( self._dropdown_list, FLAGS_EXPAND_BOTH_WAYS )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._dropdown_window.SetSizer( vbox )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def _BroadcastChoice( self, contact_name ): HC.pubsub.pub( 'add_contact', self._compose_key, contact_name )
def _GenerateMatches( self ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
num_first_letters = 1
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
entry = self.GetValue()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if entry == '':
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._first_letters = ''
matches = []
else:
if len( entry ) >= num_first_letters:
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if entry[ : num_first_letters ] != self._first_letters:
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._first_letters = entry[ : num_first_letters ]
2013-02-19 00:11:43 +00:00
2013-07-10 20:25:57 +00:00
self._cached_results = HC.app.Read( 'autocomplete_contacts', entry, name_to_exclude = self._identity.GetName() )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
matches = self._cached_results.GetMatches( entry )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
else: matches = []
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
return matches
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def _InitCachedResults( self ): return CC.AutocompleteMatches( [] )
def _InitDropDownList( self ): return ListBoxMessagesActiveOnly( self._dropdown_window, self.BroadcastChoice )
def _UpdateList( self ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
matches = self._GenerateMatches()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
# this obv needs to be SetValues or whatever
self._dropdown_list.SetTexts( matches )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if len( matches ) > 0: self._ShowDropdownIfFocussed()
else: self._HideDropdown()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
class AutoCompleteDropdownMessageTerms( AutoCompleteDropdown ):
def __init__( self, parent, page_key, identity ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
AutoCompleteDropdown.__init__( self, parent )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._page_key = page_key
self._identity = identity
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
vbox = wx.BoxSizer( wx.VERTICAL )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
vbox.AddF( self._dropdown_list, FLAGS_EXPAND_BOTH_WAYS )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._dropdown_window.SetSizer( vbox )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def _BroadcastChoice( self, predicate ): HC.pubsub.pub( 'add_predicate', self._page_key, predicate )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def _InitCachedResults( self ): return CC.AutocompleteMatchesCounted( {} )
def _InitDropDownList( self ): return ListBoxMessagesActiveOnly( self._dropdown_window, self.BroadcastChoice )
def _GenerateMatches( self ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
entry = self.GetValue()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if entry.startswith( '-' ): search_term = entry[1:]
else: search_term = entry
2013-02-19 00:11:43 +00:00
2013-07-10 20:25:57 +00:00
if search_term == '': matches = HC.app.Read( 'message_system_predicates', self._identity )
2013-03-15 02:38:12 +00:00
else: matches = [ ( entry, None ) ]
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
return matches
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def _UpdateList( self ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
matches = self._GenerateMatches()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._dropdown_list.SetTerms( matches )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if len( matches ) > 0: self._ShowDropdownIfFocussed()
else: self._HideDropdown()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
class AutoCompleteDropdownTags( AutoCompleteDropdown ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def __init__( self, parent, file_service_identifier, tag_service_identifier ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
AutoCompleteDropdown.__init__( self, parent )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._current_namespace = ''
self._current_matches = []
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._file_service_identifier = file_service_identifier
self._tag_service_identifier = tag_service_identifier
2013-02-19 00:11:43 +00:00
2013-05-29 20:19:54 +00:00
name = self._file_service_identifier.GetName()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._file_repo_button = wx.Button( self._dropdown_window, label = name )
self._file_repo_button.Bind( wx.EVT_BUTTON, self.EventFileButton )
2013-02-19 00:11:43 +00:00
2013-05-29 20:19:54 +00:00
name = self._tag_service_identifier.GetName()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._tag_repo_button = wx.Button( self._dropdown_window, label = name )
self._tag_repo_button.Bind( wx.EVT_BUTTON, self.EventTagButton )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.Bind( wx.EVT_MENU, self.EventMenu )
2013-02-19 00:11:43 +00:00
2013-05-15 18:58:14 +00:00
def _InitCachedResults( self ): return CC.AutocompleteMatchesPredicates( HC.LOCAL_FILE_SERVICE_IDENTIFIER, [] )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def _InitDropDownList( self ): return TagsBoxActiveOnly( self._dropdown_window, self.BroadcastChoice )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def _UpdateList( self ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
matches = self._GenerateMatches()
2013-03-27 20:02:51 +00:00
self._dropdown_list.SetPredicates( matches )
2013-03-15 02:38:12 +00:00
self._current_matches = matches
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def EventFileButton( self, event ):
2013-02-19 00:11:43 +00:00
2013-05-29 20:19:54 +00:00
service_identifiers = []
service_identifiers.append( HC.COMBINED_FILE_SERVICE_IDENTIFIER )
service_identifiers.append( HC.LOCAL_FILE_SERVICE_IDENTIFIER )
2013-07-10 20:25:57 +00:00
service_identifiers.extend( HC.app.Read( 'service_identifiers', ( HC.FILE_REPOSITORY, ) ) )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
menu = wx.Menu()
for service_identifier in service_identifiers: menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'change_file_repository', service_identifier ), service_identifier.GetName() )
self.PopupMenu( menu )
2013-02-19 00:11:43 +00:00
2013-03-23 17:57:29 +00:00
menu.Destroy()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def EventMenu( self, event ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
action = CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
if action is not None:
2013-02-19 00:11:43 +00:00
2013-08-14 20:21:49 +00:00
( command, data ) = action
if command == 'change_file_repository':
2013-02-19 00:11:43 +00:00
2013-08-14 20:21:49 +00:00
service_identifier = data
2013-02-19 00:11:43 +00:00
2013-08-14 20:21:49 +00:00
self._file_service_identifier = service_identifier
2013-02-19 00:11:43 +00:00
2013-08-14 20:21:49 +00:00
name = service_identifier.GetName()
self._file_repo_button.SetLabel( name )
2013-02-19 00:11:43 +00:00
2013-08-14 20:21:49 +00:00
HC.pubsub.pub( 'change_file_repository', self._page_key, service_identifier )
2013-02-19 00:11:43 +00:00
2013-08-14 20:21:49 +00:00
elif command == 'change_tag_repository':
2013-02-19 00:11:43 +00:00
2013-08-14 20:21:49 +00:00
service_identifier = data
self._tag_service_identifier = service_identifier
name = service_identifier.GetName()
self._tag_repo_button.SetLabel( name )
HC.pubsub.pub( 'change_tag_repository', self._page_key, service_identifier )
else:
event.Skip()
return # this is about select_up and select_down
2013-02-19 00:11:43 +00:00
2013-08-14 20:21:49 +00:00
self._first_letters = ''
self._current_namespace = ''
self._UpdateList()
2013-02-19 00:11:43 +00:00
def EventTagButton( self, event ):
2013-05-29 20:19:54 +00:00
service_identifiers = []
service_identifiers.append( HC.COMBINED_TAG_SERVICE_IDENTIFIER )
service_identifiers.append( HC.LOCAL_TAG_SERVICE_IDENTIFIER )
2013-07-10 20:25:57 +00:00
service_identifiers.extend( HC.app.Read( 'service_identifiers', ( HC.TAG_REPOSITORY, ) ) )
2013-02-19 00:11:43 +00:00
menu = wx.Menu()
for service_identifier in service_identifiers: menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'change_tag_repository', service_identifier ), service_identifier.GetName() )
self.PopupMenu( menu )
2013-03-23 17:57:29 +00:00
menu.Destroy()
2013-02-19 00:11:43 +00:00
class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
def __init__( self, parent, page_key, file_service_identifier, tag_service_identifier, media_callable ):
AutoCompleteDropdownTags.__init__( self, parent, file_service_identifier, tag_service_identifier )
self._media_callable = media_callable
self._page_key = page_key
self._include_current = True
self._include_pending = True
self._include_current_tags = OnOffButton( self._dropdown_window, self._page_key, 'notify_include_current', on_label = 'include current tags', off_label = 'exclude current tags' )
self._include_current_tags.SetToolTipString( 'select whether to include current tags in the search' )
self._include_pending_tags = OnOffButton( self._dropdown_window, self._page_key, 'notify_include_pending', on_label = 'include pending tags', off_label = 'exclude pending tags' )
self._include_pending_tags.SetToolTipString( 'select whether to include pending tags in the search' )
self._synchronised = OnOffButton( self._dropdown_window, self._page_key, 'notify_search_immediately', on_label = 'searching immediately', off_label = 'waiting' )
self._synchronised.SetToolTipString( 'select whether to renew the search as soon as a new predicate is entered' )
button_hbox_1 = wx.BoxSizer( wx.HORIZONTAL )
button_hbox_1.AddF( self._include_current_tags, FLAGS_EXPAND_BOTH_WAYS )
button_hbox_1.AddF( self._include_pending_tags, FLAGS_EXPAND_BOTH_WAYS )
button_hbox_2 = wx.BoxSizer( wx.HORIZONTAL )
button_hbox_2.AddF( self._file_repo_button, FLAGS_EXPAND_BOTH_WAYS )
button_hbox_2.AddF( self._tag_repo_button, FLAGS_EXPAND_BOTH_WAYS )
vbox = wx.BoxSizer( wx.VERTICAL )
2013-03-15 02:38:12 +00:00
vbox.AddF( button_hbox_1, FLAGS_EXPAND_SIZER_PERPENDICULAR )
2013-02-19 00:11:43 +00:00
vbox.AddF( self._synchronised, FLAGS_EXPAND_PERPENDICULAR )
2013-03-15 02:38:12 +00:00
vbox.AddF( button_hbox_2, FLAGS_EXPAND_SIZER_PERPENDICULAR )
2013-02-19 00:11:43 +00:00
vbox.AddF( self._dropdown_list, FLAGS_EXPAND_BOTH_WAYS )
self._dropdown_window.SetSizer( vbox )
HC.pubsub.sub( self, 'SetSynchronisedWait', 'synchronised_wait_switch' )
HC.pubsub.sub( self, 'IncludeCurrent', 'notify_include_current' )
HC.pubsub.sub( self, 'IncludePending', 'notify_include_pending' )
def _BroadcastChoice( self, predicate ): HC.pubsub.pub( 'add_predicate', self._page_key, predicate )
def _GenerateMatches( self ):
2013-08-14 20:21:49 +00:00
num_first_letters = HC.options[ 'num_autocomplete_chars' ]
2013-02-19 00:11:43 +00:00
raw_entry = self.GetValue()
2013-03-27 20:02:51 +00:00
if raw_entry.startswith( '-' ):
operator = '-'
search_text = raw_entry[1:]
else:
operator = '+'
search_text = raw_entry
2013-02-19 00:11:43 +00:00
search_text = HC.CleanTag( search_text )
if search_text == '':
self._first_letters = ''
self._current_namespace = ''
2013-05-29 20:19:54 +00:00
if self._file_service_identifier == HC.COMBINED_FILE_SERVICE_IDENTIFIER: s_i = self._tag_service_identifier
2013-02-19 00:11:43 +00:00
else: s_i = self._file_service_identifier
2013-07-10 20:25:57 +00:00
matches = HC.app.Read( 'file_system_predicates', s_i )
2013-02-19 00:11:43 +00:00
else:
must_do_a_search = False
if ':' in search_text:
( namespace, half_complete_tag ) = search_text.split( ':' )
if namespace != self._current_namespace:
self._current_namespace = namespace # do a new search, no matter what half_complete tag is
must_do_a_search = True
else:
self._current_namespace = ''
half_complete_tag = search_text
if len( half_complete_tag ) >= num_first_letters:
2013-03-27 20:02:51 +00:00
if must_do_a_search or self._first_letters == '' or not half_complete_tag.startswith( self._first_letters ):
2013-02-19 00:11:43 +00:00
2013-03-27 20:02:51 +00:00
self._first_letters = half_complete_tag
2013-02-19 00:11:43 +00:00
media = self._media_callable()
2013-06-19 20:25:06 +00:00
# if synchro not on, then can't rely on current media as being accurate for current preds, so search db normally
2013-07-10 20:25:57 +00:00
if media is None or not self._synchronised.IsOn(): self._cached_results = HC.app.Read( 'autocomplete_tags', file_service_identifier = self._file_service_identifier, tag_service_identifier = self._tag_service_identifier, half_complete_tag = search_text, include_current = self._include_current, include_pending = self._include_pending )
2013-02-19 00:11:43 +00:00
else:
2013-05-29 20:19:54 +00:00
tags_managers = []
2013-02-19 00:11:43 +00:00
for m in media:
2013-05-29 20:19:54 +00:00
if m.IsCollection(): tags_managers.extend( m.GetSingletonsTagsManagers() )
else: tags_managers.append( m.GetTagsManager() )
2013-02-19 00:11:43 +00:00
2013-05-29 20:19:54 +00:00
lists_of_tags = []
2013-02-19 00:11:43 +00:00
2013-05-29 20:19:54 +00:00
if self._include_current: lists_of_tags += [ list( tags_manager.GetCurrent( self._tag_service_identifier ) ) for tags_manager in tags_managers ]
if self._include_pending: lists_of_tags += [ list( tags_manager.GetPending( self._tag_service_identifier ) ) for tags_manager in tags_managers ]
2013-02-19 00:11:43 +00:00
2013-05-29 20:19:54 +00:00
all_tags_flat_iterable = itertools.chain.from_iterable( lists_of_tags )
2013-02-19 00:11:43 +00:00
2013-05-29 20:19:54 +00:00
all_tags_flat = [ tag for tag in all_tags_flat_iterable if HC.SearchEntryMatchesTag( half_complete_tag, tag ) ]
2013-02-19 00:11:43 +00:00
2013-05-29 20:19:54 +00:00
if self._current_namespace != '': all_tags_flat = [ tag for tag in all_tags_flat if tag.startswith( self._current_namespace + ':' ) ]
tags_to_count = collections.Counter( all_tags_flat )
2013-02-19 00:11:43 +00:00
2013-05-15 18:58:14 +00:00
self._cached_results = CC.AutocompleteMatchesPredicates( self._tag_service_identifier, [ HC.Predicate( HC.PREDICATE_TYPE_TAG, ( operator, tag ), count ) for ( tag, count ) in tags_to_count.items() ] )
2013-02-19 00:11:43 +00:00
matches = self._cached_results.GetMatches( half_complete_tag )
else: matches = []
2013-03-27 20:02:51 +00:00
if self._current_namespace != '': matches.insert( 0, HC.Predicate( HC.PREDICATE_TYPE_NAMESPACE, ( operator, namespace ), None ) )
2013-05-29 20:19:54 +00:00
entry_predicate = HC.Predicate( HC.PREDICATE_TYPE_TAG, ( operator, search_text ), None )
try:
index = matches.index( entry_predicate )
predicate = matches[ index ]
del matches[ index ]
matches.insert( 0, predicate )
except: pass
2013-03-27 20:02:51 +00:00
for match in matches:
if match.GetPredicateType() == HC.PREDICATE_TYPE_TAG: match.SetOperator( operator )
2013-02-19 00:11:43 +00:00
return matches
def IncludeCurrent( self, page_key, value ):
if page_key == self._page_key: self._include_current = value
self._first_letters = ''
self._current_namespace = ''
def IncludePending( self, page_key, value ):
if page_key == self._page_key: self._include_pending = value
self._first_letters = ''
self._current_namespace = ''
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, file_service_identifier, tag_service_identifier ):
self._chosen_tag_callable = chosen_tag_callable
2013-03-15 02:38:12 +00:00
self._page_key = None # this makes the parent's eventmenu pubsubs with page_key simpler!
2013-08-14 20:21:49 +00:00
if HC.options[ 'show_all_tags_in_autocomplete' ]: file_service_identifier = HC.COMBINED_FILE_SERVICE_IDENTIFIER
2013-03-15 02:38:12 +00:00
AutoCompleteDropdownTags.__init__( self, parent, file_service_identifier, tag_service_identifier )
vbox = wx.BoxSizer( wx.VERTICAL )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( self._file_repo_button, FLAGS_EXPAND_BOTH_WAYS )
hbox.AddF( self._tag_repo_button, FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( self._dropdown_list, FLAGS_EXPAND_BOTH_WAYS )
self._dropdown_window.SetSizer( vbox )
2013-03-27 20:02:51 +00:00
def _BroadcastChoice( self, predicate ):
2013-05-15 18:58:14 +00:00
if predicate is None: self._chosen_tag_callable( None )
2013-03-27 20:02:51 +00:00
else:
( operator, tag ) = predicate.GetValue()
2013-07-10 20:25:57 +00:00
parent_manager = HC.app.GetTagParentsManager()
2013-05-15 18:58:14 +00:00
parents = parent_manager.GetParents( self._tag_service_identifier, tag )
self._chosen_tag_callable( tag, parents )
2013-03-27 20:02:51 +00:00
2013-03-15 02:38:12 +00:00
def _GenerateMatches( self ):
2013-08-14 20:21:49 +00:00
num_first_letters = HC.options[ 'num_autocomplete_chars' ]
2013-03-15 02:38:12 +00:00
raw_entry = self.GetValue()
search_text = HC.CleanTag( raw_entry )
if search_text == '':
self._first_letters = ''
self._current_namespace = ''
matches = []
else:
must_do_a_search = False
if ':' in search_text:
( namespace, half_complete_tag ) = search_text.split( ':' )
if namespace != self._current_namespace:
self._current_namespace = namespace # do a new search, no matter what half_complete tag is
must_do_a_search = True
else:
self._current_namespace = ''
half_complete_tag = search_text
if len( half_complete_tag ) >= num_first_letters:
2013-03-27 20:02:51 +00:00
if must_do_a_search or self._first_letters == '' or not half_complete_tag.startswith( self._first_letters ):
2013-03-15 02:38:12 +00:00
2013-03-27 20:02:51 +00:00
self._first_letters = half_complete_tag
2013-03-15 02:38:12 +00:00
2013-07-10 20:25:57 +00:00
self._cached_results = HC.app.Read( 'autocomplete_tags', file_service_identifier = self._file_service_identifier, tag_service_identifier = self._tag_service_identifier, half_complete_tag = search_text, collapse = False )
2013-03-15 02:38:12 +00:00
matches = self._cached_results.GetMatches( half_complete_tag )
else: matches = []
2013-05-15 18:58:14 +00:00
# do the 'put whatever they typed in at the top, whether it has count or not'
2013-05-08 20:31:00 +00:00
# now with sibling support!
2013-05-15 18:58:14 +00:00
# and parent support!
# this is getting pretty ugly, and I should really move it into the matches processing, I think!
2013-03-27 20:02:51 +00:00
2013-05-08 20:31:00 +00:00
top_predicates = []
top_predicates.append( HC.Predicate( HC.PREDICATE_TYPE_TAG, ( '+', search_text ), 0 ) )
2013-07-10 20:25:57 +00:00
siblings_manager = HC.app.GetTagSiblingsManager()
2013-05-08 20:31:00 +00:00
sibling = siblings_manager.GetSibling( search_text )
if sibling is not None: top_predicates.append( HC.Predicate( HC.PREDICATE_TYPE_TAG, ( '+', sibling ), 0 ) )
for predicate in top_predicates:
2013-05-15 18:58:14 +00:00
parents = []
2013-05-08 20:31:00 +00:00
try:
2013-05-15 18:58:14 +00:00
index = matches.index( predicate )
predicate = matches[ index ]
2013-05-08 20:31:00 +00:00
matches.remove( predicate )
2013-05-15 18:58:14 +00:00
while matches[ index ].GetPredicateType() == HC.PREDICATE_TYPE_PARENT:
parent = matches[ index ]
matches.remove( parent )
parents.append( parent )
except:
if predicate.GetPredicateType() == HC.PREDICATE_TYPE_TAG:
tag = predicate.GetTag()
2013-07-10 20:25:57 +00:00
parents_manager = HC.app.GetTagParentsManager()
2013-05-15 18:58:14 +00:00
raw_parents = parents_manager.GetParents( self._tag_service_identifier, tag )
parents = [ HC.Predicate( HC.PREDICATE_TYPE_PARENT, raw_parent, None ) for raw_parent in raw_parents ]
2013-03-15 02:38:12 +00:00
2013-05-15 18:58:14 +00:00
parents.reverse()
for parent in parents: matches.insert( 0, parent )
2013-03-15 02:38:12 +00:00
2013-05-08 20:31:00 +00:00
matches.insert( 0, predicate )
2013-03-15 02:38:12 +00:00
return matches
class BufferedWindow( wx.Window ):
def __init__( self, *args, **kwargs ):
wx.Window.__init__( self, *args, **kwargs )
if 'size' in kwargs:
( x, y ) = kwargs[ 'size' ]
self._canvas_bmp = wx.EmptyBitmap( x, y, 24 )
else: self._canvas_bmp = wx.EmptyBitmap( 0, 0, 24 )
self.Bind( wx.EVT_PAINT, self.EventPaint )
self.Bind( wx.EVT_SIZE, self.EventResize )
def GetDC( self ): return wx.BufferedDC( wx.ClientDC( self ), self._canvas_bmp )
def EventPaint( self, event ): wx.BufferedPaintDC( self, self._canvas_bmp )
def EventResize( self, event ):
( my_width, my_height ) = self.GetClientSize()
( current_bmp_width, current_bmp_height ) = self._canvas_bmp.GetSize()
if my_width != current_bmp_width or my_height != current_bmp_height: self._canvas_bmp = wx.EmptyBitmap( my_width, my_height, 24 )
class BetterChoice( wx.Choice ):
def GetChoice( self ):
selection = self.GetSelection()
if selection != wx.NOT_FOUND: return self.GetClientData( selection )
else: raise Exception( 'choice not chosen' )
class CheckboxCollect( wx.combo.ComboCtrl ):
2013-04-03 20:56:07 +00:00
def __init__( self, parent, page_key = None ):
2013-03-15 02:38:12 +00:00
wx.combo.ComboCtrl.__init__( self, parent, style = wx.CB_READONLY )
self._page_key = page_key
2013-08-14 20:21:49 +00:00
sort_by = HC.options[ 'sort_by' ]
2013-04-03 20:56:07 +00:00
collect_types = set()
2013-03-15 02:38:12 +00:00
2013-04-03 20:56:07 +00:00
for ( sort_by_type, namespaces ) in sort_by: collect_types.update( namespaces )
2013-03-15 02:38:12 +00:00
2013-04-03 20:56:07 +00:00
collect_types = list( [ ( namespace, ( 'namespace', namespace ) ) for namespace in collect_types ] )
collect_types.sort()
2013-07-10 20:25:57 +00:00
ratings_service_identifiers = HC.app.Read( 'service_identifiers', ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ) )
2013-03-15 02:38:12 +00:00
2013-04-03 20:56:07 +00:00
for service_identifier in ratings_service_identifiers: collect_types.append( ( service_identifier.GetName(), ( 'rating', service_identifier ) ) )
2013-03-15 02:38:12 +00:00
2013-04-03 20:56:07 +00:00
popup = self._Popup( collect_types )
2013-03-15 02:38:12 +00:00
#self.UseAltPopupWindow( True )
self.SetPopupControl( popup )
def GetChoice( self ): return self._collect_by
2013-04-03 20:56:07 +00:00
def SetCollectTypes( self, collect_types, collect_type_strings ):
2013-03-15 02:38:12 +00:00
2013-04-03 20:56:07 +00:00
if len( collect_type_strings ) > 0:
2013-03-15 02:38:12 +00:00
2013-04-03 20:56:07 +00:00
self.SetValue( 'collect by ' + '-'.join( collect_type_strings ) )
2013-03-15 02:38:12 +00:00
2013-04-03 20:56:07 +00:00
self._collect_by = collect_types
2013-03-15 02:38:12 +00:00
else:
self.SetValue( 'no collections' )
self._collect_by = None
HC.pubsub.pub( 'collect_media', self._page_key, self._collect_by )
2013-03-23 17:57:29 +00:00
class _Popup( wx.combo.ComboPopup ):
2013-03-15 02:38:12 +00:00
2013-04-03 20:56:07 +00:00
def __init__( self, collect_types ):
2013-03-15 02:38:12 +00:00
2013-03-23 17:57:29 +00:00
wx.combo.ComboPopup.__init__( self )
2013-03-15 02:38:12 +00:00
2013-04-03 20:56:07 +00:00
self._collect_types = collect_types
2013-03-15 02:38:12 +00:00
2013-03-23 17:57:29 +00:00
def Create( self, parent ):
2013-04-03 20:56:07 +00:00
self._control = self._Control( parent, self.GetCombo(), self._collect_types )
2013-03-23 17:57:29 +00:00
return True
2013-03-15 02:38:12 +00:00
2013-03-23 17:57:29 +00:00
def GetAdjustedSize( self, preferred_width, preferred_height, max_height ):
return( ( preferred_width, -1 ) )
2013-03-15 02:38:12 +00:00
2013-03-23 17:57:29 +00:00
def GetControl( self ): return self._control
2013-03-15 02:38:12 +00:00
2013-03-23 17:57:29 +00:00
class _Control( wx.CheckListBox ):
2013-04-03 20:56:07 +00:00
def __init__( self, parent, special_parent, collect_types ):
texts = [ text for ( text, data ) in collect_types ] # we do this so it sizes its height properly on init
wx.CheckListBox.__init__( self, parent, choices = texts )
2013-03-23 17:57:29 +00:00
2013-04-03 20:56:07 +00:00
self.Clear()
for ( text, data ) in collect_types: self.Append( text, data )
2013-03-23 17:57:29 +00:00
self._special_parent = special_parent
2013-08-14 20:21:49 +00:00
default = HC.options[ 'default_collect' ]
2013-03-23 17:57:29 +00:00
2013-09-04 16:48:44 +00:00
if default is not None:
strings_we_added = { text for ( text, data ) in collect_types }
strings_to_check = [ s for ( namespace_gumpf, s ) in default if s in strings_we_added ]
self.SetCheckedStrings( strings_to_check )
2013-03-23 17:57:29 +00:00
self.Bind( wx.EVT_CHECKLISTBOX, self.EventChanged )
self.Bind( wx.EVT_LEFT_DOWN, self.EventLeftDown )
self.EventChanged( None )
# as inspired by http://trac.wxwidgets.org/attachment/ticket/14413/test_clb_workaround.py
# what a clusterfuck
def EventLeftDown( self, event ):
index = self.HitTest( event.GetPosition() )
if index != wx.NOT_FOUND:
self.Check( index, not self.IsChecked( index ) )
self.EventChanged( event )
event.Skip()
def EventChanged( self, event ):
2013-04-03 20:56:07 +00:00
collect_types = []
for i in self.GetChecked(): collect_types.append( self.GetClientData( i ) )
collect_type_strings = self.GetCheckedStrings()
2013-03-23 17:57:29 +00:00
2013-04-03 20:56:07 +00:00
self._special_parent.SetCollectTypes( collect_types, collect_type_strings )
2013-03-23 17:57:29 +00:00
2013-03-15 02:38:12 +00:00
class ChoiceSort( BetterChoice ):
def __init__( self, parent, page_key = None, sort_by = None ):
BetterChoice.__init__( self, parent )
self._page_key = page_key
2013-08-14 20:21:49 +00:00
if sort_by is None: sort_by = HC.options[ 'sort_by' ]
2013-03-15 02:38:12 +00:00
sort_choices = CC.SORT_CHOICES + sort_by
2013-07-10 20:25:57 +00:00
ratings_service_identifiers = HC.app.Read( 'service_identifiers', ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ) )
2013-03-15 02:38:12 +00:00
for ratings_service_identifier in ratings_service_identifiers:
sort_choices.append( ( 'rating_descend', ratings_service_identifier ) )
sort_choices.append( ( 'rating_ascend', ratings_service_identifier ) )
for ( sort_by_type, sort_by_data ) in sort_choices:
if sort_by_type == 'system': string = CC.sort_string_lookup[ sort_by_data ]
elif sort_by_type == 'namespaces': string = '-'.join( sort_by_data )
elif sort_by_type == 'rating_descend': string = sort_by_data.GetName() + ' rating highest first'
elif sort_by_type == 'rating_ascend': string = sort_by_data.GetName() + ' rating lowest first'
self.Append( 'sort by ' + string, ( sort_by_type, sort_by_data ) )
2013-08-14 20:21:49 +00:00
try: self.SetSelection( HC.options[ 'default_sort' ] )
2013-03-15 02:38:12 +00:00
except: pass
self.Bind( wx.EVT_CHOICE, self.EventChoice )
HC.pubsub.sub( self, 'ACollectHappened', 'collect_media' )
def _BroadcastSort( self ):
selection = self.GetSelection()
if selection != wx.NOT_FOUND:
sort_by = self.GetClientData( selection )
HC.pubsub.pub( 'sort_media', self._page_key, sort_by )
def ACollectHappened( self, page_key, collect_by ):
if page_key == self._page_key: self._BroadcastSort()
def EventChoice( self, event ):
if self._page_key is not None: self._BroadcastSort()
class FileDropTarget( wx.FileDropTarget ):
def __init__( self, callable ):
wx.FileDropTarget.__init__( self )
self._callable = callable
def OnDropFiles( self, x, y, paths ): wx.CallAfter( self._callable, paths )
class Frame( wx.Frame ):
def __init__( self, *args, **kwargs ):
wx.Frame.__init__( self, *args, **kwargs )
#self.SetDoubleBuffered( True )
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
2013-09-04 16:48:44 +00:00
self.SetIcon( wx.Icon( HC.STATIC_DIR + os.path.sep + 'hydrus.ico', wx.BITMAP_TYPE_ICO ) )
class FrameThatResizes( Frame ):
def __init__( self, *args, **kwargs ):
self._resize_option_prefix = kwargs[ 'resize_option_prefix' ]
del kwargs[ 'resize_option_prefix' ]
Frame.__init__( self, *args, **kwargs )
client_size = HC.options[ 'client_size' ]
self.SetInitialSize( client_size[ self._resize_option_prefix + 'restored_size' ] )
self.SetMinSize( ( 480, 360 ) )
self._TryToSetPosition()
if client_size[ self._resize_option_prefix + 'maximised' ]: self.Maximize()
self.Bind( wx.EVT_SIZE, self.EventSpecialResize )
self.Bind( wx.EVT_MOVE_END, self.EventSpecialMoveEnd )
def _TryToSetPosition( self ):
client_size = HC.options[ 'client_size' ]
position = client_size[ self._resize_option_prefix + 'restored_position' ]
display_index = wx.Display.GetFromPoint( position )
if display_index == wx.NOT_FOUND: client_size[ self._resize_option_prefix + 'restored_position' ] = [ 20, 20 ]
else:
display = wx.Display( display_index )
geometry = display.GetGeometry()
( p_x, p_y ) = position
x_bad = p_x < geometry.x or p_x > geometry.x + geometry.width
y_bad = p_y < geometry.y or p_y > geometry.y + geometry.height
if x_bad or y_bad: client_size[ self._resize_option_prefix + 'restored_position' ] = [ 20, 20 ]
self.SetPosition( client_size[ self._resize_option_prefix + 'restored_position' ] )
def EventSpecialMoveEnd( self, event ):
client_size = HC.options[ 'client_size' ]
client_size[ self._resize_option_prefix + 'restored_position' ] = list( self.GetPosition() )
event.Skip()
def EventSpecialResize( self, event ):
client_size = HC.options[ 'client_size' ]
if self.IsMaximized() or self.IsFullScreen():
client_size[ self._resize_option_prefix + 'maximised' ] = True
else:
if client_size[ self._resize_option_prefix + 'maximised' ]: # we have just restored, so set size
self.SetSize( client_size[ self._resize_option_prefix + 'restored_size' ] )
self._TryToSetPosition()
else: # we have resized manually, so set new size
client_size[ self._resize_option_prefix + 'restored_size' ] = list( self.GetSize() )
client_size[ self._resize_option_prefix + 'restored_position' ] = list( self.GetPosition() )
client_size[ self._resize_option_prefix + 'maximised' ] = False
event.Skip()
2013-03-15 02:38:12 +00:00
class Gauge( wx.Gauge ):
def __init__( self, *args, **kwargs ):
wx.Gauge.__init__( self, *args, **kwargs )
self._actual_max = None
def SetRange( self, max ):
if max > 1000:
self._actual_max = max
wx.Gauge.SetRange( self, 1000 )
else:
self._actual_max = None
wx.Gauge.SetRange( self, max )
def SetValue( self, value ):
if self._actual_max is None: wx.Gauge.SetValue( self, value )
else: wx.Gauge.SetValue( self, min( int( 1000 * ( float( value ) / self._actual_max ) ), 1000 ) )
class ListBook( wx.Panel ):
def __init__( self, *args, **kwargs ):
wx.Panel.__init__( self, *args, **kwargs )
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
self._list_box = wx.ListBox( self, style = wx.LB_SINGLE | wx.LB_SORT )
self._empty_panel = wx.Panel( self )
self._empty_panel.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
self._current_name = None
self._current_panel = self._empty_panel
self._panel_sizer = wx.BoxSizer( wx.VERTICAL )
self._panel_sizer.AddF( self._empty_panel, FLAGS_EXPAND_BOTH_WAYS )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( self._list_box, FLAGS_EXPAND_PERPENDICULAR )
hbox.AddF( self._panel_sizer, FLAGS_EXPAND_SIZER_BOTH_WAYS )
self._list_box.Bind( wx.EVT_LISTBOX, self.EventSelection )
self.SetSizer( hbox )
self.Bind( wx.EVT_MENU, self.EventMenu )
def _RecalcListBoxWidth( self ): self.Layout()
def _Select( self, selection ):
2013-03-23 17:57:29 +00:00
if selection == wx.NOT_FOUND: self._current_name = None
else: self._current_name = self._list_box.GetString( selection )
self._current_panel.Hide()
self._list_box.SetSelection( selection )
if selection == wx.NOT_FOUND: self._current_panel = self._empty_panel
else:
2013-03-15 02:38:12 +00:00
2013-03-23 17:57:29 +00:00
panel_info = self._list_box.GetClientData( selection )
2013-03-15 02:38:12 +00:00
2013-03-23 17:57:29 +00:00
if type( panel_info ) == tuple:
2013-03-15 02:38:12 +00:00
2013-03-23 17:57:29 +00:00
( classname, args, kwargs ) = panel_info
2013-03-15 02:38:12 +00:00
2013-03-23 17:57:29 +00:00
page = classname( *args, **kwargs )
page.Hide()
self._panel_sizer.AddF( page, FLAGS_EXPAND_SIZER_BOTH_WAYS )
self._list_box.SetClientData( selection, page )
2013-03-15 02:38:12 +00:00
2013-03-23 17:57:29 +00:00
self._RecalcListBoxWidth()
2013-03-15 02:38:12 +00:00
2013-03-23 17:57:29 +00:00
self._current_panel = self._list_box.GetClientData( selection )
2013-03-15 02:38:12 +00:00
self._current_panel.Show()
self.Layout()
2013-09-11 21:28:19 +00:00
self.Refresh()
2013-03-15 02:38:12 +00:00
event = wx.NotifyEvent( wx.wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, -1 )
self.ProcessEvent( event )
def AddPage( self, page, name, select = False ):
if type( page ) != tuple:
page.Hide()
self._panel_sizer.AddF( page, FLAGS_EXPAND_SIZER_BOTH_WAYS )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._list_box.Append( name, page )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._RecalcListBoxWidth()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if self._list_box.GetCount() == 1: self._Select( 0 )
elif select: self._Select( self._list_box.FindString( name ) )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def DeleteAllPages( self ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._panel_sizer.Detach( self._empty_panel )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._panel_sizer.Clear( deleteWindows = True )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._panel_sizer.AddF( self._empty_panel, FLAGS_EXPAND_SIZER_BOTH_WAYS )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._current_name = None
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._current_panel = self._empty_panel
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._list_box.Clear()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def EventMenu( self, event ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
action = CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if action is not None:
2013-02-19 00:11:43 +00:00
2013-08-14 20:21:49 +00:00
( command, data ) = action
if command == 'select_down': self.SelectDown()
elif command == 'select_up': self.SelectUp()
else: event.Skip()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def EventSelection( self, event ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if self._list_box.GetSelection() != self._list_box.FindString( self._current_name ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
event = wx.NotifyEvent( wx.wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, -1 )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.GetEventHandler().ProcessEvent( event )
if event.IsAllowed(): self._Select( self._list_box.GetSelection() )
else: self._list_box.SetSelection( self._list_box.FindString( self._current_name ) )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def GetCurrentName( self ): return self._current_name
def GetCurrentPage( self ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if self._current_panel == self._empty_panel: return None
else: return self._current_panel
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def GetNameToPageDict( self ): return { self._list_box.GetString( i ) : self._list_box.GetClientData( i ) for i in range( self._list_box.GetCount() ) if type( self._list_box.GetClientData( i ) ) != tuple }
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def NameExists( self, name, panel = None ): return self._list_box.FindString( name ) != wx.NOT_FOUND
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def DeleteCurrentPage( self ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
selection = self._list_box.GetSelection()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if selection != wx.NOT_FOUND:
next_selection = selection + 1
previous_selection = selection - 1
if next_selection < self._list_box.GetCount(): self._Select( next_selection )
elif previous_selection >= 0: self._Select( previous_selection )
else: self._Select( wx.NOT_FOUND )
panel_info = self._list_box.GetClientData( selection )
if type( panel_info ) != tuple: self._panel_sizer.Remove( panel_info )
self._list_box.Delete( selection )
self._RecalcListBoxWidth()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def RenamePage( self, name, new_name ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if self._list_box.FindString( new_name ) != wx.NOT_FOUND: raise Exception( 'That name is already in use!' )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if self._current_name == name: self._current_name = new_name
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._list_box.SetString( self._list_box.FindString( name ), new_name )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._RecalcListBoxWidth()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def Select( self, name ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
selection = self._list_box.FindString( name )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if selection != wx.NOT_FOUND and selection != self._list_box.GetSelection():
event = wx.NotifyEvent( wx.wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, -1 )
self.GetEventHandler().ProcessEvent( event )
if event.IsAllowed(): self._Select( selection )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def SelectDown( self ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
current_selection = self._list_box.FindString( self._current_name )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if current_selection != wx.NOT_FOUND:
num_entries = self._list_box.GetCount()
if current_selection == num_entries - 1: selection = 0
else: selection = current_selection + 1
if selection != current_selection: self._Select( selection )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def SelectPage( self, page ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
for i in range( self._list_box.GetCount() ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if self._list_box.GetClientData( i ) == page:
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._Select( i )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
return
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def SelectUp( self ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
current_selection = self._list_box.FindString( self._current_name )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if current_selection != wx.NOT_FOUND:
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
num_entries = self._list_box.GetCount()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if current_selection == 0: selection = num_entries - 1
else: selection = current_selection - 1
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if selection != current_selection: self._Select( selection )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
class ListCtrlAutoWidth( wx.ListCtrl, ListCtrlAutoWidthMixin ):
def __init__( self, parent, height ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
wx.ListCtrl.__init__( self, parent, size=( -1, height ), style=wx.LC_REPORT )
ListCtrlAutoWidthMixin.__init__( self )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def GetAllSelected( self ):
indices = []
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
i = self.GetFirstSelected()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
while i != -1:
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
indices.append( i )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
i = self.GetNextSelected( i )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
return indices
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def RemoveAllSelected( self ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
indices = self.GetAllSelected()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
indices.reverse() # so we don't screw with the indices of deletees below
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
for index in indices: self.DeleteItem( index )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
class ListBox( wx.ScrolledWindow ):
def __init__( self, parent, min_height = 250 ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
wx.ScrolledWindow.__init__( self, parent, style = wx.VSCROLL | wx.BORDER_DOUBLE )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._ordered_strings = []
self._strings_to_terms = {}
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._canvas_bmp = wx.EmptyBitmap( 0, 0, 24 )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._current_selected_index = None
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
dc = self._GetScrolledDC()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
dc.SetFont( wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT ) )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
( text_x, self._text_y ) = dc.GetTextExtent( 'abcdefghijklmnopqrstuvwxyz' )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._num_rows_per_page = 0
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.SetScrollRate( 0, self._text_y )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.SetMinSize( ( 50, min_height ) )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.Bind( wx.EVT_PAINT, self.EventPaint )
self.Bind( wx.EVT_SIZE, self.EventResize )
self.Bind( wx.EVT_LEFT_DOWN, self.EventMouseSelect )
self.Bind( wx.EVT_LEFT_DCLICK, self.EventDClick )
2013-03-23 17:57:29 +00:00
self.Bind( wx.EVT_RIGHT_DOWN, self.EventMouseRightClick )
2013-03-15 02:38:12 +00:00
self.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
2013-03-23 17:57:29 +00:00
self.Bind( wx.EVT_MENU, self.EventMenu )
2013-03-15 02:38:12 +00:00
def __len__( self ): return len( self._ordered_strings )
2013-05-08 20:31:00 +00:00
def _Activate( self, s, term ): pass
2013-03-15 02:38:12 +00:00
def _DrawTexts( self ):
( my_width, my_height ) = self.GetClientSize()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
dc = self._GetScrolledDC()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
dc.SetFont( wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT ) )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
i = 0
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
dc.SetBackground( wx.Brush( wx.Colour( 255, 255, 255 ) ) )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
dc.Clear()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
for ( i, text ) in enumerate( self._ordered_strings ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
( r, g, b ) = self._GetTextColour( text )
text_colour = wx.Colour( r, g, b )
if self._current_selected_index is not None and i == self._current_selected_index:
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
dc.SetBrush( wx.Brush( text_colour ) )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
dc.SetPen( wx.TRANSPARENT_PEN )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
dc.DrawRectangle( 0, i * self._text_y, my_width, self._text_y )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
text_colour = wx.WHITE
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
dc.SetTextForeground( text_colour )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
( x, y ) = ( 3, i * self._text_y )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
dc.DrawText( text, x, y )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def _GetIndexUnderMouse( self, mouse_event ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
( xUnit, yUnit ) = self.GetScrollPixelsPerUnit()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
( x_scroll, y_scroll ) = self.GetViewStart()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
y_offset = y_scroll * yUnit
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
y = mouse_event.GetY() + y_offset
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
row_index = ( y / self._text_y )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if row_index >= len( self._ordered_strings ): return None
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
return row_index
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def _GetScrolledDC( self ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
cdc = wx.ClientDC( self )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.DoPrepareDC( cdc ) # because this is a scrolled window
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
return wx.BufferedDC( cdc, self._canvas_bmp )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def _GetTextColour( self, text ): return ( 0, 111, 250 )
def _Select( self, index ):
if index is not None:
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if index == -1 or index > len( self._ordered_strings ): index = len( self._ordered_strings ) - 1
elif index == len( self._ordered_strings ) or index < -1: index = 0
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._current_selected_index = index
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._DrawTexts()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if self._current_selected_index is not None:
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
# scroll to index, if needed
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
y = self._text_y * self._current_selected_index
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
( start_x, start_y ) = self.GetViewStart()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
( x_unit, y_unit ) = self.GetScrollPixelsPerUnit()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
( width, height ) = self.GetClientSize()
if y < start_y * y_unit:
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
y_to_scroll_to = y / y_unit
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.Scroll( -1, y_to_scroll_to )
wx.PostEvent( self, wx.ScrollWinEvent( wx.wxEVT_SCROLLWIN_THUMBRELEASE ) )
elif y > ( start_y * y_unit ) + height:
y_to_scroll_to = ( y - height ) / y_unit
self.Scroll( -1, y_to_scroll_to + 3 )
wx.PostEvent( self, wx.ScrollWinEvent( wx.wxEVT_SCROLLWIN_THUMBRELEASE ) )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def _TextsHaveChanged( self ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._current_selected_index = None
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
total_height = self._text_y * len( self._ordered_strings )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
( my_x, my_y ) = self._canvas_bmp.GetSize()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if my_y != total_height: wx.PostEvent( self, wx.SizeEvent() )
else: self._DrawTexts()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def EventDClick( self, event ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
index = self._GetIndexUnderMouse( event )
2013-02-19 00:11:43 +00:00
2013-05-08 20:31:00 +00:00
if index is not None and index == self._current_selected_index:
s = self._ordered_strings[ self._current_selected_index ]
term = self._strings_to_terms[ s ]
self._Activate( s, term )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def EventKeyDown( self, event ):
key_code = event.GetKeyCode()
if self._current_selected_index is not None:
2013-02-19 00:11:43 +00:00
2013-05-08 20:31:00 +00:00
if key_code in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ):
s = self._ordered_strings[ self._current_selected_index ]
term = self._strings_to_terms[ s ]
self._Activate( s, term )
2013-03-15 02:38:12 +00:00
elif key_code in ( wx.WXK_UP, wx.WXK_NUMPAD_UP ): self._Select( self._current_selected_index - 1 )
elif key_code in ( wx.WXK_DOWN, wx.WXK_NUMPAD_DOWN ): self._Select( self._current_selected_index + 1 )
elif key_code == wx.WXK_PAGEUP: self._Select( self._current_selected_index - self._num_rows_per_page )
elif key_code == wx.WXK_PAGEDOWN: self._Select( self._current_selected_index + self._num_rows_per_page )
else: event.Skip()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
else: event.Skip()
2013-02-19 00:11:43 +00:00
2013-03-23 17:57:29 +00:00
def EventMenu( self, event ):
action = CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
if action is not None:
2013-08-14 20:21:49 +00:00
( command, data ) = action
if command == 'copy': HC.pubsub.pub( 'clipboard', 'text', data )
else:
2013-03-23 17:57:29 +00:00
2013-08-14 20:21:49 +00:00
event.Skip()
2013-03-23 17:57:29 +00:00
2013-08-14 20:21:49 +00:00
return # this is about select_up and select_down
2013-03-23 17:57:29 +00:00
def EventMouseRightClick( self, event ):
index = self._GetIndexUnderMouse( event )
self._Select( index )
if self._current_selected_index is not None:
menu = wx.Menu()
term = self._strings_to_terms[ self._ordered_strings[ self._current_selected_index ] ]
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy', term ), 'copy ' + term )
if ':' in term:
sub_term = term.split( ':', 1 )[1]
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy', sub_term ), 'copy ' + sub_term )
self.PopupMenu( menu )
menu.Destroy()
event.Skip()
2013-03-15 02:38:12 +00:00
def EventMouseSelect( self, event ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
index = self._GetIndexUnderMouse( event )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._Select( index )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
event.Skip()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def EventPaint( self, event ): wx.BufferedPaintDC( self, self._canvas_bmp, wx.BUFFER_VIRTUAL_AREA )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def EventResize( self, event ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
( client_x, client_y ) = self.GetClientSize()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
( my_x, my_y ) = self._canvas_bmp.GetSize()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._num_rows_per_page = client_y / self._text_y
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
total_height = self._text_y * len( self._ordered_strings )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if my_x != client_x or my_y != total_height:
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
new_y = max( client_y, total_height )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.SetVirtualSize( ( client_x, new_y ) )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._canvas_bmp = wx.EmptyBitmap( client_x, new_y, 24 )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._DrawTexts()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def SetTexts( self, ordered_strings ):
if ordered_strings != self._ordered_strings:
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._ordered_strings = ordered_strings
self._strings_to_terms = { s : s for s in ordered_strings }
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._TextsHaveChanged()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if len( ordered_strings ) > 0: self._Select( 0 )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
class ListBoxMessages( ListBox ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def _GetTextColour( self, predicate_string ):
if predicate_string.startswith( 'system:' ): ( r, g, b ) = ( 153, 101, 21 )
else: ( r, g, b ) = ( 0, 111, 250 )
return ( r, g, b )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
class ListBoxMessagesActiveOnly( ListBoxMessages ):
def __init__( self, parent, callable ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
ListBoxMessages.__init__( self, parent )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._callable = callable
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._matches = {}
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
2013-05-08 20:31:00 +00:00
def _Activate( self, s, term ): self._callable( term )
2013-03-15 02:38:12 +00:00
def SetTerms( self, matches ):
if matches != self._matches:
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._matches = matches
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._ordered_strings = []
self._strings_to_terms = {}
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
for ( term, count ) in matches:
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if count is None: term_string = term
else: term_string = term + ' (' + HC.ConvertIntToPrettyString( count ) + ')'
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._ordered_strings.append( term_string )
self._strings_to_terms[ term_string ] = term
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._TextsHaveChanged()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if len( matches ) > 0: self._Select( 0 )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
class ListBoxMessagesPredicates( ListBoxMessages ):
def __init__( self, parent, page_key, initial_predicates = [] ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
ListBoxMessages.__init__( self, parent )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._page_key = page_key
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if len( initial_predicates ) > 0:
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
for predicate in initial_predicates:
self._ordered_strings.append( predicate )
self._strings_to_terms[ predicate ] = predicate
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._TextsHaveChanged()
2013-02-19 00:11:43 +00:00
2013-05-08 20:31:00 +00:00
def _Activate( self, s, term ): HC.pubsub.pub( 'remove_predicate', self._page_key, term )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def ActivatePredicate( self, term ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if term in self._ordered_strings:
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._ordered_strings.remove( term )
del self._strings_to_terms[ term ]
2013-02-19 00:11:43 +00:00
else:
2013-03-15 02:38:12 +00:00
if term == 'system:inbox' and 'system:archive' in self._ordered_strings: self._ordered_strings.remove( 'system:archive' )
elif term == 'system:archive' and 'system:inbox' in self._ordered_strings: self._ordered_strings.remove( 'system:inbox' )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._ordered_strings.append( term )
self._strings_to_terms[ term ] = term
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._ordered_strings.sort()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._TextsHaveChanged()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def AddPredicate( self, predicate ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._ordered_strings.append( predicate )
self._strings_to_terms[ predicate ] = predicate
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._ordered_strings.sort()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._TextsHaveChanged()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def GetPredicates( self ): return self._ordered_strings
def HasPredicate( self, predicate ): return predicate in self._ordered_strings
def RemovePredicate( self, predicate ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._ordered_strings.remove( predicate )
del self._strings_to_terms[ predicate ]
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._TextsHaveChanged()
2013-02-19 00:11:43 +00:00
2013-03-23 17:57:29 +00:00
class NoneableSpinCtrl( wx.Panel ):
2013-07-31 21:26:38 +00:00
def __init__( self, parent, message, none_phrase = 'no limit', max = 1000000, multiplier = 1, num_dimensions = 1 ):
2013-03-23 17:57:29 +00:00
wx.Panel.__init__( self, parent )
self._num_dimensions = num_dimensions
self._multiplier = multiplier
self._checkbox = wx.CheckBox( self, label = none_phrase )
self._checkbox.Bind( wx.EVT_CHECKBOX, self.EventCheckBox )
2013-07-31 21:26:38 +00:00
self._one = wx.SpinCtrl( self, max = max, size = ( 80, -1 ) )
if num_dimensions == 2: self._two = wx.SpinCtrl( self, initial = 0, max = max, size = ( 80, -1 ) )
2013-03-23 17:57:29 +00:00
hbox = wx.BoxSizer( wx.HORIZONTAL )
2013-07-31 21:26:38 +00:00
2013-03-23 17:57:29 +00:00
hbox.AddF( wx.StaticText( self, label=message + ': ' ), FLAGS_MIXED )
hbox.AddF( self._one, FLAGS_MIXED )
if self._num_dimensions == 2:
hbox.AddF( wx.StaticText( self, label = 'x' ), FLAGS_MIXED )
hbox.AddF( self._two, FLAGS_MIXED )
hbox.AddF( self._checkbox, FLAGS_MIXED )
self.SetSizer( hbox )
def EventCheckBox( self, event ):
if self._checkbox.GetValue():
self._one.Disable()
if self._num_dimensions == 2: self._two.Disable()
else:
self._one.Enable()
if self._num_dimensions == 2: self._two.Enable()
def GetValue( self ):
if self._checkbox.GetValue(): return None
else:
if self._num_dimensions == 2: return ( self._one.GetValue() * self._multiplier, self._two.GetValue() * self._multiplier )
else: return self._one.GetValue() * self._multiplier
def SetValue( self, value ):
if value is None:
self._checkbox.SetValue( True )
self._one.Disable()
if self._num_dimensions == 2: self._two.Disable()
else:
self._checkbox.SetValue( False )
if self._num_dimensions == 2:
2013-07-31 21:26:38 +00:00
self._two.Enable()
2013-03-23 17:57:29 +00:00
( value, y ) = value
self._two.SetValue( y / self._multiplier )
2013-07-31 21:26:38 +00:00
self._one.Enable()
2013-03-23 17:57:29 +00:00
self._one.SetValue( value / self._multiplier )
class OnOffButton( wx.Button ):
def __init__( self, parent, page_key, topic, on_label, off_label = None, start_on = True ):
if start_on: label = on_label
else: label = off_label
wx.Button.__init__( self, parent, label = label )
self._page_key = page_key
self._topic = topic
self._on_label = on_label
if off_label is None: self._off_label = on_label
else: self._off_label = off_label
self._on = start_on
if self._on: self.SetForegroundColour( ( 0, 128, 0 ) )
else: self.SetForegroundColour( ( 128, 0, 0 ) )
self.Bind( wx.EVT_BUTTON, self.EventButton )
HC.pubsub.sub( self, 'HitButton', 'hit_on_off_button' )
def EventButton( self, event ):
if self._on:
self._on = False
self.SetLabel( self._off_label )
self.SetForegroundColour( ( 128, 0, 0 ) )
HC.pubsub.pub( self._topic, self._page_key, False )
else:
self._on = True
self.SetLabel( self._on_label )
self.SetForegroundColour( ( 0, 128, 0 ) )
HC.pubsub.pub( self._topic, self._page_key, True )
def IsOn( self ): return self._on
2013-07-24 20:26:00 +00:00
class PopupMessage( wx.Window ):
def __init__( self, parent ):
wx.Window.__init__( self, parent, style = wx.BORDER_SIMPLE )
2013-09-04 16:48:44 +00:00
self.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
2013-07-24 20:26:00 +00:00
def EventDismiss( self, event ):
self.GetParent().Dismiss( self )
2013-08-14 20:21:49 +00:00
class PopupMessageDismissAll( PopupMessage ):
def __init__( self, parent ):
PopupMessage.__init__( self, parent )
hbox = wx.BoxSizer( wx.HORIZONTAL )
self._text = wx.StaticText( self )
self._text.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
button = wx.Button( self, label = 'dismiss all' )
button.Bind( wx.EVT_BUTTON, self.EventButton )
button.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
hbox.AddF( self._text, FLAGS_MIXED )
hbox.AddF( button, FLAGS_MIXED )
self.SetSizer( hbox )
def EventButton( self, event ): self.GetParent().DismissAll()
def SetNumMessages( self, num_messages_pending ): self._text.SetLabel( HC.ConvertIntToPrettyString( num_messages_pending ) + ' more messages' )
2013-07-24 20:26:00 +00:00
class PopupMessageError( PopupMessage ):
2013-08-14 20:21:49 +00:00
def __init__( self, parent, etype, value, trace ):
2013-07-24 20:26:00 +00:00
PopupMessage.__init__( self, parent )
2013-08-14 20:21:49 +00:00
self._copy_text = HC.u( etype.__name__ ) + ': ' + HC.u( value ) + os.linesep + trace
2013-07-24 20:26:00 +00:00
vbox = wx.BoxSizer( wx.VERTICAL )
2013-08-14 20:21:49 +00:00
error = wx.StaticText( self, label = HC.u( etype.__name__ ), style = wx.ALIGN_CENTER )
2013-07-24 20:26:00 +00:00
error.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
2013-08-14 20:21:49 +00:00
text = wx.StaticText( self, label = HC.u( value ) )
2013-07-31 21:26:38 +00:00
text.Wrap( 380 )
2013-07-24 20:26:00 +00:00
text.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
2013-08-14 20:21:49 +00:00
self._show_tb_button = wx.Button( self, label = 'show traceback' )
self._show_tb_button.Bind( wx.EVT_BUTTON, self.EventShowButton )
self._show_tb_button.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
self._tb_text = wx.StaticText( self, label = trace )
self._tb_text.Wrap( 380 )
self._tb_text.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
self._tb_text.Hide()
self._copy_tb_button = wx.Button( self, label = 'copy info' )
self._copy_tb_button.Bind( wx.EVT_BUTTON, self.EventCopyButton )
self._copy_tb_button.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
self._copy_tb_button.Hide()
2013-07-24 20:26:00 +00:00
vbox.AddF( error, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( text, FLAGS_EXPAND_PERPENDICULAR )
2013-08-14 20:21:49 +00:00
vbox.AddF( self._show_tb_button, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._tb_text, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._copy_tb_button, FLAGS_EXPAND_PERPENDICULAR )
2013-07-24 20:26:00 +00:00
self.SetSizer( vbox )
2013-08-14 20:21:49 +00:00
def EventCopyButton( self, event ): HC.pubsub.pub( 'clipboard', 'text', self._copy_text )
def EventShowButton( self, event ):
if self._tb_text.IsShown():
self._show_tb_button.SetLabel( 'show traceback' )
self._tb_text.Hide()
self._copy_tb_button.Hide()
else:
self._show_tb_button.SetLabel( 'hide traceback' )
self._tb_text.Show()
self._copy_tb_button.Show()
2013-09-11 21:28:19 +00:00
self.GetParent().MakeSureEverythingFits()
2013-08-14 20:21:49 +00:00
2013-07-24 20:26:00 +00:00
class PopupMessageFiles( PopupMessage ):
def __init__( self, parent, message_string, hashes ):
PopupMessage.__init__( self, parent )
self._hashes = hashes
vbox = wx.BoxSizer( wx.VERTICAL )
2013-08-07 22:25:18 +00:00
button = wx.Button( self, label = message_string )
2013-07-24 20:26:00 +00:00
button.Bind( wx.EVT_BUTTON, self.EventButton )
2013-08-07 22:25:18 +00:00
button.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
2013-07-24 20:26:00 +00:00
vbox.AddF( button, FLAGS_EXPAND_PERPENDICULAR )
self.SetSizer( vbox )
def EventButton( self, event ):
2013-08-28 21:31:52 +00:00
media_results = HC.app.Read( 'media_results', HC.LOCAL_FILE_SERVICE_IDENTIFIER, self._hashes )
2013-07-24 20:26:00 +00:00
HC.pubsub.pub( 'new_page_query', HC.LOCAL_FILE_SERVICE_IDENTIFIER, initial_media_results = media_results )
2013-09-11 21:28:19 +00:00
class PopupMessageGauge( PopupMessage ):
def __init__( self, parent, job_key, message_string ):
PopupMessage.__init__( self, parent )
self._message_string = message_string
self._job_key = job_key
self._hashes = set()
self._done = False
vbox = wx.BoxSizer( wx.VERTICAL )
self._text = wx.StaticText( self, label = self._message_string , style = wx.ALIGN_CENTER )
self._text.Wrap( 380 )
self._text.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
hbox = wx.BoxSizer( wx.HORIZONTAL )
self._gauge = Gauge( self, size = ( 380, -1 ) )
self._gauge.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
self._cancel_button = wx.Button( self, label = 'cancel' )
self._cancel_button.Bind( wx.EVT_BUTTON, self.EventCancelButton )
self._cancel_button.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
hbox.AddF( self._gauge, FLAGS_EXPAND_BOTH_WAYS )
hbox.AddF( self._cancel_button, FLAGS_MIXED )
self._show_file_button = wx.Button( self, label = self._message_string )
self._show_file_button.Bind( wx.EVT_BUTTON, self.EventShowFileButton )
self._show_file_button.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
self._show_file_button.Hide()
vbox.AddF( self._text, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( hbox, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._show_file_button, FLAGS_EXPAND_PERPENDICULAR )
self.SetSizer( vbox )
HC.pubsub.sub( self, 'Failed', 'message_gauge_failed' )
HC.pubsub.sub( self, 'SetInfo', 'message_gauge_info' )
HC.pubsub.sub( self, 'Importing', 'message_gauge_importing' )
HC.pubsub.sub( self, 'Done', 'message_gauge_done' )
def Done( self, job_key, hashes ):
if job_key == self._job_key:
self._done = True
self._hashes = hashes
self._text.Hide()
self._gauge.Hide()
self._show_file_button.Show()
self.GetParent().MakeSureEverythingFits()
def EventCancelButton( self, event ):
self._job_key.Cancel()
self.GetParent().Dismiss( self )
def EventDismiss( self, event ):
if not self._done:
import ClientGUIDialogs
with ClientGUIDialogs.DialogYesNo( self, 'Do you want to continue the download in the background, or cancel it?', yes_label = 'continue', no_label = 'cancel' ) as dlg:
if dlg.ShowModal() != wx.ID_YES: self._job_key.Cancel()
self.GetParent().Dismiss( self )
def EventShowFileButton( self, event ):
media_results = HC.app.Read( 'media_results', HC.LOCAL_FILE_SERVICE_IDENTIFIER, self._hashes )
HC.pubsub.pub( 'new_page_query', HC.LOCAL_FILE_SERVICE_IDENTIFIER, initial_media_results = media_results )
def Failed( self, job_key ):
if job_key == self._job_key: self.GetParent().Dismiss( self )
def Importing( self, job_key ):
if job_key == self._job_key:
self._text.SetLabel( 'importing ' + self._message_string )
self._text.Show()
self._gauge.Hide()
self._cancel_button.Hide()
self._show_file_button.Hide()
self.GetParent().MakeSureEverythingFits()
def SetInfo( self, job_key, range, value ):
if job_key == self._job_key:
if range is None:
self._gauge.Pulse()
byte_info = HC.ConvertIntToBytes( value )
else:
self._gauge.SetRange( range )
self._gauge.SetValue( value )
byte_info = HC.ConvertIntToBytes( value ) + '/' + HC.ConvertIntToBytes( range )
self._text.SetLabel( self._message_string + ' - ' + byte_info )
self.GetParent().MakeSureEverythingFits()
2013-07-24 20:26:00 +00:00
class PopupMessageText( PopupMessage ):
def __init__( self, parent, message_string ):
PopupMessage.__init__( self, parent )
vbox = wx.BoxSizer( wx.VERTICAL )
text = wx.StaticText( self, label = message_string ) # make this multi-line. There's an easy way to do that, right? A func that takes a pixel width, I think
2013-07-31 21:26:38 +00:00
text.Wrap( 380 )
2013-07-24 20:26:00 +00:00
text.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
vbox.AddF( text, FLAGS_EXPAND_PERPENDICULAR )
self.SetSizer( vbox )
class PopupMessageManager( wx.Frame ):
def __init__( self, top_level_parent ):
wx.Frame.__init__( self, top_level_parent, style = wx.FRAME_TOOL_WINDOW | wx.FRAME_NO_TASKBAR | wx.FRAME_FLOAT_ON_PARENT | wx.BORDER_NONE )
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
2013-07-31 21:26:38 +00:00
self._max_messages_to_display = 10
2013-07-24 20:26:00 +00:00
vbox = wx.BoxSizer( wx.VERTICAL )
2013-08-14 20:21:49 +00:00
self._message_vbox = wx.BoxSizer( wx.VERTICAL )
self._dismiss_all = PopupMessageDismissAll( self )
self._dismiss_all.Hide()
vbox.AddF( self._message_vbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( self._dismiss_all, FLAGS_EXPAND_PERPENDICULAR )
2013-07-24 20:26:00 +00:00
self.SetSizer( vbox )
2013-07-31 21:26:38 +00:00
self._pending_messages = []
2013-07-24 20:26:00 +00:00
top_level_parent.Bind( wx.EVT_SIZE, self.EventMove )
top_level_parent.Bind( wx.EVT_MOVE, self.EventMove )
self._SizeAndPositionAndShow()
HC.pubsub.sub( self, 'AddMessage', 'message' )
# maybe make a ding noise when a new message arrives
2013-08-14 20:21:49 +00:00
self._old_excepthook = sys.excepthook
self._old_show_exception = HC.ShowException
sys.excepthook = CC.CatchExceptionClient
2013-08-28 21:31:52 +00:00
HC.ShowException = CC.ShowExceptionClient
2013-08-14 20:21:49 +00:00
2013-07-31 21:26:38 +00:00
def _CheckPending( self ):
2013-08-14 20:21:49 +00:00
num_messages_displayed = self._message_vbox.GetItemCount()
if len( self._pending_messages ) > 0 and num_messages_displayed < self._max_messages_to_display:
2013-07-31 21:26:38 +00:00
message = self._pending_messages.pop( 0 )
window = self._CreateMessageWindow( message )
2013-08-14 20:21:49 +00:00
self._message_vbox.AddF( window, FLAGS_EXPAND_PERPENDICULAR )
2013-07-31 21:26:38 +00:00
2013-08-14 20:21:49 +00:00
num_messages_pending = len( self._pending_messages )
if num_messages_pending > 0:
2013-07-31 21:26:38 +00:00
2013-08-14 20:21:49 +00:00
self._dismiss_all.SetNumMessages( num_messages_pending )
2013-07-31 21:26:38 +00:00
2013-08-14 20:21:49 +00:00
self._dismiss_all.Show()
2013-07-31 21:26:38 +00:00
2013-08-14 20:21:49 +00:00
else: self._dismiss_all.Hide()
self._SizeAndPositionAndShow()
2013-07-24 20:26:00 +00:00
def _CreateMessageWindow( self, message ):
message_type = message.GetType()
info = message.GetInfo()
if message_type == HC.MESSAGE_TYPE_TEXT:
message_string = info
window = PopupMessageText( self, message_string )
elif message_type == HC.MESSAGE_TYPE_ERROR:
2013-08-14 20:21:49 +00:00
( etype, value, trace ) = info
2013-07-24 20:26:00 +00:00
2013-08-14 20:21:49 +00:00
window = PopupMessageError( self, etype, value, trace )
2013-07-24 20:26:00 +00:00
elif message_type == HC.MESSAGE_TYPE_FILES:
( message_string, hashes ) = info
window = PopupMessageFiles( self, message_string, hashes )
2013-09-11 21:28:19 +00:00
elif message_type == HC.MESSAGE_TYPE_FILE_DOWNLOAD_GAUGE:
( job_key, message_string ) = info
window = PopupMessageGauge( self, job_key, message_string )
2013-07-24 20:26:00 +00:00
return window
2013-08-14 20:21:49 +00:00
def _PrintMessage( self, message ):
message_type = message.GetType()
info = message.GetInfo()
if message_type == HC.MESSAGE_TYPE_TEXT:
2013-09-04 16:48:44 +00:00
message_string = HC.u( info )
2013-08-14 20:21:49 +00:00
elif message_type == HC.MESSAGE_TYPE_ERROR:
( etype, value, trace ) = info
2013-09-04 16:48:44 +00:00
message_string = HC.u( etype.__name__ ) + ': ' + HC.u( value ) + os.linesep + HC.u( trace )
2013-08-14 20:21:49 +00:00
elif message_type == HC.MESSAGE_TYPE_FILES:
( message_string, hashes ) = info
2013-09-04 16:48:44 +00:00
message_string = HC.u( message_string )
2013-09-11 21:28:19 +00:00
elif message_type == HC.MESSAGE_TYPE_FILE_DOWNLOAD_GAUGE:
( job_key, message_string ) = info
2013-08-14 20:21:49 +00:00
2013-09-04 16:48:44 +00:00
try: print( message_string )
except: print( repr( message_string ) )
2013-08-14 20:21:49 +00:00
2013-07-24 20:26:00 +00:00
def _SizeAndPositionAndShow( self ):
self.Fit()
parent = self.GetParent()
( parent_width, parent_height ) = parent.GetClientSize()
( my_width, my_height ) = self.GetClientSize()
my_x = ( parent_width - my_width ) - 5
my_y = ( parent_height - my_height ) - 15
self.SetPosition( parent.ClientToScreenXY( my_x, my_y ) )
2013-08-14 20:21:49 +00:00
num_messages_displayed = self._message_vbox.GetItemCount()
if num_messages_displayed > 0: self.Show()
2013-07-24 20:26:00 +00:00
else: self.Hide()
def AddMessage( self, message ):
2013-08-14 20:21:49 +00:00
self._PrintMessage( message )
2013-07-31 21:26:38 +00:00
self._pending_messages.append( message )
2013-07-24 20:26:00 +00:00
2013-07-31 21:26:38 +00:00
self._CheckPending()
2013-07-24 20:26:00 +00:00
2013-08-14 20:21:49 +00:00
def CleanUp( self ):
2013-07-24 20:26:00 +00:00
2013-08-14 20:21:49 +00:00
sys.excepthook = self._old_excepthook
2013-07-24 20:26:00 +00:00
2013-08-14 20:21:49 +00:00
HC.ShowException = self._old_show_exception
2013-07-24 20:26:00 +00:00
2013-09-04 16:48:44 +00:00
self.DismissAll()
self.Hide()
2013-08-14 20:21:49 +00:00
def Dismiss( self, window ):
self._message_vbox.Detach( window )
2013-07-24 20:26:00 +00:00
window.Destroy()
self._SizeAndPositionAndShow()
2013-07-31 21:26:38 +00:00
self._CheckPending()
2013-07-24 20:26:00 +00:00
2013-08-14 20:21:49 +00:00
def DismissAll( self ):
self._pending_messages = []
sizer_items = self._message_vbox.GetChildren()
for sizer_item in sizer_items:
message_window = sizer_item.GetWindow()
self.Dismiss( message_window )
2013-07-24 20:26:00 +00:00
def EventMove( self, event ):
self._SizeAndPositionAndShow()
event.Skip()
2013-09-11 21:28:19 +00:00
def MakeSureEverythingFits( self ): self._SizeAndPositionAndShow()
2013-04-24 21:23:53 +00:00
class RegexButton( wx.Button ):
ID_REGEX_WHITESPACE = 0
ID_REGEX_NUMBER = 1
ID_REGEX_ALPHANUMERIC = 2
ID_REGEX_ANY = 3
ID_REGEX_BEGINNING = 4
ID_REGEX_END = 5
ID_REGEX_0_OR_MORE_GREEDY = 6
ID_REGEX_1_OR_MORE_GREEDY = 7
ID_REGEX_0_OR_1_GREEDY = 8
ID_REGEX_0_OR_MORE_MINIMAL = 9
ID_REGEX_1_OR_MORE_MINIMAL = 10
ID_REGEX_0_OR_1_MINIMAL = 11
ID_REGEX_EXACTLY_M = 12
ID_REGEX_M_TO_N_GREEDY = 13
ID_REGEX_M_TO_N_MINIMAL = 14
ID_REGEX_LOOKAHEAD = 15
ID_REGEX_NEGATIVE_LOOKAHEAD = 16
ID_REGEX_LOOKBEHIND = 17
ID_REGEX_NEGATIVE_LOOKBEHIND = 18
ID_REGEX_NUMBER_WITHOUT_ZEROES = 19
ID_REGEX_NUMBER_EXT = 20
ID_REGEX_AUTHOR = 21
ID_REGEX_BACKSPACE = 22
ID_REGEX_SET = 23
ID_REGEX_NOT_SET = 24
def __init__( self, parent ):
wx.Button.__init__( self, parent, label = 'regex shortcuts' )
self.Bind( wx.EVT_BUTTON, self.EventButton )
self.Bind( wx.EVT_MENU, self.EventMenu )
def EventButton( self, event ):
menu = wx.Menu()
menu.Append( -1, 'click on a phrase to copy to clipboard' )
menu.AppendSeparator()
menu.Append( self.ID_REGEX_WHITESPACE, r'whitespace character - \s' )
menu.Append( self.ID_REGEX_NUMBER, r'number character - \d' )
menu.Append( self.ID_REGEX_ALPHANUMERIC, r'alphanumeric or backspace character - \w' )
menu.Append( self.ID_REGEX_ANY, r'any character - .' )
menu.Append( self.ID_REGEX_BACKSPACE, r'backspace character - \\' )
menu.Append( self.ID_REGEX_BEGINNING, r'beginning of line - ^' )
menu.Append( self.ID_REGEX_END, r'end of line - $' )
menu.Append( self.ID_REGEX_SET, r'any of these - [...]' )
menu.Append( self.ID_REGEX_NOT_SET, r'anything other than these - [^...]' )
menu.AppendSeparator()
menu.Append( self.ID_REGEX_0_OR_MORE_GREEDY, r'0 or more matches, consuming as many as possible - *' )
menu.Append( self.ID_REGEX_1_OR_MORE_GREEDY, r'1 or more matches, consuming as many as possible - +' )
menu.Append( self.ID_REGEX_0_OR_1_GREEDY, r'0 or 1 matches, preferring 1 - ?' )
menu.Append( self.ID_REGEX_0_OR_MORE_MINIMAL, r'0 or more matches, consuming as few as possible - *?' )
menu.Append( self.ID_REGEX_1_OR_MORE_MINIMAL, r'1 or more matches, consuming as few as possible - +?' )
menu.Append( self.ID_REGEX_0_OR_1_MINIMAL, r'0 or 1 matches, preferring 0 - *' )
menu.Append( self.ID_REGEX_EXACTLY_M, r'exactly m matches - {m}' )
menu.Append( self.ID_REGEX_M_TO_N_GREEDY, r'm to n matches, consuming as many as possible - {m,n}' )
menu.Append( self.ID_REGEX_M_TO_N_MINIMAL, r'm to n matches, consuming as few as possible - {m,n}?' )
menu.AppendSeparator()
menu.Append( self.ID_REGEX_LOOKAHEAD, r'the next characters are: (non-consuming) - (?=...)' )
menu.Append( self.ID_REGEX_NEGATIVE_LOOKAHEAD, r'the next characters are not: (non-consuming) - (?!...)' )
menu.Append( self.ID_REGEX_LOOKBEHIND, r'the previous characters are: (non-consuming) - (?<=...)' )
menu.Append( self.ID_REGEX_NEGATIVE_LOOKBEHIND, r'the previous characters are not: (non-consuming) - (?<!...)' )
menu.AppendSeparator()
menu.Append( self.ID_REGEX_NUMBER_WITHOUT_ZEROES, r'0074 -> 74 - [1-9]+\d*' )
menu.Append( self.ID_REGEX_NUMBER_EXT, r'...0074.jpg -> 74 - [1-9]+\d*(?=.{4}$)' )
menu.Append( self.ID_REGEX_AUTHOR, r'E:\my collection\author name - v4c1p0074.jpg -> author name - [^\\][\w\s]*(?=\s-)' )
self.PopupMenu( menu )
menu.Destroy()
def EventMenu( self, event ):
id = event.GetId()
phrase = None
if id == self.ID_REGEX_WHITESPACE: phrase = r'\s'
elif id == self.ID_REGEX_NUMBER: phrase = r'\d'
elif id == self.ID_REGEX_ALPHANUMERIC: phrase = r'\w'
elif id == self.ID_REGEX_ANY: phrase = r'.'
elif id == self.ID_REGEX_BACKSPACE: phrase = r'\\'
elif id == self.ID_REGEX_BEGINNING: phrase = r'^'
elif id == self.ID_REGEX_END: phrase = r'$'
elif id == self.ID_REGEX_SET: phrase = r'[...]'
elif id == self.ID_REGEX_NOT_SET: phrase = r'[^...]'
elif id == self.ID_REGEX_0_OR_MORE_GREEDY: phrase = r'*'
elif id == self.ID_REGEX_1_OR_MORE_GREEDY: phrase = r'+'
elif id == self.ID_REGEX_0_OR_1_GREEDY: phrase = r'?'
elif id == self.ID_REGEX_0_OR_MORE_MINIMAL: phrase = r'*?'
elif id == self.ID_REGEX_1_OR_MORE_MINIMAL: phrase = r'+?'
elif id == self.ID_REGEX_0_OR_1_MINIMAL: phrase = r'*'
elif id == self.ID_REGEX_EXACTLY_M: phrase = r'{m}'
elif id == self.ID_REGEX_M_TO_N_GREEDY: phrase = r'{m,n}'
elif id == self.ID_REGEX_M_TO_N_MINIMAL: phrase = r'{m,n}?'
elif id == self.ID_REGEX_LOOKAHEAD: phrase = r'(?=...)'
elif id == self.ID_REGEX_NEGATIVE_LOOKAHEAD: phrase = r'(?!...)'
elif id == self.ID_REGEX_LOOKBEHIND: phrase = r'(?<=...)'
elif id == self.ID_REGEX_NEGATIVE_LOOKBEHIND: phrase = r'(?<!...)'
elif id == self.ID_REGEX_NUMBER_WITHOUT_ZEROES: phrase = r'[1-9]+\d*'
elif id == self.ID_REGEX_NUMBER_EXT: phrase = r'[1-9]+\d*(?=.{4}$)'
elif id == self.ID_REGEX_AUTHOR: phrase = r'[^\\][\w\s]*(?=\s-)'
else: event.Skip()
if phrase is not None:
if wx.TheClipboard.Open():
data = wx.TextDataObject( phrase )
wx.TheClipboard.SetData( data )
wx.TheClipboard.Close()
else: wx.MessageBox( 'I could not get permission to access the clipboard.' )
2013-03-15 02:38:12 +00:00
class SaneListCtrl( wx.ListCtrl, ListCtrlAutoWidthMixin, ColumnSorterMixin ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def __init__( self, parent, height, columns ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
num_columns = len( columns )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
wx.ListCtrl.__init__( self, parent, size=( -1, height ), style=wx.LC_REPORT )
ListCtrlAutoWidthMixin.__init__( self )
ColumnSorterMixin.__init__( self, num_columns )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.GetTopLevelParent().SetDoubleBuffered( False ) # windows double buffer makes listctrls refresh and bug out
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.itemDataMap = {}
self._next_data_index = 0
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
resize_column = 1
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
for ( i, ( name, width ) ) in enumerate( columns ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.InsertColumn( i, name, width = width )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if width == -1: resize_column = i + 1
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.setResizeColumn( resize_column )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def Append( self, display_tuple, data_tuple ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
index = wx.ListCtrl.Append( self, display_tuple )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.SetItemData( index, self._next_data_index )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.itemDataMap[ self._next_data_index ] = list( data_tuple )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._next_data_index += 1
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def GetAllSelected( self ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
indices = []
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
i = self.GetFirstSelected()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
while i != -1:
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
indices.append( i )
i = self.GetNextSelected( i )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
return indices
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def GetClientData( self, index = None ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if index is None:
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
data_indicies = [ self.GetItemData( index ) for index in range( self.GetItemCount() ) ]
2013-02-19 00:11:43 +00:00
2013-05-08 20:31:00 +00:00
datas = [ tuple( self.itemDataMap[ data_index ] ) for data_index in data_indicies ]
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
return datas
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
else:
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
data_index = self.GetItemData( index )
2013-02-19 00:11:43 +00:00
2013-05-08 20:31:00 +00:00
return tuple( self.itemDataMap[ data_index ] )
2013-02-19 00:11:43 +00:00
2013-06-12 22:53:31 +00:00
def GetIndexFromClientData( self, data ):
for index in range( self.GetItemCount() ):
if self.GetClientData( index ) == data: return index
raise Exception( 'Data not found!' )
2013-03-15 02:38:12 +00:00
def GetListCtrl( self ): return self
def RemoveAllSelected( self ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
indices = self.GetAllSelected()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
indices.reverse() # so we don't screw with the indices of deletees below
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
for index in indices: self.DeleteItem( index )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def UpdateValue( self, index, column, display_value, data_value ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.SetStringItem( index, column, display_value )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
data_index = self.GetItemData( index )
self.itemDataMap[ data_index ][ column ] = data_value
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def UpdateRow( self, index, display_tuple, data_tuple ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
column = 0
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
for value in display_tuple:
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.SetStringItem( index, column, value )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
column += 1
data_index = self.GetItemData( index )
self.itemDataMap[ data_index ] = data_tuple
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
class Shortcut( wx.TextCtrl ):
def __init__( self, parent, modifier = wx.ACCEL_NORMAL, key = wx.WXK_F7 ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._modifier = modifier
self._key = key
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
wx.TextCtrl.__init__( self, parent, style = wx.TE_PROCESS_ENTER )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._SetShortcutString()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def _SetShortcutString( self ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
display_string = ''
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if self._modifier == wx.ACCEL_ALT: display_string += 'alt + '
elif self._modifier == wx.ACCEL_CTRL: display_string += 'ctrl + '
elif self._modifier == wx.ACCEL_SHIFT: display_string += 'shift + '
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if self._key in range( 65, 91 ): display_string += chr( self._key + 32 ) # + 32 for converting ascii A -> a
elif self._key in range( 97, 123 ): display_string += chr( self._key )
else: display_string += HC.wxk_code_string_lookup[ self._key ]
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
wx.TextCtrl.SetValue( self, display_string )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def EventKeyDown( self, event ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if event.KeyCode in range( 65, 91 ) or event.KeyCode in HC.wxk_code_string_lookup.keys():
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
modifier = wx.ACCEL_NORMAL
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if event.AltDown(): modifier = wx.ACCEL_ALT
elif event.ControlDown(): modifier = wx.ACCEL_CTRL
elif event.ShiftDown(): modifier = wx.ACCEL_SHIFT
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
( self._modifier, self._key ) = HC.GetShortcutFromEvent( event )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._SetShortcutString()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def GetValue( self ): return ( self._modifier, self._key )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def SetValue( self, modifier, key ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
( self._modifier, self._key ) = ( modifier, key )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._SetShortcutString()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
class StaticBox( wx.Panel ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def __init__( self, parent, title ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
wx.Panel.__init__( self, parent, style = wx.BORDER_DOUBLE )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._sizer = wx.BoxSizer( wx.VERTICAL )
normal_font = wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT )
normal_font_size = normal_font.GetPointSize()
normal_font_family = normal_font.GetFamily()
title_font = wx.Font( int( normal_font_size ), normal_font_family, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD )
title_text = wx.StaticText( self, label = title, style = wx.ALIGN_CENTER )
title_text.SetFont( title_font )
self._sizer.AddF( title_text, FLAGS_EXPAND_PERPENDICULAR )
self.SetSizer( self._sizer )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def AddF( self, widget, flags ): self._sizer.AddF( widget, flags )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
class AdvancedOptions( StaticBox ):
def __init__( self, parent, title ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
StaticBox.__init__( self, parent, title )
self._collapsible_panel = wx.CollapsiblePane( self, label = 'expand' )
my_panel = self._collapsible_panel.GetPane()
self._InitPanel( my_panel )
self.AddF( self._collapsible_panel, FLAGS_EXPAND_PERPENDICULAR )
self.Bind( wx.EVT_COLLAPSIBLEPANE_CHANGED, self.EventChanged )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def _InitPanel( self, panel ): pass
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def EventChanged( self, event ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self.GetParent().Layout() # make this vertical only?
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if self._collapsible_panel.IsExpanded(): label = 'collapse'
else: label = 'expand'
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._collapsible_panel.SetLabel( label )
event.Skip()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
class AdvancedHentaiFoundryOptions( AdvancedOptions ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def __init__( self, parent ): AdvancedOptions.__init__( self, parent, 'advanced hentai foundry options' )
def _InitPanel( self, panel ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def offensive_choice():
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
c = wx.Choice( panel )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
c.Append( 'none', 0 )
c.Append( 'mild', 1 )
c.Append( 'moderate', 2 )
c.Append( 'strong', 3 )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
c.SetSelection( 3 )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
return c
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._rating_nudity = offensive_choice()
self._rating_violence = offensive_choice()
self._rating_profanity = offensive_choice()
self._rating_racism = offensive_choice()
self._rating_sex = offensive_choice()
self._rating_spoilers = offensive_choice()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._rating_yaoi = wx.CheckBox( panel )
self._rating_yuri = wx.CheckBox( panel )
self._rating_loli = wx.CheckBox( panel )
self._rating_shota = wx.CheckBox( panel )
self._rating_teen = wx.CheckBox( panel )
self._rating_guro = wx.CheckBox( panel )
self._rating_furry = wx.CheckBox( panel )
self._rating_beast = wx.CheckBox( panel )
self._rating_male = wx.CheckBox( panel )
self._rating_female = wx.CheckBox( panel )
self._rating_futa = wx.CheckBox( panel )
self._rating_other = wx.CheckBox( panel )
self._rating_yaoi.SetValue( True )
self._rating_yuri.SetValue( True )
self._rating_loli.SetValue( True )
self._rating_shota.SetValue( True )
self._rating_teen.SetValue( True )
self._rating_guro.SetValue( True )
self._rating_furry.SetValue( True )
self._rating_beast.SetValue( True )
self._rating_male.SetValue( True )
self._rating_female.SetValue( True )
self._rating_futa.SetValue( True )
self._rating_other.SetValue( True )
self._filter_order = wx.Choice( panel )
self._filter_order.Append( 'newest first', 'date_new' )
self._filter_order.Append( 'oldest first', 'date_old' )
self._filter_order.Append( 'most views first', 'views most' ) # no underscore
self._filter_order.Append( 'highest rating first', 'rating highest' ) # no underscore
self._filter_order.Append( 'most favourites first', 'faves most' ) # no underscore
self._filter_order.Append( 'most popular first', 'popularity most' ) # no underscore
self._filter_order.SetSelection( 0 )
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 1, 1 )
gridbox.AddF( wx.StaticText( panel, label = 'nudity' ), FLAGS_MIXED )
gridbox.AddF( self._rating_nudity, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( panel, label = 'violence' ), FLAGS_MIXED )
gridbox.AddF( self._rating_violence, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( panel, label = 'profanity' ), FLAGS_MIXED )
gridbox.AddF( self._rating_profanity, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( panel, label = 'racism' ), FLAGS_MIXED )
gridbox.AddF( self._rating_racism, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( panel, label = 'sex' ), FLAGS_MIXED )
gridbox.AddF( self._rating_sex, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( panel, label = 'spoilers' ), FLAGS_MIXED )
gridbox.AddF( self._rating_spoilers, FLAGS_EXPAND_BOTH_WAYS )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
gridbox.AddF( wx.StaticText( panel, label = 'yaoi' ), FLAGS_MIXED )
gridbox.AddF( self._rating_yaoi, FLAGS_EXPAND_BOTH_WAYS )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
gridbox.AddF( wx.StaticText( panel, label = 'yuri' ), FLAGS_MIXED )
gridbox.AddF( self._rating_yuri, FLAGS_EXPAND_BOTH_WAYS )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
gridbox.AddF( wx.StaticText( panel, label = 'loli' ), FLAGS_MIXED )
gridbox.AddF( self._rating_loli, FLAGS_EXPAND_BOTH_WAYS )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
gridbox.AddF( wx.StaticText( panel, label = 'shota' ), FLAGS_MIXED )
gridbox.AddF( self._rating_shota, FLAGS_EXPAND_BOTH_WAYS )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
gridbox.AddF( wx.StaticText( panel, label = 'teen' ), FLAGS_MIXED )
gridbox.AddF( self._rating_teen, FLAGS_EXPAND_BOTH_WAYS )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
gridbox.AddF( wx.StaticText( panel, label = 'guro' ), FLAGS_MIXED )
gridbox.AddF( self._rating_guro, FLAGS_EXPAND_BOTH_WAYS )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
gridbox.AddF( wx.StaticText( panel, label = 'furry' ), FLAGS_MIXED )
gridbox.AddF( self._rating_furry, FLAGS_EXPAND_BOTH_WAYS )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
gridbox.AddF( wx.StaticText( panel, label = 'beast' ), FLAGS_MIXED )
gridbox.AddF( self._rating_beast, FLAGS_EXPAND_BOTH_WAYS )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
gridbox.AddF( wx.StaticText( panel, label = 'male' ), FLAGS_MIXED )
gridbox.AddF( self._rating_male, FLAGS_EXPAND_BOTH_WAYS )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
gridbox.AddF( wx.StaticText( panel, label = 'female' ), FLAGS_MIXED )
gridbox.AddF( self._rating_female, FLAGS_EXPAND_BOTH_WAYS )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
gridbox.AddF( wx.StaticText( panel, label = 'futa' ), FLAGS_MIXED )
gridbox.AddF( self._rating_futa, FLAGS_EXPAND_BOTH_WAYS )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
gridbox.AddF( wx.StaticText( panel, label = 'other' ), FLAGS_MIXED )
gridbox.AddF( self._rating_other, FLAGS_EXPAND_BOTH_WAYS )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
gridbox.AddF( wx.StaticText( panel, label = 'order' ), FLAGS_MIXED )
gridbox.AddF( self._filter_order, FLAGS_EXPAND_BOTH_WAYS )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
panel.SetSizer( gridbox )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def GetInfo( self ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
info = {}
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
info[ 'rating_nudity' ] = self._rating_nudity.GetClientData( self._rating_nudity.GetSelection() )
info[ 'rating_violence' ] = self._rating_violence.GetClientData( self._rating_violence.GetSelection() )
info[ 'rating_profanity' ] = self._rating_profanity.GetClientData( self._rating_profanity.GetSelection() )
info[ 'rating_racism' ] = self._rating_racism.GetClientData( self._rating_racism.GetSelection() )
info[ 'rating_sex' ] = self._rating_sex.GetClientData( self._rating_sex.GetSelection() )
info[ 'rating_spoilers' ] = self._rating_spoilers.GetClientData( self._rating_spoilers.GetSelection() )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
info[ 'rating_yaoi' ] = int( self._rating_yaoi.GetValue() )
info[ 'rating_yuri' ] = int( self._rating_yuri.GetValue() )
info[ 'rating_loli' ] = int( self._rating_loli.GetValue() )
info[ 'rating_shota' ] = int( self._rating_shota.GetValue() )
info[ 'rating_teen' ] = int( self._rating_teen.GetValue() )
info[ 'rating_guro' ] = int( self._rating_guro.GetValue() )
info[ 'rating_furry' ] = int( self._rating_furry.GetValue() )
info[ 'rating_beast' ] = int( self._rating_beast.GetValue() )
info[ 'rating_male' ] = int( self._rating_male.GetValue() )
info[ 'rating_female' ] = int( self._rating_female.GetValue() )
info[ 'rating_futa' ] = int( self._rating_futa.GetValue() )
info[ 'rating_other' ] = int( self._rating_other.GetValue() )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
info[ 'filter_media' ] = 'A'
info[ 'filter_order' ] = self._filter_order.GetClientData( self._filter_order.GetSelection() )
info[ 'filter_type' ] = 0
return info
2013-02-19 00:11:43 +00:00
2013-04-17 21:48:18 +00:00
def SetInfo( self, info ):
self._rating_nudity.SetSelection( info[ 'rating_nudity' ] )
self._rating_violence.SetSelection( info[ 'rating_violence' ] )
self._rating_profanity.SetSelection( info[ 'rating_profanity' ] )
self._rating_racism.SetSelection( info[ 'rating_racism' ] )
self._rating_sex.SetSelection( info[ 'rating_sex' ] )
self._rating_spoilers.SetSelection( info[ 'rating_spoilers' ] )
self._rating_yaoi.SetValue( bool( info[ 'rating_yaoi' ] ) )
self._rating_yuri.SetValue( bool( info[ 'rating_yuri' ] ) )
self._rating_loli.SetValue( bool( info[ 'rating_loli' ] ) )
self._rating_shota.SetValue( bool( info[ 'rating_shota' ] ) )
self._rating_teen.SetValue( bool( info[ 'rating_teen' ] ) )
self._rating_guro.SetValue( bool( info[ 'rating_guro' ] ) )
self._rating_furry.SetValue( bool( info[ 'rating_furry' ] ) )
self._rating_beast.SetValue( bool( info[ 'rating_beast' ] ) )
self._rating_male.SetValue( bool( info[ 'rating_male' ] ) )
self._rating_female.SetValue( bool( info[ 'rating_female' ] ) )
self._rating_futa.SetValue( bool( info[ 'rating_futa' ] ) )
self._rating_other.SetValue( bool( info[ 'rating_other' ] ) )
#info[ 'filter_media' ] = 'A'
self._filter_order.SetSelection( info[ 'filter_order' ] )
#info[ 'filter_type' ] = 0
2013-03-15 02:38:12 +00:00
class AdvancedImportOptions( AdvancedOptions ):
2013-04-10 18:10:37 +00:00
def __init__( self, parent, initial_settings = {} ):
self._initial_settings = initial_settings
AdvancedOptions.__init__( self, parent, 'advanced import options' )
2013-03-15 02:38:12 +00:00
def _InitPanel( self, panel ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._auto_archive = wx.CheckBox( panel )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._exclude_deleted = wx.CheckBox( panel )
2013-02-19 00:11:43 +00:00
2013-07-31 21:26:38 +00:00
self._min_size = NoneableSpinCtrl( panel, 'minimum size (KB): ', multiplier = 1024 )
self._min_size.SetValue( 5120 )
2013-02-19 00:11:43 +00:00
2013-07-31 21:26:38 +00:00
self._min_resolution = NoneableSpinCtrl( panel, 'minimum resolution: ', num_dimensions = 2 )
self._min_resolution.SetValue( ( 50, 50 ) )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
hbox1 = wx.BoxSizer( wx.HORIZONTAL )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
hbox1.AddF( self._auto_archive, FLAGS_MIXED )
hbox1.AddF( wx.StaticText( panel, label = ' archive all imports' ), FLAGS_MIXED )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
hbox2 = wx.BoxSizer( wx.HORIZONTAL )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
hbox2.AddF( self._exclude_deleted, FLAGS_MIXED )
hbox2.AddF( wx.StaticText( panel, label = ' exclude already deleted files' ), FLAGS_MIXED )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( hbox1, FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( hbox2, FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( self._min_size, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._min_resolution, FLAGS_EXPAND_PERPENDICULAR )
panel.SetSizer( vbox )
2013-02-19 00:11:43 +00:00
2013-04-17 21:48:18 +00:00
self._SetControls( self._initial_settings )
def _SetControls( self, info ):
if 'auto_archive' in info: self._auto_archive.SetValue( info[ 'auto_archive' ] )
else: self._auto_archive.SetValue( False )
if 'exclude_deleted_files' in info: self._exclude_deleted.SetValue( info[ 'exclude_deleted_files' ] )
2013-08-14 20:21:49 +00:00
else: self._exclude_deleted.SetValue( HC.options[ 'exclude_deleted_files' ] )
2013-04-17 21:48:18 +00:00
if 'min_size' in info: self._min_size.SetValue( info[ 'min_size' ] )
else: self._min_size.SetValue( None )
if 'min_resolution' in info: self._min_resolution.SetValue( info[ 'min_resolution' ] )
else: self._min_resolution.SetValue( None )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def GetInfo( self ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
info = {}
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if self._auto_archive.GetValue(): info[ 'auto_archive' ] = True
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if self._exclude_deleted.GetValue(): info[ 'exclude_deleted_files' ] = True
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
min_size = self._min_size.GetValue()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if min_size is not None: info[ 'min_size' ] = min_size
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
min_resolution = self._min_resolution.GetValue()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if min_resolution is not None: info[ 'min_resolution' ] = min_resolution
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
return info
2013-02-19 00:11:43 +00:00
2013-04-17 21:48:18 +00:00
def SetInfo( self, info ): self._SetControls( info )
2013-03-15 02:38:12 +00:00
class AdvancedTagOptions( AdvancedOptions ):
2013-02-19 00:11:43 +00:00
2013-04-10 18:10:37 +00:00
def __init__( self, parent, info_string, namespaces = [], initial_settings = {} ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._info_string = info_string
self._namespaces = namespaces
2013-04-10 18:10:37 +00:00
self._initial_settings = initial_settings
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
self._checkboxes_to_service_identifiers = {}
self._service_identifiers_to_namespaces = {}
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
AdvancedOptions.__init__( self, parent, 'advanced tag options' )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def _InitPanel( self, panel ):
2013-02-19 00:11:43 +00:00
2013-07-10 20:25:57 +00:00
service_identifiers = HC.app.Read( 'service_identifiers', ( HC.TAG_REPOSITORY, HC.LOCAL_TAG ) )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
vbox = wx.BoxSizer( wx.VERTICAL )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if len( service_identifiers ) > 0:
for service_identifier in service_identifiers:
hbox = wx.BoxSizer( wx.HORIZONTAL )
checkbox = wx.CheckBox( panel )
2013-04-10 18:10:37 +00:00
if service_identifier in self._initial_settings: checkbox.SetValue( True )
2013-03-15 02:38:12 +00:00
checkbox.Bind( wx.EVT_CHECKBOX, self.EventChecked )
self._checkboxes_to_service_identifiers[ checkbox ] = service_identifier
hbox.AddF( wx.StaticText( panel, label = service_identifier.GetName() ), FLAGS_MIXED )
hbox.AddF( checkbox, FLAGS_MIXED )
if len( self._namespaces ) > 0:
namespace_vbox = wx.BoxSizer( wx.VERTICAL )
self._service_identifiers_to_namespaces[ service_identifier ] = []
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 1, 1 )
for namespace in self._namespaces:
if namespace == '': text = wx.StaticText( panel, label = 'no namespace' )
else: text = wx.StaticText( panel, label = namespace )
namespace_checkbox = wx.CheckBox( panel )
2013-04-10 18:10:37 +00:00
if service_identifier in self._initial_settings and namespace not in self._initial_settings[ service_identifier ]: namespace_checkbox.SetValue( False )
else: namespace_checkbox.SetValue( True )
2013-03-15 02:38:12 +00:00
namespace_checkbox.Bind( wx.EVT_CHECKBOX, self.EventChecked )
self._service_identifiers_to_namespaces[ service_identifier ].append( ( namespace, namespace_checkbox ) )
gridbox.AddF( text, FLAGS_MIXED )
gridbox.AddF( namespace_checkbox, FLAGS_EXPAND_BOTH_WAYS )
hbox.AddF( gridbox, FLAGS_MIXED )
vbox.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( panel, label = self._info_string ), FLAGS_MIXED )
hbox.AddF( vbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
panel.SetSizer( hbox )
else:
vbox.AddF( wx.StaticText( panel, label = 'no tag repositories' ), FLAGS_EXPAND_BOTH_WAYS )
panel.SetSizer( vbox )
2013-02-19 00:11:43 +00:00
2013-04-10 18:10:37 +00:00
def EventChecked( self, event ):
wx.PostEvent( self, wx.CommandEvent( commandType = wx.wxEVT_COMMAND_MENU_SELECTED, winid = CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'advanced_tag_options_changed' ) ) )
event.Skip()
2013-03-15 02:38:12 +00:00
def GetInfo( self ):
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
service_identifiers = [ self._checkboxes_to_service_identifiers[ checkbox ] for checkbox in self._checkboxes_to_service_identifiers.keys() if checkbox.GetValue() ]
2013-04-10 18:10:37 +00:00
result = {}
2013-03-15 02:38:12 +00:00
for service_identifier in service_identifiers:
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
good_namespaces = []
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if service_identifier in self._service_identifiers_to_namespaces:
namespaces = self._service_identifiers_to_namespaces[ service_identifier ]
for ( namespace, namespace_checkbox ) in namespaces:
if namespace_checkbox.GetValue(): good_namespaces.append( namespace )
2013-02-19 00:11:43 +00:00
2013-04-10 18:10:37 +00:00
result[ service_identifier ] = good_namespaces
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
return result
2013-02-19 00:11:43 +00:00
2013-04-17 21:48:18 +00:00
def SetInfo( self, info ):
for ( checkbox, service_identifier ) in self._checkboxes_to_service_identifiers.items():
if service_identifier in info:
checkbox.SetValue( True )
for ( namespace, namespace_checkbox ) in self._service_identifiers_to_namespaces[ service_identifier ]:
if namespace in info[ service_identifier ]: namespace_checkbox.SetValue( True )
else: namespace_checkbox.SetValue( False )
else:
checkbox.SetValue( False )
for ( namespace, namespace_checkbox ) in self._service_identifiers_to_namespaces[ service_identifier ]: namespace_checkbox.SetValue( True )
2013-04-03 20:56:07 +00:00
class RadioBox( StaticBox ):
def __init__( self, parent, title, choice_pairs, initial_index = None ):
StaticBox.__init__( self, parent, title )
self._indices_to_radio_buttons = {}
self._radio_buttons_to_data = {}
first_button = True
for ( index, ( text, data ) ) in enumerate( choice_pairs ):
if first_button:
style = wx.RB_GROUP
first_button = False
else: style = 0
radio_button = wx.RadioButton( self, label = text, style = style )
self.AddF( radio_button, FLAGS_EXPAND_PERPENDICULAR )
self._indices_to_radio_buttons[ index ] = radio_button
self._radio_buttons_to_data[ radio_button ] = data
if initial_index is not None and initial_index in self._indices_to_radio_buttons: self._indices_to_radio_buttons[ index ].SetValue( True )
def GetSelectedClientData( self ):
for radio_button in self._radio_buttons_to_data.keys():
if radio_button.GetValue() == True: return self._radio_buttons_to_data[ radio_button ]
def SetSelection( self, index ): self._indices_to_radio_buttons[ index ].SetValue( True )
def SetString( self, index, text ): self._indices_to_radio_buttons[ index ].SetLabel( text )
2013-02-19 00:11:43 +00:00
class TagsBox( ListBox ):
2013-08-14 20:21:49 +00:00
def _GetNamespaceColours( self ): return HC.options[ 'namespace_colours' ]
2013-02-19 00:11:43 +00:00
def _GetTextColour( self, tag_string ):
namespace_colours = self._GetNamespaceColours()
if ':' in tag_string:
( namespace, sub_tag ) = tag_string.split( ':', 1 )
if namespace.startswith( '-' ): namespace = namespace[1:]
if namespace.startswith( '(+) ' ): namespace = namespace[4:]
if namespace.startswith( '(-) ' ): namespace = namespace[4:]
if namespace.startswith( '(X) ' ): namespace = namespace[4:]
2013-05-15 18:58:14 +00:00
if namespace.startswith( ' ' ): namespace = namespace[4:]
2013-02-19 00:11:43 +00:00
if namespace in namespace_colours: ( r, g, b ) = namespace_colours[ namespace ]
else: ( r, g, b ) = namespace_colours[ None ]
else: ( r, g, b ) = namespace_colours[ '' ]
return ( r, g, b )
2013-07-24 20:26:00 +00:00
def EventMenu( self, event ):
action = CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
if action is not None:
2013-08-14 20:21:49 +00:00
( command, data ) = action
if command == 'copy': HC.pubsub.pub( 'clipboard', 'text', data )
elif command in ( 'parent', 'sibling' ):
2013-07-24 20:26:00 +00:00
2013-08-14 20:21:49 +00:00
tag = data
2013-07-24 20:26:00 +00:00
2013-08-14 20:21:49 +00:00
import ClientGUIDialogsManage
if command == 'parent':
2013-07-24 20:26:00 +00:00
2013-08-14 20:21:49 +00:00
with ClientGUIDialogsManage.DialogManageTagParents( self, tag ) as dlg: dlg.ShowModal()
2013-07-24 20:26:00 +00:00
2013-08-14 20:21:49 +00:00
elif command == 'sibling':
2013-07-24 20:26:00 +00:00
2013-08-14 20:21:49 +00:00
with ClientGUIDialogsManage.DialogManageTagSiblings( self, tag ) as dlg: dlg.ShowModal()
2013-07-24 20:26:00 +00:00
2013-08-14 20:21:49 +00:00
else:
event.Skip()
2013-07-24 20:26:00 +00:00
2013-08-14 20:21:49 +00:00
return # this is about select_up and select_down
2013-07-24 20:26:00 +00:00
def EventMouseRightClick( self, event ):
index = self._GetIndexUnderMouse( event )
self._Select( index )
if self._current_selected_index is not None:
menu = wx.Menu()
term = self._strings_to_terms[ self._ordered_strings[ self._current_selected_index ] ]
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy', term ), 'copy ' + term )
if ':' in term:
sub_term = term.split( ':', 1 )[1]
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy', sub_term ), 'copy ' + sub_term )
menu.AppendSeparator()
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'parent', term ), 'add parent to ' + term )
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'sibling', term ), 'add sibling to ' + term )
self.PopupMenu( menu )
menu.Destroy()
event.Skip()
2013-02-19 00:11:43 +00:00
class TagsBoxActiveOnly( TagsBox ):
def __init__( self, parent, callable ):
TagsBox.__init__( self, parent )
self._callable = callable
2013-03-27 20:02:51 +00:00
self._predicates = {}
2013-02-19 00:11:43 +00:00
2013-05-08 20:31:00 +00:00
def _Activate( self, s, term ): self._callable( term )
2013-02-19 00:11:43 +00:00
2013-05-15 18:58:14 +00:00
def _Select( self, index ):
if index is not None:
if self._current_selected_index is None: direction = 1
elif index - self._current_selected_index in ( -1, 1 ): direction = index - self._current_selected_index
else: direction = 1
if index == -1 or index > len( self._ordered_strings ): index = len( self._ordered_strings ) - 1
elif index == len( self._ordered_strings ) or index < -1: index = 0
s = self._ordered_strings[ index ]
new_term = self._strings_to_terms[ s ]
while new_term.GetPredicateType() == HC.PREDICATE_TYPE_PARENT:
index += direction
if index == -1 or index > len( self._ordered_strings ): index = len( self._ordered_strings ) - 1
elif index == len( self._ordered_strings ) or index < -1: index = 0
s = self._ordered_strings[ index ]
new_term = self._strings_to_terms[ s ]
ListBox._Select( self, index )
2013-03-27 20:02:51 +00:00
def SetPredicates( self, predicates ):
2013-02-19 00:11:43 +00:00
2013-05-08 20:31:00 +00:00
# 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 ):
p_list_1 = list( predicates )
p_list_2 = list( self._predicates )
p_list_1.sort()
p_list_2.sort()
for index in range( len( p_list_1 ) ):
p_1 = p_list_1[ index ]
p_2 = p_list_2[ 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:
2013-02-19 00:11:43 +00:00
2013-03-27 20:02:51 +00:00
self._predicates = predicates
2013-02-19 00:11:43 +00:00
self._ordered_strings = []
self._strings_to_terms = {}
2013-03-27 20:02:51 +00:00
for predicate in predicates:
2013-02-19 00:11:43 +00:00
2013-03-27 20:02:51 +00:00
tag_string = predicate.GetUnicode()
2013-02-19 00:11:43 +00:00
self._ordered_strings.append( tag_string )
2013-03-27 20:02:51 +00:00
self._strings_to_terms[ tag_string ] = predicate
2013-02-19 00:11:43 +00:00
self._TextsHaveChanged()
2013-03-27 20:02:51 +00:00
if len( predicates ) > 0: self._Select( 0 )
2013-02-19 00:11:43 +00:00
class TagsBoxCPP( TagsBox ):
def __init__( self, parent, page_key ):
TagsBox.__init__( self, parent, min_height = 200 )
2013-08-14 20:21:49 +00:00
self._sort = HC.options[ 'default_tag_sort' ]
2013-02-19 00:11:43 +00:00
self._page_key = page_key
2013-05-29 20:19:54 +00:00
self._tag_service_identifier = HC.COMBINED_TAG_SERVICE_IDENTIFIER
2013-02-19 00:11:43 +00:00
self._last_media = None
2013-08-28 21:31:52 +00:00
self._current_tags_to_count = collections.Counter()
self._pending_tags_to_count = collections.Counter()
self._petitioned_tags_to_count = collections.Counter()
2013-02-19 00:11:43 +00:00
HC.pubsub.sub( self, 'SetTagsByMedia', 'new_tags_selection' )
HC.pubsub.sub( self, 'ChangeTagRepository', 'change_tag_repository' )
2013-05-08 20:31:00 +00:00
def _Activate( self, s, term ):
2013-03-27 20:02:51 +00:00
2013-05-08 20:31:00 +00:00
predicate = HC.Predicate( HC.PREDICATE_TYPE_TAG, ( '+', term ), None )
2013-03-27 20:02:51 +00:00
HC.pubsub.pub( 'add_predicate', self._page_key, predicate )
2013-02-19 00:11:43 +00:00
2013-08-28 21:31:52 +00:00
def _RecalcStrings( self ):
siblings_manager = HC.app.GetTagSiblingsManager()
all_current = ( tag for tag in self._current_tags_to_count if self._current_tags_to_count[ tag ] > 0 )
all_pending = ( tag for tag in self._pending_tags_to_count if self._pending_tags_to_count[ tag ] > 0 )
all_petitioned = ( tag for tag in self._petitioned_tags_to_count if self._petitioned_tags_to_count[ tag ] > 0 )
all_tags = set( itertools.chain( all_current, all_pending, all_petitioned ) )
self._ordered_strings = []
self._strings_to_terms = {}
for tag in all_tags:
tag_string = tag
if tag in self._current_tags_to_count: tag_string += ' (' + HC.ConvertIntToPrettyString( self._current_tags_to_count[ tag ] ) + ')'
if tag in self._pending_tags_to_count: tag_string += ' (+' + HC.ConvertIntToPrettyString( self._pending_tags_to_count[ tag ] ) + ')'
if tag in self._petitioned_tags_to_count: tag_string += ' (-' + HC.ConvertIntToPrettyString( self._petitioned_tags_to_count[ tag ] ) + ')'
sibling = siblings_manager.GetSibling( tag )
if sibling is not None: tag_string += ' (' + sibling + ')'
self._ordered_strings.append( tag_string )
self._strings_to_terms[ tag_string ] = tag
self._SortTags()
2013-02-19 00:11:43 +00:00
def _SortTags( self ):
if self._sort == CC.SORT_BY_LEXICOGRAPHIC_ASC: compare_function = lambda a, b: cmp( a, b )
elif self._sort == CC.SORT_BY_LEXICOGRAPHIC_DESC: compare_function = lambda a, b: cmp( b, a )
elif self._sort in ( CC.SORT_BY_INCIDENCE_ASC, CC.SORT_BY_INCIDENCE_DESC ):
tags_to_count = collections.defaultdict( lambda: 0 )
tags_to_count.update( self._current_tags_to_count )
2013-03-15 02:38:12 +00:00
for ( tag, count ) in self._pending_tags_to_count.items(): tags_to_count[ tag ] += count
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
if self._sort == CC.SORT_BY_INCIDENCE_ASC: compare_function = lambda a, b: cmp( ( tags_to_count[ self._strings_to_terms[ a ] ], a ), ( tags_to_count[ self._strings_to_terms[ b ] ], b ) )
elif self._sort == CC.SORT_BY_INCIDENCE_DESC: compare_function = lambda a, b: cmp( ( tags_to_count[ self._strings_to_terms[ b ] ], a ), ( tags_to_count[ self._strings_to_terms[ a ] ], b ) )
2013-02-19 00:11:43 +00:00
self._ordered_strings.sort( compare_function )
self._TextsHaveChanged()
def ChangeTagRepository( self, page_key, service_identifier ):
if page_key == self._page_key:
self._tag_service_identifier = service_identifier
if self._last_media is not None: self.SetTagsByMedia( self._page_key, self._last_media )
def SetSort( self, sort ):
self._sort = sort
self._SortTags()
def SetTags( self, current_tags_to_count, pending_tags_to_count, petitioned_tags_to_count ):
2013-07-10 20:25:57 +00:00
siblings_manager = HC.app.GetTagSiblingsManager()
2013-05-08 20:31:00 +00:00
current_tags_to_count = siblings_manager.CollapseTagsToCount( current_tags_to_count )
2013-08-28 21:31:52 +00:00
self._current_tags_to_count = current_tags_to_count
self._pending_tags_to_count = pending_tags_to_count
self._petitioned_tags_to_count = petitioned_tags_to_count
self._RecalcStrings()
def SetTagsByMedia( self, page_key, media, force_reload = False ):
if page_key == self._page_key:
2013-02-19 00:11:43 +00:00
2013-08-28 21:31:52 +00:00
media = set( media )
2013-02-19 00:11:43 +00:00
2013-08-28 21:31:52 +00:00
if force_reload:
( current_tags_to_count, deleted_tags_to_count, pending_tags_to_count, petitioned_tags_to_count ) = CC.GetMediasTagCount( media, self._tag_service_identifier )
self.SetTags( current_tags_to_count, pending_tags_to_count, petitioned_tags_to_count )
else:
2013-02-19 00:11:43 +00:00
2013-08-28 21:31:52 +00:00
if self._last_media is None: ( removees, adds ) = ( set(), media )
else:
removees = self._last_media.difference( media )
adds = media.difference( self._last_media )
2013-02-19 00:11:43 +00:00
2013-08-28 21:31:52 +00:00
siblings_manager = HC.app.GetTagSiblingsManager()
2013-02-19 00:11:43 +00:00
2013-08-28 21:31:52 +00:00
( current_tags_to_count, deleted_tags_to_count, pending_tags_to_count, petitioned_tags_to_count ) = CC.GetMediasTagCount( removees, self._tag_service_identifier )
2013-05-08 20:31:00 +00:00
2013-08-28 21:31:52 +00:00
current_tags_to_count = siblings_manager.CollapseTagsToCount( current_tags_to_count )
2013-05-08 20:31:00 +00:00
2013-08-28 21:31:52 +00:00
self._current_tags_to_count.subtract( current_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 ) = CC.GetMediasTagCount( adds, self._tag_service_identifier )
current_tags_to_count = siblings_manager.CollapseTagsToCount( current_tags_to_count )
self._current_tags_to_count.update( current_tags_to_count )
self._pending_tags_to_count.update( pending_tags_to_count )
self._petitioned_tags_to_count.update( petitioned_tags_to_count )
2013-02-19 00:11:43 +00:00
self._last_media = media
2013-08-28 21:31:52 +00:00
self._RecalcStrings()
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
class TagsBoxCPPWithSorter( StaticBox ):
2013-02-19 00:11:43 +00:00
def __init__( self, parent, page_key ):
2013-03-15 02:38:12 +00:00
StaticBox.__init__( self, parent, 'selection tags' )
2013-02-19 00:11:43 +00:00
self._sorter = wx.Choice( self )
self._sorter.Append( 'lexicographic (a-z)', CC.SORT_BY_LEXICOGRAPHIC_ASC )
self._sorter.Append( 'lexicographic (z-a)', CC.SORT_BY_LEXICOGRAPHIC_DESC )
self._sorter.Append( 'incidence (desc)', CC.SORT_BY_INCIDENCE_DESC )
self._sorter.Append( 'incidence (asc)', CC.SORT_BY_INCIDENCE_ASC )
2013-08-14 20:21:49 +00:00
if HC.options[ 'default_tag_sort' ] == CC.SORT_BY_LEXICOGRAPHIC_ASC: self._sorter.Select( 0 )
elif HC.options[ 'default_tag_sort' ] == CC.SORT_BY_LEXICOGRAPHIC_DESC: self._sorter.Select( 1 )
elif HC.options[ 'default_tag_sort' ] == CC.SORT_BY_INCIDENCE_DESC: self._sorter.Select( 2 )
elif HC.options[ 'default_tag_sort' ] == CC.SORT_BY_INCIDENCE_ASC: self._sorter.Select( 3 )
2013-02-19 00:11:43 +00:00
self._sorter.Bind( wx.EVT_CHOICE, self.EventSort )
self._tags_box = TagsBoxCPP( self, page_key )
2013-03-15 02:38:12 +00:00
self.AddF( self._sorter, FLAGS_EXPAND_PERPENDICULAR )
self.AddF( self._tags_box, FLAGS_EXPAND_BOTH_WAYS )
2013-02-19 00:11:43 +00:00
def EventSort( self, event ):
selection = self._sorter.GetSelection()
if selection != wx.NOT_FOUND:
sort = self._sorter.GetClientData( selection )
self._tags_box.SetSort( sort )
class TagsBoxFlat( TagsBox ):
def __init__( self, parent, removed_callable ):
TagsBox.__init__( self, parent )
self._removed_callable = removed_callable
2013-05-29 20:19:54 +00:00
self._tags = set()
2013-02-19 00:11:43 +00:00
def _RecalcTags( self ):
2013-05-08 20:31:00 +00:00
self._strings_to_terms = {}
2013-07-10 20:25:57 +00:00
siblings_manager = HC.app.GetTagSiblingsManager()
2013-05-08 20:31:00 +00:00
for tag in self._tags:
tag_string = tag
sibling = siblings_manager.GetSibling( tag )
if sibling is not None: tag_string += ' (' + sibling + ')'
self._strings_to_terms[ tag_string ] = tag
self._ordered_strings = self._strings_to_terms.keys()
2013-02-19 00:11:43 +00:00
self._ordered_strings.sort()
self._TextsHaveChanged()
2013-05-29 20:19:54 +00:00
def _Activate( self, s, tag ):
2013-02-19 00:11:43 +00:00
2013-05-29 20:19:54 +00:00
if tag in self._tags:
2013-05-08 20:31:00 +00:00
2013-05-29 20:19:54 +00:00
self._tags.discard( tag )
2013-05-08 20:31:00 +00:00
self._RecalcTags()
self._removed_callable( tag )
2013-02-19 00:11:43 +00:00
2013-05-29 20:19:54 +00:00
def AddTag( self, tag, parents ):
2013-02-19 00:11:43 +00:00
2013-05-29 20:19:54 +00:00
if tag in self._tags: self._tags.discard( tag )
else:
self._tags.add( tag )
self._tags.update( parents )
2013-02-19 00:11:43 +00:00
self._RecalcTags()
2013-05-08 20:31:00 +00:00
def GetTags( self ): return self._tags
2013-02-19 00:11:43 +00:00
def SetTags( self, tags ):
2013-05-08 20:31:00 +00:00
self._tags = tags
2013-02-19 00:11:43 +00:00
self._RecalcTags()
class TagsBoxManage( TagsBox ):
def __init__( self, parent, callable, current_tags, deleted_tags, pending_tags, petitioned_tags ):
TagsBox.__init__( self, parent )
self._callable = callable
self._current_tags = set( current_tags )
self._deleted_tags = set( deleted_tags )
self._pending_tags = set( pending_tags )
self._petitioned_tags = set( petitioned_tags )
self._RebuildTagStrings()
2013-05-08 20:31:00 +00:00
def _Activate( self, s, term ): self._callable( term )
2013-02-19 00:11:43 +00:00
def _RebuildTagStrings( self ):
2013-07-10 20:25:57 +00:00
siblings_manager = HC.app.GetTagSiblingsManager()
2013-05-08 20:31:00 +00:00
2013-07-03 18:49:26 +00:00
all_tags = self._current_tags | self._deleted_tags | self._pending_tags | self._petitioned_tags
2013-02-19 00:11:43 +00:00
self._ordered_strings = []
self._strings_to_terms = {}
for tag in all_tags:
2013-06-12 22:53:31 +00:00
if tag in self._petitioned_tags: prefix = HC.ConvertStatusToPrefix( HC.PETITIONED )
elif tag in self._current_tags: prefix = HC.ConvertStatusToPrefix( HC.CURRENT )
elif tag in self._pending_tags:
if tag in self._deleted_tags: prefix = HC.ConvertStatusToPrefix( HC.DELETED_PENDING )
else: prefix = HC.ConvertStatusToPrefix( HC.PENDING )
else: prefix = HC.ConvertStatusToPrefix( HC.DELETED )
2013-06-19 20:25:06 +00:00
tag_string = prefix + tag
2013-02-19 00:11:43 +00:00
2013-05-08 20:31:00 +00:00
sibling = siblings_manager.GetSibling( tag )
if sibling is not None: tag_string += ' (' + sibling + ')'
2013-02-19 00:11:43 +00:00
self._ordered_strings.append( tag_string )
self._strings_to_terms[ tag_string ] = tag
self._ordered_strings.sort()
self._TextsHaveChanged()
def PetitionTag( self, tag ):
self._petitioned_tags.add( tag )
self._RebuildTagStrings()
def PendTag( self, tag ):
self._pending_tags.add( tag )
self._RebuildTagStrings()
def RescindPetition( self, tag ):
self._petitioned_tags.discard( tag )
self._RebuildTagStrings()
def RescindPend( self, tag ):
self._pending_tags.discard( tag )
self._RebuildTagStrings()
class TagsBoxOptions( TagsBox ):
def __init__( self, parent, initial_namespace_colours ):
TagsBox.__init__( self, parent )
self._namespace_colours = dict( initial_namespace_colours )
for namespace in self._namespace_colours:
if namespace is None: namespace_string = 'default namespace:tag'
elif namespace == '': namespace_string = 'unnamespaced tag'
else: namespace_string = namespace + ':tag'
self._ordered_strings.append( namespace_string )
self._strings_to_terms[ namespace_string ] = namespace
self._TextsHaveChanged()
2013-05-08 20:31:00 +00:00
def _Activate( self, s, term ): self.RemoveNamespace( term )
2013-02-19 00:11:43 +00:00
def _GetNamespaceColours( self ): return self._namespace_colours
def SetNamespaceColour( self, namespace, colour ):
if namespace not in self._namespace_colours:
namespace_string = namespace + ':tag'
self._ordered_strings.append( namespace_string )
self._strings_to_terms[ namespace_string ] = namespace
self._ordered_strings.sort()
self._namespace_colours[ namespace ] = colour.Get()
self._TextsHaveChanged()
def GetNamespaceColours( self ): return self._namespace_colours
def GetSelectedNamespaceColour( self ):
if self._current_selected_index is not None:
namespace_string = self._ordered_strings[ self._current_selected_index ]
namespace = self._strings_to_terms[ namespace_string ]
( r, g, b ) = self._namespace_colours[ namespace ]
colour = wx.Colour( r, g, b )
return ( namespace, colour )
return None
def RemoveNamespace( self, namespace ):
if namespace is not None and namespace != '':
namespace_string = namespace + ':tag'
self._ordered_strings.remove( namespace_string )
del self._strings_to_terms[ namespace_string ]
del self._namespace_colours[ namespace ]
self._TextsHaveChanged()
class TagsBoxPredicates( TagsBox ):
def __init__( self, parent, page_key, initial_predicates = [] ):
TagsBox.__init__( self, parent, min_height = 100 )
self._page_key = page_key
if len( initial_predicates ) > 0:
for predicate in initial_predicates:
2013-04-10 18:10:37 +00:00
predicate_string = predicate.GetUnicode()
self._ordered_strings.append( predicate_string )
self._strings_to_terms[ predicate_string ] = predicate
2013-02-19 00:11:43 +00:00
self._TextsHaveChanged()
2013-05-08 20:31:00 +00:00
def _Activate( self, s, term ): HC.pubsub.pub( 'remove_predicate', self._page_key, term )
2013-02-19 00:11:43 +00:00
2013-03-27 20:02:51 +00:00
def AddPredicate( self, predicate ):
2013-02-19 00:11:43 +00:00
2013-03-27 20:02:51 +00:00
predicate = predicate.GetCountlessCopy()
2013-02-19 00:11:43 +00:00
2013-03-27 20:02:51 +00:00
predicate_string = predicate.GetUnicode()
2013-02-19 00:11:43 +00:00
2013-04-03 20:56:07 +00:00
inbox_predicate = HC.SYSTEM_PREDICATE_INBOX
archive_predicate = HC.SYSTEM_PREDICATE_ARCHIVE
2013-02-19 00:11:43 +00:00
2013-03-27 20:02:51 +00:00
if predicate == inbox_predicate and self.HasPredicate( archive_predicate ): self.RemovePredicate( archive_predicate )
elif predicate == archive_predicate and self.HasPredicate( inbox_predicate ): self.RemovePredicate( inbox_predicate )
2013-04-03 20:56:07 +00:00
local_predicate = HC.SYSTEM_PREDICATE_LOCAL
not_local_predicate = HC.SYSTEM_PREDICATE_NOT_LOCAL
2013-03-27 20:02:51 +00:00
if predicate == local_predicate and self.HasPredicate( not_local_predicate ): self.RemovePredicate( not_local_predicate )
elif predicate == not_local_predicate and self.HasPredicate( local_predicate ): self.RemovePredicate( local_predicate )
self._ordered_strings.append( predicate_string )
self._strings_to_terms[ predicate_string ] = predicate
2013-02-19 00:11:43 +00:00
self._ordered_strings.sort()
self._TextsHaveChanged()
2013-03-27 20:02:51 +00:00
def GetPredicates( self ): return self._strings_to_terms.values()
2013-02-19 00:11:43 +00:00
2013-03-27 20:02:51 +00:00
def HasPredicate( self, predicate ): return predicate in self._strings_to_terms.values()
2013-02-19 00:11:43 +00:00
def RemovePredicate( self, predicate ):
2013-03-27 20:02:51 +00:00
for ( s, existing_predicate ) in self._strings_to_terms.items():
if existing_predicate == predicate:
self._ordered_strings.remove( s )
del self._strings_to_terms[ s ]
self._TextsHaveChanged()
break
2013-02-19 00:11:43 +00:00