2881 lines
106 KiB
Python
2881 lines
106 KiB
Python
import ClientCaches
|
|
import ClientConstants as CC
|
|
import ClientData
|
|
import ClientDefaults
|
|
import ClientDownloading
|
|
import ClientDragDrop
|
|
import ClientExporting
|
|
import ClientFiles
|
|
import ClientGUIACDropdown
|
|
import ClientGUICommon
|
|
import ClientGUIListBoxes
|
|
import ClientGUIListCtrl
|
|
import ClientGUIDialogs
|
|
import ClientGUIImport
|
|
import ClientGUIOptionsPanels
|
|
import ClientGUIPredicates
|
|
import ClientGUIScrolledPanelsEdit
|
|
import ClientGUIShortcuts
|
|
import ClientGUIFileSeedCache
|
|
import ClientGUITime
|
|
import ClientGUITopLevelWindows
|
|
import ClientImporting
|
|
import ClientImportLocal
|
|
import ClientImportOptions
|
|
import ClientMedia
|
|
import ClientRatings
|
|
import ClientSearch
|
|
import ClientServices
|
|
import ClientThreading
|
|
import collections
|
|
import HydrusConstants as HC
|
|
import HydrusData
|
|
import HydrusExceptions
|
|
import HydrusGlobals as HG
|
|
import HydrusNATPunch
|
|
import HydrusNetwork
|
|
import HydrusPaths
|
|
import HydrusSerialisable
|
|
import HydrusTagArchive
|
|
import HydrusTags
|
|
import HydrusText
|
|
import itertools
|
|
import os
|
|
import random
|
|
import re
|
|
import string
|
|
import time
|
|
import traceback
|
|
import wx
|
|
import yaml
|
|
|
|
# Option Enums
|
|
|
|
ID_NULL = wx.NewId()
|
|
|
|
ID_TIMER_UPDATE = wx.NewId()
|
|
|
|
'''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, 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.SetForegroundColour( ( 128, 0, 0 ) )
|
|
|
|
|
|
def PopulateControls():
|
|
|
|
self._edit_log = []
|
|
|
|
( identities, contacts, deletable_names ) = HG.client_controller.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.Add( self._add_manually, CC.FLAGS_VCENTER )
|
|
add_remove_hbox.Add( self._add_contact_address, CC.FLAGS_VCENTER )
|
|
add_remove_hbox.Add( self._remove, CC.FLAGS_VCENTER )
|
|
add_remove_hbox.Add( self._export, CC.FLAGS_VCENTER )
|
|
|
|
ok_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
|
ok_hbox.Add( self._ok, CC.FLAGS_VCENTER )
|
|
ok_hbox.Add( self._cancel, CC.FLAGS_VCENTER )
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
vbox.Add( self._contacts, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
vbox.Add( add_remove_hbox, CC.FLAGS_SMALL_INDENT )
|
|
vbox.Add( ok_hbox, CC.FLAGS_BUTTON_SIZER )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'manage contacts' )
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
( x, y ) = self.GetEffectiveMinSize()
|
|
|
|
self.SetInitialSize( ( 980, y ) )
|
|
|
|
self.SetDropTarget( ClientDragDrop.FileDropTarget( filenames_callable = 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( HydrusData.ToUnicode( e ) )
|
|
|
|
return
|
|
|
|
|
|
with ClientGUIDialogs.DialogTextEntry( self, 'Enter contact\'s address in the form contact_key@hostname:port.' ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
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( ( HC.ADD, contact ) )
|
|
|
|
page = self._Panel( self._contacts, contact, is_identity = False )
|
|
|
|
self._deletable_names.add( name )
|
|
|
|
self._contacts.AddPage( page, name, select = True )
|
|
|
|
|
|
|
|
|
|
def EventAddManually( self, event ):
|
|
|
|
try: self._CheckCurrentContactIsValid()
|
|
except Exception as e:
|
|
|
|
wx.MessageBox( HydrusData.ToUnicode( e ) )
|
|
|
|
return
|
|
|
|
|
|
with ClientGUIDialogs.DialogTextEntry( self, 'Enter new contact\'s name.' ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
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( ( HC.ADD, contact ) )
|
|
|
|
page = self._Panel( self._contacts, contact, is_identity = False )
|
|
|
|
self._deletable_names.add( name )
|
|
|
|
self._contacts.AddPage( page, name, select = True )
|
|
|
|
|
|
|
|
|
|
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( HydrusData.ToUnicode( 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:
|
|
|
|
path = HydrusData.ToUnicode( dlg.GetPath() )
|
|
|
|
with open( path, '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:
|
|
|
|
path = HydrusData.ToUnicode( dlg.GetPath() )
|
|
|
|
with open( path, 'wb' ) as f: f.write( yaml.safe_dump( contact ) )
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def EventOK( self, event ):
|
|
|
|
try: self._CheckCurrentContactIsValid()
|
|
except Exception as e:
|
|
|
|
wx.MessageBox( HydrusData.ToUnicode( e ) )
|
|
|
|
return
|
|
|
|
|
|
for ( name, page ) in self._contacts.GetNamesToActivePages().items():
|
|
|
|
if page.HasChanges(): self._edit_log.append( ( HC.EDIT, ( page.GetOriginalName(), page.GetContact() ) ) )
|
|
|
|
|
|
try:
|
|
|
|
if len( self._edit_log ) > 0: HG.client_controller.Write( 'update_contacts', self._edit_log )
|
|
|
|
finally: 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( ( HC.DELETE, name ) )
|
|
|
|
self._contacts.DeleteCurrentPage()
|
|
|
|
self._deletable_names.discard( name )
|
|
|
|
|
|
|
|
def Import( self, paths ):
|
|
|
|
try: self._CheckCurrentContactIsValid()
|
|
except Exception as e:
|
|
|
|
wx.MessageBox( HydrusData.ToUnicode( e ) )
|
|
|
|
return
|
|
|
|
|
|
for path in paths:
|
|
|
|
try:
|
|
|
|
with open( path, 'rb' ) as f: file = f.read()
|
|
|
|
obj = yaml.safe_load( file )
|
|
|
|
if type( obj ) == ClientConstantsMessages.Contact:
|
|
|
|
contact = obj
|
|
|
|
name = contact.GetName()
|
|
|
|
if self._contacts.NameExists( name ) or self._contacts.NameExists( ' identities - ' + name ) or name == 'Anonymous':
|
|
|
|
message = 'There already exists a contact or identity with the name ' + name + '. Do you want to overwrite, or make a new contact?'
|
|
|
|
with ClientGUIDialogs.DialogYesNo( self, message, title = 'Please choose what to do.', yes_label = 'overwrite', no_label = 'make new' ) as dlg:
|
|
|
|
if True:
|
|
|
|
name_to_page_dict = self._contacts.GetNamesToActivePages()
|
|
|
|
if name in name_to_page_dict: page = name_to_page_dict[ name ]
|
|
else: page = name_to_page_dict[ ' identities - ' + name ]
|
|
|
|
page.Update( contact )
|
|
|
|
else:
|
|
|
|
while self._contacts.NameExists( name ) or self._contacts.NameExists( ' identities - ' + name ) or name == 'Anonymous': name = name + str( random.randint( 0, 9 ) )
|
|
|
|
( public_key, old_name, host, port ) = contact.GetInfo()
|
|
|
|
new_contact = ClientConstantsMessages.Contact( public_key, name, host, port )
|
|
|
|
self._edit_log.append( ( HC.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( ( HC.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 = ClientGUICommon.SaneMultilineTextCtrl( self._contact_panel )
|
|
|
|
|
|
def PopulateControls():
|
|
|
|
self._name.SetValue( name )
|
|
|
|
contact_address = host + ':' + str( port )
|
|
|
|
if contact_key is not None: contact_address = contact_key.encode( 'hex' ) + '@' + contact_address
|
|
|
|
self._contact_address.SetValue( contact_address )
|
|
|
|
if public_key is not None: self._public_key.SetValue( public_key )
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
gridbox = wx.FlexGridSizer( 2 )
|
|
|
|
gridbox.AddGrowableCol( 1, 1 )
|
|
|
|
gridbox.Add( wx.StaticText( self._contact_panel, label = 'name' ), CC.FLAGS_VCENTER )
|
|
gridbox.Add( self._name, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
gridbox.Add( wx.StaticText( self._contact_panel, label = 'contact address' ), CC.FLAGS_VCENTER )
|
|
gridbox.Add( self._contact_address, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
gridbox.Add( wx.StaticText( self._contact_panel, label = 'public key' ), CC.FLAGS_VCENTER )
|
|
gridbox.Add( self._public_key, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
self._contact_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
vbox.Add( self._contact_panel, CC.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 + ':' + str( port )
|
|
|
|
if contact_key is not None: contact_address = contact_key.encode( 'hex' ) + '@' + contact_address
|
|
|
|
self._contact_address.SetValue( contact_address )
|
|
|
|
if public_key is None: public_key = ''
|
|
|
|
self._public_key.SetValue( public_key )
|
|
|
|
|
|
'''
|
|
class DialogManageExportFolders( ClientGUIDialogs.Dialog ):
|
|
|
|
def __init__( self, parent ):
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'manage export folders' )
|
|
|
|
columns = [ ( 'name', 20 ), ( 'path', -1 ), ( 'type', 12 ), ( 'query', 16 ), ( 'period', 10 ), ( 'phrase', 20 ) ]
|
|
|
|
self._export_folders = ClientGUIListCtrl.BetterListCtrl( self, 'export_folders', 6, 40, columns, self._ConvertExportFolderToListCtrlTuples, use_simple_delete = True, activation_callback = self.Edit )
|
|
|
|
export_folders = HG.client_controller.Read( 'serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_EXPORT_FOLDER )
|
|
|
|
self._export_folders.AddDatas( export_folders )
|
|
|
|
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.SetForegroundColour( ( 128, 0, 0 ) )
|
|
|
|
file_buttons = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
file_buttons.Add( self._add_button, CC.FLAGS_VCENTER )
|
|
file_buttons.Add( self._edit_button, CC.FLAGS_VCENTER )
|
|
file_buttons.Add( self._delete_button, CC.FLAGS_VCENTER )
|
|
|
|
buttons = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
buttons.Add( self._ok, CC.FLAGS_VCENTER )
|
|
buttons.Add( self._cancel, CC.FLAGS_VCENTER )
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
intro = 'Here you can set the client to regularly export a certain query to a particular location.'
|
|
|
|
vbox.Add( ClientGUICommon.BetterStaticText( self, intro ), CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
vbox.Add( self._export_folders, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
vbox.Add( file_buttons, CC.FLAGS_BUTTON_SIZER )
|
|
vbox.Add( buttons, CC.FLAGS_BUTTON_SIZER )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
( x, y ) = self.GetEffectiveMinSize()
|
|
|
|
if x < 780: x = 780
|
|
if y < 480: y = 480
|
|
|
|
self.SetInitialSize( ( x, y ) )
|
|
|
|
wx.CallAfter( self._ok.SetFocus )
|
|
|
|
|
|
def _AddFolder( self ):
|
|
|
|
new_options = HG.client_controller.new_options
|
|
|
|
phrase = new_options.GetString( 'export_phrase' )
|
|
|
|
name = 'export folder'
|
|
path = ''
|
|
export_type = HC.EXPORT_FOLDER_TYPE_REGULAR
|
|
file_search_context = ClientSearch.FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY )
|
|
period = 15 * 60
|
|
|
|
export_folder = ClientExporting.ExportFolder( name, path, export_type = export_type, file_search_context = file_search_context, period = period, phrase = phrase )
|
|
|
|
with DialogManageExportFoldersEdit( self, export_folder ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
export_folder = dlg.GetInfo()
|
|
|
|
export_folder.SetNonDupeName( self._GetExistingNames() )
|
|
|
|
self._export_folders.AddDatas( ( export_folder, ) )
|
|
|
|
|
|
|
|
|
|
def _ConvertExportFolderToListCtrlTuples( self, export_folder ):
|
|
|
|
( name, path, export_type, file_search_context, period, phrase ) = export_folder.ToTuple()
|
|
|
|
if export_type == HC.EXPORT_FOLDER_TYPE_REGULAR:
|
|
|
|
pretty_export_type = 'regular'
|
|
|
|
elif export_type == HC.EXPORT_FOLDER_TYPE_SYNCHRONISE:
|
|
|
|
pretty_export_type = 'synchronise'
|
|
|
|
|
|
pretty_file_search_context = ', '.join( predicate.GetUnicode( with_count = False ) for predicate in file_search_context.GetPredicates() )
|
|
|
|
pretty_period = HydrusData.TimeDeltaToPrettyTimeDelta( period )
|
|
|
|
pretty_phrase = phrase
|
|
|
|
display_tuple = ( name, path, pretty_export_type, pretty_file_search_context, pretty_period, pretty_phrase )
|
|
|
|
sort_tuple = ( name, path, pretty_export_type, pretty_file_search_context, period, phrase )
|
|
|
|
return ( display_tuple, sort_tuple )
|
|
|
|
|
|
def _GetExistingNames( self ):
|
|
|
|
existing_names = { export_folder.GetName() for export_folder in self._export_folders.GetData() }
|
|
|
|
return existing_names
|
|
|
|
|
|
def Edit( self ):
|
|
|
|
export_folders = self._export_folders.GetData( only_selected = True )
|
|
|
|
for export_folder in export_folders:
|
|
|
|
with DialogManageExportFoldersEdit( self, export_folder ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
self._export_folders.DeleteDatas( ( export_folder, ) )
|
|
|
|
export_folder = dlg.GetInfo()
|
|
|
|
export_folder.SetNonDupeName( self._GetExistingNames() )
|
|
|
|
self._export_folders.AddDatas( ( export_folder, ) )
|
|
|
|
else:
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
def EventAdd( self, event ):
|
|
|
|
self._AddFolder()
|
|
|
|
|
|
def EventDelete( self, event ):
|
|
|
|
self.Delete()
|
|
|
|
|
|
def EventEdit( self, event ):
|
|
|
|
self.Edit()
|
|
|
|
|
|
def EventOK( self, event ):
|
|
|
|
existing_db_names = set( HG.client_controller.Read( 'serialisable_names', HydrusSerialisable.SERIALISABLE_TYPE_EXPORT_FOLDER ) )
|
|
|
|
export_folders = self._export_folders.GetData()
|
|
|
|
good_names = set()
|
|
|
|
for export_folder in export_folders:
|
|
|
|
HG.client_controller.Write( 'serialisable', export_folder )
|
|
|
|
good_names.add( export_folder.GetName() )
|
|
|
|
|
|
names_to_delete = existing_db_names - good_names
|
|
|
|
for name in names_to_delete:
|
|
|
|
HG.client_controller.Write( 'delete_serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_EXPORT_FOLDER, name )
|
|
|
|
|
|
HG.client_controller.pub( 'notify_new_export_folders' )
|
|
|
|
self.EndModal( wx.ID_OK )
|
|
|
|
|
|
class DialogManageExportFoldersEdit( ClientGUIDialogs.Dialog ):
|
|
|
|
def __init__( self, parent, export_folder ):
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'edit export folder' )
|
|
|
|
self._export_folder = export_folder
|
|
|
|
( name, path, export_type, file_search_context, period, phrase ) = self._export_folder.ToTuple()
|
|
|
|
self._path_box = ClientGUICommon.StaticBox( self, 'name and location' )
|
|
|
|
self._name = wx.TextCtrl( self._path_box)
|
|
|
|
self._path = wx.DirPickerCtrl( self._path_box, style = wx.DIRP_USE_TEXTCTRL )
|
|
|
|
#
|
|
|
|
self._type_box = ClientGUICommon.StaticBox( self, 'type of export' )
|
|
|
|
self._type = ClientGUICommon.BetterChoice( self._type_box )
|
|
self._type.Append( 'regular', HC.EXPORT_FOLDER_TYPE_REGULAR )
|
|
self._type.Append( 'synchronise', HC.EXPORT_FOLDER_TYPE_SYNCHRONISE )
|
|
|
|
#
|
|
|
|
self._query_box = ClientGUICommon.StaticBox( self, 'query to export' )
|
|
|
|
self._page_key = 'export folders placeholder'
|
|
|
|
predicates = file_search_context.GetPredicates()
|
|
|
|
self._predicates_box = ClientGUIListBoxes.ListBoxTagsActiveSearchPredicates( self._query_box, self._page_key, predicates )
|
|
|
|
self._searchbox = ClientGUIACDropdown.AutoCompleteDropdownTagsRead( self._query_box, self._page_key, file_search_context )
|
|
|
|
#
|
|
|
|
self._period_box = ClientGUICommon.StaticBox( self, 'export period' )
|
|
|
|
self._period = ClientGUITime.TimeDeltaButton( self._period_box, min = 3 * 60, days = True, hours = True, minutes = True )
|
|
|
|
#
|
|
|
|
self._phrase_box = ClientGUICommon.StaticBox( self, 'filenames' )
|
|
|
|
self._pattern = wx.TextCtrl( self._phrase_box )
|
|
|
|
self._examples = ClientGUICommon.ExportPatternButton( self._phrase_box )
|
|
|
|
#
|
|
|
|
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.SetForegroundColour( ( 128, 0, 0 ) )
|
|
|
|
#
|
|
|
|
self._name.SetValue( name )
|
|
|
|
self._path.SetPath( path )
|
|
|
|
self._type.SelectClientData( export_type )
|
|
|
|
self._period.SetValue( period )
|
|
|
|
self._pattern.SetValue( phrase )
|
|
|
|
#
|
|
|
|
rows = []
|
|
|
|
rows.append( ( 'name: ', self._name ) )
|
|
rows.append( ( 'folder path: ', self._path ) )
|
|
|
|
gridbox = ClientGUICommon.WrapInGrid( self._path_box, rows )
|
|
|
|
self._path_box.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
|
|
#
|
|
|
|
text = '''regular - try to export the files to the directory, overwriting if the filesize if different
|
|
|
|
synchronise - try to export the files to the directory, overwriting if the filesize if different, and delete anything else in the directory
|
|
|
|
If you select synchronise, be careful!'''
|
|
|
|
st = ClientGUICommon.BetterStaticText( self._type_box, label = text )
|
|
|
|
st.SetWrapWidth( 440 )
|
|
|
|
self._type_box.Add( st, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._type_box.Add( self._type, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
self._query_box.Add( self._predicates_box, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
self._query_box.Add( self._searchbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
self._period_box.Add( self._period, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
phrase_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
phrase_hbox.Add( self._pattern, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
phrase_hbox.Add( self._examples, CC.FLAGS_VCENTER )
|
|
|
|
self._phrase_box.Add( phrase_hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
|
|
buttons = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
buttons.Add( self._ok, CC.FLAGS_VCENTER )
|
|
buttons.Add( self._cancel, CC.FLAGS_VCENTER )
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
vbox.Add( self._path_box, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
vbox.Add( self._type_box, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
vbox.Add( self._query_box, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
vbox.Add( self._period_box, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
vbox.Add( self._phrase_box, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
vbox.Add( buttons, CC.FLAGS_BUTTON_SIZER )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
( x, y ) = self.GetEffectiveMinSize()
|
|
|
|
self.SetInitialSize( ( 480, y ) )
|
|
|
|
wx.CallAfter( self._ok.SetFocus )
|
|
|
|
|
|
|
|
def EventOK( self, event ):
|
|
|
|
if self._path.GetPath() in ( '', None ):
|
|
|
|
wx.MessageBox( 'You must enter a folder path to export to!' )
|
|
|
|
return
|
|
|
|
|
|
phrase = self._pattern.GetValue()
|
|
|
|
try:
|
|
|
|
ClientExporting.ParseExportPhrase( phrase )
|
|
|
|
except:
|
|
|
|
wx.MessageBox( 'Could not parse that export phrase!' )
|
|
|
|
return
|
|
|
|
|
|
self.EndModal( wx.ID_OK )
|
|
|
|
|
|
def GetInfo( self ):
|
|
|
|
name = self._name.GetValue()
|
|
|
|
path = HydrusData.ToUnicode( self._path.GetPath() )
|
|
|
|
export_type = self._type.GetChoice()
|
|
|
|
file_search_context = self._searchbox.GetFileSearchContext()
|
|
|
|
predicates = self._predicates_box.GetPredicates()
|
|
|
|
file_search_context.SetPredicates( predicates )
|
|
|
|
period = self._period.GetValue()
|
|
|
|
phrase = self._pattern.GetValue()
|
|
|
|
export_folder = ClientExporting.ExportFolder( name, path, export_type, file_search_context, period, phrase )
|
|
|
|
return export_folder
|
|
|
|
'''
|
|
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, 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.SetForegroundColour( ( 128, 0, 0 ) )
|
|
|
|
|
|
def PopulateControls():
|
|
|
|
self._names_to_delete = []
|
|
|
|
sites = HG.client_controller.Read( 'imageboards' )
|
|
|
|
for ( name, imageboards ) in sites.items():
|
|
|
|
self._sites.AddPageArgs( name, name, self._Panel, ( self._sites, imageboards ), {} )
|
|
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
add_remove_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
|
add_remove_hbox.Add( self._add, CC.FLAGS_VCENTER )
|
|
add_remove_hbox.Add( self._remove, CC.FLAGS_VCENTER )
|
|
add_remove_hbox.Add( self._export, CC.FLAGS_VCENTER )
|
|
|
|
ok_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
|
ok_hbox.Add( self._ok, CC.FLAGS_VCENTER )
|
|
ok_hbox.Add( self._cancel, CC.FLAGS_VCENTER )
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
vbox.Add( self._sites, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
vbox.Add( add_remove_hbox, CC.FLAGS_SMALL_INDENT )
|
|
vbox.Add( ok_hbox, CC.FLAGS_BUTTON_SIZER )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'manage imageboards' )
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
( x, y ) = self.GetEffectiveMinSize()
|
|
|
|
self.SetInitialSize( ( 980, y ) )
|
|
|
|
self.SetDropTarget( ClientDragDrop.FileDropTarget( filenames_callable = self.Import ) )
|
|
|
|
wx.CallAfter( self._ok.SetFocus )
|
|
|
|
|
|
def EventAdd( self, event ):
|
|
|
|
with ClientGUIDialogs.DialogTextEntry( self, 'Enter new site\'s name.' ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
try:
|
|
|
|
name = dlg.GetValue()
|
|
|
|
if self._sites.KeyExists( name ): raise HydrusExceptions.NameException( 'That name is already in use!' )
|
|
|
|
if name == '': raise HydrusExceptions.NameException( 'Please enter a nickname for the service.' )
|
|
|
|
page = self._Panel( self._sites, [], is_new = True )
|
|
|
|
self._sites.AddPage( name, name, page, select = True )
|
|
|
|
except HydrusExceptions.NameException as e:
|
|
|
|
wx.MessageBox( HydrusData.ToUnicode( e ) )
|
|
|
|
self.EventAdd( event )
|
|
|
|
|
|
|
|
|
|
|
|
def EventExport( self, event ):
|
|
|
|
site_panel = self._sites.GetCurrentPage()
|
|
|
|
if site_panel is not None:
|
|
|
|
name = self._sites.GetCurrentKey()
|
|
|
|
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:
|
|
|
|
path = HydrusData.ToUnicode( dlg.GetPath() )
|
|
|
|
with open( path, 'wb' ) as f: f.write( yaml.safe_dump( dict ) )
|
|
|
|
|
|
|
|
|
|
|
|
def EventOK( self, event ):
|
|
|
|
try:
|
|
|
|
for name in self._names_to_delete:
|
|
|
|
HG.client_controller.Write( 'delete_imageboard', name )
|
|
|
|
|
|
for page in self._sites.GetActivePages():
|
|
|
|
if page.HasChanges():
|
|
|
|
imageboards = page.GetImageboards()
|
|
|
|
name = 'this is old code'
|
|
|
|
HG.client_controller.Write( 'imageboard', name, imageboards )
|
|
|
|
|
|
|
|
finally: self.EndModal( wx.ID_OK )
|
|
|
|
|
|
def EventRemove( self, event ):
|
|
|
|
site_panel = self._sites.GetCurrentPage()
|
|
|
|
if site_panel is not None:
|
|
|
|
name = self._sites.GetCurrentKey()
|
|
|
|
self._names_to_delete.append( name )
|
|
|
|
self._sites.DeleteCurrentPage()
|
|
|
|
|
|
|
|
def Import( self, paths ):
|
|
|
|
for path in paths:
|
|
|
|
try:
|
|
|
|
with open( path, 'rb' ) as f: file = f.read()
|
|
|
|
thing = yaml.safe_load( file )
|
|
|
|
if isinstance( thing, dict ):
|
|
|
|
( name, imageboards ) = thing.items()[0]
|
|
|
|
if not self._sites.KeyExists( name ):
|
|
|
|
page = self._Panel( self._sites, [], is_new = True )
|
|
|
|
self._sites.AddPage( name, name, page, select = True )
|
|
|
|
|
|
page = self._sites.GetPage( name )
|
|
|
|
for imageboard in imageboards:
|
|
|
|
if isinstance( imageboard, ClientData.Imageboard ): page.UpdateImageboard( imageboard )
|
|
|
|
|
|
elif isinstance( thing, ClientData.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, is_new = False ):
|
|
|
|
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():
|
|
|
|
for imageboard in imageboards:
|
|
|
|
name = imageboard.GetName()
|
|
|
|
self._imageboards.AddPageArgs( name, name, self._Panel, ( self._imageboards, imageboard ), {} )
|
|
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
add_remove_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
|
add_remove_hbox.Add( self._add, CC.FLAGS_VCENTER )
|
|
add_remove_hbox.Add( self._remove, CC.FLAGS_VCENTER )
|
|
add_remove_hbox.Add( self._export, CC.FLAGS_VCENTER )
|
|
|
|
self._site_panel.Add( self._imageboards, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
self._site_panel.Add( add_remove_hbox, CC.FLAGS_SMALL_INDENT )
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
vbox.Add( self._site_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
|
|
wx.Panel.__init__( self, parent )
|
|
|
|
self._original_imageboards = imageboards
|
|
self._has_changes = False
|
|
self._is_new = is_new
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
( x, y ) = self.GetEffectiveMinSize()
|
|
|
|
self.SetInitialSize( ( 980, y ) )
|
|
|
|
|
|
def EventAdd( self, event ):
|
|
|
|
with ClientGUIDialogs.DialogTextEntry( self, 'Enter new imageboard\'s name.' ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
try:
|
|
|
|
name = dlg.GetValue()
|
|
|
|
if self._imageboards.KeyExists( name ): raise HydrusExceptions.NameException()
|
|
|
|
if name == '': raise Exception( 'Please enter a nickname for the service.' )
|
|
|
|
imageboard = ClientData.Imageboard( name, '', 60, [], {} )
|
|
|
|
page = self._Panel( self._imageboards, imageboard, is_new = True )
|
|
|
|
self._imageboards.AddPage( name, name, page, select = True )
|
|
|
|
self._has_changes = True
|
|
|
|
except Exception as e:
|
|
|
|
wx.MessageBox( HydrusData.ToUnicode( 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:
|
|
|
|
path = HydrusData.ToUnicode( dlg.GetPath() )
|
|
|
|
with open( path, '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.GetCurrentKey()
|
|
|
|
self._imageboards.DeleteCurrentPage()
|
|
|
|
self._has_changes = True
|
|
|
|
|
|
|
|
def GetImageboards( self ):
|
|
|
|
names_to_imageboards = { imageboard.GetName() : imageboard for imageboard in self._original_imageboards if self._imageboards.KeyExists( imageboard.GetName() ) }
|
|
|
|
for page in self._imageboards.GetActivePages():
|
|
|
|
imageboard = page.GetImageboard()
|
|
|
|
names_to_imageboards[ imageboard.GetName() ] = imageboard
|
|
|
|
|
|
return names_to_imageboards.values()
|
|
|
|
|
|
def HasChanges( self ):
|
|
|
|
if self._is_new: return True
|
|
|
|
return self._has_changes or True in ( page.HasChanges() for page in self._imageboards.GetActivePages() )
|
|
|
|
|
|
def UpdateImageboard( self, imageboard ):
|
|
|
|
name = imageboard.GetName()
|
|
|
|
if not self._imageboards.KeyExists( name ):
|
|
|
|
new_imageboard = ClientData.Imageboard( name, '', 60, [], {} )
|
|
|
|
page = self._Panel( self._imageboards, new_imageboard, is_new = True )
|
|
|
|
self._imageboards.AddPage( name, name, page, select = True )
|
|
|
|
|
|
page = self._imageboards.GetPage( name )
|
|
|
|
page.Update( imageboard )
|
|
|
|
|
|
class _Panel( wx.Panel ):
|
|
|
|
def __init__( self, parent, imageboard, is_new = False ):
|
|
|
|
wx.Panel.__init__( self, parent )
|
|
|
|
self._imageboard = imageboard
|
|
self._is_new = is_new
|
|
|
|
( 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 ) ], delete_key_callback = self.Delete )
|
|
|
|
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, field_type, default, editable ) in form_fields:
|
|
|
|
self._form_fields.Append( ( name, CC.field_string_lookup[ field_type ], HydrusData.ToUnicode( default ), HydrusData.ToUnicode( editable ) ), ( name, field_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():
|
|
|
|
gridbox = wx.FlexGridSizer( 2 )
|
|
|
|
gridbox.AddGrowableCol( 1, 1 )
|
|
|
|
gridbox.Add( wx.StaticText( self._basic_info_panel, label = 'POST URL' ), CC.FLAGS_VCENTER )
|
|
gridbox.Add( self._post_url, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
gridbox.Add( wx.StaticText( self._basic_info_panel, label = 'flood time' ), CC.FLAGS_VCENTER )
|
|
gridbox.Add( self._flood_time, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
self._basic_info_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
|
|
#
|
|
|
|
h_b_box = wx.BoxSizer( wx.HORIZONTAL )
|
|
h_b_box.Add( self._add, CC.FLAGS_VCENTER )
|
|
h_b_box.Add( self._edit, CC.FLAGS_VCENTER )
|
|
h_b_box.Add( self._delete, CC.FLAGS_VCENTER )
|
|
|
|
self._form_fields_panel.Add( self._form_fields, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
self._form_fields_panel.Add( h_b_box, CC.FLAGS_BUTTON_SIZER )
|
|
|
|
#
|
|
|
|
mime_buttons_box = wx.BoxSizer( wx.HORIZONTAL )
|
|
mime_buttons_box.Add( self._mime_choice, CC.FLAGS_VCENTER )
|
|
mime_buttons_box.Add( self._add_mime, CC.FLAGS_VCENTER )
|
|
mime_buttons_box.Add( self._remove_mime, CC.FLAGS_VCENTER )
|
|
|
|
self._allowed_mimes_panel.Add( self._mimes, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
self._allowed_mimes_panel.Add( mime_buttons_box, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
|
|
self._restrictions_panel.Add( self._min_resolution, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._restrictions_panel.Add( self._max_resolution, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._restrictions_panel.Add( self._max_file_size, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._restrictions_panel.Add( self._allowed_mimes_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
#
|
|
|
|
self._imageboard_panel.Add( self._basic_info_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._imageboard_panel.Add( self._form_fields_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
self._imageboard_panel.Add( self._restrictions_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
vbox.Add( self._imageboard_panel, CC.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()
|
|
|
|
form_fields = self._form_fields.GetClientData()
|
|
|
|
restrictions = {}
|
|
|
|
value = self._min_resolution.GetValue()
|
|
if value is not None: restrictions[ CC.RESTRICTION_MIN_RESOLUTION ] = value
|
|
|
|
value = self._max_resolution.GetValue()
|
|
if value is not None: restrictions[ CC.RESTRICTION_MAX_RESOLUTION ] = 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 Delete( self ): self._form_fields.RemoveAllSelected()
|
|
|
|
def EventAdd( self, event ):
|
|
|
|
with ClientGUIDialogs.DialogInputNewFormField( self ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
( name, field_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[ field_type ], HydrusData.ToUnicode( default ), HydrusData.ToUnicode( editable ) ), ( name, field_type, default, editable ) )
|
|
|
|
|
|
|
|
|
|
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.Delete()
|
|
|
|
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, field_type, default, editable ) = self._form_fields.GetClientData( index )
|
|
|
|
form_field = ( name, field_type, default, editable )
|
|
|
|
with ClientGUIDialogs.DialogInputNewFormField( self, form_field ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
old_name = name
|
|
|
|
( name, field_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[ field_type ], HydrusData.ToUnicode( default ), HydrusData.ToUnicode( editable ) ), ( name, field_type, default, editable ) )
|
|
|
|
|
|
|
|
|
|
|
|
def GetImageboard( self ):
|
|
|
|
( name, post_url, flood_time, form_fields, restrictions ) = self._GetInfo()
|
|
|
|
return ClientData.Imageboard( name, post_url, flood_time, form_fields, restrictions )
|
|
|
|
|
|
def HasChanges( self ):
|
|
|
|
if self._is_new: return True
|
|
|
|
( 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, field_type, default, editable ) in form_fields:
|
|
|
|
self._form_fields.Append( ( name, CC.field_string_lookup[ field_type ], HydrusData.ToUnicode( default ), HydrusData.ToUnicode( editable ) ), ( name, field_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 ):
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'manage import folders' )
|
|
|
|
import_folders_panel = ClientGUIListCtrl.BetterListCtrlPanel( self )
|
|
|
|
columns = [ ( 'name', 24 ), ( 'path', -1 ), ( 'paused', 8 ), ( 'check period', 24 ) ]
|
|
|
|
self._import_folders = ClientGUIListCtrl.BetterListCtrl( import_folders_panel, 'import_folders', 8, 36, columns, self._ConvertImportFolderToListCtrlTuples, use_simple_delete = True, activation_callback = self._Edit )
|
|
|
|
import_folders_panel.SetListCtrl( self._import_folders )
|
|
|
|
import_folders_panel.AddButton( 'add', self._Add )
|
|
import_folders_panel.AddButton( 'edit', self._Edit, enabled_only_on_selection = True )
|
|
import_folders_panel.AddDeleteButton()
|
|
|
|
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.SetForegroundColour( ( 128, 0, 0 ) )
|
|
|
|
#
|
|
|
|
import_folders = HG.client_controller.Read( 'serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_IMPORT_FOLDER )
|
|
|
|
self._import_folders.SetData( import_folders )
|
|
|
|
self._import_folders.Sort()
|
|
|
|
#
|
|
|
|
buttons = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
buttons.Add( self._ok, CC.FLAGS_VCENTER )
|
|
buttons.Add( self._cancel, CC.FLAGS_VCENTER )
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
intro = 'Here you can set the client to regularly check certain folders for new files to import.'
|
|
|
|
vbox.Add( ClientGUICommon.BetterStaticText( self, intro ), CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
warning = 'WARNING: Import folders check (and potentially move/delete!) the contents of all subdirectories as well as the base directory!'
|
|
|
|
warning_st = ClientGUICommon.BetterStaticText( self, warning )
|
|
|
|
warning_st.SetForegroundColour( ( 128, 0, 0 ) )
|
|
|
|
vbox.Add( warning_st, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
vbox.Add( import_folders_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
vbox.Add( buttons, CC.FLAGS_BUTTON_SIZER )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
#
|
|
|
|
( x, y ) = self.GetEffectiveMinSize()
|
|
|
|
if x < 780: x = 780
|
|
if y < 480: y = 480
|
|
|
|
self.SetInitialSize( ( x, y ) )
|
|
|
|
wx.CallAfter( self._ok.SetFocus )
|
|
|
|
|
|
def _Add( self ):
|
|
|
|
import_folder = ClientImportLocal.ImportFolder( 'import folder' )
|
|
|
|
with DialogManageImportFoldersEdit( self, import_folder ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
import_folder = dlg.GetInfo()
|
|
|
|
import_folder.SetNonDupeName( self._GetExistingNames() )
|
|
|
|
self._import_folders.AddDatas( ( import_folder, ) )
|
|
|
|
self._import_folders.Sort()
|
|
|
|
|
|
|
|
|
|
def _ConvertImportFolderToListCtrlTuples( self, import_folder ):
|
|
|
|
( name, path, paused, check_regularly, check_period ) = import_folder.ToListBoxTuple()
|
|
|
|
if paused:
|
|
|
|
pretty_paused = 'yes'
|
|
|
|
else:
|
|
|
|
pretty_paused = ''
|
|
|
|
|
|
if not check_regularly:
|
|
|
|
pretty_check_period = 'not checking regularly'
|
|
|
|
else:
|
|
|
|
pretty_check_period = HydrusData.TimeDeltaToPrettyTimeDelta( check_period )
|
|
|
|
|
|
sort_tuple = ( name, path, paused, check_period )
|
|
display_tuple = ( name, path, pretty_paused, pretty_check_period )
|
|
|
|
return ( display_tuple, sort_tuple )
|
|
|
|
|
|
def _Edit( self ):
|
|
|
|
import_folders = self._import_folders.GetData( only_selected = True )
|
|
|
|
for import_folder in import_folders:
|
|
|
|
with DialogManageImportFoldersEdit( self, import_folder ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
self._import_folders.DeleteDatas( ( import_folder, ) )
|
|
|
|
edited_import_folder = dlg.GetInfo()
|
|
|
|
edited_import_folder.SetNonDupeName( self._GetExistingNames() )
|
|
|
|
self._import_folders.AddDatas( ( edited_import_folder, ) )
|
|
|
|
self._import_folders.Sort()
|
|
|
|
|
|
|
|
|
|
|
|
def _GetExistingNames( self ):
|
|
|
|
import_folders = self._import_folders.GetData()
|
|
|
|
names = { import_folder.GetName() for import_folder in import_folders }
|
|
|
|
return names
|
|
|
|
|
|
def EventOK( self, event ):
|
|
|
|
existing_db_names = set( HG.client_controller.Read( 'serialisable_names', HydrusSerialisable.SERIALISABLE_TYPE_IMPORT_FOLDER ) )
|
|
|
|
good_names = set()
|
|
|
|
import_folders = self._import_folders.GetData()
|
|
|
|
for import_folder in import_folders:
|
|
|
|
good_names.add( import_folder.GetName() )
|
|
|
|
HG.client_controller.Write( 'serialisable', import_folder )
|
|
|
|
|
|
names_to_delete = existing_db_names.difference( good_names )
|
|
|
|
for name in names_to_delete:
|
|
|
|
HG.client_controller.Write( 'delete_serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_IMPORT_FOLDER, name )
|
|
|
|
|
|
HG.client_controller.pub( 'notify_new_import_folders' )
|
|
|
|
self.EndModal( wx.ID_OK )
|
|
|
|
|
|
class DialogManageImportFoldersEdit( ClientGUIDialogs.Dialog ):
|
|
|
|
def __init__( self, parent, import_folder ):
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'edit import folder' )
|
|
|
|
self._import_folder = import_folder
|
|
|
|
( name, path, mimes, file_import_options, tag_import_options, tag_service_keys_to_filename_tagging_options, actions, action_locations, period, check_regularly, paused, check_now, show_working_popup, publish_files_to_popup_button, publish_files_to_page ) = self._import_folder.ToTuple()
|
|
|
|
self._panel = wx.ScrolledWindow( self )
|
|
|
|
self._folder_box = ClientGUICommon.StaticBox( self._panel, 'folder options' )
|
|
|
|
self._name = wx.TextCtrl( self._folder_box )
|
|
|
|
self._path = wx.DirPickerCtrl( self._folder_box, style = wx.DIRP_USE_TEXTCTRL )
|
|
|
|
self._check_regularly = wx.CheckBox( self._folder_box )
|
|
|
|
self._period = ClientGUITime.TimeDeltaButton( self._folder_box, min = 3 * 60, days = True, hours = True, minutes = True )
|
|
|
|
self._paused = wx.CheckBox( self._folder_box )
|
|
|
|
self._check_now = wx.CheckBox( self._folder_box )
|
|
|
|
self._show_working_popup = wx.CheckBox( self._folder_box )
|
|
self._publish_files_to_popup_button = wx.CheckBox( self._folder_box )
|
|
self._publish_files_to_page = wx.CheckBox( self._folder_box )
|
|
|
|
self._file_seed_cache_button = ClientGUIFileSeedCache.FileSeedCacheButton( self._folder_box, HG.client_controller, self._import_folder.GetFileSeedCache, file_seed_cache_set_callable = self._import_folder.SetFileSeedCache )
|
|
|
|
#
|
|
|
|
self._file_box = ClientGUICommon.StaticBox( self._panel, 'file options' )
|
|
|
|
self._mimes = ClientGUIOptionsPanels.OptionsPanelMimes( self._file_box, HC.ALLOWED_MIMES )
|
|
|
|
def create_choice():
|
|
|
|
choice = ClientGUICommon.BetterChoice( self._file_box )
|
|
|
|
for if_id in ( CC.IMPORT_FOLDER_DELETE, CC.IMPORT_FOLDER_IGNORE, CC.IMPORT_FOLDER_MOVE ):
|
|
|
|
choice.Append( CC.import_folder_string_lookup[ if_id ], if_id )
|
|
|
|
|
|
choice.Bind( wx.EVT_CHOICE, self.EventCheckLocations )
|
|
|
|
return choice
|
|
|
|
|
|
self._action_successful = create_choice()
|
|
self._location_successful = wx.DirPickerCtrl( self._file_box, style = wx.DIRP_USE_TEXTCTRL )
|
|
|
|
self._action_redundant = create_choice()
|
|
self._location_redundant = wx.DirPickerCtrl( self._file_box, style = wx.DIRP_USE_TEXTCTRL )
|
|
|
|
self._action_deleted = create_choice()
|
|
self._location_deleted = wx.DirPickerCtrl( self._file_box, style = wx.DIRP_USE_TEXTCTRL )
|
|
|
|
self._action_failed = create_choice()
|
|
self._location_failed = wx.DirPickerCtrl( self._file_box, style = wx.DIRP_USE_TEXTCTRL )
|
|
|
|
show_downloader_options = False
|
|
|
|
self._file_import_options = ClientGUIImport.FileImportOptionsButton( self._file_box, file_import_options, show_downloader_options )
|
|
|
|
#
|
|
|
|
self._tag_box = ClientGUICommon.StaticBox( self._panel, 'tag options' )
|
|
|
|
self._tag_import_options = ClientGUIImport.TagImportOptionsButton( self._tag_box, tag_import_options, show_downloader_options )
|
|
|
|
self._filename_tagging_options_box = ClientGUICommon.StaticBox( self._tag_box, 'filename tagging' )
|
|
|
|
filename_tagging_options_panel = ClientGUIListCtrl.BetterListCtrlPanel( self._filename_tagging_options_box )
|
|
|
|
columns = [ ( 'filename tagging options services', -1 ) ]
|
|
|
|
self._filename_tagging_options = ClientGUIListCtrl.BetterListCtrl( filename_tagging_options_panel, 'filename_tagging_options', 5, 25, columns, self._ConvertFilenameTaggingOptionsToListCtrlTuples, use_simple_delete = True, activation_callback = self._EditFilenameTaggingOptions )
|
|
|
|
filename_tagging_options_panel.SetListCtrl( self._filename_tagging_options )
|
|
|
|
filename_tagging_options_panel.AddButton( 'add', self._AddFilenameTaggingOptions )
|
|
filename_tagging_options_panel.AddButton( 'edit', self._EditFilenameTaggingOptions, enabled_only_on_selection = True )
|
|
filename_tagging_options_panel.AddDeleteButton()
|
|
|
|
services_manager = HG.client_controller.services_manager
|
|
|
|
#
|
|
|
|
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.SetForegroundColour( ( 128, 0, 0 ) )
|
|
|
|
#
|
|
|
|
self._name.SetValue( name )
|
|
self._path.SetPath( path )
|
|
|
|
self._check_regularly.SetValue( check_regularly )
|
|
|
|
self._period.SetValue( period )
|
|
self._paused.SetValue( paused )
|
|
|
|
self._show_working_popup.SetValue( show_working_popup )
|
|
self._publish_files_to_popup_button.SetValue( publish_files_to_popup_button )
|
|
self._publish_files_to_page.SetValue( publish_files_to_page )
|
|
|
|
self._mimes.SetValue( mimes )
|
|
|
|
self._action_successful.SelectClientData( actions[ CC.STATUS_SUCCESSFUL_AND_NEW ] )
|
|
if CC.STATUS_SUCCESSFUL_AND_NEW in action_locations:
|
|
|
|
self._location_successful.SetPath( action_locations[ CC.STATUS_SUCCESSFUL_AND_NEW ] )
|
|
|
|
|
|
self._action_redundant.SelectClientData( actions[ CC.STATUS_SUCCESSFUL_BUT_REDUNDANT ] )
|
|
if CC.STATUS_SUCCESSFUL_BUT_REDUNDANT in action_locations:
|
|
|
|
self._location_redundant.SetPath( action_locations[ CC.STATUS_SUCCESSFUL_BUT_REDUNDANT ] )
|
|
|
|
|
|
self._action_deleted.SelectClientData( actions[ CC.STATUS_DELETED ] )
|
|
if CC.STATUS_DELETED in action_locations:
|
|
|
|
self._location_deleted.SetPath( action_locations[ CC.STATUS_DELETED ] )
|
|
|
|
|
|
self._action_failed.SelectClientData( actions[ CC.STATUS_ERROR ] )
|
|
if CC.STATUS_ERROR in action_locations:
|
|
|
|
self._location_failed.SetPath( action_locations[ CC.STATUS_ERROR ] )
|
|
|
|
|
|
good_tag_service_keys_to_filename_tagging_options = { service_key : filename_tagging_options for ( service_key, filename_tagging_options ) in tag_service_keys_to_filename_tagging_options.items() if HG.client_controller.services_manager.ServiceExists( service_key ) }
|
|
|
|
self._filename_tagging_options.AddDatas( good_tag_service_keys_to_filename_tagging_options.items() )
|
|
|
|
self._filename_tagging_options.Sort()
|
|
|
|
#
|
|
|
|
rows = []
|
|
|
|
rows.append( ( 'name: ', self._name ) )
|
|
rows.append( ( 'folder path: ', self._path ) )
|
|
rows.append( ( 'currently paused (if set, will not ever do any work): ', self._paused ) )
|
|
rows.append( ( 'check regularly?: ', self._check_regularly ) )
|
|
rows.append( ( 'check period: ', self._period ) )
|
|
rows.append( ( 'check on manage dialog ok: ', self._check_now ) )
|
|
rows.append( ( 'show a popup while working: ', self._show_working_popup ) )
|
|
rows.append( ( 'publish new files to a popup button: ', self._publish_files_to_popup_button ) )
|
|
rows.append( ( 'publish new files to a page: ', self._publish_files_to_page ) )
|
|
rows.append( ( 'review currently cached import paths: ', self._file_seed_cache_button ) )
|
|
|
|
gridbox = ClientGUICommon.WrapInGrid( self._folder_box, rows )
|
|
|
|
self._folder_box.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
|
|
#
|
|
|
|
rows = []
|
|
|
|
rows.append( ( 'mimes to import: ', self._mimes ) )
|
|
|
|
mimes_gridbox = ClientGUICommon.WrapInGrid( self._file_box, rows, expand_text = True )
|
|
|
|
gridbox = wx.FlexGridSizer( 3 )
|
|
|
|
gridbox.AddGrowableCol( 1, 1 )
|
|
|
|
gridbox.Add( wx.StaticText( self._file_box, label = 'when a file imports successfully: '), CC.FLAGS_VCENTER )
|
|
gridbox.Add( self._action_successful, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
gridbox.Add( self._location_successful, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
gridbox.Add( wx.StaticText( self._file_box, label = 'when a file is already in the db: '), CC.FLAGS_VCENTER )
|
|
gridbox.Add( self._action_redundant, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
gridbox.Add( self._location_redundant, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
gridbox.Add( wx.StaticText( self._file_box, label = 'when a file has previously been deleted from the db: '), CC.FLAGS_VCENTER )
|
|
gridbox.Add( self._action_deleted, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
gridbox.Add( self._location_deleted, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
gridbox.Add( wx.StaticText( self._file_box, label = 'when a file fails to import: '), CC.FLAGS_VCENTER )
|
|
gridbox.Add( self._action_failed, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
gridbox.Add( self._location_failed, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
self._file_box.Add( mimes_gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
self._file_box.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
self._file_box.Add( self._file_import_options, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
#
|
|
|
|
self._filename_tagging_options_box.Add( filename_tagging_options_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
self._tag_box.Add( self._tag_import_options, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._tag_box.Add( self._filename_tagging_options_box, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
#
|
|
|
|
buttons = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
buttons.Add( self._ok, CC.FLAGS_VCENTER )
|
|
buttons.Add( self._cancel, CC.FLAGS_VCENTER )
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
vbox.Add( self._folder_box, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
vbox.Add( self._file_box, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
vbox.Add( self._tag_box, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
self._panel.SetSizer( vbox )
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
vbox.Add( self._panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
vbox.Add( buttons, CC.FLAGS_BUTTON_SIZER )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
( x, y ) = self.GetEffectiveMinSize()
|
|
|
|
( max_x, max_y ) = ClientGUITopLevelWindows.GetDisplaySize( self )
|
|
|
|
x = min( x + 25, max_x )
|
|
y = min( y + 25, max_y )
|
|
|
|
self._panel.SetScrollRate( 20, 20 )
|
|
|
|
self.SetInitialSize( ( x, y ) )
|
|
|
|
self._CheckLocations()
|
|
|
|
self._check_regularly.Bind( wx.EVT_CHECKBOX, self.EventCheckRegularly )
|
|
|
|
self._UpdateCheckRegularly()
|
|
|
|
wx.CallAfter( self._ok.SetFocus )
|
|
|
|
|
|
def _AddFilenameTaggingOptions( self ):
|
|
|
|
service_key = ClientGUIDialogs.SelectServiceKey( HC.TAG_SERVICES )
|
|
|
|
if service_key is None:
|
|
|
|
return
|
|
|
|
|
|
existing_service_keys = { service_key for ( service_key, filename_tagging_options ) in self._filename_tagging_options.GetData() }
|
|
|
|
if service_key in existing_service_keys:
|
|
|
|
wx.MessageBox( 'You already have an entry for that service key! Please try editing it instead!' )
|
|
|
|
return
|
|
|
|
|
|
with ClientGUITopLevelWindows.DialogEdit( self, 'edit filename tagging options' ) as dlg:
|
|
|
|
filename_tagging_options = ClientImportOptions.FilenameTaggingOptions()
|
|
|
|
panel = ClientGUIImport.EditFilenameTaggingOptionPanel( dlg, service_key, filename_tagging_options )
|
|
|
|
dlg.SetPanel( panel )
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
filename_tagging_options = panel.GetValue()
|
|
|
|
self._filename_tagging_options.AddDatas( [ ( service_key, filename_tagging_options ) ] )
|
|
|
|
self._filename_tagging_options.Sort()
|
|
|
|
|
|
|
|
|
|
def _CheckLocations( self ):
|
|
|
|
if self._action_successful.GetChoice() == CC.IMPORT_FOLDER_MOVE:
|
|
|
|
self._location_successful.Enable()
|
|
|
|
else:
|
|
|
|
self._location_successful.Disable()
|
|
|
|
|
|
if self._action_redundant.GetChoice() == CC.IMPORT_FOLDER_MOVE:
|
|
|
|
self._location_redundant.Enable()
|
|
|
|
else:
|
|
|
|
self._location_redundant.Disable()
|
|
|
|
|
|
if self._action_deleted.GetChoice() == CC.IMPORT_FOLDER_MOVE:
|
|
|
|
self._location_deleted.Enable()
|
|
|
|
else:
|
|
|
|
self._location_deleted.Disable()
|
|
|
|
|
|
if self._action_failed.GetChoice() == CC.IMPORT_FOLDER_MOVE:
|
|
|
|
self._location_failed.Enable()
|
|
|
|
else:
|
|
|
|
self._location_failed.Disable()
|
|
|
|
|
|
|
|
def _ConvertFilenameTaggingOptionsToListCtrlTuples( self, data ):
|
|
|
|
( service_key, filename_tagging_options ) = data
|
|
|
|
name = HG.client_controller.services_manager.GetName( service_key )
|
|
|
|
display_tuple = ( name, )
|
|
sort_tuple = ( name, )
|
|
|
|
return ( display_tuple, sort_tuple )
|
|
|
|
|
|
def _EditFilenameTaggingOptions( self ):
|
|
|
|
selected_data = self._filename_tagging_options.GetData( only_selected = True )
|
|
|
|
for data in selected_data:
|
|
|
|
( service_key, filename_tagging_options ) = data
|
|
|
|
with ClientGUITopLevelWindows.DialogEdit( self, 'edit filename tagging options' ) as dlg:
|
|
|
|
panel = ClientGUIImport.EditFilenameTaggingOptionPanel( dlg, service_key, filename_tagging_options )
|
|
|
|
dlg.SetPanel( panel )
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
self._filename_tagging_options.DeleteDatas( ( data, ) )
|
|
|
|
filename_tagging_options = panel.GetValue()
|
|
|
|
self._filename_tagging_options.AddDatas( [ ( service_key, filename_tagging_options ) ] )
|
|
|
|
else:
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
|
|
def _UpdateCheckRegularly( self ):
|
|
|
|
if self._check_regularly.GetValue():
|
|
|
|
self._period.Enable()
|
|
|
|
else:
|
|
|
|
self._period.Disable()
|
|
|
|
|
|
|
|
def EventCheckRegularly( self, event ):
|
|
|
|
self._UpdateCheckRegularly()
|
|
|
|
|
|
def EventCheckLocations( self, event ):
|
|
|
|
self._CheckLocations()
|
|
|
|
|
|
def EventOK( self, event ):
|
|
|
|
path = self._path.GetPath()
|
|
|
|
if path in ( '', None ):
|
|
|
|
wx.MessageBox( 'You must enter a path to import from!' )
|
|
|
|
return
|
|
|
|
|
|
if not os.path.exists( path ):
|
|
|
|
wx.MessageBox( 'The path you have entered--"' + path + '"--does not exist! The dialog will not force you to correct it, but this import folder will do no work as long as the location is missing!' )
|
|
|
|
|
|
if HC.BASE_DIR.startswith( path ) or HG.client_controller.GetDBDir().startswith( path ):
|
|
|
|
wx.MessageBox( 'You cannot set an import path that includes your install or database directory!' )
|
|
|
|
return
|
|
|
|
|
|
if self._action_successful.GetChoice() == CC.IMPORT_FOLDER_MOVE:
|
|
|
|
path = self._location_successful.GetPath()
|
|
|
|
if path in ( '', None ):
|
|
|
|
wx.MessageBox( 'You must enter a path for your successful file move location!' )
|
|
|
|
return
|
|
|
|
|
|
if not os.path.exists( path ):
|
|
|
|
wx.MessageBox( 'The path you have entered for your successful file move location--"' + path + '"--does not exist! The dialog will not force you to correct it, but you should not let this import folder run until you have corrected or created it!' )
|
|
|
|
|
|
|
|
if self._action_redundant.GetChoice() == CC.IMPORT_FOLDER_MOVE:
|
|
|
|
path = self._location_redundant.GetPath()
|
|
|
|
if path in ( '', None ):
|
|
|
|
wx.MessageBox( 'You must enter a path for your redundant file move location!' )
|
|
|
|
return
|
|
|
|
|
|
if not os.path.exists( path ):
|
|
|
|
wx.MessageBox( 'The path you have entered for your redundant file move location--"' + path + '"--does not exist! The dialog will not force you to correct it, but you should not let this import folder run until you have corrected or created it!' )
|
|
|
|
|
|
|
|
if self._action_deleted.GetChoice() == CC.IMPORT_FOLDER_MOVE:
|
|
|
|
path = self._location_deleted.GetPath()
|
|
|
|
if path in ( '', None ):
|
|
|
|
wx.MessageBox( 'You must enter a path for your deleted file move location!' )
|
|
|
|
return
|
|
|
|
|
|
if not os.path.exists( path ):
|
|
|
|
wx.MessageBox( 'The path you have entered for your deleted file move location--"' + path + '"--does not exist! The dialog will not force you to correct it, but you should not let this import folder run until you have corrected or created it!' )
|
|
|
|
|
|
|
|
if self._action_failed.GetChoice() == CC.IMPORT_FOLDER_MOVE:
|
|
|
|
path = self._location_failed.GetPath()
|
|
|
|
if path in ( '', None ):
|
|
|
|
wx.MessageBox( 'You must enter a path for your failed file move location!' )
|
|
|
|
return
|
|
|
|
|
|
if not os.path.exists( path ):
|
|
|
|
wx.MessageBox( 'The path you have entered for your failed file move location--"' + path + '"--does not exist! The dialog will not force you to correct it, but you should not let this import folder run until you have corrected or created it!' )
|
|
|
|
|
|
|
|
self.EndModal( wx.ID_OK )
|
|
|
|
|
|
def GetInfo( self ):
|
|
|
|
name = self._name.GetValue()
|
|
path = HydrusData.ToUnicode( self._path.GetPath() )
|
|
mimes = self._mimes.GetValue()
|
|
file_import_options = self._file_import_options.GetValue()
|
|
tag_import_options = self._tag_import_options.GetValue()
|
|
|
|
actions = {}
|
|
action_locations = {}
|
|
|
|
actions[ CC.STATUS_SUCCESSFUL_AND_NEW ] = self._action_successful.GetChoice()
|
|
if actions[ CC.STATUS_SUCCESSFUL_AND_NEW ] == CC.IMPORT_FOLDER_MOVE:
|
|
|
|
action_locations[ CC.STATUS_SUCCESSFUL_AND_NEW ] = HydrusData.ToUnicode( self._location_successful.GetPath() )
|
|
|
|
|
|
actions[ CC.STATUS_SUCCESSFUL_BUT_REDUNDANT ] = self._action_redundant.GetChoice()
|
|
if actions[ CC.STATUS_SUCCESSFUL_BUT_REDUNDANT ] == CC.IMPORT_FOLDER_MOVE:
|
|
|
|
action_locations[ CC.STATUS_SUCCESSFUL_BUT_REDUNDANT ] = HydrusData.ToUnicode( self._location_redundant.GetPath() )
|
|
|
|
|
|
actions[ CC.STATUS_DELETED ] = self._action_deleted.GetChoice()
|
|
if actions[ CC.STATUS_DELETED] == CC.IMPORT_FOLDER_MOVE:
|
|
|
|
action_locations[ CC.STATUS_DELETED ] = HydrusData.ToUnicode( self._location_deleted.GetPath() )
|
|
|
|
|
|
actions[ CC.STATUS_ERROR ] = self._action_failed.GetChoice()
|
|
if actions[ CC.STATUS_ERROR ] == CC.IMPORT_FOLDER_MOVE:
|
|
|
|
action_locations[ CC.STATUS_ERROR ] = HydrusData.ToUnicode( self._location_failed.GetPath() )
|
|
|
|
|
|
period = self._period.GetValue()
|
|
check_regularly = self._check_regularly.GetValue()
|
|
|
|
paused = self._paused.GetValue()
|
|
|
|
check_now = self._check_now.GetValue()
|
|
|
|
show_working_popup = self._show_working_popup.GetValue()
|
|
publish_files_to_popup_button = self._publish_files_to_popup_button.GetValue()
|
|
publish_files_to_page = self._publish_files_to_page.GetValue()
|
|
|
|
tag_service_keys_to_filename_tagging_options = dict( self._filename_tagging_options.GetData() )
|
|
|
|
self._import_folder.SetTuple( name, path, mimes, file_import_options, tag_import_options, tag_service_keys_to_filename_tagging_options, actions, action_locations, period, check_regularly, paused, check_now, show_working_popup, publish_files_to_popup_button, publish_files_to_page )
|
|
|
|
return self._import_folder
|
|
|
|
|
|
class DialogManageRatings( ClientGUIDialogs.Dialog ):
|
|
|
|
def __init__( self, parent, media ):
|
|
|
|
self._hashes = set()
|
|
|
|
for m in media: self._hashes.update( m.GetHashes() )
|
|
|
|
( remember, position ) = HC.options[ 'rating_dialog_position' ]
|
|
|
|
if remember and position is not None:
|
|
|
|
my_position = 'custom'
|
|
|
|
wx.CallAfter( self.SetPosition, position )
|
|
|
|
else:
|
|
|
|
my_position = 'topleft'
|
|
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, 'manage ratings for ' + HydrusData.ToHumanInt( len( self._hashes ) ) + ' files', position = my_position )
|
|
|
|
#
|
|
|
|
like_services = HG.client_controller.services_manager.GetServices( ( HC.LOCAL_RATING_LIKE, ), randomised = False )
|
|
numerical_services = HG.client_controller.services_manager.GetServices( ( HC.LOCAL_RATING_NUMERICAL, ), randomised = False )
|
|
|
|
self._panels = []
|
|
|
|
if len( like_services ) > 0:
|
|
|
|
self._panels.append( self._LikePanel( self, like_services, media ) )
|
|
|
|
|
|
if len( numerical_services ) > 0:
|
|
|
|
self._panels.append( self._NumericalPanel( self, numerical_services, media ) )
|
|
|
|
|
|
self._apply = wx.Button( self, id = wx.ID_OK, 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.SetForegroundColour( ( 128, 0, 0 ) )
|
|
|
|
#
|
|
|
|
buttonbox = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
buttonbox.Add( self._apply, CC.FLAGS_VCENTER )
|
|
buttonbox.Add( self._cancel, CC.FLAGS_VCENTER )
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
for panel in self._panels:
|
|
|
|
vbox.Add( panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
|
|
vbox.Add( buttonbox, CC.FLAGS_BUTTON_SIZER )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
( x, y ) = self.GetEffectiveMinSize()
|
|
|
|
self.SetInitialSize( ( x, y ) )
|
|
|
|
#
|
|
|
|
self._my_shortcut_handler = ClientGUIShortcuts.ShortcutsHandler( self, [ 'media' ] )
|
|
|
|
|
|
def EventOK( self, event ):
|
|
|
|
try:
|
|
|
|
service_keys_to_content_updates = {}
|
|
|
|
for panel in self._panels:
|
|
|
|
sub_service_keys_to_content_updates = panel.GetContentUpdates()
|
|
|
|
service_keys_to_content_updates.update( sub_service_keys_to_content_updates )
|
|
|
|
|
|
if len( service_keys_to_content_updates ) > 0:
|
|
|
|
HG.client_controller.Write( 'content_updates', service_keys_to_content_updates )
|
|
|
|
|
|
( remember, position ) = HC.options[ 'rating_dialog_position' ]
|
|
|
|
current_position = self.GetPosition()
|
|
|
|
if remember and position != current_position:
|
|
|
|
HC.options[ 'rating_dialog_position' ] = ( remember, current_position )
|
|
|
|
HG.client_controller.Write( 'save_options', HC.options )
|
|
|
|
|
|
finally:
|
|
|
|
self.EndModal( wx.ID_OK )
|
|
|
|
|
|
|
|
def ProcessApplicationCommand( self, command ):
|
|
|
|
command_processed = True
|
|
|
|
command_type = command.GetCommandType()
|
|
data = command.GetData()
|
|
|
|
if command_type == CC.APPLICATION_COMMAND_TYPE_SIMPLE:
|
|
|
|
action = data
|
|
|
|
if action == 'manage_file_ratings':
|
|
|
|
self.EventOK( None )
|
|
|
|
else:
|
|
|
|
command_processed = False
|
|
|
|
|
|
else:
|
|
|
|
command_processed = False
|
|
|
|
|
|
return command_processed
|
|
|
|
|
|
class _LikePanel( wx.Panel ):
|
|
|
|
def __init__( self, parent, services, media ):
|
|
|
|
wx.Panel.__init__( self, parent )
|
|
|
|
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_FRAMEBK ) )
|
|
|
|
self._services = services
|
|
|
|
self._media = media
|
|
|
|
self._service_keys_to_controls = {}
|
|
self._service_keys_to_original_ratings_states = {}
|
|
|
|
rows = []
|
|
|
|
for service in self._services:
|
|
|
|
name = service.GetName()
|
|
|
|
service_key = service.GetServiceKey()
|
|
|
|
rating_state = ClientRatings.GetLikeStateFromMedia( self._media, service_key )
|
|
|
|
control = ClientGUICommon.RatingLikeDialog( self, service_key )
|
|
|
|
control.SetRatingState( rating_state )
|
|
|
|
self._service_keys_to_controls[ service_key ] = control
|
|
self._service_keys_to_original_ratings_states[ service_key ] = rating_state
|
|
|
|
rows.append( ( name + ': ', control ) )
|
|
|
|
|
|
gridbox = ClientGUICommon.WrapInGrid( self, rows, expand_text = True )
|
|
|
|
self.SetSizer( gridbox )
|
|
|
|
|
|
def GetContentUpdates( self ):
|
|
|
|
service_keys_to_content_updates = {}
|
|
|
|
hashes = { hash for hash in itertools.chain.from_iterable( ( media.GetHashes() for media in self._media ) ) }
|
|
|
|
for ( service_key, control ) in self._service_keys_to_controls.items():
|
|
|
|
original_rating_state = self._service_keys_to_original_ratings_states[ service_key ]
|
|
|
|
rating_state = control.GetRatingState()
|
|
|
|
if rating_state != original_rating_state:
|
|
|
|
if rating_state == ClientRatings.LIKE: rating = 1
|
|
elif rating_state == ClientRatings.DISLIKE: rating = 0
|
|
else: rating = None
|
|
|
|
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( rating, hashes ) )
|
|
|
|
service_keys_to_content_updates[ service_key ] = ( content_update, )
|
|
|
|
|
|
|
|
return service_keys_to_content_updates
|
|
|
|
|
|
|
|
class _NumericalPanel( wx.Panel ):
|
|
|
|
def __init__( self, parent, services, media ):
|
|
|
|
wx.Panel.__init__( self, parent )
|
|
|
|
self._services = services
|
|
|
|
self._media = media
|
|
|
|
self._service_keys_to_controls = {}
|
|
self._service_keys_to_original_ratings_states = {}
|
|
|
|
rows = []
|
|
|
|
for service in self._services:
|
|
|
|
name = service.GetName()
|
|
|
|
service_key = service.GetServiceKey()
|
|
|
|
( rating_state, rating ) = ClientRatings.GetNumericalStateFromMedia( self._media, service_key )
|
|
|
|
control = ClientGUICommon.RatingNumericalDialog( self, service_key )
|
|
|
|
if rating_state != ClientRatings.SET:
|
|
|
|
control.SetRatingState( rating_state )
|
|
|
|
else:
|
|
|
|
control.SetRating( rating )
|
|
|
|
|
|
self._service_keys_to_controls[ service_key ] = control
|
|
self._service_keys_to_original_ratings_states[ service_key ] = ( rating_state, rating )
|
|
|
|
rows.append( ( name + ': ', control ) )
|
|
|
|
|
|
gridbox = ClientGUICommon.WrapInGrid( self, rows, expand_text = True )
|
|
|
|
self.SetSizer( gridbox )
|
|
|
|
|
|
def GetContentUpdates( self ):
|
|
|
|
service_keys_to_content_updates = {}
|
|
|
|
hashes = { hash for hash in itertools.chain.from_iterable( ( media.GetHashes() for media in self._media ) ) }
|
|
|
|
for ( service_key, control ) in self._service_keys_to_controls.items():
|
|
|
|
( original_rating_state, original_rating ) = self._service_keys_to_original_ratings_states[ service_key ]
|
|
|
|
rating_state = control.GetRatingState()
|
|
|
|
if rating_state == ClientRatings.NULL:
|
|
|
|
rating = None
|
|
|
|
else:
|
|
|
|
rating = control.GetRating()
|
|
|
|
|
|
if rating != original_rating:
|
|
|
|
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( rating, hashes ) )
|
|
|
|
service_keys_to_content_updates[ service_key ] = ( content_update, )
|
|
|
|
|
|
|
|
return service_keys_to_content_updates
|
|
|
|
|
|
|
|
class DialogManageUPnP( ClientGUIDialogs.Dialog ):
|
|
|
|
def __init__( self, parent ):
|
|
|
|
title = 'manage local upnp'
|
|
|
|
ClientGUIDialogs.Dialog.__init__( self, parent, title )
|
|
|
|
self._hidden_cancel = wx.Button( self, id = wx.ID_CANCEL, size = ( 0, 0 ) )
|
|
|
|
self._status_st = ClientGUICommon.BetterStaticText( self )
|
|
|
|
self._mappings_list_ctrl = ClientGUIListCtrl.SaneListCtrl( self, 480, [ ( 'description', -1 ), ( 'internal ip', 100 ), ( 'internal port', 80 ), ( 'external ip', 100 ), ( 'external port', 80 ), ( 'protocol', 80 ), ( 'lease', 80 ) ], delete_key_callback = self.RemoveMappings, activation_callback = self.EditMappings )
|
|
|
|
self._add_custom = wx.Button( self, label = 'add custom mapping' )
|
|
self._add_custom.Bind( wx.EVT_BUTTON, self.EventAddCustomMapping )
|
|
|
|
self._edit = wx.Button( self, label = 'edit mapping' )
|
|
self._edit.Bind( wx.EVT_BUTTON, self.EventEditMapping )
|
|
|
|
self._remove = wx.Button( self, label = 'remove mapping' )
|
|
self._remove.Bind( wx.EVT_BUTTON, self.EventRemoveMapping )
|
|
|
|
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 ) )
|
|
|
|
#
|
|
|
|
edit_buttons = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
edit_buttons.Add( self._add_custom, CC.FLAGS_VCENTER )
|
|
edit_buttons.Add( self._edit, CC.FLAGS_VCENTER )
|
|
edit_buttons.Add( self._remove, CC.FLAGS_VCENTER )
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
vbox.Add( self._status_st, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
vbox.Add( self._mappings_list_ctrl, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
vbox.Add( edit_buttons, CC.FLAGS_BUTTON_SIZER )
|
|
vbox.Add( self._ok, CC.FLAGS_LONE_BUTTON )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
( x, y ) = self.GetEffectiveMinSize()
|
|
|
|
x = max( x, 760 )
|
|
|
|
self.SetInitialSize( ( x, y ) )
|
|
|
|
#
|
|
|
|
self._RefreshMappings()
|
|
|
|
|
|
def _RefreshMappings( self ):
|
|
|
|
def THREADdo_it():
|
|
|
|
def wx_code( mappings ):
|
|
|
|
if not self:
|
|
|
|
return
|
|
|
|
|
|
self._mappings = mappings
|
|
|
|
for mapping in self._mappings:
|
|
|
|
self._mappings_list_ctrl.Append( mapping, mapping )
|
|
|
|
|
|
self._status_st.SetLabelText( '' )
|
|
|
|
|
|
try:
|
|
|
|
mappings = HydrusNATPunch.GetUPnPMappings()
|
|
|
|
except Exception as e:
|
|
|
|
HydrusData.ShowException( e )
|
|
|
|
wx.CallAfter( wx.MessageBox, 'Could not load mappings:' + os.linesep * 2 + str( e ) )
|
|
|
|
return
|
|
|
|
|
|
wx.CallAfter( wx_code, mappings )
|
|
|
|
|
|
self._status_st.SetLabelText( 'Refreshing mappings--please wait...' )
|
|
|
|
self._mappings_list_ctrl.DeleteAllItems()
|
|
|
|
HG.client_controller.CallToThread( THREADdo_it )
|
|
|
|
|
|
def EditMappings( self ):
|
|
|
|
do_refresh = False
|
|
|
|
for index in self._mappings_list_ctrl.GetAllSelected():
|
|
|
|
( description, internal_ip, internal_port, external_ip, external_port, protocol, duration ) = self._mappings_list_ctrl.GetClientData( index )
|
|
|
|
with ClientGUIDialogs.DialogInputUPnPMapping( self, external_port, protocol, internal_port, description, duration ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
( external_port, protocol, internal_port, description, duration ) = dlg.GetInfo()
|
|
|
|
HydrusNATPunch.RemoveUPnPMapping( external_port, protocol )
|
|
|
|
internal_client = HydrusNATPunch.GetLocalIP()
|
|
|
|
HydrusNATPunch.AddUPnPMapping( internal_client, internal_port, external_port, protocol, description, duration = duration )
|
|
|
|
do_refresh = True
|
|
|
|
|
|
|
|
|
|
if do_refresh:
|
|
|
|
self._RefreshMappings()
|
|
|
|
|
|
|
|
def EventAddCustomMapping( self, event ):
|
|
|
|
do_refresh = False
|
|
|
|
external_port = HC.DEFAULT_SERVICE_PORT
|
|
protocol = 'TCP'
|
|
internal_port = HC.DEFAULT_SERVICE_PORT
|
|
description = 'hydrus service'
|
|
duration = 0
|
|
|
|
with ClientGUIDialogs.DialogInputUPnPMapping( self, external_port, protocol, internal_port, description, duration ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
( external_port, protocol, internal_port, description, duration ) = dlg.GetInfo()
|
|
|
|
for ( existing_description, existing_internal_ip, existing_internal_port, existing_external_ip, existing_external_port, existing_protocol, existing_lease ) in self._mappings:
|
|
|
|
if external_port == existing_external_port and protocol == existing_protocol:
|
|
|
|
wx.MessageBox( 'That external port already exists!' )
|
|
|
|
return
|
|
|
|
|
|
|
|
internal_client = HydrusNATPunch.GetLocalIP()
|
|
|
|
HydrusNATPunch.AddUPnPMapping( internal_client, internal_port, external_port, protocol, description, duration = duration )
|
|
|
|
do_refresh = True
|
|
|
|
|
|
|
|
if do_refresh:
|
|
|
|
self._RefreshMappings()
|
|
|
|
|
|
|
|
def EventEditMapping( self, event ):
|
|
|
|
self.EditMappings()
|
|
|
|
|
|
def EventOK( self, event ):
|
|
|
|
self.EndModal( wx.ID_OK )
|
|
|
|
|
|
def EventRemoveMapping( self, event ):
|
|
|
|
self.RemoveMappings()
|
|
|
|
|
|
def RemoveMappings( self ):
|
|
|
|
do_refresh = False
|
|
|
|
for index in self._mappings_list_ctrl.GetAllSelected():
|
|
|
|
( description, internal_ip, internal_port, external_ip, external_port, protocol, duration ) = self._mappings_list_ctrl.GetClientData( index )
|
|
|
|
HydrusNATPunch.RemoveUPnPMapping( external_port, protocol )
|
|
|
|
do_refresh = True
|
|
|
|
|
|
if do_refresh:
|
|
|
|
self._RefreshMappings()
|
|
|
|
|