hydrus/include/ClientGUIDialogs.py

9761 lines
387 KiB
Python
Executable File

import Crypto.PublicKey.RSA
import HydrusConstants as HC
import HydrusMessageHandling
import ClientConstants as CC
import ClientConstantsMessages
import ClientGUICommon
import collections
import os
import random
import re
import subprocess
import time
import traceback
import urllib
import wx
import yaml
# Option Enums
ID_NULL = wx.NewId()
ID_TIMER_UPDATE = wx.NewId()
# Hue is generally 200, Sat and Lum changes based on need
COLOUR_SELECTED = wx.Colour( 217, 242, 255 )
COLOUR_SELECTED_DARK = wx.Colour( 1, 17, 26 )
COLOUR_UNSELECTED = wx.Colour( 223, 227, 230 )
# Sizer Flags
FLAGS_NONE = wx.SizerFlags( 0 )
FLAGS_SMALL_INDENT = wx.SizerFlags( 0 ).Border( wx.ALL, 2 )
FLAGS_BIG_INDENT = wx.SizerFlags( 0 ).Border( wx.ALL, 8 )
FLAGS_EXPAND_PERPENDICULAR = wx.SizerFlags( 0 ).Border( wx.ALL, 2 ).Expand()
FLAGS_EXPAND_BOTH_WAYS = wx.SizerFlags( 2 ).Border( wx.ALL, 2 ).Expand()
FLAGS_EXPAND_DEPTH_ONLY = wx.SizerFlags( 2 ).Border( wx.ALL, 2 ).Align( wx.ALIGN_CENTER_VERTICAL )
FLAGS_EXPAND_SIZER_PERPENDICULAR = wx.SizerFlags( 0 ).Expand()
FLAGS_EXPAND_SIZER_BOTH_WAYS = wx.SizerFlags( 2 ).Expand()
FLAGS_EXPAND_SIZER_DEPTH_ONLY = wx.SizerFlags( 2 ).Align( wx.ALIGN_CENTER_VERTICAL )
FLAGS_BUTTON_SIZERS = wx.SizerFlags( 0 ).Align( wx.ALIGN_RIGHT )
FLAGS_LONE_BUTTON = wx.SizerFlags( 0 ).Border( wx.ALL, 2 ).Align( wx.ALIGN_RIGHT )
FLAGS_MIXED = wx.SizerFlags( 0 ).Border( wx.ALL, 2 ).Align( wx.ALIGN_CENTER_VERTICAL )
def SelectServiceIdentifier( permission = None, service_types = HC.ALL_SERVICES, service_identifiers = None, unallowed = None ):
if service_identifiers is None:
services = wx.GetApp().Read( 'services', service_types )
if permission is not None: services = [ service for service in services if service.GetAccount().HasPermission( permission ) ]
service_identifiers = [ service.GetServiceIdentifier() for service in services ]
if unallowed is not None: service_identifiers.difference_update( unallowed )
if len( service_identifiers ) == 0: return None
elif len( service_identifiers ) == 1:
( service_identifier, ) = service_identifiers
return service_identifier
else:
names_to_service_identifiers = { service_identifier.GetName() : service_identifier for service_identifier in service_identifiers }
with DialogSelectFromListOfStrings( wx.GetApp().GetGUI(), 'select service', [ service_identifier.GetName() for service_identifier in service_identifiers ] ) as dlg:
if dlg.ShowModal() == wx.ID_OK: return names_to_service_identifiers[ dlg.GetString() ]
else: return None
def ShowMessage( parent, message ):
with DialogMessage( parent, message ) as dlg: dlg.ShowModal()
class Dialog( wx.Dialog ):
def __init__( self, parent, title, style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, position = 'topleft' ):
self._options = wx.GetApp().Read( 'options' )
if position == 'topleft':
( pos_x, pos_y ) = wx.GetApp().GetGUI().GetPositionTuple()
pos = ( pos_x + 50, pos_y + 100 )
else: pos = ( -1, -1 )
wx.Dialog.__init__( self, parent, title = title, style = style, pos = pos )
self.SetDoubleBuffered( True )
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
self.SetIcon( wx.Icon( HC.STATIC_DIR + os.path.sep + 'hydrus.ico', wx.BITMAP_TYPE_ICO ) )
if position == 'center': wx.CallAfter( self.Center )
class DialogChooseNewServiceMethod( Dialog ):
def __init__( self, parent ):
def InitialiseControls():
register_message = 'I want to set up a new account. I have a registration key (a key starting with \'r\').'
self._register = wx.Button( self, id = wx.ID_OK, label = register_message )
self._register.Bind( wx.EVT_BUTTON, self.EventRegister )
setup_message = 'The account is already set up; I just want to add it to this client. I have a normal access key.'
self._setup = wx.Button( self, id = wx.ID_OK, label = setup_message )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label = 'cancel', size = ( 0, 0 ) )
def InitialisePanel():
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._register, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( wx.StaticText( self, label = '-or-', style = wx.ALIGN_CENTER ), FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._setup, FLAGS_EXPAND_PERPENDICULAR )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'how to set up the account?', position = 'center' )
InitialiseControls()
InitialisePanel()
self._register = False
def EventRegister( self, event ):
self._register = True
self.EndModal( wx.ID_OK )
def GetRegister( self ): return self._register
class DialogFinishFiltering( Dialog ):
def __init__( self, parent, num_kept, num_deleted, keep = 'Keep', delete = 'delete' ):
def InitialiseControls():
self._commit = wx.Button( self, label = 'commit' )
self._commit.Bind( wx.EVT_BUTTON, self.EventCommit )
self._commit.SetForegroundColour( ( 0, 128, 0 ) )
self._forget = wx.Button( self, label = 'forget' )
self._forget.Bind( wx.EVT_BUTTON, self.EventForget )
self._forget.SetForegroundColour( ( 128, 0, 0 ) )
self._back = wx.Button( self, id = wx.ID_CANCEL, label = 'back to filtering' )
self._back.Bind( wx.EVT_BUTTON, self.EventBack )
def InitialisePanel():
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( self._commit, FLAGS_EXPAND_BOTH_WAYS )
hbox.AddF( self._forget, FLAGS_EXPAND_BOTH_WAYS )
vbox = wx.BoxSizer( wx.VERTICAL )
label = keep + ' ' + HC.ConvertIntToPrettyString( num_kept ) + ' and ' + delete + ' ' + HC.ConvertIntToPrettyString( num_deleted ) + ' files?'
vbox.AddF( wx.StaticText( self, label = label, style = wx.ALIGN_CENTER ), FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( wx.StaticText( self, label = '-or-', style = wx.ALIGN_CENTER ), FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._back, FLAGS_EXPAND_PERPENDICULAR )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'are you sure?', position = 'center' )
InitialiseControls()
InitialisePanel()
def EventBack( self, event ): self.EndModal( wx.ID_CANCEL )
def EventCommit( self, event ): self.EndModal( wx.ID_YES )
def EventForget( self, event ): self.EndModal( wx.ID_NO )
class DialogFinishRatingFiltering( Dialog ):
def __init__( self, parent, num_certain_ratings, num_uncertain_ratings ):
def InitialiseControls():
self._commit = wx.Button( self, label = 'commit' )
self._commit.Bind( wx.EVT_BUTTON, self.EventCommit )
self._commit.SetForegroundColour( ( 0, 128, 0 ) )
self._forget = wx.Button( self, label = 'forget' )
self._forget.Bind( wx.EVT_BUTTON, self.EventForget )
self._forget.SetForegroundColour( ( 128, 0, 0 ) )
self._back = wx.Button( self, id = wx.ID_CANCEL, label = 'back to filtering' )
self._back.Bind( wx.EVT_BUTTON, self.EventBack )
def InitialisePanel():
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( self._commit, FLAGS_EXPAND_BOTH_WAYS )
hbox.AddF( self._forget, FLAGS_EXPAND_BOTH_WAYS )
vbox = wx.BoxSizer( wx.VERTICAL )
info_strings = []
if num_certain_ratings > 0: info_strings.append( HC.ConvertIntToPrettyString( num_certain_ratings ) + ' ratings' )
if num_uncertain_ratings > 0: info_strings.append( HC.ConvertIntToPrettyString( num_uncertain_ratings ) + ' uncertain changes' )
label = 'Apply ' + ' and '.join( info_strings ) + '?'
vbox.AddF( wx.StaticText( self, label = label, style = wx.ALIGN_CENTER ), FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( wx.StaticText( self, label = '-or-', style = wx.ALIGN_CENTER ), FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._back, FLAGS_EXPAND_PERPENDICULAR )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'are you sure?', position = 'center' )
InitialiseControls()
InitialisePanel()
def EventBack( self, event ): self.EndModal( wx.ID_CANCEL )
def EventCommit( self, event ): self.EndModal( wx.ID_YES )
def EventForget( self, event ): self.EndModal( wx.ID_NO )
class DialogFirstStart( Dialog ):
def __init__( self, parent ):
def InitialiseControls():
self._ok = wx.Button( self, id = wx.ID_OK, label = 'ok!' )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label = 'cancel', size = ( 0, 0 ) )
def InitialisePanel():
message1 = 'Hi, this looks like the first time you have started the hydrus client. Don\'t forget to check out the'
link = wx.HyperlinkCtrl( self, id = -1, label = 'help', url = 'file://' + HC.BASE_DIR + '/help/index.html' )
message2 = 'if you haven\'t already.'
message3 = 'When you close this dialog, the client will start its local http server. You will probably get a firewall warning.'
message4 = 'You can block it if you like, or you can allow it. It doesn\'t phone home, or expose your files to your network; it just provides another way to locally export your files.'
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self, label = message1 ), FLAGS_MIXED )
hbox.AddF( link, FLAGS_MIXED )
hbox.AddF( wx.StaticText( self, label = message2 ), FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( wx.StaticText( self, label = message3 ), FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( wx.StaticText( self, label = message4 ), FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._ok, FLAGS_LONE_BUTTON )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'First start', position = 'center' )
InitialiseControls()
InitialisePanel()
class DialogInputCustomFilterAction( Dialog ):
def __init__( self, parent, modifier = wx.ACCEL_NORMAL, key = wx.WXK_F7, service_identifier = None, action = 'archive' ):
self._service_identifier = service_identifier
self._action = action
self._current_ratings_like_service = None
self._current_ratings_numerical_service = None
def InitialiseControls():
service_identifiers = wx.GetApp().Read( 'service_identifiers', ( HC.LOCAL_TAG, HC.TAG_REPOSITORY, HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ) )
self._shortcut_panel = ClientGUICommon.StaticBox( self, 'shortcut' )
self._shortcut = ClientGUICommon.Shortcut( self._shortcut_panel, modifier, key )
self._none_panel = ClientGUICommon.StaticBox( self, 'non-service actions' )
self._none_actions = wx.Choice( self._none_panel, choices = [ 'manage_tags', 'manage_ratings', 'archive', 'inbox', 'delete', 'fullscreen_switch', 'frame_back', 'frame_next', 'previous', 'next', 'first', 'last' ] )
self._ok_none = wx.Button( self._none_panel, label = 'ok' )
self._ok_none.Bind( wx.EVT_BUTTON, self.EventOKNone )
self._ok_none.SetForegroundColour( ( 0, 128, 0 ) )
self._tag_panel = ClientGUICommon.StaticBox( self, 'tag service actions' )
self._tag_service_identifiers = wx.Choice( self._tag_panel )
self._tag_value = wx.TextCtrl( self._tag_panel, style = wx.TE_READONLY )
self._tag_input = ClientGUICommon.AutoCompleteDropdownTagsWrite( self._tag_panel, self.SetTag, HC.LOCAL_FILE_SERVICE_IDENTIFIER, HC.NULL_SERVICE_IDENTIFIER )
self._ok_tag = wx.Button( self._tag_panel, label = 'ok' )
self._ok_tag.Bind( wx.EVT_BUTTON, self.EventOKTag )
self._ok_tag.SetForegroundColour( ( 0, 128, 0 ) )
self._ratings_like_panel = ClientGUICommon.StaticBox( self, 'ratings like service actions' )
self._ratings_like_service_identifiers = wx.Choice( self._ratings_like_panel )
self._ratings_like_service_identifiers.Bind( wx.EVT_CHOICE, self.EventRecalcActions )
self._ratings_like_like = wx.RadioButton( self._ratings_like_panel, style = wx.RB_GROUP, label = 'like' )
self._ratings_like_dislike = wx.RadioButton( self._ratings_like_panel, label = 'dislike' )
self._ratings_like_remove = wx.RadioButton( self._ratings_like_panel, label = 'remove rating' )
self._ok_ratings_like = wx.Button( self._ratings_like_panel, label = 'ok' )
self._ok_ratings_like.Bind( wx.EVT_BUTTON, self.EventOKRatingsLike )
self._ok_ratings_like.SetForegroundColour( ( 0, 128, 0 ) )
self._ratings_numerical_panel = ClientGUICommon.StaticBox( self, 'ratings numerical service actions' )
self._ratings_numerical_service_identifiers = wx.Choice( self._ratings_numerical_panel )
self._ratings_numerical_service_identifiers.Bind( wx.EVT_CHOICE, self.EventRecalcActions )
self._ratings_numerical_slider = wx.Slider( self._ratings_numerical_panel, style = wx.SL_AUTOTICKS | wx.SL_LABELS )
self._ratings_numerical_remove = wx.CheckBox( self._ratings_numerical_panel, label = 'remove rating' )
self._ok_ratings_numerical = wx.Button( self._ratings_numerical_panel, label = 'ok' )
self._ok_ratings_numerical.Bind( wx.EVT_BUTTON, self.EventOKRatingsNumerical )
self._ok_ratings_numerical.SetForegroundColour( ( 0, 128, 0 ) )
for service_identifier in service_identifiers:
service_type = service_identifier.GetType()
if service_type in ( HC.LOCAL_TAG, HC.TAG_REPOSITORY ): choice = self._tag_service_identifiers
elif service_type == HC.LOCAL_RATING_LIKE: choice = self._ratings_like_service_identifiers
elif service_type == HC.LOCAL_RATING_NUMERICAL: choice = self._ratings_numerical_service_identifiers
choice.Append( service_identifier.GetName(), service_identifier )
self._SetActions()
if self._service_identifier is None:
self._none_actions.SetStringSelection( self._action )
else:
service_name = self._service_identifier.GetName()
service_type = self._service_identifier.GetType()
if service_type in ( HC.LOCAL_TAG, HC.TAG_REPOSITORY ):
self._tag_service_identifiers.SetStringSelection( service_name )
self._tag_value.SetValue( self._action )
elif service_type == HC.LOCAL_RATING_LIKE:
self._ratings_like_service_identifiers.SetStringSelection( service_name )
self._SetActions()
if self._action is None: self._ratings_like_remove.SetValue( True )
elif self._action == True: self._ratings_like_like.SetValue( True )
elif self._action == False: self._ratings_like_dislike.SetValue( True )
elif service_type == HC.LOCAL_RATING_NUMERICAL:
self._ratings_numerical_service_identifiers.SetStringSelection( service_name )
self._SetActions()
if self._action is None: self._ratings_numerical_remove.SetValue( True )
else:
( lower, upper ) = self._current_ratings_numerical_service.GetExtraInfo()
slider_value = int( self._action * ( upper - lower ) ) + lower
self._ratings_numerical_slider.SetValue( slider_value )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label='cancel' )
self._cancel.Bind( wx.EVT_BUTTON, self.EventCancel )
self._cancel.SetSize( ( 0, 0 ) )
def InitialisePanel():
self._shortcut_panel.AddF( self._shortcut, FLAGS_EXPAND_PERPENDICULAR )
none_hbox = wx.BoxSizer( wx.HORIZONTAL )
none_hbox.AddF( self._none_actions, FLAGS_EXPAND_DEPTH_ONLY )
none_hbox.AddF( self._ok_none, FLAGS_MIXED )
self._none_panel.AddF( none_hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
tag_sub_vbox = wx.BoxSizer( wx.VERTICAL )
tag_sub_vbox.AddF( self._tag_value, FLAGS_EXPAND_BOTH_WAYS )
tag_sub_vbox.AddF( self._tag_input, FLAGS_EXPAND_BOTH_WAYS )
tag_hbox = wx.BoxSizer( wx.HORIZONTAL )
tag_hbox.AddF( self._tag_service_identifiers, FLAGS_EXPAND_DEPTH_ONLY )
tag_hbox.AddF( tag_sub_vbox, FLAGS_EXPAND_SIZER_BOTH_WAYS )
tag_hbox.AddF( self._ok_tag, FLAGS_MIXED )
self._tag_panel.AddF( tag_hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
ratings_like_hbox = wx.BoxSizer( wx.HORIZONTAL )
ratings_like_hbox.AddF( self._ratings_like_service_identifiers, FLAGS_EXPAND_DEPTH_ONLY )
ratings_like_hbox.AddF( self._ratings_like_like, FLAGS_MIXED )
ratings_like_hbox.AddF( self._ratings_like_dislike, FLAGS_MIXED )
ratings_like_hbox.AddF( self._ratings_like_remove, FLAGS_MIXED )
ratings_like_hbox.AddF( self._ok_ratings_like, FLAGS_MIXED )
self._ratings_like_panel.AddF( ratings_like_hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
ratings_numerical_hbox = wx.BoxSizer( wx.HORIZONTAL )
ratings_numerical_hbox.AddF( self._ratings_numerical_service_identifiers, FLAGS_EXPAND_DEPTH_ONLY )
ratings_numerical_hbox.AddF( self._ratings_numerical_slider, FLAGS_MIXED )
ratings_numerical_hbox.AddF( self._ratings_numerical_remove, FLAGS_MIXED )
ratings_numerical_hbox.AddF( self._ok_ratings_numerical, FLAGS_MIXED )
self._ratings_numerical_panel.AddF( ratings_numerical_hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._none_panel, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._tag_panel, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._ratings_like_panel, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._ratings_numerical_panel, FLAGS_EXPAND_PERPENDICULAR )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( self._shortcut_panel, FLAGS_MIXED )
hbox.AddF( wx.StaticText( self, label = u'\u2192' ), FLAGS_MIXED )
hbox.AddF( vbox, FLAGS_EXPAND_SIZER_BOTH_WAYS )
self.SetSizer( hbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( 680, y ) )
Dialog.__init__( self, parent, 'input custom filter action' )
InitialiseControls()
InitialisePanel()
def _SetActions( self ):
if self._ratings_like_service_identifiers.GetCount() > 0:
selection = self._ratings_like_service_identifiers.GetSelection()
if selection != wx.NOT_FOUND:
service_identifier = self._ratings_like_service_identifiers.GetClientData( selection )
service = wx.GetApp().Read( 'service', service_identifier )
self._current_ratings_like_service = service
( like, dislike ) = service.GetExtraInfo()
self._ratings_like_like.SetLabel( like )
self._ratings_like_dislike.SetLabel( dislike )
else:
self._ratings_like_like.SetLabel( 'like' )
self._ratings_like_dislike.SetLabel( 'dislike' )
if self._ratings_numerical_service_identifiers.GetCount() > 0:
selection = self._ratings_numerical_service_identifiers.GetSelection()
if selection != wx.NOT_FOUND:
service_identifier = self._ratings_numerical_service_identifiers.GetClientData( selection )
service = wx.GetApp().Read( 'service', service_identifier )
self._current_ratings_numerical_service = service
( lower, upper ) = service.GetExtraInfo()
self._ratings_numerical_slider.SetRange( lower, upper )
else: self._ratings_numerical_slider.SetRange( 0, 5 )
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventOKNone( self, event ):
self._service_identifier = None
self._action = self._none_actions.GetStringSelection()
self._pretty_action = self._action
self.EndModal( wx.ID_OK )
def EventOKRatingsLike( self, event ):
selection = self._ratings_like_service_identifiers.GetSelection()
if selection != wx.NOT_FOUND:
self._service_identifier = self._ratings_like_service_identifiers.GetClientData( selection )
( like, dislike ) = self._current_ratings_like_service.GetExtraInfo()
if self._ratings_like_like.GetValue():
self._action = 1.0
self._pretty_action = like
elif self._ratings_like_dislike.GetValue():
self._action = 0.0
self._pretty_action = dislike
else:
self._action = None
self._pretty_action = 'remove'
self.EndModal( wx.ID_OK )
else: self.EndModal( wx.ID_CANCEL )
def EventOKRatingsNumerical( self, event ):
selection = self._ratings_numerical_service_identifiers.GetSelection()
if selection != wx.NOT_FOUND:
self._service_identifier = self._ratings_numerical_service_identifiers.GetClientData( selection )
if self._ratings_numerical_remove.GetValue():
self._action = None
self._pretty_action = 'remove'
else:
self._pretty_action = str( self._ratings_numerical_slider.GetValue() )
( lower, upper ) = self._current_ratings_numerical_service.GetExtraInfo()
self._action = ( float( self._pretty_action ) - float( lower ) ) / ( upper - lower )
self.EndModal( wx.ID_OK )
else: self.EndModal( wx.ID_CANCEL )
def EventOKTag( self, event ):
selection = self._tag_service_identifiers.GetSelection()
if selection != wx.NOT_FOUND:
self._service_identifier = self._tag_service_identifiers.GetClientData( selection )
self._action = self._tag_value.GetValue()
self._pretty_action = self._action
self.EndModal( wx.ID_OK )
else: self.EndModal( wx.ID_CANCEL )
def EventRecalcActions( self, event ):
self._SetActions()
event.Skip()
def GetInfo( self ):
( modifier, key ) = self._shortcut.GetValue()
if self._service_identifier is None: pretty_service_identifier = ''
else: pretty_service_identifier = self._service_identifier.GetName()
# ignore this pretty_action
( pretty_modifier, pretty_key, pretty_action ) = HC.ConvertShortcutToPrettyShortcut( modifier, key, self._action )
return ( ( pretty_modifier, pretty_key, pretty_service_identifier, self._pretty_action ), ( modifier, key, self._service_identifier, self._action ) )
def SetTag( self, tag ): self._tag_value.SetValue( tag )
class DialogInputNamespaceRegex( Dialog ):
def __init__( self, parent, namespace = '', regex = '' ):
def InitialiseControls():
self._namespace = wx.TextCtrl( self )
self._namespace.SetValue( namespace )
self._regex = wx.TextCtrl( self )
self._regex.SetValue( regex )
self._shortcuts = ClientGUICommon.RegexButton( self )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label='Cancel' )
self._cancel.Bind( wx.EVT_BUTTON, self.EventCancel )
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
def InitialisePanel():
control_box = wx.BoxSizer( wx.HORIZONTAL )
control_box.AddF( self._namespace, FLAGS_EXPAND_BOTH_WAYS )
control_box.AddF( wx.StaticText( self, label = ':' ), FLAGS_MIXED )
control_box.AddF( self._regex, FLAGS_EXPAND_BOTH_WAYS )
b_box = wx.BoxSizer( wx.HORIZONTAL )
b_box.AddF( self._ok, FLAGS_MIXED )
b_box.AddF( self._cancel, FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( control_box, FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( self._shortcuts, FLAGS_LONE_BUTTON )
vbox.AddF( b_box, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'configure quick namespace' )
InitialiseControls()
InitialisePanel()
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventOk( self, event ): self.EndModal( wx.ID_OK )
def GetInfo( self ):
namespace = self._namespace.GetValue()
regex = self._regex.GetValue()
return ( namespace, regex )
class DialogInputNewAccounts( Dialog ):
def __init__( self, parent, service_identifier ):
def InitialiseControls():
self._num = wx.SpinCtrl( self, min=1, max=10000 )
self._num.SetValue( 1 )
service = wx.GetApp().Read( 'service', service_identifier )
connection = service.GetConnection()
account_types = connection.Get( 'account_types' )
self._account_types = wx.Choice( self, size = ( 400, -1 ) )
for account_type in account_types: self._account_types.Append( account_type.ConvertToString(), account_type )
self._account_types.SetSelection( 0 ) # admin
self._expiration = wx.Choice( self )
for ( str, value ) in HC.expirations: self._expiration.Append( str, value )
self._expiration.SetSelection( 3 ) # one year
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label='Cancel' )
self._cancel.Bind( wx.EVT_BUTTON, self.EventCancel )
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
def InitialisePanel():
ctrl_box = wx.BoxSizer( wx.HORIZONTAL )
ctrl_box.AddF( self._num, FLAGS_SMALL_INDENT )
ctrl_box.AddF( self._account_types, FLAGS_SMALL_INDENT )
ctrl_box.AddF( self._expiration, FLAGS_SMALL_INDENT )
b_box = wx.BoxSizer( wx.HORIZONTAL )
b_box.AddF( self._ok, FLAGS_MIXED )
b_box.AddF( self._cancel, FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( ctrl_box, FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( b_box, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'configure new accounts' )
self._service_identifier = service_identifier
InitialiseControls()
InitialisePanel()
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventOk( self, event ):
num = self._num.GetValue()
account_type = self._account_types.GetClientData( self._account_types.GetSelection() )
title = account_type.GetTitle()
expiration = self._expiration.GetClientData( self._expiration.GetSelection() )
service = wx.GetApp().Read( 'service', self._service_identifier )
try:
connection = service.GetConnection()
if expiration is None: access_keys = connection.Get( 'registration_keys', num = num, title = title )
else: access_keys = connection.Get( 'registration_keys', num = num, title = title, expiration = expiration )
except Exception as e: wx.MessageBox( unicode( e ) )
self.EndModal( wx.ID_OK )
class DialogInputNewAccountType( Dialog ):
def __init__( self, parent, account_type = None ):
def InitialiseControls():
self._title = wx.TextCtrl( self, value = title )
self._permissions_panel = ClientGUICommon.StaticBox( self, 'permissions' )
self._permissions = wx.ListBox( self._permissions_panel )
for permission in permissions: self._permissions.Append( HC.permissions_string_lookup[ permission ], permission )
self._permission_choice = wx.Choice( self._permissions_panel )
for permission in HC.CREATABLE_PERMISSIONS: self._permission_choice.Append( HC.permissions_string_lookup[ permission ], permission )
self._permission_choice.SetSelection( 0 )
self._add_permission = wx.Button( self._permissions_panel, label = 'add' )
self._add_permission.Bind( wx.EVT_BUTTON, self.EventAddPermission )
self._remove_permission = wx.Button( self._permissions_panel, label = 'remove' )
self._remove_permission.Bind( wx.EVT_BUTTON, self.EventRemovePermission )
self._max_num_mb = ClientGUICommon.NoneableSpinCtrl( self, 'max monthly data (MB)', max_num_bytes, multiplier = 1048576 )
self._max_num_requests = ClientGUICommon.NoneableSpinCtrl( self, 'max monthly requests', max_num_requests )
self._apply = wx.Button( self, label='apply' )
self._apply.Bind( wx.EVT_BUTTON, self.EventOk )
self._apply.SetForegroundColour( ( 0, 128, 0 ) )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label='cancel' )
self._cancel.Bind( wx.EVT_BUTTON, self.EventCancel )
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
def InitialisePanel():
t_box = wx.BoxSizer( wx.HORIZONTAL )
t_box.AddF( wx.StaticText( self, label = 'title: ' ), FLAGS_SMALL_INDENT )
t_box.AddF( self._title, FLAGS_EXPAND_BOTH_WAYS )
perm_buttons_box = wx.BoxSizer( wx.HORIZONTAL )
perm_buttons_box.AddF( self._permission_choice, FLAGS_MIXED )
perm_buttons_box.AddF( self._add_permission, FLAGS_MIXED )
perm_buttons_box.AddF( self._remove_permission, FLAGS_MIXED )
self._permissions_panel.AddF( self._permissions, FLAGS_EXPAND_BOTH_WAYS )
self._permissions_panel.AddF( perm_buttons_box, FLAGS_EXPAND_SIZER_PERPENDICULAR )
b_box = wx.BoxSizer( wx.HORIZONTAL )
b_box.AddF( self._apply, FLAGS_MIXED )
b_box.AddF( self._cancel, FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( t_box, FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( self._permissions_panel, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._max_num_mb, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._max_num_requests, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( b_box, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( 800, y ) )
if account_type is None:
title = ''
permissions = [ HC.GET_DATA ]
max_num_bytes = 104857600
max_num_requests = 1000
else:
title = account_type.GetTitle()
permissions = account_type.GetPermissions()
( max_num_bytes, max_num_requests ) = account_type.GetMaxMonthlyData()
Dialog.__init__( self, parent, 'edit account type' )
InitialiseControls()
InitialisePanel()
def EventAddPermission( self, event ):
selection = self._permission_choice.GetSelection()
if selection != wx.NOT_FOUND:
permission = self._permission_choice.GetClientData( selection )
existing_permissions = [ self._permissions.GetClientData( i ) for i in range( self._permissions.GetCount() ) ]
if permission not in existing_permissions: self._permissions.Append( HC.permissions_string_lookup[ permission ], permission )
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventCheckBox( self, event ):
if self._max_num_requests_checkbox.GetValue(): self._max_num_requests.Disable()
else: self._max_num_requests.Enable()
def EventOk( self, event ): self.EndModal( wx.ID_OK )
def EventRemovePermission( self, event ):
selection = self._permissions.GetSelection()
if selection != wx.NOT_FOUND: self._permissions.Delete( selection )
def GetAccountType( self ):
title = self._title.GetValue()
permissions = [ self._permissions.GetClientData( i ) for i in range( self._permissions.GetCount() ) ]
max_num_bytes = self._max_num_mb.GetValue()
max_num_requests = self._max_num_requests.GetValue()
return HC.AccountType( title, permissions, ( max_num_bytes, max_num_requests ) )
class DialogInputNewFormField( Dialog ):
def __init__( self, parent, form_field = None ):
if form_field is None: ( name, type, default, editable ) = ( '', CC.FIELD_TEXT, '', True )
else: ( name, type, default, editable ) = form_field
def InitialiseControls():
self._name = wx.TextCtrl( self, value = name )
self._type = wx.Choice( self )
for temp_type in CC.FIELDS: self._type.Append( CC.field_string_lookup[ temp_type ], temp_type )
self._type.Select( type )
self._default = wx.TextCtrl( self, value = default )
self._editable = wx.CheckBox( self )
self._editable.SetValue( editable )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label='Cancel' )
self._cancel.Bind( wx.EVT_BUTTON, self.EventCancel )
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
def InitialisePanel():
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 1, 1 )
gridbox.AddF( wx.StaticText( self, label='name' ), FLAGS_MIXED )
gridbox.AddF( self._name, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( self, label='type' ), FLAGS_MIXED )
gridbox.AddF( self._type, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( self, label='default' ), FLAGS_MIXED )
gridbox.AddF( self._default, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( self, label='editable' ), FLAGS_MIXED )
gridbox.AddF( self._editable, FLAGS_EXPAND_BOTH_WAYS )
b_box = wx.BoxSizer( wx.HORIZONTAL )
b_box.AddF( self._ok, FLAGS_MIXED )
b_box.AddF( self._cancel, FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( gridbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( b_box, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'configure form field' )
InitialiseControls()
InitialisePanel()
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventOk( self, event ): self.EndModal( wx.ID_OK )
def GetFormField( self ):
name = self._name.GetValue()
type = self._type.GetClientData( self._type.GetSelection() )
default = self._default.GetValue()
editable = self._editable.GetValue()
return ( name, type, default, editable )
class DialogInputFileSystemPredicate( Dialog ):
def __init__( self, parent, type ):
def Age():
def InitialiseControls():
( sign, years, months, days ) = system_predicates[ 'age' ]
self._sign = wx.Choice( self, choices=[ '<', u'\u2248', '>' ] )
self._sign.SetSelection( sign )
self._years = wx.SpinCtrl( self, max = 30 )
self._years.SetValue( years )
self._months = wx.SpinCtrl( self, max = 60 )
self._months.SetValue( months )
self._days = wx.SpinCtrl( self, max = 90 )
self._days.SetValue( days )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
def InitialisePanel():
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self, label='system:age' ), FLAGS_MIXED )
hbox.AddF( self._sign, FLAGS_MIXED )
hbox.AddF( self._years, FLAGS_MIXED )
hbox.AddF( wx.StaticText( self, label='years' ), FLAGS_MIXED )
hbox.AddF( self._months, FLAGS_MIXED )
hbox.AddF( wx.StaticText( self, label='months' ), FLAGS_MIXED )
hbox.AddF( self._days, FLAGS_MIXED )
hbox.AddF( wx.StaticText( self, label='days' ), FLAGS_MIXED )
hbox.AddF( self._ok, FLAGS_MIXED )
self.SetSizer( hbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'enter age predicate' )
InitialiseControls()
InitialisePanel()
def Duration():
def InitialiseControls():
( sign, s, ms ) = system_predicates[ 'duration' ]
self._sign = wx.Choice( self, choices=[ '<', u'\u2248', '=', '>' ] )
self._sign.SetSelection( sign )
self._duration_s = wx.SpinCtrl( self, max = 3599 )
self._duration_s.SetValue( s )
self._duration_ms = wx.SpinCtrl( self, max = 999 )
self._duration_ms.SetValue( ms )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
def InitialisePanel():
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self, label='system:duration' ), FLAGS_MIXED )
hbox.AddF( self._sign, FLAGS_MIXED )
hbox.AddF( self._duration_s, FLAGS_MIXED )
hbox.AddF( wx.StaticText( self, label='s' ), FLAGS_MIXED )
hbox.AddF( self._duration_ms, FLAGS_MIXED )
hbox.AddF( wx.StaticText( self, label='ms' ), FLAGS_MIXED )
hbox.AddF( self._ok, FLAGS_MIXED )
self.SetSizer( hbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'enter duration predicate' )
InitialiseControls()
InitialisePanel()
def FileService():
def InitialiseControls():
self._sign = wx.Choice( self )
self._sign.Append( 'is', True )
self._sign.Append( 'is not', False )
self._sign.SetSelection( 0 )
self._current_pending = wx.Choice( self )
self._current_pending.Append( 'currently in', HC.CURRENT )
self._current_pending.Append( 'pending to', HC.PENDING )
self._current_pending.SetSelection( 0 )
service_identifiers = wx.GetApp().Read( 'service_identifiers', ( HC.FILE_REPOSITORY, HC.LOCAL_FILE ) )
self._file_service_identifier = wx.Choice( self )
for service_identifier in service_identifiers: self._file_service_identifier.Append( service_identifier.GetName(), service_identifier )
self._file_service_identifier.SetSelection( 0 )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
def InitialisePanel():
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self, label='system:file service:' ), FLAGS_MIXED )
hbox.AddF( self._sign, FLAGS_MIXED )
hbox.AddF( self._current_pending, FLAGS_MIXED )
hbox.AddF( self._file_service_identifier, FLAGS_MIXED )
hbox.AddF( self._ok, FLAGS_MIXED )
self.SetSizer( hbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'enter file service predicate' )
InitialiseControls()
InitialisePanel()
def Hash():
def InitialiseControls():
self._hash = wx.TextCtrl( self )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
def InitialisePanel():
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self, label='system:hash=' ), FLAGS_MIXED )
hbox.AddF( self._hash, FLAGS_MIXED )
hbox.AddF( self._ok, FLAGS_MIXED )
self.SetSizer( hbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'enter hash predicate' )
InitialiseControls()
InitialisePanel()
def Height():
def InitialiseControls():
( sign, height ) = system_predicates[ 'height' ]
self._sign = wx.Choice( self, choices=[ '<', u'\u2248', '=', '>' ] )
self._sign.SetSelection( sign )
self._height = wx.SpinCtrl( self, max = 200000 )
self._height.SetValue( height )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
def InitialisePanel():
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self, label='system:height' ), FLAGS_MIXED )
hbox.AddF( self._sign, FLAGS_MIXED )
hbox.AddF( self._height, FLAGS_MIXED )
hbox.AddF( self._ok, FLAGS_MIXED )
self.SetSizer( hbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'enter height predicate' )
InitialiseControls()
InitialisePanel()
def Limit():
def InitialiseControls():
limit = system_predicates[ 'limit' ]
self._limit = wx.SpinCtrl( self, max = 1000000 )
self._limit.SetValue( limit )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
def InitialisePanel():
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self, label='system:limit=' ), FLAGS_MIXED )
hbox.AddF( self._limit, FLAGS_MIXED )
hbox.AddF( self._ok, FLAGS_MIXED )
self.SetSizer( hbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'enter limit predicate' )
InitialiseControls()
InitialisePanel()
def Mime():
def InitialiseControls():
( media, type ) = system_predicates[ 'mime' ]
self._mime_media = wx.Choice( self, choices=[ 'image', 'application', 'video' ] )
self._mime_media.SetSelection( media )
self._mime_media.Bind( wx.EVT_CHOICE, self.EventMime )
self._mime_type = wx.Choice( self, choices=[], size = ( 120, -1 ) )
self.EventMime( None )
self._mime_type.SetSelection( type )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
def InitialisePanel():
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self, label='system:mime' ), FLAGS_MIXED )
hbox.AddF( self._mime_media, FLAGS_MIXED )
hbox.AddF( wx.StaticText( self, label='/' ), FLAGS_MIXED )
hbox.AddF( self._mime_type, FLAGS_MIXED )
hbox.AddF( self._ok, FLAGS_MIXED )
self.SetSizer( hbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'enter mime predicate' )
InitialiseControls()
InitialisePanel()
def NumTags():
def InitialiseControls():
( sign, num_tags ) = system_predicates[ 'num_tags' ]
self._sign = wx.Choice( self, choices=[ '<', u'\u2248', '=', '>' ] )
self._sign.SetSelection( sign )
self._num_tags = wx.SpinCtrl( self, max = 2000 )
self._num_tags.SetValue( num_tags )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
def InitialisePanel():
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self, label='system:num_tags' ), FLAGS_MIXED )
hbox.AddF( self._sign, FLAGS_MIXED )
hbox.AddF( self._num_tags, FLAGS_MIXED )
hbox.AddF( self._ok, FLAGS_MIXED )
self.SetSizer( hbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'enter number of tags predicate' )
InitialiseControls()
InitialisePanel()
def NumWords():
def InitialiseControls():
( sign, num_words ) = system_predicates[ 'num_words' ]
self._sign = wx.Choice( self, choices=[ '<', u'\u2248', '=', '>' ] )
self._sign.SetSelection( sign )
self._num_words = wx.SpinCtrl( self, max = 1000000 )
self._num_words.SetValue( num_words )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
def InitialisePanel():
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self, label='system:num_words' ), FLAGS_MIXED )
hbox.AddF( self._sign, FLAGS_MIXED )
hbox.AddF( self._num_words, FLAGS_MIXED )
hbox.AddF( self._ok, FLAGS_MIXED )
self.SetSizer( hbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'enter number of words predicate' )
InitialiseControls()
InitialisePanel()
def Rating():
def InitialiseControls():
self._service_numerical = wx.Choice( self )
for service in self._local_numericals: self._service_numerical.Append( service.GetServiceIdentifier().GetName(), service )
self._service_numerical.Bind( wx.EVT_CHOICE, self.EventRatingsService )
( sign, value ) = system_predicates[ 'local_rating_numerical' ]
self._sign_numerical = wx.Choice( self, choices=[ '>', '<', '=', u'\u2248', '=rated', '=not rated', '=uncertain' ] )
self._sign_numerical.SetSelection( sign )
self._value_numerical = wx.SpinCtrl( self, min = 0, max = 50000 ) # set bounds based on current service
self._value_numerical.SetValue( value )
self._first_ok = wx.Button( self, label='Ok', id = HC.LOCAL_RATING_NUMERICAL )
self._first_ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._first_ok.SetForegroundColour( ( 0, 128, 0 ) )
self._service_like = wx.Choice( self )
for service in self._local_likes: self._service_like.Append( service.GetServiceIdentifier().GetName(), service )
self._service_like.Bind( wx.EVT_CHOICE, self.EventRatingsService )
value = system_predicates[ 'local_rating_like' ]
self._value_like = wx.Choice( self, choices=[ 'like', 'dislike', 'rated', 'not rated' ] ) # set words based on current service
self._value_like.SetSelection( value )
self._second_ok = wx.Button( self, label='Ok', id = HC.LOCAL_RATING_LIKE )
self._second_ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._second_ok.SetForegroundColour( ( 0, 128, 0 ) )
if len( self._local_numericals ) > 0: self._service_numerical.SetSelection( 0 )
if len( self._local_likes ) > 0: self._service_like.SetSelection( 0 )
self.EventRatingsService( None )
def InitialisePanel():
vbox = wx.BoxSizer( wx.VERTICAL )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self, label='system:rating:' ), FLAGS_MIXED )
hbox.AddF( self._service_numerical, FLAGS_MIXED )
hbox.AddF( self._sign_numerical, FLAGS_MIXED )
hbox.AddF( self._value_numerical, FLAGS_MIXED )
hbox.AddF( self._first_ok, FLAGS_MIXED )
vbox.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self, label='system:rating:' ), FLAGS_MIXED )
hbox.AddF( self._service_like, FLAGS_MIXED )
hbox.AddF( wx.StaticText( self, label='=' ), FLAGS_MIXED )
hbox.AddF( self._value_like, FLAGS_MIXED )
hbox.AddF( self._second_ok, FLAGS_MIXED )
vbox.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'enter rating predicate' )
self._local_numericals = wx.GetApp().Read( 'services', ( HC.LOCAL_RATING_NUMERICAL, ) )
self._local_likes = wx.GetApp().Read( 'services', ( HC.LOCAL_RATING_LIKE, ) )
InitialiseControls()
InitialisePanel()
def Ratio():
def InitialiseControls():
( sign, width, height ) = system_predicates[ 'ratio' ]
self._sign = wx.Choice( self, choices=[ '=', u'\u2248' ] )
self._sign.SetSelection( sign )
self._width = wx.SpinCtrl( self, max = 50000 )
self._width.SetValue( width )
self._height = wx.SpinCtrl( self, max = 50000 )
self._height.SetValue( height )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
def InitialisePanel():
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self, label='system:ratio' ), FLAGS_MIXED )
hbox.AddF( self._sign, FLAGS_MIXED )
hbox.AddF( self._width, FLAGS_MIXED )
hbox.AddF( wx.StaticText( self, label=':' ), FLAGS_MIXED )
hbox.AddF( self._height, FLAGS_MIXED )
hbox.AddF( self._ok, FLAGS_MIXED )
self.SetSizer( hbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'enter ratio predicate' )
InitialiseControls()
InitialisePanel()
def Size():
def InitialiseControls():
( sign, size, unit ) = system_predicates[ 'size' ]
self._sign = wx.Choice( self, choices=[ '<', u'\u2248', '=', '>' ] )
self._sign.SetSelection( sign )
self._size = wx.SpinCtrl( self, max = 1048576 )
self._size.SetValue( size )
self._unit = wx.Choice( self, choices=[ 'B', 'KB', 'MB', 'GB' ] )
self._unit.SetSelection( unit )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
def InitialisePanel():
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self, label='system:size' ), FLAGS_MIXED )
hbox.AddF( self._sign, FLAGS_MIXED )
hbox.AddF( self._size, FLAGS_MIXED )
hbox.AddF( self._unit, FLAGS_MIXED )
hbox.AddF( self._ok, FLAGS_MIXED )
self.SetSizer( hbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'enter size predicate' )
InitialiseControls()
InitialisePanel()
def Width():
def InitialiseControls():
( sign, width ) = system_predicates[ 'width' ]
self._sign = wx.Choice( self, choices=[ '<', u'\u2248', '=', '>' ] )
self._sign.SetSelection( sign )
self._width = wx.SpinCtrl( self, max = 200000 )
self._width.SetValue( width )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
def InitialisePanel():
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self, label='system:width' ), FLAGS_MIXED )
hbox.AddF( self._sign, FLAGS_MIXED )
hbox.AddF( self._width, FLAGS_MIXED )
hbox.AddF( self._ok, FLAGS_MIXED )
self.SetSizer( hbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'enter width predicate' )
InitialiseControls()
InitialisePanel()
def SimilarTo():
def InitialiseControls():
self._hash = wx.TextCtrl( self )
self._hash.SetValue( 'enter hash' )
self._max_hamming = wx.SpinCtrl( self, initial = 5, max = 256 )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
def InitialisePanel():
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self, label='system:similar_to' ), FLAGS_MIXED )
hbox.AddF( self._hash, FLAGS_MIXED )
hbox.AddF( wx.StaticText( self, label=u'\u2248' ), FLAGS_MIXED )
hbox.AddF( self._max_hamming, FLAGS_MIXED )
hbox.AddF( self._ok, FLAGS_MIXED )
self.SetSizer( hbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'enter similar to predicate' )
InitialiseControls()
InitialisePanel()
options = wx.GetApp().Read( 'options' )
system_predicates = options[ 'file_system_predicates' ]
self._type = type
if self._type == HC.SYSTEM_PREDICATE_TYPE_AGE: Age()
elif self._type == HC.SYSTEM_PREDICATE_TYPE_DURATION: Duration()
elif self._type == HC.SYSTEM_PREDICATE_TYPE_HASH: Hash()
elif self._type == HC.SYSTEM_PREDICATE_TYPE_HEIGHT: Height()
elif self._type == HC.SYSTEM_PREDICATE_TYPE_LIMIT: Limit()
elif self._type == HC.SYSTEM_PREDICATE_TYPE_MIME: Mime()
elif self._type == HC.SYSTEM_PREDICATE_TYPE_NUM_TAGS: NumTags()
elif self._type == HC.SYSTEM_PREDICATE_TYPE_RATING: Rating()
elif self._type == HC.SYSTEM_PREDICATE_TYPE_RATIO: Ratio()
elif self._type == HC.SYSTEM_PREDICATE_TYPE_SIZE: Size()
elif self._type == HC.SYSTEM_PREDICATE_TYPE_WIDTH: Width()
elif self._type == HC.SYSTEM_PREDICATE_TYPE_SIMILAR_TO: SimilarTo()
elif self._type == HC.SYSTEM_PREDICATE_TYPE_NUM_WORDS: NumWords()
elif self._type == HC.SYSTEM_PREDICATE_TYPE_FILE_SERVICE: FileService()
self._hidden_cancel_button = wx.Button( self, id = wx.ID_CANCEL, label = 'cancel', size = ( 0, 0 ) )
self._hidden_cancel_button.Bind( wx.EVT_BUTTON, self.EventCancel )
# hide doesn't keep the escape hotkey, so say size = ( 0, 0 )
# self._hidden_cancel_button.Hide()
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventMime( self, event ):
media = self._mime_media.GetStringSelection()
self._mime_type.Clear()
if media == 'image':
self._mime_type.Append( 'any', HC.IMAGES )
self._mime_type.Append( 'jpeg', HC.IMAGE_JPEG )
self._mime_type.Append( 'png', HC.IMAGE_PNG )
self._mime_type.Append( 'gif', HC.IMAGE_GIF )
elif media == 'application':
self._mime_type.Append( 'any', HC.APPLICATIONS )
self._mime_type.Append( 'pdf', HC.APPLICATION_PDF )
self._mime_type.Append( 'x-shockwave-flash', HC.APPLICATION_FLASH )
elif media == 'video':
self._mime_type.Append( 'x-flv', HC.VIDEO_FLV )
self._mime_type.SetSelection( 0 )
def EventOk( self, event ):
if self._type == HC.SYSTEM_PREDICATE_TYPE_AGE: info = ( self._sign.GetStringSelection(), self._years.GetValue(), self._months.GetValue(), self._days.GetValue() )
elif self._type == HC.SYSTEM_PREDICATE_TYPE_DURATION: info = ( self._sign.GetStringSelection(), self._duration_s.GetValue() * 1000 + self._duration_ms.GetValue() )
elif self._type == HC.SYSTEM_PREDICATE_TYPE_HASH:
hex_filter = lambda c: c in '0123456789abcdef'
hash = filter( hex_filter, self._hash.GetValue() )
if len( hash ) == 0: hash == '00'
elif len( hash ) % 2 == 1: hash += '0' # since we are later decoding to byte
info = hash.decode( 'hex' )
elif self._type == HC.SYSTEM_PREDICATE_TYPE_HEIGHT: info = ( self._sign.GetStringSelection(), self._height.GetValue() )
elif self._type == HC.SYSTEM_PREDICATE_TYPE_LIMIT: info = self._limit.GetValue()
elif self._type == HC.SYSTEM_PREDICATE_TYPE_MIME: info = self._mime_type.GetClientData( self._mime_type.GetSelection() )
elif self._type == HC.SYSTEM_PREDICATE_TYPE_NUM_TAGS: info = ( self._sign.GetStringSelection(), self._num_tags.GetValue() )
elif self._type == HC.SYSTEM_PREDICATE_TYPE_NUM_WORDS: info = ( self._sign.GetStringSelection(), self._num_words.GetValue() )
elif self._type == HC.SYSTEM_PREDICATE_TYPE_RATING:
id = event.GetId()
if id == HC.LOCAL_RATING_LIKE:
service_identifier = self._service_like.GetClientData( self._service_like.GetSelection() ).GetServiceIdentifier()
operator = '='
selection = self._value_like.GetSelection()
if selection == 0: value = '1'
elif selection == 1: value = '0'
elif selection == 2: value = 'rated'
elif selection == 3: value = 'not rated'
info = ( service_identifier, operator, value )
elif id == HC.LOCAL_RATING_NUMERICAL:
service = self._service_numerical.GetClientData( self._service_numerical.GetSelection() )
service_identifier = service.GetServiceIdentifier()
operator = self._sign_numerical.GetStringSelection()
if operator in ( '=rated', '=not rated', '=uncertain' ):
value = operator[1:]
operator = '='
else:
( lower, upper ) = service.GetExtraInfo()
value_raw = self._value_numerical.GetValue()
value = float( value_raw - lower ) / float( upper - lower )
info = ( service_identifier, operator, value )
elif self._type == HC.SYSTEM_PREDICATE_TYPE_RATIO: info = ( self._sign.GetStringSelection(), float( ( self._width.GetValue() ) / float( self._height.GetValue() ) ) )
elif self._type == HC.SYSTEM_PREDICATE_TYPE_SIZE: info = ( self._sign.GetStringSelection(), self._size.GetValue(), HC.ConvertUnitToInteger( self._unit.GetStringSelection() ) )
elif self._type == HC.SYSTEM_PREDICATE_TYPE_WIDTH: info = ( self._sign.GetStringSelection(), self._width.GetValue() )
elif self._type == HC.SYSTEM_PREDICATE_TYPE_SIMILAR_TO:
hex_filter = lambda c: c in '0123456789abcdef'
hash = filter( hex_filter, self._hash.GetValue() )
if len( hash ) == 0: hash == '00'
elif len( hash ) % 2 == 1: hash += '0' # since we are later decoding to byte
info = ( hash.decode( 'hex' ), self._max_hamming.GetValue() )
elif self._type == HC.SYSTEM_PREDICATE_TYPE_FILE_SERVICE: info = ( self._sign.GetClientData( self._sign.GetSelection() ), self._current_pending.GetClientData( self._current_pending.GetSelection() ), self._file_service_identifier.GetClientData( self._file_service_identifier.GetSelection() ) )
self._predicate = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( self._type, info ), None )
self.EndModal( wx.ID_OK )
def EventRatingsService( self, event ):
try:
service = self._service_numerical.GetClientData( self._service_numerical.GetSelection() )
( min, max ) = service.GetExtraInfo()
self._value_numerical.SetRange( min, max )
service = self._service_like.GetClientData( self._service_like.GetSelection() )
except: pass
try:
( like, dislike ) = service.GetExtraInfo()
selection = self._value_like.GetSelection()
self._value_like.SetString( 0, like )
self._value_like.SetString( 1, dislike )
self._value_like.SetSelection( selection )
except: pass
def GetPredicate( self ): return self._predicate
class DialogInputMessageSystemPredicate( Dialog ):
def __init__( self, parent, type ):
def Age():
def InitialiseControls():
self._sign = wx.Choice( self, choices=[ '<', u'\u2248', '>' ] )
self._sign.SetSelection( 0 )
self._years = wx.SpinCtrl( self, initial = 0, max = 30 )
self._months = wx.SpinCtrl( self, initial = 0, max = 60 )
self._days = wx.SpinCtrl( self, initial = 7, max = 90 )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
def InitialisePanel():
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self, label='system:age' ), FLAGS_MIXED )
hbox.AddF( self._sign, FLAGS_MIXED )
hbox.AddF( self._years, FLAGS_MIXED )
hbox.AddF( wx.StaticText( self, label='years' ), FLAGS_MIXED )
hbox.AddF( self._months, FLAGS_MIXED )
hbox.AddF( wx.StaticText( self, label='months' ), FLAGS_MIXED )
hbox.AddF( self._days, FLAGS_MIXED )
hbox.AddF( wx.StaticText( self, label='days' ), FLAGS_MIXED )
hbox.AddF( self._ok, FLAGS_MIXED )
self.SetSizer( hbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'enter age predicate' )
InitialiseControls()
InitialisePanel()
def From():
def InitialiseControls():
contact_names = wx.GetApp().Read( 'contact_names' )
self._contact = wx.Choice( self, choices=contact_names )
self._contact.SetSelection( 0 )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
def InitialisePanel():
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self, label='system:from' ), FLAGS_MIXED )
hbox.AddF( self._contact, FLAGS_MIXED )
hbox.AddF( self._ok, FLAGS_MIXED )
self.SetSizer( hbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'enter from predicate' )
InitialiseControls()
InitialisePanel()
def StartedBy():
def InitialiseControls():
contact_names = wx.GetApp().Read( 'contact_names' )
self._contact = wx.Choice( self, choices=contact_names )
self._contact.SetSelection( 0 )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
def InitialisePanel():
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self, label='system:started_by' ), FLAGS_MIXED )
hbox.AddF( self._contact, FLAGS_MIXED )
hbox.AddF( self._ok, FLAGS_MIXED )
self.SetSizer( hbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'enter started by predicate' )
InitialiseControls()
InitialisePanel()
def To():
def InitialiseControls():
contact_names = [ name for name in wx.GetApp().Read( 'contact_names' ) if name != 'Anonymous' ]
self._contact = wx.Choice( self, choices=contact_names )
self._contact.SetSelection( 0 )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
def InitialisePanel():
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self, label='system:to' ), FLAGS_MIXED )
hbox.AddF( self._contact, FLAGS_MIXED )
hbox.AddF( self._ok, FLAGS_MIXED )
self.SetSizer( hbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'enter to predicate' )
InitialiseControls()
InitialisePanel()
def NumAttachments():
def InitialiseControls():
self._sign = wx.Choice( self, choices=[ '<', '=', '>' ] )
self._sign.SetSelection( 0 )
self._num_attachments = wx.SpinCtrl( self, initial = 4, max = 2000 )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
def InitialisePanel():
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self, label='system:numattachments' ), FLAGS_MIXED )
hbox.AddF( self._sign, FLAGS_MIXED )
hbox.AddF( self._num_attachments, FLAGS_MIXED )
hbox.AddF( self._ok, FLAGS_MIXED )
self.SetSizer( hbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'enter number of attachments predicate' )
InitialiseControls()
InitialisePanel()
self._type = type
if self._type == 'system:age': Age()
elif self._type == 'system:started_by': StartedBy()
elif self._type == 'system:from': From()
elif self._type == 'system:to': To()
elif self._type == 'system:numattachments': NumAttachments()
self._hidden_cancel_button = wx.Button( self, id = wx.ID_CANCEL, label = 'cancel', size = ( 0, 0 ) )
self._hidden_cancel_button.Bind( wx.EVT_BUTTON, self.EventCancel )
# hide doesn't keep the escape hotkey, so say size = ( 0, 0 )
# self._hidden_cancel_button.Hide()
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventOk( self, event ): self.EndModal( wx.ID_OK )
def GetString( self ):
if self._type == 'system:age': return 'system:age' + self._sign.GetStringSelection() + str( self._years.GetValue() ) + 'y' + str( self._months.GetValue() ) + 'm' + str( self._days.GetValue() ) + 'd'
elif self._type == 'system:started_by': return 'system:started_by=' + self._contact.GetStringSelection()
elif self._type == 'system:from': return 'system:from=' + self._contact.GetStringSelection()
elif self._type == 'system:to': return 'system:to=' + self._contact.GetStringSelection()
elif self._type == 'system:numattachments': return 'system:numattachments' + self._sign.GetStringSelection() + str( self._num_attachments.GetValue() )
class DialogInputShortcut( Dialog ):
def __init__( self, parent, modifier = wx.ACCEL_NORMAL, key = wx.WXK_F7, action = 'new_page' ):
self._action = action
def InitialiseControls():
self._shortcut = ClientGUICommon.Shortcut( self, modifier, key )
self._actions = wx.Choice( self, choices = [ 'archive', 'inbox', 'close_page', 'filter', 'fullscreen_switch', 'ratings_filter', 'frame_back', 'frame_next', 'manage_ratings', 'manage_tags', 'new_page', 'refresh', 'set_search_focus', 'show_hide_splitters', 'synchronised_wait_switch', 'previous', 'next', 'first', 'last' ] )
self._actions.SetSelection( self._actions.FindString( action ) )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label='Cancel' )
self._cancel.Bind( wx.EVT_BUTTON, self.EventCancel )
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
def InitialisePanel():
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( self._shortcut, FLAGS_MIXED )
hbox.AddF( self._actions, FLAGS_EXPAND_PERPENDICULAR )
b_box = wx.BoxSizer( wx.HORIZONTAL )
b_box.AddF( self._ok, FLAGS_MIXED )
b_box.AddF( self._cancel, FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( b_box, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'configure shortcut' )
InitialiseControls()
InitialisePanel()
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventOk( self, event ): self.EndModal( wx.ID_OK )
def GetInfo( self ):
( modifier, key ) = self._shortcut.GetValue()
return ( modifier, key, self._actions.GetStringSelection() )
class DialogManageAccountTypes( Dialog ):
def __init__( self, parent, service_identifier ):
def InitialiseControls():
service = wx.GetApp().Read( 'service', service_identifier )
connection = service.GetConnection()
account_types = connection.Get( 'account_types' )
self._titles_to_account_types = {}
self._account_types_panel = ClientGUICommon.StaticBox( self, 'account types' )
self._ctrl_account_types = ClientGUICommon.SaneListCtrl( self._account_types_panel, 350, [ ( 'title', 120 ), ( 'permissions', -1 ), ( 'max monthly bytes', 120 ), ( 'max monthly requests', 120 ) ] )
for account_type in account_types:
title = account_type.GetTitle()
self._titles_to_account_types[ title ] = account_type
permissions = account_type.GetPermissions()
permissions_string = ', '.join( [ HC.permissions_string_lookup[ permission ] for permission in permissions ] )
( max_num_bytes, max_num_requests ) = account_type.GetMaxMonthlyData()
( max_num_bytes_string, max_num_requests_string ) = account_type.GetMaxMonthlyDataString()
self._ctrl_account_types.Append( ( title, permissions_string, max_num_bytes_string, max_num_requests_string ), ( title, len( permissions ), max_num_bytes, max_num_requests ) )
self._add = wx.Button( self._account_types_panel, label='add' )
self._add.Bind( wx.EVT_BUTTON, self.EventAdd )
self._edit = wx.Button( self._account_types_panel, label='edit' )
self._edit.Bind( wx.EVT_BUTTON, self.EventEdit )
self._delete = wx.Button( self._account_types_panel, label='delete' )
self._delete.Bind( wx.EVT_BUTTON, self.EventDelete )
self._apply = wx.Button( self, label='apply' )
self._apply.Bind( wx.EVT_BUTTON, self.EventOk )
self._apply.SetForegroundColour( ( 0, 128, 0 ) )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label='cancel' )
self._cancel.Bind( wx.EVT_BUTTON, self.EventCancel )
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
def InitialisePanel():
h_b_box = wx.BoxSizer( wx.HORIZONTAL )
h_b_box.AddF( self._add, FLAGS_MIXED )
h_b_box.AddF( self._edit, FLAGS_MIXED )
h_b_box.AddF( self._delete, FLAGS_MIXED )
self._account_types_panel.AddF( self._ctrl_account_types, FLAGS_EXPAND_BOTH_WAYS )
self._account_types_panel.AddF( h_b_box, FLAGS_BUTTON_SIZERS )
b_box = wx.BoxSizer( wx.HORIZONTAL )
b_box.AddF( self._apply, FLAGS_MIXED )
b_box.AddF( self._cancel, FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._account_types_panel, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( b_box, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( 980, y ) )
Dialog.__init__( self, parent, 'manage account types' )
self._service_identifier = service_identifier
self._edit_log = []
try:
InitialiseControls()
InitialisePanel()
except: raise
def EventAdd( self, event ):
try:
with DialogInputNewAccountType( self ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
account_type = dlg.GetAccountType()
title = account_type.GetTitle()
permissions = account_type.GetPermissions()
permissions_string = ', '.join( [ HC.permissions_string_lookup[ permission ] for permission in permissions ] )
( max_num_bytes, max_num_requests ) = account_type.GetMaxMonthlyData()
( max_num_bytes_string, max_num_requests_string ) = account_type.GetMaxMonthlyDataString()
if title in self._titles_to_account_types: raise Exception( 'You already have an account type called ' + title + '; delete or edit that one first' )
self._titles_to_account_types[ title ] = account_type
self._edit_log.append( ( 'add', account_type ) )
self._ctrl_account_types.Append( ( title, permissions_string, max_num_bytes_string, max_num_requests_string ), ( title, len( permissions ), max_num_bytes, max_num_requests ) )
except Exception as e: wx.MessageBox( unicode( e ) )
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventDelete( self, event ):
indices = self._ctrl_account_types.GetAllSelected()
titles_about_to_delete = { self._ctrl_account_types.GetClientData( index )[0] for index in indices }
all_titles = set( self._titles_to_account_types.keys() )
titles_can_move_to = list( all_titles - titles_about_to_delete )
if len( titles_can_move_to ) == 0:
wx.MessageBox( 'You cannot delete every account type!' )
return
for title in titles_about_to_delete:
with DialogSelectFromListOfStrings( self, 'what should deleted ' + title + ' accounts become?', titles_can_move_to ) as dlg:
if dlg.ShowModal() == wx.ID_OK: title_to_move_to = dlg.GetString()
else: return
self._edit_log.append( ( 'delete', ( title, title_to_move_to ) ) )
self._ctrl_account_types.RemoveAllSelected()
def EventEdit( self, event ):
indices = self._ctrl_account_types.GetAllSelected()
for index in indices:
title = self._ctrl_account_types.GetClientData( index )[0]
account_type = self._titles_to_account_types[ title ]
try:
with DialogInputNewAccountType( self, account_type ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
old_title = title
account_type = dlg.GetAccountType()
title = account_type.GetTitle()
permissions = account_type.GetPermissions()
permissions_string = ', '.join( [ HC.permissions_string_lookup[ permission ] for permission in permissions ] )
( max_num_bytes, max_num_requests ) = account_type.GetMaxMonthlyData()
( max_num_bytes_string, max_num_requests_string ) = account_type.GetMaxMonthlyDataString()
if old_title != title:
if title in self._titles_to_account_types: raise Exception( 'You already have an account type called ' + title + '; delete or edit that one first' )
del self._titles_to_account_types[ old_title ]
self._titles_to_account_types[ title ] = account_type
self._edit_log.append( ( 'edit', ( old_title, account_type ) ) )
self._ctrl_account_types.UpdateRow( index, ( title, permissions_string, max_num_bytes_string, max_num_requests_string ), ( title, len( permissions ), max_num_bytes, max_num_requests ) )
except Exception as e: wx.MessageBox( unicode( e ) )
def EventOk( self, event ):
try:
service = wx.GetApp().Read( 'service', self._service_identifier )
connection = service.GetConnection()
connection.Post( 'account_types_modification', edit_log = self._edit_log )
except Exception as e: wx.MessageBox( unicode( e ) )
self.EndModal( wx.ID_OK )
class DialogManageBoorus( Dialog ):
def __init__( self, parent ):
def InitialiseControls():
self._edit_log = []
self._boorus = ClientGUICommon.ListBook( self )
boorus = wx.GetApp().Read( 'boorus' )
for booru in boorus:
name = booru.GetName()
page_info = ( self._Panel, ( self._boorus, booru ), {} )
self._boorus.AddPage( page_info, name )
self._add = wx.Button( self, label='add' )
self._add.Bind( wx.EVT_BUTTON, self.EventAdd )
self._add.SetForegroundColour( ( 0, 128, 0 ) )
self._remove = wx.Button( self, label='remove' )
self._remove.Bind( wx.EVT_BUTTON, self.EventRemove )
self._remove.SetForegroundColour( ( 128, 0, 0 ) )
self._export = wx.Button( self, label='export' )
self._export.Bind( wx.EVT_BUTTON, self.EventExport )
self._ok = wx.Button( self, label='ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label='cancel' )
self._cancel.Bind( wx.EVT_BUTTON, self.EventCancel )
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
def InitialisePanel():
add_remove_hbox = wx.BoxSizer( wx.HORIZONTAL )
add_remove_hbox.AddF( self._add, FLAGS_MIXED )
add_remove_hbox.AddF( self._remove, FLAGS_MIXED )
add_remove_hbox.AddF( self._export, FLAGS_MIXED )
ok_hbox = wx.BoxSizer( wx.HORIZONTAL )
ok_hbox.AddF( self._ok, FLAGS_MIXED )
ok_hbox.AddF( self._cancel, FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._boorus, FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( add_remove_hbox, FLAGS_SMALL_INDENT )
vbox.AddF( ok_hbox, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( 980, y ) )
Dialog.__init__( self, parent, 'manage boorus' )
InitialiseControls()
InitialisePanel()
self.SetDropTarget( ClientGUICommon.FileDropTarget( self.Import ) )
def EventAdd( self, event ):
with wx.TextEntryDialog( self, 'Enter new booru\'s name' ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
try:
name = dlg.GetValue()
if self._boorus.NameExists( name ): raise Exception( 'That name is already in use!' )
if name == '': raise Exception( 'Please enter a nickname for the service.' )
booru = CC.Booru( name, 'search_url', '+', 1, 'thumbnail', '', 'original image', {} )
self._edit_log.append( ( 'add', name ) )
page = self._Panel( self._boorus, booru )
self._boorus.AddPage( page, name, select = True )
except Exception as e:
wx.MessageBox( unicode( e ) )
self.EventAdd( event )
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventExport( self, event ):
booru_panel = self._boorus.GetCurrentPage()
if booru_panel is not None:
name = self._boorus.GetCurrentName()
booru = booru_panel.GetBooru()
with wx.FileDialog( self, 'select where to export booru', defaultFile = 'booru.yaml', style = wx.FD_SAVE ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
with open( dlg.GetPath(), 'wb' ) as f: f.write( yaml.safe_dump( booru ) )
def EventOk( self, event ):
for ( name, page ) in self._boorus.GetNameToPageDict().items():
if page.HasChanges(): self._edit_log.append( ( 'edit', ( name, page.GetBooru() ) ) )
try:
if len( self._edit_log ) > 0: wx.GetApp().Write( 'update_boorus', self._edit_log )
except Exception as e: wx.MessageBox( 'Saving boorus to DB raised this error: ' + unicode( e ) )
self.EndModal( wx.ID_OK )
def EventRemove( self, event ):
booru_panel = self._boorus.GetCurrentPage()
if booru_panel is not None:
name = self._boorus.GetCurrentName()
self._edit_log.append( ( 'delete', name ) )
self._boorus.DeleteCurrentPage()
def Import( self, paths ):
for path in paths:
try:
with open( path, 'rb' ) as f: file = f.read()
thing = yaml.safe_load( file )
if type( thing ) == CC.Booru:
booru = thing
name = booru.GetName()
if not self._boorus.NameExists( name ):
new_booru = CC.Booru( name, 'search_url', '+', 1, 'thumbnail', '', 'original image', {} )
self._edit_log.append( ( 'add', name ) )
page = self._Panel( self._boorus, new_booru )
self._boorus.AddPage( page, name, select = True )
page = self._boorus.GetNameToPageDict()[ name ]
page.Update( booru )
except:
wx.MessageBox( traceback.format_exc() )
class _Panel( wx.Panel ):
def __init__( self, parent, booru ):
wx.Panel.__init__( self, parent )
self._booru = booru
( search_url, search_separator, advance_by_page_num, thumb_classname, image_id, image_data, tag_classnames_to_namespaces ) = booru.GetData()
def InitialiseControls():
self._booru_panel = ClientGUICommon.StaticBox( self, 'booru' )
#
self._search_panel = ClientGUICommon.StaticBox( self._booru_panel, 'search' )
self._search_url = wx.TextCtrl( self._search_panel, value = search_url )
self._search_url.Bind( wx.EVT_TEXT, self.EventHTML )
self._search_separator = wx.Choice( self._search_panel, choices = [ '+', '&', '%20' ] )
self._search_separator.Select( self._search_separator.FindString( search_separator ) )
self._search_separator.Bind( wx.EVT_CHOICE, self.EventHTML )
self._advance_by_page_num = wx.CheckBox( self._search_panel )
self._advance_by_page_num.SetValue( advance_by_page_num )
self._thumb_classname = wx.TextCtrl( self._search_panel, value = thumb_classname )
self._thumb_classname.Bind( wx.EVT_TEXT, self.EventHTML )
self._example_html_search = wx.StaticText( self._search_panel, style = wx.ST_NO_AUTORESIZE )
#
self._image_panel = ClientGUICommon.StaticBox( self._booru_panel, 'image' )
self._image_info = wx.TextCtrl( self._image_panel )
self._image_info.Bind( wx.EVT_TEXT, self.EventHTML )
self._image_id = wx.RadioButton( self._image_panel, style = wx.RB_GROUP )
self._image_id.Bind( wx.EVT_RADIOBUTTON, self.EventHTML )
self._image_data = wx.RadioButton( self._image_panel )
self._image_data.Bind( wx.EVT_RADIOBUTTON, self.EventHTML )
if image_id is None:
self._image_info.SetValue( image_data )
self._image_data.SetValue( True )
else:
self._image_info.SetValue( image_id )
self._image_id.SetValue( True )
self._example_html_image = wx.StaticText( self._image_panel, style = wx.ST_NO_AUTORESIZE )
#
self._tag_panel = ClientGUICommon.StaticBox( self._booru_panel, 'tags' )
self._tag_classnames_to_namespaces = wx.ListBox( self._tag_panel, style = wx.LB_SORT )
self._tag_classnames_to_namespaces.Bind( wx.EVT_LEFT_DCLICK, self.EventRemove )
for ( tag_classname, namespace ) in tag_classnames_to_namespaces.items(): self._tag_classnames_to_namespaces.Append( tag_classname + ' : ' + namespace, ( tag_classname, namespace ) )
self._tag_classname = wx.TextCtrl( self._tag_panel )
self._namespace = wx.TextCtrl( self._tag_panel )
self._add = wx.Button( self._tag_panel, label = 'add' )
self._add.Bind( wx.EVT_BUTTON, self.EventAdd )
self._example_html_tags = wx.StaticText( self._tag_panel, style = wx.ST_NO_AUTORESIZE )
def InitialisePanel():
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 1, 1 )
gridbox.AddF( wx.StaticText( self._search_panel, label='search url' ), FLAGS_MIXED )
gridbox.AddF( self._search_url, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( self._search_panel, label='search tag separator' ), FLAGS_MIXED )
gridbox.AddF( self._search_separator, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( self._search_panel, label='advance by page num' ), FLAGS_MIXED )
gridbox.AddF( self._advance_by_page_num, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( self._search_panel, label='thumbnail classname' ), FLAGS_MIXED )
gridbox.AddF( self._thumb_classname, FLAGS_EXPAND_BOTH_WAYS )
self._search_panel.AddF( gridbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
self._search_panel.AddF( self._example_html_search, FLAGS_EXPAND_PERPENDICULAR )
#
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 1, 1 )
gridbox.AddF( wx.StaticText( self._image_panel, label='text' ), FLAGS_MIXED )
gridbox.AddF( self._image_info, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( self._image_panel, label='id of <img>' ), FLAGS_MIXED )
gridbox.AddF( self._image_id, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( self._image_panel, label='text of <a>' ), FLAGS_MIXED )
gridbox.AddF( self._image_data, FLAGS_EXPAND_BOTH_WAYS )
self._image_panel.AddF( gridbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
self._image_panel.AddF( self._example_html_image, FLAGS_EXPAND_PERPENDICULAR )
#
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( self._tag_classname, FLAGS_MIXED )
hbox.AddF( self._namespace, FLAGS_MIXED )
hbox.AddF( self._add, FLAGS_MIXED )
self._tag_panel.AddF( self._tag_classnames_to_namespaces, FLAGS_EXPAND_BOTH_WAYS )
self._tag_panel.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
self._tag_panel.AddF( self._example_html_tags, FLAGS_EXPAND_PERPENDICULAR )
#
self._booru_panel.AddF( self._search_panel, FLAGS_EXPAND_PERPENDICULAR )
self._booru_panel.AddF( self._image_panel, FLAGS_EXPAND_PERPENDICULAR )
self._booru_panel.AddF( self._tag_panel, FLAGS_EXPAND_BOTH_WAYS )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._booru_panel, FLAGS_EXPAND_BOTH_WAYS )
self.SetSizer( vbox )
InitialiseControls()
InitialisePanel()
def _GetInfo( self ):
booru_name = self._booru.GetName()
search_url = self._search_url.GetValue()
search_separator = self._search_separator.GetStringSelection()
advance_by_page_num = self._advance_by_page_num.GetValue()
thumb_classname = self._thumb_classname.GetValue()
if self._image_id.GetValue():
image_id = self._image_info.GetValue()
image_data = None
else:
image_id = None
image_data = self._image_info.GetValue()
tag_classnames_to_namespaces = { tag_classname : namespace for ( tag_classname, namespace ) in [ self._tag_classnames_to_namespaces.GetClientData( i ) for i in range( self._tag_classnames_to_namespaces.GetCount() ) ] }
return ( booru_name, search_url, search_separator, advance_by_page_num, thumb_classname, image_id, image_data, tag_classnames_to_namespaces )
def EventAdd( self, event ):
tag_classname = self._tag_classname.GetValue()
namespace = self._namespace.GetValue()
if tag_classname != '':
self._tag_classnames_to_namespaces.Append( tag_classname + ' : ' + namespace, ( tag_classname, namespace ) )
self._tag_classname.SetValue( '' )
self._namespace.SetValue( '' )
self.EventHTML( event )
def EventHTML( self, event ):
pass
def EventRemove( self, event ):
selection = self._tag_classnames_to_namespaces.GetSelection()
if selection != wx.NOT_FOUND:
self._tag_classnames_to_namespaces.Delete( selection )
self.EventHTML( event )
def GetBooru( self ):
( booru_name, search_url, search_separator, advance_by_page_num, thumb_classname, image_id, image_data, tag_classnames_to_namespaces ) = self._GetInfo()
return CC.Booru( booru_name, search_url, search_separator, advance_by_page_num, thumb_classname, image_id, image_data, tag_classnames_to_namespaces )
def HasChanges( self ):
( booru_name, my_search_url, my_search_separator, my_advance_by_page_num, my_thumb_classname, my_image_id, my_image_data, my_tag_classnames_to_namespaces ) = self._GetInfo()
( search_url, search_separator, advance_by_page_num, thumb_classname, image_id, image_data, tag_classnames_to_namespaces ) = self._booru.GetData()
if search_url != my_search_url: return True
if search_separator != my_search_separator: return True
if advance_by_page_num != my_advance_by_page_num: return True
if thumb_classname != my_thumb_classname: return True
if image_id != my_image_id: return True
if image_data != my_image_data: return True
if tag_classnames_to_namespaces != my_tag_classnames_to_namespaces: return True
return False
def Update( self, booru ):
( search_url, search_separator, advance_by_page_num, thumb_classname, image_id, image_data, tag_classnames_to_namespaces ) = booru.GetData()
self._search_url.SetValue( search_url )
self._search_separator.Select( self._search_separator.FindString( search_separator ) )
self._advance_by_page_num.SetValue( advance_by_page_num )
self._thumb_classname.SetValue( thumb_classname )
if image_id is None:
self._image_info.SetValue( image_data )
self._image_data.SetValue( True )
else:
self._image_info.SetValue( image_id )
self._image_id.SetValue( True )
self._tag_classnames_to_namespaces.Clear()
for ( tag_classname, namespace ) in tag_classnames_to_namespaces.items(): self._tag_classnames_to_namespaces.Append( tag_classname + ' : ' + namespace, ( tag_classname, namespace ) )
class DialogManageContacts( Dialog ):
def __init__( self, parent ):
def InitialiseControls():
self._edit_log = []
self._contacts = ClientGUICommon.ListBook( self )
( identities, contacts, deletable_names ) = wx.GetApp().Read( 'identities_and_contacts' )
self._deletable_names = deletable_names
for identity in identities:
name = identity.GetName()
page_info = ( self._Panel, ( self._contacts, identity ), { 'is_identity' : True } )
self._contacts.AddPage( page_info, ' identity - ' + name )
for contact in contacts:
name = contact.GetName()
page_info = ( self._Panel, ( self._contacts, contact ), { 'is_identity' : False } )
self._contacts.AddPage( page_info, name )
# bind events after population
self._contacts.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventContactChanging )
self._contacts.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGED, self.EventContactChanged )
self._add_contact_address = wx.Button( self, label='add by contact address' )
self._add_contact_address.Bind( wx.EVT_BUTTON, self.EventAddByContactAddress )
self._add_contact_address.SetForegroundColour( ( 0, 128, 0 ) )
self._add_manually = wx.Button( self, label='add manually' )
self._add_manually.Bind( wx.EVT_BUTTON, self.EventAddManually )
self._add_manually.SetForegroundColour( ( 0, 128, 0 ) )
self._remove = wx.Button( self, label='remove' )
self._remove.Bind( wx.EVT_BUTTON, self.EventRemove )
self._remove.SetForegroundColour( ( 128, 0, 0 ) )
self._export = wx.Button( self, label='export' )
self._export.Bind( wx.EVT_BUTTON, self.EventExport )
self._ok = wx.Button( self, label='ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label='cancel' )
self._cancel.Bind( wx.EVT_BUTTON, self.EventCancel )
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
def InitialisePanel():
add_remove_hbox = wx.BoxSizer( wx.HORIZONTAL )
add_remove_hbox.AddF( self._add_manually, FLAGS_MIXED )
add_remove_hbox.AddF( self._add_contact_address, FLAGS_MIXED )
add_remove_hbox.AddF( self._remove, FLAGS_MIXED )
add_remove_hbox.AddF( self._export, FLAGS_MIXED )
ok_hbox = wx.BoxSizer( wx.HORIZONTAL )
ok_hbox.AddF( self._ok, FLAGS_MIXED )
ok_hbox.AddF( self._cancel, FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._contacts, FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( add_remove_hbox, FLAGS_SMALL_INDENT )
vbox.AddF( ok_hbox, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( 980, y ) )
Dialog.__init__( self, parent, 'manage contacts' )
InitialiseControls()
InitialisePanel()
self.SetDropTarget( ClientGUICommon.FileDropTarget( self.Import ) )
self.EventContactChanged( None )
def _CheckCurrentContactIsValid( self ):
contact_panel = self._contacts.GetCurrentPage()
if contact_panel is not None:
contact = contact_panel.GetContact()
old_name = self._contacts.GetCurrentName()
name = contact.GetName()
if name != old_name and ' identity - ' + name != old_name:
if self._contacts.NameExists( name ) or self._contacts.NameExists( ' identity - ' + name ) or name == 'Anonymous': raise Exception( 'That name is already in use!' )
if old_name.startswith( ' identity - ' ): self._contacts.RenamePage( old_name, ' identity - ' + name )
else: self._contacts.RenamePage( old_name, name )
def EventAddByContactAddress( self, event ):
try: self._CheckCurrentContactIsValid()
except Exception as e:
wx.MessageBox( unicode( e ) )
return
with wx.TextEntryDialog( self, 'Enter contact\'s address in the form contact_key@hostname:port' ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
try:
contact_address = dlg.GetValue()
try:
( contact_key_encoded, address ) = contact_address.split( '@' )
contact_key = contact_key_encoded.decode( 'hex' )
( host, port ) = address.split( ':' )
port = int( port )
except: raise Exception( 'Could not parse the address!' )
name = contact_key_encoded
contact = ClientConstantsMessages.Contact( None, name, host, port )
try:
connection = contact.GetConnection()
public_key = connection.Get( 'public_key', contact_key = contact_key.encode( 'hex' ) )
except: raise Exception( 'Could not fetch the contact\'s public key from the address:' + os.linesep + traceback.format_exc() )
contact = ClientConstantsMessages.Contact( public_key, name, host, port )
self._edit_log.append( ( 'add', contact ) )
page = self._Panel( self._contacts, contact, is_identity = False )
self._deletable_names.add( name )
self._contacts.AddPage( page, name, select = True )
except Exception as e:
wx.MessageBox( unicode( e ) )
self.EventAddByContactAddress( event )
def EventAddManually( self, event ):
try: self._CheckCurrentContactIsValid()
except Exception as e:
wx.MessageBox( unicode( e ) )
return
with wx.TextEntryDialog( self, 'Enter new contact\'s name' ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
try:
name = dlg.GetValue()
if self._contacts.NameExists( name ) or self._contacts.NameExists( ' identity - ' + name ) or name == 'Anonymous': raise Exception( 'That name is already in use!' )
if name == '': raise Exception( 'Please enter a nickname for the service.' )
public_key = None
host = 'hostname'
port = 45871
contact = ClientConstantsMessages.Contact( public_key, name, host, port )
self._edit_log.append( ( 'add', contact ) )
page = self._Panel( self._contacts, contact, is_identity = False )
self._deletable_names.add( name )
self._contacts.AddPage( page, name, select = True )
except Exception as e:
wx.MessageBox( unicode( e ) )
self.EventAddManually( event )
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventContactChanged( self, event ):
contact_panel = self._contacts.GetCurrentPage()
if contact_panel is not None:
old_name = contact_panel.GetOriginalName()
if old_name in self._deletable_names: self._remove.Enable()
else: self._remove.Disable()
def EventContactChanging( self, event ):
try: self._CheckCurrentContactIsValid()
except Exception as e:
wx.MessageBox( unicode( e ) )
event.Veto()
def EventExport( self, event ):
contact_panel = self._contacts.GetCurrentPage()
if contact_panel is not None:
name = self._contacts.GetCurrentName()
contact = contact_panel.GetContact()
try:
with wx.FileDialog( self, 'select where to export contact', defaultFile = name + '.yaml', style = wx.FD_SAVE ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
with open( dlg.GetPath(), 'wb' ) as f: f.write( yaml.safe_dump( contact ) )
except:
with wx.FileDialog( self, 'select where to export contact', defaultFile = 'contact.yaml', style = wx.FD_SAVE ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
with open( dlg.GetPath(), 'wb' ) as f: f.write( yaml.safe_dump( contact ) )
def EventOk( self, event ):
try: self._CheckCurrentContactIsValid()
except Exception as e:
wx.MessageBox( unicode( e ) )
return
for ( name, page ) in self._contacts.GetNameToPageDict().items():
if page.HasChanges(): self._edit_log.append( ( 'edit', ( page.GetOriginalName(), page.GetContact() ) ) )
try:
if len( self._edit_log ) > 0: wx.GetApp().Write( 'update_contacts', self._edit_log )
except Exception as e: wx.MessageBox( 'Saving contacts to DB raised this error: ' + unicode( e ) )
self.EndModal( wx.ID_OK )
# this isn't used yet!
def EventRemove( self, event ):
contact_panel = self._contacts.GetCurrentPage()
if contact_panel is not None:
name = contact_panel.GetOriginalName()
self._edit_log.append( ( 'delete', name ) )
self._contacts.DeleteCurrentPage()
self._deletable_names.discard( name )
def Import( self, paths ):
try: self._CheckCurrentContactIsValid()
except Exception as e:
wx.MessageBox( unicode( e ) )
return
for path in paths:
try:
with open( path, 'rb' ) as f: file = f.read()
obj = yaml.safe_load( file )
if type( obj ) == ClientConstantsMessages.Contact:
contact = obj
name = contact.GetName()
if self._contacts.NameExists( name ) or self._contacts.NameExists( ' identities - ' + name ) or name == 'Anonymous':
message = 'There already exists a contact or identity with the name ' + name + '. Do you want to overwrite, or make a new contact?'
with DialogYesNo( self, message, yes_label = 'overwrite', no_label = 'make new' ) as dlg:
if True:
name_to_page_dict = self._contacts.GetNameToPageDict()
if name in name_to_page_dict: page = name_to_page_dict[ name ]
else: page = name_to_page_dict[ ' identities - ' + name ]
page.Update( contact )
else:
while self._contacts.NameExists( name ) or self._contacts.NameExists( ' identities - ' + name ) or name == 'Anonymous': name = name + str( random.randint( 0, 9 ) )
( public_key, old_name, host, port ) = contact.GetInfo()
new_contact = ClientConstantsMessages.Contact( public_key, name, host, port )
self._edit_log.append( ( 'add', contact ) )
self._deletable_names.add( name )
page = self._Panel( self._contacts, contact, False )
self._contacts.AddPage( page, name, select = True )
else:
( public_key, old_name, host, port ) = contact.GetInfo()
new_contact = ClientConstantsMessages.Contact( public_key, name, host, port )
self._edit_log.append( ( 'add', contact ) )
self._deletable_names.add( name )
page = self._Panel( self._contacts, contact, False )
self._contacts.AddPage( page, name, select = True )
except:
wx.MessageBox( traceback.format_exc() )
class _Panel( wx.Panel ):
def __init__( self, parent, contact, is_identity ):
wx.Panel.__init__( self, parent )
self._contact = contact
self._is_identity = is_identity
( public_key, name, host, port ) = contact.GetInfo()
contact_key = contact.GetContactKey()
def InitialiseControls():
self._contact_panel = ClientGUICommon.StaticBox( self, 'contact' )
self._name = wx.TextCtrl( self._contact_panel, value = name )
contact_address = host + ':' + str( port )
if contact_key is not None: contact_address = contact_key.encode( 'hex' ) + '@' + contact_address
self._contact_address = wx.TextCtrl( self._contact_panel, value = contact_address )
self._public_key = wx.TextCtrl( self._contact_panel, style = wx.TE_MULTILINE )
if public_key is not None: self._public_key.SetValue( public_key )
def InitialisePanel():
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 1, 1 )
gridbox.AddF( wx.StaticText( self._contact_panel, label='name' ), FLAGS_MIXED )
gridbox.AddF( self._name, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( self._contact_panel, label='contact address' ), FLAGS_MIXED )
gridbox.AddF( self._contact_address, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( self._contact_panel, label = 'public key' ), FLAGS_MIXED )
gridbox.AddF( self._public_key, FLAGS_EXPAND_BOTH_WAYS )
self._contact_panel.AddF( gridbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._contact_panel, FLAGS_EXPAND_BOTH_WAYS )
self.SetSizer( vbox )
InitialiseControls()
InitialisePanel()
def _GetInfo( self ):
public_key = self._public_key.GetValue()
if public_key == '': public_key = None
name = self._name.GetValue()
contact_address = self._contact_address.GetValue()
try:
if '@' in contact_address: ( contact_key, address ) = contact_address.split( '@' )
else: address = contact_address
( host, port ) = address.split( ':' )
try: port = int( port )
except:
port = 45871
wx.MessageBox( 'Could not parse the port!' )
except:
host = 'hostname'
port = 45871
wx.MessageBox( 'Could not parse the contact\'s address!' )
return [ public_key, name, host, port ]
def GetContact( self ):
[ public_key, name, host, port ] = self._GetInfo()
return ClientConstantsMessages.Contact( public_key, name, host, port )
def GetOriginalName( self ): return self._contact.GetName()
def HasChanges( self ):
[ my_public_key, my_name, my_host, my_port ] = self._GetInfo()
[ public_key, name, host, port ] = self._contact.GetInfo()
if my_public_key != public_key: return True
if my_name != name: return True
if my_host != host: return True
if my_port != port: return True
return False
def Update( self, contact ):
( public_key, name, host, port ) = contact.GetInfo()
contact_key = contact.GetContactKey()
self._name.SetValue( name )
contact_address = host + ':' + str( port )
if contact_key is not None: contact_address = contact_key.encode( 'hex' ) + '@' + contact_address
self._contact_address.SetValue( contact_address )
if public_key is None: public_key = ''
self._public_key.SetValue( public_key )
class DialogManage4chanPass( Dialog ):
def __init__( self, parent ):
def InitialiseControls():
self._token = wx.TextCtrl( self, value = token )
self._pin = wx.TextCtrl( self, value = pin )
self._status = wx.StaticText( self )
self._SetStatus()
self._reauthenticate = wx.Button( self, label = 'reauthenticate' )
self._reauthenticate.Bind( wx.EVT_BUTTON, self.EventReauthenticate )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label='Cancel' )
self._cancel.Bind( wx.EVT_BUTTON, self.EventCancel )
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
def InitialisePanel():
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 1, 1 )
gridbox.AddF( wx.StaticText( self, label='token' ), FLAGS_MIXED )
gridbox.AddF( self._token, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( self, label='pin' ), FLAGS_MIXED )
gridbox.AddF( self._pin, FLAGS_EXPAND_BOTH_WAYS )
b_box = wx.BoxSizer( wx.HORIZONTAL )
b_box.AddF( self._ok, FLAGS_MIXED )
b_box.AddF( self._cancel, FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( gridbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( self._status, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._reauthenticate, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( b_box, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
x = max( x, 240 )
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'manage 4chan pass' )
( token, pin, self._timeout ) = wx.GetApp().Read( '4chan_pass' )
InitialiseControls()
InitialisePanel()
def _SetStatus( self ):
if self._timeout == 0: label = 'not authenticated'
elif self._timeout < int( time.time() ): label = 'timed out'
else: label = 'authenticated - ' + HC.ConvertTimestampToPrettyExpires( self._timeout )
self._status.SetLabel( label )
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventOk( self, event ):
token = self._token.GetValue()
pin = self._pin.GetValue()
wx.GetApp().Write( '4chan_pass', token, pin, self._timeout )
self.EndModal( wx.ID_OK )
def EventReauthenticate( self, event ):
try:
token = self._token.GetValue()
pin = self._pin.GetValue()
form_fields = {}
form_fields[ 'act' ] = 'do_login'
form_fields[ 'id' ] = token
form_fields[ 'pin' ] = pin
form_fields[ 'long_login' ] = 'yes'
( ct, body ) = CC.GenerateMultipartFormDataCTAndBodyFromDict( form_fields )
headers = {}
headers[ 'Content-Type' ] = ct
connection = HC.AdvancedHTTPConnection( url = 'https://sys.4chan.org/', accept_cookies = True )
response = connection.request( 'POST', '/auth', headers = headers, body = body )
self._timeout = int( time.time() ) + 365 * 24 * 3600
wx.GetApp().Write( '4chan_pass', token, pin, self._timeout )
self._SetStatus()
except Exception as e:
wx.MessageBox( traceback.format_exc() )
wx.MessageBox( unicode( e ) )
class DialogManageImageboards( Dialog ):
def __init__( self, parent ):
def InitialiseControls():
self._edit_log = []
self._sites = ClientGUICommon.ListBook( self )
sites = wx.GetApp().Read( 'imageboards' )
for ( name, imageboards ) in sites:
page_info = ( self._Panel, ( self._sites, imageboards ), {} )
self._sites.AddPage( page_info, name )
self._add = wx.Button( self, label='add' )
self._add.Bind( wx.EVT_BUTTON, self.EventAdd )
self._add.SetForegroundColour( ( 0, 128, 0 ) )
self._remove = wx.Button( self, label='remove' )
self._remove.Bind( wx.EVT_BUTTON, self.EventRemove )
self._remove.SetForegroundColour( ( 128, 0, 0 ) )
self._export = wx.Button( self, label='export' )
self._export.Bind( wx.EVT_BUTTON, self.EventExport )
self._ok = wx.Button( self, label='ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label='cancel' )
self._cancel.Bind( wx.EVT_BUTTON, self.EventCancel )
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
def InitialisePanel():
add_remove_hbox = wx.BoxSizer( wx.HORIZONTAL )
add_remove_hbox.AddF( self._add, FLAGS_MIXED )
add_remove_hbox.AddF( self._remove, FLAGS_MIXED )
add_remove_hbox.AddF( self._export, FLAGS_MIXED )
ok_hbox = wx.BoxSizer( wx.HORIZONTAL )
ok_hbox.AddF( self._ok, FLAGS_MIXED )
ok_hbox.AddF( self._cancel, FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._sites, FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( add_remove_hbox, FLAGS_SMALL_INDENT )
vbox.AddF( ok_hbox, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( 980, y ) )
Dialog.__init__( self, parent, 'manage imageboards' )
InitialiseControls()
InitialisePanel()
self.SetDropTarget( ClientGUICommon.FileDropTarget( self.Import ) )
def EventAdd( self, event ):
with wx.TextEntryDialog( self, 'Enter new site\'s name' ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
try:
name = dlg.GetValue()
if self._sites.NameExists( name ): raise Exception( 'That name is already in use!' )
if name == '': raise Exception( 'Please enter a nickname for the service.' )
self._edit_log.append( ( 'add', name ) )
page = self._Panel( self._sites, [] )
self._sites.AddPage( page, name, select = True )
except Exception as e:
wx.MessageBox( unicode( e ) )
self.EventAdd( event )
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventExport( self, event ):
site_panel = self._sites.GetCurrentPage()
if site_panel is not None:
name = self._sites.GetCurrentName()
imageboards = site_panel.GetImageboards()
dict = { name : imageboards }
with wx.FileDialog( self, 'select where to export site', defaultFile = 'site.yaml', style = wx.FD_SAVE ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
with open( dlg.GetPath(), 'wb' ) as f: f.write( yaml.safe_dump( dict ) )
def EventOk( self, event ):
for ( name, page ) in self._sites.GetNameToPageDict().items():
if page.HasChanges(): self._edit_log.append( ( 'edit', ( name, page.GetChanges() ) ) )
try:
if len( self._edit_log ) > 0: wx.GetApp().Write( 'update_imageboards', self._edit_log )
except Exception as e: wx.MessageBox( 'Saving imageboards to DB raised this error: ' + unicode( e ) )
self.EndModal( wx.ID_OK )
def EventRemove( self, event ):
site_panel = self._sites.GetCurrentPage()
if site_panel is not None:
name = self._sites.GetCurrentName()
self._edit_log.append( ( 'delete', name ) )
self._sites.DeleteCurrentPage()
def Import( self, paths ):
for path in paths:
try:
with open( path, 'rb' ) as f: file = f.read()
thing = yaml.safe_load( file )
if type( thing ) == dict:
( name, imageboards ) = thing.items()[0]
if not self._sites.NameExists( name ):
self._edit_log.append( ( 'add', name ) )
page = self._Panel( self._sites, [] )
self._sites.AddPage( page, name, select = True )
page = self._sites.GetNameToPageDict()[ name ]
for imageboard in imageboards:
if type( imageboard ) == CC.Imageboard: page.UpdateImageboard( imageboard )
elif type( thing ) == CC.Imageboard:
imageboard = thing
page = self._sites.GetCurrentPage()
page.UpdateImageboard( imageboard )
except:
wx.MessageBox( traceback.format_exc() )
class _Panel( wx.Panel ):
def __init__( self, parent, imageboards ):
wx.Panel.__init__( self, parent )
def InitialiseControls():
self._edit_log = []
self._site_panel = ClientGUICommon.StaticBox( self, 'site' )
self._imageboards = ClientGUICommon.ListBook( self._site_panel )
for imageboard in imageboards:
name = imageboard.GetName()
page_info = ( self._Panel, ( self._imageboards, imageboard ), {} )
self._imageboards.AddPage( page_info, name )
self._add = wx.Button( self._site_panel, label='add' )
self._add.Bind( wx.EVT_BUTTON, self.EventAdd )
self._add.SetForegroundColour( ( 0, 128, 0 ) )
self._remove = wx.Button( self._site_panel, label='remove' )
self._remove.Bind( wx.EVT_BUTTON, self.EventRemove )
self._remove.SetForegroundColour( ( 128, 0, 0 ) )
self._export = wx.Button( self._site_panel, label='export' )
self._export.Bind( wx.EVT_BUTTON, self.EventExport )
def InitialisePanel():
add_remove_hbox = wx.BoxSizer( wx.HORIZONTAL )
add_remove_hbox.AddF( self._add, FLAGS_MIXED )
add_remove_hbox.AddF( self._remove, FLAGS_MIXED )
add_remove_hbox.AddF( self._export, FLAGS_MIXED )
self._site_panel.AddF( self._imageboards, FLAGS_EXPAND_BOTH_WAYS )
self._site_panel.AddF( add_remove_hbox, FLAGS_SMALL_INDENT )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._site_panel, FLAGS_EXPAND_BOTH_WAYS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( 980, y ) )
InitialiseControls()
InitialisePanel()
def EventAdd( self, event ):
with wx.TextEntryDialog( self, 'Enter new imageboard\'s name' ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
try:
name = dlg.GetValue()
if self._imageboards.NameExists( name ): raise Exception( 'That name is already in use!' )
if name == '': raise Exception( 'Please enter a nickname for the service.' )
imageboard = CC.Imageboard( name, '', 60, [], {} )
self._edit_log.append( ( 'add', name ) )
page = self._Panel( self._imageboards, imageboard )
self._imageboards.AddPage( page, name, select = True )
except Exception as e:
wx.MessageBox( unicode( e ) )
self.EventAdd( event )
def EventExport( self, event ):
imageboard_panel = self._imageboards.GetCurrentPage()
if imageboard_panel is not None:
imageboard = imageboard_panel.GetImageboard()
with wx.FileDialog( self, 'select where to export imageboard', defaultFile = 'imageboard.yaml', style = wx.FD_SAVE ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
with open( dlg.GetPath(), 'wb' ) as f: f.write( yaml.safe_dump( imageboard ) )
def EventRemove( self, event ):
imageboard_panel = self._imageboards.GetCurrentPage()
if imageboard_panel is not None:
name = self._imageboards.GetCurrentName()
self._edit_log.append( ( 'delete', name ) )
self._imageboards.DeleteCurrentPage()
def GetChanges( self ):
for page in self._imageboards.GetNameToPageDict().values():
if page.HasChanges(): self._edit_log.append( ( 'edit', page.GetImageboard() ) )
return self._edit_log
def GetImageboards( self ): return [ page.GetImageboard() for page in self._imageboards.GetNameToPageDict().values() ]
def HasChanges( self ): return len( self._edit_log ) > 0 or True in ( page.HasChanges() for page in self._imageboards.GetNameToPageDict().values() )
def UpdateImageboard( self, imageboard ):
name = imageboard.GetName()
if not self._imageboards.NameExists( name ):
new_imageboard = CC.Imageboard( name, '', 60, [], {} )
self._edit_log.append( ( 'add', name ) )
page = self._Panel( self._imageboards, new_imageboard )
self._imageboards.AddPage( page, name, select = True )
page = self._imageboards.GetNameToPageDict()[ name ]
page.Update( imageboard )
class _Panel( wx.Panel ):
def __init__( self, parent, imageboard ):
wx.Panel.__init__( self, parent )
self._imageboard = imageboard
( post_url, flood_time, form_fields, restrictions ) = self._imageboard.GetBoardInfo()
def InitialiseControls():
self._imageboard_panel = ClientGUICommon.StaticBox( self, 'imageboard' )
#
self._basic_info_panel = ClientGUICommon.StaticBox( self._imageboard_panel, 'basic info' )
self._post_url = wx.TextCtrl( self._basic_info_panel, value = post_url )
self._flood_time = wx.SpinCtrl( self._basic_info_panel, min = 5, max = 1200 )
self._flood_time.SetValue( flood_time )
#
self._form_fields_panel = ClientGUICommon.StaticBox( self._imageboard_panel, 'form fields' )
self._form_fields = ClientGUICommon.SaneListCtrl( self._form_fields_panel, 350, [ ( 'name', 120 ), ( 'type', 120 ), ( 'default', -1 ), ( 'editable', 120 ) ] )
for ( name, type, default, editable ) in form_fields:
self._form_fields.Append( ( name, CC.field_string_lookup[ type ], str( default ), str( editable ) ), ( name, type, default, editable ) )
self._add = wx.Button( self._form_fields_panel, label='add' )
self._add.Bind( wx.EVT_BUTTON, self.EventAdd )
self._edit = wx.Button( self._form_fields_panel, label='edit' )
self._edit.Bind( wx.EVT_BUTTON, self.EventEdit )
self._delete = wx.Button( self._form_fields_panel, label='delete' )
self._delete.Bind( wx.EVT_BUTTON, self.EventDelete )
#
self._restrictions_panel = ClientGUICommon.StaticBox( self._imageboard_panel, 'restrictions' )
if CC.RESTRICTION_MIN_RESOLUTION in restrictions: value = restrictions[ CC.RESTRICTION_MIN_RESOLUTION ]
else: value = None
self._min_resolution = ClientGUICommon.NoneableSpinCtrl( self._restrictions_panel, 'min resolution', value, num_dimensions = 2 )
if CC.RESTRICTION_MAX_RESOLUTION in restrictions: value = restrictions[ CC.RESTRICTION_MAX_RESOLUTION ]
else: value = None
self._max_resolution = ClientGUICommon.NoneableSpinCtrl( self._restrictions_panel, 'max resolution', value, num_dimensions = 2 )
if CC.RESTRICTION_MAX_FILE_SIZE in restrictions: value = restrictions[ CC.RESTRICTION_MAX_FILE_SIZE ]
else: value = None
self._max_file_size = ClientGUICommon.NoneableSpinCtrl( self._restrictions_panel, 'max file size (KB)', value, multiplier = 1024 )
self._allowed_mimes_panel = ClientGUICommon.StaticBox( self._restrictions_panel, 'allowed mimes' )
self._mimes = wx.ListBox( self._allowed_mimes_panel )
if CC.RESTRICTION_ALLOWED_MIMES in restrictions: mimes = restrictions[ CC.RESTRICTION_ALLOWED_MIMES ]
else: mimes = []
for mime in mimes: self._mimes.Append( HC.mime_string_lookup[ mime ], mime )
self._mime_choice = wx.Choice( self._allowed_mimes_panel )
for mime in HC.ALLOWED_MIMES: self._mime_choice.Append( HC.mime_string_lookup[ mime ], mime )
self._mime_choice.SetSelection( 0 )
self._add_mime = wx.Button( self._allowed_mimes_panel, label = 'add' )
self._add_mime.Bind( wx.EVT_BUTTON, self.EventAddMime )
self._remove_mime = wx.Button( self._allowed_mimes_panel, label = 'remove' )
self._remove_mime.Bind( wx.EVT_BUTTON, self.EventRemoveMime )
def InitialisePanel():
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
#
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 1, 1 )
gridbox.AddF( wx.StaticText( self._basic_info_panel, label='POST URL' ), FLAGS_MIXED )
gridbox.AddF( self._post_url, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( self._basic_info_panel, label='flood time' ), FLAGS_MIXED )
gridbox.AddF( self._flood_time, FLAGS_EXPAND_BOTH_WAYS )
self._basic_info_panel.AddF( gridbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
#
h_b_box = wx.BoxSizer( wx.HORIZONTAL )
h_b_box.AddF( self._add, FLAGS_MIXED )
h_b_box.AddF( self._edit, FLAGS_MIXED )
h_b_box.AddF( self._delete, FLAGS_MIXED )
self._form_fields_panel.AddF( self._form_fields, FLAGS_EXPAND_BOTH_WAYS )
self._form_fields_panel.AddF( h_b_box, FLAGS_BUTTON_SIZERS )
#
mime_buttons_box = wx.BoxSizer( wx.HORIZONTAL )
mime_buttons_box.AddF( self._mime_choice, FLAGS_MIXED )
mime_buttons_box.AddF( self._add_mime, FLAGS_MIXED )
mime_buttons_box.AddF( self._remove_mime, FLAGS_MIXED )
self._allowed_mimes_panel.AddF( self._mimes, FLAGS_EXPAND_BOTH_WAYS )
self._allowed_mimes_panel.AddF( mime_buttons_box, FLAGS_EXPAND_SIZER_PERPENDICULAR )
self._restrictions_panel.AddF( self._min_resolution, FLAGS_EXPAND_PERPENDICULAR )
self._restrictions_panel.AddF( self._max_resolution, FLAGS_EXPAND_PERPENDICULAR )
self._restrictions_panel.AddF( self._max_file_size, FLAGS_EXPAND_PERPENDICULAR )
self._restrictions_panel.AddF( self._allowed_mimes_panel, FLAGS_EXPAND_BOTH_WAYS )
#
self._imageboard_panel.AddF( self._basic_info_panel, FLAGS_EXPAND_PERPENDICULAR )
self._imageboard_panel.AddF( self._form_fields_panel, FLAGS_EXPAND_BOTH_WAYS )
self._imageboard_panel.AddF( self._restrictions_panel, FLAGS_EXPAND_PERPENDICULAR )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._imageboard_panel, FLAGS_EXPAND_BOTH_WAYS )
self.SetSizer( vbox )
InitialiseControls()
InitialisePanel()
def _GetInfo( self ):
imageboard_name = self._imageboard.GetName()
post_url = self._post_url.GetValue()
flood_time = self._flood_time.GetValue()
# list instead of tumple cause of yaml comparisons
form_fields = self._form_fields.GetClientData()
restrictions = {}
# yaml list again
value = self._min_resolution.GetValue()
if value is not None: restrictions[ CC.RESTRICTION_MIN_RESOLUTION ] = list( value )
# yaml list again
value = self._max_resolution.GetValue()
if value is not None: restrictions[ CC.RESTRICTION_MAX_RESOLUTION ] = list( value )
value = self._max_file_size.GetValue()
if value is not None: restrictions[ CC.RESTRICTION_MAX_FILE_SIZE ] = value
mimes = [ self._mimes.GetClientData( i ) for i in range( self._mimes.GetCount() ) ]
if len( mimes ) > 0: restrictions[ CC.RESTRICTION_ALLOWED_MIMES ] = mimes
return ( imageboard_name, post_url, flood_time, form_fields, restrictions )
def EventAdd( self, event ):
try:
with DialogInputNewFormField( self ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
( name, type, default, editable ) = dlg.GetFormField()
if name in [ form_field[0] for form_field in self._form_fields.GetClientData() ]:
wx.MessageBox( 'There is already a field named ' + name )
self.EventAdd( event )
return
self._form_fields.Append( ( name, CC.field_string_lookup[ type ], str( default ), str( editable ) ), ( name, type, default, editable ) )
except Exception as e: wx.MessageBox( unicode( e ) )
def EventAddMime( self, event ):
selection = self._mime_choice.GetSelection()
if selection != wx.NOT_FOUND:
mime = self._mime_choice.GetClientData( selection )
existing_mimes = [ self._mimes.GetClientData( i ) for i in range( self._mimes.GetCount() ) ]
if mime not in existing_mimes: self._mimes.Append( HC.mime_string_lookup[ mime ], mime )
def EventDelete( self, event ): self._form_fields.RemoveAllSelected()
def EventRemoveMime( self, event ):
selection = self._mimes.GetSelection()
if selection != wx.NOT_FOUND: self._mimes.Delete( selection )
def EventEdit( self, event ):
indices = self._form_fields.GetAllSelected()
for index in indices:
( name, type, default, editable ) = self._form_fields.GetClientData( index )
form_field = ( name, type, default, editable )
try:
with DialogInputNewFormField( self, form_field ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
old_name = name
( name, type, default, editable ) = dlg.GetFormField()
if old_name != name:
if name in [ form_field[0] for form_field in self._form_fields.GetClientData() ]: raise Exception( 'You already have a form field called ' + name + '; delete or edit that one first' )
self._form_fields.UpdateRow( index, ( name, CC.field_string_lookup[ type ], str( default ), str( editable ) ), ( name, type, default, editable ) )
except Exception as e: wx.MessageBox( unicode( e ) )
def GetImageboard( self ):
( name, post_url, flood_time, form_fields, restrictions ) = self._GetInfo()
return CC.Imageboard( name, post_url, flood_time, form_fields, restrictions )
def HasChanges( self ):
( my_name, my_post_url, my_flood_time, my_form_fields, my_restrictions ) = self._GetInfo()
( post_url, flood_time, form_fields, restrictions ) = self._imageboard.GetBoardInfo()
if post_url != my_post_url: return True
if flood_time != my_flood_time: return True
if set( [ tuple( item ) for item in form_fields ] ) != set( [ tuple( item ) for item in my_form_fields ] ): return True
if restrictions != my_restrictions: return True
return False
def Update( self, imageboard ):
( post_url, flood_time, form_fields, restrictions ) = imageboard.GetBoardInfo()
self._post_url.SetValue( post_url )
self._flood_time.SetValue( flood_time )
self._form_fields.ClearAll()
self._form_fields.InsertColumn( 0, 'name', width = 120 )
self._form_fields.InsertColumn( 1, 'type', width = 120 )
self._form_fields.InsertColumn( 2, 'default' )
self._form_fields.InsertColumn( 3, 'editable', width = 120 )
self._form_fields.setResizeColumn( 3 ) # default
for ( name, type, default, editable ) in form_fields:
self._form_fields.Append( ( name, CC.field_string_lookup[ type ], str( default ), str( editable ) ), ( name, type, default, editable ) )
if CC.RESTRICTION_MIN_RESOLUTION in restrictions: value = restrictions[ CC.RESTRICTION_MIN_RESOLUTION ]
else: value = None
self._min_resolution.SetValue( value )
if CC.RESTRICTION_MAX_RESOLUTION in restrictions: value = restrictions[ CC.RESTRICTION_MAX_RESOLUTION ]
else: value = None
self._max_resolution.SetValue( value )
if CC.RESTRICTION_MAX_FILE_SIZE in restrictions: value = restrictions[ CC.RESTRICTION_MAX_FILE_SIZE ]
else: value = None
self._max_file_size.SetValue( value )
self._mimes.Clear()
if CC.RESTRICTION_ALLOWED_MIMES in restrictions: mimes = restrictions[ CC.RESTRICTION_ALLOWED_MIMES ]
else: mimes = []
for mime in mimes: self._mimes.Append( HC.mime_string_lookup[ mime ], mime )
class DialogManageOptionsFileRepository( Dialog ):
def __init__( self, parent, service_identifier ):
def InitialiseControls():
self._file_repository_panel = ClientGUICommon.StaticBox( self, 'file repository' )
self._max_monthly_data = ClientGUICommon.NoneableSpinCtrl( self._file_repository_panel, 'max monthly data (MB)', options[ 'max_monthly_data' ], multiplier = 1048576 )
self._max_storage = ClientGUICommon.NoneableSpinCtrl( self._file_repository_panel, 'max storage (MB)', options[ 'max_monthly_data' ], multiplier = 1048576 )
self._log_uploader_ips = wx.CheckBox( self._file_repository_panel, label='' )
self._log_uploader_ips.SetValue( options[ 'log_uploader_ips' ] )
self._message = wx.TextCtrl( self._file_repository_panel, value = options[ 'message' ] )
self._save_button = wx.Button( self, label='Save' )
self._save_button.Bind( wx.EVT_BUTTON, self.EventOK )
self._save_button.SetForegroundColour( ( 0, 128, 0 ) )
self._close_button = wx.Button( self, id = wx.ID_CANCEL, label='Cancel' )
self._close_button.Bind( wx.EVT_BUTTON, self.EventCancel )
self._close_button.SetForegroundColour( ( 128, 0, 0 ) )
def InitialisePanel():
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 1, 1 )
gridbox.AddF( wx.StaticText( self._file_repository_panel, label='Log uploader ips?' ), FLAGS_MIXED )
gridbox.AddF( self._log_uploader_ips, FLAGS_MIXED )
gridbox.AddF( wx.StaticText( self._file_repository_panel, label='Message' ), FLAGS_MIXED )
gridbox.AddF( self._message, FLAGS_MIXED )
self._file_repository_panel.AddF( self._max_monthly_data, FLAGS_EXPAND_PERPENDICULAR )
self._file_repository_panel.AddF( self._max_storage, FLAGS_EXPAND_PERPENDICULAR )
self._file_repository_panel.AddF( gridbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
buttons = wx.BoxSizer( wx.HORIZONTAL )
buttons.AddF( self._save_button, FLAGS_SMALL_INDENT )
buttons.AddF( self._close_button, FLAGS_SMALL_INDENT )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._file_repository_panel, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( buttons, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x + 80, y ) )
Dialog.__init__( self, parent, service_identifier.GetName() + ' options' )
self._service_identifier = service_identifier
self._service = wx.GetApp().Read( 'service', service_identifier )
connection = self._service.GetConnection()
options = connection.Get( 'options' )
InitialiseControls()
InitialisePanel()
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventOK( self, event ):
options = {}
options[ 'max_monthly_data' ] = self._max_monthly_data.GetValue()
options[ 'max_storage' ] = self._max_storage.GetValue()
options[ 'log_uploader_ips' ] = self._log_uploader_ips.GetValue()
options[ 'message' ] = self._message.GetValue()
try:
connection = self._service.GetConnection()
connection.Post( 'options', options = options )
except Exception as e: wx.MessageBox( 'Something went wrong when trying to send the options to the file repository: ' + unicode( e ) )
self.EndModal( wx.ID_OK )
class DialogManageOptionsLocal( Dialog ):
def __init__( self, parent ):
def InitialiseControls():
self._listbook = ClientGUICommon.ListBook( self )
# files and memory
self._file_page = wx.Panel( self._listbook )
self._file_page.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
self._export_location = wx.DirPickerCtrl( self._file_page, style = wx.DIRP_USE_TEXTCTRL )
if self._options[ 'export_path' ] is not None: self._export_location.SetPath( HC.ConvertPortablePathToAbsPath( self._options[ 'export_path' ] ) )
self._exclude_deleted_files = wx.CheckBox( self._file_page, label='' )
self._exclude_deleted_files.SetValue( self._options[ 'exclude_deleted_files' ] )
self._thumbnail_cache_size = wx.SpinCtrl( self._file_page, min = 10, max = 3000 )
self._thumbnail_cache_size.SetValue( int( self._options[ 'thumbnail_cache_size' ] / 1048576 ) )
self._thumbnail_cache_size.Bind( wx.EVT_SPINCTRL, self.EventThumbnailsUpdate )
self._estimated_number_thumbnails = wx.StaticText( self._file_page, label = '' )
self._preview_cache_size = wx.SpinCtrl( self._file_page, min = 20, max = 3000 )
self._preview_cache_size.SetValue( int( self._options[ 'preview_cache_size' ] / 1048576 ) )
self._preview_cache_size.Bind( wx.EVT_SPINCTRL, self.EventPreviewsUpdate )
self._estimated_number_previews = wx.StaticText( self._file_page, label = '' )
self._fullscreen_cache_size = wx.SpinCtrl( self._file_page, min = 100, max = 3000 )
self._fullscreen_cache_size.SetValue( int( self._options[ 'fullscreen_cache_size' ] / 1048576 ) )
self._fullscreen_cache_size.Bind( wx.EVT_SPINCTRL, self.EventFullscreensUpdate )
self._estimated_number_fullscreens = wx.StaticText( self._file_page, label = '' )
( thumbnail_width, thumbnail_height ) = self._options[ 'thumbnail_dimensions' ]
self._thumbnail_width = wx.SpinCtrl( self._file_page, min=20, max=200 )
self._thumbnail_width.SetValue( thumbnail_width )
self._thumbnail_width.Bind( wx.EVT_SPINCTRL, self.EventThumbnailsUpdate )
self._thumbnail_height = wx.SpinCtrl( self._file_page, min=20, max=200 )
self._thumbnail_height.SetValue( thumbnail_height )
self._thumbnail_height.Bind( wx.EVT_SPINCTRL, self.EventThumbnailsUpdate )
self._num_autocomplete_chars = wx.SpinCtrl( self._file_page, min = 1, max = 100 )
self._num_autocomplete_chars.SetValue( self._options[ 'num_autocomplete_chars' ] )
self._num_autocomplete_chars.SetToolTipString( 'how many characters you enter before the gui fetches autocomplete results from the db' + os.linesep + 'increase this if you find autocomplete results are slow' )
self._listbook.AddPage( self._file_page, 'files and memory' )
# gui
self._gui_page = wx.Panel( self._listbook )
self._gui_page.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
self._gui_capitalisation = wx.CheckBox( self._gui_page )
self._gui_capitalisation.SetValue( self._options[ 'gui_capitalisation' ] )
self._gui_show_all_tags_in_autocomplete = wx.CheckBox( self._gui_page )
self._gui_show_all_tags_in_autocomplete.SetValue( self._options[ 'show_all_tags_in_autocomplete' ] )
self._default_tag_sort = wx.Choice( self._gui_page )
self._default_tag_sort.Append( 'lexicographic (a-z)', CC.SORT_BY_LEXICOGRAPHIC_ASC )
self._default_tag_sort.Append( 'lexicographic (z-a)', CC.SORT_BY_LEXICOGRAPHIC_DESC )
self._default_tag_sort.Append( 'incidence (desc)', CC.SORT_BY_INCIDENCE_DESC )
self._default_tag_sort.Append( 'incidence (asc)', CC.SORT_BY_INCIDENCE_ASC )
if self._options[ 'default_tag_sort' ] == CC.SORT_BY_LEXICOGRAPHIC_ASC: self._default_tag_sort.Select( 0 )
elif self._options[ 'default_tag_sort' ] == CC.SORT_BY_LEXICOGRAPHIC_DESC: self._default_tag_sort.Select( 1 )
elif self._options[ 'default_tag_sort' ] == CC.SORT_BY_INCIDENCE_DESC: self._default_tag_sort.Select( 2 )
elif self._options[ 'default_tag_sort' ] == CC.SORT_BY_INCIDENCE_ASC: self._default_tag_sort.Select( 3 )
service_identifiers = wx.GetApp().Read( 'service_identifiers', ( HC.LOCAL_TAG, HC.TAG_REPOSITORY ) )
self._default_tag_repository = wx.Choice( self._gui_page )
for service_identifier in service_identifiers: self._default_tag_repository.Append( service_identifier.GetName(), service_identifier )
self._default_tag_repository.SetStringSelection( self._options[ 'default_tag_repository' ].GetName() )
self._fullscreen_borderless = wx.CheckBox( self._gui_page )
self._fullscreen_borderless.SetValue( self._options[ 'fullscreen_borderless' ] )
self._listbook.AddPage( self._gui_page, 'gui' )
# default file system predicates
system_predicates = self._options[ 'file_system_predicates' ]
self._file_system_predicates_page = wx.Panel( self._listbook )
self._file_system_predicates_page.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
( sign, years, months, days ) = system_predicates[ 'age' ]
self._file_system_predicate_age_sign = wx.Choice( self._file_system_predicates_page, choices=[ '<', u'\u2248', '>' ] )
self._file_system_predicate_age_sign.SetSelection( sign )
self._file_system_predicate_age_years = wx.SpinCtrl( self._file_system_predicates_page, max = 30 )
self._file_system_predicate_age_years.SetValue( years )
self._file_system_predicate_age_months = wx.SpinCtrl( self._file_system_predicates_page, max = 60 )
self._file_system_predicate_age_months.SetValue( months )
self._file_system_predicate_age_days = wx.SpinCtrl( self._file_system_predicates_page, max = 90 )
self._file_system_predicate_age_days.SetValue( days )
( sign, s, ms ) = system_predicates[ 'duration' ]
self._file_system_predicate_duration_sign = wx.Choice( self._file_system_predicates_page, choices=[ '<', u'\u2248', '=', '>' ] )
self._file_system_predicate_duration_sign.SetSelection( sign )
self._file_system_predicate_duration_s = wx.SpinCtrl( self._file_system_predicates_page, max = 3599 )
self._file_system_predicate_duration_s.SetValue( s )
self._file_system_predicate_duration_ms = wx.SpinCtrl( self._file_system_predicates_page, max = 999 )
self._file_system_predicate_duration_ms.SetValue( ms )
( sign, height ) = system_predicates[ 'height' ]
self._file_system_predicate_height_sign = wx.Choice( self._file_system_predicates_page, choices=[ '<', u'\u2248', '=', '>' ] )
self._file_system_predicate_height_sign.SetSelection( sign )
self._file_system_predicate_height = wx.SpinCtrl( self._file_system_predicates_page, max = 200000 )
self._file_system_predicate_height.SetValue( height )
limit = system_predicates[ 'limit' ]
self._file_system_predicate_limit = wx.SpinCtrl( self._file_system_predicates_page, max = 1000000 )
self._file_system_predicate_limit.SetValue( limit )
( media, type ) = system_predicates[ 'mime' ]
self._file_system_predicate_mime_media = wx.Choice( self._file_system_predicates_page, choices=[ 'image', 'application' ] )
self._file_system_predicate_mime_media.SetSelection( media )
self._file_system_predicate_mime_media.Bind( wx.EVT_CHOICE, self.EventFileSystemPredicateMime )
self._file_system_predicate_mime_type = wx.Choice( self._file_system_predicates_page, choices=[], size = ( 120, -1 ) )
self.EventFileSystemPredicateMime( None )
self._file_system_predicate_mime_type.SetSelection( type )
( sign, num_tags ) = system_predicates[ 'num_tags' ]
self._file_system_predicate_num_tags_sign = wx.Choice( self._file_system_predicates_page, choices=[ '<', '=', '>' ] )
self._file_system_predicate_num_tags_sign.SetSelection( sign )
self._file_system_predicate_num_tags = wx.SpinCtrl( self._file_system_predicates_page, max = 2000 )
self._file_system_predicate_num_tags.SetValue( num_tags )
( sign, value ) = system_predicates[ 'local_rating_numerical' ]
self._file_system_predicate_local_rating_numerical_sign = wx.Choice( self._file_system_predicates_page, choices=[ '>', '<', '=', u'\u2248', '=rated', '=not rated', '=uncertain' ] )
self._file_system_predicate_local_rating_numerical_sign.SetSelection( sign )
self._file_system_predicate_local_rating_numerical_value = wx.SpinCtrl( self._file_system_predicates_page, min = 0, max = 50000 )
self._file_system_predicate_local_rating_numerical_value.SetValue( value )
value = system_predicates[ 'local_rating_like' ]
self._file_system_predicate_local_rating_like_value = wx.Choice( self._file_system_predicates_page, choices=[ 'like', 'dislike', 'rated', 'not rated' ] )
self._file_system_predicate_local_rating_like_value.SetSelection( value )
( sign, width, height ) = system_predicates[ 'ratio' ]
self._file_system_predicate_ratio_sign = wx.Choice( self._file_system_predicates_page, choices=[ '=', u'\u2248' ] )
self._file_system_predicate_ratio_sign.SetSelection( sign )
self._file_system_predicate_ratio_width = wx.SpinCtrl( self._file_system_predicates_page, max = 50000 )
self._file_system_predicate_ratio_width.SetValue( width )
self._file_system_predicate_ratio_height = wx.SpinCtrl( self._file_system_predicates_page, max = 50000 )
self._file_system_predicate_ratio_height.SetValue( height )
( sign, size, unit ) = system_predicates[ 'size' ]
self._file_system_predicate_size_sign = wx.Choice( self._file_system_predicates_page, choices=[ '<', u'\u2248', '=', '>' ] )
self._file_system_predicate_size_sign.SetSelection( sign )
self._file_system_predicate_size = wx.SpinCtrl( self._file_system_predicates_page, max = 1048576 )
self._file_system_predicate_size.SetValue( size )
self._file_system_predicate_size_unit = wx.Choice( self._file_system_predicates_page, choices=[ 'B', 'KB', 'MB', 'GB' ] )
self._file_system_predicate_size_unit.SetSelection( unit )
( sign, width ) = system_predicates[ 'width' ]
self._file_system_predicate_width_sign = wx.Choice( self._file_system_predicates_page, choices=[ '<', u'\u2248', '=', '>' ] )
self._file_system_predicate_width_sign.SetSelection( sign )
self._file_system_predicate_width = wx.SpinCtrl( self._file_system_predicates_page, max = 200000 )
self._file_system_predicate_width.SetValue( width )
( sign, num_words ) = system_predicates[ 'num_words' ]
self._file_system_predicate_num_words_sign = wx.Choice( self._file_system_predicates_page, choices=[ '<', u'\u2248', '=', '>' ] )
self._file_system_predicate_num_words_sign.SetSelection( sign )
self._file_system_predicate_num_words = wx.SpinCtrl( self._file_system_predicates_page, max = 1000000 )
self._file_system_predicate_num_words.SetValue( num_words )
self._listbook.AddPage( self._file_system_predicates_page, 'default file system predicates' )
# colours
self._colour_page = wx.Panel( self._listbook )
self._colour_page.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
self._namespace_colours = ClientGUICommon.TagsBoxOptions( self._colour_page, self._options[ 'namespace_colours' ] )
self._edit_namespace_colour = wx.Button( self._colour_page, label = 'edit selected' )
self._edit_namespace_colour.Bind( wx.EVT_BUTTON, self.EventEditNamespaceColour )
self._new_namespace_colour = wx.TextCtrl( self._colour_page, style = wx.TE_PROCESS_ENTER )
self._new_namespace_colour.Bind( wx.EVT_KEY_DOWN, self.EventKeyDownNamespace )
self._listbook.AddPage( self._colour_page, 'colours' )
# sort/collect
self._sort_by_page = wx.Panel( self._listbook )
self._sort_by_page.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
self._default_sort = ClientGUICommon.ChoiceSort( self._sort_by_page, sort_by = self._options[ 'sort_by' ] )
self._default_collect = ClientGUICommon.CheckboxCollect( self._sort_by_page )
self._sort_by = wx.ListBox( self._sort_by_page )
self._sort_by.Bind( wx.EVT_LEFT_DCLICK, self.EventRemoveSortBy )
for ( sort_by_type, sort_by ) in self._options[ 'sort_by' ]: self._sort_by.Append( '-'.join( sort_by ), sort_by )
self._new_sort_by = wx.TextCtrl( self._sort_by_page, style = wx.TE_PROCESS_ENTER )
self._new_sort_by.Bind( wx.EVT_KEY_DOWN, self.EventKeyDownSortBy )
self._listbook.AddPage( self._sort_by_page, 'sort/collect' )
# shortcuts
self._shortcuts_page = wx.Panel( self._listbook )
self._shortcuts_page.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
self._shortcuts = ClientGUICommon.SaneListCtrl( self._shortcuts_page, 480, [ ( 'modifier', 120 ), ( 'key', 120 ), ( 'action', -1 ) ] )
for ( modifier, key_dict ) in self._options[ 'shortcuts' ].items():
for ( key, action ) in key_dict.items():
( pretty_modifier, pretty_key, pretty_action ) = HC.ConvertShortcutToPrettyShortcut( modifier, key, action )
self._shortcuts.Append( ( pretty_modifier, pretty_key, pretty_action ), ( modifier, key, action ) )
self._SortListCtrl()
self._shortcuts_add = wx.Button( self._shortcuts_page, label = 'add' )
self._shortcuts_add.Bind( wx.EVT_BUTTON, self.EventShortcutsAdd )
self._shortcuts_edit = wx.Button( self._shortcuts_page, label = 'edit' )
self._shortcuts_edit.Bind( wx.EVT_BUTTON, self.EventShortcutsEdit )
self._shortcuts_delete = wx.Button( self._shortcuts_page, label = 'delete' )
self._shortcuts_delete.Bind( wx.EVT_BUTTON, self.EventShortcutsDelete )
self._listbook.AddPage( self._shortcuts_page, 'shortcuts' )
#
self._save_button = wx.Button( self, label='Save' )
self._save_button.Bind( wx.EVT_BUTTON, self.EventOK )
self._save_button.SetForegroundColour( ( 0, 128, 0 ) )
self._close_button = wx.Button( self, id = wx.ID_CANCEL, label='Cancel' )
self._close_button.Bind( wx.EVT_BUTTON, self.EventCancel )
self._close_button.SetForegroundColour( ( 128, 0, 0 ) )
def InitialisePanel():
thumbnails_sizer = wx.BoxSizer( wx.HORIZONTAL )
thumbnails_sizer.AddF( self._thumbnail_cache_size, FLAGS_MIXED )
thumbnails_sizer.AddF( self._estimated_number_thumbnails, FLAGS_MIXED )
previews_sizer = wx.BoxSizer( wx.HORIZONTAL )
previews_sizer.AddF( self._preview_cache_size, FLAGS_MIXED )
previews_sizer.AddF( self._estimated_number_previews, FLAGS_MIXED )
fullscreens_sizer = wx.BoxSizer( wx.HORIZONTAL )
fullscreens_sizer.AddF( self._fullscreen_cache_size, FLAGS_MIXED )
fullscreens_sizer.AddF( self._estimated_number_fullscreens, FLAGS_MIXED )
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 1, 1 )
gridbox.AddF( wx.StaticText( self._file_page, label='Default export directory: ' ), FLAGS_MIXED )
gridbox.AddF( self._export_location, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( self._file_page, label='Exclude deleted files from new imports and remote searches: ' ), FLAGS_MIXED )
gridbox.AddF( self._exclude_deleted_files, FLAGS_MIXED )
gridbox.AddF( wx.StaticText( self._file_page, label='MB memory reserved for thumbnail cache: ' ), FLAGS_MIXED )
gridbox.AddF( thumbnails_sizer, FLAGS_NONE )
gridbox.AddF( wx.StaticText( self._file_page, label='MB memory reserved for preview cache: ' ), FLAGS_MIXED )
gridbox.AddF( previews_sizer, FLAGS_NONE )
gridbox.AddF( wx.StaticText( self._file_page, label='MB memory reserved for fullscreen cache: ' ), FLAGS_MIXED )
gridbox.AddF( fullscreens_sizer, FLAGS_NONE )
gridbox.AddF( wx.StaticText( self._file_page, label='Thumbnail width: ' ), FLAGS_MIXED )
gridbox.AddF( self._thumbnail_width, FLAGS_MIXED )
gridbox.AddF( wx.StaticText( self._file_page, label='Thumbnail height: ' ), FLAGS_MIXED )
gridbox.AddF( self._thumbnail_height, FLAGS_MIXED )
gridbox.AddF( wx.StaticText( self._file_page, label='Autocomplete character threshold: ' ), FLAGS_MIXED )
gridbox.AddF( self._num_autocomplete_chars, FLAGS_MIXED )
self._file_page.SetSizer( gridbox )
#
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 1, 1 )
gridbox.AddF( wx.StaticText( self._gui_page, label = 'Default tag service in manage tag dialogs:' ), FLAGS_MIXED )
gridbox.AddF( self._default_tag_repository, FLAGS_MIXED )
gridbox.AddF( wx.StaticText( self._gui_page, label = 'Default tag sort on management panel:' ), FLAGS_MIXED )
gridbox.AddF( self._default_tag_sort, FLAGS_MIXED )
gridbox.AddF( wx.StaticText( self._gui_page, label='Capitalise gui: ' ), FLAGS_MIXED )
gridbox.AddF( self._gui_capitalisation, FLAGS_MIXED )
gridbox.AddF( wx.StaticText( self._gui_page, label='By default, search non-local tags in write-autocomplete: ' ), FLAGS_MIXED )
gridbox.AddF( self._gui_show_all_tags_in_autocomplete, FLAGS_MIXED )
gridbox.AddF( wx.StaticText( self._gui_page, label='By default, show fullscreen without borders: ' ), FLAGS_MIXED )
gridbox.AddF( self._fullscreen_borderless, FLAGS_MIXED )
self._gui_page.SetSizer( gridbox )
#
vbox = wx.BoxSizer( wx.VERTICAL )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self._file_system_predicates_page, label='system:age' ), FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_age_sign, FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_age_years, FLAGS_MIXED )
hbox.AddF( wx.StaticText( self._file_system_predicates_page, label='years' ), FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_age_months, FLAGS_MIXED )
hbox.AddF( wx.StaticText( self._file_system_predicates_page, label='months' ), FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_age_days, FLAGS_MIXED )
hbox.AddF( wx.StaticText( self._file_system_predicates_page, label='days' ), FLAGS_MIXED )
vbox.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self._file_system_predicates_page, label='system:duration' ), FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_duration_sign, FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_duration_s, FLAGS_MIXED )
hbox.AddF( wx.StaticText( self._file_system_predicates_page, label='s' ), FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_duration_ms, FLAGS_MIXED )
hbox.AddF( wx.StaticText( self._file_system_predicates_page, label='ms' ), FLAGS_MIXED )
vbox.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self._file_system_predicates_page, label='system:height' ), FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_height_sign, FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_height, FLAGS_MIXED )
vbox.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self._file_system_predicates_page, label='system:limit=' ), FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_limit, FLAGS_MIXED )
vbox.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self._file_system_predicates_page, label='system:mime' ), FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_mime_media, FLAGS_MIXED )
hbox.AddF( wx.StaticText( self._file_system_predicates_page, label='/' ), FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_mime_type, FLAGS_MIXED )
vbox.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self._file_system_predicates_page, label='system:num_tags' ), FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_num_tags_sign, FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_num_tags, FLAGS_MIXED )
vbox.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self._file_system_predicates_page, label='system:local_rating_like' ), FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_local_rating_like_value, FLAGS_MIXED )
vbox.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self._file_system_predicates_page, label='system:local_rating_numerical' ), FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_local_rating_numerical_sign, FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_local_rating_numerical_value, FLAGS_MIXED )
vbox.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self._file_system_predicates_page, label='system:ratio' ), FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_ratio_sign, FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_ratio_width, FLAGS_MIXED )
hbox.AddF( wx.StaticText( self._file_system_predicates_page, label=':' ), FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_ratio_height, FLAGS_MIXED )
vbox.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self._file_system_predicates_page, label='system:size' ), FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_size_sign, FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_size, FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_size_unit, FLAGS_MIXED )
vbox.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self._file_system_predicates_page, label='system:width' ), FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_width_sign, FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_width, FLAGS_MIXED )
vbox.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self._file_system_predicates_page, label='system:num_words' ), FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_num_words_sign, FLAGS_MIXED )
hbox.AddF( self._file_system_predicate_num_words, FLAGS_MIXED )
vbox.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
self._file_system_predicates_page.SetSizer( vbox )
#
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._namespace_colours, FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( self._new_namespace_colour, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._edit_namespace_colour, FLAGS_EXPAND_PERPENDICULAR )
self._colour_page.SetSizer( vbox )
#
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 1, 1 )
gridbox.AddF( wx.StaticText( self._sort_by_page, label='Default sort: ' ), FLAGS_MIXED )
gridbox.AddF( self._default_sort, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( self._sort_by_page, label='Default collect: ' ), FLAGS_MIXED )
gridbox.AddF( self._default_collect, FLAGS_EXPAND_BOTH_WAYS )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( gridbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( self._sort_by, FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( self._new_sort_by, FLAGS_EXPAND_PERPENDICULAR )
self._sort_by_page.SetSizer( vbox )
#
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( wx.StaticText( self._shortcuts_page, label = 'These shortcuts are global to the main gui! You probably want to stick to function keys or ctrl + something!' ), FLAGS_MIXED )
vbox.AddF( self._shortcuts, FLAGS_EXPAND_BOTH_WAYS )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( self._shortcuts_add, FLAGS_BUTTON_SIZERS )
hbox.AddF( self._shortcuts_edit, FLAGS_BUTTON_SIZERS )
hbox.AddF( self._shortcuts_delete, FLAGS_BUTTON_SIZERS )
vbox.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
self._shortcuts_page.SetSizer( vbox )
#
buttons = wx.BoxSizer( wx.HORIZONTAL )
buttons.AddF( self._save_button, FLAGS_SMALL_INDENT )
buttons.AddF( self._close_button, FLAGS_SMALL_INDENT )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._listbook, FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( buttons, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
if x < 800: x = 800
if y < 600: y = 600
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'hydrus client options' )
InitialiseControls()
InitialisePanel()
self.EventFullscreensUpdate( None )
self.EventPreviewsUpdate( None )
self.EventThumbnailsUpdate( None )
wx.CallAfter( self._file_page.Layout ) # draws the static texts correctly
def _SortListCtrl( self ): self._shortcuts.SortListItems( 2 )
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventEditNamespaceColour( self, event ):
result = self._namespace_colours.GetSelectedNamespaceColour()
if result is not None:
( namespace, colour ) = result
colour_data = wx.ColourData()
colour_data.SetColour( colour )
colour_data.SetChooseFull( True )
with wx.ColourDialog( self, data = colour_data ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
colour_data = dlg.GetColourData()
colour = colour_data.GetColour()
self._namespace_colours.SetNamespaceColour( namespace, colour )
def EventFileSystemPredicateMime( self, event ):
media = self._file_system_predicate_mime_media.GetStringSelection()
self._file_system_predicate_mime_type.Clear()
if media == 'image':
self._file_system_predicate_mime_type.Append( 'any', HC.IMAGES )
self._file_system_predicate_mime_type.Append( 'jpeg', HC.IMAGE_JPEG )
self._file_system_predicate_mime_type.Append( 'png', HC.IMAGE_PNG )
self._file_system_predicate_mime_type.Append( 'gif', HC.IMAGE_GIF )
elif media == 'application':
self._file_system_predicate_mime_type.Append( 'any', HC.APPLICATIONS )
self._file_system_predicate_mime_type.Append( 'pdf', HC.APPLICATION_PDF )
self._file_system_predicate_mime_type.Append( 'x-shockwave-flash', HC.APPLICATION_FLASH )
elif media == 'video':
self._file_system_predicate_mime_type.Append( 'x-flv', HC.VIDEO_FLV )
self._file_system_predicate_mime_type.SetSelection( 0 )
def EventFullscreensUpdate( self, event ):
( width, height ) = wx.GetDisplaySize()
estimated_bytes_per_fullscreen = 3 * width * height
self._estimated_number_fullscreens.SetLabel( '(about ' + HC.ConvertIntToPrettyString( ( self._fullscreen_cache_size.GetValue() * 1048576 ) / estimated_bytes_per_fullscreen ) + '-' + HC.ConvertIntToPrettyString( ( self._fullscreen_cache_size.GetValue() * 1048576 ) / ( estimated_bytes_per_fullscreen / 4 ) ) + ' images)' )
def EventKeyDownNamespace( self, event ):
if event.KeyCode in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ):
namespace = self._new_namespace_colour.GetValue()
if namespace != '':
self._namespace_colours.SetNamespaceColour( namespace, wx.Colour( random.randint( 0, 255 ), random.randint( 0, 255 ), random.randint( 0, 255 ) ) )
self._new_namespace_colour.SetValue( '' )
else: event.Skip()
def EventKeyDownSortBy( self, event ):
if event.KeyCode in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ):
sort_by_string = self._new_sort_by.GetValue()
if sort_by_string != '':
try: sort_by = sort_by_string.split( '-' )
except:
wx.MessageBox( 'Could not parse that sort by string!' )
return
self._sort_by.Append( sort_by_string, sort_by )
self._new_sort_by.SetValue( '' )
else: event.Skip()
def EventOK( self, event ):
self._options[ 'gui_capitalisation' ] = self._gui_capitalisation.GetValue()
self._options[ 'show_all_tags_in_autocomplete' ] = self._gui_show_all_tags_in_autocomplete.GetValue()
self._options[ 'fullscreen_borderless' ] = self._fullscreen_borderless.GetValue()
self._options[ 'export_path' ] = HC.ConvertAbsPathToPortablePath( self._export_location.GetPath() )
self._options[ 'default_sort' ] = self._default_sort.GetSelection()
self._options[ 'default_collect' ] = self._default_collect.GetChoice()
self._options[ 'exclude_deleted_files' ] = self._exclude_deleted_files.GetValue()
self._options[ 'thumbnail_cache_size' ] = self._thumbnail_cache_size.GetValue() * 1048576
self._options[ 'preview_cache_size' ] = self._preview_cache_size.GetValue() * 1048576
self._options[ 'fullscreen_cache_size' ] = self._fullscreen_cache_size.GetValue() * 1048576
self._options[ 'thumbnail_dimensions' ] = [ self._thumbnail_width.GetValue(), self._thumbnail_height.GetValue() ]
self._options[ 'num_autocomplete_chars' ] = self._num_autocomplete_chars.GetValue()
self._options[ 'namespace_colours' ] = self._namespace_colours.GetNamespaceColours()
sort_by_choices = []
for sort_by in [ self._sort_by.GetClientData( i ) for i in range( self._sort_by.GetCount() ) ]: sort_by_choices.append( ( 'namespaces', sort_by ) )
self._options[ 'sort_by' ] = sort_by_choices
system_predicates = {}
system_predicates[ 'age' ] = ( self._file_system_predicate_age_sign.GetSelection(), self._file_system_predicate_age_years.GetValue(), self._file_system_predicate_age_months.GetValue(), self._file_system_predicate_age_days.GetValue() )
system_predicates[ 'duration' ] = ( self._file_system_predicate_duration_sign.GetSelection(), self._file_system_predicate_duration_s.GetValue(), self._file_system_predicate_duration_ms.GetValue() )
system_predicates[ 'height' ] = ( self._file_system_predicate_height_sign.GetSelection(), self._file_system_predicate_height.GetValue() )
system_predicates[ 'limit' ] = self._file_system_predicate_limit.GetValue()
system_predicates[ 'mime' ] = ( self._file_system_predicate_mime_media.GetSelection(), self._file_system_predicate_mime_type.GetSelection() )
system_predicates[ 'num_tags' ] = ( self._file_system_predicate_num_tags_sign.GetSelection(), self._file_system_predicate_num_tags.GetValue() )
system_predicates[ 'local_rating_like' ] = self._file_system_predicate_local_rating_like_value.GetSelection()
system_predicates[ 'local_rating_numerical' ] = ( self._file_system_predicate_local_rating_numerical_sign.GetSelection(), self._file_system_predicate_local_rating_numerical_value.GetValue() )
system_predicates[ 'ratio' ] = ( self._file_system_predicate_ratio_sign.GetSelection(), self._file_system_predicate_ratio_width.GetValue(), self._file_system_predicate_ratio_height.GetValue() )
system_predicates[ 'size' ] = ( self._file_system_predicate_size_sign.GetSelection(), self._file_system_predicate_size.GetValue(), self._file_system_predicate_size_unit.GetSelection() )
system_predicates[ 'width' ] = ( self._file_system_predicate_width_sign.GetSelection(), self._file_system_predicate_width.GetValue() )
system_predicates[ 'num_words' ] = ( self._file_system_predicate_num_words_sign.GetSelection(), self._file_system_predicate_num_words.GetValue() )
self._options[ 'file_system_predicates' ] = system_predicates
shortcuts = {}
shortcuts[ wx.ACCEL_NORMAL ] = {}
shortcuts[ wx.ACCEL_CTRL ] = {}
shortcuts[ wx.ACCEL_ALT ] = {}
shortcuts[ wx.ACCEL_SHIFT ] = {}
for ( modifier, key, action ) in self._shortcuts.GetClientData(): shortcuts[ modifier ][ key ] = action
self._options[ 'shortcuts' ] = shortcuts
self._options[ 'default_tag_repository' ] = self._default_tag_repository.GetClientData( self._default_tag_repository.GetSelection() )
self._options[ 'default_tag_sort' ] = self._default_tag_sort.GetClientData( self._default_tag_sort.GetSelection() )
try: wx.GetApp().Write( 'save_options' )
except: wx.MessageBox( traceback.format_exc() )
self.EndModal( wx.ID_OK )
def EventRemoveSortBy( self, event ):
selection = self._sort_by.GetSelection()
if selection != wx.NOT_FOUND: self._sort_by.Delete( selection )
def EventPreviewsUpdate( self, event ):
estimated_bytes_per_preview = 3 * 400 * 400
self._estimated_number_previews.SetLabel( '(about ' + HC.ConvertIntToPrettyString( ( self._preview_cache_size.GetValue() * 1048576 ) / estimated_bytes_per_preview ) + ' previews)' )
def EventShortcutsAdd( self, event ):
with DialogInputShortcut( self ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
( modifier, key, action ) = dlg.GetInfo()
( pretty_modifier, pretty_key, pretty_action ) = HC.ConvertShortcutToPrettyShortcut( modifier, key, action )
self._shortcuts.Append( ( pretty_modifier, pretty_key, pretty_action ), ( modifier, key, action ) )
self._SortListCtrl()
def EventShortcutsDelete( self, event ): self._shortcuts.RemoveAllSelected()
def EventShortcutsEdit( self, event ):
indices = self._shortcuts.GetAllSelected()
for index in indices:
( modifier, key, action ) = self._shortcuts.GetClientData( index )
try:
with DialogInputShortcut( self, modifier, key, action ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
( modifier, key, action ) = dlg.GetInfo()
( pretty_modifier, pretty_key, pretty_action ) = HC.ConvertShortcutToPrettyShortcut( modifier, key, action )
self._shortcuts.UpdateRow( index, ( pretty_modifier, pretty_key, pretty_action ), ( modifier, key, action ) )
self._SortListCtrl()
except Exception as e: wx.MessageBox( unicode( e ) )
def EventThumbnailsUpdate( self, event ):
estimated_bytes_per_thumb = 3 * self._thumbnail_height.GetValue() * self._thumbnail_width.GetValue()
self._estimated_number_thumbnails.SetLabel( '(about ' + HC.ConvertIntToPrettyString( ( self._thumbnail_cache_size.GetValue() * 1048576 ) / estimated_bytes_per_thumb ) + ' thumbnails)' )
class DialogManageOptionsServerAdmin( Dialog ):
def __init__( self, parent, service_identifier ):
def InitialiseControls():
self._server_panel = ClientGUICommon.StaticBox( self, 'server' )
self._max_monthly_data = ClientGUICommon.NoneableSpinCtrl( self._server_panel, 'max monthly data (MB)', options[ 'max_monthly_data' ], multiplier = 1048576 )
self._max_storage = ClientGUICommon.NoneableSpinCtrl( self._server_panel, 'max storage (MB)', options[ 'max_monthly_data' ], multiplier = 1048576 )
self._message = wx.TextCtrl( self._server_panel, value = options[ 'message' ] )
self._save_button = wx.Button( self, label='Save' )
self._save_button.Bind( wx.EVT_BUTTON, self.EventOK )
self._save_button.SetForegroundColour( ( 0, 128, 0 ) )
self._close_button = wx.Button( self, id = wx.ID_CANCEL, label='Cancel' )
self._close_button.Bind( wx.EVT_BUTTON, self.EventCancel )
self._close_button.SetForegroundColour( ( 128, 0, 0 ) )
def InitialisePanel():
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 1, 1 )
gridbox.AddF( wx.StaticText( self._server_panel, label='Message' ), FLAGS_MIXED )
gridbox.AddF( self._message, FLAGS_MIXED )
self._server_panel.AddF( self._max_monthly_data, FLAGS_EXPAND_PERPENDICULAR )
self._server_panel.AddF( self._max_storage, FLAGS_EXPAND_PERPENDICULAR )
self._server_panel.AddF( gridbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
buttons = wx.BoxSizer( wx.HORIZONTAL )
buttons.AddF( self._save_button, FLAGS_SMALL_INDENT )
buttons.AddF( self._close_button, FLAGS_SMALL_INDENT )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._server_panel, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( buttons, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x + 80, y ) )
Dialog.__init__( self, parent, service_identifier.GetName() + ' options' )
self._service_identifier = service_identifier
self._service = wx.GetApp().Read( 'service', service_identifier )
connection = self._service.GetConnection()
options = connection.Get( 'options' )
InitialiseControls()
InitialisePanel()
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventOK( self, event ):
options = {}
options[ 'max_monthly_data' ] = self._max_monthly_data.GetValue()
options[ 'max_storage' ] = self._max_storage.GetValue()
options[ 'message' ] = self._message.GetValue()
try:
connection = self._service.GetConnection()
connection.Post( 'options', options = options )
except Exception as e: wx.MessageBox( 'Something went wrong when trying to send the options to the server admin: ' + unicode( e ) )
self.EndModal( wx.ID_OK )
class DialogManageOptionsTagRepository( Dialog ):
def __init__( self, parent, service_identifier ):
def InitialiseControls():
self._tag_repository_panel = ClientGUICommon.StaticBox( self, 'tag repository' )
self._max_monthly_data = ClientGUICommon.NoneableSpinCtrl( self._tag_repository_panel, 'max monthly data (MB)', options[ 'max_monthly_data' ], multiplier = 1048576 )
self._message = wx.TextCtrl( self._tag_repository_panel, value = options[ 'message' ] )
self._save_button = wx.Button( self, label='Save' )
self._save_button.Bind( wx.EVT_BUTTON, self.EventOK )
self._save_button.SetForegroundColour( ( 0, 128, 0 ) )
self._close_button = wx.Button( self, id = wx.ID_CANCEL, label='Cancel' )
self._close_button.Bind( wx.EVT_BUTTON, self.EventCancel )
self._close_button.SetForegroundColour( ( 128, 0, 0 ) )
def InitialisePanel():
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 1, 1 )
gridbox.AddF( wx.StaticText( self._tag_repository_panel, label='Message' ), FLAGS_MIXED )
gridbox.AddF( self._message, FLAGS_MIXED )
self._tag_repository_panel.AddF( self._max_monthly_data, FLAGS_EXPAND_PERPENDICULAR )
self._tag_repository_panel.AddF( gridbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
buttons = wx.BoxSizer( wx.HORIZONTAL )
buttons.AddF( self._save_button, FLAGS_SMALL_INDENT )
buttons.AddF( self._close_button, FLAGS_SMALL_INDENT )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._tag_repository_panel, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( buttons, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x + 80, y ) )
Dialog.__init__( self, parent, service_identifier.GetName() + ' options' )
self._service_identifier = service_identifier
self._service = wx.GetApp().Read( 'service', service_identifier )
connection = self._service.GetConnection()
options = connection.Get( 'options' )
InitialiseControls()
InitialisePanel()
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventOK( self, event ):
options = {}
options[ 'max_monthly_data' ] = self._max_monthly_data.GetValue()
options[ 'message' ] = self._message.GetValue()
try:
connection = self._service.GetConnection()
connection.Post( 'options', options = options )
except Exception as e: wx.MessageBox( 'Something went wrong when trying to send the options to the tag repository: ' + unicode( e ) )
self.EndModal( wx.ID_OK )
class DialogManagePixivAccount( Dialog ):
def __init__( self, parent ):
def InitialiseControls():
self._id = wx.TextCtrl( self, value = id )
self._password = wx.TextCtrl( self, value = password )
self._status = wx.StaticText( self )
self._test = wx.Button( self, label = 'test' )
self._test.Bind( wx.EVT_BUTTON, self.EventTest )
self._ok = wx.Button( self, label='Ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label='Cancel' )
self._cancel.Bind( wx.EVT_BUTTON, self.EventCancel )
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
def InitialisePanel():
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 1, 1 )
gridbox.AddF( wx.StaticText( self, label='id/email' ), FLAGS_MIXED )
gridbox.AddF( self._id, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( self, label='password' ), FLAGS_MIXED )
gridbox.AddF( self._password, FLAGS_EXPAND_BOTH_WAYS )
b_box = wx.BoxSizer( wx.HORIZONTAL )
b_box.AddF( self._ok, FLAGS_MIXED )
b_box.AddF( self._cancel, FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( gridbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( self._status, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._test, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( b_box, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
x = max( x, 240 )
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'manage pixiv account' )
( id, password ) = wx.GetApp().Read( 'pixiv_account' )
InitialiseControls()
InitialisePanel()
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventOk( self, event ):
id = self._id.GetValue()
password = self._password.GetValue()
wx.GetApp().Write( 'pixiv_account', id, password )
self.EndModal( wx.ID_OK )
def EventTest( self, event ):
try:
id = self._id.GetValue()
password = self._password.GetValue()
form_fields = {}
form_fields[ 'mode' ] = 'login'
form_fields[ 'pixiv_id' ] = id
form_fields[ 'pass' ] = password
body = urllib.urlencode( form_fields )
headers = {}
headers[ 'Content-Type' ] = 'application/x-www-form-urlencoded'
connection = HC.AdvancedHTTPConnection( url = 'http://www.pixiv.net/', accept_cookies = True )
response = connection.request( 'POST', '/login.php', headers = headers, body = body, follow_redirects = False )
cookies = connection.GetCookies()
# _ only given to logged in php sessions
if 'PHPSESSID' in cookies and '_' in cookies[ 'PHPSESSID' ]: self._status.SetLabel( 'OK!' )
else: self._status.SetLabel( 'Did not work!' )
wx.CallLater( 2000, self._status.SetLabel, '' )
except Exception as e:
wx.MessageBox( traceback.format_exc() )
wx.MessageBox( unicode( e ) )
class DialogManageRatings( Dialog ):
def __init__( self, parent, media ):
def InitialiseControls():
service_identifiers = wx.GetApp().Read( 'service_identifiers', HC.RATINGS_SERVICES )
# sort according to local/remote, I guess
# and maybe sub-sort according to name?
# maybe just do two get s_i queries
self._panels = []
for service_identifier in service_identifiers: self._panels.append( self._Panel( self, service_identifier, media ) )
self._apply = wx.Button( self, label='Apply' )
self._apply.Bind( wx.EVT_BUTTON, self.EventOk )
self._apply.SetForegroundColour( ( 0, 128, 0 ) )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label='Cancel' )
self._cancel.Bind( wx.EVT_BUTTON, self.EventCancel )
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
def InitialisePanel():
buttonbox = wx.BoxSizer( wx.HORIZONTAL )
buttonbox.AddF( self._apply, FLAGS_MIXED )
buttonbox.AddF( self._cancel, FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
for panel in self._panels: vbox.AddF( panel, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( buttonbox, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x + 200, y ) )
self._hashes = HC.IntelligentMassUnion( ( m.GetHashes() for m in media ) )
Dialog.__init__( self, parent, 'manage ratings for ' + HC.ConvertIntToPrettyString( len( self._hashes ) ) + ' files' )
InitialiseControls()
InitialisePanel()
self.Bind( wx.EVT_MENU, self.EventMenu )
self.RefreshAcceleratorTable()
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventMenu( self, event ):
action = CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
if action is not None:
( command, data ) = action
if command == 'manage_ratings': self.EventCancel( event )
elif command == 'ok': self.EventOk( event )
else: event.Skip()
def EventOk( self, event ):
try:
content_updates = []
for panel in self._panels:
if panel.HasChanges():
service_identifier = panel.GetServiceIdentifier()
rating = panel.GetRating()
content_updates.append( HC.ContentUpdate( HC.CONTENT_UPDATE_RATING, service_identifier, self._hashes, info = rating ) )
if len( content_updates ) > 0: wx.GetApp().Write( 'content_updates', content_updates )
except Exception as e: wx.MessageBox( 'Saving pending mapping changes to DB raised this error: ' + unicode( e ) )
self.EndModal( wx.ID_OK )
def RefreshAcceleratorTable( self ):
interested_actions = [ 'manage_ratings' ]
entries = []
for ( modifier, key_dict ) in self._options[ 'shortcuts' ].items(): entries.extend( [ ( modifier, key, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( action ) ) for ( key, action ) in key_dict.items() if action in interested_actions ] )
self.SetAcceleratorTable( wx.AcceleratorTable( entries ) )
class _Panel( wx.Panel ):
def __init__( self, parent, service_identifier, media ):
wx.Panel.__init__( self, parent )
self._service_identifier = service_identifier
self._service = wx.GetApp().Read( 'service', service_identifier )
extra_info = self._service.GetExtraInfo()
self._media = media
service_type = service_identifier.GetType()
def InitialiseControls():
self._ratings_panel = ClientGUICommon.StaticBox( self, self._service_identifier.GetName() )
self._current_score = wx.StaticText( self._ratings_panel, style = wx.ALIGN_CENTER )
score_font = self._GetScoreFont()
self._current_score.SetFont( score_font )
if service_type in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ): all_rating_services = [ local_ratings for ( local_ratings, remote_ratings ) in [ media.GetRatings() for media in self._media ] ]
elif service_type in ( HC.RATING_LIKE_REPOSITORY, HC.RATING_NUMERICAL_REPOSITORY ): all_rating_services = [ remote_ratings for ( local_ratings, remote_ratings ) in [ media.GetRatings() for media in self._media ] ]
if service_type in ( HC.LOCAL_RATING_LIKE, HC.RATING_LIKE_REPOSITORY ):
( like, dislike ) = extra_info
if service_type == HC.LOCAL_RATING_LIKE:
ratings = [ rating_services.GetRating( self._service_identifier ) for rating_services in all_rating_services ]
if all( ( i is None for i in ratings ) ):
choices = [ like, dislike, 'make no changes' ]
if len( self._media ) > 1: self._current_score.SetLabel( 'none rated' )
else: self._current_score.SetLabel( 'not rated' )
elif None in ratings:
choices = [ like, dislike, 'remove rating', 'make no changes' ]
self._current_score.SetLabel( 'not all rated' )
else:
if all( ( i == 1 for i in ratings ) ):
choices = [ dislike, 'remove rating', 'make no changes' ]
if len( self._media ) > 1: self._current_score.SetLabel( 'all ' + like )
else: self._current_score.SetLabel( like )
elif all( ( i == 0 for i in ratings ) ):
choices = [ like, 'remove rating', 'make no changes' ]
if len( self._media ) > 1: self._current_score.SetLabel( 'all ' + dislike )
else: self._current_score.SetLabel( dislike )
else:
choices = [ like, dislike, 'remove rating', 'make no changes' ]
overall_rating = float( sum( ratings ) ) / float( len( ratings ) )
self._current_score.SetLabel( str( '%.2f' % overall_rating ) )
if len( self._media ) > 1:
ratings_counter = collections.Counter( ratings )
likes = ratings_counter[ 1 ]
dislikes = ratings_counter[ 0 ]
nones = ratings_counter[ None ]
scores = []
if likes > 0: scores.append( str( likes ) + ' likes' )
if dislikes > 0: scores.append( str( dislikes ) + ' dislikes' )
if nones > 0: scores.append( str( nones ) + ' not rated' )
self._current_score.SetLabel( ', '.join( scores ) )
else:
( rating, ) = ratings
if rating is None: self._current_score.SetLabel( 'not rated' )
elif rating == 1: self._current_score.SetLabel( like )
elif rating == 0: self._current_score.SetLabel( dislike )
else:
self._current_score.SetLabel( '23 ' + like + 's, 44 ' + dislike + 's' )
elif service_type in ( HC.LOCAL_RATING_NUMERICAL, HC.RATING_NUMERICAL_REPOSITORY ):
if service_type == HC.LOCAL_RATING_NUMERICAL:
( min, max ) = extra_info
self._slider = wx.Slider( self._ratings_panel, minValue = min, maxValue = max, style = wx.SL_AUTOTICKS | wx.SL_LABELS )
self._slider.Bind( wx.EVT_SLIDER, self.EventSlider )
ratings = [ rating_services.GetRating( self._service_identifier ) for rating_services in all_rating_services ]
if all( ( i is None for i in ratings ) ):
choices = [ 'set rating', 'make no changes' ]
if len( self._media ) > 1: self._current_score.SetLabel( 'none rated' )
else: self._current_score.SetLabel( 'not rated' )
elif None in ratings:
choices = [ 'set rating', 'remove rating', 'make no changes' ]
if len( self._media ) > 1: self._current_score.SetLabel( 'not all rated' )
else: self._current_score.SetLabel( 'not rated' )
else:
# you know what? this should really be a bargraph or something!
# *
# *
# *
# * *
# * * * *
# None 0 1 2 3 4 5
# but we can't rely on integers, so just think about it
# some kind of sense of distribution would be helpful though
choices = [ 'set rating', 'remove rating', 'make no changes' ]
overall_rating = float( sum( ratings ) ) / float( len( ratings ) )
overall_rating_converted = ( overall_rating * ( max - min ) ) + min
self._slider.SetValue( int( overall_rating_converted + 0.5 ) )
str_overall_rating = str( '%.2f' % overall_rating_converted )
if min in ( 0, 1 ): str_overall_rating += '/' + str( '%.2f' % max )
self._current_score.SetLabel( str_overall_rating )
else:
self._current_score.SetLabel( '3.82/5' )
if service_type in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ):
initial_index = choices.index( 'make no changes' )
choice_pairs = [ ( choice, choice ) for choice in choices ]
self._choices = ClientGUICommon.RadioBox( self._ratings_panel, 'actions', choice_pairs, initial_index )
def InitialisePanel():
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
if service_type in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ): label = 'local rating'
elif service_type in ( HC.RATING_LIKE_REPOSITORY, HC.RATING_NUMERICAL_REPOSITORY ): label = 'remote rating'
self._ratings_panel.AddF( self._current_score, FLAGS_EXPAND_PERPENDICULAR )
if service_type in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ):
if service_type == HC.LOCAL_RATING_LIKE:
self._ratings_panel.AddF( self._choices, FLAGS_EXPAND_PERPENDICULAR )
elif service_type == HC.LOCAL_RATING_NUMERICAL:
self._ratings_panel.AddF( self._slider, FLAGS_EXPAND_PERPENDICULAR )
self._ratings_panel.AddF( self._choices, FLAGS_EXPAND_PERPENDICULAR )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._ratings_panel, FLAGS_EXPAND_PERPENDICULAR )
self.SetSizer( vbox )
InitialiseControls()
InitialisePanel()
def _GetScoreFont( self ):
normal_font = wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT )
normal_font_size = normal_font.GetPointSize()
normal_font_family = normal_font.GetFamily()
return wx.Font( normal_font_size * 2, normal_font_family, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD )
def EventSlider( self, event ):
rating = self._slider.GetValue()
self._choices.SetSelection( 0 )
self._choices.SetString( 0, 'set rating to ' + str( rating ) )
event.Skip()
def GetRating( self ):
service_type = self._service_identifier.GetType()
choice_text = self._choices.GetSelectedClientData()
if choice_text == 'remove rating': return None
else:
if service_type == HC.LOCAL_RATING_LIKE:
( like, dislike ) = self._service.GetExtraInfo()
if choice_text == like: rating = 1
elif choice_text == dislike: rating = 0
elif service_type == HC.LOCAL_RATING_NUMERICAL: rating = float( self._slider.GetValue() - self._slider.GetMin() ) / float( self._slider.GetMax() - self._slider.GetMin() )
return rating
def HasChanges( self ):
choice_text = self._choices.GetSelectedClientData()
if choice_text == 'make no changes': return False
else: return True
def GetServiceIdentifier( self ): return self._service_identifier
class DialogManageServer( Dialog ):
def __init__( self, parent, service_identifier ):
def InitialiseControls():
self._edit_log = []
self._services_listbook = ClientGUICommon.ListBook( self )
self._services_listbook.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGED, self.EventServiceChanged )
self._services_listbook.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventServiceChanging )
self._service_types = wx.Choice( self )
for service_type in [ HC.TAG_REPOSITORY, HC.FILE_REPOSITORY, HC.MESSAGE_DEPOT ]: self._service_types.Append( HC.service_string_lookup[ service_type ], service_type )
self._service_types.SetSelection( 0 )
self._add = wx.Button( self, label='add' )
self._add.Bind( wx.EVT_BUTTON, self.EventAdd )
self._add.SetForegroundColour( ( 0, 128, 0 ) )
self._remove = wx.Button( self, label='remove' )
self._remove.Bind( wx.EVT_BUTTON, self.EventRemove )
self._remove.SetForegroundColour( ( 128, 0, 0 ) )
self._ok = wx.Button( self, label='ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label='cancel' )
self._cancel.Bind( wx.EVT_BUTTON, self.EventCancel )
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
# goes after self._remove, because of events
for service_identifier in self._service_identifiers:
page = self._Panel( self._services_listbook, service_identifier )
name = HC.service_string_lookup[ service_identifier.GetType() ]
self._services_listbook.AddPage( page, name )
def InitialisePanel():
add_remove_hbox = wx.BoxSizer( wx.HORIZONTAL )
add_remove_hbox.AddF( self._service_types, FLAGS_MIXED )
add_remove_hbox.AddF( self._add, FLAGS_MIXED )
add_remove_hbox.AddF( self._remove, FLAGS_MIXED )
ok_hbox = wx.BoxSizer( wx.HORIZONTAL )
ok_hbox.AddF( self._ok, FLAGS_MIXED )
ok_hbox.AddF( self._cancel, FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._services_listbook, FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( add_remove_hbox, FLAGS_SMALL_INDENT )
vbox.AddF( ok_hbox, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
if y < 400: y = 400 # listbook's setsize ( -1, 400 ) is buggy
self.SetInitialSize( ( 680, y ) )
Dialog.__init__( self, parent, 'manage ' + service_identifier.GetName() + ' services' )
self._service = wx.GetApp().Read( 'service', service_identifier )
connection = self._service.GetConnection()
self._service_identifiers = connection.Get( 'services' )
InitialiseControls()
InitialisePanel()
current_page = self._services_listbook.GetCurrentPage()
if current_page.GetOriginalServiceIdentifier().GetType() == HC.SERVER_ADMIN: self._remove.Disable()
else: self._remove.Enable()
def _CheckCurrentServiceIsValid( self ):
service_panel = self._services_listbook.GetCurrentPage()
if service_panel is not None:
port = service_panel.GetInfo()
for existing_port in [ page.GetInfo() for page in self._services_listbook.GetNameToPageDict().values() if page != service_panel ]:
if port == existing_port: raise Exception( 'That port is already in use!' )
def EventAdd( self, event ):
try:
self._CheckCurrentServiceIsValid()
service_type = self._service_types.GetClientData( self._service_types.GetSelection() )
existing_ports = [ page.GetInfo() for page in self._services_listbook.GetNameToPageDict().values() ]
port = HC.DEFAULT_SERVICE_PORT
while port in existing_ports: port += 1
service_identifier = HC.ServerServiceIdentifier( service_type, port )
self._edit_log.append( ( HC.ADD, service_identifier ) )
page = self._Panel( self._services_listbook, service_identifier )
name = HC.service_string_lookup[ service_type ]
self._services_listbook.AddPage( page, name, select = True )
except Exception as e: wx.MessageBox( unicode( e ) )
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventOk( self, event ):
try: self._CheckCurrentServiceIsValid()
except Exception as e:
wx.MessageBox( unicode( e ) )
return
for page in self._services_listbook.GetNameToPageDict().values():
if page.HasChanges(): self._edit_log.append( ( HC.EDIT, ( page.GetOriginalServiceIdentifier(), page.GetInfo() ) ) )
try:
if len( self._edit_log ) > 0:
connection = self._service.GetConnection()
connection.Post( 'services_modification', edit_log = self._edit_log )
wx.GetApp().Write( 'update_server_services', self._service.GetServiceIdentifier(), self._edit_log )
except Exception as e: wx.MessageBox( 'Saving services to server raised this error: ' + unicode( e ) )
self.EndModal( wx.ID_OK )
def EventRemove( self, event ):
service_panel = self._services_listbook.GetCurrentPage()
if service_panel is not None:
service_identifier = service_panel.GetOriginalServiceIdentifier()
self._edit_log.append( ( HC.DELETE, service_identifier ) )
self._services_listbook.DeleteCurrentPage()
def EventServiceChanged( self, event ):
page = self._services_listbook.GetCurrentPage()
if page.GetOriginalServiceIdentifier().GetType() == HC.SERVER_ADMIN: self._remove.Disable()
else: self._remove.Enable()
def EventServiceChanging( self, event ):
try: self._CheckCurrentServiceIsValid()
except Exception as e:
wx.MessageBox( unicode( e ) )
event.Veto()
class _Panel( wx.Panel ):
def __init__( self, parent, service_identifier ):
wx.Panel.__init__( self, parent )
self._service_identifier = service_identifier
service_type = service_identifier.GetType()
def InitialiseControls():
self._service_panel = ClientGUICommon.StaticBox( self, 'service' )
self._service_port = wx.SpinCtrl( self._service_panel, min = 1, max = 65535 )
self._service_port.SetValue( service_identifier.GetPort() )
def InitialisePanel():
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
vbox = wx.BoxSizer( wx.VERTICAL )
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 1, 1 )
gridbox.AddF( wx.StaticText( self._service_panel, label='port' ), FLAGS_MIXED )
gridbox.AddF( self._service_port, FLAGS_EXPAND_BOTH_WAYS )
self._service_panel.AddF( gridbox, FLAGS_EXPAND_SIZER_BOTH_WAYS )
vbox.AddF( self._service_panel, FLAGS_EXPAND_BOTH_WAYS )
self.SetSizer( vbox )
InitialiseControls()
InitialisePanel()
def GetInfo( self ):
port = self._service_port.GetValue()
return port
def HasChanges( self ):
port = self.GetInfo()
if port != self._service_identifier.GetPort(): return True
return False
def GetOriginalServiceIdentifier( self ): return self._service_identifier
class DialogManageServices( Dialog ):
def __init__( self, parent ):
def InitialiseControls():
self._edit_log = []
self._listbook = ClientGUICommon.ListBook( self )
self._listbook.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventServiceChanging )
self._local_ratings_like = ClientGUICommon.ListBook( self._listbook )
self._local_ratings_like.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventServiceChanging )
self._local_ratings_numerical = ClientGUICommon.ListBook( self._listbook )
self._local_ratings_numerical.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventServiceChanging )
self._tag_repositories = ClientGUICommon.ListBook( self._listbook )
self._tag_repositories.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventServiceChanging )
self._file_repositories = ClientGUICommon.ListBook( self._listbook )
self._file_repositories.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventServiceChanging )
self._message_depots = ClientGUICommon.ListBook( self._listbook )
self._message_depots.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventServiceChanging )
self._servers_admin = ClientGUICommon.ListBook( self._listbook )
self._servers_admin.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventServiceChanging )
services = wx.GetApp().Read( 'services', HC.RESTRICTED_SERVICES + [ HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ] )
for service in services:
service_identifier = service.GetServiceIdentifier()
service_type = service_identifier.GetType()
name = service_identifier.GetName()
if service_type in HC.REMOTE_SERVICES: credentials = service.GetCredentials()
else: credentials = None
extra_info = service.GetExtraInfo()
if service_type == HC.LOCAL_RATING_LIKE: listbook = self._local_ratings_like
elif service_type == HC.LOCAL_RATING_NUMERICAL: listbook = self._local_ratings_numerical
elif service_type == HC.TAG_REPOSITORY: listbook = self._tag_repositories
elif service_type == HC.FILE_REPOSITORY: listbook = self._file_repositories
elif service_type == HC.MESSAGE_DEPOT: listbook = self._message_depots
elif service_type == HC.SERVER_ADMIN: listbook = self._servers_admin
else: continue
page_info = ( self._Panel, ( listbook, service_identifier, credentials, extra_info ), {} )
listbook.AddPage( page_info, name )
self._listbook.AddPage( self._local_ratings_like, 'local ratings like' )
self._listbook.AddPage( self._local_ratings_numerical, 'local ratings numerical' )
self._listbook.AddPage( self._tag_repositories, 'tags' )
self._listbook.AddPage( self._file_repositories, 'files' )
self._listbook.AddPage( self._message_depots, 'message depots' )
self._listbook.AddPage( self._servers_admin, 'servers admin' )
self._add = wx.Button( self, label='add' )
self._add.Bind( wx.EVT_BUTTON, self.EventAdd )
self._add.SetForegroundColour( ( 0, 128, 0 ) )
self._remove = wx.Button( self, label='remove' )
self._remove.Bind( wx.EVT_BUTTON, self.EventRemove )
self._remove.SetForegroundColour( ( 128, 0, 0 ) )
self._export = wx.Button( self, label='export' )
self._export.Bind( wx.EVT_BUTTON, self.EventExport )
self._ok = wx.Button( self, label='ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label='cancel' )
self._cancel.Bind( wx.EVT_BUTTON, self.EventCancel )
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
# these need to be below the addpages because they'd fire the events
self._listbook.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventPageChanging, source = self._listbook )
def InitialisePanel():
add_remove_hbox = wx.BoxSizer( wx.HORIZONTAL )
add_remove_hbox.AddF( self._add, FLAGS_MIXED )
add_remove_hbox.AddF( self._remove, FLAGS_MIXED )
add_remove_hbox.AddF( self._export, FLAGS_MIXED )
ok_hbox = wx.BoxSizer( wx.HORIZONTAL )
ok_hbox.AddF( self._ok, FLAGS_MIXED )
ok_hbox.AddF( self._cancel, FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._listbook, FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( add_remove_hbox, FLAGS_SMALL_INDENT )
vbox.AddF( ok_hbox, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( 880, y + 220 ) )
Dialog.__init__( self, parent, 'manage services' )
InitialiseControls()
InitialisePanel()
self.SetDropTarget( ClientGUICommon.FileDropTarget( self.Import ) )
def _CheckCurrentServiceIsValid( self ):
services_listbook = self._listbook.GetCurrentPage()
if services_listbook is not None:
service_panel = services_listbook.GetCurrentPage()
if service_panel is not None:
( service_identifier, credentials, extra_info ) = service_panel.GetInfo()
old_name = services_listbook.GetCurrentName()
name = service_identifier.GetName()
if old_name is not None and name != old_name:
if services_listbook.NameExists( name ): raise Exception( 'That name is already in use!' )
services_listbook.RenamePage( old_name, name )
def EventAdd( self, event ):
with wx.TextEntryDialog( self, 'Enter new service\'s name' ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
try:
name = dlg.GetValue()
services_listbook = self._listbook.GetCurrentPage()
if services_listbook.NameExists( name ): raise Exception( 'That name is already in use!' )
if name == '': raise Exception( 'Please enter a nickname for the service.' )
if services_listbook == self._local_ratings_like: service_type = HC.LOCAL_RATING_LIKE
elif services_listbook == self._local_ratings_numerical: service_type = HC.LOCAL_RATING_NUMERICAL
elif services_listbook == self._tag_repositories: service_type = HC.TAG_REPOSITORY
elif services_listbook == self._file_repositories: service_type = HC.FILE_REPOSITORY
elif services_listbook == self._message_depots: service_type = HC.MESSAGE_DEPOT
elif services_listbook == self._servers_admin: service_type = HC.SERVER_ADMIN
service_identifier = HC.ClientServiceIdentifier( os.urandom( 32 ), service_type, name )
if service_type in HC.REMOTE_SERVICES:
if service_type == HC.SERVER_ADMIN: credentials = CC.Credentials( 'hostname', 45870, '' )
elif service_type in HC.RESTRICTED_SERVICES:
with DialogChooseNewServiceMethod( self ) as dlg:
if dlg.ShowModal() != wx.ID_OK: return
register = dlg.GetRegister()
if register:
with DialogRegisterService( self ) as dlg:
if dlg.ShowModal() != wx.ID_OK: return
credentials = dlg.GetCredentials()
else: credentials = CC.Credentials( 'hostname', 45871, '' )
else: credentials = CC.Credentials( 'hostname', 45871 )
else: credentials = None
if service_type == HC.MESSAGE_DEPOT:
identity_name = 'identity@' + name
check_period = 180
private_key = HydrusMessageHandling.GenerateNewPrivateKey()
receive_anon = True
extra_info = ( identity_name, check_period, private_key, receive_anon )
elif service_type == HC.LOCAL_RATING_LIKE: extra_info = ( 'like', 'dislike' )
elif service_type == HC.LOCAL_RATING_NUMERICAL: extra_info = ( 0, 5 )
else: extra_info = None
self._edit_log.append( ( 'add', ( service_identifier, credentials, extra_info ) ) )
page = self._Panel( services_listbook, service_identifier, credentials, extra_info )
services_listbook.AddPage( page, name, select = True )
except Exception as e:
wx.MessageBox( unicode( e ) )
self.EventAdd( event )
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventExport( self, event ):
try: self._CheckCurrentServiceIsValid()
except Exception as e:
wx.MessageBox( unicode( e ) )
return
services_listbook = self._listbook.GetCurrentPage()
if services_listbook is not None:
service_panel = services_listbook.GetCurrentPage()
( service_identifier, credentials, extra_info ) = service_panel.GetInfo()
name = service_identifier.GetName()
try:
with wx.FileDialog( self, 'select where to export service', defaultFile = name + '.yaml', style = wx.FD_SAVE ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
with open( dlg.GetPath(), 'wb' ) as f: f.write( yaml.safe_dump( ( service_identifier, credentials, extra_info ) ) )
except:
with wx.FileDialog( self, 'select where to export service', defaultFile = 'service.yaml', style = wx.FD_SAVE ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
with open( dlg.GetPath(), 'wb' ) as f: f.write( yaml.safe_dump( ( service_identifier, credentials, extra_info ) ) )
def EventOk( self, event ):
try: self._CheckCurrentServiceIsValid()
except Exception as e:
wx.MessageBox( unicode( e ) )
return
all_pages = []
all_pages.extend( self._local_ratings_like.GetNameToPageDict().values() )
all_pages.extend( self._local_ratings_numerical.GetNameToPageDict().values() )
all_pages.extend( self._tag_repositories.GetNameToPageDict().values() )
all_pages.extend( self._file_repositories.GetNameToPageDict().values() )
all_pages.extend( self._message_depots.GetNameToPageDict().values() )
all_pages.extend( self._servers_admin.GetNameToPageDict().values() )
for page in all_pages:
if page.HasChanges(): self._edit_log.append( ( 'edit', ( page.GetOriginalServiceIdentifier(), page.GetInfo() ) ) )
try:
if len( self._edit_log ) > 0: wx.GetApp().Write( 'update_services', self._edit_log )
except Exception as e: wx.MessageBox( 'Saving services to DB raised this error: ' + unicode( e ) )
self.EndModal( wx.ID_OK )
def EventPageChanging( self, event ):
try: self._CheckCurrentServiceIsValid()
except Exception as e:
wx.MessageBox( unicode( e ) )
event.Veto()
def EventRemove( self, event ):
services_listbook = self._listbook.GetCurrentPage()
service_panel = services_listbook.GetCurrentPage()
if service_panel is not None:
service_identifier = service_panel.GetOriginalServiceIdentifier()
self._edit_log.append( ( 'delete', service_identifier ) )
services_listbook.DeleteCurrentPage()
def EventServiceChanging( self, event ):
try: self._CheckCurrentServiceIsValid()
except Exception as e:
wx.MessageBox( unicode( e ) )
event.Veto()
def Import( self, paths ):
try: self._CheckCurrentSubscriptionIsValid()
except Exception as e:
wx.MessageBox( unicode( e ) )
return
for path in paths:
try:
with open( path, 'rb' ) as f: file = f.read()
( service_identifier, credentials, extra_info ) = yaml.safe_load( file )
name = service_identifier.GetName()
service_type = service_identifier.GetType()
if service_type == HC.TAG_REPOSITORY: services_listbook = self._tag_repositories
elif service_type == HC.FILE_REPOSITORY: services_listbook = self._file_repositories
elif service_type == HC.MESSAGE_DEPOT: services_listbook = self._message_depots
elif service_type == HC.SERVER_ADMIN: services_listbook = self._servers_admin
self._listbook.SelectPage( services_listbook )
if services_listbook.NameExists( name ):
message = 'A service already exists with that name. Overwrite it?'
with DialogYesNo( self, message ) as dlg:
if dlg.ShowModal() == wx.ID_YES:
page = services_listbook.GetNameToPageDict()[ name ]
page.Update( service_identifier, credentials, extra_info )
else:
self._edit_log.append( ( 'add', ( service_identifier, credentials, extra_info ) ) )
page = self._Panel( services_listbook, service_identifier, credentials, extra_info )
services_listbook.AddPage( page, name, select = True )
except:
wx.MessageBox( traceback.format_exc() )
class _Panel( wx.Panel ):
def __init__( self, parent, service_identifier, credentials, extra_info ):
wx.Panel.__init__( self, parent )
self._service_identifier = service_identifier
self._credentials = credentials
self._extra_info = extra_info
service_type = service_identifier.GetType()
def InitialiseControls():
self._service_panel = ClientGUICommon.StaticBox( self, 'service' )
self._service_name = wx.TextCtrl( self._service_panel, value = self._service_identifier.GetName() )
if service_type in HC.REMOTE_SERVICES: self._service_credentials = wx.TextCtrl( self._service_panel, value = self._credentials.GetConnectionString() )
if service_type == HC.MESSAGE_DEPOT:
( identity_name, check_period, private_key, receive_anon ) = self._extra_info
self._identity_name = wx.TextCtrl( self._service_panel, value = identity_name )
self._check_period = wx.SpinCtrl( self._service_panel, min = 60, max = 86400 * 7 )
self._check_period.SetValue( check_period )
self._private_key = wx.TextCtrl( self._service_panel, value = private_key, style = wx.TE_MULTILINE )
self._receive_anon = wx.CheckBox( self._service_panel )
self._receive_anon.SetValue( receive_anon )
elif service_identifier.GetType() == HC.LOCAL_RATING_LIKE:
( like, dislike ) = self._extra_info
self._like = wx.TextCtrl( self._service_panel, value = like )
self._dislike = wx.TextCtrl( self._service_panel, value = dislike )
elif service_identifier.GetType() == HC.LOCAL_RATING_NUMERICAL:
( lower, upper ) = self._extra_info
self._lower = wx.SpinCtrl( self._service_panel, min = -2000, max = 2000 )
self._lower.SetValue( lower )
self._upper = wx.SpinCtrl( self._service_panel, min = -2000, max = 2000 )
self._upper.SetValue( upper )
def InitialisePanel():
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
vbox = wx.BoxSizer( wx.VERTICAL )
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 1, 1 )
gridbox.AddF( wx.StaticText( self._service_panel, label='name' ), FLAGS_MIXED )
gridbox.AddF( self._service_name, FLAGS_EXPAND_BOTH_WAYS )
if service_type in HC.REMOTE_SERVICES:
gridbox.AddF( wx.StaticText( self._service_panel, label='credentials' ), FLAGS_MIXED )
gridbox.AddF( self._service_credentials, FLAGS_EXPAND_BOTH_WAYS )
if service_type == HC.MESSAGE_DEPOT:
gridbox.AddF( wx.StaticText( self._service_panel, label='identity name' ), FLAGS_MIXED )
gridbox.AddF( self._identity_name, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( self._service_panel, label='update period' ), FLAGS_MIXED )
gridbox.AddF( self._check_period, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( self._service_panel, label='private key' ), FLAGS_MIXED )
gridbox.AddF( self._private_key, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( self._service_panel, label='receive messages from Anonymous?' ), FLAGS_MIXED )
gridbox.AddF( self._receive_anon, FLAGS_EXPAND_BOTH_WAYS )
elif service_identifier.GetType() == HC.LOCAL_RATING_LIKE:
gridbox.AddF( wx.StaticText( self._service_panel, label='like' ), FLAGS_MIXED )
gridbox.AddF( self._like, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( self._service_panel, label='dislike' ), FLAGS_MIXED )
gridbox.AddF( self._dislike, FLAGS_EXPAND_BOTH_WAYS )
elif service_identifier.GetType() == HC.LOCAL_RATING_NUMERICAL:
gridbox.AddF( wx.StaticText( self._service_panel, label='lower limit' ), FLAGS_MIXED )
gridbox.AddF( self._lower, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( self._service_panel, label='upper limit' ), FLAGS_MIXED )
gridbox.AddF( self._upper, FLAGS_EXPAND_BOTH_WAYS )
self._service_panel.AddF( gridbox, FLAGS_EXPAND_SIZER_BOTH_WAYS )
vbox.AddF( self._service_panel, FLAGS_EXPAND_BOTH_WAYS )
self.SetSizer( vbox )
InitialiseControls()
InitialisePanel()
def GetInfo( self ):
service_key = self._service_identifier.GetServiceKey()
service_type = self._service_identifier.GetType()
name = self._service_name.GetValue()
if name == '': raise Exception( 'Please enter a name' )
service_identifier = HC.ClientServiceIdentifier( service_key, service_type, name )
if service_type in HC.REMOTE_SERVICES:
connection_string = self._service_credentials.GetValue()
if connection_string == '': raise Exception( 'Please enter some credentials' )
if '@' in connection_string:
try: ( access_key, address ) = connection_string.split( '@' )
except: raise Exception( 'Could not parse those credentials - no \'@\' symbol!' )
try: access_key = access_key.decode( 'hex' )
except: raise Exception( 'Could not parse those credentials - could not understand access key!' )
try: ( host, port ) = address.split( ':' )
except: raise Exception( 'Could not parse those credentials - no \':\' symbol!' )
try: port = int( port )
except: raise Exception( 'Could not parse those credentials - could not understand the port!' )
credentials = CC.Credentials( host, port, access_key )
else:
try: ( host, port ) = connection_string.split( ':' )
except: raise Exception( 'Could not parse those credentials - no \':\' symbol!' )
try: port = int( port )
except: raise Exception( 'Could not parse those credentials - could not understand the port!' )
credentials = CC.Credentials( host, port )
else: credentials = None
if service_type == HC.MESSAGE_DEPOT: extra_info = ( self._identity_name.GetValue(), self._check_period.GetValue(), self._private_key.GetValue(), self._receive_anon.GetValue() )
elif service_type == HC.LOCAL_RATING_LIKE: extra_info = ( self._like.GetValue(), self._dislike.GetValue() )
elif service_type == HC.LOCAL_RATING_NUMERICAL:
( lower, upper ) = ( self._lower.GetValue(), self._upper.GetValue() )
if upper < lower: upper = lower + 1
extra_info = ( lower, upper )
else: extra_info = None
return ( service_identifier, credentials, extra_info )
def HasChanges( self ):
( service_identifier, credentials, extra_info ) = self.GetInfo()
if service_identifier != self._service_identifier: return True
if credentials != self._credentials: return True
if extra_info != self._extra_info: return True
return False
def GetOriginalServiceIdentifier( self ): return self._service_identifier
def Update( self, service_identifier, credentials, extra_info ):
service_type = service_identifier.GetType()
self._service_name.SetValue( service_identifier.GetName() )
if service_type in HC.REMOTE_SERVICES: self._service_credentials.SetValue( credentials.GetConnectionString() )
if service_type == HC.MESSAGE_DEPOT:
if len( extra_info ) == 3:
( identity_name, check_period, private_key ) = extra_info
receive_anon = True
else: ( identity_name, check_period, private_key, receive_anon ) = extra_info
self._identity_name.SetValue( identity_name )
self._check_period.SetValue( check_period )
self._private_key.SetValue( private_key )
self._receive_anon.SetValue( receive_anon )
elif service_type == HC.LOCAL_RATING_LIKE:
( like, dislike ) = extra_info
self._like.SetValue( like )
self._dislike.SetValue( dislike )
elif service_type == HC.LOCAL_RATING_NUMERICAL:
( lower, upper ) = extra_info
self._lower.SetValue( lower )
self._upper.SetValue( upper )
class DialogManageSubscriptions( Dialog ):
def __init__( self, parent ):
def InitialiseControls():
self._listbook = ClientGUICommon.ListBook( self )
self._listbook.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventServiceChanging )
types_to_listbooks = {}
self._deviant_art = ClientGUICommon.ListBook( self._listbook )
self._deviant_art.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventServiceChanging )
self._hentai_foundry = ClientGUICommon.ListBook( self._listbook )
self._hentai_foundry.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventServiceChanging )
self._giphy = ClientGUICommon.ListBook( self._listbook )
self._giphy.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventServiceChanging )
self._pixiv = ClientGUICommon.ListBook( self._listbook )
self._pixiv.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventServiceChanging )
self._booru = ClientGUICommon.ListBook( self._listbook )
self._booru.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventServiceChanging )
self._tumblr = ClientGUICommon.ListBook( self._listbook )
self._tumblr.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventServiceChanging )
types_to_listbooks[ HC.SITE_DOWNLOAD_TYPE_DEVIANT_ART ] = self._deviant_art
types_to_listbooks[ HC.SITE_DOWNLOAD_TYPE_HENTAI_FOUNDRY ] = self._hentai_foundry
types_to_listbooks[ HC.SITE_DOWNLOAD_TYPE_GIPHY ] = self._giphy
types_to_listbooks[ HC.SITE_DOWNLOAD_TYPE_PIXIV ] = self._pixiv
types_to_listbooks[ HC.SITE_DOWNLOAD_TYPE_BOORU ] = self._booru
types_to_listbooks[ HC.SITE_DOWNLOAD_TYPE_TUMBLR ] = self._tumblr
for ( site_download_type, name, query_type, query, frequency_type, frequency_number, advanced_tag_options, advanced_import_options, last_checked, url_cache ) in self._original_subscriptions:
listbook = types_to_listbooks[ site_download_type ]
page = self._Panel( listbook, site_download_type, name, query_type, query, frequency_type, frequency_number, advanced_tag_options, advanced_import_options, last_checked, url_cache )
listbook.AddPage( page, name )
self._listbook.AddPage( self._deviant_art, 'deviant art' )
self._listbook.AddPage( self._hentai_foundry, 'hentai foundry' )
self._listbook.AddPage( self._giphy, 'giphy' )
self._listbook.AddPage( self._pixiv, 'pixiv' )
self._listbook.AddPage( self._booru, 'booru' )
self._listbook.AddPage( self._tumblr, 'tumblr' )
self._add = wx.Button( self, label='add' )
self._add.Bind( wx.EVT_BUTTON, self.EventAdd )
self._add.SetForegroundColour( ( 0, 128, 0 ) )
self._remove = wx.Button( self, label='remove' )
self._remove.Bind( wx.EVT_BUTTON, self.EventRemove )
self._remove.SetForegroundColour( ( 128, 0, 0 ) )
self._export = wx.Button( self, label='export' )
self._export.Bind( wx.EVT_BUTTON, self.EventExport )
self._ok = wx.Button( self, label='ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label='cancel' )
self._cancel.Bind( wx.EVT_BUTTON, self.EventCancel )
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
# these need to be below the addpages because they'd fire the events
self._listbook.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventPageChanging, source = self._listbook )
def InitialisePanel():
add_remove_hbox = wx.BoxSizer( wx.HORIZONTAL )
add_remove_hbox.AddF( self._add, FLAGS_MIXED )
add_remove_hbox.AddF( self._remove, FLAGS_MIXED )
add_remove_hbox.AddF( self._export, FLAGS_MIXED )
ok_hbox = wx.BoxSizer( wx.HORIZONTAL )
ok_hbox.AddF( self._ok, FLAGS_MIXED )
ok_hbox.AddF( self._cancel, FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._listbook, FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( add_remove_hbox, FLAGS_SMALL_INDENT )
vbox.AddF( ok_hbox, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( 680, max( 720, y ) ) )
Dialog.__init__( self, parent, 'manage subscriptions' )
self._original_subscriptions = wx.GetApp().Read( 'subscriptions' )
InitialiseControls()
InitialisePanel()
self.SetDropTarget( ClientGUICommon.FileDropTarget( self.Import ) )
def _CheckCurrentSubscriptionIsValid( self ):
subs_listbook = self._listbook.GetCurrentPage()
if subs_listbook is not None:
sub_panel = subs_listbook.GetCurrentPage()
if sub_panel is not None:
name = sub_panel.GetName()
old_name = subs_listbook.GetCurrentName()
if old_name is not None and name != old_name:
if subs_listbook.NameExists( name ): raise Exception( 'That name is already in use!' )
subs_listbook.RenamePage( old_name, name )
def EventAdd( self, event ):
with wx.TextEntryDialog( self, 'Enter name for subscription' ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
try:
name = dlg.GetValue()
subscription_listbook = self._listbook.GetCurrentPage()
if subscription_listbook.NameExists( name ): raise Exception( 'That name is already in use!' )
if name == '': raise Exception( 'Please enter a nickname for the subscription.' )
if subscription_listbook == self._deviant_art: site_download_type = HC.SITE_DOWNLOAD_TYPE_DEVIANT_ART
elif subscription_listbook == self._hentai_foundry: site_download_type = HC.SITE_DOWNLOAD_TYPE_HENTAI_FOUNDRY
elif subscription_listbook == self._giphy: site_download_type = HC.SITE_DOWNLOAD_TYPE_GIPHY
elif subscription_listbook == self._pixiv: site_download_type = HC.SITE_DOWNLOAD_TYPE_PIXIV
elif subscription_listbook == self._booru: site_download_type = HC.SITE_DOWNLOAD_TYPE_BOORU
elif subscription_listbook == self._tumblr: site_download_type = HC.SITE_DOWNLOAD_TYPE_TUMBLR
if site_download_type in ( HC.SITE_DOWNLOAD_TYPE_DEVIANT_ART, HC.SITE_DOWNLOAD_TYPE_TUMBLR ): query_type = 'artist'
else: query_type = 'tags'
if site_download_type == HC.SITE_DOWNLOAD_TYPE_BOORU: query_type = ( '', query_type )
query = ''
frequency_type = 86400
frequency_number = 7
advanced_tag_options = {}
advanced_import_options = {} # blaaah not sure
last_checked = None
url_cache = set()
page = self._Panel( subscription_listbook, site_download_type, name, query_type, query, frequency_type, frequency_number, advanced_tag_options, advanced_import_options, last_checked, url_cache )
subscription_listbook.AddPage( page, name, select = True )
except Exception as e:
wx.MessageBox( unicode( e ) )
self.EventAdd( event )
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventExport( self, event ):
try: self._CheckCurrentSubscriptionIsValid()
except Exception as e:
wx.MessageBox( unicode( e ) )
return
subscription_listbook = self._listbook.GetCurrentPage()
if subscription_listbook is not None:
sub_panel = subscription_listbook.GetCurrentPage()
if sub_panel is not None:
name = subscription_listbook.GetCurrentName()
info = sub_panel.GetInfo()
( site_download_type, name, query_type, query, frequency_type, frequency_number, advanced_tag_options, advanced_import_options, last_checked, url_cache ) = info
advanced_tag_options = advanced_tag_options.items() # yaml parsing bug
info = ( site_download_type, name, query_type, query, frequency_type, frequency_number, advanced_tag_options, advanced_import_options, last_checked, url_cache )
try:
with wx.FileDialog( self, 'select where to export subscription', defaultFile = name + '.yaml', style = wx.FD_SAVE ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
with open( dlg.GetPath(), 'wb' ) as f: f.write( yaml.safe_dump( info ) )
except:
with wx.FileDialog( self, 'select where to export subscription', defaultFile = 'subscription.yaml', style = wx.FD_SAVE ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
with open( dlg.GetPath(), 'wb' ) as f: f.write( yaml.safe_dump( info ) )
def EventOk( self, event ):
try: self._CheckCurrentSubscriptionIsValid()
except Exception as e:
wx.MessageBox( unicode( e ) )
return
all_pages = []
all_pages.extend( self._deviant_art.GetNameToPageDict().values() )
all_pages.extend( self._hentai_foundry.GetNameToPageDict().values() )
all_pages.extend( self._giphy.GetNameToPageDict().values() )
all_pages.extend( self._pixiv.GetNameToPageDict().values() )
all_pages.extend( self._booru.GetNameToPageDict().values() )
all_pages.extend( self._tumblr.GetNameToPageDict().values() )
subscriptions = [ page.GetInfo() for page in all_pages ]
try: wx.GetApp().Write( 'subscriptions', subscriptions )
except Exception as e: wx.MessageBox( 'Saving services to DB raised this error: ' + unicode( e ) )
self.EndModal( wx.ID_OK )
def EventPageChanging( self, event ):
try: self._CheckCurrentSubscriptionIsValid()
except Exception as e:
wx.MessageBox( unicode( e ) )
event.Veto()
def EventRemove( self, event ):
subscription_listbook = self._listbook.GetCurrentPage()
sub_panel = subscription_listbook.GetCurrentPage()
if sub_panel is not None: subscription_listbook.DeleteCurrentPage()
def EventServiceChanging( self, event ):
try: self._CheckCurrentSubscriptionIsValid()
except Exception as e:
wx.MessageBox( unicode( e ) )
event.Veto()
def Import( self, paths ):
try: self._CheckCurrentSubscriptionIsValid()
except Exception as e:
wx.MessageBox( unicode( e ) )
return
for path in paths:
try:
with open( path, 'rb' ) as f: file = f.read()
( site_download_type, name, query_type, query, frequency_type, frequency_number, advanced_tag_options, advanced_import_options, last_checked, url_cache ) = yaml.safe_load( file )
advanced_tag_options = dict( advanced_tag_options ) # yaml parsing bug
if site_download_type == HC.SITE_DOWNLOAD_TYPE_BOORU: services_listbook = self._booru
elif site_download_type == HC.SITE_DOWNLOAD_TYPE_DEVIANT_ART: services_listbook = self._deviant_art
elif site_download_type == HC.SITE_DOWNLOAD_TYPE_GIPHY: services_listbook = self._giphy
elif site_download_type == HC.SITE_DOWNLOAD_TYPE_HENTAI_FOUNDRY: services_listbook = self._hentai_foundry
elif site_download_type == HC.SITE_DOWNLOAD_TYPE_PIXIV: services_listbook = self._pixiv
elif site_download_type == HC.SITE_DOWNLOAD_TYPE_TUMBLR: services_listbook = self._tumblr
self._listbook.SelectPage( services_listbook )
if services_listbook.NameExists( name ):
message = 'A service already exists with that name. Overwrite it?'
with DialogYesNo( self, message ) as dlg:
if dlg.ShowModal() == wx.ID_YES:
page = services_listbook.GetNameToPageDict()[ name ]
page.Update( query_type, query, frequency_type, frequency_number, advanced_tag_options, advanced_import_options, last_checked, url_cache )
else:
page = self._Panel( services_listbook, site_download_type, name, query_type, query, frequency_type, frequency_number, advanced_tag_options, advanced_import_options, last_checked, url_cache )
services_listbook.AddPage( page, name, select = True )
except:
wx.MessageBox( traceback.format_exc() )
class _Panel( wx.ScrolledWindow ):
def __init__( self, parent, site_download_type, name, query_type, query, frequency_type, frequency_number, advanced_tag_options, advanced_import_options, last_checked, url_cache ):
wx.ScrolledWindow.__init__( self, parent )
self._reset_cache = False
self.SetScrollRate( 0, 20 )
self.SetMinSize( ( 540, 620 ) )
self._original_info = ( site_download_type, name, query_type, query, frequency_type, frequency_number, advanced_tag_options, advanced_import_options, last_checked, url_cache )
# init controls
self._name_panel = ClientGUICommon.StaticBox( self, 'name' )
self._name = wx.TextCtrl( self._name_panel )
self._query_panel = ClientGUICommon.StaticBox( self, 'query' )
self._query = wx.TextCtrl( self._query_panel )
self._booru_selector = wx.ListBox( self._query_panel )
if site_download_type == HC.SITE_DOWNLOAD_TYPE_BOORU:
boorus = wx.GetApp().Read( 'boorus' )
for booru in boorus: self._booru_selector.Append( booru.GetName(), booru )
else: self._booru_selector.Hide()
self._query_type = ClientGUICommon.RadioBox( self._query_panel, 'query type', ( ( 'artist', 'artist' ), ( 'tags', 'tags' ) ) )
if site_download_type in ( HC.SITE_DOWNLOAD_TYPE_BOORU, HC.SITE_DOWNLOAD_TYPE_DEVIANT_ART, HC.SITE_DOWNLOAD_TYPE_GIPHY, HC.SITE_DOWNLOAD_TYPE_TUMBLR ): self._query_type.Hide()
self._frequency_number = wx.SpinCtrl( self._query_panel )
self._frequency_type = wx.Choice( self._query_panel )
for ( title, timespan ) in ( ( 'days', 86400 ), ( 'weeks', 86400 * 7 ), ( 'months', 86400 * 30 ) ): self._frequency_type.Append( title, timespan )
self._info_panel = ClientGUICommon.StaticBox( self, 'info' )
self._reset_cache_button = wx.Button( self._info_panel, label = ' reset cache on dialog ok ' )
self._reset_cache_button.Bind( wx.EVT_BUTTON, self.EventResetCache )
if site_download_type == HC.SITE_DOWNLOAD_TYPE_BOORU: namespaces = [ 'creator', 'series', 'character', '' ]
elif site_download_type == HC.SITE_DOWNLOAD_TYPE_DEVIANT_ART: namespaces = [ 'creator', 'title', '' ]
elif site_download_type == HC.SITE_DOWNLOAD_TYPE_GIPHY: namespaces = [ '' ]
elif site_download_type == HC.SITE_DOWNLOAD_TYPE_HENTAI_FOUNDRY: namespaces = [ 'creator', 'title', '' ]
elif site_download_type == HC.SITE_DOWNLOAD_TYPE_PIXIV: namespaces = [ 'creator', 'title', '' ]
elif site_download_type == HC.SITE_DOWNLOAD_TYPE_TUMBLR: namespaces = [ '' ]
self._advanced_tag_options = ClientGUICommon.AdvancedTagOptions( self, 'send tags to ', namespaces )
self._advanced_import_options = ClientGUICommon.AdvancedImportOptions( self )
self._SetControls( *self._original_info )
# init panel
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
self._name_panel.AddF( self._name, FLAGS_EXPAND_PERPENDICULAR )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self._query_panel, label = 'Check subscription every ' ), FLAGS_MIXED )
hbox.AddF( self._frequency_number, FLAGS_MIXED )
hbox.AddF( self._frequency_type, FLAGS_MIXED )
self._query_panel.AddF( self._query, FLAGS_EXPAND_PERPENDICULAR )
self._query_panel.AddF( self._query_type, FLAGS_EXPAND_PERPENDICULAR )
self._query_panel.AddF( self._booru_selector, FLAGS_EXPAND_PERPENDICULAR )
self._query_panel.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
if last_checked is None: last_checked_message = 'not yet initialised'
else:
now = int( time.time() )
if last_checked < now: last_checked_message = HC.ConvertTimestampToPrettySync( last_checked )
else: last_checked_message = 'due to error, update is delayed. next check in ' + HC.ConvertTimestampToPrettyPending( last_checked )
self._info_panel.AddF( wx.StaticText( self._info_panel, label = last_checked_message ), FLAGS_EXPAND_PERPENDICULAR )
self._info_panel.AddF( wx.StaticText( self._info_panel, label = str( len( url_cache ) ) + ' urls in cache' ), FLAGS_EXPAND_PERPENDICULAR )
self._info_panel.AddF( self._reset_cache_button, FLAGS_LONE_BUTTON )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._name_panel, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._query_panel, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._info_panel, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._advanced_tag_options, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._advanced_import_options, FLAGS_EXPAND_PERPENDICULAR )
self.SetSizer( vbox )
def _SetControls( self, site_download_type, name, query_type, query, frequency_type, frequency_number, advanced_tag_options, advanced_import_options, last_checked, url_cache ):
self._name.SetValue( name )
self._query.SetValue( query )
if site_download_type == HC.SITE_DOWNLOAD_TYPE_BOORU:
( booru_name, query_type ) = query_type
index = self._booru_selector.FindString( booru_name )
if index != wx.NOT_FOUND: self._booru_selector.Select( index )
initial_index = 1
else:
self._booru_selector.Hide()
if query_type == 'artist': initial_index = 0
elif query_type == 'tags': initial_index = 1
self._query_type.SetSelection( initial_index )
if site_download_type in ( HC.SITE_DOWNLOAD_TYPE_BOORU, HC.SITE_DOWNLOAD_TYPE_DEVIANT_ART, HC.SITE_DOWNLOAD_TYPE_GIPHY, HC.SITE_DOWNLOAD_TYPE_TUMBLR ): self._query_type.Hide()
self._frequency_number.SetValue( frequency_number )
index_to_select = None
i = 0
for ( title, timespan ) in ( ( 'days', 86400 ), ( 'weeks', 86400 * 7 ), ( 'months', 86400 * 30 ) ):
if frequency_type == timespan: index_to_select = i
i += 1
if index_to_select is not None: self._frequency_type.Select( index_to_select )
self._reset_cache_button.SetLabel( ' reset cache on dialog ok ' )
self._advanced_tag_options.SetInfo( advanced_tag_options )
self._advanced_import_options.SetInfo( advanced_import_options )
def EventResetCache( self, event ):
self._reset_cache = True
self._reset_cache_button.SetLabel( 'cache will be reset on dialog ok' )
self._reset_cache_button.Disable()
def GetInfo( self ):
( site_download_type, name, query_type, query, frequency_type, frequency_number, advanced_tag_options, advanced_import_options, last_checked, url_cache ) = self._original_info
name = self._name.GetValue()
query_type = self._query_type.GetSelectedClientData()
if site_download_type == HC.SITE_DOWNLOAD_TYPE_BOORU:
booru_name = self._booru_selector.GetStringSelection()
query_type = ( booru_name, query_type )
query = self._query.GetValue()
frequency_number = self._frequency_number.GetValue()
frequency_type = self._frequency_type.GetClientData( self._frequency_type.GetSelection() )
advanced_tag_options = self._advanced_tag_options.GetInfo()
advanced_import_options = self._advanced_import_options.GetInfo()
if self._reset_cache:
last_checked = None
url_cache = set()
return ( site_download_type, name, query_type, query, frequency_type, frequency_number, advanced_tag_options, advanced_import_options, last_checked, url_cache )
def GetName( self ): return self._name.GetValue()
def Update( self, query_type, query, frequency_type, frequency_number, advanced_tag_options, advanced_import_options, last_checked, url_cache ):
site_download_type = self._original_info[0]
name = self._original_info[1]
self._original_info = ( site_download_type, name, query_type, query, frequency_type, frequency_number, advanced_tag_options, advanced_import_options, last_checked, url_cache )
self._SetControls( *self._original_info )
class DialogManageTagServicePrecedence( Dialog ):
def __init__( self, parent ):
def InitialiseControls():
message = 'When services dispute over a file\'s tags,' + os.linesep + 'higher services will overrule those below.'
self._explain = wx.StaticText( self, label = message )
self._tag_services = wx.ListBox( self )
tag_service_precedence = wx.GetApp().Read( 'tag_service_precedence' )
for service_identifier in tag_service_precedence:
name = service_identifier.GetName()
self._tag_services.Append( name, service_identifier )
self._up = wx.Button( self, label = u'\u2191' )
self._up.Bind( wx.EVT_BUTTON, self.EventUp )
self._down = wx.Button( self, label = u'\u2193' )
self._down.Bind( wx.EVT_BUTTON, self.EventDown )
self._apply = wx.Button( self, label='apply' )
self._apply.Bind( wx.EVT_BUTTON, self.EventOK )
self._apply.SetForegroundColour( ( 0, 128, 0 ) )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label='cancel' )
self._cancel.Bind( wx.EVT_BUTTON, self.EventCancel )
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
def InitialisePanel():
updown_vbox = wx.BoxSizer( wx.VERTICAL )
updown_vbox.AddF( self._up, FLAGS_MIXED )
updown_vbox.AddF( self._down, FLAGS_MIXED )
main_hbox = wx.BoxSizer( wx.HORIZONTAL )
main_hbox.AddF( self._tag_services, FLAGS_EXPAND_BOTH_WAYS )
main_hbox.AddF( updown_vbox, FLAGS_MIXED )
buttons = wx.BoxSizer( wx.HORIZONTAL )
buttons.AddF( self._apply, FLAGS_SMALL_INDENT )
buttons.AddF( self._cancel, FLAGS_SMALL_INDENT )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._explain, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( main_hbox, FLAGS_EXPAND_SIZER_BOTH_WAYS )
vbox.AddF( buttons, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
if y < 400: y = 400
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'manage tag service precedence' )
InitialiseControls()
InitialisePanel()
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventOK( self, event ):
try:
service_identifiers = [ self._tag_services.GetClientData( i ) for i in range( self._tag_services.GetCount() ) ]
wx.GetApp().Write( 'set_tag_service_precedence', service_identifiers )
except Exception as e: wx.MessageBox( 'Something went wrong when trying to save tag service precedence to the database: ' + unicode( e ) )
self.EndModal( wx.ID_OK )
def EventUp( self, event ):
selection = self._tag_services.GetSelection()
if selection != wx.NOT_FOUND:
if selection > 0:
service_identifier = self._tag_services.GetClientData( selection )
name = service_identifier.GetName()
self._tag_services.Delete( selection )
self._tag_services.Insert( name, selection - 1, service_identifier )
self._tag_services.Select( selection - 1 )
def EventDown( self, event ):
selection = self._tag_services.GetSelection()
if selection != wx.NOT_FOUND:
if selection + 1 < self._tag_services.GetCount():
service_identifier = self._tag_services.GetClientData( selection )
name = service_identifier.GetName()
self._tag_services.Delete( selection )
self._tag_services.Insert( name, selection + 1, service_identifier )
self._tag_services.Select( selection + 1 )
class DialogManageTags( Dialog ):
def __init__( self, parent, file_service_identifier, media ):
def InitialiseControls():
self._tag_repositories = ClientGUICommon.ListBook( self )
self._tag_repositories.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGED, self.EventServiceChanged )
service_identifiers = wx.GetApp().Read( 'service_identifiers', ( HC.TAG_REPOSITORY, ) )
for service_identifier in list( service_identifiers ) + [ HC.LOCAL_TAG_SERVICE_IDENTIFIER ]:
service_type = service_identifier.GetType()
page_info = ( self._Panel, ( self._tag_repositories, self._file_service_identifier, service_identifier, media ), {} )
name = service_identifier.GetName()
self._tag_repositories.AddPage( page_info, name )
default_tag_repository = self._options[ 'default_tag_repository' ]
self._tag_repositories.Select( default_tag_repository.GetName() )
self._apply = wx.Button( self, label='Apply' )
self._apply.Bind( wx.EVT_BUTTON, self.EventOk )
self._apply.SetForegroundColour( ( 0, 128, 0 ) )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label='Cancel' )
self._cancel.Bind( wx.EVT_BUTTON, self.EventCancel )
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
def InitialisePanel():
buttonbox = wx.BoxSizer( wx.HORIZONTAL )
buttonbox.AddF( self._apply, FLAGS_MIXED )
buttonbox.AddF( self._cancel, FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._tag_repositories, FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( buttonbox, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x + 200, 500 ) )
self._file_service_identifier = file_service_identifier
self._hashes = HC.IntelligentMassUnion( ( m.GetHashes() for m in media ) )
Dialog.__init__( self, parent, 'manage tags for ' + HC.ConvertIntToPrettyString( len( self._hashes ) ) + ' files' )
InitialiseControls()
InitialisePanel()
self.Bind( wx.EVT_MENU, self.EventMenu )
self.RefreshAcceleratorTable()
def _SetSearchFocus( self ):
page = self._tag_repositories.GetCurrentPage()
page.SetTagBoxFocus()
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventMenu( self, event ):
action = CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
if action is not None:
( command, data ) = action
if command == 'manage_tags': self.EventCancel( event )
elif command == 'set_search_focus': self._SetSearchFocus()
elif command == 'ok': self.EventOk( event )
else: event.Skip()
def EventOk( self, event ):
try:
content_updates = []
for page in self._tag_repositories.GetNameToPageDict().values():
if page.HasChanges():
service_identifier = page.GetServiceIdentifier()
edit_log = page.GetEditLog()
content_updates.append( HC.ContentUpdate( HC.CONTENT_UPDATE_EDIT_LOG, service_identifier, self._hashes, info = edit_log ) )
if len( content_updates ) > 0: wx.GetApp().Write( 'content_updates', content_updates )
except Exception as e: wx.MessageBox( 'Saving pending mapping changes to DB raised this error: ' + unicode( e ) )
self.EndModal( wx.ID_OK )
def EventServiceChanged( self, event ):
page = self._tag_repositories.GetCurrentPage()
wx.CallAfter( page.SetTagBoxFocus )
def RefreshAcceleratorTable( self ):
interested_actions = [ 'manage_tags', 'set_search_focus' ]
entries = []
for ( modifier, key_dict ) in self._options[ 'shortcuts' ].items(): entries.extend( [ ( modifier, key, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( action ) ) for ( key, action ) in key_dict.items() if action in interested_actions ] )
self.SetAcceleratorTable( wx.AcceleratorTable( entries ) )
class _Panel( wx.Panel ):
def __init__( self, parent, file_service_identifier, tag_service_identifier, media ):
def InitialiseControls():
self._tags_box = ClientGUICommon.TagsBoxManage( self, self.AddTag, self._current_tags, self._deleted_tags, self._pending_tags, self._petitioned_tags )
self._add_tag_box = ClientGUICommon.AutoCompleteDropdownTagsWrite( self, self.AddTag, self._file_service_identifier, self._tag_service_identifier )
self._show_deleted_tags = wx.CheckBox( self, label='Show deleted tags' )
self._show_deleted_tags.Bind( wx.EVT_CHECKBOX, self.EventShowDeletedTags )
self._modify_mappers = wx.Button( self, label='Modify mappers' )
self._modify_mappers.Bind( wx.EVT_BUTTON, self.EventModify )
self._copy_tags = wx.Button( self, label = 'copy tags' )
self._copy_tags.Bind( wx.EVT_BUTTON, self.EventCopyTags )
self._paste_tags = wx.Button( self, label = 'paste tags' )
self._paste_tags.Bind( wx.EVT_BUTTON, self.EventPasteTags )
if self._i_am_local_tag_service:
self._show_deleted_tags.Hide()
self._modify_mappers.Hide()
else:
if not self._account.HasPermission( HC.POST_DATA ): self._add_tag_box.Hide()
if not self._account.HasPermission( HC.MANAGE_USERS ): self._modify_mappers.Hide()
def InitialisePanel():
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
special_hbox = wx.BoxSizer( wx.HORIZONTAL )
special_hbox.AddF( self._show_deleted_tags, FLAGS_MIXED )
special_hbox.AddF( self._modify_mappers, FLAGS_MIXED )
copy_paste_hbox = wx.BoxSizer( wx.HORIZONTAL )
copy_paste_hbox.AddF( self._copy_tags, FLAGS_MIXED )
copy_paste_hbox.AddF( self._paste_tags, FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._tags_box, FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( self._add_tag_box, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( copy_paste_hbox, FLAGS_BUTTON_SIZERS )
vbox.AddF( special_hbox, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
wx.Panel.__init__( self, parent )
self._file_service_identifier = file_service_identifier
self._tag_service_identifier = tag_service_identifier
self._i_am_local_tag_service = self._tag_service_identifier.GetType() == HC.LOCAL_TAG
self._edit_log = []
if not self._i_am_local_tag_service:
service = wx.GetApp().Read( 'service', tag_service_identifier )
self._account = service.GetAccount()
( self._current_tags, self._deleted_tags, self._pending_tags, self._petitioned_tags ) = CC.MediaIntersectCDPPTagServiceIdentifiers( media, tag_service_identifier )
self._current_tags.sort()
self._pending_tags.sort()
InitialiseControls()
InitialisePanel()
def AddTag( self, tag ):
if tag is None: wx.PostEvent( self, wx.CommandEvent( commandType = wx.wxEVT_COMMAND_MENU_SELECTED, winid = CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'ok' ) ) )
else:
if self._i_am_local_tag_service:
if tag in self._pending_tags:
self._pending_tags.remove( tag )
self._tags_box.RescindPend( tag )
elif tag in self._petitioned_tags:
self._petitioned_tags.remove( tag )
self._tags_box.RescindPetition( tag )
elif tag in self._current_tags:
self._petitioned_tags.append( tag )
self._tags_box.PetitionTag( tag )
else:
self._pending_tags.append( tag )
self._tags_box.PendTag( tag )
self._edit_log = []
self._edit_log.extend( [ ( HC.CONTENT_UPDATE_ADD, tag ) for tag in self._pending_tags ] )
self._edit_log.extend( [ ( HC.CONTENT_UPDATE_DELETE, tag ) for tag in self._petitioned_tags ] )
else:
if tag in self._pending_tags:
self._pending_tags.remove( tag )
self._tags_box.RescindPend( tag )
self._edit_log.append( ( HC.CONTENT_UPDATE_RESCIND_PENDING, tag ) )
elif tag in self._petitioned_tags:
self._petitioned_tags.remove( tag )
self._tags_box.RescindPetition( tag )
self._edit_log.append( ( HC.CONTENT_UPDATE_RESCIND_PETITION, tag ) )
elif tag in self._current_tags:
if self._account.HasPermission( HC.RESOLVE_PETITIONS ):
self._edit_log.append( ( HC.CONTENT_UPDATE_PETITION, ( tag, 'admin' ) ) )
self._petitioned_tags.append( tag )
self._tags_box.PetitionTag( tag )
elif self._account.HasPermission( HC.POST_PETITIONS ):
message = 'Enter a reason for this tag to be removed. A janitor will review your petition.'
with wx.TextEntryDialog( self, message ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
self._edit_log.append( ( HC.CONTENT_UPDATE_PETITION, ( tag, dlg.GetValue() ) ) )
self._petitioned_tags.append( tag )
self._tags_box.PetitionTag( tag )
elif tag in self._deleted_tags:
if self._account.HasPermission( HC.RESOLVE_PETITIONS ):
self._edit_log.append( ( HC.CONTENT_UPDATE_PENDING, tag ) )
self._pending_tags.append( tag )
self._tags_box.PendTag( tag )
else:
self._edit_log.append( ( HC.CONTENT_UPDATE_PENDING, tag ) )
self._pending_tags.append( tag )
self._tags_box.PendTag( tag )
def EventCopyTags( self, event ):
if wx.TheClipboard.Open():
tags = self._current_tags + self._pending_tags
text = yaml.safe_dump( tags )
data = wx.TextDataObject( text )
wx.TheClipboard.SetData( data )
wx.TheClipboard.Close()
else: wx.MessageBox( 'I could not get permission to access the clipboard.' )
def EventModify( self, event ):
tag = self._tags_box.GetSelectedTag()
if tag is not None and tag in self._current_tags or tag in self._petitioned_tags:
subject_identifiers = [ HC.AccountIdentifier( hash = hash, tag = tag ) for hash in self._hashes ]
try:
with DialogModifyAccounts( self, self._tag_service_identifier, subject_identifiers ) as dlg: dlg.ShowModal()
except Exception as e: wx.MessageBox( unicode( e ) )
def EventPasteTags( self, event ):
if wx.TheClipboard.Open():
data = wx.TextDataObject()
wx.TheClipboard.GetData( data )
wx.TheClipboard.Close()
text = data.GetText()
try:
tags = yaml.safe_load( text )
tags = [ tag for tag in tags if tag not in self._current_tags and tag not in self._pending_tags ]
for tag in tags: self.AddTag( tag )
except: wx.MessageBox( 'I could not understand what was in the clipboard' )
else: wx.MessageBox( 'I could not get permission to access the clipboard.' )
def EventShowDeletedTags( self, event ): self._tags_box.SetShowDeletedTags( self._show_deleted_tags.GetValue() )
def EventTagsBoxAction( self, event ):
tag = self._tags_box.GetSelectedTag()
if tag is not None: self.AddTag( tag )
def GetEditLog( self ): return self._edit_log
def GetServiceIdentifier( self ): return self._tag_service_identifier
def HasChanges( self ): return len( self._edit_log ) > 0
def SetTagBoxFocus( self ):
if self._i_am_local_tag_service or self._account.HasPermission( HC.POST_DATA ): self._add_tag_box.SetFocus()
class DialogMessage( Dialog ):
def __init__( self, parent, message, ok_label = 'ok' ):
def InitialiseControls():
self._ok = wx.Button( self, id = wx.ID_CANCEL, label = ok_label )
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
def InitialisePanel():
vbox = wx.BoxSizer( wx.VERTICAL )
text = wx.StaticText( self, label = str( message ) )
text.Wrap( 480 )
vbox.AddF( text, FLAGS_BIG_INDENT )
vbox.AddF( self._ok, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'message', position = 'center' )
InitialiseControls()
InitialisePanel()
def EventOk( self, event ): self.EndModal( wx.ID_OK )
class DialogModifyAccounts( Dialog ):
def __init__( self, parent, service_identifier, subject_identifiers ):
def InitialiseControls():
connection = self._service.GetConnection()
if len( self._subject_identifiers ) == 1:
( subject_identifier, ) = self._subject_identifiers
subject_string = connection.Get( 'account_info', subject_identifier = subject_identifier )
else: subject_string = 'modifying ' + HC.ConvertIntToPrettyString( len( self._subject_identifiers ) ) + ' accounts'
self._account_info_panel = ClientGUICommon.StaticBox( self, 'account info' )
self._subject_text = wx.StaticText( self._account_info_panel, label = str( subject_string ) )
account_types = connection.Get( 'account_types' )
self._account_types_panel = ClientGUICommon.StaticBox( self, 'account types' )
self._account_types = wx.Choice( self._account_types_panel )
for account_type in account_types: self._account_types.Append( account_type.ConvertToString(), account_type )
self._account_types.SetSelection( 0 )
self._account_types_ok = wx.Button( self._account_types_panel, label = 'Ok' )
self._account_types_ok.Bind( wx.EVT_BUTTON, self.EventChangeAccountType )
self._expiration_panel = ClientGUICommon.StaticBox( self, 'change expiration' )
self._add_to_expires = wx.Choice( self._expiration_panel )
for ( string, value ) in HC.expirations:
if value is not None: self._add_to_expires.Append( string, value ) # don't want 'add no limit'
self._add_to_expires.SetSelection( 1 ) # three months
self._add_to_expires_ok = wx.Button( self._expiration_panel, label = 'Ok' )
self._add_to_expires_ok.Bind( wx.EVT_BUTTON, self.EventAddToExpires )
self._set_expires = wx.Choice( self._expiration_panel )
for ( string, value ) in HC.expirations: self._set_expires.Append( string, value )
self._set_expires.SetSelection( 1 ) # three months
self._set_expires_ok = wx.Button( self._expiration_panel, label = 'Ok' )
self._set_expires_ok.Bind( wx.EVT_BUTTON, self.EventSetExpires )
self._ban_panel = ClientGUICommon.StaticBox( self, 'bans' )
self._ban = wx.Button( self._ban_panel, label = 'ban user' )
self._ban.Bind( wx.EVT_BUTTON, self.EventBan )
self._ban.SetBackgroundColour( ( 255, 0, 0 ) )
self._ban.SetForegroundColour( ( 255, 255, 0 ) )
self._superban = wx.Button( self._ban_panel, label = 'ban user and delete every contribution they have ever made' )
self._superban.Bind( wx.EVT_BUTTON, self.EventSuperban )
self._superban.SetBackgroundColour( ( 255, 0, 0 ) )
self._superban.SetForegroundColour( ( 255, 255, 0 ) )
self._exit = wx.Button( self, id = wx.ID_CANCEL, label='Exit' )
self._exit.Bind( wx.EVT_BUTTON, lambda event: self.EndModal( wx.ID_OK ) )
if not self._service.GetAccount().HasPermission( HC.GENERAL_ADMIN ):
self._account_types_ok.Disable()
self._add_to_expires_ok.Disable()
self._set_expires_ok.Disable()
def InitialisePanel():
self._account_info_panel.AddF( self._subject_text, FLAGS_EXPAND_PERPENDICULAR )
account_types_hbox = wx.BoxSizer( wx.HORIZONTAL )
account_types_hbox.AddF( self._account_types, FLAGS_MIXED )
account_types_hbox.AddF( self._account_types_ok, FLAGS_MIXED )
self._account_types_panel.AddF( account_types_hbox, FLAGS_EXPAND_PERPENDICULAR )
add_to_expires_box = wx.BoxSizer( wx.HORIZONTAL )
add_to_expires_box.AddF( wx.StaticText( self._expiration_panel, label = 'add to expires: ' ), FLAGS_MIXED )
add_to_expires_box.AddF( self._add_to_expires, FLAGS_EXPAND_BOTH_WAYS )
add_to_expires_box.AddF( self._add_to_expires_ok, FLAGS_MIXED )
set_expires_box = wx.BoxSizer( wx.HORIZONTAL )
set_expires_box.AddF( wx.StaticText( self._expiration_panel, label = 'set expires to: ' ), FLAGS_MIXED )
set_expires_box.AddF( self._set_expires, FLAGS_EXPAND_BOTH_WAYS )
set_expires_box.AddF( self._set_expires_ok, FLAGS_MIXED )
self._expiration_panel.AddF( add_to_expires_box, FLAGS_EXPAND_PERPENDICULAR )
self._expiration_panel.AddF( set_expires_box, FLAGS_EXPAND_PERPENDICULAR )
self._ban_panel.AddF( self._ban, FLAGS_EXPAND_PERPENDICULAR )
self._ban_panel.AddF( self._superban, FLAGS_EXPAND_PERPENDICULAR )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._account_info_panel, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._account_types_panel, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._expiration_panel, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._ban_panel, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._exit, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'modify account' )
self._service = wx.GetApp().Read( 'service', service_identifier )
self._subject_identifiers = set( subject_identifiers )
InitialiseControls()
InitialisePanel()
def _DoModification( self, action, **kwargs ):
try:
connection = self._service.GetConnection()
kwargs[ 'subject_identifiers' ] = list( self._subject_identifiers )
kwargs[ 'action' ] = action
connection.Post( 'account_modification', **kwargs )
if len( self._subject_identifiers ) == 1:
( subject_identifier, ) = self._subject_identifiers
self._subject_text.SetLabel( str( connection.Get( 'account_info', subject_identifier = subject_identifier ) ) )
except Exception as e: wx.MessageBox( unicode( e ) )
if len( self._subject_identifiers ) > 1: wx.MessageBox( 'Done!' )
def EventAddToExpires( self, event ): self._DoModification( HC.ADD_TO_EXPIRES, expiration = self._add_to_expires.GetClientData( self._add_to_expires.GetSelection() ) )
def EventBan( self, event ):
with wx.TextEntryDialog( self, 'Enter reason for the ban' ) as dlg:
if dlg.ShowModal() == wx.ID_OK: self._DoModification( HC.BAN, reason = dlg.GetValue() )
def EventChangeAccountType( self, event ): self._DoModification( HC.CHANGE_ACCOUNT_TYPE, title = self._account_types.GetClientData( self._account_types.GetSelection() ).GetTitle() )
def EventSetExpires( self, event ): self._DoModification( HC.SET_EXPIRES, expiry = int( time.time() ) + self._set_expires.GetClientData( self._set_expires.GetSelection() ) )
def EventSuperban( self, event ):
with wx.TextEntryDialog( self, 'Enter reason for the superban' ) as dlg:
if dlg.ShowModal() == wx.ID_OK: self._DoModification( HC.SUPERBAN, reason = dlg.GetValue() )
class DialogNews( Dialog ):
def __init__( self, parent, service_identifier ):
def InitialiseControls():
self._news = wx.TextCtrl( self, style=wx.TE_READONLY | wx.TE_MULTILINE )
self._previous = wx.Button( self, label='<' )
self._previous.Bind( wx.EVT_BUTTON, self.EventPrevious )
self._news_position = wx.TextCtrl( self )
self._next = wx.Button( self, label='>' )
self._next.Bind( wx.EVT_BUTTON, self.EventNext )
self._done = wx.Button( self, id = wx.ID_CANCEL, label='Done' )
self._done.Bind( wx.EVT_BUTTON, self.EventOk )
def InitialisePanel():
self._newslist = wx.GetApp().Read( 'news', service_identifier )
self._current_news_position = len( self._newslist )
self._ShowNews()
buttonbox = wx.BoxSizer( wx.HORIZONTAL )
buttonbox.AddF( self._previous, FLAGS_MIXED )
buttonbox.AddF( self._news_position, FLAGS_MIXED )
buttonbox.AddF( self._next, FLAGS_MIXED )
donebox = wx.BoxSizer( wx.HORIZONTAL )
donebox.AddF( self._done, FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._news, FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( buttonbox, FLAGS_BUTTON_SIZERS )
vbox.AddF( donebox, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x + 200, 580 ) )
Dialog.__init__( self, parent, 'news' )
InitialiseControls()
InitialisePanel()
def _ShowNews( self ):
if self._current_news_position == 0:
self._news.SetValue( '' )
self._news_position.SetValue( 'No News' )
else:
( news, timestamp ) = self._newslist[ self._current_news_position - 1 ]
self._news.SetValue( time.ctime( timestamp ) + ':' + os.linesep + os.linesep + news )
self._news_position.SetValue( HC.ConvertIntToPrettyString( self._current_news_position ) + ' / ' + HC.ConvertIntToPrettyString( len( self._newslist ) ) )
def EventNext( self, event ):
if self._current_news_position < len( self._newslist ): self._current_news_position += 1
self._ShowNews()
def EventOk( self, event ): self.EndModal( wx.ID_OK )
def EventPrevious( self, event ):
if self._current_news_position > 1: self._current_news_position -= 1
self._ShowNews()
class DialogPathsToTagsRegex( Dialog ):
def __init__( self, parent, paths ):
def InitialiseControls():
self._tag_repositories = ClientGUICommon.ListBook( self )
self._tag_repositories.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGED, self.EventServiceChanged )
services = wx.GetApp().Read( 'services', ( HC.TAG_REPOSITORY, ) )
for service in services:
account = service.GetAccount()
if account.HasPermission( HC.POST_DATA ):
service_identifier = service.GetServiceIdentifier()
page_info = ( self._Panel, ( self._tag_repositories, service_identifier, paths ), {} )
name = service_identifier.GetName()
self._tag_repositories.AddPage( page_info, name )
page = self._Panel( self._tag_repositories, HC.LOCAL_TAG_SERVICE_IDENTIFIER, paths )
name = HC.LOCAL_TAG_SERVICE_IDENTIFIER.GetName()
self._tag_repositories.AddPage( page, name )
default_tag_repository = self._options[ 'default_tag_repository' ]
self._tag_repositories.Select( default_tag_repository.GetName() )
self._add_button = wx.Button( self, label='Import Files' )
self._add_button.Bind( wx.EVT_BUTTON, self.EventOK )
self._add_button.SetForegroundColour( ( 0, 128, 0 ) )
self._close_button = wx.Button( self, id = wx.ID_CANCEL, label='Back to File Selection' )
self._close_button.Bind( wx.EVT_BUTTON, self.EventCancel )
self._close_button.SetForegroundColour( ( 128, 0, 0 ) )
def InitialisePanel():
buttons = wx.BoxSizer( wx.HORIZONTAL )
buttons.AddF( self._add_button, FLAGS_SMALL_INDENT )
buttons.AddF( self._close_button, FLAGS_SMALL_INDENT )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._tag_repositories, FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( buttons, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
self.SetInitialSize( ( 980, 680 ) )
Dialog.__init__( self, parent, 'path tagging' )
self._paths = paths
InitialiseControls()
InitialisePanel()
interested_actions = [ 'set_search_focus' ]
entries = []
for ( modifier, key_dict ) in self._options[ 'shortcuts' ].items(): entries.extend( [ ( modifier, key, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( action ) ) for ( key, action ) in key_dict.items() if action in interested_actions ] )
self.SetAcceleratorTable( wx.AcceleratorTable( entries ) )
self.Bind( wx.EVT_MENU, self.EventMenu )
def _SetSearchFocus( self ):
page = self._tag_repositories.GetCurrentPage()
page.SetTagBoxFocus()
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventMenu( self, event ):
action = CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
if action is not None:
try:
( command, data ) = action
if command == 'set_search_focus': self._SetSearchFocus()
else: event.Skip()
except Exception as e:
wx.MessageBox( unicode( e ) )
wx.MessageBox( traceback.format_exc() )
def EventOK( self, event ): self.EndModal( wx.ID_OK )
def EventServiceChanged( self, event ):
page = self._tag_repositories.GetCurrentPage()
wx.CallAfter( page.SetTagBoxFocus )
def GetInfo( self ):
paths_to_tags = {}
try:
for path in self._paths:
all_tags = {}
for page in self._tag_repositories.GetNameToPageDict().values():
tags = page.GetTags( path )
if len( tags ) > 0:
service_identifier = page.GetServiceIdentifier()
all_tags[ service_identifier ] = tags
if len( all_tags ) > 0: paths_to_tags[ path ] = all_tags
except Exception as e: wx.MessageBox( 'Saving pending mapping changes to DB raised this error: ' + unicode( e ) )
return paths_to_tags
class _Panel( wx.Panel ):
def __init__( self, parent, service_identifier, paths ):
def InitialiseControls():
self._paths_list = ClientGUICommon.SaneListCtrl( self, 250, [ ( 'path', 400 ), ( 'tags', -1 ) ] )
self._paths_list.Bind( wx.EVT_LIST_ITEM_SELECTED, self.EventItemSelected )
self._paths_list.Bind( wx.EVT_LIST_ITEM_DESELECTED, self.EventItemSelected )
#
self._quick_namespaces_panel = ClientGUICommon.StaticBox( self, 'quick namespaces' )
self._quick_namespaces_list = ClientGUICommon.SaneListCtrl( self._quick_namespaces_panel, 200, [ ( 'namespace', 80 ), ( 'regex', -1 ) ] )
self._add_quick_namespace_button = wx.Button( self._quick_namespaces_panel, label = 'add' )
self._add_quick_namespace_button.Bind( wx.EVT_BUTTON, self.EventAddQuickNamespace )
self._edit_quick_namespace_button = wx.Button( self._quick_namespaces_panel, label = 'edit' )
self._edit_quick_namespace_button.Bind( wx.EVT_BUTTON, self.EventEditQuickNamespace )
self._delete_quick_namespace_button = wx.Button( self._quick_namespaces_panel, label = 'delete' )
self._delete_quick_namespace_button.Bind( wx.EVT_BUTTON, self.EventDeleteQuickNamespace )
self._regex_shortcuts = ClientGUICommon.RegexButton( self._quick_namespaces_panel )
self._regex_link = wx.HyperlinkCtrl( self._quick_namespaces_panel, id = -1, label = 'a good regex introduction', url = 'http://www.aivosto.com/vbtips/regex.html' )
#
self._regexes_panel = ClientGUICommon.StaticBox( self, 'regexes' )
self._regexes = wx.ListBox( self._regexes_panel )
self._regexes.Bind( wx.EVT_LISTBOX_DCLICK, self.EventRemoveRegex )
self._regex_box = wx.TextCtrl( self._regexes_panel, style=wx.TE_PROCESS_ENTER )
self._regex_box.Bind( wx.EVT_TEXT_ENTER, self.EventAddRegex )
#
self._tags_panel = ClientGUICommon.StaticBox( self, 'tags for all' )
self._tags = ClientGUICommon.TagsBoxFlat( self._tags_panel, self.TagRemoved )
self._tag_box = ClientGUICommon.AutoCompleteDropdownTagsWrite( self._tags_panel, self.AddTag, HC.LOCAL_FILE_SERVICE_IDENTIFIER, service_identifier )
#
self._single_tags_panel = ClientGUICommon.StaticBox( self, 'tags just for this file' )
self._paths_to_single_tags = collections.defaultdict( list )
self._single_tags = ClientGUICommon.TagsBoxFlat( self._single_tags_panel, self.SingleTagRemoved )
self._single_tag_box = ClientGUICommon.AutoCompleteDropdownTagsWrite( self._single_tags_panel, self.AddTagSingle, HC.LOCAL_FILE_SERVICE_IDENTIFIER, service_identifier )
self._single_tag_box.Disable()
for path in self._paths:
tags = self._GetTags( path )
tags_string = ', '.join( tags )
self._paths_list.Append( ( path, tags_string ), ( path, tags ) )
def InitialisePanel():
button_box = wx.BoxSizer( wx.HORIZONTAL )
button_box.AddF( self._add_quick_namespace_button, FLAGS_MIXED )
button_box.AddF( self._edit_quick_namespace_button, FLAGS_MIXED )
button_box.AddF( self._delete_quick_namespace_button, FLAGS_MIXED )
self._quick_namespaces_panel.AddF( self._quick_namespaces_list, FLAGS_EXPAND_PERPENDICULAR )
self._quick_namespaces_panel.AddF( button_box, FLAGS_BUTTON_SIZERS )
self._quick_namespaces_panel.AddF( self._regex_shortcuts, FLAGS_LONE_BUTTON )
self._quick_namespaces_panel.AddF( self._regex_link, FLAGS_LONE_BUTTON )
self._regexes_panel.AddF( self._regexes, FLAGS_EXPAND_BOTH_WAYS )
self._regexes_panel.AddF( self._regex_box, FLAGS_EXPAND_PERPENDICULAR )
self._tags_panel.AddF( self._tags, FLAGS_EXPAND_BOTH_WAYS )
self._tags_panel.AddF( self._tag_box, FLAGS_EXPAND_PERPENDICULAR )
self._single_tags_panel.AddF( self._single_tags, FLAGS_EXPAND_BOTH_WAYS )
self._single_tags_panel.AddF( self._single_tag_box, FLAGS_EXPAND_PERPENDICULAR )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( self._quick_namespaces_panel, FLAGS_EXPAND_BOTH_WAYS )
hbox.AddF( self._regexes_panel, FLAGS_EXPAND_BOTH_WAYS )
hbox.AddF( self._tags_panel, FLAGS_EXPAND_BOTH_WAYS )
hbox.AddF( self._single_tags_panel, FLAGS_EXPAND_BOTH_WAYS )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._paths_list, FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( hbox, FLAGS_EXPAND_SIZER_BOTH_WAYS )
self.SetSizer( vbox )
wx.Panel.__init__( self, parent )
self._service_identifier = service_identifier
self._paths = paths
InitialiseControls()
InitialisePanel()
def _GetTags( self, path ):
tags = []
tags.extend( self._tags.GetTags() )
for regex in self._regexes.GetStrings():
try:
m = re.search( regex, path )
if m is not None:
match = m.group()
if len( match ) > 0: tags.append( match )
except: pass
for ( namespace, regex ) in self._quick_namespaces_list.GetClientData():
try:
m = re.search( regex, path )
if m is not None:
match = m.group()
if len( match ) > 0: tags.append( namespace + ':' + match )
except: pass
if path in self._paths_to_single_tags: tags.extend( self._paths_to_single_tags[ path ] )
tags = [ HC.CleanTag( tag ) for tag in tags ]
return tags
def _RefreshFileList( self ):
for ( index, ( path, old_tags ) ) in enumerate( self._paths_list.GetClientData() ):
# when doing regexes, make sure not to include '' results, same for system: and - started tags.
tags = self._GetTags( path )
if tags != old_tags:
tags_string = ', '.join( tags )
self._paths_list.UpdateRow( index, ( path, tags_string ), ( path, tags ) )
def AddTag( self, tag ):
if tag is not None:
self._tags.AddTag( tag )
self._tag_box.Clear()
self._RefreshFileList()
def AddTagSingle( self, tag ):
if tag is not None:
self._single_tags.AddTag( tag )
self._single_tag_box.Clear()
indices = self._paths_list.GetAllSelected()
for index in indices:
( path, old_tags ) = self._paths_list.GetClientData( index )
if tag not in self._paths_to_single_tags[ path ]: self._paths_to_single_tags[ path ].append( tag )
self._RefreshFileList() # make this more clever
def EventAddRegex( self, event ):
regex = self._regex_box.GetValue()
if regex != '':
self._regexes.Append( regex )
self._regex_box.Clear()
self._RefreshFileList()
def EventAddQuickNamespace( self, event ):
with DialogInputNamespaceRegex( self ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
( namespace, regex ) = dlg.GetInfo()
self._quick_namespaces_list.Append( ( namespace, regex ), ( namespace, regex ) )
self._RefreshFileList()
def EventDeleteQuickNamespace( self, event ):
self._quick_namespaces_list.RemoveAllSelected()
self._RefreshFileList()
def EventEditQuickNamespace( self, event ):
for index in self._quick_namespaces_list.GetAllSelected():
( namespace, regex ) = self._quick_namespaces_list.GetClientData( index = index )
with DialogInputNamespaceRegex( self, namespace = namespace, regex = regex ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
( namespace, regex ) = dlg.GetInfo()
self._quick_namespaces_list.UpdateRow( index, ( namespace, regex ), ( namespace, regex ) )
self._RefreshFileList()
def EventItemSelected( self, event ):
single_tags = set()
indices = self._paths_list.GetAllSelected()
if len( indices ) > 0:
for index in indices:
path = self._paths_list.GetClientData( index )[0]
if path in self._paths_to_single_tags: single_tags.update( self._paths_to_single_tags[ path ] )
self._single_tag_box.Enable()
else: self._single_tag_box.Disable()
single_tags = list( single_tags )
single_tags.sort()
self._single_tags.SetTags( single_tags )
def EventRemoveRegex( self, event ):
selection = self._regexes.GetSelection()
if selection != wx.NOT_FOUND:
if len( self._regex_box.GetValue() ) == 0: self._regex_box.SetValue( self._regexes.GetString( selection ) )
self._regexes.Delete( selection )
self._RefreshFileList()
def GetServiceIdentifier( self ): return self._service_identifier
# this prob needs to be made cleverer if I do the extra column
def GetTags( self, path ): return self._GetTags( path )
def SetTagBoxFocus( self ): self._tag_box.SetFocus()
def SingleTagRemoved( self, tag ):
indices = self._paths_list.GetAllSelected()
for index in indices:
( path, old_tags ) = self._paths_list.GetClientData( index )
if tag in self._paths_to_single_tags[ path ]: self._paths_to_single_tags[ path ].remove( tag )
self._RefreshFileList()
def TagRemoved( self, tag ): self._RefreshFileList()
class DialogProgress( Dialog ):
def __init__( self, parent, job_key, cancel_event = None ):
def InitialiseControls():
self._status = wx.StaticText( self, label = 'initialising', style = wx.ALIGN_CENTER | wx.ST_NO_AUTORESIZE )
self._gauge = ClientGUICommon.Gauge( self, range = 100 )
self._time_taken_so_far = wx.StaticText( self, label = 'initialising', style = wx.ALIGN_CENTER | wx.ST_NO_AUTORESIZE )
self._time_left = wx.StaticText( self, label = 'initialising', style = wx.ALIGN_CENTER | wx.ST_NO_AUTORESIZE )
if cancel_event is not None:
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label = 'cancel' )
self._cancel.Bind( wx.EVT_BUTTON, self.EventCancel )
self._time_started = None
def InitialisePanel():
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._status, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._gauge, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._time_taken_so_far, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._time_left, FLAGS_EXPAND_PERPENDICULAR )
if cancel_event is not None: vbox.AddF( self._cancel, FLAGS_LONE_BUTTON )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
if x < 640: x = 640
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'progress', style = wx.SYSTEM_MENU | wx.CAPTION | wx.RESIZE_BORDER, position = 'center' )
self._job_key = job_key
self._cancel_event = cancel_event
InitialiseControls()
InitialisePanel()
self.Bind( wx.EVT_TIMER, self.EventTimer, id = ID_TIMER_UPDATE )
self._timer = wx.Timer( self, id = ID_TIMER_UPDATE )
self._timer.Start( 1000, wx.TIMER_CONTINUOUS )
HC.pubsub.sub( self, 'Update', 'progress_update' )
def _DisplayTimes( self ):
value = self._gauge.GetValue()
range = self._gauge.GetRange()
if self._time_started is not None:
time_taken_so_far = time.clock() - self._time_started
if value > 1: time_left = HC.ConvertTimeToPrettyTime( time_taken_so_far * ( float( range - value ) / float( value ) ) )
else: time_left = 'unknown'
self._time_taken_so_far.SetLabel( 'elapsed: ' + HC.ConvertTimeToPrettyTime( time_taken_so_far ) )
self._time_left.SetLabel( 'remaining: ' + time_left )
def EventCancel( self, event ):
self._cancel.Disable()
self._cancel_event.set()
def EventTimer( self, event ):
value = self._gauge.GetValue()
range = self._gauge.GetRange()
if value == range: self.EndModal( wx.OK )
else: self._DisplayTimes()
def Update( self, job_key, index, range, status ):
if job_key == self._job_key:
if self._time_started is None: self._time_started = time.clock()
if range != self._gauge.GetRange(): self._gauge.SetRange( range )
self._gauge.SetValue( index )
self._status.SetLabel( status )
self._DisplayTimes()
class DialogRegisterService( Dialog ):
def __init__( self, parent ):
def InitialiseControls():
self._address = wx.TextCtrl( self, value = 'hostname:port' )
self._registration_key = wx.TextCtrl( self, value = 'r0000000000000000000000000000000000000000000000000000000000000000' )
self._register = wx.Button( self, label = 'register' )
self._register.Bind( wx.EVT_BUTTON, self.EventRegister )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label = 'cancel' )
def InitialisePanel():
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( wx.StaticText( self, label = 'Please fill out the forms with the appropriate information for your service.' ), FLAGS_EXPAND_PERPENDICULAR )
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 1, 1 )
gridbox.AddF( wx.StaticText( self, label='address' ), FLAGS_MIXED )
gridbox.AddF( self._address, FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( wx.StaticText( self, label='registration key' ), FLAGS_MIXED )
gridbox.AddF( self._registration_key, FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( gridbox, FLAGS_EXPAND_SIZER_BOTH_WAYS )
buttonbox = wx.BoxSizer( wx.HORIZONTAL )
buttonbox.AddF( self._register, FLAGS_MIXED )
buttonbox.AddF( self._cancel, FLAGS_MIXED )
vbox.AddF( buttonbox, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'register account', position = 'center' )
InitialiseControls()
InitialisePanel()
self._register = False
def EventRegister( self, event ):
address = self._address.GetValue()
try:
( host, port ) = address.split( ':' )
port = int( port )
except:
wx.MessageBox( 'Could not parse that address!' )
return
registration_key_encoded = self._registration_key.GetValue()
if registration_key_encoded[0] == 'r': registration_key_encoded = registration_key_encoded[1:]
try: registration_key = registration_key_encoded.decode( 'hex' )
except:
wx.MessageBox( 'Could not parse that registration key!' )
return
connection = HC.AdvancedHTTPConnection( host = host, port = port )
headers = {}
headers[ 'Authorization' ] = 'hydrus_network ' + registration_key.encode( 'hex' )
try: access_key = connection.request( 'GET', '/access_key', headers = headers )
except Exception as e:
wx.MessageBox( unicode( e ) )
return
self._credentials = CC.Credentials( host, port, access_key )
self.EndModal( wx.ID_OK )
def GetCredentials( self ): return self._credentials
class DialogSelectBooru( Dialog ):
def __init__( self, parent ):
def InitialiseControls():
boorus = wx.GetApp().Read( 'boorus' )
self._boorus = wx.ListBox( self, style = wx.LB_SORT )
self._boorus.Bind( wx.EVT_LISTBOX_DCLICK, self.EventSelect )
self._boorus.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
for booru in boorus: self._boorus.Append( booru.GetName(), booru )
def InitialisePanel():
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._boorus, FLAGS_EXPAND_PERPENDICULAR )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
if x < 320: x = 320
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'select booru' )
InitialiseControls()
InitialisePanel()
def EventKeyDown( self, event ):
if event.KeyCode == wx.WXK_SPACE:
selection = self._boorus.GetSelection()
if selection != wx.NOT_FOUND: self.EndModal( wx.ID_OK )
elif event.KeyCode == wx.WXK_ESCAPE: self.EndModal( wx.ID_CANCEL )
else: event.Skip()
def EventSelect( self, event ): self.EndModal( wx.ID_OK )
def GetBooru( self ): return self._boorus.GetClientData( self._boorus.GetSelection() )
class DialogSelectImageboard( Dialog ):
def __init__( self, parent ):
def InitialiseControls():
self._tree = wx.TreeCtrl( self )
self._tree.Bind( wx.EVT_TREE_ITEM_ACTIVATED, self.EventSelect )
all_imageboards = wx.GetApp().Read( 'imageboards' )
root_item = self._tree.AddRoot( 'all sites' )
for ( site, imageboards ) in all_imageboards:
site_item = self._tree.AppendItem( root_item, site )
for imageboard in imageboards:
name = imageboard.GetName()
self._tree.AppendItem( site_item, name, data = wx.TreeItemData( imageboard ) )
self._tree.Expand( root_item )
def InitialisePanel():
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._tree, FLAGS_EXPAND_BOTH_WAYS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
if x < 320: x = 320
if y < 640: y = 640
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'select imageboard' )
InitialiseControls()
InitialisePanel()
def EventSelect( self, event ):
item = self._tree.GetSelection()
data_object = self._tree.GetItemData( item )
if data_object is None: self._tree.Toggle( item )
else: self.EndModal( wx.ID_OK )
def GetImageboard( self ): return self._tree.GetItemData( self._tree.GetSelection() ).GetData()
class DialogSelectFromListOfStrings( Dialog ):
def __init__( self, parent, title, list_of_strings ):
def InitialiseControls():
self._strings = wx.ListBox( self, choices = list_of_strings )
self._strings.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
self._strings.Bind( wx.EVT_LISTBOX_DCLICK, self.EventSelect )
def InitialisePanel():
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._strings, FLAGS_EXPAND_PERPENDICULAR )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
if x < 320: x = 320
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, title )
InitialiseControls()
InitialisePanel()
def EventKeyDown( self, event ):
if event.KeyCode == wx.WXK_SPACE:
selection = self._strings.GetSelection()
if selection != wx.NOT_FOUND: self.EndModal( wx.ID_OK )
elif event.KeyCode == wx.WXK_ESCAPE: self.EndModal( wx.ID_CANCEL )
else: event.Skip()
def EventSelect( self, event ): self.EndModal( wx.ID_OK )
def GetString( self ): return self._strings.GetStringSelection()
class DialogSelectLocalFiles( Dialog ):
def __init__( self, parent, paths = [] ):
def InitialiseControls():
self._paths_list = ClientGUICommon.SaneListCtrl( self, 480, [ ( 'path', -1 ), ( 'guessed mime', 110 ), ( 'size', 60 ) ] )
self._paths_list.SetMinSize( ( 780, 360 ) )
self._add_button = wx.Button( self, label='Import now' )
self._add_button.Bind( wx.EVT_BUTTON, self.EventOK )
self._add_button.SetForegroundColour( ( 0, 128, 0 ) )
self._tag_button = wx.Button( self, label = 'Add tags before importing' )
self._tag_button.Bind( wx.EVT_BUTTON, self.EventTags )
self._tag_button.SetForegroundColour( ( 0, 128, 0 ) )
self._close_button = wx.Button( self, id = wx.ID_CANCEL, label='Cancel' )
self._close_button.Bind( wx.EVT_BUTTON, self.EventCancel )
self._close_button.SetForegroundColour( ( 128, 0, 0 ) )
self._advanced_import_options = ClientGUICommon.AdvancedImportOptions( self )
self._add_files_button = wx.Button( self, label='Add Files' )
self._add_files_button.Bind( wx.EVT_BUTTON, self.EventAddPaths )
self._add_folder_button = wx.Button( self, label='Add Folder' )
self._add_folder_button.Bind( wx.EVT_BUTTON, self.EventAddFolder )
self._remove_files_button = wx.Button( self, label='Remove Files' )
self._remove_files_button.Bind( wx.EVT_BUTTON, self.EventRemovePaths )
def InitialisePanel():
file_buttons = wx.BoxSizer( wx.HORIZONTAL )
file_buttons.AddF( ( 20, 0 ), FLAGS_NONE )
file_buttons.AddF( self._add_files_button, FLAGS_MIXED )
file_buttons.AddF( self._add_folder_button, FLAGS_MIXED )
file_buttons.AddF( self._remove_files_button, FLAGS_MIXED )
buttons = wx.BoxSizer( wx.HORIZONTAL )
buttons.AddF( self._add_button, FLAGS_MIXED )
buttons.AddF( self._tag_button, FLAGS_MIXED )
buttons.AddF( self._close_button, FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._paths_list, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( file_buttons, FLAGS_BUTTON_SIZERS )
vbox.AddF( self._advanced_import_options, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( ( 0, 5 ), FLAGS_NONE )
vbox.AddF( buttons, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'importing files' )
self.SetDropTarget( ClientGUICommon.FileDropTarget( self._AddPathsToList ) )
InitialiseControls()
InitialisePanel()
self._AddPathsToList( paths )
def _AddPathsToList( self, paths ):
good_paths = CC.ParseImportablePaths( paths )
odd_paths = False
for path in good_paths:
mime = HC.GetMimeFromPath( path )
if mime in HC.ALLOWED_MIMES:
info = os.lstat( path )
size = info[6]
if size > 0:
pretty_size = HC.ConvertIntToBytes( size )
self._paths_list.Append( ( path, HC.mime_string_lookup[ mime ], pretty_size ), ( path, HC.mime_string_lookup[ mime ], size ) )
else: odd_paths = True
if odd_paths: wx.MessageBox( 'At present hydrus can handle only jpegs, pngs, bmps, gifs, swfs, flvs and pdfs. The other files have not been added.' )
def _GetPaths( self ): return [ row[0] for row in self._paths_list.GetClientData() ]
def EventAddPaths( self, event ):
with wx.FileDialog( self, 'Select the files to add.', style=wx.FD_MULTIPLE ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
paths = dlg.GetPaths()
self._AddPathsToList( paths )
def EventAddFolder( self, event ):
with wx.DirDialog( self, 'Select a folder to add.', style=wx.DD_DIR_MUST_EXIST ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
self._AddPathsToList( ( path, ) )
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventOK( self, event ):
paths = self._GetPaths()
if len( paths ) > 0:
advanced_import_options = self._advanced_import_options.GetInfo()
HC.pubsub.pub( 'new_hdd_import', paths, advanced_import_options = advanced_import_options )
self.EndModal( wx.ID_OK )
def EventRemovePaths( self, event ): self._paths_list.RemoveAllSelected()
def EventTags( self, event ):
try:
paths = self._GetPaths()
if len( paths ) > 0:
advanced_import_options = self._advanced_import_options.GetInfo()
with DialogPathsToTagsRegex( self, paths ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
paths_to_tags = dlg.GetInfo()
HC.pubsub.pub( 'new_hdd_import', paths, advanced_import_options = advanced_import_options, paths_to_tags = paths_to_tags )
self.EndModal( wx.ID_OK )
except: wx.MessageBox( traceback.format_exc() )
class DialogSetupCustomFilterActions( Dialog ):
def __init__( self, parent ):
def InitialiseControls():
self._actions = ClientGUICommon.SaneListCtrl( self, 480, [ ( 'modifier', 150 ), ( 'key', 150 ), ( 'service', -1 ), ( 'action', 250 ) ] )
self._actions.SetMinSize( ( 780, 360 ) )
self._favourites = wx.ListBox( self )
self._favourites.Bind( wx.EVT_LISTBOX, self.EventSelectFavourite )
self._save_favourite = wx.Button( self, label = 'save' )
self._save_favourite.Bind( wx.EVT_BUTTON, self.EventSaveFavourite )
self._save_new_favourite = wx.Button( self, label = 'save as' )
self._save_new_favourite.Bind( wx.EVT_BUTTON, self.EventSaveNewFavourite )
self._delete_favourite = wx.Button( self, label = 'delete' )
self._delete_favourite.Bind( wx.EVT_BUTTON, self.EventDeleteFavourite )
self._current_actions_selection = wx.NOT_FOUND
default_actions = self._GetDefaultActions()
self._favourites.Append( 'default', default_actions )
favourites = wx.GetApp().Read( 'favourite_custom_filter_actions' )
if 'previous' in favourites: self._favourites.Append( 'previous', favourites[ 'previous' ] )
else: self._favourites.Append( 'previous', default_actions )
for ( name, actions ) in favourites.items():
if name != 'previous': self._favourites.Append( name, actions )
self._favourites.Select( 1 ) # previous
self._add = wx.Button( self, label='add' )
self._add.Bind( wx.EVT_BUTTON, self.EventAdd )
self._add.SetForegroundColour( ( 0, 128, 0 ) )
self._edit = wx.Button( self, label='edit' )
self._edit.Bind( wx.EVT_BUTTON, self.EventEdit )
self._remove = wx.Button( self, label='remove' )
self._remove.Bind( wx.EVT_BUTTON, self.EventRemove )
self._remove.SetForegroundColour( ( 128, 0, 0 ) )
self._ok = wx.Button( self, label = 'ok' )
self._ok.Bind( wx.EVT_BUTTON, self.EventOK )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label='cancel' )
self._cancel.Bind( wx.EVT_BUTTON, self.EventCancel )
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
def InitialisePanel():
action_buttons = wx.BoxSizer( wx.HORIZONTAL )
action_buttons.AddF( self._add, FLAGS_MIXED )
action_buttons.AddF( self._edit, FLAGS_MIXED )
action_buttons.AddF( self._remove, FLAGS_MIXED )
buttons = wx.BoxSizer( wx.HORIZONTAL )
buttons.AddF( self._ok, FLAGS_MIXED )
buttons.AddF( self._cancel, FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._actions, FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( action_buttons, FLAGS_BUTTON_SIZERS )
vbox.AddF( buttons, FLAGS_BUTTON_SIZERS )
button_hbox = wx.BoxSizer( wx.HORIZONTAL )
button_hbox.AddF( self._save_favourite, FLAGS_MIXED )
button_hbox.AddF( self._save_new_favourite, FLAGS_MIXED )
button_hbox.AddF( self._delete_favourite, FLAGS_MIXED )
f_vbox = wx.BoxSizer( wx.VERTICAL )
f_vbox.AddF( self._favourites, FLAGS_EXPAND_BOTH_WAYS )
f_vbox.AddF( button_hbox, FLAGS_BUTTON_SIZERS )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( f_vbox, FLAGS_EXPAND_PERPENDICULAR )
hbox.AddF( vbox, FLAGS_EXPAND_BOTH_WAYS )
self.SetSizer( hbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'setup custom filter' )
InitialiseControls()
InitialisePanel()
wx.CallAfter( self.EventSelectFavourite, None )
def _GetDefaultActions( self ):
default_actions = []
for ( modifier, key_dict ) in self._options[ 'shortcuts' ].items():
for ( key, action ) in key_dict.items():
if action in ( 'manage_tags', 'manage_ratings', 'archive', 'inbox', 'fullscreen_switch', 'frame_back', 'frame_next', 'previous', 'next', 'first', 'last', 'pan_up', 'pan_down', 'pan_left', 'pan_right' ):
service_identifier = None
default_actions.append( ( modifier, key, service_identifier, action ) )
( modifier, key, service_identifier, action ) = ( wx.ACCEL_NORMAL, wx.WXK_DELETE, None, 'delete' )
default_actions.append( ( modifier, key, service_identifier, action ) )
return default_actions
def _IsUntouchableFavouriteSelection( self, selection ):
name = self._favourites.GetString( selection )
if name in ( 'previous', 'default' ): return True
else: return False
def _SortListCtrl( self ): self._actions.SortListItems( 3 )
def EventAdd( self, event ):
with DialogInputCustomFilterAction( self ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
( pretty_tuple, data_tuple ) = dlg.GetInfo()
self._actions.Append( pretty_tuple, data_tuple )
self._SortListCtrl()
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
def EventDeleteFavourite( self, event ):
selection = self._favourites.GetSelection()
if selection != wx.NOT_FOUND:
if not self._IsUntouchableFavouriteSelection( selection ):
self._favourites.Delete( selection )
def EventEdit( self, event ):
for index in self._actions.GetAllSelected():
( modifier, key, service_identifier, action ) = self._actions.GetClientData( index )
with DialogInputCustomFilterAction( self, modifier = modifier, key = key, service_identifier = service_identifier, action = action ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
( pretty_tuple, data_tuple ) = dlg.GetInfo()
self._actions.UpdateRow( index, pretty_tuple, data_tuple )
self._SortListCtrl()
def EventOK( self, event ):
favourites = {}
for i in range( self._favourites.GetCount() ):
name = self._favourites.GetString( i )
if name == 'default': continue
actions = self._favourites.GetClientData( i )
favourites[ name ] = actions
favourites[ 'previous' ] = self._actions.GetClientData() # overwrite
wx.GetApp().Write( 'favourite_custom_filter_actions', favourites )
self.EndModal( wx.ID_OK )
def EventRemove( self, event ): self._actions.RemoveAllSelected()
def EventSaveFavourite( self, event ):
selection = self._favourites.GetSelection()
if selection != wx.NOT_FOUND:
if not self._IsUntouchableFavouriteSelection( selection ):
actions = self._actions.GetClientData()
self._favourites.SetClientData( selection, actions )
def EventSaveNewFavourite( self, event ):
existing_names = { self._favourites.GetString( i ) for i in range( self._favourites.GetCount() ) }
with wx.TextEntryDialog( self, 'Enter name for these favourite actions' ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
name = dlg.GetValue()
if name == '': return
while name in existing_names: name += str( random.randint( 0, 9 ) )
actions = self._actions.GetClientData()
self._favourites.Append( name, actions )
def EventSelectFavourite( self, event ):
selection = self._favourites.GetSelection()
if selection != wx.NOT_FOUND:
if selection != self._current_actions_selection:
self._actions.DeleteAllItems()
name = self._favourites.GetString( selection )
if name in ( 'default', 'previous' ):
self._save_favourite.Disable()
self._delete_favourite.Disable()
else:
self._save_favourite.Enable()
self._delete_favourite.Enable()
actions = self._favourites.GetClientData( selection )
for ( modifier, key, service_identifier, action ) in actions:
( pretty_modifier, pretty_key, pretty_action ) = HC.ConvertShortcutToPrettyShortcut( modifier, key, action )
if service_identifier is None: pretty_service_identifier = ''
else: pretty_service_identifier = service_identifier.GetName()
self._actions.Append( ( pretty_modifier, pretty_key, pretty_service_identifier, pretty_action ), ( modifier, key, service_identifier, action ) )
self._SortListCtrl()
def GetActions( self ):
raw_data = self._actions.GetClientData()
actions = collections.defaultdict( dict )
for ( modifier, key, service_identifier, action ) in raw_data: actions[ modifier ][ key ] = ( service_identifier, action )
return actions
class DialogSetupExport( Dialog ):
ID_HASH = 0
ID_TAGS = 1
ID_NN_TAGS = 2
ID_NAMESPACE = 3
ID_TAG = 4
def __init__( self, parent, flat_media ):
def InitialiseControls():
self._tags_box = ClientGUICommon.TagsBoxCPPWithSorter( self, self._page_key )
self._tags_box.SetMinSize( ( 220, 300 ) )
self._paths = ClientGUICommon.SaneListCtrl( self, 480, [ ( 'number', 60 ), ( 'mime', 70 ), ( 'path', -1 ) ] )
self._paths.Bind( wx.EVT_LIST_ITEM_SELECTED, self.EventSelectPath )
self._paths.Bind( wx.EVT_LIST_ITEM_DESELECTED, self.EventSelectPath )
self._paths.SetMinSize( ( 740, 360 ) )
for ( i, media ) in enumerate( flat_media ):
mime = media.GetMime()
pretty_tuple = ( str( i + 1 ), HC.mime_string_lookup[ mime ], '' )
data_tuple = ( ( i, media ), mime, '' )
self._paths.Append( pretty_tuple, data_tuple )
self._directory_picker = wx.DirPickerCtrl( self )
if self._options[ 'export_path' ] is not None: self._directory_picker.SetPath( HC.ConvertPortablePathToAbsPath( self._options[ 'export_path' ] ) )
self._directory_picker.Bind( wx.EVT_DIRPICKER_CHANGED, self.EventRecalcPaths )
self._open_location = wx.Button( self, label = 'open this location' )
self._open_location.Bind( wx.EVT_BUTTON, self.EventOpenLocation )
self._pattern = wx.TextCtrl( self )
self._pattern.SetValue( '{hash}' )
self._update = wx.Button( self, label = 'update' )
self._update.Bind( wx.EVT_BUTTON, self.EventRecalcPaths )
self._examples = wx.Button( self, label = 'pattern shortcuts' )
self._examples.Bind( wx.EVT_BUTTON, self.EventPatternShortcuts )
self._export = wx.Button( self, label = 'export' )
self._export.Bind( wx.EVT_BUTTON, self.EventExport )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label='close' )
def InitialisePanel():
top_hbox = wx.BoxSizer( wx.HORIZONTAL )
top_hbox.AddF( self._tags_box, FLAGS_EXPAND_PERPENDICULAR )
top_hbox.AddF( self._paths, FLAGS_EXPAND_BOTH_WAYS )
destination_hbox = wx.BoxSizer( wx.HORIZONTAL )
destination_hbox.AddF( self._directory_picker, FLAGS_EXPAND_BOTH_WAYS )
destination_hbox.AddF( self._open_location, FLAGS_MIXED )
pattern_hbox = wx.BoxSizer( wx.HORIZONTAL )
pattern_hbox.AddF( self._pattern, FLAGS_EXPAND_BOTH_WAYS )
pattern_hbox.AddF( self._update, FLAGS_MIXED )
pattern_hbox.AddF( self._examples, FLAGS_MIXED )
pattern_hbox.AddF( self._export, FLAGS_MIXED )
button_hbox = wx.BoxSizer( wx.HORIZONTAL )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( top_hbox, FLAGS_EXPAND_SIZER_BOTH_WAYS )
vbox.AddF( destination_hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( pattern_hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( self._cancel, FLAGS_LONE_BUTTON )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'setup export' )
self._page_key = os.urandom( 32 )
InitialiseControls()
InitialisePanel()
wx.CallAfter( self.EventSelectPath, None )
wx.CallAfter( self.EventRecalcPaths, None )
self.Bind( wx.EVT_MENU, self.EventMenu )
def _GetPath( self, media, terms ):
directory = self._directory_picker.GetPath()
filename = ''
for ( term_type, term ) in terms:
tags = media.GetTags()
if term_type == 'string': filename += term
elif term_type == 'namespace':
tags = tags.GetNamespaceSlice( ( term, ) )
filename += ', '.join( [ tag.split( ':' )[1] for tag in tags ] )
elif term_type == 'predicate':
if term in ( 'tags', 'nn tags' ):
( current, deleted, pending, petitioned ) = tags.GetUnionCDPP()
tags = list( current.union( pending ) )
if term == 'nn tags': tags = [ tag for tag in tags if ':' not in tag ]
else: tags = [ tag if ':' not in tag else tag.split( ':' )[1] for tag in tags ]
tags.sort()
filename += ', '.join( tags )
elif term == 'hash':
hash = media.GetHash()
filename += hash.encode( 'hex' )
elif term_type == 'tag':
if ':' in term: term = term.split( ':' )[1]
if tags.HasTag( term ): filename += term
mime = media.GetMime()
ext = HC.mime_ext_lookup[ mime ]
return directory + os.path.sep + filename + ext
def _RecalcPaths( self ):
pattern = self._pattern.GetValue()
try:
terms = [ ( 'string', pattern ) ]
new_terms = []
for ( term_type, term ) in terms:
if term_type == 'string':
while '[' in term:
( pre, term ) = term.split( '[', 1 )
( namespace, term ) = term.split( ']', 1 )
new_terms.append( ( 'string', pre ) )
new_terms.append( ( 'namespace', namespace ) )
new_terms.append( ( term_type, term ) )
terms = new_terms
new_terms = []
for ( term_type, term ) in terms:
if term_type == 'string':
while '{' in term:
( pre, term ) = term.split( '{', 1 )
( predicate, term ) = term.split( '}', 1 )
new_terms.append( ( 'string', pre ) )
new_terms.append( ( 'predicate', predicate ) )
new_terms.append( ( term_type, term ) )
terms = new_terms
new_terms = []
for ( term_type, term ) in terms:
if term_type == 'string':
while '(' in term:
( pre, term ) = term.split( '(', 1 )
( tag, term ) = term.split( ')', 1 )
new_terms.append( ( 'string', pre ) )
new_terms.append( ( 'tag', tag ) )
new_terms.append( ( term_type, term ) )
terms = new_terms
except: raise Exception( 'Could not parse that pattern!' )
all_paths = set()
for ( index, ( ( ordering_index, media ), mime, old_path ) ) in enumerate( self._paths.GetClientData() ):
path = self._GetPath( media, terms )
if path in all_paths:
i = 1
while self._GetPath( media, terms + [ ( 'string', str( i ) ) ] ) in all_paths: i += 1
path = self._GetPath( media, terms + [ ( 'string', str( i ) ) ] )
all_paths.add( path )
if path != old_path:
mime = media.GetMime()
self._paths.UpdateRow( index, ( str( ordering_index + 1 ), HC.mime_string_lookup[ mime ], path ), ( ( ordering_index, media ), mime, path ) )
def EventExport( self, event ):
try: self._RecalcPaths()
except Exception as e:
wx.MessageBox( unicode( e ) )
return
for ( ( ordering_index, media ), mime, path ) in self._paths.GetClientData():
try:
hash = media.GetHash()
file = wx.GetApp().Read( 'file', hash )
with open( path, 'wb' ) as f: f.write( file )
except:
wx.MessageBox( 'Encountered a problem while attempting to export file with index ' + str( ordering_index + 1 ) + '.' + os.linesep + + os.linesep + traceback.format_exc() )
break
def EventMenu( self, event ):
id = event.GetId()
phrase = None
if id == self.ID_HASH: phrase = r'{hash}'
if id == self.ID_TAGS: phrase = r'{tags}'
if id == self.ID_NN_TAGS: phrase = r'{nn tags}'
if id == self.ID_NAMESPACE: phrase = r'[...]'
if id == self.ID_TAG: phrase = r'(...)'
else: event.Skip()
if phrase is not None:
if wx.TheClipboard.Open():
data = wx.TextDataObject( phrase )
wx.TheClipboard.SetData( data )
wx.TheClipboard.Close()
else: wx.MessageBox( 'I could not get permission to access the clipboard.' )
def EventPatternShortcuts( self, event ):
menu = wx.Menu()
menu.Append( -1, 'click on a phrase to copy to clipboard' )
menu.AppendSeparator()
menu.Append( self.ID_HASH, r'the file\'s hash - {hash}' )
menu.Append( self.ID_TAGS, r'all the file\'s tags - {tags}' )
menu.Append( self.ID_NN_TAGS, r'all the file\'s non-namespaced tags - {nn tags}' )
menu.AppendSeparator()
menu.Append( self.ID_NAMESPACE, r'all instances of a particular namespace - [...]' )
menu.AppendSeparator()
menu.Append( self.ID_TAG, r'a particular tag, if the file has it - (...)' )
self.PopupMenu( menu )
menu.Destroy()
def EventOpenLocation( self, event ):
directory = self._directory_picker.GetPath()
if directory is not None and directory != '':
try:
if 'Windows' in os.environ.get( 'os' ): subprocess.Popen( [ 'explorer', directory ] )
else: subprocess.Popen( [ 'explorer', directory ] )
except: wx.MessageBox( 'Could not open that location!' )
def EventRecalcPaths( self, event ):
try: self._RecalcPaths()
except Exception as e: wx.MessageBox( unicode( e ) )
def EventSelectPath( self, event ):
indices = self._paths.GetAllSelected()
if len( indices ) == 0:
all_media = [ media for ( ( ordering_index, media ), mime, old_path ) in self._paths.GetClientData() ]
else:
all_media = [ media for ( ( ordering_index, media ), mime, old_path ) in [ self._paths.GetClientData( index ) for index in indices ] ]
HC.pubsub.pub( 'new_tags_selection', self._page_key, all_media )
class DialogYesNo( Dialog ):
def __init__( self, parent, message, yes_label = 'yes', no_label = 'no' ):
def InitialiseControls():
self._yes = wx.Button( self, label = yes_label )
self._yes.Bind( wx.EVT_BUTTON, self.EventYes )
self._yes.SetForegroundColour( ( 0, 128, 0 ) )
self._no = wx.Button( self, id = wx.ID_CANCEL, label = no_label )
self._no.Bind( wx.EVT_BUTTON, self.EventNo )
self._no.SetForegroundColour( ( 128, 0, 0 ) )
def InitialisePanel():
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( self._yes, FLAGS_SMALL_INDENT )
hbox.AddF( self._no, FLAGS_SMALL_INDENT )
vbox = wx.BoxSizer( wx.VERTICAL )
text = wx.StaticText( self, label = message )
text.Wrap( 480 )
vbox.AddF( text, FLAGS_BIG_INDENT )
vbox.AddF( hbox, FLAGS_BUTTON_SIZERS )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
Dialog.__init__( self, parent, 'are you sure?', position = 'center' )
InitialiseControls()
InitialisePanel()
self.Bind( wx.EVT_CHAR_HOOK, self.EventCharHook )
def EventCharHook( self, event ):
if event.KeyCode == wx.WXK_ESCAPE: self.EndModal( wx.ID_NO )
else: event.Skip()
def EventNo( self, event ): self.EndModal( wx.ID_NO )
def EventYes( self, event ): self.EndModal( wx.ID_YES )