7208 lines
291 KiB
Python
7208 lines
291 KiB
Python
import Crypto.PublicKey.RSA
|
|
import HydrusConstants as HC
|
|
import HydrusEncryption
|
|
import HydrusTags
|
|
import ClientConstants as CC
|
|
import ClientConstantsMessages
|
|
import ClientGUICommon
|
|
import ClientGUIDialogs
|
|
import collections
|
|
import itertools
|
|
import os
|
|
import random
|
|
import re
|
|
import string
|
|
import subprocess
|
|
import time
|
|
import traceback
|
|
import urllib
|
|
import wx
|
|
import yaml
|
|
import zipfile
|
|
|
|
# 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 )
|
|
|
|
class DialogManage4chanPass( ClientGUIDialogs.Dialog ):
|
|
|
|
def __init__( self, parent ):
|
|
|
|
def InitialiseControls():
|
|
|
|
self._token = wx.TextCtrl( self )
|
|
self._pin = wx.TextCtrl( self )
|
|
|
|
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 PopulateControls():
|
|
|
|
self._token.SetValue( token )
|
|
self._pin.SetValue( pin )
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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 )
|
|
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'manage 4chan pass' )
|
|
|
|
( token, pin, self._timeout ) = HC.app.Read( '4chan_pass' )
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
( x, y ) = self.GetEffectiveMinSize()
|
|
|
|
x = max( x, 240 )
|
|
|
|
self.SetInitialSize( ( x, y ) )
|
|
|
|
wx.CallAfter( self._ok.SetFocus )
|
|
|
|
|
|
def _SetStatus( self ):
|
|
|
|
if self._timeout == 0: label = 'not authenticated'
|
|
elif self._timeout < HC.GetNow(): 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()
|
|
|
|
HC.app.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()
|
|
|
|
if token == '' and pin == '':
|
|
|
|
self._timeout = 0
|
|
|
|
else:
|
|
|
|
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.get_connection( url = 'https://sys.4chan.org/', accept_cookies = True )
|
|
|
|
response = connection.request( 'POST', '/auth', headers = headers, body = body )
|
|
|
|
self._timeout = HC.GetNow() + 365 * 24 * 3600
|
|
|
|
|
|
HC.app.Write( '4chan_pass', token, pin, self._timeout )
|
|
|
|
self._SetStatus()
|
|
|
|
except Exception as e:
|
|
wx.MessageBox( traceback.format_exc() )
|
|
wx.MessageBox( HC.u( e ) )
|
|
|
|
|
|
class DialogManageAccountTypes( ClientGUIDialogs.Dialog ):
|
|
|
|
def __init__( self, parent, service_identifier ):
|
|
|
|
def InitialiseControls():
|
|
|
|
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 ) ] )
|
|
|
|
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 PopulateControls():
|
|
|
|
service = HC.app.Read( 'service', service_identifier )
|
|
|
|
connection = service.GetConnection()
|
|
|
|
account_types = connection.Get( 'account_types' )
|
|
|
|
self._titles_to_account_types = {}
|
|
|
|
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 ) )
|
|
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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 )
|
|
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'manage account types' )
|
|
|
|
self._service_identifier = service_identifier
|
|
|
|
self._edit_log = []
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
( x, y ) = self.GetEffectiveMinSize()
|
|
|
|
self.SetInitialSize( ( 980, y ) )
|
|
|
|
wx.CallAfter( self._apply.SetFocus )
|
|
|
|
|
|
def EventAdd( self, event ):
|
|
|
|
try:
|
|
|
|
with ClientGUIDialogs.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( HC.u( 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 ClientGUIDialogs.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 ClientGUIDialogs.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( HC.u( e ) )
|
|
|
|
|
|
|
|
def EventOK( self, event ):
|
|
|
|
try:
|
|
|
|
service = HC.app.Read( 'service', self._service_identifier )
|
|
|
|
connection = service.GetConnection()
|
|
|
|
connection.Post( 'account_types_modification', edit_log = self._edit_log )
|
|
|
|
except Exception as e: wx.MessageBox( HC.u( e ) )
|
|
|
|
self.EndModal( wx.ID_OK )
|
|
|
|
|
|
class DialogManageBoorus( ClientGUIDialogs.Dialog ):
|
|
|
|
def __init__( self, parent ):
|
|
|
|
def InitialiseControls():
|
|
|
|
self._edit_log = []
|
|
|
|
self._boorus = ClientGUICommon.ListBook( self )
|
|
|
|
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 PopulateControls():
|
|
|
|
boorus = HC.app.Read( 'boorus' )
|
|
|
|
for booru in boorus:
|
|
|
|
name = booru.GetName()
|
|
|
|
page_info = ( self._Panel, ( self._boorus, booru ), {} )
|
|
|
|
self._boorus.AddPage( page_info, name )
|
|
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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 )
|
|
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'manage boorus' )
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
self.SetDropTarget( ClientGUICommon.FileDropTarget( self.Import ) )
|
|
|
|
( x, y ) = self.GetEffectiveMinSize()
|
|
|
|
self.SetInitialSize( ( 980, y ) )
|
|
|
|
wx.CallAfter( self._ok.SetFocus )
|
|
|
|
|
|
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( HC.u( 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 HC.o( 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: HC.app.Write( 'update_boorus', self._edit_log )
|
|
|
|
except Exception as e: wx.MessageBox( 'Saving boorus to DB raised this error: ' + HC.u( 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 HC.o( 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 )
|
|
self._search_url.Bind( wx.EVT_TEXT, self.EventHTML )
|
|
|
|
self._search_separator = wx.Choice( self._search_panel, choices = [ '+', '&', '%20' ] )
|
|
self._search_separator.Bind( wx.EVT_CHOICE, self.EventHTML )
|
|
|
|
self._advance_by_page_num = wx.CheckBox( self._search_panel )
|
|
|
|
self._thumb_classname = wx.TextCtrl( self._search_panel )
|
|
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 )
|
|
|
|
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 )
|
|
|
|
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 PopulateControls():
|
|
|
|
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 )
|
|
|
|
|
|
#
|
|
|
|
for ( tag_classname, namespace ) in tag_classnames_to_namespaces.items(): self._tag_classnames_to_namespaces.Append( tag_classname + ' : ' + namespace, ( tag_classname, namespace ) )
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
|
|
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( ClientGUIDialogs.Dialog ):
|
|
|
|
def __init__( self, parent ):
|
|
|
|
def InitialiseControls():
|
|
|
|
self._contacts = ClientGUICommon.ListBook( self )
|
|
|
|
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 PopulateControls():
|
|
|
|
self._edit_log = []
|
|
|
|
( identities, contacts, deletable_names ) = HC.app.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 )
|
|
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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 )
|
|
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'manage contacts' )
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
( x, y ) = self.GetEffectiveMinSize()
|
|
|
|
self.SetInitialSize( ( 980, y ) )
|
|
|
|
self.SetDropTarget( ClientGUICommon.FileDropTarget( self.Import ) )
|
|
|
|
self.EventContactChanged( None )
|
|
|
|
wx.CallAfter( self._ok.SetFocus )
|
|
|
|
|
|
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( HC.u( 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( HC.u( e ) )
|
|
|
|
self.EventAddByContactAddress( event )
|
|
|
|
|
|
|
|
|
|
|
|
def EventAddManually( self, event ):
|
|
|
|
try: self._CheckCurrentContactIsValid()
|
|
except Exception as e:
|
|
|
|
wx.MessageBox( HC.u( 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( HC.u( 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( HC.u( 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 HC.o( 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 HC.o( dlg.GetPath(), 'wb' ) as f: f.write( yaml.safe_dump( contact ) )
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def EventOK( self, event ):
|
|
|
|
try: self._CheckCurrentContactIsValid()
|
|
except Exception as e:
|
|
|
|
wx.MessageBox( HC.u( 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: HC.app.Write( 'update_contacts', self._edit_log )
|
|
|
|
except Exception as e: wx.MessageBox( 'Saving contacts to DB raised this error: ' + HC.u( 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( HC.u( e ) )
|
|
|
|
return
|
|
|
|
|
|
for path in paths:
|
|
|
|
try:
|
|
|
|
with HC.o( 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 ClientGUIDialogs.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 + HC.u( 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 )
|
|
|
|
self._contact_address = wx.TextCtrl( self._contact_panel )
|
|
|
|
self._public_key = wx.TextCtrl( self._contact_panel, style = wx.TE_MULTILINE )
|
|
|
|
|
|
def PopulateControls():
|
|
|
|
self._name.SetValue( name )
|
|
|
|
contact_address = host + ':' + HC.u( 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 not None: self._public_key.SetValue( public_key )
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
|
|
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 + ':' + HC.u( 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 DialogManageImageboards( ClientGUIDialogs.Dialog ):
|
|
|
|
def __init__( self, parent ):
|
|
|
|
def InitialiseControls():
|
|
|
|
self._sites = ClientGUICommon.ListBook( self )
|
|
|
|
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 PopulateControls():
|
|
|
|
self._edit_log = []
|
|
|
|
sites = HC.app.Read( 'imageboards' )
|
|
|
|
for ( name, imageboards ) in sites:
|
|
|
|
page_info = ( self._Panel, ( self._sites, imageboards ), {} )
|
|
|
|
self._sites.AddPage( page_info, name )
|
|
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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 )
|
|
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'manage imageboards' )
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
( x, y ) = self.GetEffectiveMinSize()
|
|
|
|
self.SetInitialSize( ( 980, y ) )
|
|
|
|
self.SetDropTarget( ClientGUICommon.FileDropTarget( self.Import ) )
|
|
|
|
wx.CallAfter( self._ok.SetFocus )
|
|
|
|
|
|
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( HC.u( 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 HC.o( 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: HC.app.Write( 'update_imageboards', self._edit_log )
|
|
|
|
except Exception as e: wx.MessageBox( 'Saving imageboards to DB raised this error: ' + HC.u( 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 HC.o( 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._site_panel = ClientGUICommon.StaticBox( self, 'site' )
|
|
|
|
self._imageboards = ClientGUICommon.ListBook( self._site_panel )
|
|
|
|
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 PopulateControls():
|
|
|
|
self._edit_log = []
|
|
|
|
for imageboard in imageboards:
|
|
|
|
name = imageboard.GetName()
|
|
|
|
page_info = ( self._Panel, ( self._imageboards, imageboard ), {} )
|
|
|
|
self._imageboards.AddPage( page_info, name )
|
|
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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 )
|
|
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
( x, y ) = self.GetEffectiveMinSize()
|
|
|
|
self.SetInitialSize( ( 980, y ) )
|
|
|
|
|
|
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( HC.u( 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 HC.o( 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 )
|
|
|
|
self._flood_time = wx.SpinCtrl( self._basic_info_panel, min = 5, max = 1200 )
|
|
|
|
#
|
|
|
|
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 ) ] )
|
|
|
|
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' )
|
|
|
|
self._min_resolution = ClientGUICommon.NoneableSpinCtrl( self._restrictions_panel, 'min resolution', num_dimensions = 2 )
|
|
|
|
self._max_resolution = ClientGUICommon.NoneableSpinCtrl( self._restrictions_panel, 'max resolution', num_dimensions = 2 )
|
|
|
|
self._max_file_size = ClientGUICommon.NoneableSpinCtrl( self._restrictions_panel, 'max file size (KB)', multiplier = 1024 )
|
|
|
|
self._allowed_mimes_panel = ClientGUICommon.StaticBox( self._restrictions_panel, 'allowed mimes' )
|
|
|
|
self._mimes = wx.ListBox( self._allowed_mimes_panel )
|
|
|
|
self._mime_choice = wx.Choice( self._allowed_mimes_panel )
|
|
|
|
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 PopulateControls():
|
|
|
|
#
|
|
|
|
self._post_url.SetValue( post_url )
|
|
|
|
self._flood_time.SetRange( 5, 1200 )
|
|
self._flood_time.SetValue( flood_time )
|
|
|
|
#
|
|
|
|
for ( name, type, default, editable ) in form_fields:
|
|
|
|
self._form_fields.Append( ( name, CC.field_string_lookup[ type ], HC.u( default ), HC.u( 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 )
|
|
|
|
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 )
|
|
|
|
for mime in HC.ALLOWED_MIMES: self._mime_choice.Append( HC.mime_string_lookup[ mime ], mime )
|
|
|
|
self._mime_choice.SetSelection( 0 )
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
|
|
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 ClientGUIDialogs.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 ], HC.u( default ), HC.u( editable ) ), ( name, type, default, editable ) )
|
|
|
|
|
|
|
|
except Exception as e: wx.MessageBox( HC.u( 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 ClientGUIDialogs.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 ], HC.u( default ), HC.u( editable ) ), ( name, type, default, editable ) )
|
|
|
|
|
|
|
|
except Exception as e: wx.MessageBox( HC.u( 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 ], HC.u( default ), HC.u( 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 DialogManageImportFolders( ClientGUIDialogs.Dialog ):
|
|
|
|
def __init__( self, parent ):
|
|
|
|
def InitialiseControls():
|
|
|
|
self._import_folders = ClientGUICommon.SaneListCtrl( self, 480, [ ( 'path', -1 ), ( 'type', 120 ), ( 'check period', 120 ), ( 'local tag', 120 ) ] )
|
|
|
|
self._import_folders.SetMinSize( ( 780, 360 ) )
|
|
|
|
self._add_button = wx.Button( self, label='add' )
|
|
self._add_button.Bind( wx.EVT_BUTTON, self.EventAdd )
|
|
|
|
self._edit_button = wx.Button( self, label='edit' )
|
|
self._edit_button.Bind( wx.EVT_BUTTON, self.EventEdit )
|
|
|
|
self._delete_button = wx.Button( self, label='delete' )
|
|
self._delete_button.Bind( wx.EVT_BUTTON, self.EventDelete )
|
|
|
|
self._ok = wx.Button( self, id = wx.ID_OK, 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 PopulateControls():
|
|
|
|
self._original_import_folders = HC.app.Read( 'import_folders' )
|
|
|
|
for ( path, details ) in self._original_import_folders:
|
|
|
|
type = details[ 'type' ]
|
|
check_period = details[ 'check_period' ]
|
|
local_tag = details[ 'local_tag' ]
|
|
|
|
( pretty_type, pretty_check_period, pretty_local_tag ) = self._GetPrettyVariables( type, check_period, local_tag )
|
|
|
|
self._import_folders.Append( ( path, pretty_type, pretty_check_period, pretty_local_tag ), ( path, type, check_period, local_tag ) )
|
|
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
file_buttons = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
file_buttons.AddF( self._add_button, FLAGS_MIXED )
|
|
file_buttons.AddF( self._edit_button, FLAGS_MIXED )
|
|
file_buttons.AddF( self._delete_button, 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._import_folders, FLAGS_EXPAND_PERPENDICULAR )
|
|
vbox.AddF( file_buttons, FLAGS_BUTTON_SIZERS )
|
|
vbox.AddF( buttons, FLAGS_BUTTON_SIZERS )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'manage import folders' )
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
self.SetDropTarget( ClientGUICommon.FileDropTarget( self._AddFolders ) )
|
|
|
|
( x, y ) = self.GetEffectiveMinSize()
|
|
|
|
self.SetInitialSize( ( x, y ) )
|
|
|
|
wx.CallAfter( self._ok.SetFocus )
|
|
|
|
|
|
def _AddFolder( self, path ):
|
|
|
|
all_existing_client_data = self._import_folders.GetClientData()
|
|
|
|
if path not in ( existing_path for ( existing_path, type, check_period, local_tag ) in all_existing_client_data ):
|
|
|
|
type = HC.IMPORT_FOLDER_TYPE_SYNCHRONISE
|
|
check_period = 15 * 60
|
|
local_tag = None
|
|
|
|
with DialogManageImportFoldersEdit( self, path, type, check_period, local_tag ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
( path, type, check_period, local_tag ) = dlg.GetInfo()
|
|
|
|
( pretty_type, pretty_check_period, pretty_local_tag ) = self._GetPrettyVariables( type, check_period, local_tag )
|
|
|
|
self._import_folders.Append( ( path, pretty_type, pretty_check_period, pretty_local_tag ), ( path, type, check_period, local_tag ) )
|
|
|
|
|
|
|
|
|
|
|
|
def _AddFolders( self, paths ):
|
|
|
|
for path in paths:
|
|
|
|
if os.path.isdir( path ): self._AddFolder( path )
|
|
|
|
|
|
|
|
def _GetPrettyVariables( self, type, check_period, local_tag ):
|
|
|
|
if type == HC.IMPORT_FOLDER_TYPE_DELETE: pretty_type = 'delete'
|
|
elif type == HC.IMPORT_FOLDER_TYPE_SYNCHRONISE: pretty_type = 'synchronise'
|
|
|
|
pretty_check_period = HC.u( check_period / 60 ) + ' minutes'
|
|
|
|
if local_tag == None: pretty_local_tag = ''
|
|
else: pretty_local_tag = local_tag
|
|
|
|
return ( pretty_type, pretty_check_period, pretty_local_tag )
|
|
|
|
|
|
def EventAdd( self, event ):
|
|
|
|
with wx.DirDialog( self, 'Select a folder to add.' ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
path = dlg.GetPath()
|
|
|
|
self._AddFolder( path )
|
|
|
|
|
|
|
|
|
|
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
|
|
|
|
def EventDelete( self, event ): self._import_folders.RemoveAllSelected()
|
|
|
|
def EventEdit( self, event ):
|
|
|
|
indices = self._import_folders.GetAllSelected()
|
|
|
|
for index in indices:
|
|
|
|
( path, type, check_period, local_tag ) = self._import_folders.GetClientData( index )
|
|
|
|
with DialogManageImportFoldersEdit( self, path, type, check_period, local_tag ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
( path, type, check_period, local_tag ) = dlg.GetInfo()
|
|
|
|
( pretty_type, pretty_check_period, pretty_local_tag ) = self._GetPrettyVariables( type, check_period, local_tag )
|
|
|
|
self._import_folders.UpdateRow( index, ( path, pretty_type, pretty_check_period, pretty_local_tag ), ( path, type, check_period, local_tag ) )
|
|
|
|
|
|
|
|
|
|
|
|
def EventOK( self, event ):
|
|
|
|
client_data = self._import_folders.GetClientData()
|
|
|
|
original_paths_to_details = dict( self._original_import_folders )
|
|
|
|
import_folders = []
|
|
|
|
for ( path, type, check_period, local_tag ) in client_data:
|
|
|
|
if path in original_paths_to_details: details = original_paths_to_details[ path ]
|
|
else: details = { 'last_checked' : 0, 'cached_imported_paths' : set(), 'failed_imported_paths' : set() }
|
|
|
|
details[ 'type' ] = type
|
|
details[ 'check_period' ] = check_period
|
|
details[ 'local_tag'] = local_tag
|
|
|
|
import_folders.append( ( path, details ) )
|
|
|
|
|
|
HC.app.Write( 'import_folders', import_folders )
|
|
|
|
self.EndModal( wx.ID_OK )
|
|
|
|
|
|
class DialogManageImportFoldersEdit( ClientGUIDialogs.Dialog ):
|
|
|
|
def __init__( self, parent, path, type, check_period, local_tag ):
|
|
|
|
def InitialiseControls():
|
|
|
|
self._path = wx.DirPickerCtrl( self, style = wx.DIRP_USE_TEXTCTRL )
|
|
|
|
self._type = ClientGUICommon.BetterChoice( self )
|
|
self._type.Append( 'delete', HC.IMPORT_FOLDER_TYPE_DELETE )
|
|
self._type.Append( 'synchronise', HC.IMPORT_FOLDER_TYPE_SYNCHRONISE )
|
|
message = '''delete - try to import all files in folder and delete them if they succeed
|
|
|
|
synchronise - try to import all new files in folder'''
|
|
self._type.SetToolTipString( message )
|
|
|
|
self._check_period = wx.SpinCtrl( self )
|
|
|
|
self._local_tag = wx.TextCtrl( self )
|
|
self._local_tag.SetToolTipString( 'add this tag on the local tag service to anything imported from the folder' )
|
|
|
|
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' )
|
|
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
|
|
|
|
|
|
def PopulateControls():
|
|
|
|
self._path.SetPath( path )
|
|
|
|
self._type.Select( type )
|
|
|
|
self._check_period.SetRange( 3, 180 )
|
|
|
|
self._check_period.SetValue( check_period / 60 )
|
|
|
|
if local_tag is not None: self._local_tag.SetValue( local_tag )
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
gridbox = wx.FlexGridSizer( 0, 2 )
|
|
|
|
gridbox.AddGrowableCol( 1, 1 )
|
|
|
|
gridbox.AddF( wx.StaticText( self, label='path:' ), FLAGS_MIXED )
|
|
gridbox.AddF( self._path, 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='check period (minutes):' ), FLAGS_MIXED )
|
|
gridbox.AddF( self._check_period, FLAGS_EXPAND_BOTH_WAYS )
|
|
gridbox.AddF( wx.StaticText( self, label='local tag:' ), FLAGS_MIXED )
|
|
gridbox.AddF( self._local_tag, FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
buttons = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
buttons.AddF( self._ok, FLAGS_MIXED )
|
|
buttons.AddF( self._cancel, FLAGS_MIXED )
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
vbox.AddF( gridbox, FLAGS_EXPAND_BOTH_WAYS )
|
|
vbox.AddF( buttons, FLAGS_BUTTON_SIZERS )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
( x, y ) = self.GetEffectiveMinSize()
|
|
|
|
self.SetInitialSize( ( 640, y ) )
|
|
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'edit import folder' )
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
wx.CallAfter( self._ok.SetFocus )
|
|
|
|
|
|
def GetInfo( self ):
|
|
|
|
path = self._path.GetPath()
|
|
|
|
type = self._type.GetChoice()
|
|
|
|
check_period = self._check_period.GetValue() * 60
|
|
|
|
local_tag = self._local_tag.GetValue()
|
|
|
|
return ( path, type, check_period, local_tag )
|
|
|
|
|
|
class DialogManageOptionsFileRepository( ClientGUIDialogs.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)', multiplier = 1048576 )
|
|
self._max_storage = ClientGUICommon.NoneableSpinCtrl( self._file_repository_panel, 'max storage (MB)', multiplier = 1048576 )
|
|
|
|
self._log_uploader_ips = wx.CheckBox( self._file_repository_panel, label='' )
|
|
|
|
self._message = wx.TextCtrl( self._file_repository_panel )
|
|
|
|
self._ok = wx.Button( self, label='Save' )
|
|
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 PopulateControls():
|
|
|
|
self._service = HC.app.Read( 'service', self._service_identifier )
|
|
|
|
connection = self._service.GetConnection()
|
|
|
|
options = connection.Get( 'options' )
|
|
|
|
self._max_monthly_data.SetValue( options[ 'max_monthly_data' ] )
|
|
self._max_storage.SetValue( options[ 'max_storage' ] )
|
|
|
|
self._log_uploader_ips.SetValue( options[ 'log_uploader_ips' ] )
|
|
|
|
self._message.SetValue( options[ 'message' ] )
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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._ok, FLAGS_SMALL_INDENT )
|
|
buttons.AddF( self._cancel, 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 ) )
|
|
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, service_identifier.GetName() + ' options' )
|
|
|
|
self._service_identifier = service_identifier
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
wx.CallAfter( self._ok.SetFocus )
|
|
|
|
|
|
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: ' + HC.u( e ) )
|
|
|
|
self.EndModal( wx.ID_OK )
|
|
|
|
|
|
class DialogManageOptionsLocal( ClientGUIDialogs.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 )
|
|
|
|
self._exclude_deleted_files = wx.CheckBox( self._file_page, label='' )
|
|
|
|
self._thumbnail_cache_size = wx.SpinCtrl( self._file_page, min = 10, max = 3000 )
|
|
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.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.Bind( wx.EVT_SPINCTRL, self.EventFullscreensUpdate )
|
|
|
|
self._estimated_number_fullscreens = wx.StaticText( self._file_page, label = '' )
|
|
|
|
self._thumbnail_width = wx.SpinCtrl( self._file_page, min=20, max=200 )
|
|
self._thumbnail_width.Bind( wx.EVT_SPINCTRL, self.EventThumbnailsUpdate )
|
|
|
|
self._thumbnail_height = wx.SpinCtrl( self._file_page, min=20, max=200 )
|
|
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.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._confirm_client_exit = wx.CheckBox( self._gui_page )
|
|
|
|
self._gui_capitalisation = wx.CheckBox( self._gui_page )
|
|
|
|
self._gui_show_all_tags_in_autocomplete = wx.CheckBox( self._gui_page )
|
|
|
|
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 )
|
|
|
|
self._default_tag_repository = wx.Choice( self._gui_page )
|
|
|
|
self._fullscreen_borderless = wx.CheckBox( self._gui_page )
|
|
|
|
self._listbook.AddPage( self._gui_page, 'gui' )
|
|
|
|
# sound
|
|
|
|
self._sound_page = wx.Panel( self._listbook )
|
|
self._sound_page.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
|
|
|
|
self._play_dumper_noises = wx.CheckBox( self._sound_page, label = 'play success/fail noises when dumping' )
|
|
|
|
self._listbook.AddPage( self._sound_page, 'sound' )
|
|
|
|
# default 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 ) )
|
|
|
|
self._file_system_predicate_age_sign = wx.Choice( self._file_system_predicates_page, choices=[ '<', u'\u2248', '>' ] )
|
|
|
|
self._file_system_predicate_age_years = wx.SpinCtrl( self._file_system_predicates_page, max = 30 )
|
|
|
|
self._file_system_predicate_age_months = wx.SpinCtrl( self._file_system_predicates_page, max = 60 )
|
|
|
|
self._file_system_predicate_age_days = wx.SpinCtrl( self._file_system_predicates_page, max = 90 )
|
|
|
|
self._file_system_predicate_duration_sign = wx.Choice( self._file_system_predicates_page, choices=[ '<', u'\u2248', '=', '>' ] )
|
|
|
|
self._file_system_predicate_duration_s = wx.SpinCtrl( self._file_system_predicates_page, max = 3599 )
|
|
|
|
self._file_system_predicate_duration_ms = wx.SpinCtrl( self._file_system_predicates_page, max = 999 )
|
|
|
|
self._file_system_predicate_height_sign = wx.Choice( self._file_system_predicates_page, choices=[ '<', u'\u2248', '=', '>' ] )
|
|
|
|
self._file_system_predicate_height = wx.SpinCtrl( self._file_system_predicates_page, max = 200000 )
|
|
|
|
self._file_system_predicate_limit = wx.SpinCtrl( self._file_system_predicates_page, max = 1000000 )
|
|
|
|
self._file_system_predicate_mime_media = wx.Choice( self._file_system_predicates_page, choices=[ 'image', 'application' ] )
|
|
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._file_system_predicate_num_tags_sign = wx.Choice( self._file_system_predicates_page, choices=[ '<', '=', '>' ] )
|
|
|
|
self._file_system_predicate_num_tags = wx.SpinCtrl( self._file_system_predicates_page, max = 2000 )
|
|
|
|
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_value = wx.SpinCtrl( self._file_system_predicates_page, min = 0, max = 50000 )
|
|
|
|
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_ratio_sign = wx.Choice( self._file_system_predicates_page, choices=[ '=', u'\u2248' ] )
|
|
|
|
self._file_system_predicate_ratio_width = wx.SpinCtrl( self._file_system_predicates_page, max = 50000 )
|
|
|
|
self._file_system_predicate_ratio_height = wx.SpinCtrl( self._file_system_predicates_page, max = 50000 )
|
|
|
|
self._file_system_predicate_size_sign = wx.Choice( self._file_system_predicates_page, choices=[ '<', u'\u2248', '=', '>' ] )
|
|
|
|
self._file_system_predicate_size = wx.SpinCtrl( self._file_system_predicates_page, max = 1048576 )
|
|
|
|
self._file_system_predicate_size_unit = wx.Choice( self._file_system_predicates_page, choices=[ 'B', 'KB', 'MB', 'GB' ] )
|
|
|
|
self._file_system_predicate_width_sign = wx.Choice( self._file_system_predicates_page, choices=[ '<', u'\u2248', '=', '>' ] )
|
|
|
|
self._file_system_predicate_width = wx.SpinCtrl( self._file_system_predicates_page, max = 200000 )
|
|
|
|
self._file_system_predicate_num_words_sign = wx.Choice( self._file_system_predicates_page, choices=[ '<', u'\u2248', '=', '>' ] )
|
|
|
|
self._file_system_predicate_num_words = wx.SpinCtrl( self._file_system_predicates_page, max = 1000000 )
|
|
|
|
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 )
|
|
|
|
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 ) ] )
|
|
|
|
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._ok = wx.Button( self, label='Save' )
|
|
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 PopulateControls():
|
|
|
|
if self._options[ 'export_path' ] is not None: self._export_location.SetPath( HC.ConvertPortablePathToAbsPath( self._options[ 'export_path' ] ) )
|
|
|
|
self._exclude_deleted_files.SetValue( self._options[ 'exclude_deleted_files' ] )
|
|
|
|
self._thumbnail_cache_size.SetValue( int( self._options[ 'thumbnail_cache_size' ] / 1048576 ) )
|
|
|
|
self._preview_cache_size.SetValue( int( self._options[ 'preview_cache_size' ] / 1048576 ) )
|
|
|
|
self._fullscreen_cache_size.SetValue( int( self._options[ 'fullscreen_cache_size' ] / 1048576 ) )
|
|
|
|
( thumbnail_width, thumbnail_height ) = self._options[ 'thumbnail_dimensions' ]
|
|
|
|
self._thumbnail_width.SetValue( thumbnail_width )
|
|
|
|
self._thumbnail_height.SetValue( thumbnail_height )
|
|
|
|
self._num_autocomplete_chars.SetValue( self._options[ 'num_autocomplete_chars' ] )
|
|
|
|
#
|
|
|
|
self._confirm_client_exit.SetValue( self._options[ 'confirm_client_exit' ] )
|
|
|
|
self._gui_capitalisation.SetValue( self._options[ 'gui_capitalisation' ] )
|
|
|
|
self._gui_show_all_tags_in_autocomplete.SetValue( self._options[ 'show_all_tags_in_autocomplete' ] )
|
|
|
|
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 = HC.app.Read( 'service_identifiers', ( HC.LOCAL_TAG, HC.TAG_REPOSITORY ) )
|
|
|
|
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.SetValue( self._options[ 'fullscreen_borderless' ] )
|
|
|
|
#
|
|
|
|
self._play_dumper_noises.SetValue( self._options[ 'play_dumper_noises' ] )
|
|
|
|
#
|
|
|
|
system_predicates = self._options[ 'file_system_predicates' ]
|
|
|
|
( sign, years, months, days ) = system_predicates[ 'age' ]
|
|
|
|
self._file_system_predicate_age_sign.SetSelection( sign )
|
|
self._file_system_predicate_age_years.SetValue( years )
|
|
self._file_system_predicate_age_months.SetValue( months )
|
|
self._file_system_predicate_age_days.SetValue( days )
|
|
|
|
( sign, s, ms ) = system_predicates[ 'duration' ]
|
|
|
|
self._file_system_predicate_duration_sign.SetSelection( sign )
|
|
self._file_system_predicate_duration_s.SetValue( s )
|
|
self._file_system_predicate_duration_ms.SetValue( ms )
|
|
|
|
( sign, height ) = system_predicates[ 'height' ]
|
|
|
|
self._file_system_predicate_height_sign.SetSelection( sign )
|
|
self._file_system_predicate_height.SetValue( height )
|
|
|
|
limit = system_predicates[ 'limit' ]
|
|
|
|
self._file_system_predicate_limit.SetValue( limit )
|
|
|
|
( media, type ) = system_predicates[ 'mime' ]
|
|
|
|
self._file_system_predicate_mime_media.SetSelection( media )
|
|
|
|
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.SetSelection( sign )
|
|
self._file_system_predicate_num_tags.SetValue( num_tags )
|
|
|
|
( sign, value ) = system_predicates[ 'local_rating_numerical' ]
|
|
|
|
self._file_system_predicate_local_rating_numerical_sign.SetSelection( sign )
|
|
self._file_system_predicate_local_rating_numerical_value.SetValue( value )
|
|
|
|
value = system_predicates[ 'local_rating_like' ]
|
|
|
|
self._file_system_predicate_local_rating_like_value.SetSelection( value )
|
|
|
|
( sign, width, height ) = system_predicates[ 'ratio' ]
|
|
|
|
self._file_system_predicate_ratio_sign.SetSelection( sign )
|
|
self._file_system_predicate_ratio_width.SetValue( width )
|
|
self._file_system_predicate_ratio_height.SetValue( height )
|
|
|
|
( sign, size, unit ) = system_predicates[ 'size' ]
|
|
|
|
self._file_system_predicate_size_sign.SetSelection( sign )
|
|
self._file_system_predicate_size.SetValue( size )
|
|
self._file_system_predicate_size_unit.SetSelection( unit )
|
|
|
|
( sign, width ) = system_predicates[ 'width' ]
|
|
|
|
self._file_system_predicate_width_sign.SetSelection( sign )
|
|
self._file_system_predicate_width.SetValue( width )
|
|
|
|
( sign, num_words ) = system_predicates[ 'num_words' ]
|
|
|
|
self._file_system_predicate_num_words_sign.SetSelection( sign )
|
|
self._file_system_predicate_num_words.SetValue( num_words )
|
|
|
|
#
|
|
|
|
#
|
|
|
|
for ( sort_by_type, sort_by ) in self._options[ 'sort_by' ]: self._sort_by.Append( '-'.join( sort_by ), sort_by )
|
|
|
|
#
|
|
|
|
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()
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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 = 'Confirm client exit:' ), FLAGS_MIXED )
|
|
gridbox.AddF( self._confirm_client_exit, FLAGS_MIXED )
|
|
|
|
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._play_dumper_noises, FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
self._sound_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._ok, FLAGS_SMALL_INDENT )
|
|
buttons.AddF( self._cancel, 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 ) )
|
|
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'hydrus client options' )
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
self.EventFullscreensUpdate( None )
|
|
self.EventPreviewsUpdate( None )
|
|
self.EventThumbnailsUpdate( None )
|
|
|
|
wx.CallAfter( self._file_page.Layout ) # draws the static texts correctly
|
|
|
|
wx.CallAfter( self._ok.SetFocus )
|
|
|
|
|
|
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[ 'play_dumper_noises' ] = self._play_dumper_noises.GetValue()
|
|
|
|
self._options[ 'confirm_client_exit' ] = self._confirm_client_exit.GetValue()
|
|
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: HC.app.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 ClientGUIDialogs.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 ClientGUIDialogs.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( HC.u( 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( ClientGUIDialogs.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)', multiplier = 1048576 )
|
|
|
|
self._max_storage = ClientGUICommon.NoneableSpinCtrl( self._server_panel, 'max storage (MB)', multiplier = 1048576 )
|
|
|
|
self._message = wx.TextCtrl( self._server_panel )
|
|
|
|
self._ok = wx.Button( self, label='Save' )
|
|
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 PopulateControls():
|
|
|
|
self._service = HC.app.Read( 'service', self._service_identifier )
|
|
|
|
connection = self._service.GetConnection()
|
|
|
|
options = connection.Get( 'options' )
|
|
|
|
self._max_monthly_data.SetValue( options[ 'max_monthly_data' ] )
|
|
self._max_storage.SetValue( options[ 'max_storage' ] )
|
|
|
|
self._message.SetValue( options[ 'message' ] )
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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._ok, FLAGS_SMALL_INDENT )
|
|
buttons.AddF( self._cancel, 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 )
|
|
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, service_identifier.GetName() + ' options' )
|
|
|
|
self._service_identifier = service_identifier
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
( x, y ) = self.GetEffectiveMinSize()
|
|
|
|
self.SetInitialSize( ( x + 80, y ) )
|
|
|
|
wx.CallAfter( self._ok.SetFocus )
|
|
|
|
|
|
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: ' + HC.u( e ) )
|
|
|
|
self.EndModal( wx.ID_OK )
|
|
|
|
|
|
class DialogManageOptionsTagRepository( ClientGUIDialogs.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)', multiplier = 1048576 )
|
|
|
|
self._message = wx.TextCtrl( self._tag_repository_panel )
|
|
|
|
self._ok = wx.Button( self, label='Save' )
|
|
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 PopulateControls():
|
|
|
|
self._service = HC.app.Read( 'service', self._service_identifier )
|
|
|
|
connection = self._service.GetConnection()
|
|
|
|
options = connection.Get( 'options' )
|
|
|
|
self._max_monthly_data.SetValue( options[ 'max_monthly_data' ] )
|
|
|
|
self._message.SetValue( options[ 'message' ] )
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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._ok, FLAGS_SMALL_INDENT )
|
|
buttons.AddF( self._cancel, 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 )
|
|
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, service_identifier.GetName() + ' options' )
|
|
|
|
self._service_identifier = service_identifier
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
( x, y ) = self.GetEffectiveMinSize()
|
|
|
|
self.SetInitialSize( ( x + 80, y ) )
|
|
|
|
wx.CallAfter( self._ok.SetFocus )
|
|
|
|
|
|
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: ' + HC.u( e ) )
|
|
|
|
self.EndModal( wx.ID_OK )
|
|
|
|
|
|
class DialogManagePixivAccount( ClientGUIDialogs.Dialog ):
|
|
|
|
def __init__( self, parent ):
|
|
|
|
def InitialiseControls():
|
|
|
|
self._id = wx.TextCtrl( self )
|
|
self._password = wx.TextCtrl( self )
|
|
|
|
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 PopulateControls():
|
|
|
|
( id, password ) = HC.app.Read( 'pixiv_account' )
|
|
|
|
self._id.SetValue( id )
|
|
self._password.SetValue( password )
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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 ) )
|
|
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'manage pixiv account' )
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
wx.CallAfter( self._ok.SetFocus )
|
|
|
|
|
|
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
|
|
|
|
def EventOK( self, event ):
|
|
|
|
id = self._id.GetValue()
|
|
password = self._password.GetValue()
|
|
|
|
HC.app.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.get_connection( 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( HC.u( e ) )
|
|
|
|
|
|
class DialogManageRatings( ClientGUIDialogs.Dialog ):
|
|
|
|
def __init__( self, parent, media ):
|
|
|
|
def InitialiseControls():
|
|
|
|
service_identifiers = HC.app.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 PopulateControls():
|
|
|
|
pass
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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 = set()
|
|
|
|
for m in media: self._hashes.update( m.GetHashes() )
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'manage ratings for ' + HC.ConvertIntToPrettyString( len( self._hashes ) ) + ' files' )
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
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:
|
|
|
|
service_identfiers_to_content_updates = {}
|
|
|
|
for panel in self._panels:
|
|
|
|
if panel.HasChanges():
|
|
|
|
( service_identifier, content_updates ) = panel.GetContentUpdates()
|
|
|
|
service_identfiers_to_content_updates[ service_identifier ] = content_updates
|
|
|
|
|
|
|
|
HC.app.Write( 'content_updates', service_identfiers_to_content_updates )
|
|
|
|
except Exception as e: wx.MessageBox( 'Saving ratings changes to DB raised this error: ' + HC.u( 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 = HC.app.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( HC.u( '%.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( HC.u( likes ) + ' likes' )
|
|
if dislikes > 0: scores.append( HC.u( dislikes ) + ' dislikes' )
|
|
if nones > 0: scores.append( HC.u( 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 = HC.u( '%.2f' % overall_rating_converted )
|
|
|
|
if min in ( 0, 1 ): str_overall_rating += '/' + HC.u( '%.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 PopulateControls():
|
|
|
|
pass
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
|
|
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 ' + HC.u( rating ) )
|
|
|
|
event.Skip()
|
|
|
|
|
|
def GetContentUpdates( self ):
|
|
|
|
service_type = self._service_identifier.GetType()
|
|
|
|
choice_text = self._choices.GetSelectedClientData()
|
|
|
|
if choice_text == 'remove rating': rating = 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() )
|
|
|
|
|
|
hashes = { hash for hash in itertools.chain.from_iterable( ( media.GetHashes() for media in self._media ) ) }
|
|
|
|
content_update = HC.ContentUpdate( HC.CONTENT_DATA_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( rating, hashes ) )
|
|
|
|
return ( self._service_identifier, [ content_update ] )
|
|
|
|
|
|
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( ClientGUIDialogs.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 )
|
|
|
|
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 PopulateControls():
|
|
|
|
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 )
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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 ) )
|
|
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'manage ' + service_identifier.GetName() + ' services' )
|
|
|
|
self._service = HC.app.Read( 'service', service_identifier )
|
|
|
|
connection = self._service.GetConnection()
|
|
|
|
self._service_identifiers = connection.Get( 'services' )
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
current_page = self._services_listbook.GetCurrentPage()
|
|
|
|
if current_page.GetOriginalServiceIdentifier().GetType() == HC.SERVER_ADMIN: self._remove.Disable()
|
|
else: self._remove.Enable()
|
|
|
|
wx.CallAfter( self._ok.SetFocus )
|
|
|
|
|
|
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( HC.u( e ) )
|
|
|
|
|
|
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
|
|
|
|
def EventOK( self, event ):
|
|
|
|
try: self._CheckCurrentServiceIsValid()
|
|
except Exception as e:
|
|
|
|
wx.MessageBox( HC.u( 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 )
|
|
|
|
HC.app.Write( 'update_server_services', self._service.GetServiceIdentifier(), self._edit_log )
|
|
|
|
|
|
except Exception as e: wx.MessageBox( 'Saving services to server raised this error: ' + HC.u( 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( HC.u( e ) )
|
|
|
|
event.Veto()
|
|
|
|
|
|
|
|
class _Panel( wx.Panel ):
|
|
|
|
def __init__( self, parent, service_identifier ):
|
|
|
|
wx.Panel.__init__( self, parent )
|
|
|
|
self._service_identifier = service_identifier
|
|
|
|
def InitialiseControls():
|
|
|
|
self._service_panel = ClientGUICommon.StaticBox( self, 'service' )
|
|
|
|
self._service_port = wx.SpinCtrl( self._service_panel, min = 1, max = 65535 )
|
|
|
|
|
|
def PopulateControls():
|
|
|
|
self._service_port.SetValue( self._service_identifier.GetPort() )
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
|
|
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( ClientGUIDialogs.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._listbook.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventPageChanging, source = self._listbook )
|
|
|
|
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 )
|
|
|
|
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 PopulateControls():
|
|
|
|
services = HC.app.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' )
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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 )
|
|
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'manage services' )
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
( x, y ) = self.GetEffectiveMinSize()
|
|
|
|
self.SetInitialSize( ( 880, y + 220 ) )
|
|
|
|
self.SetDropTarget( ClientGUICommon.FileDropTarget( self.Import ) )
|
|
|
|
wx.CallAfter( self._ok.SetFocus )
|
|
|
|
|
|
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 ClientGUIDialogs.DialogChooseNewServiceMethod( self ) as dlg:
|
|
|
|
if dlg.ShowModal() != wx.ID_OK: return
|
|
|
|
register = dlg.GetRegister()
|
|
|
|
if register:
|
|
|
|
with ClientGUIDialogs.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 = HydrusEncryption.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( HC.u( 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( HC.u( 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 HC.o( 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 HC.o( 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( HC.u( 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: HC.app.Write( 'update_services', self._edit_log )
|
|
|
|
except Exception as e: wx.MessageBox( 'Saving services to DB raised this error: ' + HC.u( e ) )
|
|
|
|
self.EndModal( wx.ID_OK )
|
|
|
|
|
|
def EventPageChanging( self, event ):
|
|
|
|
try: self._CheckCurrentServiceIsValid()
|
|
except Exception as e:
|
|
|
|
wx.MessageBox( HC.u( 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( HC.u( e ) )
|
|
|
|
event.Veto()
|
|
|
|
|
|
|
|
def Import( self, paths ):
|
|
|
|
try: self._CheckCurrentSubscriptionIsValid()
|
|
except Exception as e:
|
|
|
|
wx.MessageBox( HC.u( e ) )
|
|
|
|
return
|
|
|
|
|
|
for path in paths:
|
|
|
|
try:
|
|
|
|
with HC.o( 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 ClientGUIDialogs.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 )
|
|
|
|
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 PopulateControls():
|
|
|
|
self._service_name.SetValue( self._service_identifier.GetName() )
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
|
|
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( ClientGUIDialogs.Dialog ):
|
|
|
|
def __init__( self, parent ):
|
|
|
|
def InitialiseControls():
|
|
|
|
self._listbook = ClientGUICommon.ListBook( self )
|
|
self._listbook.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventServiceChanging )
|
|
self._listbook.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventPageChanging, source = self._listbook )
|
|
|
|
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._newgrounds = ClientGUICommon.ListBook( self._listbook )
|
|
self._newgrounds.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 )
|
|
|
|
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 PopulateControls():
|
|
|
|
types_to_listbooks = {}
|
|
|
|
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
|
|
types_to_listbooks[ HC.SITE_DOWNLOAD_TYPE_NEWGROUNDS ] = self._newgrounds
|
|
|
|
for ( site_download_type, name, query_type, query, frequency_type, frequency_number, advanced_tag_options, advanced_import_options, last_checked, url_cache, paused ) 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, paused )
|
|
|
|
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._newgrounds, 'newgrounds' )
|
|
self._listbook.AddPage( self._pixiv, 'pixiv' )
|
|
self._listbook.AddPage( self._booru, 'booru' )
|
|
self._listbook.AddPage( self._tumblr, 'tumblr' )
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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 )
|
|
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'manage subscriptions' )
|
|
|
|
self._original_subscriptions = HC.app.Read( 'subscriptions' )
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
( x, y ) = self.GetEffectiveMinSize()
|
|
|
|
self.SetInitialSize( ( 680, max( 720, y ) ) )
|
|
|
|
self.SetDropTarget( ClientGUICommon.FileDropTarget( self.Import ) )
|
|
|
|
wx.CallAfter( self._ok.SetFocus )
|
|
|
|
|
|
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
|
|
elif subscription_listbook == self._newgrounds: site_download_type = HC.SITE_DOWNLOAD_TYPE_NEWGROUNDS
|
|
|
|
if site_download_type == HC.SITE_DOWNLOAD_TYPE_PIXIV:
|
|
|
|
( id, password ) = HC.app.Read( 'pixiv_account' )
|
|
|
|
if id == '' and password == '':
|
|
|
|
wx.MessageBox( 'You need to set up your pixiv credentials before you can add a pixiv subscription!' )
|
|
|
|
return
|
|
|
|
|
|
|
|
if site_download_type in ( HC.SITE_DOWNLOAD_TYPE_DEVIANT_ART, HC.SITE_DOWNLOAD_TYPE_TUMBLR, HC.SITE_DOWNLOAD_TYPE_NEWGROUNDS ): 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()
|
|
|
|
paused = False
|
|
|
|
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, paused )
|
|
|
|
subscription_listbook.AddPage( page, name, select = True )
|
|
|
|
except Exception as e:
|
|
|
|
wx.MessageBox( HC.u( 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( HC.u( 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 HC.o( 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 HC.o( dlg.GetPath(), 'wb' ) as f: f.write( yaml.safe_dump( info ) )
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def EventOK( self, event ):
|
|
|
|
try: self._CheckCurrentSubscriptionIsValid()
|
|
except Exception as e:
|
|
|
|
wx.MessageBox( HC.u( 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() )
|
|
all_pages.extend( self._newgrounds.GetNameToPageDict().values() )
|
|
|
|
subscriptions = [ page.GetInfo() for page in all_pages ]
|
|
|
|
try: HC.app.Write( 'subscriptions', subscriptions )
|
|
except Exception as e: wx.MessageBox( 'Saving services to DB raised this error: ' + HC.u( e ) )
|
|
|
|
self.EndModal( wx.ID_OK )
|
|
|
|
|
|
def EventPageChanging( self, event ):
|
|
|
|
try: self._CheckCurrentSubscriptionIsValid()
|
|
except Exception as e:
|
|
|
|
wx.MessageBox( HC.u( 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( HC.u( e ) )
|
|
|
|
event.Veto()
|
|
|
|
|
|
|
|
def Import( self, paths ):
|
|
|
|
try: self._CheckCurrentSubscriptionIsValid()
|
|
except Exception as e:
|
|
|
|
wx.MessageBox( HC.u( e ) )
|
|
|
|
return
|
|
|
|
|
|
for path in paths:
|
|
|
|
try:
|
|
|
|
with HC.o( 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
|
|
elif site_download_type == HC.SITE_DOWNLOAD_TYPE_NEWGROUNDS: services_listbook = self._newgrounds
|
|
|
|
self._listbook.SelectPage( services_listbook )
|
|
|
|
if services_listbook.NameExists( name ):
|
|
|
|
message = 'A service already exists with that name. Overwrite it?'
|
|
|
|
with ClientGUIDialogs.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, paused ):
|
|
|
|
def InitialiseControls():
|
|
|
|
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 )
|
|
|
|
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, HC.SITE_DOWNLOAD_TYPE_NEWGROUNDS ): 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._paused = wx.CheckBox( self._info_panel, label = 'paused' )
|
|
|
|
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 = [ '' ]
|
|
elif site_download_type == HC.SITE_DOWNLOAD_TYPE_NEWGROUNDS: namespaces = [ 'creator', 'title', '' ]
|
|
|
|
self._advanced_tag_options = ClientGUICommon.AdvancedTagOptions( self, 'send tags to ', namespaces )
|
|
|
|
self._advanced_import_options = ClientGUICommon.AdvancedImportOptions( self )
|
|
|
|
|
|
def PopulateControls():
|
|
|
|
if site_download_type == HC.SITE_DOWNLOAD_TYPE_BOORU:
|
|
|
|
boorus = HC.app.Read( 'boorus' )
|
|
|
|
for booru in boorus: self._booru_selector.Append( booru.GetName(), booru )
|
|
|
|
else: self._booru_selector.Hide()
|
|
|
|
#
|
|
|
|
self._original_info = ( site_download_type, name, query_type, query, frequency_type, frequency_number, advanced_tag_options, advanced_import_options, last_checked, url_cache, paused )
|
|
|
|
self._SetControls( *self._original_info )
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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 = HC.GetNow()
|
|
|
|
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 = HC.u( len( url_cache ) ) + ' urls in cache' ), FLAGS_EXPAND_PERPENDICULAR )
|
|
self._info_panel.AddF( self._paused, FLAGS_LONE_BUTTON )
|
|
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 )
|
|
|
|
|
|
wx.ScrolledWindow.__init__( self, parent )
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
self._reset_cache = False
|
|
|
|
self.SetScrollRate( 0, 20 )
|
|
|
|
self.SetMinSize( ( 540, 620 ) )
|
|
|
|
|
|
def _SetControls( self, site_download_type, name, query_type, query, frequency_type, frequency_number, advanced_tag_options, advanced_import_options, last_checked, url_cache, paused ):
|
|
|
|
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, HC.SITE_DOWNLOAD_TYPE_NEWGROUNDS ): 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._paused.SetValue( paused )
|
|
|
|
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, paused ) = 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()
|
|
|
|
|
|
paused = self._paused.GetValue()
|
|
|
|
return ( site_download_type, name, query_type, query, frequency_type, frequency_number, advanced_tag_options, advanced_import_options, last_checked, url_cache, paused )
|
|
|
|
|
|
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, paused ):
|
|
|
|
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, paused )
|
|
|
|
self._SetControls( *self._original_info )
|
|
|
|
|
|
|
|
class DialogManageTagParents( ClientGUIDialogs.Dialog ):
|
|
|
|
def __init__( self, parent, tag = None ):
|
|
|
|
def InitialiseControls():
|
|
|
|
self._tag_repositories = ClientGUICommon.ListBook( self )
|
|
self._tag_repositories.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGED, self.EventServiceChanged )
|
|
|
|
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 PopulateControls():
|
|
|
|
services = HC.app.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, tag ), {} )
|
|
|
|
name = service_identifier.GetName()
|
|
|
|
self._tag_repositories.AddPage( page_info, name )
|
|
|
|
|
|
|
|
page = self._Panel( self._tag_repositories, HC.LOCAL_TAG_SERVICE_IDENTIFIER, tag )
|
|
|
|
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() )
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
buttons = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
buttons.AddF( self._ok, FLAGS_SMALL_INDENT )
|
|
buttons.AddF( self._cancel, 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( ( 550, 680 ) )
|
|
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'tag parents' )
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
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( HC.u( e ) )
|
|
wx.MessageBox( traceback.format_exc() )
|
|
|
|
|
|
|
|
|
|
def EventOK( self, event ):
|
|
|
|
service_identifiers_to_content_updates = {}
|
|
|
|
try:
|
|
|
|
for page in self._tag_repositories.GetNameToPageDict().values():
|
|
|
|
( service_identifier, content_updates ) = page.GetContentUpdates()
|
|
|
|
service_identifiers_to_content_updates[ service_identifier ] = content_updates
|
|
|
|
|
|
HC.app.Write( 'content_updates', service_identifiers_to_content_updates )
|
|
|
|
except Exception as e: wx.MessageBox( 'Saving tag parent changes to DB raised this error: ' + HC.u( e ) )
|
|
|
|
self.EndModal( wx.ID_OK )
|
|
|
|
|
|
def EventServiceChanged( self, event ):
|
|
|
|
page = self._tag_repositories.GetCurrentPage()
|
|
|
|
wx.CallAfter( page.SetTagBoxFocus )
|
|
|
|
|
|
class _Panel( wx.Panel ):
|
|
|
|
def __init__( self, parent, service_identifier, tag = None ):
|
|
|
|
def InitialiseControls():
|
|
|
|
self._tag_parents = ClientGUICommon.SaneListCtrl( self, 250, [ ( '', 30 ), ( 'child', 160 ), ( 'parent', -1 ) ] )
|
|
self._tag_parents.Bind( wx.EVT_LIST_ITEM_ACTIVATED, self.EventActivated )
|
|
|
|
self._tag_parents.Bind( wx.EVT_LIST_ITEM_SELECTED, self.EventItemSelected )
|
|
self._tag_parents.Bind( wx.EVT_LIST_ITEM_DESELECTED, self.EventItemSelected )
|
|
|
|
self._child_text = wx.StaticText( self )
|
|
self._parent_text = wx.StaticText( self )
|
|
|
|
self._child_input = ClientGUICommon.AutoCompleteDropdownTagsWrite( self, self.SetChild, HC.LOCAL_FILE_SERVICE_IDENTIFIER, service_identifier )
|
|
self._parent_input = ClientGUICommon.AutoCompleteDropdownTagsWrite( self, self.SetParent, HC.LOCAL_FILE_SERVICE_IDENTIFIER, service_identifier )
|
|
|
|
self._add = wx.Button( self, label = 'add' )
|
|
self._add.Bind( wx.EVT_BUTTON, self.EventAddButton )
|
|
self._add.Disable()
|
|
|
|
|
|
def PopulateControls():
|
|
|
|
for ( status, pairs ) in self._original_statuses_to_pairs.items():
|
|
|
|
sign = HC.ConvertStatusToPrefix( status )
|
|
|
|
for ( child, parent ) in pairs: self._tag_parents.Append( ( sign, child, parent ), ( status, child, parent ) )
|
|
|
|
|
|
if tag is not None: self.SetChild( tag )
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
text_box = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
text_box.AddF( self._child_text, FLAGS_EXPAND_BOTH_WAYS )
|
|
text_box.AddF( self._parent_text, FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
input_box = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
input_box.AddF( self._child_input, FLAGS_EXPAND_BOTH_WAYS )
|
|
input_box.AddF( self._parent_input, FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
vbox.AddF( self._tag_parents, FLAGS_EXPAND_BOTH_WAYS )
|
|
vbox.AddF( self._add, FLAGS_LONE_BUTTON )
|
|
vbox.AddF( text_box, FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
vbox.AddF( input_box, FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
|
|
wx.Panel.__init__( self, parent )
|
|
|
|
self._service_identifier = service_identifier
|
|
|
|
if self._service_identifier != HC.LOCAL_TAG_SERVICE_IDENTIFIER:
|
|
|
|
service = HC.app.Read( 'service', service_identifier )
|
|
|
|
self._account = service.GetAccount()
|
|
|
|
|
|
self._original_statuses_to_pairs = HC.app.Read( 'tag_parents', service_identifier )
|
|
|
|
self._current_statuses_to_pairs = collections.defaultdict( set )
|
|
|
|
self._current_statuses_to_pairs.update( { key : set( value ) for ( key, value ) in self._original_statuses_to_pairs.items() } )
|
|
|
|
self._pairs_to_reasons = {}
|
|
|
|
self._current_parent = None
|
|
self._current_child = None
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
|
|
def _AddPair( self, child, parent ):
|
|
|
|
old_status = None
|
|
new_status = None
|
|
|
|
pair = ( child, parent )
|
|
|
|
pair_string = child + '->' + parent
|
|
|
|
if pair in self._current_statuses_to_pairs[ HC.CURRENT ]:
|
|
|
|
message = pair_string + ' already exists.'
|
|
|
|
with ClientGUIDialogs.DialogYesNo( self, message, yes_label = 'petition it', no_label = 'do nothing' ) as dlg:
|
|
|
|
if self._service_identifier != HC.LOCAL_TAG_SERVICE_IDENTIFIER:
|
|
|
|
if dlg.ShowModal() == wx.ID_YES:
|
|
|
|
if self._account.HasPermission( HC.RESOLVE_PETITIONS ): reason = 'admin'
|
|
else:
|
|
|
|
message = 'Enter a reason for this pair to be removed. A janitor will review your petition.'
|
|
|
|
with wx.TextEntryDialog( self, message ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK: reason = dlg.GetValue()
|
|
else: return
|
|
|
|
|
|
|
|
self._pairs_to_reasons[ pair ] = reason
|
|
|
|
else: return
|
|
|
|
|
|
old_status = HC.CURRENT
|
|
new_status = HC.PETITIONED
|
|
|
|
|
|
elif pair in self._current_statuses_to_pairs[ HC.PENDING ]:
|
|
|
|
message = pair_string + ' is pending.'
|
|
|
|
with ClientGUIDialogs.DialogYesNo( self, message, yes_label = 'rescind the pend', no_label = 'do nothing' ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_YES:
|
|
|
|
old_status = HC.PENDING
|
|
|
|
if pair in self._current_statuses_to_pairs[ HC.DELETED ]: new_status = HC.DELETED
|
|
|
|
else: return
|
|
|
|
|
|
elif pair in self._current_statuses_to_pairs[ HC.PETITIONED ]:
|
|
|
|
message = pair_string + ' is petitioned.'
|
|
|
|
with ClientGUIDialogs.DialogYesNo( self, message, yes_label = 'rescind the petition', no_label = 'do nothing' ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_YES:
|
|
|
|
old_status = HC.PETITIONED
|
|
new_status = HC.CURRENT
|
|
|
|
else: return
|
|
|
|
|
|
else:
|
|
|
|
if self._CanAdd( child, parent ):
|
|
|
|
if self._service_identifier != HC.LOCAL_TAG_SERVICE_IDENTIFIER:
|
|
|
|
if self._account.HasPermission( HC.RESOLVE_PETITIONS ): reason = 'admin'
|
|
else:
|
|
|
|
message = 'Enter a reason for ' + pair_string + ' to be added. A janitor will review your petition.'
|
|
|
|
with wx.TextEntryDialog( self, message ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK: reason = dlg.GetValue()
|
|
else: return
|
|
|
|
|
|
|
|
self._pairs_to_reasons[ pair ] = reason
|
|
|
|
|
|
if pair in self._current_statuses_to_pairs[ HC.DELETED ]: old_status = HC.DELETED
|
|
|
|
new_status = HC.PENDING
|
|
|
|
|
|
|
|
if old_status is not None:
|
|
|
|
self._current_statuses_to_pairs[ old_status ].discard( pair )
|
|
|
|
index = self._tag_parents.GetIndexFromClientData( ( old_status, child, parent ) )
|
|
|
|
self._tag_parents.DeleteItem( index )
|
|
|
|
|
|
if new_status is not None:
|
|
|
|
self._current_statuses_to_pairs[ new_status ].add( pair )
|
|
|
|
sign = HC.ConvertStatusToPrefix( new_status )
|
|
|
|
self._tag_parents.Append( ( sign, child, parent ), ( new_status, child, parent ) )
|
|
|
|
|
|
|
|
def _CanAdd( self, potential_child, potential_parent ):
|
|
|
|
if potential_child == potential_parent: return False
|
|
|
|
current_pairs = self._current_statuses_to_pairs[ HC.CURRENT ].union( self._current_statuses_to_pairs[ HC.PENDING ] )
|
|
|
|
current_children = { child for ( child, parent ) in current_pairs }
|
|
|
|
# test for loops
|
|
|
|
if potential_parent in current_children:
|
|
|
|
simple_children_to_parents = HydrusTags.BuildSimpleChildrenToParents( current_pairs )
|
|
|
|
if HydrusTags.LoopInSimpleChildrenToParents( simple_children_to_parents, potential_child, potential_parent ):
|
|
|
|
wx.MessageBox( 'Adding that pair would create a loop!' )
|
|
|
|
return False
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
def _SetButtonStatus( self ):
|
|
|
|
if self._current_parent is None or self._current_child is None: self._add.Disable()
|
|
else: self._add.Enable()
|
|
|
|
|
|
def EventActivated( self, event ):
|
|
|
|
all_selected = self._tag_parents.GetAllSelected()
|
|
|
|
if len( all_selected ) > 0:
|
|
|
|
selection = all_selected[0]
|
|
|
|
( status, child, parent ) = self._tag_parents.GetClientData( selection )
|
|
|
|
self._AddPair( child, parent )
|
|
|
|
|
|
|
|
def EventAddButton( self, event ):
|
|
|
|
if self._current_child is not None and self._current_parent is not None:
|
|
|
|
self._AddPair( self._current_child, self._current_parent )
|
|
|
|
self.SetChild( None )
|
|
self.SetParent( None )
|
|
|
|
|
|
|
|
def EventItemSelected( self, event ):
|
|
|
|
self._SetButtonStatus()
|
|
|
|
|
|
def GetContentUpdates( self ):
|
|
|
|
# we make it manually here because of the mass pending tags done (but not undone on a rescind) on a pending pair!
|
|
# we don't want to send a pend and then rescind it, cause that will spam a thousand bad tags and not undo it
|
|
|
|
content_updates = []
|
|
|
|
if self._service_identifier == HC.LOCAL_TAG_SERVICE_IDENTIFIER:
|
|
|
|
for pair in self._current_statuses_to_pairs[ HC.PENDING ]: content_updates.append( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_TAG_PARENTS, HC.CONTENT_UPDATE_ADD, pair ) )
|
|
for pair in self._current_statuses_to_pairs[ HC.PETITIONED ]: content_updates.append( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_TAG_PARENTS, HC.CONTENT_UPDATE_DELETE, pair ) )
|
|
|
|
else:
|
|
|
|
current_pending = self._current_statuses_to_pairs[ HC.PENDING ]
|
|
original_pending = self._original_statuses_to_pairs[ HC.PENDING ]
|
|
|
|
current_petitioned = self._current_statuses_to_pairs[ HC.PETITIONED ]
|
|
original_petitioned = self._original_statuses_to_pairs[ HC.PETITIONED ]
|
|
|
|
new_pends = current_pending.difference( original_pending )
|
|
rescinded_pends = original_pending.difference( current_pending )
|
|
|
|
new_petitions = current_petitioned.difference( original_petitioned )
|
|
rescinded_petitions = original_petitioned.difference( current_petitioned )
|
|
|
|
content_updates.extend( ( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_TAG_PARENTS, HC.CONTENT_UPDATE_PENDING, ( pair, self._pairs_to_reasons[ pair ] ) ) for pair in new_pends ) )
|
|
content_updates.extend( ( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_TAG_PARENTS, HC.CONTENT_UPDATE_RESCIND_PENDING, pair ) for pair in rescinded_pends ) )
|
|
content_updates.extend( ( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_TAG_PARENTS, HC.CONTENT_UPDATE_PETITION, ( pair, self._pairs_to_reasons[ pair ] ) ) for pair in new_petitions ) )
|
|
content_updates.extend( ( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_TAG_PARENTS, HC.CONTENT_UPDATE_RESCIND_PETITION, pair ) for pair in rescinded_petitions ) )
|
|
|
|
|
|
return ( self._service_identifier, content_updates )
|
|
|
|
|
|
def SetChild( self, tag, parents = [] ):
|
|
|
|
if tag is not None and tag == self._current_parent: self.SetParent( None )
|
|
|
|
self._current_child = tag
|
|
|
|
if tag is None: self._child_text.SetLabel( '' )
|
|
else: self._child_text.SetLabel( tag )
|
|
|
|
self._SetButtonStatus()
|
|
|
|
|
|
def SetParent( self, tag, parents = [] ):
|
|
|
|
if tag is not None and tag == self._current_child: self.SetChild( None )
|
|
|
|
self._current_parent = tag
|
|
|
|
if tag is None: self._parent_text.SetLabel( '' )
|
|
else: self._parent_text.SetLabel( tag )
|
|
|
|
self._SetButtonStatus()
|
|
|
|
|
|
def SetTagBoxFocus( self ):
|
|
|
|
if self._current_child is None: self._child_input.SetFocus()
|
|
else: self._parent_input.SetFocus()
|
|
|
|
|
|
|
|
class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|
|
|
def __init__( self, parent, tag = None ):
|
|
|
|
def InitialiseControls():
|
|
|
|
self._tag_repositories = ClientGUICommon.ListBook( self )
|
|
self._tag_repositories.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGED, self.EventServiceChanged )
|
|
|
|
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 PopulateControls():
|
|
|
|
page = self._Panel( self._tag_repositories, HC.LOCAL_TAG_SERVICE_IDENTIFIER, tag )
|
|
|
|
name = HC.LOCAL_TAG_SERVICE_IDENTIFIER.GetName()
|
|
|
|
self._tag_repositories.AddPage( page, name )
|
|
|
|
services = HC.app.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, tag ), {} )
|
|
|
|
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() )
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
buttons = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
buttons.AddF( self._ok, FLAGS_SMALL_INDENT )
|
|
buttons.AddF( self._cancel, 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( ( 550, 680 ) )
|
|
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'tag siblings' )
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
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( HC.u( e ) )
|
|
wx.MessageBox( traceback.format_exc() )
|
|
|
|
|
|
|
|
|
|
def EventOK( self, event ):
|
|
|
|
service_identifiers_to_content_updates = {}
|
|
|
|
try:
|
|
|
|
for page in self._tag_repositories.GetNameToPageDict().values():
|
|
|
|
( service_identifier, content_updates ) = page.GetContentUpdates()
|
|
|
|
service_identifiers_to_content_updates[ service_identifier ] = content_updates
|
|
|
|
|
|
HC.app.Write( 'content_updates', service_identifiers_to_content_updates )
|
|
|
|
except Exception as e: wx.MessageBox( 'Saving tag sibling changes to DB raised this error: ' + HC.u( e ) )
|
|
|
|
self.EndModal( wx.ID_OK )
|
|
|
|
|
|
def EventServiceChanged( self, event ):
|
|
|
|
page = self._tag_repositories.GetCurrentPage()
|
|
|
|
wx.CallAfter( page.SetTagBoxFocus )
|
|
|
|
|
|
class _Panel( wx.Panel ):
|
|
|
|
def __init__( self, parent, service_identifier, tag = None ):
|
|
|
|
def InitialiseControls():
|
|
|
|
self._tag_siblings = ClientGUICommon.SaneListCtrl( self, 250, [ ( '', 30 ), ( 'old', 160 ), ( 'new', -1 ) ] )
|
|
self._tag_siblings.Bind( wx.EVT_LIST_ITEM_ACTIVATED, self.EventActivated )
|
|
self._tag_siblings.Bind( wx.EVT_LIST_ITEM_SELECTED, self.EventItemSelected )
|
|
self._tag_siblings.Bind( wx.EVT_LIST_ITEM_DESELECTED, self.EventItemSelected )
|
|
|
|
self._old_text = wx.StaticText( self )
|
|
self._new_text = wx.StaticText( self )
|
|
|
|
self._old_input = ClientGUICommon.AutoCompleteDropdownTagsWrite( self, self.SetOld, HC.LOCAL_FILE_SERVICE_IDENTIFIER, service_identifier )
|
|
self._new_input = ClientGUICommon.AutoCompleteDropdownTagsWrite( self, self.SetNew, HC.LOCAL_FILE_SERVICE_IDENTIFIER, service_identifier )
|
|
|
|
self._add = wx.Button( self, label = 'add' )
|
|
self._add.Bind( wx.EVT_BUTTON, self.EventAddButton )
|
|
self._add.Disable()
|
|
|
|
|
|
def PopulateControls():
|
|
|
|
for ( status, pairs ) in self._original_statuses_to_pairs.items():
|
|
|
|
sign = HC.ConvertStatusToPrefix( status )
|
|
|
|
for ( old, new ) in pairs: self._tag_siblings.Append( ( sign, old, new ), ( status, old, new ) )
|
|
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
text_box = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
text_box.AddF( self._old_text, FLAGS_EXPAND_BOTH_WAYS )
|
|
text_box.AddF( self._new_text, FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
input_box = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
input_box.AddF( self._old_input, FLAGS_EXPAND_BOTH_WAYS )
|
|
input_box.AddF( self._new_input, FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
vbox.AddF( self._tag_siblings, FLAGS_EXPAND_BOTH_WAYS )
|
|
vbox.AddF( self._add, FLAGS_LONE_BUTTON )
|
|
vbox.AddF( text_box, FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
vbox.AddF( input_box, FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
|
|
wx.Panel.__init__( self, parent )
|
|
|
|
self._service_identifier = service_identifier
|
|
|
|
if self._service_identifier != HC.LOCAL_TAG_SERVICE_IDENTIFIER:
|
|
|
|
service = HC.app.Read( 'service', service_identifier )
|
|
|
|
self._account = service.GetAccount()
|
|
|
|
|
|
self._original_statuses_to_pairs = HC.app.Read( 'tag_siblings', service_identifier )
|
|
|
|
self._current_statuses_to_pairs = collections.defaultdict( set )
|
|
|
|
self._current_statuses_to_pairs.update( { key : set( value ) for ( key, value ) in self._original_statuses_to_pairs.items() } )
|
|
|
|
self._pairs_to_reasons = {}
|
|
|
|
self._current_old = None
|
|
self._current_new = None
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
if tag is not None: self.SetOld( tag )
|
|
|
|
|
|
def _AddPair( self, old, new ):
|
|
|
|
old_status = None
|
|
new_status = None
|
|
|
|
pair = ( old, new )
|
|
|
|
pair_string = old + '->' + new
|
|
|
|
if pair in self._current_statuses_to_pairs[ HC.CURRENT ]:
|
|
|
|
message = pair_string + ' already exists.'
|
|
|
|
with ClientGUIDialogs.DialogYesNo( self, message, yes_label = 'petition it', no_label = 'do nothing' ) as dlg:
|
|
|
|
if self._service_identifier != HC.LOCAL_TAG_SERVICE_IDENTIFIER:
|
|
|
|
if dlg.ShowModal() == wx.ID_YES:
|
|
|
|
if self._account.HasPermission( HC.RESOLVE_PETITIONS ): reason = 'admin'
|
|
else:
|
|
|
|
message = 'Enter a reason for this pair to be removed. A janitor will review your petition.'
|
|
|
|
with wx.TextEntryDialog( self, message ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK: reason = dlg.GetValue()
|
|
else: return
|
|
|
|
|
|
|
|
self._pairs_to_reasons[ pair ] = reason
|
|
|
|
else: return
|
|
|
|
|
|
old_status = HC.CURRENT
|
|
new_status = HC.PETITIONED
|
|
|
|
|
|
elif pair in self._current_statuses_to_pairs[ HC.PENDING ]:
|
|
|
|
message = pair_string + ' is pending.'
|
|
|
|
with ClientGUIDialogs.DialogYesNo( self, message, yes_label = 'rescind the pend', no_label = 'do nothing' ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_YES:
|
|
|
|
old_status = HC.PENDING
|
|
|
|
if pair in self._current_statuses_to_pairs[ HC.DELETED ]: new_status = HC.DELETED
|
|
|
|
else: return
|
|
|
|
|
|
elif pair in self._current_statuses_to_pairs[ HC.PETITIONED ]:
|
|
|
|
message = pair_string + ' is petitioned.'
|
|
|
|
with ClientGUIDialogs.DialogYesNo( self, message, yes_label = 'rescind the petition', no_label = 'do nothing' ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_YES:
|
|
|
|
old_status = HC.PETITIONED
|
|
new_status = HC.CURRENT
|
|
|
|
else: return
|
|
|
|
|
|
else:
|
|
|
|
if self._CanAdd( old, new ):
|
|
|
|
if self._service_identifier != HC.LOCAL_TAG_SERVICE_IDENTIFIER:
|
|
|
|
if self._account.HasPermission( HC.RESOLVE_PETITIONS ): reason = 'admin'
|
|
else:
|
|
|
|
message = 'Enter a reason for ' + pair_string + ' to be added. A janitor will review your petition.'
|
|
|
|
with wx.TextEntryDialog( self, message ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK: reason = dlg.GetValue()
|
|
else: return
|
|
|
|
|
|
|
|
self._pairs_to_reasons[ pair ] = reason
|
|
|
|
|
|
if pair in self._current_statuses_to_pairs[ HC.DELETED ]: old_status = HC.DELETED
|
|
|
|
new_status = HC.PENDING
|
|
|
|
|
|
|
|
if old_status is not None:
|
|
|
|
self._current_statuses_to_pairs[ old_status ].discard( pair )
|
|
|
|
index = self._tag_siblings.GetIndexFromClientData( ( old_status, old, new ) )
|
|
|
|
self._tag_siblings.DeleteItem( index )
|
|
|
|
|
|
if new_status is not None:
|
|
|
|
self._current_statuses_to_pairs[ new_status ].add( pair )
|
|
|
|
sign = HC.ConvertStatusToPrefix( new_status )
|
|
|
|
self._tag_siblings.Append( ( sign, old, new ), ( new_status, old, new ) )
|
|
|
|
|
|
|
|
def _CanAdd( self, potential_old, potential_new ):
|
|
|
|
current_pairs = self._current_statuses_to_pairs[ HC.CURRENT ].union( self._current_statuses_to_pairs[ HC.PENDING ] )
|
|
|
|
current_olds = { old for ( old, new ) in current_pairs }
|
|
|
|
# test for ambiguity
|
|
|
|
if potential_old in current_olds:
|
|
|
|
wx.MessageBox( 'There already is a relationship set for the tag ' + potential_old + '.' )
|
|
|
|
return False
|
|
|
|
|
|
# test for loops
|
|
|
|
if potential_new in current_olds:
|
|
|
|
d = dict( current_pairs )
|
|
|
|
next_new = potential_new
|
|
|
|
while next_new in d:
|
|
|
|
next_new = d[ next_new ]
|
|
|
|
if next_new == potential_old:
|
|
|
|
wx.MessageBox( 'Adding that pair would create a loop!' )
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
def _SetButtonStatus( self ):
|
|
|
|
if self._current_new is None or self._current_old is None: self._add.Disable()
|
|
else: self._add.Enable()
|
|
|
|
|
|
def EventActivated( self, event ):
|
|
|
|
all_selected = self._tag_siblings.GetAllSelected()
|
|
|
|
if len( all_selected ) > 0:
|
|
|
|
selection = all_selected[0]
|
|
|
|
( status, old, new ) = self._tag_siblings.GetClientData( selection )
|
|
|
|
self._AddPair( old, new )
|
|
|
|
|
|
|
|
def EventAddButton( self, event ):
|
|
|
|
if self._current_old is not None and self._current_new is not None:
|
|
|
|
self._AddPair( self._current_old, self._current_new )
|
|
|
|
self.SetOld( None )
|
|
self.SetNew( None )
|
|
|
|
|
|
|
|
def EventItemSelected( self, event ):
|
|
|
|
self._SetButtonStatus()
|
|
|
|
|
|
def GetContentUpdates( self ):
|
|
|
|
# we make it manually here because of the mass pending tags done (but not undone on a rescind) on a pending pair!
|
|
# we don't want to send a pend and then rescind it, cause that will spam a thousand bad tags and not undo it
|
|
|
|
# actually, we don't do this for siblings, but we do for parents, and let's have them be the same
|
|
|
|
content_updates = []
|
|
|
|
if self._service_identifier == HC.LOCAL_TAG_SERVICE_IDENTIFIER:
|
|
|
|
for pair in self._current_statuses_to_pairs[ HC.PENDING ]: content_updates.append( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_TAG_SIBLINGS, HC.CONTENT_UPDATE_ADD, pair ) )
|
|
for pair in self._current_statuses_to_pairs[ HC.PETITIONED ]: content_updates.append( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_TAG_SIBLINGS, HC.CONTENT_UPDATE_DELETE, pair ) )
|
|
|
|
else:
|
|
|
|
current_pending = self._current_statuses_to_pairs[ HC.PENDING ]
|
|
original_pending = self._original_statuses_to_pairs[ HC.PENDING ]
|
|
|
|
current_petitioned = self._current_statuses_to_pairs[ HC.PETITIONED ]
|
|
original_petitioned = self._original_statuses_to_pairs[ HC.PETITIONED ]
|
|
|
|
new_pends = current_pending.difference( original_pending )
|
|
rescinded_pends = original_pending.difference( current_pending )
|
|
|
|
new_petitions = current_petitioned.difference( original_petitioned )
|
|
rescinded_petitions = original_petitioned.difference( current_petitioned )
|
|
|
|
content_updates.extend( ( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_TAG_SIBLINGS, HC.CONTENT_UPDATE_PENDING, ( pair, self._pairs_to_reasons[ pair ] ) ) for pair in new_pends ) )
|
|
content_updates.extend( ( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_TAG_SIBLINGS, HC.CONTENT_UPDATE_RESCIND_PENDING, pair ) for pair in rescinded_pends ) )
|
|
content_updates.extend( ( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_TAG_SIBLINGS, HC.CONTENT_UPDATE_PETITION, ( pair, self._pairs_to_reasons[ pair ] ) ) for pair in new_petitions ) )
|
|
content_updates.extend( ( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_TAG_SIBLINGS, HC.CONTENT_UPDATE_RESCIND_PETITION, pair ) for pair in rescinded_petitions ) )
|
|
|
|
|
|
return ( self._service_identifier, content_updates )
|
|
|
|
|
|
def SetNew( self, new, parents = [] ):
|
|
|
|
if new is not None and new == self._current_old: self.SetOld( None )
|
|
|
|
self._current_new = new
|
|
|
|
if new is None: self._new_text.SetLabel( '' )
|
|
else: self._new_text.SetLabel( new )
|
|
|
|
self._SetButtonStatus()
|
|
|
|
|
|
def SetOld( self, old, parents = [] ):
|
|
|
|
if old is not None:
|
|
|
|
current_pairs = self._current_statuses_to_pairs[ HC.CURRENT ].union( self._current_statuses_to_pairs[ HC.PENDING ] )
|
|
|
|
current_olds = { current_old for ( current_old, current_new ) in current_pairs }
|
|
|
|
# test for ambiguity
|
|
|
|
while old in current_olds:
|
|
|
|
olds_to_news = dict( current_pairs )
|
|
|
|
new = olds_to_news[ old ]
|
|
|
|
message = 'There already is a relationship set for ' + old + '! It goes to ' + new + '.'
|
|
|
|
with ClientGUIDialogs.DialogYesNo( self, message, yes_label = 'I want to overwrite it', no_label = 'do nothing' ) as dlg:
|
|
|
|
if self._service_identifier != HC.LOCAL_TAG_SERVICE_IDENTIFIER:
|
|
|
|
if dlg.ShowModal() != wx.ID_YES: return
|
|
|
|
self._AddPair( old, new )
|
|
|
|
|
|
|
|
current_pairs = self._current_statuses_to_pairs[ HC.CURRENT ].union( self._current_statuses_to_pairs[ HC.PENDING ] )
|
|
|
|
current_olds = { current_old for ( current_old, current_new ) in current_pairs }
|
|
|
|
|
|
|
|
#
|
|
|
|
if old is not None and old == self._current_new: self.SetNew( None )
|
|
|
|
self._current_old = old
|
|
|
|
if old is None: self._old_text.SetLabel( '' )
|
|
else: self._old_text.SetLabel( old )
|
|
|
|
self._SetButtonStatus()
|
|
|
|
|
|
def SetTagBoxFocus( self ):
|
|
|
|
if self._current_old is None: self._old_input.SetFocus()
|
|
else: self._new_input.SetFocus()
|
|
|
|
|
|
|
|
class DialogManageTagServicePrecedence( ClientGUIDialogs.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 )
|
|
|
|
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 PopulateControls():
|
|
|
|
tag_service_precedence = HC.app.Read( 'tag_service_precedence' )
|
|
|
|
for service_identifier in tag_service_precedence:
|
|
|
|
name = service_identifier.GetName()
|
|
|
|
self._tag_services.Append( name, service_identifier )
|
|
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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 ) )
|
|
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'manage tag service precedence' )
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
wx.CallAfter( self._apply.SetFocus )
|
|
|
|
|
|
def EventCancel( self, event ): self.EndModal( wx.ID_CANCEL )
|
|
|
|
def EventOK( self, event ):
|
|
|
|
message = 'This operation may take several minutes to complete. Are you sure?'
|
|
|
|
with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_YES:
|
|
|
|
try:
|
|
|
|
service_identifiers = [ self._tag_services.GetClientData( i ) for i in range( self._tag_services.GetCount() ) ]
|
|
|
|
HC.app.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: ' + HC.u( 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( ClientGUIDialogs.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 )
|
|
|
|
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 PopulateControls():
|
|
|
|
service_identifiers = HC.app.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() )
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
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 = set()
|
|
|
|
for m in media: self._hashes.update( m.GetHashes() )
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'manage tags for ' + HC.ConvertIntToPrettyString( len( self._hashes ) ) + ' files' )
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
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:
|
|
|
|
service_identfiers_to_content_updates = {}
|
|
|
|
for page in self._tag_repositories.GetNameToPageDict().values():
|
|
|
|
( service_identifier, content_updates ) = page.GetContentUpdates()
|
|
|
|
service_identfiers_to_content_updates[ service_identifier ] = content_updates
|
|
|
|
|
|
if len( service_identfiers_to_content_updates ) > 0: HC.app.Write( 'content_updates', service_identfiers_to_content_updates )
|
|
|
|
except Exception as e: wx.MessageBox( 'Saving mapping changes to DB raised this error: ' + HC.u( 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._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 )
|
|
|
|
|
|
def PopulateControls():
|
|
|
|
pass
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
|
|
|
|
if self._i_am_local_tag_service: 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()
|
|
|
|
|
|
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( self._modify_mappers, 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._hashes = { hash for hash in itertools.chain.from_iterable( ( m.GetHashes() for m in media ) ) }
|
|
|
|
self._content_updates = []
|
|
|
|
if not self._i_am_local_tag_service:
|
|
|
|
service = HC.app.Read( 'service', tag_service_identifier )
|
|
|
|
self._account = service.GetAccount()
|
|
|
|
|
|
tags_managers = [ m.GetTagsManager() for m in media ]
|
|
|
|
( self._current_tags, self._deleted_tags, self._pending_tags, self._petitioned_tags ) = CC.IntersectTags( tags_managers, tag_service_identifier )
|
|
|
|
self._current_tags.sort()
|
|
self._pending_tags.sort()
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
|
|
def _AddTag( self, tag, only_add = False ):
|
|
|
|
if self._i_am_local_tag_service:
|
|
|
|
if tag in self._pending_tags:
|
|
|
|
if only_add: return
|
|
|
|
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:
|
|
|
|
if only_add: return
|
|
|
|
self._petitioned_tags.append( tag )
|
|
|
|
self._tags_box.PetitionTag( tag )
|
|
|
|
else:
|
|
|
|
self._pending_tags.append( tag )
|
|
|
|
self._tags_box.PendTag( tag )
|
|
|
|
|
|
self._content_updates = []
|
|
|
|
self._content_updates.extend( [ HC.ContentUpdate( HC.CONTENT_DATA_TYPE_MAPPINGS, HC.CONTENT_UPDATE_ADD, ( tag, self._hashes ) ) for tag in self._pending_tags ] )
|
|
self._content_updates.extend( [ HC.ContentUpdate( HC.CONTENT_DATA_TYPE_MAPPINGS, HC.CONTENT_UPDATE_DELETE, ( tag, self._hashes ) ) for tag in self._petitioned_tags ] )
|
|
|
|
else:
|
|
|
|
if tag in self._pending_tags:
|
|
|
|
if only_add: return
|
|
|
|
self._pending_tags.remove( tag )
|
|
|
|
self._tags_box.RescindPend( tag )
|
|
|
|
self._content_updates.append( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_MAPPINGS, HC.CONTENT_UPDATE_RESCIND_PENDING, ( tag, self._hashes ) ) )
|
|
|
|
elif tag in self._petitioned_tags:
|
|
|
|
self._petitioned_tags.remove( tag )
|
|
|
|
self._tags_box.RescindPetition( tag )
|
|
|
|
self._content_updates.append( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_MAPPINGS, HC.CONTENT_UPDATE_RESCIND_PETITION, ( tag, self._hashes ) ) )
|
|
|
|
elif tag in self._current_tags:
|
|
|
|
if only_add: return
|
|
|
|
if self._account.HasPermission( HC.RESOLVE_PETITIONS ):
|
|
|
|
self._content_updates.append( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_MAPPINGS, HC.CONTENT_UPDATE_PETITION, ( tag, self._hashes, '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._content_updates.append( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_MAPPINGS, HC.CONTENT_UPDATE_PETITION, ( tag, self._hashes, dlg.GetValue() ) ) )
|
|
|
|
self._petitioned_tags.append( tag )
|
|
|
|
self._tags_box.PetitionTag( tag )
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
self._content_updates.append( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_MAPPINGS, HC.CONTENT_UPDATE_PENDING, ( tag, self._hashes ) ) )
|
|
|
|
self._pending_tags.append( tag )
|
|
|
|
self._tags_box.PendTag( tag )
|
|
|
|
|
|
|
|
|
|
def AddTag( self, tag, parents = [] ):
|
|
|
|
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:
|
|
|
|
self._AddTag( tag )
|
|
|
|
for parent in parents: self._AddTag( parent, only_add = True )
|
|
|
|
|
|
|
|
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 ClientGUIDialogs.DialogModifyAccounts( self, self._tag_service_identifier, subject_identifiers ) as dlg: dlg.ShowModal()
|
|
|
|
except Exception as e: wx.MessageBox( HC.u( 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 EventTagsBoxAction( self, event ):
|
|
|
|
tag = self._tags_box.GetSelectedTag()
|
|
|
|
if tag is not None: self.AddTag( tag )
|
|
|
|
|
|
def GetContentUpdates( self ): return ( self._tag_service_identifier, self._content_updates )
|
|
|
|
def GetServiceIdentifier( self ): return self._tag_service_identifier
|
|
|
|
def HasChanges( self ): return len( self._content_updates ) > 0
|
|
|
|
def SetTagBoxFocus( self ):
|
|
|
|
if self._i_am_local_tag_service or self._account.HasPermission( HC.POST_DATA ): self._add_tag_box.SetFocus()
|
|
|
|
|
|
|