hydrus/include/ClientGUICommon.py

3229 lines
93 KiB
Python
Raw Normal View History

2015-03-18 21:46:29 +00:00
import ClientCaches
import ClientData
2013-02-19 00:11:43 +00:00
import ClientConstants as CC
2016-11-16 20:21:43 +00:00
import ClientGUIMenus
2017-05-24 20:28:24 +00:00
import ClientGUITopLevelWindows
2017-08-09 21:33:51 +00:00
import ClientMedia
2015-05-13 20:22:39 +00:00
import ClientRatings
2017-03-08 23:23:12 +00:00
import ClientThreading
2017-03-15 20:13:04 +00:00
import HydrusConstants as HC
import HydrusData
import HydrusExceptions
2017-05-10 21:33:58 +00:00
import HydrusGlobals as HG
2017-12-13 22:33:07 +00:00
import HydrusText
2013-02-19 00:11:43 +00:00
import os
2013-08-14 20:21:49 +00:00
import sys
2017-02-01 21:11:17 +00:00
import threading
2013-02-19 00:11:43 +00:00
import time
import traceback
import wx
2016-05-18 20:07:14 +00:00
import wx.lib.newevent
2013-02-19 00:11:43 +00:00
ID_TIMER_ANIMATED = wx.NewId()
ID_TIMER_SLIDESHOW = wx.NewId()
ID_TIMER_MEDIA_INFO_DISPLAY = wx.NewId()
2018-01-17 22:52:10 +00:00
def ApplyContentApplicationCommandToMedia( parent, command, media ):
data = command.GetData()
( service_key, content_type, action, value ) = data
try:
service = HG.client_controller.services_manager.GetService( service_key )
except HydrusExceptions.DataMissing:
command_processed = False
return command_processed
service_type = service.GetServiceType()
hashes = set()
for m in media:
hashes.update( m.GetHashes() )
if service_type in HC.TAG_SERVICES:
tag = value
can_add = False
can_pend = False
can_delete = False
can_petition = True
can_rescind_pend = False
can_rescind_petition = False
for m in media:
tags_manager = m.GetTagsManager()
current = tags_manager.GetCurrent( service_key )
pending = tags_manager.GetPending( service_key )
petitioned = tags_manager.GetPetitioned( service_key )
if tag not in current:
can_add = True
if tag not in current and tag not in pending:
can_pend = True
if tag in current and action == HC.CONTENT_UPDATE_FLIP:
can_delete = True
if tag in current and tag not in petitioned and action == HC.CONTENT_UPDATE_FLIP:
can_petition = True
if tag in pending and action == HC.CONTENT_UPDATE_FLIP:
can_rescind_pend = True
if tag in petitioned:
can_rescind_petition = True
if service_type == HC.LOCAL_TAG:
tags = [ tag ]
if can_add:
content_update_action = HC.CONTENT_UPDATE_ADD
tag_parents_manager = HG.client_controller.GetManager( 'tag_parents' )
parents = tag_parents_manager.GetParents( service_key, tag )
tags.extend( parents )
elif can_delete:
content_update_action = HC.CONTENT_UPDATE_DELETE
else:
return True
rows = [ ( tag, hashes ) for tag in tags ]
else:
if can_rescind_petition:
content_update_action = HC.CONTENT_UPDATE_RESCIND_PETITION
rows = [ ( tag, hashes ) ]
elif can_pend:
tags = [ tag ]
content_update_action = HC.CONTENT_UPDATE_PEND
tag_parents_manager = HG.client_controller.GetManager( 'tag_parents' )
parents = tag_parents_manager.GetParents( service_key, tag )
tags.extend( parents )
rows = [ ( tag, hashes ) for tag in tags ]
elif can_rescind_pend:
content_update_action = HC.CONTENT_UPDATE_RESCIND_PEND
rows = [ ( tag, hashes ) ]
elif can_petition:
message = 'Enter a reason for this tag to be removed. A janitor will review your petition.'
import ClientGUIDialogs
with ClientGUIDialogs.DialogTextEntry( parent, message ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
content_update_action = HC.CONTENT_UPDATE_PETITION
rows = [ ( dlg.GetValue(), tag, hashes ) ]
else:
return True
else:
return True
content_updates = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, content_update_action, row ) for row in rows ]
elif service_type in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ):
rating = value
can_set = False
can_unset = False
for m in media:
ratings_manager = m.GetRatingsManager()
current_rating = ratings_manager.GetRating( service_key )
if current_rating == rating and action == HC.CONTENT_UPDATE_FLIP:
can_unset = True
else:
can_set = True
if can_set:
row = ( rating, hashes )
elif can_unset:
row = ( None, hashes )
else:
return True
content_updates = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, row ) ]
else:
return False
HG.client_controller.Write( 'content_updates', { service_key : content_updates } )
return True
2017-09-06 20:18:20 +00:00
def GetFocusTLP():
2017-04-05 21:16:40 +00:00
focus = wx.Window.FindFocus()
2017-09-06 20:18:20 +00:00
return GetTLP( focus )
def GetTLP( window ):
if window is None:
2017-04-05 21:16:40 +00:00
2017-09-06 20:18:20 +00:00
return None
2017-04-05 21:16:40 +00:00
2017-09-06 20:18:20 +00:00
elif isinstance( window, wx.TopLevelWindow ):
return window
else:
return window.GetTopLevelParent()
2017-04-05 21:16:40 +00:00
2017-04-19 20:58:30 +00:00
2017-11-22 21:03:07 +00:00
def GetTLPParents( window ):
if not isinstance( window, wx.TopLevelWindow ):
window = GetTLP( window )
parents = []
parent = window.GetParent()
while parent is not None:
parents.append( parent )
parent = parent.GetParent()
return parents
2017-11-29 21:48:23 +00:00
def GetXYTopTLP( screen_position ):
2017-11-22 21:03:07 +00:00
tlps = wx.GetTopLevelWindows()
2017-11-29 21:48:23 +00:00
hittest_tlps = [ tlp for tlp in tlps if tlp.HitTest( tlp.ScreenToClient( screen_position ) ) == wx.HT_WINDOW_INSIDE and tlp.IsShown() ]
2017-11-22 21:03:07 +00:00
if len( hittest_tlps ) == 0:
return None
most_childish = hittest_tlps[0]
for tlp in hittest_tlps[1:]:
if most_childish in GetTLPParents( tlp ):
most_childish = tlp
return most_childish
2018-01-03 22:37:30 +00:00
def IsWXAncestor( child, ancestor, through_tlws = False ):
2017-04-19 20:58:30 +00:00
2017-09-06 20:18:20 +00:00
parent = child
2017-04-19 20:58:30 +00:00
2018-01-03 22:37:30 +00:00
if through_tlws:
2017-04-05 21:16:40 +00:00
2018-01-03 22:37:30 +00:00
while not parent is None:
2017-04-05 21:16:40 +00:00
2018-01-03 22:37:30 +00:00
if parent == ancestor:
return True
parent = parent.GetParent()
2017-04-05 21:16:40 +00:00
2018-01-03 22:37:30 +00:00
else:
while not isinstance( parent, wx.TopLevelWindow ):
if parent == ancestor:
return True
parent = parent.GetParent()
2017-04-19 20:58:30 +00:00
2017-04-05 21:16:40 +00:00
return False
2017-09-06 20:18:20 +00:00
def NotebookScreenToHitTest( notebook, screen_position ):
2017-04-19 20:58:30 +00:00
2017-09-06 20:18:20 +00:00
if HC.PLATFORM_OSX:
2017-04-05 21:16:40 +00:00
2017-09-06 20:18:20 +00:00
# OS X has some unusual coordinates for its notebooks
# the notebook tabs are not considered to be in the client area (they are actually negative on getscreenposition())
# its hittest works on window coords, not client coords
# hence to get hittest position, we get our parent's client position and adjust by our given position in that
2017-04-05 21:16:40 +00:00
2017-09-06 20:18:20 +00:00
# this also seems to cause menus popped on notebooks to spawn high and left, wew
2017-04-05 21:16:40 +00:00
2017-09-06 20:18:20 +00:00
( my_x, my_y ) = notebook.GetPosition()
( p_x, p_y ) = notebook.GetParent().ScreenToClient( wx.GetMousePosition() )
position = ( p_x - my_x, p_y - my_y )
2017-04-05 21:16:40 +00:00
else:
2017-09-06 20:18:20 +00:00
position = notebook.ScreenToClient( screen_position )
2017-04-05 21:16:40 +00:00
2017-09-06 20:18:20 +00:00
return notebook.HitTest( position )
2018-01-10 22:41:51 +00:00
def SetBitmapButtonBitmap( button, bitmap ):
# the button's bitmap, retrieved via GetBitmap, is not the same as the one we gave it!
# hence testing bitmap vs that won't work to save time on an update loop, so we'll just save it here custom
# this isn't a big memory deal for our purposes since they are small and mostly if not all from the GlobalBMPs library so shared anyway
if hasattr( button, 'last_bitmap' ):
if button.last_bitmap == bitmap:
return
button.SetBitmap( bitmap )
button.last_bitmap = bitmap
2017-04-19 20:58:30 +00:00
def TLPHasFocus( window ):
focus_tlp = GetFocusTLP()
window_tlp = GetTLP( window )
2017-04-05 21:16:40 +00:00
return window_tlp == focus_tlp
def WindowHasFocus( window ):
focus = wx.Window.FindFocus()
if focus is None:
return False
return window == focus
2017-09-06 20:18:20 +00:00
def WindowOrAnyTLPChildHasFocus( window ):
2017-04-05 21:16:40 +00:00
2017-09-06 20:18:20 +00:00
focus = wx.Window.FindFocus()
2017-04-05 21:16:40 +00:00
2017-09-06 20:18:20 +00:00
while focus is not None:
2017-04-05 21:16:40 +00:00
2017-09-06 20:18:20 +00:00
if focus == window:
2017-04-05 21:16:40 +00:00
return True
2017-09-06 20:18:20 +00:00
focus = focus.GetParent()
return False
def WindowOrSameTLPChildHasFocus( window ):
focus = wx.Window.FindFocus()
while focus is not None:
if focus == window:
return True
if isinstance( focus, wx.TopLevelWindow ):
return False
focus = focus.GetParent()
2017-04-05 21:16:40 +00:00
return False
2016-08-31 19:55:14 +00:00
def WrapInGrid( parent, rows, expand_text = False ):
2018-01-03 22:37:30 +00:00
gridbox = wx.FlexGridSizer( 2 )
2016-08-31 19:55:14 +00:00
if expand_text:
gridbox.AddGrowableCol( 0, 1 )
text_flags = CC.FLAGS_VCENTER_EXPAND_DEPTH_ONLY # Trying to expand both ways nixes the center. This seems to work right.
control_flags = CC.FLAGS_VCENTER
2016-09-07 20:01:05 +00:00
sizer_flags = CC.FLAGS_SIZER_VCENTER
2016-08-31 19:55:14 +00:00
else:
gridbox.AddGrowableCol( 1, 1 )
text_flags = CC.FLAGS_VCENTER
control_flags = CC.FLAGS_EXPAND_BOTH_WAYS
2016-09-07 20:01:05 +00:00
sizer_flags = CC.FLAGS_EXPAND_SIZER_BOTH_WAYS
2016-08-31 19:55:14 +00:00
for ( text, control ) in rows:
2016-09-07 20:01:05 +00:00
if isinstance( control, wx.Sizer ):
cflags = sizer_flags
else:
cflags = control_flags
2017-04-19 20:58:30 +00:00
st = BetterStaticText( parent, text )
2018-01-03 22:37:30 +00:00
gridbox.Add( st, text_flags )
gridbox.Add( control, cflags )
2016-08-31 19:55:14 +00:00
return gridbox
2018-02-28 22:30:36 +00:00
def WrapInText( control, parent, text, colour = None ):
2016-08-17 20:07:22 +00:00
hbox = wx.BoxSizer( wx.HORIZONTAL )
2017-04-19 20:58:30 +00:00
st = BetterStaticText( parent, text )
2018-02-28 22:30:36 +00:00
if colour is not None:
st.SetForegroundColour( colour )
2018-01-03 22:37:30 +00:00
hbox.Add( st, CC.FLAGS_VCENTER )
hbox.Add( control, CC.FLAGS_EXPAND_BOTH_WAYS )
2016-08-17 20:07:22 +00:00
return hbox
2017-01-18 22:52:39 +00:00
class BetterBitmapButton( wx.BitmapButton ):
2017-01-25 22:56:55 +00:00
def __init__( self, parent, bitmap, func, *args, **kwargs ):
2017-01-18 22:52:39 +00:00
wx.BitmapButton.__init__( self, parent, bitmap = bitmap )
2017-01-25 22:56:55 +00:00
self._func = func
2017-01-18 22:52:39 +00:00
self._args = args
self._kwargs = kwargs
self.Bind( wx.EVT_BUTTON, self.EventButton )
def EventButton( self, event ):
2017-01-25 22:56:55 +00:00
self._func( *self._args, **self._kwargs )
2017-01-18 22:52:39 +00:00
2016-10-19 20:02:56 +00:00
class BetterButton( wx.Button ):
2017-01-25 22:56:55 +00:00
def __init__( self, parent, label, func, *args, **kwargs ):
2016-10-19 20:02:56 +00:00
2017-04-19 20:58:30 +00:00
wx.Button.__init__( self, parent, style = wx.BU_EXACTFIT )
self.SetLabelText( label )
2016-10-19 20:02:56 +00:00
2017-01-25 22:56:55 +00:00
self._func = func
2016-12-07 22:12:52 +00:00
self._args = args
self._kwargs = kwargs
2016-10-19 20:02:56 +00:00
self.Bind( wx.EVT_BUTTON, self.EventButton )
def EventButton( self, event ):
2017-01-25 22:56:55 +00:00
self._func( *self._args, **self._kwargs )
2016-10-19 20:02:56 +00:00
2017-05-31 21:50:53 +00:00
class BetterCheckListBox( wx.CheckListBox ):
def GetChecked( self ):
result = [ self.GetClientData( index ) for index in wx.CheckListBox.GetChecked( self ) ]
return result
2016-10-19 20:02:56 +00:00
class BetterChoice( wx.Choice ):
2017-10-11 17:38:14 +00:00
def Append( self, display_string, client_data ):
wx.Choice.Append( self, display_string, client_data )
if self.GetCount() == 1:
self.Select( 0 )
2016-10-19 20:02:56 +00:00
def GetChoice( self ):
selection = self.GetSelection()
if selection != wx.NOT_FOUND: return self.GetClientData( selection )
2016-11-09 23:13:22 +00:00
elif self.GetCount() > 0: return self.GetClientData( 0 )
else: return None
2016-10-19 20:02:56 +00:00
def SelectClientData( self, client_data ):
for i in range( self.GetCount() ):
if client_data == self.GetClientData( i ):
self.Select( i )
return
2016-11-09 23:13:22 +00:00
if self.GetCount() > 0:
self.Select( 0 )
2016-10-19 20:02:56 +00:00
2017-01-04 22:48:23 +00:00
class BetterRadioBox( wx.RadioBox ):
def __init__( self, *args, **kwargs ):
self._indices_to_data = { i : data for ( i, ( s, data ) ) in enumerate( kwargs[ 'choices' ] ) }
kwargs[ 'choices' ] = [ s for ( s, data ) in kwargs[ 'choices' ] ]
wx.RadioBox.__init__( self, *args, **kwargs )
def GetChoice( self ):
index = self.GetSelection()
return self._indices_to_data[ index ]
2017-04-19 20:58:30 +00:00
class BetterStaticText( wx.StaticText ):
2017-04-26 21:58:12 +00:00
def __init__( self, parent, label = None, **kwargs ):
2017-04-19 20:58:30 +00:00
2017-04-26 21:58:12 +00:00
wx.StaticText.__init__( self, parent, **kwargs )
2017-04-19 20:58:30 +00:00
if label is not None:
# to escape mnemonic '&' swallowing
self.SetLabelText( label )
# at some point, rewrite this to be a control that'll produce a custom geteffectiveminsize and use wx.lib.wordwrap to dc draw the text
# st.Wrap is a pain to deal with here, seems to sometimes/always not be able to increase after an initial non-zero call
2017-07-12 20:03:45 +00:00
def SetLabelText( self, text ):
if text != self.GetLabelText():
wx.StaticText.SetLabelText( self, text )
2013-03-15 02:38:12 +00:00
class BufferedWindow( wx.Window ):
def __init__( self, *args, **kwargs ):
wx.Window.__init__( self, *args, **kwargs )
if 'size' in kwargs:
( x, y ) = kwargs[ 'size' ]
2018-01-03 22:37:30 +00:00
self._canvas_bmp = wx.Bitmap( x, y, 24 )
2013-03-15 02:38:12 +00:00
2015-11-18 22:44:07 +00:00
else:
2018-01-03 22:37:30 +00:00
self._canvas_bmp = wx.Bitmap( 20, 20, 24 )
2015-11-18 22:44:07 +00:00
self._dirty = True
2013-03-15 02:38:12 +00:00
self.Bind( wx.EVT_PAINT, self.EventPaint )
self.Bind( wx.EVT_SIZE, self.EventResize )
2015-02-25 19:34:30 +00:00
self.Bind( wx.EVT_ERASE_BACKGROUND, self.EventEraseBackground )
2013-03-15 02:38:12 +00:00
2015-11-18 22:44:07 +00:00
def _Draw( self, dc ):
raise NotImplementedError()
2013-03-15 02:38:12 +00:00
2015-02-25 19:34:30 +00:00
def EventEraseBackground( self, event ): pass
2015-11-18 22:44:07 +00:00
def EventPaint( self, event ):
dc = wx.BufferedPaintDC( self, self._canvas_bmp )
if self._dirty:
self._Draw( dc )
2013-03-15 02:38:12 +00:00
def EventResize( self, event ):
( my_width, my_height ) = self.GetClientSize()
( current_bmp_width, current_bmp_height ) = self._canvas_bmp.GetSize()
2015-11-18 22:44:07 +00:00
if my_width != current_bmp_width or my_height != current_bmp_height:
2018-01-03 22:37:30 +00:00
self._canvas_bmp = wx.Bitmap( my_width, my_height, 24 )
2015-11-18 22:44:07 +00:00
self._dirty = True
2015-12-02 22:32:18 +00:00
self.Refresh()
2013-03-15 02:38:12 +00:00
2015-05-13 20:22:39 +00:00
class BufferedWindowIcon( BufferedWindow ):
def __init__( self, parent, bmp ):
BufferedWindow.__init__( self, parent, size = bmp.GetSize() )
self._bmp = bmp
2015-11-18 22:44:07 +00:00
def _Draw( self, dc ):
2015-05-13 20:22:39 +00:00
background_colour = self.GetParent().GetBackgroundColour()
dc.SetBackground( wx.Brush( background_colour ) )
dc.Clear()
2015-11-18 22:44:07 +00:00
dc.DrawBitmap( self._bmp, 0, 0 )
self._dirty = False
2015-05-13 20:22:39 +00:00
2018-01-03 22:37:30 +00:00
class CheckboxCollect( wx.ComboCtrl ):
2013-03-15 02:38:12 +00:00
2013-04-03 20:56:07 +00:00
def __init__( self, parent, page_key = None ):
2013-03-15 02:38:12 +00:00
2018-01-03 22:37:30 +00:00
wx.ComboCtrl.__init__( self, parent, style = wx.CB_READONLY )
2013-03-15 02:38:12 +00:00
self._page_key = page_key
2017-08-09 21:33:51 +00:00
self._collect_by = HC.options[ 'default_collect' ]
popup = self._Popup( self._collect_by )
2013-03-15 02:38:12 +00:00
#self.UseAltPopupWindow( True )
self.SetPopupControl( popup )
2017-08-09 21:33:51 +00:00
self.SetValue( 'no collections' )
2017-05-24 20:28:24 +00:00
def GetChoice( self ):
2013-12-04 22:44:16 +00:00
2017-05-24 20:28:24 +00:00
return self._collect_by
2013-12-04 22:44:16 +00:00
2013-03-15 02:38:12 +00:00
2017-05-24 20:28:24 +00:00
def SetCollectTypes( self, collect_by, description ):
2013-03-15 02:38:12 +00:00
2017-05-24 20:28:24 +00:00
self._collect_by = collect_by
self.SetValue( description )
2013-03-15 02:38:12 +00:00
2017-05-10 21:33:58 +00:00
HG.client_controller.pub( 'collect_media', self._page_key, self._collect_by )
2013-03-15 02:38:12 +00:00
2018-01-03 22:37:30 +00:00
class _Popup( wx.ComboPopup ):
2013-03-15 02:38:12 +00:00
2017-08-09 21:33:51 +00:00
def __init__( self, collect_by ):
2013-03-15 02:38:12 +00:00
2018-01-03 22:37:30 +00:00
wx.ComboPopup.__init__( self )
2013-03-15 02:38:12 +00:00
2017-08-09 21:33:51 +00:00
self._initial_collect_by = collect_by
2018-01-10 22:41:51 +00:00
self._control = None
2013-03-15 02:38:12 +00:00
2013-03-23 17:57:29 +00:00
def Create( self, parent ):
2018-01-03 22:37:30 +00:00
self._control = self._Control( parent, self.GetComboCtrl(), self._initial_collect_by )
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
2017-05-24 20:28:24 +00:00
def GetControl( self ):
return self._control
2013-03-15 02:38:12 +00:00
2018-01-10 22:41:51 +00:00
def GetStringValue( self ):
# this is an abstract method that provides the strin to put in the comboctrl
# I've never used/needed it, but one user reported getting the NotImplemented thing by repeatedly clicking, so let's add it anyway
if self._control is None:
return 'initialising'
else:
return self._control.GetDescription()
2013-03-23 17:57:29 +00:00
class _Control( wx.CheckListBox ):
2017-08-09 21:33:51 +00:00
def __init__( self, parent, special_parent, collect_by ):
2017-05-24 20:28:24 +00:00
text_and_data_tuples = set()
sort_by = HC.options[ 'sort_by' ]
for ( sort_by_type, namespaces ) in sort_by:
text_and_data_tuples.update( namespaces )
text_and_data_tuples = list( [ ( namespace, ( 'namespace', namespace ) ) for namespace in text_and_data_tuples ] )
text_and_data_tuples.sort()
2017-06-28 20:23:21 +00:00
ratings_services = HG.client_controller.services_manager.GetServices( ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ) )
2013-04-03 20:56:07 +00:00
2017-05-24 20:28:24 +00:00
for ratings_service in ratings_services:
text_and_data_tuples.append( ( ratings_service.GetName(), ( 'rating', ratings_service.GetServiceKey() ) ) )
texts = [ text for ( text, data ) in text_and_data_tuples ] # we do this so it sizes its height properly on init
2013-04-03 20:56:07 +00:00
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()
2017-05-24 20:28:24 +00:00
for ( text, data ) in text_and_data_tuples:
self.Append( text, data )
2013-03-23 17:57:29 +00:00
self._special_parent = special_parent
2017-05-24 20:28:24 +00:00
self.Bind( wx.EVT_CHECKLISTBOX, self.EventChanged )
self.Bind( wx.EVT_LEFT_DOWN, self.EventLeftDown )
2017-08-09 21:33:51 +00:00
wx.CallAfter( self.SetValue, collect_by )
2017-05-24 20:28:24 +00:00
def _BroadcastCollect( self ):
( collect_by, description ) = self._GetValues()
self._special_parent.SetCollectTypes( collect_by, description )
def _GetValues( self ):
collect_by = []
for index in self.GetChecked():
2013-09-04 16:48:44 +00:00
2017-05-24 20:28:24 +00:00
collect_by.append( self.GetClientData( index ) )
2013-09-04 16:48:44 +00:00
2017-05-24 20:28:24 +00:00
collect_by_strings = self.GetCheckedStrings()
if len( collect_by ) > 0:
2013-09-04 16:48:44 +00:00
2017-05-24 20:28:24 +00:00
description = 'collect by ' + '-'.join( collect_by_strings )
else:
description = 'no collections'
2013-09-04 16:48:44 +00:00
2013-03-23 17:57:29 +00:00
2017-05-24 20:28:24 +00:00
return ( collect_by, description )
2013-03-23 17:57:29 +00:00
# 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 ):
2017-05-24 20:28:24 +00:00
self._BroadcastCollect()
2013-12-04 22:44:16 +00:00
2018-01-10 22:41:51 +00:00
def GetDescription( self ):
( collect_by, description ) = self._GetValues()
return description
2017-05-24 20:28:24 +00:00
def SetValue( self, collect_by ):
2013-12-04 22:44:16 +00:00
2017-05-24 20:28:24 +00:00
# an old possible value, now collapsed to []
if collect_by is None:
collect_by = []
desired_collect_by_rows = set( collect_by )
2013-04-03 20:56:07 +00:00
2017-05-24 20:28:24 +00:00
indices_to_check = []
2013-04-03 20:56:07 +00:00
2017-05-24 20:28:24 +00:00
for index in range( self.GetCount() ):
if self.GetClientData( index ) in desired_collect_by_rows:
indices_to_check.append( index )
2013-03-23 17:57:29 +00:00
2017-08-09 21:33:51 +00:00
if len( indices_to_check ) > 0:
self.SetChecked( indices_to_check )
self._BroadcastCollect()
2013-03-23 17:57:29 +00:00
2013-03-15 02:38:12 +00:00
2017-09-27 21:52:54 +00:00
class CheckboxManager( object ):
def GetCurrentValue( self ):
raise NotImplementedError()
def Invert( self ):
raise NotImplementedError()
class CheckboxManagerCalls( CheckboxManager ):
def __init__( self, invert_call, value_call ):
CheckboxManager.__init__( self )
self._invert_call = invert_call
self._value_call = value_call
def GetCurrentValue( self ):
return self._value_call()
def Invert( self ):
self._invert_call()
class CheckboxManagerOptions( CheckboxManager ):
def __init__( self, boolean_name ):
CheckboxManager.__init__( self )
self._boolean_name = boolean_name
def GetCurrentValue( self ):
2017-12-06 22:06:56 +00:00
new_options = HG.client_controller.new_options
2017-09-27 21:52:54 +00:00
return new_options.GetBoolean( self._boolean_name )
def Invert( self ):
2017-12-06 22:06:56 +00:00
new_options = HG.client_controller.new_options
2017-09-27 21:52:54 +00:00
new_options.InvertBoolean( self._boolean_name )
2017-08-09 21:33:51 +00:00
class ChoiceSort( wx.Panel ):
2013-03-15 02:38:12 +00:00
2017-08-09 21:33:51 +00:00
def __init__( self, parent, management_controller = None ):
2013-03-15 02:38:12 +00:00
2017-08-09 21:33:51 +00:00
wx.Panel.__init__( self, parent )
2013-03-15 02:38:12 +00:00
2017-08-09 21:33:51 +00:00
self._management_controller = management_controller
self._sort_type_choice = BetterChoice( self )
self._sort_asc_choice = BetterChoice( self )
2013-03-15 02:38:12 +00:00
2017-08-09 21:33:51 +00:00
asc_width = ClientData.ConvertTextToPixelWidth( self._sort_asc_choice, 15 )
2017-05-24 20:28:24 +00:00
2017-08-09 21:33:51 +00:00
self._sort_asc_choice.SetMinSize( ( asc_width, -1 ) )
2013-03-15 02:38:12 +00:00
2017-08-09 21:33:51 +00:00
sort_types = ClientData.GetSortTypeChoices()
for sort_type in sort_types:
2017-05-24 20:28:24 +00:00
2017-08-09 21:33:51 +00:00
example_sort = ClientMedia.MediaSort( sort_type, CC.SORT_ASC )
2013-03-15 02:38:12 +00:00
2017-08-09 21:33:51 +00:00
self._sort_type_choice.Append( example_sort.GetSortTypeString(), sort_type )
type_width = ClientData.ConvertTextToPixelWidth( self._sort_type_choice, 10 )
self._sort_type_choice.SetMinSize( ( type_width, -1 ) )
self._sort_asc_choice.Append( '', CC.SORT_ASC )
self._UpdateAscLabels()
#
hbox = wx.BoxSizer( wx.HORIZONTAL )
2018-01-03 22:37:30 +00:00
hbox.Add( self._sort_type_choice, CC.FLAGS_EXPAND_BOTH_WAYS )
hbox.Add( self._sort_asc_choice, CC.FLAGS_VCENTER )
2017-08-09 21:33:51 +00:00
self.SetSizer( hbox )
self._sort_type_choice.Bind( wx.EVT_CHOICE, self.EventSortTypeChoice )
self._sort_asc_choice.Bind( wx.EVT_CHOICE, self.EventSortAscChoice )
HG.client_controller.sub( self, 'ACollectHappened', 'collect_media' )
2017-10-18 19:41:25 +00:00
HG.client_controller.sub( self, 'BroadcastSort', 'do_page_sort' )
2017-08-09 21:33:51 +00:00
if self._management_controller is not None and self._management_controller.HasVariable( 'media_sort' ):
media_sort = self._management_controller.GetVariable( 'media_sort' )
try:
2014-08-27 22:15:22 +00:00
2017-08-09 21:33:51 +00:00
self.SetSort( media_sort )
2014-08-27 22:15:22 +00:00
2017-08-09 21:33:51 +00:00
except:
2014-08-27 22:15:22 +00:00
2017-08-09 21:33:51 +00:00
default_sort = ClientMedia.MediaSort( ( 'system', CC.SORT_FILES_BY_FILESIZE ), CC.SORT_ASC )
2014-08-27 22:15:22 +00:00
2017-08-09 21:33:51 +00:00
self.SetSort( default_sort )
2014-08-27 22:15:22 +00:00
2013-03-15 02:38:12 +00:00
2017-08-09 21:33:51 +00:00
def _BroadcastSort( self ):
media_sort = self._GetCurrentSort()
if self._management_controller is not None:
self._management_controller.SetVariable( 'media_sort', media_sort )
page_key = self._management_controller.GetKey( 'page' )
HG.client_controller.pub( 'sort_media', page_key, media_sort )
2013-03-15 02:38:12 +00:00
2017-08-09 21:33:51 +00:00
def _GetCurrentSort( self ):
2013-03-15 02:38:12 +00:00
2017-08-09 21:33:51 +00:00
sort_type = self._sort_type_choice.GetChoice()
sort_asc = self._sort_asc_choice.GetChoice()
media_sort = ClientMedia.MediaSort( sort_type, sort_asc )
return media_sort
2013-03-15 02:38:12 +00:00
2017-08-09 21:33:51 +00:00
def _UpdateAscLabels( self ):
2013-03-15 02:38:12 +00:00
2017-08-09 21:33:51 +00:00
media_sort = self._GetCurrentSort()
self._sort_asc_choice.Clear()
2013-03-15 02:38:12 +00:00
2017-08-09 21:33:51 +00:00
if media_sort.CanAsc():
( asc_str, desc_str ) = media_sort.GetSortAscStrings()
self._sort_asc_choice.Append( asc_str, CC.SORT_ASC )
self._sort_asc_choice.Append( desc_str, CC.SORT_DESC )
self._sort_asc_choice.SelectClientData( media_sort.sort_asc )
self._sort_asc_choice.Enable()
else:
self._sort_asc_choice.Append( '', CC.SORT_ASC )
2013-03-15 02:38:12 +00:00
2017-08-09 21:33:51 +00:00
self._sort_asc_choice.SelectClientData( CC.SORT_ASC )
2013-03-15 02:38:12 +00:00
2017-08-09 21:33:51 +00:00
self._sort_asc_choice.Disable()
2013-03-15 02:38:12 +00:00
def ACollectHappened( self, page_key, collect_by ):
2017-08-09 21:33:51 +00:00
if self._management_controller is not None:
2017-05-24 20:28:24 +00:00
2017-08-09 21:33:51 +00:00
my_page_key = self._management_controller.GetKey( 'page' )
if page_key == my_page_key:
self._BroadcastSort()
2017-05-24 20:28:24 +00:00
2017-10-18 19:41:25 +00:00
def BroadcastSort( self, page_key = None ):
if page_key is not None and page_key != self._management_controller.GetKey( 'page' ):
return
2017-05-24 20:28:24 +00:00
self._BroadcastSort()
2013-03-15 02:38:12 +00:00
2017-08-09 21:33:51 +00:00
def EventSortAscChoice( self, event ):
2013-03-15 02:38:12 +00:00
2017-08-09 21:33:51 +00:00
self._BroadcastSort()
def EventSortTypeChoice( self, event ):
self._UpdateAscLabels()
self._BroadcastSort()
def GetSort( self ):
return self._GetCurrentSort()
def SetSort( self, media_sort ):
self._sort_type_choice.SelectClientData( media_sort.sort_type )
self._sort_asc_choice.SelectClientData( media_sort.sort_asc )
self._UpdateAscLabels()
2013-03-15 02:38:12 +00:00
2018-03-07 22:48:29 +00:00
class AlphaColourControl( wx.Panel ):
def __init__( self, parent ):
wx.Panel.__init__( self, parent )
self._colour_picker = wx.ColourPickerCtrl( self )
self._alpha_selector = wx.SpinCtrl( self, min = 0, max = 255 )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.Add( self._colour_picker, CC.FLAGS_VCENTER )
hbox.Add( BetterStaticText( self, 'alpha: ' ), CC.FLAGS_VCENTER )
hbox.Add( self._alpha_selector, CC.FLAGS_VCENTER )
self.SetSizer( hbox )
def GetValue( self ):
colour = self._colour_picker.GetColour()
( r, g, b, a ) = colour.Get() # no alpha support here, so it'll be 255
a = self._alpha_selector.GetValue()
colour = wx.Colour( r, g, b, a )
return colour
def SetValue( self, colour ):
( r, g, b, a ) = colour.Get()
picker_colour = wx.Colour( r, g, b )
self._colour_picker.SetColour( picker_colour )
self._alpha_selector.SetValue( a )
2017-08-30 20:27:47 +00:00
class ExportPatternButton( BetterButton ):
2014-02-26 22:09:54 +00:00
def __init__( self, parent ):
2017-08-30 20:27:47 +00:00
BetterButton.__init__( self, parent, 'pattern shortcuts', self._Hit )
2014-02-26 22:09:54 +00:00
2017-08-30 20:27:47 +00:00
def _Hit( self ):
2014-02-26 22:09:54 +00:00
menu = wx.Menu()
2017-08-30 20:27:47 +00:00
ClientGUIMenus.AppendMenuLabel( menu, 'click on a phrase to copy to clipboard' )
2014-02-26 22:09:54 +00:00
2017-03-15 20:13:04 +00:00
ClientGUIMenus.AppendSeparator( menu )
2014-02-26 22:09:54 +00:00
2017-08-30 20:27:47 +00:00
ClientGUIMenus.AppendMenuItem( self, menu, 'the file\'s hash - {hash}', 'copy "{hash}" to the clipboard', HG.client_controller.pub, 'clipboard', 'text', '{hash}' )
ClientGUIMenus.AppendMenuItem( self, menu, 'all the file\'s tags - {tags}', 'copy "{tags}" to the clipboard', HG.client_controller.pub, 'clipboard', 'text', '{tags}' )
ClientGUIMenus.AppendMenuItem( self, menu, 'all the file\'s non-namespaced tags - {nn tags}', 'copy "{nn tags}" to the clipboard', HG.client_controller.pub, 'clipboard', 'text', '{nn tags}' )
2014-02-26 22:09:54 +00:00
2017-03-15 20:13:04 +00:00
ClientGUIMenus.AppendSeparator( menu )
2014-02-26 22:09:54 +00:00
2017-08-30 20:27:47 +00:00
ClientGUIMenus.AppendMenuItem( self, menu, u'all instances of a particular namespace - [\u2026]', u'copy "[\u2026]" to the clipboard', HG.client_controller.pub, 'clipboard', 'text', u'[\u2026]' )
2014-02-26 22:09:54 +00:00
2017-03-15 20:13:04 +00:00
ClientGUIMenus.AppendSeparator( menu )
2014-02-26 22:09:54 +00:00
2017-08-30 20:27:47 +00:00
ClientGUIMenus.AppendMenuItem( self, menu, u'a particular tag, if the file has it - (\u2026)', u'copy "(\u2026)" to the clipboard', HG.client_controller.pub, 'clipboard', 'text', u'(\u2026)' )
2014-02-26 22:09:54 +00:00
2017-05-10 21:33:58 +00:00
HG.client_controller.PopupMenu( self, menu )
2014-02-26 22:09:54 +00:00
2014-04-16 20:31:59 +00:00
class FitResistantStaticText( wx.StaticText ):
2014-04-23 20:56:12 +00:00
# this is a huge damn mess! I think I really need to be doing this inside or before the parent's fit, or something
def __init__( self, *args, **kwargs ):
wx.StaticText.__init__( self, *args, **kwargs )
self._wrap = 380
if 'label' in kwargs: self._last_label = kwargs[ 'label' ]
else: self._last_label = ''
2014-04-16 20:31:59 +00:00
def Wrap( self, width ):
2014-04-23 20:56:12 +00:00
self._wrap = width
wx.StaticText.Wrap( self, self._wrap )
2014-04-16 20:31:59 +00:00
( x, y ) = self.GetSize()
2014-04-23 20:56:12 +00:00
if x > self._wrap: x = self._wrap
if x < 150: x = 150
2014-04-16 20:31:59 +00:00
self.SetMinSize( ( x, y ) )
2014-04-23 20:56:12 +00:00
self.SetMaxSize( ( self._wrap, -1 ) )
2016-03-23 19:42:56 +00:00
def SetLabelText( self, label ):
2014-04-23 20:56:12 +00:00
if label != self._last_label:
self._last_label = label
2016-03-23 19:42:56 +00:00
wx.StaticText.SetLabelText( self, label )
2014-04-23 20:56:12 +00:00
self.Wrap( self._wrap )
2014-04-16 20:31:59 +00:00
2013-03-15 02:38:12 +00:00
class Gauge( wx.Gauge ):
def __init__( self, *args, **kwargs ):
wx.Gauge.__init__( self, *args, **kwargs )
2017-07-12 20:03:45 +00:00
self._actual_range = None
self._is_pulsing = False
2013-03-15 02:38:12 +00:00
2017-07-12 20:03:45 +00:00
def SetRange( self, range ):
2013-03-15 02:38:12 +00:00
2017-07-12 20:03:45 +00:00
if range is None:
2017-01-25 22:56:55 +00:00
self.Pulse()
2017-07-12 20:03:45 +00:00
self._is_pulsing = True
2013-03-15 02:38:12 +00:00
else:
if self._is_pulsing:
self.StopPulsing()
2017-07-12 20:03:45 +00:00
if range > 1000:
self._actual_range = range
range = 1000
else:
self._actual_range = None
if range != self.GetRange():
wx.Gauge.SetRange( self, range )
2013-03-15 02:38:12 +00:00
def SetValue( self, value ):
if not self._is_pulsing:
2017-01-25 22:56:55 +00:00
if value is None:
2017-07-12 20:03:45 +00:00
self.Pulse()
2017-07-12 20:03:45 +00:00
self._is_pulsing = True
else:
if self._actual_range is not None:
value = min( int( 1000 * ( float( value ) / self._actual_range ) ), 1000 )
2017-07-12 20:03:45 +00:00
2017-08-09 21:33:51 +00:00
value = min( value, self.GetRange() )
if value != self.GetValue():
wx.Gauge.SetValue( self, value )
2017-07-12 20:03:45 +00:00
2017-01-25 22:56:55 +00:00
2013-03-15 02:38:12 +00:00
def StopPulsing( self ):
self._is_pulsing = False
self.SetRange( 1 )
self.SetValue( 1 )
self.SetValue( 0 )
2013-03-15 02:38:12 +00:00
class ListBook( wx.Panel ):
def __init__( self, *args, **kwargs ):
wx.Panel.__init__( self, *args, **kwargs )
2016-08-10 19:04:08 +00:00
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_FRAMEBK ) )
2013-03-15 02:38:12 +00:00
2016-05-18 20:07:14 +00:00
self._keys_to_active_pages = {}
self._keys_to_proto_pages = {}
2015-05-06 20:26:18 +00:00
2016-05-25 21:54:03 +00:00
# Don't use LB_SORT! Linux can't handle clientdata that jumps around!
self._list_box = wx.ListBox( self, style = wx.LB_SINGLE )
2013-03-15 02:38:12 +00:00
self._empty_panel = wx.Panel( self )
2016-08-10 19:04:08 +00:00
self._empty_panel.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_FRAMEBK ) )
2013-03-15 02:38:12 +00:00
2016-05-18 20:07:14 +00:00
self._current_key = None
2013-03-15 02:38:12 +00:00
self._current_panel = self._empty_panel
self._panel_sizer = wx.BoxSizer( wx.VERTICAL )
2018-01-03 22:37:30 +00:00
self._panel_sizer.Add( self._empty_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
2013-03-15 02:38:12 +00:00
hbox = wx.BoxSizer( wx.HORIZONTAL )
2018-01-03 22:37:30 +00:00
hbox.Add( self._list_box, CC.FLAGS_EXPAND_PERPENDICULAR )
hbox.Add( self._panel_sizer, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
2013-03-15 02:38:12 +00:00
self._list_box.Bind( wx.EVT_LISTBOX, self.EventSelection )
self.SetSizer( hbox )
2016-05-18 20:07:14 +00:00
def _ActivatePage( self, key ):
( classname, args, kwargs ) = self._keys_to_proto_pages[ key ]
page = classname( *args, **kwargs )
page.Hide()
2018-01-03 22:37:30 +00:00
self._panel_sizer.Add( page, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
2016-05-18 20:07:14 +00:00
self._keys_to_active_pages[ key ] = page
del self._keys_to_proto_pages[ key ]
2016-07-06 21:13:15 +00:00
self._panel_sizer.CalcMin()
2016-05-18 20:07:14 +00:00
self._RecalcListBoxWidth()
2013-12-18 22:49:24 +00:00
2016-05-18 20:07:14 +00:00
def _GetIndex( self, key ):
for i in range( self._list_box.GetCount() ):
2013-12-18 22:49:24 +00:00
2016-05-18 20:07:14 +00:00
i_key = self._list_box.GetClientData( i )
if i_key == key:
2013-12-18 22:49:24 +00:00
2016-05-18 20:07:14 +00:00
return i
2013-12-18 22:49:24 +00:00
2016-05-18 20:07:14 +00:00
return wx.NOT_FOUND
2013-12-18 22:49:24 +00:00
2016-07-06 21:13:15 +00:00
def _RecalcListBoxWidth( self ):
self.Layout()
2013-03-15 02:38:12 +00:00
def _Select( self, selection ):
2016-05-18 20:07:14 +00:00
if selection == wx.NOT_FOUND:
self._current_key = None
else:
self._current_key = self._list_box.GetClientData( selection )
2013-03-23 17:57:29 +00:00
self._current_panel.Hide()
self._list_box.SetSelection( selection )
2016-05-18 20:07:14 +00:00
if selection == wx.NOT_FOUND:
self._current_panel = self._empty_panel
2013-03-23 17:57:29 +00:00
else:
2013-03-15 02:38:12 +00:00
2016-05-18 20:07:14 +00:00
if self._current_key in self._keys_to_proto_pages:
2013-03-23 17:57:29 +00:00
2016-05-18 20:07:14 +00:00
self._ActivatePage( self._current_key )
2013-03-15 02:38:12 +00:00
2016-05-18 20:07:14 +00:00
self._current_panel = self._keys_to_active_pages[ self._current_key ]
2013-03-23 17:57:29 +00:00
2013-03-15 02:38:12 +00:00
self._current_panel.Show()
self.Layout()
2013-09-11 21:28:19 +00:00
self.Refresh()
2016-11-30 20:24:17 +00:00
# this tells any parent scrolled panel to update its virtualsize and recalc its scrollbars
event = wx.NotifyEvent( wx.wxEVT_SIZE, self.GetId() )
2016-07-06 21:13:15 +00:00
2018-02-21 21:59:37 +00:00
wx.QueueEvent( self.GetEventHandler(), event )
2016-07-06 21:13:15 +00:00
2016-11-30 20:24:17 +00:00
# now the virtualsize is updated, we now tell any parent resizing frame/dialog that is interested in resizing that now is the time
2017-05-24 20:28:24 +00:00
ClientGUITopLevelWindows.PostSizeChangedEvent( self )
2016-07-06 21:13:15 +00:00
2013-03-15 02:38:12 +00:00
event = wx.NotifyEvent( wx.wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, -1 )
2018-02-21 21:59:37 +00:00
wx.QueueEvent( self.GetEventHandler(), event )
2013-03-15 02:38:12 +00:00
2016-05-18 20:07:14 +00:00
def AddPage( self, display_name, key, page, select = False ):
if self._GetIndex( key ) != wx.NOT_FOUND:
raise HydrusExceptions.NameException( 'That entry already exists!' )
2013-03-15 02:38:12 +00:00
2016-01-13 22:08:19 +00:00
if not isinstance( page, tuple ):
2013-03-15 02:38:12 +00:00
page.Hide()
2018-01-03 22:37:30 +00:00
self._panel_sizer.Add( page, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
2013-03-15 02:38:12 +00:00
2013-02-19 00:11:43 +00:00
2016-05-25 21:54:03 +00:00
# Can't do LB_SORT because of Linux not being able to track clientdata, have to do it manually.
current_display_names = self._list_box.GetStrings()
insertion_index = len( current_display_names )
for ( i, current_display_name ) in enumerate( current_display_names ):
2017-02-01 21:11:17 +00:00
if current_display_name > display_name:
2016-01-06 21:17:20 +00:00
2017-02-01 21:11:17 +00:00
insertion_index = i
2016-02-17 22:06:47 +00:00
2017-02-01 21:11:17 +00:00
break
2016-01-06 21:17:20 +00:00
2014-08-13 22:18:12 +00:00
2017-02-01 21:11:17 +00:00
self._list_box.Insert( display_name, insertion_index, key )
self._keys_to_active_pages[ key ] = page
self._RecalcListBoxWidth()
if self._list_box.GetCount() == 1:
2016-04-27 19:20:37 +00:00
2017-02-01 21:11:17 +00:00
self._Select( 0 )
elif select:
index = self._GetIndex( key )
self._Select( index )
2016-04-27 19:20:37 +00:00
2014-02-26 22:09:54 +00:00
2017-02-01 21:11:17 +00:00
def AddPageArgs( self, display_name, key, classname, args, kwargs ):
2013-07-24 20:26:00 +00:00
2017-02-01 21:11:17 +00:00
if self._GetIndex( key ) != wx.NOT_FOUND:
raise HydrusExceptions.NameException( 'That entry already exists!' )
2013-08-14 20:21:49 +00:00
2017-02-01 21:11:17 +00:00
# Can't do LB_SORT because of Linux not being able to track clientdata, have to do it manually.
2013-08-14 20:21:49 +00:00
2017-02-01 21:11:17 +00:00
current_display_names = self._list_box.GetStrings()
2013-08-14 20:21:49 +00:00
2017-02-01 21:11:17 +00:00
insertion_index = len( current_display_names )
for ( i, current_display_name ) in enumerate( current_display_names ):
if current_display_name > display_name:
insertion_index = i
break
2013-07-24 20:26:00 +00:00
2017-02-01 21:11:17 +00:00
self._list_box.Insert( display_name, insertion_index, key )
2013-07-24 20:26:00 +00:00
2017-02-01 21:11:17 +00:00
self._keys_to_proto_pages[ key ] = ( classname, args, kwargs )
2014-02-26 22:09:54 +00:00
2017-02-01 21:11:17 +00:00
self._RecalcListBoxWidth()
2014-02-26 22:09:54 +00:00
2017-02-01 21:11:17 +00:00
if self._list_box.GetCount() == 1:
self._Select( 0 )
2014-02-26 22:09:54 +00:00
2013-07-31 21:26:38 +00:00
2017-02-01 21:11:17 +00:00
def DeleteAllPages( self ):
2016-01-06 21:17:20 +00:00
2017-02-01 21:11:17 +00:00
self._panel_sizer.Detach( self._empty_panel )
2016-03-16 22:19:14 +00:00
2018-01-03 22:37:30 +00:00
self._panel_sizer.Clear( delete_windows = True )
2016-01-06 21:17:20 +00:00
2018-01-03 22:37:30 +00:00
self._panel_sizer.Add( self._empty_panel, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
2016-01-06 21:17:20 +00:00
2017-02-01 21:11:17 +00:00
self._current_key = None
2016-01-06 21:17:20 +00:00
2017-02-01 21:11:17 +00:00
self._current_panel = self._empty_panel
2016-01-06 21:17:20 +00:00
2017-02-01 21:11:17 +00:00
self._keys_to_active_pages = {}
self._keys_to_proto_pages = {}
2016-01-06 21:17:20 +00:00
2017-02-01 21:11:17 +00:00
self._list_box.Clear()
2016-01-06 21:17:20 +00:00
2017-02-01 21:11:17 +00:00
def DeleteCurrentPage( self ):
2013-07-31 21:26:38 +00:00
2017-02-01 21:11:17 +00:00
selection = self._list_box.GetSelection()
2013-08-14 20:21:49 +00:00
2017-02-01 21:11:17 +00:00
if selection != wx.NOT_FOUND:
2013-07-31 21:26:38 +00:00
2017-02-01 21:11:17 +00:00
key_to_delete = self._current_key
page_to_delete = self._current_panel
2013-07-31 21:26:38 +00:00
2017-02-01 21:11:17 +00:00
next_selection = selection + 1
previous_selection = selection - 1
2014-12-10 22:02:39 +00:00
2017-02-01 21:11:17 +00:00
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 )
2016-01-06 21:17:20 +00:00
2017-02-01 21:11:17 +00:00
self._panel_sizer.Detach( page_to_delete )
2013-07-31 21:26:38 +00:00
2017-02-01 21:11:17 +00:00
page_to_delete.Destroy()
2013-07-31 21:26:38 +00:00
2017-02-01 21:11:17 +00:00
del self._keys_to_active_pages[ key_to_delete ]
2015-02-03 20:40:21 +00:00
2017-02-01 21:11:17 +00:00
self._list_box.Delete( selection )
2015-02-03 20:40:21 +00:00
2017-02-01 21:11:17 +00:00
self._RecalcListBoxWidth()
2015-02-03 20:40:21 +00:00
2017-02-01 21:11:17 +00:00
def EventSelection( self, event ):
if self._list_box.GetSelection() != self._GetIndex( self._current_key ):
event = wx.NotifyEvent( wx.wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, -1 )
2018-02-21 21:59:37 +00:00
wx.QueueEvent( self.GetEventHandler(), event )
2017-02-01 21:11:17 +00:00
if event.IsAllowed():
2016-01-06 21:17:20 +00:00
2017-02-01 21:11:17 +00:00
self._Select( self._list_box.GetSelection() )
2016-01-06 21:17:20 +00:00
else:
2017-02-01 21:11:17 +00:00
self._list_box.SetSelection( self._GetIndex( self._current_key ) )
2016-01-06 21:17:20 +00:00
2013-08-14 20:21:49 +00:00
2013-07-24 20:26:00 +00:00
2017-02-01 21:11:17 +00:00
def GetCurrentKey( self ):
2016-07-13 17:37:44 +00:00
2017-02-01 21:11:17 +00:00
return self._current_key
2016-07-13 17:37:44 +00:00
2017-02-01 21:11:17 +00:00
def GetCurrentPage( self ):
2015-10-28 21:29:05 +00:00
2017-02-01 21:11:17 +00:00
if self._current_panel == self._empty_panel:
return None
else:
return self._current_panel
2015-10-28 21:29:05 +00:00
2017-02-01 21:11:17 +00:00
def GetActivePages( self ):
2015-10-28 21:29:05 +00:00
2017-02-01 21:11:17 +00:00
return self._keys_to_active_pages.values()
2016-12-07 22:12:52 +00:00
2017-02-01 21:11:17 +00:00
def GetPage( self, key ):
2016-12-07 22:12:52 +00:00
2017-02-01 21:11:17 +00:00
if key in self._keys_to_proto_pages:
self._ActivatePage( key )
2015-10-28 21:29:05 +00:00
2017-02-01 21:11:17 +00:00
if key in self._keys_to_active_pages:
return self._keys_to_active_pages[ key ]
2013-07-24 20:26:00 +00:00
2017-02-01 21:11:17 +00:00
raise Exception( 'That page not found!' )
2013-08-14 20:21:49 +00:00
2017-02-01 21:11:17 +00:00
2017-03-22 22:38:15 +00:00
def GetPageCount( self ):
return len( self._keys_to_active_pages ) + len( self._keys_to_proto_pages )
2017-02-01 21:11:17 +00:00
def KeyExists( self, key ):
2015-02-03 20:40:21 +00:00
2017-02-01 21:11:17 +00:00
return key in self._keys_to_active_pages or key in self._keys_to_proto_pages
2014-10-29 21:39:01 +00:00
2017-02-01 21:11:17 +00:00
def RenamePage( self, key, new_name ):
2013-07-24 20:26:00 +00:00
2017-02-01 21:11:17 +00:00
index = self._GetIndex( key )
2015-02-03 20:40:21 +00:00
2017-02-01 21:11:17 +00:00
if index != wx.NOT_FOUND:
2015-10-28 21:29:05 +00:00
2017-02-01 21:11:17 +00:00
self._list_box.SetString( index, new_name )
2015-10-28 21:29:05 +00:00
2013-07-24 20:26:00 +00:00
2017-02-01 21:11:17 +00:00
self._RecalcListBoxWidth()
2013-07-24 20:26:00 +00:00
2017-02-01 21:11:17 +00:00
def Select( self, key ):
2015-12-09 23:16:41 +00:00
2017-02-01 21:11:17 +00:00
index = self._GetIndex( key )
2015-12-09 23:16:41 +00:00
2017-02-01 21:11:17 +00:00
if index != wx.NOT_FOUND and index != self._list_box.GetSelection():
2015-12-09 23:16:41 +00:00
2017-02-01 21:11:17 +00:00
event = wx.NotifyEvent( wx.wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, -1 )
2015-12-09 23:16:41 +00:00
2018-02-21 21:59:37 +00:00
wx.QueueEvent( self.GetEventHandler(), event )
2015-12-09 23:16:41 +00:00
2017-02-01 21:11:17 +00:00
if event.IsAllowed():
self._Select( index )
2015-12-09 23:16:41 +00:00
2017-02-01 21:11:17 +00:00
def SelectDown( self ):
2013-07-24 20:26:00 +00:00
2017-02-01 21:11:17 +00:00
current_selection = self._list_box.GetSelection()
if current_selection != wx.NOT_FOUND:
2016-12-21 22:30:54 +00:00
2017-02-01 21:11:17 +00:00
num_entries = self._list_box.GetCount()
2016-12-21 22:30:54 +00:00
2017-02-01 21:11:17 +00:00
if current_selection == num_entries - 1: selection = 0
else: selection = current_selection + 1
2016-01-06 21:17:20 +00:00
2017-02-01 21:11:17 +00:00
if selection != current_selection:
self._Select( selection )
2016-01-06 21:17:20 +00:00
2017-02-01 21:11:17 +00:00
def SelectPage( self, page_to_select ):
2013-07-24 20:26:00 +00:00
2017-02-01 21:11:17 +00:00
for ( key, page ) in self._keys_to_active_pages.items():
2016-01-06 21:17:20 +00:00
2017-02-01 21:11:17 +00:00
if page == page_to_select:
self._Select( self._GetIndex( key ) )
return
2016-01-06 21:17:20 +00:00
2013-07-24 20:26:00 +00:00
2015-02-03 20:40:21 +00:00
2017-02-01 21:11:17 +00:00
def SelectUp( self ):
2013-07-24 20:26:00 +00:00
2017-02-01 21:11:17 +00:00
current_selection = self._list_box.GetSelection()
2015-10-28 21:29:05 +00:00
2017-02-01 21:11:17 +00:00
if current_selection != wx.NOT_FOUND:
num_entries = self._list_box.GetCount()
if current_selection == 0: selection = num_entries - 1
else: selection = current_selection - 1
2015-10-28 21:29:05 +00:00
2017-02-01 21:11:17 +00:00
if selection != current_selection:
self._Select( selection )
2015-10-28 21:29:05 +00:00
2015-02-03 20:40:21 +00:00
2017-03-15 20:13:04 +00:00
class MenuBitmapButton( BetterBitmapButton ):
def __init__( self, parent, bitmap, menu_items ):
BetterBitmapButton.__init__( self, parent, bitmap, self.DoMenu )
self._menu_items = menu_items
2017-01-25 22:56:55 +00:00
2017-01-18 22:52:39 +00:00
def DoMenu( self ):
menu = wx.Menu()
2017-01-25 22:56:55 +00:00
for ( item_type, title, description, data ) in self._menu_items:
2017-01-18 22:52:39 +00:00
2017-01-25 22:56:55 +00:00
if item_type == 'normal':
2017-03-15 20:13:04 +00:00
func = data
2017-01-25 22:56:55 +00:00
2017-03-15 20:13:04 +00:00
ClientGUIMenus.AppendMenuItem( self, menu, title, description, func )
2017-01-25 22:56:55 +00:00
elif item_type == 'check':
2017-03-15 20:13:04 +00:00
check_manager = data
2017-01-25 22:56:55 +00:00
2017-03-15 20:13:04 +00:00
current_value = check_manager.GetCurrentValue()
func = check_manager.Invert
2017-01-25 22:56:55 +00:00
2017-08-16 21:58:06 +00:00
if current_value is not None:
ClientGUIMenus.AppendMenuCheckItem( self, menu, title, description, current_value, func )
2017-01-25 22:56:55 +00:00
elif item_type == 'separator':
2017-03-15 20:13:04 +00:00
ClientGUIMenus.AppendSeparator( menu )
2017-01-25 22:56:55 +00:00
2017-01-18 22:52:39 +00:00
2017-05-10 21:33:58 +00:00
HG.client_controller.PopupMenu( self, menu )
2017-01-18 22:52:39 +00:00
2016-11-16 20:21:43 +00:00
class MenuButton( BetterButton ):
def __init__( self, parent, label, menu_items ):
BetterButton.__init__( self, parent, label, self.DoMenu )
self._menu_items = menu_items
def DoMenu( self ):
menu = wx.Menu()
2017-01-25 22:56:55 +00:00
for ( item_type, title, description, data ) in self._menu_items:
2016-11-16 20:21:43 +00:00
2017-01-25 22:56:55 +00:00
if item_type == 'normal':
callable = data
ClientGUIMenus.AppendMenuItem( self, menu, title, description, callable )
elif item_type == 'check':
2017-03-15 20:13:04 +00:00
check_manager = data
2017-01-25 22:56:55 +00:00
2017-03-15 20:13:04 +00:00
initial_value = check_manager.GetInitialValue()
2017-01-25 22:56:55 +00:00
2017-03-15 20:13:04 +00:00
ClientGUIMenus.AppendMenuCheckItem( self, menu, title, description, initial_value, check_manager.Invert )
2017-01-25 22:56:55 +00:00
2017-08-16 21:58:06 +00:00
2017-01-25 22:56:55 +00:00
elif item_type == 'separator':
2017-03-15 20:13:04 +00:00
ClientGUIMenus.AppendSeparator( menu )
2017-01-25 22:56:55 +00:00
2017-03-02 02:14:56 +00:00
elif item_type == 'label':
ClientGUIMenus.AppendMenuLabel( menu, title, description )
2016-11-16 20:21:43 +00:00
2017-05-10 21:33:58 +00:00
HG.client_controller.PopupMenu( self, menu )
2016-11-16 20:21:43 +00:00
2017-03-02 02:14:56 +00:00
def SetMenuItems( self, menu_items ):
self._menu_items = menu_items
2017-10-11 17:38:14 +00:00
class NetworkContextButton( BetterButton ):
2018-03-22 00:03:33 +00:00
def __init__( self, parent, network_context, limited_types = None, allow_default = True ):
2017-10-11 17:38:14 +00:00
BetterButton.__init__( self, parent, network_context.ToUnicode(), self._Edit )
self._network_context = network_context
2018-03-22 00:03:33 +00:00
self._limited_types = limited_types
self._allow_default = allow_default
2017-10-11 17:38:14 +00:00
def _Edit( self ):
import ClientGUITopLevelWindows
import ClientGUIScrolledPanelsEdit
with ClientGUITopLevelWindows.DialogEdit( self, 'edit network context' ) as dlg:
2018-03-22 00:03:33 +00:00
panel = ClientGUIScrolledPanelsEdit.EditNetworkContextPanel( dlg, self._network_context, limited_types = self._limited_types, allow_default = self._allow_default )
2017-10-11 17:38:14 +00:00
dlg.SetPanel( panel )
if dlg.ShowModal() == wx.ID_OK:
self._network_context = panel.GetValue()
self._Update()
def _Update( self ):
self.SetLabelText( self._network_context.ToUnicode() )
def GetValue( self ):
return self._network_context
def SetValue( self, network_context ):
self._network_context = network_context
self._Update()
2015-02-03 20:40:21 +00:00
class NoneableSpinCtrl( wx.Panel ):
2017-03-02 02:14:56 +00:00
def __init__( self, parent, message = '', none_phrase = 'no limit', min = 0, max = 1000000, unit = None, multiplier = 1, num_dimensions = 1 ):
2013-02-19 00:11:43 +00:00
2015-02-03 20:40:21 +00:00
wx.Panel.__init__( self, parent )
2013-02-19 00:11:43 +00:00
2015-12-02 22:32:18 +00:00
self._unit = unit
2015-02-03 20:40:21 +00:00
self._multiplier = multiplier
2015-12-02 22:32:18 +00:00
self._num_dimensions = num_dimensions
2013-03-15 02:38:12 +00:00
2017-04-19 20:58:30 +00:00
self._checkbox = wx.CheckBox( self )
2015-02-03 20:40:21 +00:00
self._checkbox.Bind( wx.EVT_CHECKBOX, self.EventCheckBox )
2017-04-19 20:58:30 +00:00
self._checkbox.SetLabelText( none_phrase )
2013-02-19 00:11:43 +00:00
2018-03-22 00:03:33 +00:00
self._one = wx.SpinCtrl( self, min = min, max = max )
2018-03-28 21:55:58 +00:00
width = ClientData.ConvertTextToPixelWidth( self._one, len( str( max ) ) + 5 )
2018-03-22 00:03:33 +00:00
self._one.SetInitialSize( ( width, -1 ) )
2013-02-19 00:11:43 +00:00
2015-12-23 22:51:04 +00:00
if num_dimensions == 2:
2018-03-22 00:03:33 +00:00
self._two = wx.SpinCtrl( self, initial = 0, min = min, max = max )
2018-03-28 21:55:58 +00:00
width = ClientData.ConvertTextToPixelWidth( self._two, len( str( max ) ) + 5 )
2018-03-22 00:03:33 +00:00
self._two.SetInitialSize( ( width, -1 ) )
2015-12-23 22:51:04 +00:00
2013-02-19 00:11:43 +00:00
2015-02-03 20:40:21 +00:00
hbox = wx.BoxSizer( wx.HORIZONTAL )
2013-02-19 00:11:43 +00:00
2015-07-15 20:28:26 +00:00
if len( message ) > 0:
2018-01-03 22:37:30 +00:00
hbox.Add( BetterStaticText( self, message + ': ' ), CC.FLAGS_VCENTER )
2015-07-15 20:28:26 +00:00
2018-01-03 22:37:30 +00:00
hbox.Add( self._one, CC.FLAGS_VCENTER )
2013-02-19 00:11:43 +00:00
2015-02-03 20:40:21 +00:00
if self._num_dimensions == 2:
2018-01-03 22:37:30 +00:00
hbox.Add( BetterStaticText( self, 'x' ), CC.FLAGS_VCENTER )
hbox.Add( self._two, CC.FLAGS_VCENTER )
2015-02-03 20:40:21 +00:00
2013-02-19 00:11:43 +00:00
2015-12-02 22:32:18 +00:00
if self._unit is not None:
2018-01-03 22:37:30 +00:00
hbox.Add( BetterStaticText( self, self._unit ), CC.FLAGS_VCENTER )
2015-12-02 22:32:18 +00:00
2018-01-03 22:37:30 +00:00
hbox.Add( self._checkbox, CC.FLAGS_VCENTER )
2013-02-19 00:11:43 +00:00
2015-02-03 20:40:21 +00:00
self.SetSizer( hbox )
2013-02-19 00:11:43 +00:00
2015-04-01 20:44:54 +00:00
def Bind( self, event_type, callback ):
self._checkbox.Bind( wx.EVT_CHECKBOX, callback )
2017-09-27 21:52:54 +00:00
2015-04-01 20:44:54 +00:00
self._one.Bind( wx.EVT_SPINCTRL, callback )
2017-09-27 21:52:54 +00:00
if self._num_dimensions == 2:
self._two.Bind( wx.EVT_SPINCTRL, callback )
2015-04-01 20:44:54 +00:00
2015-02-03 20:40:21 +00:00
def EventCheckBox( self, event ):
2013-02-19 00:11:43 +00:00
2015-02-03 20:40:21 +00:00
if self._checkbox.GetValue():
2013-02-19 00:11:43 +00:00
2015-02-03 20:40:21 +00:00
self._one.Disable()
if self._num_dimensions == 2: self._two.Disable()
2013-02-19 00:11:43 +00:00
2015-02-03 20:40:21 +00:00
else:
2013-02-19 00:11:43 +00:00
2015-02-03 20:40:21 +00:00
self._one.Enable()
if self._num_dimensions == 2: self._two.Enable()
2013-02-19 00:11:43 +00:00
2015-02-03 20:40:21 +00:00
def GetValue( self ):
2013-02-19 00:11:43 +00:00
2017-10-18 19:41:25 +00:00
if self._checkbox.GetValue():
return None
2015-02-03 20:40:21 +00:00
else:
2017-10-18 19:41:25 +00:00
if self._num_dimensions == 2:
return ( self._one.GetValue() * self._multiplier, self._two.GetValue() * self._multiplier )
else:
return self._one.GetValue() * self._multiplier
2015-02-03 20:40:21 +00:00
2013-02-19 00:11:43 +00:00
2018-01-03 22:37:30 +00:00
def SetToolTip( self, text ):
2016-06-22 20:59:24 +00:00
2018-01-03 22:37:30 +00:00
wx.Panel.SetToolTip( self, text )
2016-06-22 20:59:24 +00:00
for c in self.GetChildren():
2018-01-03 22:37:30 +00:00
c.SetToolTip( text )
2016-06-22 20:59:24 +00:00
2015-02-03 20:40:21 +00:00
def SetValue( self, value ):
2013-03-15 02:38:12 +00:00
2015-02-03 20:40:21 +00:00
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:
self._two.Enable()
( value, y ) = value
self._two.SetValue( y / self._multiplier )
self._one.Enable()
self._one.SetValue( value / self._multiplier )
2013-02-19 00:11:43 +00:00
2017-10-18 19:41:25 +00:00
class NoneableTextCtrl( wx.Panel ):
def __init__( self, parent, message = '', none_phrase = 'no limit' ):
wx.Panel.__init__( self, parent )
self._checkbox = wx.CheckBox( self )
self._checkbox.Bind( wx.EVT_CHECKBOX, self.EventCheckBox )
self._checkbox.SetLabelText( none_phrase )
self._text = wx.TextCtrl( self )
hbox = wx.BoxSizer( wx.HORIZONTAL )
if len( message ) > 0:
2018-01-03 22:37:30 +00:00
hbox.Add( BetterStaticText( self, message + ': ' ), CC.FLAGS_VCENTER )
2017-10-18 19:41:25 +00:00
2018-01-03 22:37:30 +00:00
hbox.Add( self._text, CC.FLAGS_VCENTER )
hbox.Add( self._checkbox, CC.FLAGS_VCENTER )
2017-10-18 19:41:25 +00:00
self.SetSizer( hbox )
def Bind( self, event_type, callback ):
self._checkbox.Bind( wx.EVT_CHECKBOX, callback )
self._text.Bind( wx.EVT_TEXT, callback )
def EventCheckBox( self, event ):
if self._checkbox.GetValue():
self._text.Disable()
else:
self._text.Enable()
def GetValue( self ):
if self._checkbox.GetValue():
return None
else:
return self._text.GetValue()
2018-01-03 22:37:30 +00:00
def SetToolTip( self, text ):
2017-10-18 19:41:25 +00:00
2018-01-03 22:37:30 +00:00
wx.Panel.SetToolTip( self, text )
2017-10-18 19:41:25 +00:00
for c in self.GetChildren():
2018-01-03 22:37:30 +00:00
c.SetToolTip( text )
2017-10-18 19:41:25 +00:00
def SetValue( self, value ):
if value is None:
self._checkbox.SetValue( True )
self._text.Disable()
else:
self._checkbox.SetValue( False )
self._text.Enable()
self._text.SetValue( value )
2015-02-03 20:40:21 +00:00
class OnOffButton( wx.Button ):
2013-03-15 02:38:12 +00:00
2015-02-03 20:40:21 +00:00
def __init__( self, parent, page_key, topic, on_label, off_label = None, start_on = True ):
2013-03-15 02:38:12 +00:00
2015-02-03 20:40:21 +00:00
if start_on: label = on_label
else: label = off_label
2013-03-15 02:38:12 +00:00
2015-02-03 20:40:21 +00:00
wx.Button.__init__( self, parent, label = label )
2013-03-15 02:38:12 +00:00
2015-02-03 20:40:21 +00:00
self._page_key = page_key
self._topic = topic
self._on_label = on_label
2013-03-15 02:38:12 +00:00
2015-02-03 20:40:21 +00:00
if off_label is None: self._off_label = on_label
else: self._off_label = off_label
2013-09-18 17:23:30 +00:00
2015-02-03 20:40:21 +00:00
self._on = start_on
2013-09-18 17:23:30 +00:00
2015-02-03 20:40:21 +00:00
if self._on: self.SetForegroundColour( ( 0, 128, 0 ) )
else: self.SetForegroundColour( ( 128, 0, 0 ) )
2013-09-18 17:23:30 +00:00
2015-02-03 20:40:21 +00:00
self.Bind( wx.EVT_BUTTON, self.EventButton )
2013-09-18 17:23:30 +00:00
2017-05-10 21:33:58 +00:00
HG.client_controller.sub( self, 'HitButton', 'hit_on_off_button' )
2013-09-18 17:23:30 +00:00
2015-02-03 20:40:21 +00:00
def EventButton( self, event ):
2013-09-18 17:23:30 +00:00
2015-02-03 20:40:21 +00:00
if self._on:
2013-09-18 17:23:30 +00:00
2015-02-03 20:40:21 +00:00
self._on = False
2013-09-18 17:23:30 +00:00
2016-03-23 19:42:56 +00:00
self.SetLabelText( self._off_label )
2013-09-18 17:23:30 +00:00
2015-02-03 20:40:21 +00:00
self.SetForegroundColour( ( 128, 0, 0 ) )
2013-09-18 17:23:30 +00:00
2017-05-10 21:33:58 +00:00
HG.client_controller.pub( self._topic, self._page_key, False )
2013-09-18 17:23:30 +00:00
2015-02-03 20:40:21 +00:00
else:
2013-09-18 17:23:30 +00:00
2015-02-03 20:40:21 +00:00
self._on = True
2013-09-18 17:23:30 +00:00
2016-03-23 19:42:56 +00:00
self.SetLabelText( self._on_label )
2013-09-18 17:23:30 +00:00
2015-02-03 20:40:21 +00:00
self.SetForegroundColour( ( 0, 128, 0 ) )
2014-02-12 23:09:38 +00:00
2017-05-10 21:33:58 +00:00
HG.client_controller.pub( self._topic, self._page_key, True )
2014-02-12 23:09:38 +00:00
2013-02-19 00:11:43 +00:00
2015-02-03 20:40:21 +00:00
def IsOn( self ): return self._on
2014-09-03 20:26:49 +00:00
2017-07-27 00:47:13 +00:00
class RatingLike( wx.Window ):
2014-02-12 23:09:38 +00:00
2017-07-27 00:47:13 +00:00
def __init__( self, parent, service_key ):
2014-09-03 20:26:49 +00:00
2017-07-27 00:47:13 +00:00
wx.Window.__init__( self, parent )
2014-09-03 20:26:49 +00:00
2017-07-27 00:47:13 +00:00
self._service_key = service_key
2014-09-03 20:26:49 +00:00
2018-01-03 22:37:30 +00:00
self._canvas_bmp = wx.Bitmap( 16, 16, 24 )
2016-09-07 20:01:05 +00:00
2017-07-27 00:47:13 +00:00
self.Bind( wx.EVT_PAINT, self.EventPaint )
self.Bind( wx.EVT_ERASE_BACKGROUND, self.EventEraseBackground )
2016-09-07 20:01:05 +00:00
2017-07-27 00:47:13 +00:00
self.Bind( wx.EVT_LEFT_DOWN, self.EventLeftDown )
self.Bind( wx.EVT_LEFT_DCLICK, self.EventLeftDown )
self.Bind( wx.EVT_RIGHT_DOWN, self.EventRightDown )
self.Bind( wx.EVT_RIGHT_DCLICK, self.EventRightDown )
2016-09-07 20:01:05 +00:00
2017-07-27 00:47:13 +00:00
self.SetMinSize( ( 16, 16 ) )
self._dirty = True
2016-09-07 20:01:05 +00:00
2015-02-03 20:40:21 +00:00
2017-07-27 00:47:13 +00:00
def _Draw( self, dc ):
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
raise NotImplementedError()
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
def EventEraseBackground( self, event ): pass
def EventLeftDown( self, event ):
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
raise NotImplementedError()
2013-03-15 02:38:12 +00:00
2017-07-27 00:47:13 +00:00
def EventPaint( self, event ):
2013-09-18 17:23:30 +00:00
2017-07-27 00:47:13 +00:00
dc = wx.BufferedPaintDC( self, self._canvas_bmp )
2013-09-18 17:23:30 +00:00
2017-07-27 00:47:13 +00:00
if self._dirty:
self._Draw( dc )
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
def EventRightDown( self, event ):
2016-09-07 20:01:05 +00:00
2017-07-27 00:47:13 +00:00
raise NotImplementedError()
2016-09-07 20:01:05 +00:00
2014-09-03 20:26:49 +00:00
2017-07-27 00:47:13 +00:00
def GetServiceKey( self ):
2016-09-07 20:01:05 +00:00
2017-07-27 00:47:13 +00:00
return self._service_key
2016-09-07 20:01:05 +00:00
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
class RatingLikeDialog( RatingLike ):
def __init__( self, parent, service_key ):
2016-09-07 20:01:05 +00:00
2017-07-27 00:47:13 +00:00
RatingLike.__init__( self, parent, service_key )
2016-09-07 20:01:05 +00:00
2017-07-27 00:47:13 +00:00
self._rating_state = ClientRatings.NULL
2014-02-12 23:09:38 +00:00
2017-07-27 00:47:13 +00:00
def _Draw( self, dc ):
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
dc.SetBackground( wx.Brush( self.GetParent().GetBackgroundColour() ) )
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
dc.Clear()
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
( pen_colour, brush_colour ) = ClientRatings.GetPenAndBrushColours( self._service_key, self._rating_state )
2013-03-15 02:38:12 +00:00
2017-07-27 00:47:13 +00:00
ClientRatings.DrawLike( dc, 0, 0, self._service_key, self._rating_state )
2013-03-15 02:38:12 +00:00
2017-07-27 00:47:13 +00:00
self._dirty = False
2013-03-15 02:38:12 +00:00
2017-07-27 00:47:13 +00:00
def EventLeftDown( self, event ):
2013-03-15 02:38:12 +00:00
2017-07-27 00:47:13 +00:00
if self._rating_state == ClientRatings.LIKE: self._rating_state = ClientRatings.NULL
else: self._rating_state = ClientRatings.LIKE
2013-03-15 02:38:12 +00:00
2017-07-27 00:47:13 +00:00
self._dirty = True
2017-01-25 22:56:55 +00:00
2017-07-27 00:47:13 +00:00
self.Refresh()
2016-05-25 21:54:03 +00:00
2017-07-27 00:47:13 +00:00
def EventRightDown( self, event ):
2013-03-15 02:38:12 +00:00
2017-07-27 00:47:13 +00:00
if self._rating_state == ClientRatings.DISLIKE: self._rating_state = ClientRatings.NULL
else: self._rating_state = ClientRatings.DISLIKE
2013-03-15 02:38:12 +00:00
2017-07-27 00:47:13 +00:00
self._dirty = True
2013-03-15 02:38:12 +00:00
2017-07-27 00:47:13 +00:00
self.Refresh()
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
def GetRatingState( self ):
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
return self._rating_state
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
def SetRatingState( self, rating_state ):
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
self._rating_state = rating_state
2015-03-18 21:46:29 +00:00
2017-07-27 00:47:13 +00:00
self._dirty = True
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
self.Refresh()
2013-02-19 00:11:43 +00:00
2015-02-03 20:40:21 +00:00
2017-07-27 00:47:13 +00:00
class RatingLikeCanvas( RatingLike ):
def __init__( self, parent, service_key, canvas_key ):
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
RatingLike.__init__( self, parent, service_key )
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
self._canvas_key = canvas_key
self._current_media = None
self._rating_state = None
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
service = HG.client_controller.services_manager.GetService( service_key )
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
name = service.GetName()
2013-02-19 00:11:43 +00:00
2018-01-03 22:37:30 +00:00
self.SetToolTip( name )
2015-07-15 20:28:26 +00:00
2017-07-27 00:47:13 +00:00
HG.client_controller.sub( self, 'ProcessContentUpdates', 'content_updates_gui' )
HG.client_controller.sub( self, 'SetDisplayMedia', 'canvas_new_display_media' )
2015-07-15 20:28:26 +00:00
2015-02-03 20:40:21 +00:00
2017-07-27 00:47:13 +00:00
def _Draw( self, dc ):
2016-05-25 21:54:03 +00:00
2017-07-27 00:47:13 +00:00
dc.SetBackground( wx.Brush( self.GetParent().GetBackgroundColour() ) )
dc.Clear()
2016-05-25 21:54:03 +00:00
2017-07-27 00:47:13 +00:00
if self._current_media is not None:
2016-09-28 18:48:01 +00:00
2017-07-27 00:47:13 +00:00
self._rating_state = ClientRatings.GetLikeStateFromMedia( ( self._current_media, ), self._service_key )
2016-09-28 18:48:01 +00:00
2017-07-27 00:47:13 +00:00
ClientRatings.DrawLike( dc, 0, 0, self._service_key, self._rating_state )
2016-09-28 18:48:01 +00:00
2016-05-25 21:54:03 +00:00
2017-07-27 00:47:13 +00:00
self._dirty = False
2015-02-03 20:40:21 +00:00
2017-07-27 00:47:13 +00:00
def EventLeftDown( self, event ):
2015-10-07 21:56:22 +00:00
2017-07-27 00:47:13 +00:00
if self._current_media is not None:
2015-02-03 20:40:21 +00:00
2017-07-27 00:47:13 +00:00
if self._rating_state == ClientRatings.LIKE: rating = None
else: rating = 1
2015-02-03 20:40:21 +00:00
2017-07-27 00:47:13 +00:00
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( rating, self._hashes ) )
2015-02-03 20:40:21 +00:00
2017-07-27 00:47:13 +00:00
HG.client_controller.Write( 'content_updates', { self._service_key : ( content_update, ) } )
2015-02-03 20:40:21 +00:00
2013-09-18 17:23:30 +00:00
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
def EventRightDown( self, event ):
2016-09-28 18:48:01 +00:00
2017-07-27 00:47:13 +00:00
if self._current_media is not None:
if self._rating_state == ClientRatings.DISLIKE: rating = None
else: rating = 0
2015-02-03 20:40:21 +00:00
2017-07-27 00:47:13 +00:00
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( rating, self._hashes ) )
2015-02-03 20:40:21 +00:00
2017-07-27 00:47:13 +00:00
HG.client_controller.Write( 'content_updates', { self._service_key : ( content_update, ) } )
2015-02-03 20:40:21 +00:00
2013-03-15 02:38:12 +00:00
2013-04-17 21:48:18 +00:00
2017-07-27 00:47:13 +00:00
def ProcessContentUpdates( self, service_keys_to_content_updates ):
2013-04-10 18:10:37 +00:00
2017-07-27 00:47:13 +00:00
if self._current_media is not None:
for ( service_key, content_updates ) in service_keys_to_content_updates.items():
for content_update in content_updates:
( data_type, action, row ) = content_update.ToTuple()
if data_type == HC.CONTENT_TYPE_RATINGS:
hashes = content_update.GetHashes()
if len( self._hashes.intersection( hashes ) ) > 0:
self._dirty = True
self.Refresh()
return
2015-02-03 20:40:21 +00:00
2017-07-27 00:47:13 +00:00
def SetDisplayMedia( self, canvas_key, media ):
if canvas_key == self._canvas_key:
2015-02-03 20:40:21 +00:00
2017-07-27 00:47:13 +00:00
self._current_media = media
2015-02-03 20:40:21 +00:00
2017-07-27 00:47:13 +00:00
if self._current_media is None:
self._hashes = set()
else:
self._hashes = self._current_media.GetHashes()
2015-02-03 20:40:21 +00:00
2017-07-27 00:47:13 +00:00
self._dirty = True
2015-02-03 20:40:21 +00:00
2017-07-27 00:47:13 +00:00
self.Refresh()
2015-02-03 20:40:21 +00:00
2013-04-10 18:10:37 +00:00
2013-03-15 02:38:12 +00:00
2017-07-27 00:47:13 +00:00
class RatingNumerical( wx.Window ):
def __init__( self, parent, service_key ):
2016-04-14 01:54:29 +00:00
2017-07-27 00:47:13 +00:00
wx.Window.__init__( self, parent )
2016-04-14 01:54:29 +00:00
2017-07-27 00:47:13 +00:00
self._service_key = service_key
2014-02-12 23:09:38 +00:00
2017-07-27 00:47:13 +00:00
self._service = HG.client_controller.services_manager.GetService( self._service_key )
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
self._num_stars = self._service.GetNumStars()
self._allow_zero = self._service.AllowZero()
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
my_width = ClientRatings.GetNumericalWidth( self._service_key )
2016-09-07 20:01:05 +00:00
2018-01-03 22:37:30 +00:00
self._canvas_bmp = wx.Bitmap( my_width, 16, 24 )
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
self.Bind( wx.EVT_PAINT, self.EventPaint )
self.Bind( wx.EVT_ERASE_BACKGROUND, self.EventEraseBackground )
2016-09-28 18:48:01 +00:00
2017-07-27 00:47:13 +00:00
self.Bind( wx.EVT_LEFT_DOWN, self.EventLeftDown )
self.Bind( wx.EVT_LEFT_DCLICK, self.EventLeftDown )
self.Bind( wx.EVT_RIGHT_DOWN, self.EventRightDown )
self.Bind( wx.EVT_RIGHT_DCLICK, self.EventRightDown )
2016-09-28 18:48:01 +00:00
2017-07-27 00:47:13 +00:00
self.SetMinSize( ( my_width, 16 ) )
2016-09-28 18:48:01 +00:00
2017-07-27 00:47:13 +00:00
self._dirty = True
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
def _Draw( self, dc ):
2016-09-28 18:48:01 +00:00
2017-07-27 00:47:13 +00:00
raise NotImplementedError()
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
def _GetRatingFromClickEvent( self, event ):
x = event.GetX()
y = event.GetY()
( my_width, my_height ) = self.GetClientSize()
# assuming a border of 2 on every side here
2013-03-15 02:38:12 +00:00
2017-07-27 00:47:13 +00:00
my_active_width = my_width - 4
my_active_height = my_height - 4
x_adjusted = x - 2
y_adjusted = y - 2
2016-09-28 18:48:01 +00:00
2017-07-27 00:47:13 +00:00
if 0 <= y and y <= my_active_height:
2015-02-03 20:40:21 +00:00
2017-07-27 00:47:13 +00:00
if 0 <= x and x <= my_active_width:
2015-02-03 20:40:21 +00:00
2017-07-27 00:47:13 +00:00
proportion_filled = float( x_adjusted ) / my_active_width
2016-09-28 18:48:01 +00:00
2017-07-27 00:47:13 +00:00
if self._allow_zero:
rating = round( proportion_filled * self._num_stars ) / self._num_stars
else:
rating = float( int( proportion_filled * self._num_stars ) ) / ( self._num_stars - 1 )
return rating
2016-09-28 18:48:01 +00:00
2017-07-27 00:47:13 +00:00
return None
2015-05-20 21:31:40 +00:00
def EventEraseBackground( self, event ): pass
def EventLeftDown( self, event ):
raise NotImplementedError()
def EventPaint( self, event ):
2015-11-18 22:44:07 +00:00
dc = wx.BufferedPaintDC( self, self._canvas_bmp )
2015-05-20 21:31:40 +00:00
if self._dirty:
2015-11-18 22:44:07 +00:00
self._Draw( dc )
2015-05-20 21:31:40 +00:00
def EventRightDown( self, event ):
raise NotImplementedError()
2015-07-01 22:02:07 +00:00
def GetServiceKey( self ):
return self._service_key
2015-05-20 21:31:40 +00:00
class RatingNumericalDialog( RatingNumerical ):
def __init__( self, parent, service_key ):
RatingNumerical.__init__( self, parent, service_key )
self._rating_state = ClientRatings.NULL
2015-10-21 21:53:10 +00:00
self._rating = None
2015-05-20 21:31:40 +00:00
2015-11-18 22:44:07 +00:00
def _Draw( self, dc ):
2015-05-20 21:31:40 +00:00
dc.SetBackground( wx.Brush( self.GetParent().GetBackgroundColour() ) )
dc.Clear()
2015-06-10 19:40:25 +00:00
ClientRatings.DrawNumerical( dc, 0, 0, self._service_key, self._rating_state, self._rating )
2015-05-20 21:31:40 +00:00
self._dirty = False
def EventLeftDown( self, event ):
rating = self._GetRatingFromClickEvent( event )
if rating is not None:
self._rating_state = ClientRatings.SET
self._rating = rating
self._dirty = True
self.Refresh()
def EventRightDown( self, event ):
self._rating_state = ClientRatings.NULL
self._dirty = True
self.Refresh()
def GetRating( self ):
return self._rating
def GetRatingState( self ):
return self._rating_state
def SetRating( self, rating ):
self._rating_state = ClientRatings.SET
self._rating = rating
self._dirty = True
self.Refresh()
def SetRatingState( self, rating_state ):
self._rating_state = rating_state
self._dirty = True
self.Refresh()
class RatingNumericalCanvas( RatingNumerical ):
def __init__( self, parent, service_key, canvas_key ):
RatingNumerical.__init__( self, parent, service_key )
self._canvas_key = canvas_key
self._current_media = None
self._rating_state = None
self._rating = None
name = self._service.GetName()
2018-01-03 22:37:30 +00:00
self.SetToolTip( name )
2015-05-20 21:31:40 +00:00
2017-05-10 21:33:58 +00:00
HG.client_controller.sub( self, 'ProcessContentUpdates', 'content_updates_gui' )
HG.client_controller.sub( self, 'SetDisplayMedia', 'canvas_new_display_media' )
2015-05-20 21:31:40 +00:00
2015-11-18 22:44:07 +00:00
def _Draw( self, dc ):
2015-05-20 21:31:40 +00:00
dc.SetBackground( wx.Brush( self.GetParent().GetBackgroundColour() ) )
dc.Clear()
if self._current_media is not None:
( self._rating_state, self._rating ) = ClientRatings.GetNumericalStateFromMedia( ( self._current_media, ), self._service_key )
2015-06-10 19:40:25 +00:00
ClientRatings.DrawNumerical( dc, 0, 0, self._service_key, self._rating_state, self._rating )
2015-05-20 21:31:40 +00:00
self._dirty = False
def EventLeftDown( self, event ):
if self._current_media is not None:
rating = self._GetRatingFromClickEvent( event )
if rating is not None:
2015-10-14 21:02:25 +00:00
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( rating, self._hashes ) )
2015-05-20 21:31:40 +00:00
2017-05-10 21:33:58 +00:00
HG.client_controller.Write( 'content_updates', { self._service_key : ( content_update, ) } )
2015-05-20 21:31:40 +00:00
def EventRightDown( self, event ):
if self._current_media is not None:
rating = None
2015-10-14 21:02:25 +00:00
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( rating, self._hashes ) )
2015-05-20 21:31:40 +00:00
2017-05-10 21:33:58 +00:00
HG.client_controller.Write( 'content_updates', { self._service_key : ( content_update, ) } )
2015-05-20 21:31:40 +00:00
def ProcessContentUpdates( self, service_keys_to_content_updates ):
if self._current_media is not None:
for ( service_key, content_updates ) in service_keys_to_content_updates.items():
for content_update in content_updates:
( data_type, action, row ) = content_update.ToTuple()
2015-10-14 21:02:25 +00:00
if data_type == HC.CONTENT_TYPE_RATINGS:
2015-05-20 21:31:40 +00:00
hashes = content_update.GetHashes()
if len( self._hashes.intersection( hashes ) ) > 0:
self._dirty = True
self.Refresh()
return
def SetDisplayMedia( self, canvas_key, media ):
if canvas_key == self._canvas_key:
self._current_media = media
2015-11-18 22:44:07 +00:00
if self._current_media is None:
self._hashes = set()
else:
self._hashes = self._current_media.GetHashes()
2015-05-20 21:31:40 +00:00
self._dirty = True
self.Refresh()
2015-05-13 20:22:39 +00:00
2017-09-06 20:18:20 +00:00
class RegexButton( BetterButton ):
2015-02-03 20:40:21 +00:00
def __init__( self, parent ):
2013-07-24 20:26:00 +00:00
2017-09-06 20:18:20 +00:00
BetterButton.__init__( self, parent, 'regex shortcuts', self._ShowMenu )
2013-07-24 20:26:00 +00:00
2015-02-03 20:40:21 +00:00
2017-09-06 20:18:20 +00:00
def _ShowMenu( self ):
2013-11-06 18:22:07 +00:00
2015-02-03 20:40:21 +00:00
menu = wx.Menu()
2013-07-24 20:26:00 +00:00
2017-09-06 20:18:20 +00:00
ClientGUIMenus.AppendMenuLabel( menu, 'click on a phrase to copy it to the clipboard' )
2013-07-24 20:26:00 +00:00
2017-03-15 20:13:04 +00:00
ClientGUIMenus.AppendSeparator( menu )
2014-03-12 22:08:23 +00:00
2015-09-23 21:21:02 +00:00
submenu = wx.Menu()
2015-02-03 20:40:21 +00:00
2017-09-06 20:18:20 +00:00
ClientGUIMenus.AppendMenuItem( self, submenu, r'whitespace character - \s', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', r'\s' )
ClientGUIMenus.AppendMenuItem( self, submenu, r'number character - \d', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', r'\d' )
ClientGUIMenus.AppendMenuItem( self, submenu, r'alphanumeric or backspace character - \w', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', r'\w' )
ClientGUIMenus.AppendMenuItem( self, submenu, r'any character - .', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', r'.' )
ClientGUIMenus.AppendMenuItem( self, submenu, r'backslash character - \\', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', r'\\' )
ClientGUIMenus.AppendMenuItem( self, submenu, r'beginning of line - ^', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', r'^' )
ClientGUIMenus.AppendMenuItem( self, submenu, r'end of line - $', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', r'$' )
ClientGUIMenus.AppendMenuItem( self, submenu, u'any of these - [\u2026]', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', u'[\u2026]' )
ClientGUIMenus.AppendMenuItem( self, submenu, u'anything other than these - [^\u2026]', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', u'[^\u2026]' )
2015-02-03 20:40:21 +00:00
2017-03-15 20:13:04 +00:00
ClientGUIMenus.AppendSeparator( submenu )
2015-02-03 20:40:21 +00:00
2017-09-06 20:18:20 +00:00
ClientGUIMenus.AppendMenuItem( self, submenu, r'0 or more matches, consuming as many as possible - *', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', r'*' )
ClientGUIMenus.AppendMenuItem( self, submenu, r'1 or more matches, consuming as many as possible - +', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', r'+' )
ClientGUIMenus.AppendMenuItem( self, submenu, r'0 or 1 matches, preferring 1 - ?', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', r'?' )
ClientGUIMenus.AppendMenuItem( self, submenu, r'0 or more matches, consuming as few as possible - *?', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', r'*?' )
ClientGUIMenus.AppendMenuItem( self, submenu, r'1 or more matches, consuming as few as possible - +?', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', r'+?' )
ClientGUIMenus.AppendMenuItem( self, submenu, r'0 or 1 matches, preferring 0 - ??', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', r'??' )
ClientGUIMenus.AppendMenuItem( self, submenu, r'exactly m matches - {m}', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', r'{m}' )
ClientGUIMenus.AppendMenuItem( self, submenu, r'm to n matches, consuming as many as possible - {m,n}', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', r'{m,n}' )
ClientGUIMenus.AppendMenuItem( self, submenu, r'm to n matches, consuming as few as possible - {m,n}?', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', r'{m,n}?' )
2015-02-03 20:40:21 +00:00
2017-03-15 20:13:04 +00:00
ClientGUIMenus.AppendSeparator( submenu )
2015-02-03 20:40:21 +00:00
2017-09-06 20:18:20 +00:00
ClientGUIMenus.AppendMenuItem( self, submenu, u'the next characters are: (non-consuming) - (?=\u2026)', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', u'(?=\u2026)' )
ClientGUIMenus.AppendMenuItem( self, submenu, u'the next characters are not: (non-consuming) - (?!\u2026)', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', u'(?!\u2026)' )
ClientGUIMenus.AppendMenuItem( self, submenu, u'the previous characters are: (non-consuming) - (?<=\u2026)', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', u'(?<=\u2026)' )
ClientGUIMenus.AppendMenuItem( self, submenu, u'the previous characters are not: (non-consuming) - (?<!\u2026)', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', u'(?<!\u2026)' )
2015-02-03 20:40:21 +00:00
2017-03-15 20:13:04 +00:00
ClientGUIMenus.AppendSeparator( submenu )
2015-02-03 20:40:21 +00:00
2017-09-06 20:18:20 +00:00
ClientGUIMenus.AppendMenuItem( self, submenu, r'0074 -> 74 - [1-9]+\d*', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', r'[1-9]+\d*' )
ClientGUIMenus.AppendMenuItem( self, submenu, r'filename - (?<=' + os.path.sep.encode( 'string_escape' ) + r')[^' + os.path.sep.encode( 'string_escape' ) + r']*?(?=\..*$)', 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', '(?<=' + os.path.sep.encode( 'string_escape' ) + r')[^' + os.path.sep.encode( 'string_escape' ) + r']*?(?=\..*$)' )
2015-09-23 21:21:02 +00:00
2017-09-06 20:18:20 +00:00
ClientGUIMenus.AppendMenu( menu, submenu, 'regex components' )
2015-09-23 21:21:02 +00:00
submenu = wx.Menu()
2017-09-06 20:18:20 +00:00
ClientGUIMenus.AppendMenuItem( self, submenu, 'manage favourites', 'manage some custom favourite phrases', self._ManageFavourites )
2015-09-23 21:21:02 +00:00
2017-03-15 20:13:04 +00:00
ClientGUIMenus.AppendSeparator( submenu )
2015-09-23 21:21:02 +00:00
2017-09-06 20:18:20 +00:00
for ( regex_phrase, description ) in HC.options[ 'regex_favourites' ]:
2015-09-23 21:21:02 +00:00
2017-09-06 20:18:20 +00:00
ClientGUIMenus.AppendMenuItem( self, submenu, description, 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', regex_phrase )
2015-09-23 21:21:02 +00:00
2015-02-03 20:40:21 +00:00
2017-09-06 20:18:20 +00:00
ClientGUIMenus.AppendMenu( menu, submenu, 'favourites' )
2015-02-03 20:40:21 +00:00
2017-05-10 21:33:58 +00:00
HG.client_controller.PopupMenu( self, menu )
2014-03-12 22:08:23 +00:00
2017-09-06 20:18:20 +00:00
def _ManageFavourites( self ):
2013-02-19 00:11:43 +00:00
2017-11-08 22:07:12 +00:00
regex_favourites = HC.options[ 'regex_favourites' ]
2013-02-19 00:11:43 +00:00
2017-11-08 22:07:12 +00:00
with ClientGUITopLevelWindows.DialogEdit( self, 'manage regex favourites' ) as dlg:
2015-09-23 21:21:02 +00:00
2017-11-08 22:07:12 +00:00
import ClientGUIScrolledPanelsEdit
panel = ClientGUIScrolledPanelsEdit.EditRegexFavourites( dlg, regex_favourites )
dlg.SetPanel( panel )
if dlg.ShowModal() == wx.ID_OK:
regex_favourites = panel.GetValue()
HC.options[ 'regex_favourites' ] = regex_favourites
HG.client_controller.Write( 'save_options', HC.options )
2015-09-23 21:21:02 +00:00
2013-02-19 00:11:43 +00:00
2016-11-02 21:09:14 +00:00
class SaneMultilineTextCtrl( wx.TextCtrl ):
def __init__( self, parent, style = None ):
if style is None:
style = wx.TE_MULTILINE
else:
style |= wx.TE_MULTILINE
wx.TextCtrl.__init__( self, parent, style = style )
self.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
def EventKeyDown( self, event ):
ctrl = event.CmdDown()
key_code = event.GetKeyCode()
if ctrl and key_code in ( ord( 'A' ), ord( 'a' ) ):
self.SelectAll()
else:
event.Skip()
2017-04-12 21:46:46 +00:00
class Shortcut( wx.Panel ):
2013-10-09 18:13:42 +00:00
2017-04-12 21:46:46 +00:00
def __init__( self, parent ):
wx.Panel.__init__( self, parent )
self._mouse_radio = wx.RadioButton( self, style = wx.RB_GROUP, label = 'mouse' )
self._mouse_shortcut = ShortcutMouse( self, self._mouse_radio )
self._keyboard_radio = wx.RadioButton( self, label = 'keyboard' )
self._keyboard_shortcut = ShortcutKeyboard( self, self._keyboard_radio )
#
vbox = wx.BoxSizer( wx.VERTICAL )
2018-01-03 22:37:30 +00:00
vbox.Add( BetterStaticText( self, 'Mouse events only work for the duplicate and archive/delete filters atm!' ), CC.FLAGS_EXPAND_PERPENDICULAR )
2017-04-12 21:46:46 +00:00
2018-01-03 22:37:30 +00:00
gridbox = wx.FlexGridSizer( 2 )
2017-04-12 21:46:46 +00:00
gridbox.AddGrowableCol( 1, 1 )
2018-01-03 22:37:30 +00:00
gridbox.Add( self._mouse_radio, CC.FLAGS_VCENTER )
gridbox.Add( self._mouse_shortcut, CC.FLAGS_EXPAND_BOTH_WAYS )
gridbox.Add( self._keyboard_radio, CC.FLAGS_VCENTER )
gridbox.Add( self._keyboard_shortcut, CC.FLAGS_EXPAND_BOTH_WAYS )
2017-04-12 21:46:46 +00:00
2018-01-03 22:37:30 +00:00
vbox.Add( gridbox, CC.FLAGS_EXPAND_BOTH_WAYS )
2017-04-12 21:46:46 +00:00
self.SetSizer( vbox )
def GetValue( self ):
if self._mouse_radio.GetValue() == True:
return self._mouse_shortcut.GetValue()
else:
return self._keyboard_shortcut.GetValue()
def SetValue( self, shortcut ):
if shortcut.GetShortcutType() == CC.SHORTCUT_TYPE_MOUSE:
self._mouse_radio.SetValue( True )
self._mouse_shortcut.SetValue( shortcut )
else:
self._keyboard_radio.SetValue( True )
self._keyboard_shortcut.SetValue( shortcut )
class ShortcutKeyboard( wx.TextCtrl ):
def __init__( self, parent, related_radio = None ):
self._shortcut = ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F7, [] )
2015-02-03 20:40:21 +00:00
2017-04-12 21:46:46 +00:00
self._related_radio = related_radio
2015-02-03 20:40:21 +00:00
wx.TextCtrl.__init__( self, parent, style = wx.TE_PROCESS_ENTER )
self.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
self._SetShortcutString()
2013-10-09 18:13:42 +00:00
2015-02-03 20:40:21 +00:00
def _SetShortcutString( self ):
2013-10-09 18:13:42 +00:00
2017-04-12 21:46:46 +00:00
display_string = self._shortcut.ToString()
2015-02-03 20:40:21 +00:00
wx.TextCtrl.SetValue( self, display_string )
def EventKeyDown( self, event ):
2017-04-12 21:46:46 +00:00
shortcut = ClientData.ConvertKeyEventToShortcut( event )
if shortcut is not None:
self._shortcut = shortcut
if self._related_radio is not None:
self._related_radio.SetValue( True )
self._SetShortcutString()
def GetValue( self ):
return self._shortcut
def SetValue( self, shortcut ):
self._shortcut = shortcut
self._SetShortcutString()
class ShortcutMouse( wx.Button ):
def __init__( self, parent, related_radio = None ):
self._shortcut = ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [] )
2017-04-05 21:16:40 +00:00
2017-04-12 21:46:46 +00:00
self._related_radio = related_radio
wx.Button.__init__( self, parent )
self.Bind( wx.EVT_MOUSE_EVENTS, self.EventMouse )
self._SetShortcutString()
def _SetShortcutString( self ):
display_string = self._shortcut.ToString()
self.SetLabel( display_string )
def EventMouse( self, event ):
2017-09-13 20:50:41 +00:00
self.SetFocus()
2017-04-12 21:46:46 +00:00
shortcut = ClientData.ConvertMouseEventToShortcut( event )
if shortcut is not None:
2013-10-09 18:13:42 +00:00
2017-04-12 21:46:46 +00:00
self._shortcut = shortcut
if self._related_radio is not None:
self._related_radio.SetValue( True )
2013-10-09 18:13:42 +00:00
2015-02-03 20:40:21 +00:00
self._SetShortcutString()
2017-04-12 21:46:46 +00:00
2013-10-09 18:13:42 +00:00
2015-02-03 20:40:21 +00:00
2017-04-12 21:46:46 +00:00
def GetValue( self ):
return self._shortcut
2015-02-03 20:40:21 +00:00
2017-04-12 21:46:46 +00:00
def SetValue( self, shortcut ):
2013-10-09 18:13:42 +00:00
2017-04-12 21:46:46 +00:00
self._shortcut = shortcut
2015-02-03 20:40:21 +00:00
self._SetShortcutString()
2013-10-09 18:13:42 +00:00
2015-02-03 20:40:21 +00:00
class StaticBox( wx.Panel ):
2013-10-09 18:13:42 +00:00
2015-02-03 20:40:21 +00:00
def __init__( self, parent, title ):
2013-10-09 18:13:42 +00:00
2015-02-03 20:40:21 +00:00
wx.Panel.__init__( self, parent, style = wx.BORDER_DOUBLE )
2013-10-09 18:13:42 +00:00
2016-08-10 19:04:08 +00:00
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_FRAMEBK ) )
2015-02-03 20:40:21 +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 )
2018-01-03 22:37:30 +00:00
self._sizer.Add( title_text, CC.FLAGS_EXPAND_PERPENDICULAR )
2015-02-03 20:40:21 +00:00
self.SetSizer( self._sizer )
2013-10-09 18:13:42 +00:00
2018-01-31 22:58:15 +00:00
def Add( self, widget, flags ):
self._sizer.Add( widget, flags )
2015-02-03 20:40:21 +00:00
class StaticBoxSorterForListBoxTags( StaticBox ):
def __init__( self, parent, title ):
2013-10-09 18:13:42 +00:00
2015-02-03 20:40:21 +00:00
StaticBox.__init__( self, parent, title )
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 )
2016-04-27 19:20:37 +00:00
self._sorter.Append( 'lexicographic (a-z) (grouped by namespace)', CC.SORT_BY_LEXICOGRAPHIC_NAMESPACE_ASC )
self._sorter.Append( 'lexicographic (z-a) (grouped by namespace)', CC.SORT_BY_LEXICOGRAPHIC_NAMESPACE_DESC )
2015-02-03 20:40:21 +00:00
self._sorter.Append( 'incidence (desc)', CC.SORT_BY_INCIDENCE_DESC )
self._sorter.Append( 'incidence (asc)', CC.SORT_BY_INCIDENCE_ASC )
2016-09-28 18:48:01 +00:00
self._sorter.Append( 'incidence (desc) (grouped by namespace)', CC.SORT_BY_INCIDENCE_NAMESPACE_DESC )
self._sorter.Append( 'incidence (asc) (grouped by namespace)', CC.SORT_BY_INCIDENCE_NAMESPACE_ASC )
2015-02-03 20:40:21 +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 )
2016-04-27 19:20:37 +00:00
elif HC.options[ 'default_tag_sort' ] == CC.SORT_BY_LEXICOGRAPHIC_NAMESPACE_ASC: self._sorter.Select( 2 )
elif HC.options[ 'default_tag_sort' ] == CC.SORT_BY_LEXICOGRAPHIC_NAMESPACE_DESC: self._sorter.Select( 3 )
elif HC.options[ 'default_tag_sort' ] == CC.SORT_BY_INCIDENCE_DESC: self._sorter.Select( 4 )
elif HC.options[ 'default_tag_sort' ] == CC.SORT_BY_INCIDENCE_ASC: self._sorter.Select( 5 )
2016-09-28 18:48:01 +00:00
elif HC.options[ 'default_tag_sort' ] == CC.SORT_BY_INCIDENCE_NAMESPACE_DESC: self._sorter.Select( 6 )
elif HC.options[ 'default_tag_sort' ] == CC.SORT_BY_INCIDENCE_NAMESPACE_ASC: self._sorter.Select( 7 )
2015-02-03 20:40:21 +00:00
self._sorter.Bind( wx.EVT_CHOICE, self.EventSort )
2018-01-03 22:37:30 +00:00
self.Add( self._sorter, CC.FLAGS_EXPAND_PERPENDICULAR )
2015-02-03 20:40:21 +00:00
2016-01-13 22:08:19 +00:00
def ChangeTagService( self, service_key ): self._tags_box.ChangeTagService( service_key )
2015-02-03 20:40:21 +00:00
def EventSort( self, event ):
selection = self._sorter.GetSelection()
if selection != wx.NOT_FOUND:
2013-10-09 18:13:42 +00:00
2015-02-03 20:40:21 +00:00
sort = self._sorter.GetClientData( selection )
2013-10-09 18:13:42 +00:00
2015-02-03 20:40:21 +00:00
self._tags_box.SetSort( sort )
2013-10-09 18:13:42 +00:00
2015-02-03 20:40:21 +00:00
def SetTagsBox( self, tags_box ):
2013-02-19 00:11:43 +00:00
2015-02-03 20:40:21 +00:00
self._tags_box = tags_box
2013-02-19 00:11:43 +00:00
2018-01-03 22:37:30 +00:00
self.Add( self._tags_box, CC.FLAGS_EXPAND_BOTH_WAYS )
2013-02-19 00:11:43 +00:00
2015-02-03 20:40:21 +00:00
2015-11-11 21:20:41 +00:00
def SetTagsByMedia( self, media, force_reload = False ):
self._tags_box.SetTagsByMedia( media, force_reload = force_reload )
2015-02-03 20:40:21 +00:00
2017-09-27 21:52:54 +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 )
2018-01-03 22:37:30 +00:00
self.Add( radio_button, CC.FLAGS_EXPAND_PERPENDICULAR )
2017-09-27 21:52:54 +00:00
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[ initial_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 ]
2017-11-29 21:48:23 +00:00
def SetSelection( self, index ):
self._indices_to_radio_buttons[ index ].SetValue( True )
2017-09-27 21:52:54 +00:00
2017-11-29 21:48:23 +00:00
def SetString( self, index, text ):
self._indices_to_radio_buttons[ index ].SetLabelText( text )
2017-09-27 21:52:54 +00:00
2017-01-11 22:31:30 +00:00
class TextAndGauge( wx.Panel ):
def __init__( self, parent ):
wx.Panel.__init__( self, parent )
2017-04-19 20:58:30 +00:00
self._st = BetterStaticText( self )
2017-01-11 22:31:30 +00:00
self._gauge = Gauge( self )
vbox = wx.BoxSizer( wx.VERTICAL )
2018-01-03 22:37:30 +00:00
vbox.Add( self._st, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.Add( self._gauge, CC.FLAGS_EXPAND_PERPENDICULAR )
2017-01-11 22:31:30 +00:00
self.SetSizer( vbox )
def SetValue( self, text, value, range ):
2017-11-29 21:48:23 +00:00
if not self:
return
2017-07-05 21:09:28 +00:00
if text != self._st.GetLabelText():
self._st.SetLabelText( text )
2017-01-11 22:31:30 +00:00
2017-07-12 20:03:45 +00:00
self._gauge.SetRange( range )
self._gauge.SetValue( value )
2017-01-11 22:31:30 +00:00
2017-12-13 22:33:07 +00:00
class TextAndPasteCtrl( wx.Panel ):
def __init__( self, parent, add_callable ):
self._add_callable = add_callable
wx.Panel.__init__( self, parent )
self._text_input = wx.TextCtrl( self, style = wx.TE_PROCESS_ENTER )
self._text_input.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
self._paste_button = BetterBitmapButton( self, CC.GlobalBMPs.paste, self._Paste )
2018-01-03 22:37:30 +00:00
self._paste_button.SetToolTip( 'Paste multiple inputs from the clipboard. Assumes the texts are newline-separated.' )
2017-12-13 22:33:07 +00:00
#
hbox = wx.BoxSizer( wx.HORIZONTAL )
2018-01-03 22:37:30 +00:00
hbox.Add( self._text_input, CC.FLAGS_EXPAND_BOTH_WAYS )
hbox.Add( self._paste_button, CC.FLAGS_VCENTER )
2017-12-13 22:33:07 +00:00
self.SetSizer( hbox )
def _Paste( self ):
raw_text = HG.client_controller.GetClipboardText()
try:
texts = [ text for text in HydrusText.DeserialiseNewlinedTexts( raw_text ) if text != '' ]
if len( texts ) > 0:
self._add_callable( texts )
except:
wx.MessageBox( 'I could not understand what was in the clipboard' )
def EventKeyDown( self, event ):
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
if key in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ):
text = self._text_input.GetValue()
if text != '':
self._add_callable( ( text, ) )
self._text_input.SetValue( '' )
else:
event.Skip()
def GetValue( self ):
return self._text_input.GetValue()
def SetValue( self, text ):
self._text_input.SetValue( text )
2017-02-01 21:11:17 +00:00
class ThreadToGUIUpdater( object ):
2018-03-07 22:48:29 +00:00
def __init__( self, win, func ):
2017-02-01 21:11:17 +00:00
2018-03-07 22:48:29 +00:00
self._win = win
2017-02-01 21:11:17 +00:00
self._func = func
self._lock = threading.Lock()
self._dirty_count = 0
2018-03-07 22:48:29 +00:00
2017-02-01 21:11:17 +00:00
self._args = None
self._kwargs = None
2018-03-07 22:48:29 +00:00
self._doing_it = False
2017-02-01 21:11:17 +00:00
2018-03-07 22:48:29 +00:00
def WXDoIt( self ):
2017-02-01 21:11:17 +00:00
with self._lock:
2018-03-07 22:48:29 +00:00
if not self._win:
self._win = None
return
2017-06-21 21:15:59 +00:00
try:
self._func( *self._args, **self._kwargs )
except HydrusExceptions.ShutdownException:
pass
2017-02-01 21:11:17 +00:00
self._dirty_count = 0
2018-03-07 22:48:29 +00:00
self._doing_it = False
2017-02-01 21:11:17 +00:00
2018-03-07 22:48:29 +00:00
# the point here is that we can spam this a hundred times a second, updating the args and kwargs, and wx will catch up to it when it can
2017-02-01 21:11:17 +00:00
# if wx feels like running fast, it'll update at 60fps
# if not, we won't get bungled up with 10,000+ pubsub events in the event queue
def Update( self, *args, **kwargs ):
with self._lock:
self._args = args
self._kwargs = kwargs
2018-03-07 22:48:29 +00:00
if not self._doing_it and not HG.view_shutdown:
2017-02-01 21:11:17 +00:00
2018-03-07 22:48:29 +00:00
wx.CallAfter( self.WXDoIt )
2017-02-01 21:11:17 +00:00
2018-03-07 22:48:29 +00:00
self._doing_it = True
2017-02-01 21:11:17 +00:00
self._dirty_count += 1
take_a_break = self._dirty_count % 1000 == 0
# just in case we are choking the wx thread, let's give it a break every now and then
if take_a_break:
time.sleep( 0.25 )