2339 lines
102 KiB
Python
Executable File
2339 lines
102 KiB
Python
Executable File
import httplib
|
|
import HydrusConstants as HC
|
|
import ClientConstants as CC
|
|
import ClientConstantsMessages
|
|
import ClientGUICommon
|
|
import ClientGUIDialogs
|
|
import ClientGUIDialogsManage
|
|
import ClientGUIMessages
|
|
import ClientGUIPages
|
|
import os
|
|
import random
|
|
import subprocess
|
|
import sys
|
|
import threading
|
|
import time
|
|
import traceback
|
|
import webbrowser
|
|
import wx
|
|
|
|
# timers
|
|
|
|
ID_TIMER_UPDATES = wx.NewId()
|
|
|
|
# Sizer Flags
|
|
|
|
FLAGS_NONE = wx.SizerFlags( 0 )
|
|
|
|
FLAGS_SMALL_INDENT = wx.SizerFlags( 0 ).Border( wx.ALL, 2 )
|
|
|
|
FLAGS_EXPAND_PERPENDICULAR = wx.SizerFlags( 0 ).Border( wx.ALL, 2 ).Expand()
|
|
FLAGS_EXPAND_BOTH_WAYS = wx.SizerFlags( 2 ).Border( wx.ALL, 2 ).Expand()
|
|
|
|
FLAGS_EXPAND_SIZER_PERPENDICULAR = wx.SizerFlags( 0 ).Expand()
|
|
FLAGS_EXPAND_SIZER_BOTH_WAYS = wx.SizerFlags( 2 ).Expand()
|
|
|
|
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 FrameGUI( ClientGUICommon.Frame ):
|
|
|
|
def __init__( self ):
|
|
|
|
ClientGUICommon.Frame.__init__( self, None, title = HC.app.PrepStringForDisplay( 'Hydrus Client' ) )
|
|
|
|
self.SetDropTarget( ClientGUICommon.FileDropTarget( self.ImportFiles ) )
|
|
|
|
self._statusbar = self.CreateStatusBar()
|
|
self._statusbar.SetFieldsCount( 5 )
|
|
self._statusbar.SetStatusWidths( [ -1, -1, 100, 120, 50 ] )
|
|
|
|
self._statusbar_media = ''
|
|
self._statusbar_service = ''
|
|
self._statusbar_inbox = ''
|
|
self._statusbar_downloads = ''
|
|
self._statusbar_db_locked = ''
|
|
|
|
self.SetIcon( wx.Icon( HC.STATIC_DIR + os.path.sep + 'hydrus.ico', wx.BITMAP_TYPE_ICO ) )
|
|
|
|
min_width = 920
|
|
min_height = 600
|
|
|
|
display_index = wx.Display.GetFromWindow( self )
|
|
|
|
if display_index != wx.NOT_FOUND:
|
|
|
|
display = wx.Display( display_index )
|
|
|
|
( display_width, display_height ) = display.GetGeometry().GetSize()
|
|
|
|
initial_width = max( int( display_width * 0.75 ), min_width )
|
|
initial_height = max( int( display_height * 0.75 ), min_height )
|
|
|
|
self.SetInitialSize( ( initial_width, initial_height ) )
|
|
self.SetMinSize( ( 480, 360 ) )
|
|
|
|
|
|
self.Maximize()
|
|
|
|
self._notebook = wx.Notebook( self )
|
|
self._notebook.Bind( wx.EVT_MIDDLE_DOWN, self.EventNotebookMiddleClick )
|
|
self._notebook.Bind( wx.EVT_RIGHT_DCLICK, self.EventNotebookMiddleClick )
|
|
self._notebook.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGED, self.EventNotebookPageChanged )
|
|
|
|
HC.app.SetTopWindow( self )
|
|
|
|
self.RefreshAcceleratorTable()
|
|
|
|
self._message_manager = ClientGUICommon.PopupMessageManager( self )
|
|
|
|
self.Bind( wx.EVT_MENU, self.EventMenu )
|
|
self.Bind( wx.EVT_CLOSE, self.EventExit )
|
|
self.Bind( wx.EVT_SET_FOCUS, self.EventFocus )
|
|
|
|
HC.pubsub.sub( self, 'NewCompose', 'new_compose_frame' )
|
|
HC.pubsub.sub( self, 'NewPageImportBooru', 'new_page_import_booru' )
|
|
HC.pubsub.sub( self, 'NewPageImportGallery', 'new_page_import_gallery' )
|
|
HC.pubsub.sub( self, 'NewPageImportHDD', 'new_hdd_import' )
|
|
HC.pubsub.sub( self, 'NewPageImportThreadWatcher', 'new_page_import_thread_watcher' )
|
|
HC.pubsub.sub( self, 'NewPageImportURL', 'new_page_import_url' )
|
|
HC.pubsub.sub( self, 'NewPageMessages', 'new_page_messages' )
|
|
HC.pubsub.sub( self, 'NewPagePetitions', 'new_page_petitions' )
|
|
HC.pubsub.sub( self, 'NewPageQuery', 'new_page_query' )
|
|
HC.pubsub.sub( self, 'NewPageThreadDumper', 'new_thread_dumper' )
|
|
HC.pubsub.sub( self, 'NewSimilarTo', 'new_similar_to' )
|
|
HC.pubsub.sub( self, 'RefreshMenuBar', 'refresh_menu_bar' )
|
|
HC.pubsub.sub( self, 'RefreshMenuBar', 'notify_new_pending' )
|
|
HC.pubsub.sub( self, 'RefreshMenuBar', 'notify_new_permissions' )
|
|
HC.pubsub.sub( self, 'RefreshMenuBar', 'notify_new_services' )
|
|
HC.pubsub.sub( self, 'RefreshAcceleratorTable', 'options_updated' )
|
|
HC.pubsub.sub( self, 'RefreshStatusBar', 'refresh_status' )
|
|
HC.pubsub.sub( self, 'SetDBLockedStatus', 'db_locked_status' )
|
|
HC.pubsub.sub( self, 'SetDownloadsStatus', 'downloads_status' )
|
|
HC.pubsub.sub( self, 'SetInboxStatus', 'inbox_status' )
|
|
HC.pubsub.sub( self, 'SetServiceStatus', 'service_status' )
|
|
|
|
self.RefreshMenuBar()
|
|
|
|
self._RefreshStatusBar()
|
|
|
|
self.Show( True )
|
|
|
|
wx.CallAfter( self._NewPageQuery, HC.LOCAL_FILE_SERVICE_IDENTIFIER )
|
|
|
|
|
|
def _THREADUploadPending( self, service_identifier, job_key, cancel_event ):
|
|
|
|
try:
|
|
|
|
HC.pubsub.pub( 'progress_update', job_key, 0, 3, u'gathering pending and petitioned' )
|
|
|
|
result = HC.app.Read( 'pending', service_identifier )
|
|
|
|
service_type = service_identifier.GetType()
|
|
|
|
service = HC.app.Read( 'service', service_identifier )
|
|
|
|
if service_type == HC.FILE_REPOSITORY:
|
|
|
|
( upload_hashes, update ) = result
|
|
|
|
media_results = HC.app.Read( 'media_results', HC.LOCAL_FILE_SERVICE_IDENTIFIER, upload_hashes )
|
|
|
|
num_uploads = len( media_results )
|
|
|
|
HC.pubsub.pub( 'progress_update', job_key, 1, num_uploads + 3, u'connecting to repository' )
|
|
|
|
connection = service.GetConnection()
|
|
|
|
good_hashes = []
|
|
|
|
error_messages = set()
|
|
|
|
for ( index, media_result ) in enumerate( media_results ):
|
|
|
|
hash = media_result.GetHash()
|
|
|
|
mime = media_result.GetMime()
|
|
|
|
HC.pubsub.pub( 'progress_update', job_key, index + 2, num_uploads + 3, u'Uploading file ' + HC.ConvertIntToPrettyString( index + 1 ) + ' of ' + HC.ConvertIntToPrettyString( num_uploads ) )
|
|
|
|
if cancel_event.isSet(): break
|
|
|
|
try:
|
|
|
|
path = CC.GetFilePath( hash, mime )
|
|
|
|
with open( path, 'rb' ) as f: file = f.read()
|
|
|
|
connection.Post( 'file', file = file )
|
|
|
|
good_hashes.append( hash )
|
|
|
|
except Exception as e:
|
|
|
|
message = 'Upload pending files error:' + os.linesep + traceback.format_exc()
|
|
|
|
HC.pubsub.pub( 'progress_update', job_key, num_uploads + 1, num_uploads + 3, 'Error' )
|
|
|
|
HC.pubsub.pub( 'message', HC.Message( HC.MESSAGE_TYPE_TEXT, message ) )
|
|
|
|
time.sleep( 1 )
|
|
|
|
|
|
|
|
if not update.IsEmpty():
|
|
|
|
HC.pubsub.pub( 'progress_update', job_key, num_uploads + 1, num_uploads + 3, u'uploading petitions' )
|
|
|
|
connection.Post( 'update', update = update )
|
|
|
|
|
|
HC.pubsub.pub( 'progress_update', job_key, num_uploads + 2, num_uploads + 3, u'saving changes to local database' )
|
|
|
|
content_updates = []
|
|
|
|
media_results = HC.app.Read( 'media_results', HC.LOCAL_FILE_SERVICE_IDENTIFIER, good_hashes )
|
|
|
|
for media_result in media_results:
|
|
|
|
( hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tags_manager, file_service_identifiers_cdpp, local_ratings, remote_ratings ) = media_result.ToTuple()
|
|
|
|
timestamp = HC.GetNow()
|
|
|
|
content_update_row = ( hash, size, mime, timestamp, width, height, duration, num_frames, num_words )
|
|
|
|
content_updates.append( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_FILES, HC.CONTENT_UPDATE_ADD, content_update_row ) )
|
|
|
|
|
|
content_updates.extend( update.GetContentUpdates( for_client = True ) )
|
|
|
|
service_identfiers_to_content_updates = { service_identifier : content_updates }
|
|
|
|
elif service_type == HC.TAG_REPOSITORY:
|
|
|
|
update = result
|
|
|
|
HC.pubsub.pub( 'progress_update', job_key, 1, 3, u'connecting to repository' )
|
|
|
|
connection = service.GetConnection()
|
|
|
|
HC.pubsub.pub( 'progress_update', job_key, 2, 3, u'posting update' )
|
|
|
|
connection.Post( 'update', update = update )
|
|
|
|
service_identfiers_to_content_updates = { service_identifier : update.GetContentUpdates( for_client = True ) }
|
|
|
|
|
|
HC.app.Write( 'content_updates', service_identfiers_to_content_updates )
|
|
|
|
except Exception as e: HC.ShowException( e )
|
|
|
|
HC.pubsub.pub( 'progress_update', job_key, 3, 3, u'done!' )
|
|
|
|
HC.pubsub.pub( 'notify_new_pending' )
|
|
|
|
|
|
def _AboutWindow( self ):
|
|
|
|
aboutinfo = wx.AboutDialogInfo()
|
|
|
|
aboutinfo.SetIcon( wx.Icon( HC.STATIC_DIR + os.path.sep + 'hydrus.ico', wx.BITMAP_TYPE_ICO ) )
|
|
aboutinfo.SetName( 'hydrus client' )
|
|
aboutinfo.SetVersion( HC.u( HC.SOFTWARE_VERSION ) )
|
|
aboutinfo.SetDescription( CC.CLIENT_DESCRIPTION )
|
|
|
|
with open( HC.BASE_DIR + os.path.sep + 'license.txt', 'rb' ) as f: license = f.read()
|
|
|
|
aboutinfo.SetLicense( license )
|
|
|
|
aboutinfo.SetDevelopers( [ 'Anonymous' ] )
|
|
aboutinfo.SetWebSite( 'http://hydrusnetwork.github.io/hydrus/' )
|
|
|
|
wx.AboutBox( aboutinfo )
|
|
|
|
|
|
def _AccountInfo( self, service_identifier ):
|
|
|
|
with wx.TextEntryDialog( self, 'Access key' ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
subject_access_key = dlg.GetValue().decode( 'hex' )
|
|
|
|
service = HC.app.Read( 'service', service_identifier )
|
|
|
|
connection = service.GetConnection()
|
|
|
|
account_info = connection.Get( 'account_info', subject_access_key = subject_access_key.encode( 'hex' ) )
|
|
|
|
wx.MessageBox( HC.u( account_info ) )
|
|
|
|
|
|
|
|
|
|
def _AutoRepoSetup( self ):
|
|
|
|
message = 'This will attempt to set up your client with my repositories\' credentials, letting you tag on the public tag repository and see some files.'
|
|
|
|
with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_YES:
|
|
|
|
edit_log = []
|
|
|
|
tag_repo_identifier = HC.ClientServiceIdentifier( os.urandom( 32 ), HC.TAG_REPOSITORY, 'public tag repository' )
|
|
tag_repo_credentials = CC.Credentials( 'hydrus.no-ip.org', 45871, '4a285629721ca442541ef2c15ea17d1f7f7578b0c3f4f5f2a05f8f0ab297786f'.decode( 'hex' ) )
|
|
|
|
edit_log.append( ( 'add', ( tag_repo_identifier, tag_repo_credentials, None ) ) )
|
|
|
|
file_repo_identifier = HC.ClientServiceIdentifier( os.urandom( 32 ), HC.FILE_REPOSITORY, 'read-only art file repository' )
|
|
file_repo_credentials = CC.Credentials( 'hydrus.no-ip.org', 45872, '8f8a3685abc19e78a92ba61d84a0482b1cfac176fd853f46d93fe437a95e40a5'.decode( 'hex' ) )
|
|
|
|
edit_log.append( ( 'add', ( file_repo_identifier, file_repo_credentials, None ) ) )
|
|
|
|
HC.app.Write( 'update_services', edit_log )
|
|
|
|
HC.pubsub.pub( 'message', HC.Message( HC.MESSAGE_TYPE_TEXT, 'Auto repo setup done!' ) )
|
|
|
|
|
|
|
|
|
|
def _AutoServerSetup( self ):
|
|
|
|
message = 'This will attempt to start the server in the same install directory as this client, initialise it, and store the resultant admin accounts.'
|
|
|
|
with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_YES:
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
connection = httplib.HTTPConnection( '127.0.0.1', HC.DEFAULT_SERVER_ADMIN_PORT )
|
|
|
|
connection.connect()
|
|
|
|
connection.close()
|
|
|
|
already_running = True
|
|
|
|
except:
|
|
|
|
already_running = False
|
|
|
|
|
|
if already_running:
|
|
|
|
message = 'The server appears to be already running. Either that, or something else is using port ' + HC.u( HC.DEFAULT_SERVER_ADMIN_PORT ) + '.' + os.linesep + 'Would you like to try to initialise the server that is already running?'
|
|
|
|
with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_NO: return
|
|
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
my_scriptname = sys.argv[0]
|
|
|
|
if my_scriptname.endswith( 'pyw' ): subprocess.Popen( [ 'pythonw', HC.BASE_DIR + os.path.sep + 'server.pyw' ] )
|
|
else:
|
|
|
|
# The problem here is that, for mystical reasons, a PyInstaller exe can't launch another using subprocess, so we do it via explorer.
|
|
|
|
subprocess.Popen( [ 'explorer', HC.BASE_DIR + os.path.sep + 'server.exe' ] )
|
|
|
|
|
|
time.sleep( 10 ) # give it time to init its db
|
|
|
|
except:
|
|
|
|
wx.MessageBox( 'I tried to start the server, but something failed!' )
|
|
wx.MessageBox( traceback.format_exc() )
|
|
|
|
return
|
|
|
|
|
|
|
|
edit_log = []
|
|
|
|
admin_service_identifier = HC.ClientServiceIdentifier( os.urandom( 32 ), HC.SERVER_ADMIN, 'local server admin' )
|
|
admin_service_credentials = CC.Credentials( '127.0.0.1', HC.DEFAULT_SERVER_ADMIN_PORT, '' )
|
|
|
|
edit_log.append( ( 'add', ( admin_service_identifier, admin_service_credentials, None ) ) )
|
|
|
|
HC.app.Write( 'update_services', edit_log )
|
|
|
|
i = 0
|
|
|
|
while True:
|
|
|
|
time.sleep( i + 1 )
|
|
|
|
try:
|
|
|
|
service = HC.app.Read( 'service', admin_service_identifier )
|
|
|
|
break
|
|
|
|
except: pass
|
|
|
|
i += 1
|
|
|
|
if i > 5:
|
|
|
|
wx.MessageBox( 'For some reason, I could not add the new server to the db! Perhaps it is very busy. Please contact the administrator, or sort it out yourself!' )
|
|
|
|
return
|
|
|
|
|
|
|
|
connection = service.GetConnection()
|
|
|
|
connection.Get( 'init' )
|
|
|
|
edit_log = []
|
|
|
|
tag_service_identifier = HC.ServerServiceIdentifier( HC.TAG_REPOSITORY, HC.DEFAULT_SERVICE_PORT )
|
|
file_service_identifier = HC.ServerServiceIdentifier( HC.FILE_REPOSITORY, HC.DEFAULT_SERVICE_PORT + 1 )
|
|
|
|
edit_log.append( ( HC.ADD, tag_service_identifier ) )
|
|
edit_log.append( ( HC.ADD, file_service_identifier ) )
|
|
|
|
connection.Post( 'services_modification', edit_log = edit_log )
|
|
|
|
HC.app.Write( 'update_server_services', admin_service_identifier, edit_log )
|
|
|
|
wx.MessageBox( 'Done! Check services->review services to see your new server and its services.' )
|
|
|
|
except: wx.MessageBox( traceback.format_exc() )
|
|
|
|
|
|
|
|
|
|
def _BackupService( self, service_identifier ):
|
|
|
|
message = 'This will tell the service to lock and copy its database files. It will not be able to serve any requests until the operation is complete.'
|
|
|
|
with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_YES:
|
|
|
|
service = HC.app.Read( 'service', service_identifier )
|
|
|
|
connection = service.GetConnection()
|
|
|
|
with wx.BusyCursor(): connection.Post( 'backup' )
|
|
|
|
wx.MessageBox( 'Done!' )
|
|
|
|
|
|
|
|
|
|
def _CloseCurrentPage( self ):
|
|
|
|
selection = self._notebook.GetSelection()
|
|
|
|
if selection != wx.NOT_FOUND:
|
|
|
|
page = self._notebook.GetPage( selection )
|
|
|
|
try: page.TryToClose()
|
|
except: return
|
|
|
|
self._notebook.DeletePage( selection )
|
|
|
|
|
|
|
|
def _DeletePending( self, service_identifier ):
|
|
|
|
with ClientGUIDialogs.DialogYesNo( self, 'Are you sure you want to delete the pending data for ' + service_identifier.GetName() + '?' ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_YES: HC.app.Write( 'delete_pending', service_identifier )
|
|
|
|
|
|
|
|
def _News( self, service_identifier ):
|
|
|
|
with ClientGUIDialogs.DialogNews( self, service_identifier ) as dlg: dlg.ShowModal()
|
|
|
|
|
|
def _Stats( self, service_identifier ):
|
|
|
|
service = HC.app.Read( 'service', service_identifier )
|
|
|
|
connection = service.GetConnection()
|
|
|
|
stats = connection.Get( 'stats' )
|
|
|
|
wx.MessageBox( HC.u( stats ) )
|
|
|
|
|
|
def _FetchIP( self, service_identifier ):
|
|
|
|
with wx.TextEntryDialog( self, 'File Hash' ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
hash = dlg.GetValue().decode( 'hex' )
|
|
|
|
service = HC.app.Read( 'service', service_identifier )
|
|
|
|
connection = service.GetConnection()
|
|
|
|
with wx.BusyCursor(): ( ip, timestamp ) = connection.Get( 'ip', hash = hash.encode( 'hex' ) )
|
|
|
|
message = 'File Hash: ' + hash.encode( 'hex' ) + os.linesep + 'Uploader\'s IP: ' + ip + os.linesep + 'Upload Time (GMT): ' + time.asctime( time.gmtime( int( timestamp ) ) )
|
|
|
|
print( message )
|
|
|
|
wx.MessageBox( message + os.linesep + 'This has been written to the log.' )
|
|
|
|
|
|
|
|
|
|
def _ImportFiles( self, paths = [] ):
|
|
|
|
with ClientGUIDialogs.DialogSelectLocalFiles( self, paths ) as dlg: dlg.ShowModal()
|
|
|
|
|
|
def _Manage4chanPass( self ):
|
|
|
|
with ClientGUIDialogsManage.DialogManage4chanPass( self ) as dlg: dlg.ShowModal()
|
|
|
|
|
|
def _ManageAccountTypes( self, service_identifier ):
|
|
|
|
with ClientGUIDialogsManage.DialogManageAccountTypes( self, service_identifier ) as dlg: dlg.ShowModal()
|
|
|
|
|
|
def _ManageBoorus( self ):
|
|
|
|
with ClientGUIDialogsManage.DialogManageBoorus( self ) as dlg: dlg.ShowModal()
|
|
|
|
|
|
def _ManageContacts( self ):
|
|
|
|
with ClientGUIDialogsManage.DialogManageContacts( self ) as dlg: dlg.ShowModal()
|
|
|
|
|
|
def _ManageImageboards( self ):
|
|
|
|
with ClientGUIDialogsManage.DialogManageImageboards( self ) as dlg: dlg.ShowModal()
|
|
|
|
|
|
def _ManageImportFolders( self ):
|
|
|
|
with ClientGUIDialogsManage.DialogManageImportFolders( self ) as dlg: dlg.ShowModal()
|
|
|
|
|
|
def _ManageOptions( self, service_identifier ):
|
|
|
|
if service_identifier.GetType() == HC.LOCAL_FILE:
|
|
|
|
with ClientGUIDialogsManage.DialogManageOptionsLocal( self ) as dlg: dlg.ShowModal()
|
|
|
|
else:
|
|
|
|
if service_identifier.GetType() == HC.FILE_REPOSITORY:
|
|
|
|
with ClientGUIDialogsManage.DialogManageOptionsFileRepository( self, service_identifier ) as dlg: dlg.ShowModal()
|
|
|
|
elif service_identifier.GetType() == HC.TAG_REPOSITORY:
|
|
|
|
with ClientGUIDialogsManage.DialogManageOptionsTagRepository( self, service_identifier ) as dlg: dlg.ShowModal()
|
|
|
|
elif service_identifier.GetType() == HC.MESSAGE_DEPOT:
|
|
|
|
with ClientGUIDialogsManage.DialogManageOptionsMessageDepot( self, service_identifier ) as dlg: dlg.ShowModal()
|
|
|
|
elif service_identifier.GetType() == HC.SERVER_ADMIN:
|
|
|
|
with ClientGUIDialogsManage.DialogManageOptionsServerAdmin( self, service_identifier ) as dlg: dlg.ShowModal()
|
|
|
|
|
|
|
|
|
|
def _ManagePixivAccount( self ):
|
|
|
|
with ClientGUIDialogsManage.DialogManagePixivAccount( self ) as dlg: dlg.ShowModal()
|
|
|
|
|
|
def _ManageServer( self, service_identifier ):
|
|
|
|
with ClientGUIDialogsManage.DialogManageServer( self, service_identifier ) as dlg: dlg.ShowModal()
|
|
|
|
|
|
def _ManageServices( self ):
|
|
|
|
original_pause_status = HC.options[ 'pause_repo_sync' ]
|
|
|
|
HC.options[ 'pause_repo_sync' ] = True
|
|
|
|
try:
|
|
|
|
with ClientGUIDialogsManage.DialogManageServices( self ) as dlg: dlg.ShowModal()
|
|
|
|
finally: HC.options[ 'pause_repo_sync' ] = original_pause_status
|
|
|
|
|
|
def _ManageSubscriptions( self ):
|
|
|
|
original_pause_status = HC.options[ 'pause_subs_sync' ]
|
|
|
|
HC.options[ 'pause_subs_sync' ] = True
|
|
|
|
try:
|
|
|
|
with ClientGUIDialogsManage.DialogManageSubscriptions( self ) as dlg: dlg.ShowModal()
|
|
|
|
finally: HC.options[ 'pause_subs_sync' ] = original_pause_status
|
|
|
|
|
|
def _ManageTagParents( self ):
|
|
|
|
with ClientGUIDialogsManage.DialogManageTagParents( self ) as dlg: dlg.ShowModal()
|
|
|
|
|
|
def _ManageTagServicePrecedence( self ):
|
|
|
|
with ClientGUIDialogsManage.DialogManageTagServicePrecedence( self ) as dlg: dlg.ShowModal()
|
|
|
|
|
|
def _ManageTagSiblings( self ):
|
|
|
|
with ClientGUIDialogsManage.DialogManageTagSiblings( self ) as dlg: dlg.ShowModal()
|
|
|
|
|
|
def _ModifyAccount( self, service_identifier ):
|
|
|
|
service = HC.app.Read( 'service', service_identifier )
|
|
|
|
with wx.TextEntryDialog( self, 'Enter the access key for the account to be modified' ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
try: access_key = dlg.GetValue().decode( 'hex' )
|
|
except:
|
|
|
|
wx.MessageBox( 'Could not parse that access key' )
|
|
|
|
return
|
|
|
|
|
|
subject_identifiers = ( HC.AccountIdentifier( access_key = access_key ), )
|
|
|
|
with ClientGUIDialogs.DialogModifyAccounts( self, service_identifier, subject_identifiers ) as dlg2: dlg2.ShowModal()
|
|
|
|
|
|
|
|
|
|
def _NewAccounts( self, service_identifier ):
|
|
|
|
with ClientGUIDialogs.DialogInputNewAccounts( self, service_identifier ) as dlg: dlg.ShowModal()
|
|
|
|
|
|
def _NewPageImportBooru( self ):
|
|
|
|
with ClientGUIDialogs.DialogSelectBooru( self ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
booru = dlg.GetBooru()
|
|
|
|
new_page = ClientGUIPages.PageImportBooru( self._notebook, booru )
|
|
|
|
self._notebook.AddPage( new_page, booru.GetName(), select = True )
|
|
|
|
self._notebook.SetSelection( self._notebook.GetPageCount() - 1 )
|
|
|
|
new_page.SetSearchFocus()
|
|
|
|
|
|
|
|
|
|
def _NewPageImportGallery( self, name ):
|
|
|
|
if name == 'deviant art by artist': new_page = ClientGUIPages.PageImportDeviantArt( self._notebook )
|
|
elif name == 'hentai foundry by artist': new_page = ClientGUIPages.PageImportHentaiFoundryArtist( self._notebook )
|
|
elif name == 'hentai foundry by tags': new_page = ClientGUIPages.PageImportHentaiFoundryTags( self._notebook )
|
|
elif name == 'giphy': new_page = ClientGUIPages.PageImportGiphy( self._notebook )
|
|
elif name == 'newgrounds': new_page = ClientGUIPages.PageImportNewgrounds( self._notebook )
|
|
elif name == 'pixiv by artist': new_page = ClientGUIPages.PageImportPixivArtist( self._notebook )
|
|
elif name == 'pixiv by tag': new_page = ClientGUIPages.PageImportPixivTag( self._notebook )
|
|
elif name == 'tumblr': new_page = ClientGUIPages.PageImportTumblr( self._notebook )
|
|
|
|
self._notebook.AddPage( new_page, name, select = True )
|
|
|
|
self._notebook.SetSelection( self._notebook.GetPageCount() - 1 )
|
|
|
|
new_page.SetSearchFocus()
|
|
|
|
|
|
def _NewPageImportThreadWatcher( self ):
|
|
|
|
new_page = ClientGUIPages.PageImportThreadWatcher( self._notebook )
|
|
|
|
self._notebook.AddPage( new_page, 'thread watcher', select = True )
|
|
|
|
self._notebook.SetSelection( self._notebook.GetPageCount() - 1 )
|
|
|
|
new_page.SetSearchFocus()
|
|
|
|
|
|
def _NewPageImportURL( self ):
|
|
|
|
new_page = ClientGUIPages.PageImportURL( self._notebook )
|
|
|
|
self._notebook.AddPage( new_page, 'download', select = True )
|
|
|
|
self._notebook.SetSelection( self._notebook.GetPageCount() - 1 )
|
|
|
|
new_page.SetSearchFocus()
|
|
|
|
|
|
def _NewPageLog( self ):
|
|
|
|
new_page = ClientGUIPages.PageLog( self._notebook )
|
|
|
|
self._notebook.AddPage( new_page, 'log', select = True )
|
|
|
|
self._notebook.SetSelection( self._notebook.GetPageCount() - 1 )
|
|
|
|
|
|
def _NewPageMessages( self, identity ):
|
|
|
|
new_page = ClientGUIPages.PageMessages( self._notebook, identity )
|
|
|
|
self._notebook.AddPage( new_page, identity.GetName() + ' messages', select = True )
|
|
|
|
self._notebook.SetSelection( self._notebook.GetPageCount() - 1 )
|
|
|
|
new_page.SetSearchFocus()
|
|
|
|
|
|
def _NewPagePetitions( self, service_identifier = None ):
|
|
|
|
if service_identifier is None: service_identifier = ClientGUIDialogs.SelectServiceIdentifier( service_types = HC.REPOSITORIES, permission = HC.RESOLVE_PETITIONS )
|
|
|
|
if service_identifier is not None:
|
|
|
|
service = HC.app.Read( 'service', service_identifier )
|
|
|
|
account = service.GetAccount()
|
|
|
|
if not account.HasPermission( HC.RESOLVE_PETITIONS ): return
|
|
|
|
self._notebook.AddPage( ClientGUIPages.PagePetitions( self._notebook, service_identifier ), service_identifier.GetName() + ' petitions', select = True )
|
|
|
|
|
|
|
|
def _NewPageQuery( self, service_identifier, initial_media_results = [], initial_predicates = [] ):
|
|
|
|
if service_identifier is None: service_identifier = ClientGUIDialogs.SelectServiceIdentifier( service_types = ( HC.FILE_REPOSITORY, ) )
|
|
|
|
if service_identifier is not None:
|
|
|
|
new_page = ClientGUIPages.PageQuery( self._notebook, service_identifier, initial_media_results = initial_media_results, initial_predicates = initial_predicates )
|
|
|
|
self._notebook.AddPage( new_page, 'files', select = True )
|
|
|
|
wx.CallAfter( new_page.SetSearchFocus )
|
|
|
|
|
|
|
|
def _OpenExportFolder( self ):
|
|
|
|
export_path = HC.ConvertPortablePathToAbsPath( HC.options[ 'export_path' ] )
|
|
|
|
if export_path is None: wx.MessageBox( 'Export folder is missing or not set.' )
|
|
else:
|
|
|
|
export_path = os.path.normpath( export_path ) # windows complains about those forward slashes when launching from the command line
|
|
|
|
if 'Windows' in os.environ.get( 'os' ): subprocess.Popen( [ 'explorer', export_path ] )
|
|
else: subprocess.Popen( [ 'explorer', export_path ] )
|
|
|
|
|
|
|
|
def _PauseSync( self, sync_type ):
|
|
|
|
if sync_type == 'repo': HC.options[ 'pause_repo_sync' ] = not HC.options[ 'pause_repo_sync' ]
|
|
elif sync_type == 'subs': HC.options[ 'pause_subs_sync' ] = not HC.options[ 'pause_subs_sync' ]
|
|
|
|
try: HC.app.Write( 'save_options' )
|
|
except: wx.MessageBox( traceback.format_exc() )
|
|
|
|
self.RefreshMenuBar()
|
|
|
|
HC.pubsub.pub( 'notify_new_subscriptions' ) # this pushes the daemon to restart if sleeping
|
|
|
|
|
|
def _PostNews( self, service_identifier ):
|
|
|
|
with wx.TextEntryDialog( self, 'Enter the news you would like to post.' ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
news = dlg.GetValue()
|
|
|
|
service = HC.app.Read( 'service', service_identifier )
|
|
|
|
connection = service.GetConnection()
|
|
|
|
with wx.BusyCursor(): connection.Post( 'news', news = news )
|
|
|
|
|
|
|
|
|
|
def _RefreshStatusBar( self ):
|
|
|
|
page = self._notebook.GetCurrentPage()
|
|
|
|
if page is None: media_status = ''
|
|
else: media_status = page.GetPrettyStatus()
|
|
|
|
self._statusbar_media = media_status
|
|
|
|
self._statusbar.SetStatusText( self._statusbar_media, number = 0 )
|
|
self._statusbar.SetStatusText( self._statusbar_service, number = 1 )
|
|
self._statusbar.SetStatusText( self._statusbar_inbox, number = 2 )
|
|
self._statusbar.SetStatusText( self._statusbar_downloads, number = 3 )
|
|
self._statusbar.SetStatusText( self._statusbar_db_locked, number = 4 )
|
|
|
|
|
|
def _ReviewServices( self ):
|
|
|
|
try: FrameReviewServices()
|
|
except: wx.MessageBox( traceback.format_exc() )
|
|
|
|
|
|
def _SetPassword( self ):
|
|
|
|
message = '''You can set a password to be asked for whenever the client starts.
|
|
|
|
Though not foolproof, it will stop noobs from easily seeing your files if you leave your machine unattended.
|
|
|
|
Do not ever forget your password! If you do, you'll have to manually insert a yaml-dumped python dictionary into a sqlite database or recompile from source to regain easy access. This is not trivial.
|
|
|
|
The password is cleartext here but obscured in the entry dialog. Enter a blank password to remove.'''
|
|
|
|
with wx.TextEntryDialog( self, message, 'Enter new password' ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
password = dlg.GetValue()
|
|
|
|
if password == '': password = None
|
|
|
|
HC.app.Write( 'set_password', password )
|
|
|
|
|
|
|
|
|
|
def _SetMediaFocus( self ):
|
|
|
|
page = self._notebook.GetCurrentPage()
|
|
|
|
if page is not None: page.SetMediaFocus()
|
|
|
|
|
|
def _SetSearchFocus( self ):
|
|
|
|
page = self._notebook.GetCurrentPage()
|
|
|
|
if page is not None: page.SetSearchFocus()
|
|
|
|
|
|
def _SetSynchronisedWait( self ):
|
|
|
|
page = self._notebook.GetCurrentPage()
|
|
|
|
if page is not None: page.SetSynchronisedWait()
|
|
|
|
|
|
def _UploadPending( self, service_identifier ):
|
|
|
|
job_key = os.urandom( 32 )
|
|
|
|
if service_identifier.GetType() == HC.TAG_REPOSITORY: cancel_event = None
|
|
else: cancel_event = threading.Event()
|
|
|
|
with ClientGUIDialogs.DialogProgress( self, job_key, cancel_event ) as dlg:
|
|
|
|
threading.Thread( target = self._THREADUploadPending, args = ( service_identifier, job_key, cancel_event ) ).start()
|
|
|
|
dlg.ShowModal()
|
|
|
|
|
|
|
|
def _VacuumDatabase( self ):
|
|
|
|
message = 'This will rebuild the database, rewriting all indices and tables to be contiguous, optimising most operations. If you have a large database, it will take some time.'
|
|
|
|
with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_YES: HC.app.Write( 'vacuum' )
|
|
|
|
|
|
|
|
def DoFirstStart( self ):
|
|
|
|
with ClientGUIDialogs.DialogFirstStart( self ) as dlg: dlg.ShowModal()
|
|
|
|
|
|
def EventExit( self, event ):
|
|
|
|
if HC.options[ 'confirm_client_exit' ]:
|
|
|
|
message = 'Are you sure you want to exit the client?'
|
|
|
|
with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_NO: return
|
|
|
|
|
|
|
|
page = self._notebook.GetCurrentPage()
|
|
|
|
if page is not None and self.IsMaximized():
|
|
|
|
( HC.options[ 'hpos' ], HC.options[ 'vpos' ] ) = page.GetSashPositions()
|
|
|
|
with wx.BusyCursor(): HC.app.Write( 'save_options' )
|
|
|
|
|
|
for page in [ self._notebook.GetPage( i ) for i in range( self._notebook.GetPageCount() ) ]:
|
|
|
|
try: page.TryToClose()
|
|
except: return
|
|
|
|
|
|
self._message_manager.CleanUp()
|
|
self._message_manager.Destroy()
|
|
|
|
self.Hide()
|
|
|
|
# for some insane reason, the read makes the controller block until the writes are done!??!
|
|
# hence the hide, to make it appear the destroy is actually happening on time
|
|
|
|
HC.app.MaintainDB()
|
|
|
|
self.Destroy()
|
|
|
|
|
|
def EventFocus( self, event ):
|
|
|
|
page = self._notebook.GetCurrentPage()
|
|
|
|
if page is not None: page.SetMediaFocus()
|
|
|
|
|
|
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 == 'account_info': self._AccountInfo( data )
|
|
elif command == 'auto_repo_setup': self._AutoRepoSetup()
|
|
elif command == 'auto_server_setup': self._AutoServerSetup()
|
|
elif command == 'backup_service': self._BackupService( data )
|
|
elif command == 'clear_caches': HC.app.ClearCaches()
|
|
elif command == 'close_page': self._CloseCurrentPage()
|
|
elif command == 'debug_garbage':
|
|
|
|
import gc
|
|
import collections
|
|
import types
|
|
|
|
gc.collect()
|
|
|
|
count = collections.Counter()
|
|
|
|
class_count = collections.Counter()
|
|
|
|
for o in gc.get_objects():
|
|
|
|
count[ type( o ) ] += 1
|
|
|
|
if type( o ) == types.InstanceType: class_count[ o.__class__.__name__ ] += 1
|
|
elif type( o ) == types.BuiltinFunctionType: class_count[ o.__name__ ] += 1
|
|
elif type( o ) == types.BuiltinMethodType: class_count[ o.__name__ ] += 1
|
|
|
|
|
|
print( 'gc:' )
|
|
|
|
for ( k, v ) in count.items():
|
|
|
|
if v > 100: print ( k, v )
|
|
|
|
|
|
for ( k, v ) in class_count.items():
|
|
|
|
if v > 100: print ( k, v )
|
|
|
|
|
|
print( 'garbage: ' + HC.u( gc.garbage ) )
|
|
|
|
elif command == 'delete_pending': self._DeletePending( data )
|
|
elif command == 'exit': self.EventExit( event )
|
|
elif command == 'fetch_ip': self._FetchIP( data )
|
|
elif command == 'forum': webbrowser.open( 'http://hydrus.x10.mx/forum' )
|
|
elif command == 'help': webbrowser.open( 'file://' + HC.BASE_DIR + '/help/index.html' )
|
|
elif command == 'help_about': self._AboutWindow()
|
|
elif command == 'help_shortcuts': wx.MessageBox( CC.SHORTCUT_HELP )
|
|
elif command == 'import': self._ImportFiles()
|
|
elif command == 'manage_4chan_pass': self._Manage4chanPass()
|
|
elif command == 'manage_account_types': self._ManageAccountTypes( data )
|
|
elif command == 'manage_boorus': self._ManageBoorus()
|
|
elif command == 'manage_contacts': self._ManageContacts()
|
|
elif command == 'manage_imageboards': self._ManageImageboards()
|
|
elif command == 'manage_import_folders': self._ManageImportFolders()
|
|
elif command == 'manage_pixiv_account': self._ManagePixivAccount()
|
|
elif command == 'manage_server_services': self._ManageServer( data )
|
|
elif command == 'manage_services': self._ManageServices()
|
|
elif command == 'manage_subscriptions': self._ManageSubscriptions()
|
|
elif command == 'manage_tag_parents': self._ManageTagParents()
|
|
elif command == 'manage_tag_service_precedence': self._ManageTagServicePrecedence()
|
|
elif command == 'manage_tag_siblings': self._ManageTagSiblings()
|
|
elif command == 'modify_account': self._ModifyAccount( data )
|
|
elif command == 'new_accounts': self._NewAccounts( data )
|
|
elif command == 'new_import_booru': self._NewPageImportBooru()
|
|
elif command == 'new_import_thread_watcher': self._NewPageImportThreadWatcher()
|
|
elif command == 'new_import_url': self._NewPageImportURL()
|
|
elif command == 'new_log_page': self._NewPageLog()
|
|
elif command == 'new_messages_page': self._NewPageMessages( data )
|
|
elif command == 'new_page': FramePageChooser()
|
|
elif command == 'new_page_query': self._NewPageQuery( data )
|
|
elif command == 'news': self._News( data )
|
|
elif command == 'open_export_folder': self._OpenExportFolder()
|
|
elif command == 'options': self._ManageOptions( data )
|
|
elif command == 'pause_repo_sync': self._PauseSync( 'repo' )
|
|
elif command == 'pause_subs_sync': self._PauseSync( 'subs' )
|
|
elif command == 'petitions': self._NewPagePetitions( data )
|
|
elif command == 'post_news': self._PostNews( data )
|
|
elif command == 'refresh':
|
|
|
|
page = self._notebook.GetCurrentPage()
|
|
|
|
if page is not None: page.RefreshQuery()
|
|
|
|
elif command == 'review_services': self._ReviewServices()
|
|
elif command == 'show_hide_splitters':
|
|
|
|
page = self._notebook.GetCurrentPage()
|
|
|
|
if page is not None: page.ShowHideSplit()
|
|
|
|
elif command == 'set_password': self._SetPassword()
|
|
elif command == 'set_media_focus': self._SetMediaFocus()
|
|
elif command == 'set_search_focus': self._SetSearchFocus()
|
|
elif command == 'site': webbrowser.open( 'http://hydrusnetwork.github.io/hydrus/' )
|
|
elif command == 'stats': self._Stats( data )
|
|
elif command == 'synchronised_wait_switch': self._SetSynchronisedWait()
|
|
elif command == 'tumblr': webbrowser.open( 'http://hydrus.tumblr.com/' )
|
|
elif command == 'twitter': webbrowser.open( 'http://twitter.com/#!/hydrusnetwork' )
|
|
elif command == 'upload_pending': self._UploadPending( data )
|
|
elif command == 'vacuum_db': self._VacuumDatabase()
|
|
else: event.Skip()
|
|
|
|
|
|
|
|
def EventNotebookMiddleClick( self, event ):
|
|
|
|
( tab_index, flags ) = self._notebook.HitTest( ( event.GetX(), event.GetY() ) )
|
|
|
|
page = self._notebook.GetPage( tab_index )
|
|
|
|
try: page.TryToClose()
|
|
except: return
|
|
|
|
self._notebook.DeletePage( tab_index )
|
|
|
|
|
|
def EventNotebookPageChanged( self, event ):
|
|
|
|
old_selection = event.GetOldSelection()
|
|
selection = event.GetSelection()
|
|
|
|
if old_selection != -1: self._notebook.GetPage( old_selection ).PageHidden()
|
|
|
|
if selection != -1: self._notebook.GetPage( selection ).PageShown()
|
|
|
|
self._RefreshStatusBar()
|
|
|
|
event.Skip( True )
|
|
|
|
|
|
def ImportFiles( self, paths ): self._ImportFiles( paths )
|
|
|
|
def NewCompose( self, identity ):
|
|
|
|
draft_key = os.urandom( 32 )
|
|
conversation_key = draft_key
|
|
subject = ''
|
|
contact_from = identity
|
|
contacts_to = []
|
|
recipients_visible = False
|
|
body = ''
|
|
attachments = []
|
|
|
|
empty_draft_message = ClientConstantsMessages.DraftMessage( draft_key, conversation_key, subject, contact_from, contacts_to, recipients_visible, body, attachments, is_new = True )
|
|
|
|
FrameComposeMessage( empty_draft_message )
|
|
|
|
|
|
def NewPageImportBooru( self ): self._NewPageImportBooru()
|
|
|
|
def NewPageImportGallery( self, name ): self._NewPageImportGallery( name )
|
|
|
|
def NewPageImportHDD( self, paths_info, **kwargs ):
|
|
|
|
new_page = ClientGUIPages.PageImportHDD( self._notebook, paths_info, **kwargs )
|
|
|
|
self._notebook.AddPage( new_page, 'import', select = True )
|
|
|
|
self._notebook.SetSelection( self._notebook.GetPageCount() - 1 )
|
|
|
|
|
|
def NewPageImportThreadWatcher( self ): self._NewPageImportThreadWatcher()
|
|
|
|
def NewPageImportURL( self ): self._NewPageImportURL()
|
|
|
|
def NewPageMessages( self, identity ): self._NewPageMessages( identity )
|
|
|
|
def NewPagePetitions( self, service_identifier ): self._NewPagePetitions( service_identifier )
|
|
|
|
def NewPageQuery( self, service_identifier, initial_media_results = [], initial_predicates = [] ): self._NewPageQuery( service_identifier, initial_media_results = initial_media_results, initial_predicates = initial_predicates )
|
|
|
|
def NewPageThreadDumper( self, hashes ):
|
|
|
|
with ClientGUIDialogs.DialogSelectImageboard( self ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
imageboard = dlg.GetImageboard()
|
|
|
|
new_page = ClientGUIPages.PageThreadDumper( self._notebook, imageboard, hashes )
|
|
|
|
self._notebook.AddPage( new_page, 'imageboard dumper', select = True )
|
|
|
|
new_page.SetSearchFocus()
|
|
|
|
|
|
|
|
|
|
def NewSimilarTo( self, file_service_identifier, hash ): self._NewPageQuery( file_service_identifier, initial_predicates = [ HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_SIMILAR_TO, ( hash, 5 ) ), None ) ] )
|
|
|
|
def RefreshAcceleratorTable( self ):
|
|
|
|
interested_actions = [ 'archive', 'inbox', 'close_page', 'filter', 'ratings_filter', 'manage_ratings', 'manage_tags', 'new_page', 'refresh', 'set_media_focus', 'set_search_focus', 'show_hide_splitters', 'synchronised_wait_switch' ]
|
|
|
|
entries = []
|
|
|
|
for ( modifier, key_dict ) in HC.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 ) )
|
|
|
|
|
|
def RefreshMenuBar( self ):
|
|
|
|
p = HC.app.PrepStringForDisplay
|
|
|
|
services = HC.app.Read( 'services' )
|
|
|
|
tag_repositories = [ service for service in services if service.GetServiceIdentifier().GetType() == HC.TAG_REPOSITORY ]
|
|
|
|
tag_service_identifiers = [ repository.GetServiceIdentifier() for repository in tag_repositories ]
|
|
download_tag_service_identifiers = [ repository.GetServiceIdentifier() for repository in tag_repositories if repository.GetAccount().HasPermission( HC.GET_DATA ) ]
|
|
petition_resolve_tag_service_identifiers = [ repository.GetServiceIdentifier() for repository in tag_repositories if repository.GetAccount().HasPermission( HC.RESOLVE_PETITIONS ) ]
|
|
admin_tag_service_identifiers = [ repository.GetServiceIdentifier() for repository in tag_repositories if repository.GetAccount().HasPermission( HC.GENERAL_ADMIN ) ]
|
|
|
|
file_repositories = [ service for service in services if service.GetServiceIdentifier().GetType() == HC.FILE_REPOSITORY ]
|
|
|
|
file_service_identifiers = [ repository.GetServiceIdentifier() for repository in file_repositories ]
|
|
download_file_service_identifiers = [ repository.GetServiceIdentifier() for repository in file_repositories if repository.GetAccount().HasPermission( HC.GET_DATA ) ]
|
|
petition_resolve_file_service_identifiers = [ repository.GetServiceIdentifier() for repository in file_repositories if repository.GetAccount().HasPermission( HC.RESOLVE_PETITIONS ) ]
|
|
admin_file_service_identifiers = [ repository.GetServiceIdentifier() for repository in file_repositories if repository.GetAccount().HasPermission( HC.GENERAL_ADMIN ) ]
|
|
|
|
message_depots = [ service for service in services if service.GetServiceIdentifier().GetType() == HC.MESSAGE_DEPOT ]
|
|
|
|
admin_message_depots = [ message_depot.GetServiceIdentifier() for message_depot in message_depots if message_depot.GetAccount().HasPermission( HC.GENERAL_ADMIN ) ]
|
|
|
|
identities = HC.app.Read( 'identities' )
|
|
|
|
servers_admin = [ service for service in services if service.GetServiceIdentifier().GetType() == HC.SERVER_ADMIN ]
|
|
|
|
server_admin_identifiers = [ service.GetServiceIdentifier() for service in servers_admin if service.GetAccount().HasPermission( HC.GENERAL_ADMIN ) ]
|
|
|
|
menu = wx.MenuBar()
|
|
|
|
file = wx.Menu()
|
|
file.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'import' ), p( '&Import Files' ), p( 'Add new files to the database.' ) )
|
|
file.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_import_folders' ), p( 'Manage Import Folders' ), p( 'Manage folders from which the client can automatically import.' ) )
|
|
file.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'open_export_folder' ), p( 'Open E&xport Folder' ), p( 'Open the export folder so you can easily access files you have exported.' ) )
|
|
file.AppendSeparator()
|
|
file.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'options', HC.LOCAL_FILE_SERVICE_IDENTIFIER ), p( '&Options' ) )
|
|
file.AppendSeparator()
|
|
file.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'exit' ), p( '&Exit' ) )
|
|
|
|
menu.Append( file, p( '&File' ) )
|
|
|
|
view = wx.Menu()
|
|
view.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'refresh' ), p( '&Refresh' ), p( 'Refresh the current view.' ) )
|
|
view.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'show_hide_splitters' ), p( 'Show/Hide Splitters' ), p( 'Show or hide the current page\'s splitters.' ) )
|
|
view.AppendSeparator()
|
|
view.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'new_page' ), p( 'Pick a New &Page' ), p( 'Pick a new page.' ) )
|
|
view.AppendSeparator()
|
|
view.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'new_page_query', HC.LOCAL_FILE_SERVICE_IDENTIFIER ), p( '&New Local Search' ), p( 'Open a new search tab for your files' ) )
|
|
for s_i in file_service_identifiers: view.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'new_page_query', s_i ), p( 'New ' + s_i.GetName() + ' Search' ), p( 'Open a new search tab for ' + s_i.GetName() + '.' ) )
|
|
if len( petition_resolve_tag_service_identifiers ) > 0 or len( petition_resolve_file_service_identifiers ) > 0:
|
|
|
|
view.AppendSeparator()
|
|
for s_i in petition_resolve_tag_service_identifiers: view.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'petitions', s_i ), p( s_i.GetName() + ' Petitions' ), p( 'Open a petition tab for ' + s_i.GetName() ) )
|
|
for s_i in petition_resolve_file_service_identifiers: view.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'petitions', s_i ), p( s_i.GetName() + ' Petitions' ), p( 'Open a petition tab for ' + s_i.GetName() ) )
|
|
|
|
view.AppendSeparator()
|
|
view.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'new_import_url' ), p( '&New URL Download Page' ), p( 'Open a new tab to download files from galleries or threads.' ) )
|
|
view.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'new_import_booru' ), p( '&New Booru Download Page' ), p( 'Open a new tab to download files from a booru.' ) )
|
|
view.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'new_import_thread_watcher' ), p( '&New Thread Watcher Page' ), p( 'Open a new tab to watch a thread.' ) )
|
|
view.AppendSeparator()
|
|
if len( identities ) > 0:
|
|
|
|
for identity in identities: view.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'new_messages_page', identity ), p( identity.GetName() + ' Message Page' ), p( 'Open a new tab to review the messages for ' + identity.GetName() ) )
|
|
view.AppendSeparator()
|
|
|
|
view.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'new_log_page' ), p( '&New Log Page' ), p( 'Open a new tab to show recently logged events.' ) )
|
|
|
|
menu.Append( view, p( '&View' ) )
|
|
|
|
database = wx.Menu()
|
|
database.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'set_password' ), p( 'Set a &Password' ), p( 'Set a password for the database so only you can access it.' ) )
|
|
database.AppendSeparator()
|
|
#database.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'reindex_db' ), '&reindex', 'reindex the database.' )
|
|
database.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'vacuum_db' ), p( '&Vacuum' ), p( 'Rebuild the Database.' ) )
|
|
database.AppendSeparator()
|
|
database.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'clear_caches' ), p( '&Clear Caches' ), p( 'Fully clear the fullscreen, preview and thumbnail caches.' ) )
|
|
|
|
menu.Append( database, p( '&Database' ) )
|
|
|
|
nums_pending = HC.app.Read( 'nums_pending' )
|
|
|
|
pending = wx.Menu()
|
|
|
|
total_num_pending = 0
|
|
|
|
for ( service_identifier, info ) in nums_pending.items():
|
|
|
|
service_type = service_identifier.GetType()
|
|
|
|
if service_type == HC.TAG_REPOSITORY:
|
|
|
|
num_pending = info[ HC.SERVICE_INFO_NUM_PENDING_MAPPINGS ] + info[ HC.SERVICE_INFO_NUM_PENDING_TAG_SIBLINGS ] + info[ HC.SERVICE_INFO_NUM_PENDING_TAG_PARENTS ]
|
|
num_petitioned = info[ HC.SERVICE_INFO_NUM_PETITIONED_MAPPINGS ] + info[ HC.SERVICE_INFO_NUM_PETITIONED_TAG_SIBLINGS ] + info[ HC.SERVICE_INFO_NUM_PETITIONED_TAG_PARENTS ]
|
|
|
|
elif service_type == HC.FILE_REPOSITORY:
|
|
|
|
num_pending = info[ HC.SERVICE_INFO_NUM_PENDING_FILES ]
|
|
num_petitioned = info[ HC.SERVICE_INFO_NUM_PETITIONED_FILES ]
|
|
|
|
|
|
if num_pending + num_petitioned > 0:
|
|
|
|
submenu = wx.Menu()
|
|
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'upload_pending', service_identifier ), p( '&Upload' ), p( 'Upload ' + service_identifier.GetName() + '\'s Pending and Petitions.' ) )
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete_pending', service_identifier ), p( '&Forget' ), p( 'Clear ' + service_identifier.GetName() + '\'s Pending and Petitions.' ) )
|
|
|
|
pending.AppendMenu( CC.ID_NULL, p( service_identifier.GetName() + ' Pending (' + HC.ConvertIntToPrettyString( num_pending ) + '/' + HC.ConvertIntToPrettyString( num_petitioned ) + ')' ), submenu )
|
|
|
|
|
|
total_num_pending += num_pending + num_petitioned
|
|
|
|
|
|
if total_num_pending > 0: menu.Append( pending, p( '&Pending (' + HC.ConvertIntToPrettyString( total_num_pending ) + ')' ) )
|
|
else: pending.Destroy()
|
|
|
|
services = wx.Menu()
|
|
|
|
submenu = wx.Menu()
|
|
|
|
pause_repo_sync_id = CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'pause_repo_sync' )
|
|
pause_subs_sync_id = CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'pause_subs_sync' )
|
|
|
|
submenu.AppendCheckItem( pause_repo_sync_id, p( '&Repositories Synchronisation' ), p( 'Pause the client\'s synchronisation with hydrus repositories.' ) )
|
|
submenu.AppendCheckItem( pause_subs_sync_id, p( '&Subscriptions Synchronisation' ), p( 'Pause the client\'s synchronisation with website subscriptions.' ) )
|
|
|
|
submenu.Check( pause_repo_sync_id, HC.options[ 'pause_repo_sync' ] )
|
|
submenu.Check( pause_subs_sync_id, HC.options[ 'pause_subs_sync' ] )
|
|
|
|
services.AppendMenu( CC.ID_NULL, p( 'Pause' ), submenu )
|
|
|
|
services.AppendSeparator()
|
|
services.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'review_services' ), p( '&Review Services' ), p( 'Review your services.' ) )
|
|
services.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_services' ), p( '&Add, Remove or Edit Services' ), p( 'Edit your services.' ) )
|
|
services.AppendSeparator()
|
|
services.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_tag_siblings' ), p( '&Manage Tag Siblings' ), p( 'Set certain tags to be automatically replaced with other tags.' ) )
|
|
services.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_tag_parents' ), p( '&Manage Tag Parents' ), p( 'Set certain tags to be automatically added with other tags.' ) )
|
|
#services.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_tag_service_precedence' ), p( '&Manage Tag Service Precedence' ), p( 'Change the order in which tag repositories\' taxonomies will be added to the database.' ) )
|
|
services.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_tag_service_precedence' ), p( '&Manage Tag Service Precedence' ), p( 'Change the order in which tag repositories\' taxonomies will be added to the database.' ) )
|
|
services.AppendSeparator()
|
|
services.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_boorus' ), p( 'Manage &Boorus' ), p( 'Change the html parsing information for boorus to download from.' ) )
|
|
services.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_imageboards' ), p( 'Manage &Imageboards' ), p( 'Change the html POST form information for imageboards to dump to.' ) )
|
|
services.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_4chan_pass' ), p( 'Manage &4chan Pass' ), p( 'Set up your 4chan pass, so you can dump without having to fill in a captcha.' ) )
|
|
services.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_pixiv_account' ), p( 'Manage &Pixiv Account' ), p( 'Set up your pixiv username and password.' ) )
|
|
services.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_subscriptions' ), p( 'Manage &Subscriptions' ), p( 'Change the queries you want the client to regularly import from.' ) )
|
|
services.AppendSeparator()
|
|
services.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_contacts' ), p( 'Manage &Contacts and Identities' ), p( 'Change the names and addresses of the people you talk to.' ) )
|
|
services.AppendSeparator()
|
|
submenu = wx.Menu()
|
|
for s_i in tag_service_identifiers: submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'news', s_i ), p( s_i.GetName() ), p( 'Review ' + s_i.GetName() + '\'s past news.' ) )
|
|
for s_i in file_service_identifiers: submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'news', s_i ), p( s_i.GetName() ), p( 'Review ' + s_i.GetName() + '\'s past news.' ) )
|
|
services.AppendMenu( CC.ID_NULL, p( 'News' ), submenu )
|
|
|
|
menu.Append( services, p( '&Services' ) )
|
|
|
|
if len( admin_tag_service_identifiers ) > 0 or len( admin_file_service_identifiers ) > 0 or len( server_admin_identifiers ) > 0:
|
|
|
|
admin = wx.Menu()
|
|
|
|
for s_i in admin_tag_service_identifiers:
|
|
|
|
submenu = wx.Menu()
|
|
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'new_accounts', s_i ), p( 'Create New &Accounts' ), p( 'Create new accounts.' ) )
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_account_types', s_i ), p( '&Manage Account Types' ), p( 'Add, edit and delete account types for the tag repository.' ) )
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'modify_account', s_i ), p( '&Modify an Account' ), p( 'Modify a specific account\'s type and expiration.' ) )
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'account_info', s_i ), p( '&Get an Account\'s Info' ), p( 'Fetch information about an account from the tag repository.' ) )
|
|
submenu.AppendSeparator()
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'options', s_i ), p( '&Options' ), p( 'Set the tag repository\'s options.' ) )
|
|
submenu.AppendSeparator()
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'stats', s_i ), p( '&Get Stats' ), p( 'Fetch operating statistics from the tag repository.' ) )
|
|
submenu.AppendSeparator()
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'post_news', s_i ), p( '&Post News' ), p( 'Post a news item to the tag repository.' ) )
|
|
|
|
admin.AppendMenu( CC.ID_NULL, p( s_i.GetName() ), submenu )
|
|
|
|
|
|
for s_i in admin_file_service_identifiers:
|
|
|
|
submenu = wx.Menu()
|
|
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'new_accounts', s_i ), p( 'Create New &Accounts' ), p( 'Create new accounts.' ) )
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_account_types', s_i ), p( '&Manage Account Types' ), p( 'Add, edit and delete account types for the file repository.' ) )
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'modify_account', s_i ), p( '&Modify an Account' ), p( 'Modify a specific account\'s type and expiration.' ) )
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'account_info', s_i ), p( '&Get an Account\'s Info' ), p( 'Fetch information about an account from the file repository.' ) )
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'fetch_ip', s_i ), p( '&Get an Uploader\'s IP Address' ), p( 'Fetch an uploader\'s ip address.' ) )
|
|
submenu.AppendSeparator()
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'options', s_i ), p( '&Options' ), p( 'Set the file repository\'s options.' ) )
|
|
submenu.AppendSeparator()
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'stats', s_i ), p( '&Get Stats' ), p( 'Fetch operating statistics from the file repository.' ) )
|
|
submenu.AppendSeparator()
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'post_news', s_i ), p( '&Post News' ), p( 'Post a news item to the file repository.' ) )
|
|
|
|
admin.AppendMenu( CC.ID_NULL, p( s_i.GetName() ), submenu )
|
|
|
|
|
|
for s_i in admin_message_depots:
|
|
|
|
submenu = wx.Menu()
|
|
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'new_accounts', s_i ), p( 'Create New &Accounts' ), p( 'Create new accounts.' ) )
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_account_types', s_i ), p( '&Manage Account Types' ), p( 'Add, edit and delete account types for the file repository.' ) )
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'modify_account', s_i ), p( '&Modify an Account' ), p( 'Modify a specific account\'s type and expiration.' ) )
|
|
|
|
admin.AppendMenu( CC.ID_NULL, p( s_i.GetName() ), submenu )
|
|
|
|
|
|
for s_i in server_admin_identifiers:
|
|
|
|
submenu = wx.Menu()
|
|
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'options', s_i ), p( '&Options' ), p( 'Set the server\'s options.' ) )
|
|
submenu.AppendSeparator()
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_server_services', s_i ), p( 'Manage &Services' ), p( 'Add, edit, and delete this server\'s services.' ) )
|
|
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'backup_service', s_i ), p( 'Make a &Backup' ), p( 'Back up this server\'s database.' ) )
|
|
|
|
admin.AppendMenu( CC.ID_NULL, p( s_i.GetName() ), submenu )
|
|
|
|
|
|
menu.Append( admin, p( '&Admin' ) )
|
|
|
|
|
|
help = wx.Menu()
|
|
help.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'help' ), p( '&Help' ) )
|
|
dont_know = wx.Menu()
|
|
dont_know.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'auto_repo_setup' ), p( 'Just set up some repositories for me, please' ) )
|
|
dont_know.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'auto_server_setup' ), p( 'Just set up the server on this computer, please' ) )
|
|
help.AppendMenu( wx.ID_NONE, p( 'I don\'t know what I am doing' ), dont_know )
|
|
links = wx.Menu()
|
|
tumblr = wx.MenuItem( links, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'tumblr' ), p( 'Tumblr' ) )
|
|
tumblr.SetBitmap( wx.Bitmap( HC.STATIC_DIR + os.path.sep + 'tumblr.png' ) )
|
|
twitter = wx.MenuItem( links, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'twitter' ), p( 'Twitter' ) )
|
|
twitter.SetBitmap( wx.Bitmap( HC.STATIC_DIR + os.path.sep + 'twitter.png' ) )
|
|
site = wx.MenuItem( links, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'site' ), p( 'Site' ) )
|
|
site.SetBitmap( wx.Bitmap( HC.STATIC_DIR + os.path.sep + 'file_repository_small.png' ) )
|
|
forum = wx.MenuItem( links, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'forum' ), p( 'Forum' ) )
|
|
forum.SetBitmap( wx.Bitmap( HC.STATIC_DIR + os.path.sep + 'file_repository_small.png' ) )
|
|
links.AppendItem( tumblr )
|
|
links.AppendItem( twitter )
|
|
links.AppendItem( site )
|
|
links.AppendItem( forum )
|
|
help.AppendMenu( wx.ID_NONE, p( 'Links' ), links )
|
|
debug = wx.Menu()
|
|
debug.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'debug_garbage' ), p( 'Garbage' ) )
|
|
help.AppendMenu( wx.ID_NONE, p( 'Debug' ), debug )
|
|
help.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'help_shortcuts' ), p( '&Shortcuts' ) )
|
|
help.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'help_about' ), p( '&About' ) )
|
|
|
|
menu.Append( help, p( '&Help' ) )
|
|
|
|
old_menu = self.GetMenuBar()
|
|
|
|
self.SetMenuBar( menu )
|
|
|
|
if old_menu is not None: old_menu.Destroy()
|
|
|
|
|
|
def RefreshStatusBar( self ): self._RefreshStatusBar()
|
|
|
|
def SetDBLockedStatus( self, status ):
|
|
|
|
if self.IsShown():
|
|
|
|
self._statusbar_db_locked = status
|
|
|
|
self._RefreshStatusBar()
|
|
|
|
|
|
|
|
def SetDownloadsStatus( self, status ):
|
|
|
|
if self.IsShown():
|
|
|
|
self._statusbar_downloads = status
|
|
|
|
self._RefreshStatusBar()
|
|
|
|
|
|
|
|
def SetInboxStatus( self, status ):
|
|
|
|
if self.IsShown():
|
|
|
|
self._statusbar_inbox = status
|
|
|
|
self._RefreshStatusBar()
|
|
|
|
|
|
|
|
def SetServiceStatus( self, status ):
|
|
|
|
if self.IsShown():
|
|
|
|
self._statusbar_service = status
|
|
|
|
self._RefreshStatusBar()
|
|
|
|
|
|
|
|
class FrameComposeMessage( ClientGUICommon.Frame ):
|
|
|
|
def __init__( self, empty_draft_message ):
|
|
|
|
ClientGUICommon.Frame.__init__( self, None, title = HC.app.PrepStringForDisplay( 'Compose Message' ) )
|
|
|
|
self.SetIcon( wx.Icon( HC.STATIC_DIR + os.path.sep + 'hydrus.ico', wx.BITMAP_TYPE_ICO ) )
|
|
|
|
self.SetInitialSize( ( 920, 600 ) )
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
self._draft_panel = ClientGUIMessages.DraftPanel( self, empty_draft_message )
|
|
|
|
vbox.AddF( self._draft_panel, FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
self.Show( True )
|
|
|
|
HC.pubsub.sub( self, 'DeleteConversation', 'delete_conversation_gui' )
|
|
HC.pubsub.sub( self, 'DeleteDraft', 'delete_draft_gui' )
|
|
|
|
|
|
def DeleteConversation( self, conversation_key ):
|
|
|
|
if self._draft_panel.GetConversationKey() == conversation_key: self.Destroy()
|
|
|
|
|
|
def DeleteDraft( self, draft_key ):
|
|
|
|
if draft_key == self._draft_panel.GetDraftKey(): self.Destroy()
|
|
|
|
|
|
class FramePageChooser( ClientGUICommon.Frame ):
|
|
|
|
def __init__( self ):
|
|
|
|
def InitialiseControls():
|
|
|
|
self._button_hidden = wx.Button( self )
|
|
self._button_hidden.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
|
|
self._button_hidden.Hide()
|
|
|
|
self._button_1 = wx.Button( self, label = '', id = 1 )
|
|
self._button_2 = wx.Button( self, label = '', id = 2 )
|
|
self._button_3 = wx.Button( self, label = '', id = 3 )
|
|
self._button_4 = wx.Button( self, label = '', id = 4 )
|
|
self._button_5 = wx.Button( self, label = '', id = 5 )
|
|
self._button_6 = wx.Button( self, label = '', id = 6 )
|
|
self._button_7 = wx.Button( self, label = '', id = 7 )
|
|
self._button_8 = wx.Button( self, label = '', id = 8 )
|
|
self._button_9 = wx.Button( self, label = '', id = 9 )
|
|
|
|
|
|
def PopulateControls():
|
|
|
|
pass
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
|
|
|
|
gridbox = wx.GridSizer( 0, 3 )
|
|
|
|
gridbox.AddF( self._button_1, FLAGS_EXPAND_BOTH_WAYS )
|
|
gridbox.AddF( self._button_2, FLAGS_EXPAND_BOTH_WAYS )
|
|
gridbox.AddF( self._button_3, FLAGS_EXPAND_BOTH_WAYS )
|
|
gridbox.AddF( self._button_4, FLAGS_EXPAND_BOTH_WAYS )
|
|
gridbox.AddF( self._button_5, FLAGS_EXPAND_BOTH_WAYS )
|
|
gridbox.AddF( self._button_6, FLAGS_EXPAND_BOTH_WAYS )
|
|
gridbox.AddF( self._button_7, FLAGS_EXPAND_BOTH_WAYS )
|
|
gridbox.AddF( self._button_8, FLAGS_EXPAND_BOTH_WAYS )
|
|
gridbox.AddF( self._button_9, FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
self.SetSizer( gridbox )
|
|
|
|
self.SetInitialSize( ( 420, 210 ) )
|
|
|
|
|
|
ClientGUICommon.Frame.__init__( self, None, title = HC.app.PrepStringForDisplay( 'New Page' ) )
|
|
|
|
self.Center()
|
|
|
|
self.SetIcon( wx.Icon( HC.STATIC_DIR + os.path.sep + 'hydrus.ico', wx.BITMAP_TYPE_ICO ) )
|
|
|
|
self._keycodes_to_ids = {}
|
|
|
|
self._keycodes_to_ids[ wx.WXK_NUMPAD1 ] = 1
|
|
self._keycodes_to_ids[ wx.WXK_NUMPAD2 ] = 2
|
|
self._keycodes_to_ids[ wx.WXK_NUMPAD3 ] = 3
|
|
self._keycodes_to_ids[ wx.WXK_NUMPAD4 ] = 4
|
|
self._keycodes_to_ids[ wx.WXK_NUMPAD5 ] = 5
|
|
self._keycodes_to_ids[ wx.WXK_NUMPAD6 ] = 6
|
|
self._keycodes_to_ids[ wx.WXK_NUMPAD7 ] = 7
|
|
self._keycodes_to_ids[ wx.WXK_NUMPAD8 ] = 8
|
|
self._keycodes_to_ids[ wx.WXK_NUMPAD9 ] = 9
|
|
|
|
self._keycodes_to_ids[ wx.WXK_UP ] = 2
|
|
self._keycodes_to_ids[ wx.WXK_DOWN ] = 8
|
|
self._keycodes_to_ids[ wx.WXK_LEFT ] = 4
|
|
self._keycodes_to_ids[ wx.WXK_RIGHT ] = 6
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
self._services = HC.app.Read( 'services' )
|
|
|
|
self._petition_service_identifiers = [ service.GetServiceIdentifier() for service in self._services if service.GetServiceIdentifier().GetType() in HC.REPOSITORIES and service.GetAccount().HasPermission( HC.RESOLVE_PETITIONS ) ]
|
|
|
|
self._identities = HC.app.Read( 'identities' )
|
|
|
|
self._InitButtons( 'home' )
|
|
|
|
self.Bind( wx.EVT_BUTTON, self.EventButton )
|
|
self.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
|
|
|
|
self._button_hidden.SetFocus()
|
|
|
|
self.Show( True )
|
|
|
|
|
|
def _AddEntry( self, button, entry ):
|
|
|
|
id = button.GetId()
|
|
|
|
self._command_dict[ id ] = entry
|
|
|
|
( entry_type, obj ) = entry
|
|
|
|
if entry_type == 'menu': button.SetLabel( obj )
|
|
elif entry_type in ( 'page_query', 'page_petitions' ):
|
|
|
|
name = obj.GetName()
|
|
|
|
button.SetLabel( name )
|
|
|
|
elif entry_type == 'page_import_booru': button.SetLabel( 'booru' )
|
|
elif entry_type == 'page_import_gallery': button.SetLabel( obj )
|
|
elif entry_type == 'page_messages':
|
|
|
|
name = obj.GetName()
|
|
|
|
button.SetLabel( name )
|
|
|
|
elif entry_type == 'page_import_thread_watcher': button.SetLabel( 'thread watcher' )
|
|
elif entry_type == 'page_import_url': button.SetLabel( 'url' )
|
|
|
|
button.Show()
|
|
|
|
|
|
def _InitButtons( self, menu_keyword ):
|
|
|
|
self._command_dict = {}
|
|
|
|
if menu_keyword == 'home':
|
|
|
|
entries = [ ( 'menu', 'files' ), ( 'menu', 'download' ) ]
|
|
|
|
if len( self._petition_service_identifiers ) > 0: entries.append( ( 'menu', 'petitions' ) )
|
|
|
|
if len( self._identities ) > 0: entries.append( ( 'menu', 'messages' ) )
|
|
|
|
elif menu_keyword == 'files':
|
|
|
|
file_repos = [ ( 'page_query', service_identifier ) for service_identifier in [ service.GetServiceIdentifier() for service in self._services ] if service_identifier.GetType() == HC.FILE_REPOSITORY ]
|
|
|
|
entries = [ ( 'page_query', HC.LOCAL_FILE_SERVICE_IDENTIFIER ) ] + file_repos
|
|
|
|
elif menu_keyword == 'download': entries = [ ( 'page_import_url', None ), ( 'page_import_thread_watcher', None ), ( 'menu', 'gallery' ) ]
|
|
elif menu_keyword == 'gallery':
|
|
|
|
entries = [ ( 'page_import_booru', None ), ( 'page_import_gallery', 'giphy' ), ( 'page_import_gallery', 'deviant art by artist' ), ( 'menu', 'hentai foundry' ), ( 'page_import_gallery', 'newgrounds' ) ]
|
|
|
|
( id, password ) = HC.app.Read( 'pixiv_account' )
|
|
|
|
if id != '' and password != '': entries.append( ( 'menu', 'pixiv' ) )
|
|
|
|
entries.extend( [ ( 'page_import_gallery', 'tumblr' ) ] )
|
|
|
|
elif menu_keyword == 'hentai foundry': entries = [ ( 'page_import_gallery', 'hentai foundry by artist' ), ( 'page_import_gallery', 'hentai foundry by tags' ) ]
|
|
elif menu_keyword == 'pixiv': entries = [ ( 'page_import_gallery', 'pixiv by artist' ), ( 'page_import_gallery', 'pixiv by tag' ) ]
|
|
elif menu_keyword == 'messages': entries = [ ( 'page_messages', identity ) for identity in self._identities ]
|
|
elif menu_keyword == 'petitions': entries = [ ( 'page_petitions', service_identifier ) for service_identifier in self._petition_service_identifiers ]
|
|
|
|
if len( entries ) <= 4:
|
|
|
|
self._button_1.Hide()
|
|
self._button_3.Hide()
|
|
self._button_5.Hide()
|
|
self._button_7.Hide()
|
|
self._button_9.Hide()
|
|
|
|
usable_buttons = [ self._button_2, self._button_4, self._button_6, self._button_8 ]
|
|
|
|
elif len( entries ) <= 9: usable_buttons = [ self._button_1, self._button_2, self._button_3, self._button_4, self._button_5, self._button_6, self._button_7, self._button_8, self._button_9 ]
|
|
else:
|
|
|
|
pass # sort out a multi-page solution? maybe only if this becomes a big thing; the person can always select from the menus, yeah?
|
|
|
|
usable_buttons = [ self._button_1, self._button_2, self._button_3, self._button_4, self._button_5, self._button_6, self._button_7, self._button_8, self._button_9 ]
|
|
entries = entries[:9]
|
|
|
|
|
|
for entry in entries: self._AddEntry( usable_buttons.pop( 0 ), entry )
|
|
|
|
for button in usable_buttons: button.Hide()
|
|
|
|
|
|
def EventButton( self, event ):
|
|
|
|
id = event.GetId()
|
|
|
|
if id in self._command_dict:
|
|
|
|
( entry_type, obj ) = self._command_dict[ id ]
|
|
|
|
if entry_type == 'menu': self._InitButtons( obj )
|
|
else:
|
|
|
|
if entry_type == 'page_query': HC.pubsub.pub( 'new_page_query', obj )
|
|
elif entry_type == 'page_import_booru': HC.pubsub.pub( 'new_page_import_booru' )
|
|
elif entry_type == 'page_import_gallery': HC.pubsub.pub( 'new_page_import_gallery', obj )
|
|
elif entry_type == 'page_import_thread_watcher': HC.pubsub.pub( 'new_page_import_thread_watcher' )
|
|
elif entry_type == 'page_import_url': HC.pubsub.pub( 'new_page_import_url' )
|
|
elif entry_type == 'page_messages': HC.pubsub.pub( 'new_page_messages', obj )
|
|
elif entry_type == 'page_petitions': HC.pubsub.pub( 'new_page_petitions', obj )
|
|
|
|
self.Destroy()
|
|
|
|
|
|
|
|
self._button_hidden.SetFocus()
|
|
|
|
|
|
def EventKeyDown( self, event ):
|
|
|
|
if event.KeyCode in self._keycodes_to_ids.keys():
|
|
|
|
id = self._keycodes_to_ids[ event.KeyCode ]
|
|
|
|
new_event = wx.CommandEvent( wx.wxEVT_COMMAND_BUTTON_CLICKED, winid = id )
|
|
|
|
self.ProcessEvent( new_event )
|
|
|
|
elif event.KeyCode == wx.WXK_ESCAPE: self.Destroy()
|
|
else: event.Skip()
|
|
|
|
|
|
class FrameReviewServices( ClientGUICommon.Frame ):
|
|
|
|
def __init__( self ):
|
|
|
|
def InitialiseControls():
|
|
|
|
self._listbook = ClientGUICommon.ListBook( self )
|
|
|
|
self._edit = wx.Button( self, label='add, remove or edit services' )
|
|
self._edit.Bind( wx.EVT_BUTTON, self.EventEdit )
|
|
|
|
self._ok = wx.Button( self, label='ok' )
|
|
self._ok.Bind( wx.EVT_BUTTON, self.EventOk )
|
|
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
|
|
|
|
|
|
def PopulateControls():
|
|
|
|
self._InitialiseServices()
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
vbox.AddF( self._listbook, FLAGS_EXPAND_BOTH_WAYS )
|
|
vbox.AddF( self._edit, FLAGS_SMALL_INDENT )
|
|
vbox.AddF( self._ok, FLAGS_BUTTON_SIZERS )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
self.SetInitialSize( ( 880, 620 ) )
|
|
|
|
|
|
( pos_x, pos_y ) = HC.app.GetGUI().GetPositionTuple()
|
|
|
|
pos = ( pos_x + 25, pos_y + 50 )
|
|
|
|
ClientGUICommon.Frame.__init__( self, None, title = HC.app.PrepStringForDisplay( 'Review Services' ), pos = pos )
|
|
|
|
self.SetIcon( wx.Icon( HC.STATIC_DIR + os.path.sep + 'hydrus.ico', wx.BITMAP_TYPE_ICO ) )
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
self.Show( True )
|
|
|
|
HC.pubsub.sub( self, 'RefreshServices', 'notify_new_services' )
|
|
|
|
wx.CallAfter( self.Raise )
|
|
|
|
wx.CallAfter( self._ok.SetFocus )
|
|
|
|
|
|
def _InitialiseServices( self ):
|
|
|
|
self._listbook.DeleteAllPages()
|
|
|
|
listbook_dict = {}
|
|
|
|
service_identifiers = HC.app.Read( 'service_identifiers' )
|
|
|
|
for service_identifier in service_identifiers:
|
|
|
|
service_type = service_identifier.GetType()
|
|
|
|
if service_type in ( HC.LOCAL_FILE, HC.LOCAL_TAG ):
|
|
|
|
page = self._Panel( self._listbook, service_identifier )
|
|
|
|
name = service_identifier.GetName()
|
|
|
|
self._listbook.AddPage( page, name )
|
|
|
|
else:
|
|
|
|
if service_type not in listbook_dict:
|
|
|
|
listbook = ClientGUICommon.ListBook( self._listbook )
|
|
|
|
listbook_dict[ service_type ] = listbook
|
|
|
|
if service_type == HC.TAG_REPOSITORY: name = 'tags'
|
|
elif service_type == HC.FILE_REPOSITORY: name = 'files'
|
|
elif service_type == HC.MESSAGE_DEPOT: name = 'message depots'
|
|
elif service_type == HC.SERVER_ADMIN: name = 'servers admin'
|
|
elif service_type == HC.LOCAL_RATING_LIKE: name = 'local ratings like'
|
|
elif service_type == HC.LOCAL_RATING_NUMERICAL: name = 'local ratings numerical'
|
|
|
|
self._listbook.AddPage( listbook, name )
|
|
|
|
|
|
listbook = listbook_dict[ service_type ]
|
|
|
|
page = ( self._Panel, [ listbook, service_identifier ], {} )
|
|
|
|
name = service_identifier.GetName()
|
|
|
|
listbook.AddPage( page, name )
|
|
|
|
|
|
|
|
wx.CallAfter( self._listbook.Layout )
|
|
|
|
|
|
def EventEdit( self, event ):
|
|
|
|
original_pause_status = HC.options[ 'pause_repo_sync' ]
|
|
|
|
HC.options[ 'pause_repo_sync' ] = True
|
|
|
|
try:
|
|
|
|
with ClientGUIDialogsManage.DialogManageServices( self ) as dlg: dlg.ShowModal()
|
|
|
|
except: wx.MessageBox( traceback.format_exc() )
|
|
|
|
HC.options[ 'pause_repo_sync' ] = original_pause_status
|
|
|
|
|
|
def EventOk( self, event ): self.Destroy()
|
|
|
|
def RefreshServices( self ): self._InitialiseServices()
|
|
|
|
class _Panel( wx.ScrolledWindow ):
|
|
|
|
def __init__( self, parent, service_identifier ):
|
|
|
|
def InitialiseControls():
|
|
|
|
if service_type in HC.RESTRICTED_SERVICES:
|
|
|
|
self._permissions_panel = ClientGUICommon.StaticBox( self, 'service permissions' )
|
|
|
|
self._account_type = wx.StaticText( self._permissions_panel )
|
|
|
|
self._age = ClientGUICommon.Gauge( self._permissions_panel )
|
|
|
|
self._age_text = wx.StaticText( self._permissions_panel, style = wx.ALIGN_CENTER | wx.ST_NO_AUTORESIZE )
|
|
|
|
( max_num_bytes, max_num_requests ) = account_type.GetMaxMonthlyData()
|
|
|
|
self._bytes = ClientGUICommon.Gauge( self._permissions_panel )
|
|
|
|
self._bytes_text = wx.StaticText( self._permissions_panel, style = wx.ALIGN_CENTER | wx.ST_NO_AUTORESIZE )
|
|
|
|
self._requests = ClientGUICommon.Gauge( self._permissions_panel )
|
|
|
|
self._requests_text = wx.StaticText( self._permissions_panel, style = wx.ALIGN_CENTER | wx.ST_NO_AUTORESIZE )
|
|
|
|
if max_num_bytes is None: self._bytes.Hide()
|
|
if expires is None: self._age.Hide()
|
|
if max_num_requests is None: self._requests.Hide()
|
|
|
|
|
|
if service_type in HC.REPOSITORIES:
|
|
|
|
self._synchro_panel = ClientGUICommon.StaticBox( self, 'repository synchronisation' )
|
|
|
|
self._updates = ClientGUICommon.Gauge( self._synchro_panel )
|
|
|
|
self._updates_text = wx.StaticText( self._synchro_panel, style = wx.ALIGN_CENTER | wx.ST_NO_AUTORESIZE )
|
|
|
|
|
|
if service_type in HC.REPOSITORIES + [ HC.LOCAL_FILE, HC.LOCAL_TAG, HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ]:
|
|
|
|
self._info_panel = ClientGUICommon.StaticBox( self, 'service information' )
|
|
|
|
if service_type in ( HC.LOCAL_FILE, HC.FILE_REPOSITORY ):
|
|
|
|
self._files_text = wx.StaticText( self._info_panel, style = wx.ALIGN_CENTER | wx.ST_NO_AUTORESIZE )
|
|
|
|
self._deleted_files_text = wx.StaticText( self._info_panel, style = wx.ALIGN_CENTER | wx.ST_NO_AUTORESIZE )
|
|
|
|
if service_type == HC.FILE_REPOSITORY:
|
|
|
|
self._num_thumbs = 0
|
|
self._num_local_thumbs = 0
|
|
|
|
self._thumbnails = ClientGUICommon.Gauge( self._info_panel )
|
|
|
|
self._thumbnails_text = wx.StaticText( self._info_panel, style = wx.ALIGN_CENTER | wx.ST_NO_AUTORESIZE )
|
|
|
|
|
|
elif service_type in ( HC.LOCAL_TAG, HC.TAG_REPOSITORY ):
|
|
|
|
self._tags_text = wx.StaticText( self._info_panel, style = wx.ALIGN_CENTER | wx.ST_NO_AUTORESIZE )
|
|
|
|
if service_type == HC.TAG_REPOSITORY:
|
|
|
|
self._deleted_tags_text = wx.StaticText( self._info_panel, style = wx.ALIGN_CENTER | wx.ST_NO_AUTORESIZE )
|
|
|
|
|
|
elif service_type in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ):
|
|
|
|
self._ratings_text = wx.StaticText( self._info_panel, style = wx.ALIGN_CENTER | wx.ST_NO_AUTORESIZE )
|
|
|
|
|
|
|
|
if service_type in HC.RESTRICTED_SERVICES:
|
|
|
|
if service_type in HC.REPOSITORIES:
|
|
|
|
self._reset = wx.Button( self, label='reset cache' )
|
|
self._reset.Bind( wx.EVT_BUTTON, self.EventServiceReset )
|
|
|
|
|
|
if service_type == HC.SERVER_ADMIN:
|
|
|
|
self._init = wx.Button( self, label='initialise server' )
|
|
self._init.Bind( wx.EVT_BUTTON, self.EventServerInitialise )
|
|
|
|
|
|
self._refresh = wx.Button( self, label='refresh account' )
|
|
self._refresh.Bind( wx.EVT_BUTTON, self.EventServiceRefreshAccount )
|
|
|
|
|
|
|
|
def PopulateControls():
|
|
|
|
self._DisplayService()
|
|
|
|
|
|
def ArrangeControls():
|
|
|
|
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
if service_type in HC.RESTRICTED_SERVICES:
|
|
|
|
self._permissions_panel.AddF( self._account_type, FLAGS_EXPAND_PERPENDICULAR )
|
|
self._permissions_panel.AddF( self._age, FLAGS_EXPAND_PERPENDICULAR )
|
|
self._permissions_panel.AddF( self._age_text, FLAGS_EXPAND_PERPENDICULAR )
|
|
self._permissions_panel.AddF( self._bytes, FLAGS_EXPAND_PERPENDICULAR )
|
|
self._permissions_panel.AddF( self._bytes_text, FLAGS_EXPAND_PERPENDICULAR )
|
|
self._permissions_panel.AddF( self._requests, FLAGS_EXPAND_PERPENDICULAR )
|
|
self._permissions_panel.AddF( self._requests_text, FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
vbox.AddF( self._permissions_panel, FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
|
|
if service_type in HC.REPOSITORIES:
|
|
|
|
self._synchro_panel.AddF( self._updates, FLAGS_EXPAND_PERPENDICULAR )
|
|
self._synchro_panel.AddF( self._updates_text, FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
vbox.AddF( self._synchro_panel, FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
|
|
if service_type in HC.REPOSITORIES + [ HC.LOCAL_FILE, HC.LOCAL_TAG, HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ]:
|
|
|
|
if service_type in ( HC.LOCAL_FILE, HC.FILE_REPOSITORY ):
|
|
|
|
self._info_panel.AddF( self._files_text, FLAGS_EXPAND_PERPENDICULAR )
|
|
self._info_panel.AddF( self._deleted_files_text, FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
if service_type == HC.FILE_REPOSITORY:
|
|
|
|
self._info_panel.AddF( self._thumbnails, FLAGS_EXPAND_PERPENDICULAR )
|
|
self._info_panel.AddF( self._thumbnails_text, FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
|
|
elif service_type in ( HC.LOCAL_TAG, HC.TAG_REPOSITORY ):
|
|
|
|
self._info_panel.AddF( self._tags_text, FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
if service_type == HC.TAG_REPOSITORY:
|
|
|
|
self._info_panel.AddF( self._deleted_tags_text, FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
|
|
elif service_type in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ):
|
|
|
|
self._info_panel.AddF( self._ratings_text, FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
|
|
vbox.AddF( self._info_panel, FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
|
|
if service_type in HC.RESTRICTED_SERVICES:
|
|
|
|
repo_buttons_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
if service_type in HC.REPOSITORIES: repo_buttons_hbox.AddF( self._reset, FLAGS_MIXED )
|
|
|
|
if service_type == HC.SERVER_ADMIN: repo_buttons_hbox.AddF( self._init, FLAGS_MIXED )
|
|
|
|
repo_buttons_hbox.AddF( self._refresh, FLAGS_MIXED )
|
|
|
|
vbox.AddF( repo_buttons_hbox, FLAGS_BUTTON_SIZERS )
|
|
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
|
|
wx.ScrolledWindow.__init__( self, parent )
|
|
|
|
self.SetScrollRate( 0, 20 )
|
|
|
|
self._service_identifier = service_identifier
|
|
|
|
self._service = HC.app.Read( 'service', self._service_identifier )
|
|
|
|
service_type = service_identifier.GetType()
|
|
|
|
if service_type in HC.RESTRICTED_SERVICES:
|
|
|
|
account = self._service.GetAccount()
|
|
|
|
account_type = account.GetAccountType()
|
|
|
|
expires = account.GetExpires()
|
|
|
|
|
|
InitialiseControls()
|
|
|
|
PopulateControls()
|
|
|
|
ArrangeControls()
|
|
|
|
self._timer_updates = wx.Timer( self, id = ID_TIMER_UPDATES )
|
|
|
|
if service_type in HC.REPOSITORIES:
|
|
|
|
self.Bind( wx.EVT_TIMER, self.EventTimerUpdates, id = ID_TIMER_UPDATES )
|
|
|
|
self._timer_updates.Start( 1000, wx.TIMER_CONTINUOUS )
|
|
|
|
|
|
HC.pubsub.sub( self, 'ProcessServiceUpdates', 'service_updates_gui' )
|
|
HC.pubsub.sub( self, 'AddThumbnailCount', 'add_thumbnail_count' )
|
|
|
|
|
|
def _DisplayAccountInfo( self ):
|
|
|
|
self._service = HC.app.Read( 'service', self._service_identifier )
|
|
|
|
service_type = self._service_identifier.GetType()
|
|
|
|
now = HC.GetNow()
|
|
|
|
if service_type in HC.RESTRICTED_SERVICES:
|
|
|
|
account = self._service.GetAccount()
|
|
|
|
account_type = account.GetAccountType()
|
|
|
|
self._account_type.SetLabel( account_type.ConvertToString() )
|
|
|
|
if service_type in HC.REPOSITORIES:
|
|
|
|
if not account.IsBanned():
|
|
|
|
created = account.GetCreated()
|
|
expires = account.GetExpires()
|
|
|
|
if expires is None: self._age.Hide()
|
|
else:
|
|
|
|
self._age.Show()
|
|
|
|
self._age.SetRange( expires - created )
|
|
self._age.SetValue( min( now - created, expires - created ) )
|
|
|
|
|
|
self._age_text.SetLabel( account.GetExpiresString() )
|
|
|
|
first_begin = self._service.GetFirstBegin()
|
|
next_begin = self._service.GetNextBegin()
|
|
|
|
if first_begin == 0:
|
|
|
|
num_updates = 0
|
|
num_updates_downloaded = 0
|
|
|
|
self._updates.SetValue( 0 )
|
|
|
|
else:
|
|
|
|
num_updates = ( now - first_begin ) / HC.UPDATE_DURATION
|
|
num_updates_downloaded = ( next_begin - first_begin ) / HC.UPDATE_DURATION
|
|
|
|
self._updates.SetRange( num_updates )
|
|
self._updates.SetValue( num_updates_downloaded )
|
|
|
|
|
|
self._updates_text.SetLabel( HC.ConvertIntToPrettyString( num_updates_downloaded ) + '/' + HC.ConvertIntToPrettyString( num_updates ) + ' - ' + self._service.GetUpdateStatus() )
|
|
|
|
( max_num_bytes, max_num_requests ) = account_type.GetMaxMonthlyData()
|
|
( used_bytes, used_requests ) = account.GetUsedData()
|
|
|
|
if max_num_bytes is None: self._bytes.Hide()
|
|
else:
|
|
|
|
self._bytes.Show()
|
|
|
|
self._bytes.SetRange( max_num_bytes )
|
|
self._bytes.SetValue( used_bytes )
|
|
|
|
|
|
self._bytes_text.SetLabel( account.GetUsedBytesString() )
|
|
|
|
if max_num_requests is None: self._requests.Hide()
|
|
else:
|
|
|
|
self._requests.Show()
|
|
|
|
self._requests.SetValue( max_num_requests )
|
|
self._requests.SetValue( min( used_requests, max_num_requests ) )
|
|
|
|
|
|
self._requests_text.SetLabel( account.GetUsedRequestsString() )
|
|
|
|
|
|
|
|
self._refresh.Enable()
|
|
|
|
|
|
|
|
def _DisplayNumThumbs( self ):
|
|
|
|
self._thumbnails.SetRange( self._num_thumbs )
|
|
self._thumbnails.SetValue( min( self._num_local_thumbs, self._num_thumbs ) )
|
|
|
|
self._thumbnails_text.SetLabel( HC.ConvertIntToPrettyString( self._num_local_thumbs ) + '/' + HC.ConvertIntToPrettyString( self._num_thumbs ) + ' thumbnails downloaded' )
|
|
|
|
|
|
def _DisplayService( self ):
|
|
|
|
service_type = self._service_identifier.GetType()
|
|
|
|
self._DisplayAccountInfo()
|
|
|
|
if service_type in HC.REPOSITORIES + [ HC.LOCAL_FILE, HC.LOCAL_TAG, HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ]:
|
|
|
|
service_info = HC.app.Read( 'service_info', self._service_identifier )
|
|
|
|
if service_type in ( HC.LOCAL_FILE, HC.FILE_REPOSITORY ):
|
|
|
|
num_files = service_info[ HC.SERVICE_INFO_NUM_FILES ]
|
|
total_size = service_info[ HC.SERVICE_INFO_TOTAL_SIZE ]
|
|
num_deleted_files = service_info[ HC.SERVICE_INFO_NUM_DELETED_FILES ]
|
|
|
|
self._files_text.SetLabel( HC.ConvertIntToPrettyString( num_files ) + ' files, totalling ' + HC.ConvertIntToBytes( total_size ) )
|
|
|
|
self._deleted_files_text.SetLabel( HC.ConvertIntToPrettyString( num_deleted_files ) + ' deleted files' )
|
|
|
|
if service_type == HC.FILE_REPOSITORY:
|
|
|
|
self._num_thumbs = service_info[ HC.SERVICE_INFO_NUM_THUMBNAILS ]
|
|
self._num_local_thumbs = service_info[ HC.SERVICE_INFO_NUM_THUMBNAILS_LOCAL ]
|
|
|
|
self._DisplayNumThumbs()
|
|
|
|
|
|
elif service_type in ( HC.LOCAL_TAG, HC.TAG_REPOSITORY ):
|
|
|
|
num_files = service_info[ HC.SERVICE_INFO_NUM_FILES ]
|
|
num_namespaces = service_info[ HC.SERVICE_INFO_NUM_NAMESPACES ]
|
|
num_tags = service_info[ HC.SERVICE_INFO_NUM_TAGS ]
|
|
num_mappings = service_info[ HC.SERVICE_INFO_NUM_MAPPINGS ]
|
|
|
|
self._tags_text.SetLabel( HC.ConvertIntToPrettyString( num_files ) + ' hashes, ' + HC.ConvertIntToPrettyString( num_namespaces ) + ' namespaces, ' + HC.ConvertIntToPrettyString( num_tags ) + ' tags, totalling ' + HC.ConvertIntToPrettyString( num_mappings ) + ' mappings' )
|
|
|
|
if service_type == HC.TAG_REPOSITORY:
|
|
|
|
num_deleted_mappings = service_info[ HC.SERVICE_INFO_NUM_DELETED_MAPPINGS ]
|
|
|
|
self._deleted_tags_text.SetLabel( HC.ConvertIntToPrettyString( num_deleted_mappings ) + ' deleted mappings' )
|
|
|
|
|
|
elif service_type in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ):
|
|
|
|
num_ratings = service_info[ HC.SERVICE_INFO_NUM_FILES ]
|
|
|
|
self._ratings_text.SetLabel( HC.u( num_ratings ) + ' files rated' )
|
|
|
|
|
|
|
|
if service_type == HC.SERVER_ADMIN:
|
|
|
|
if self._service.IsInitialised():
|
|
|
|
self._init.Disable()
|
|
self._refresh.Enable()
|
|
|
|
else:
|
|
|
|
self._init.Enable()
|
|
self._refresh.Disable()
|
|
|
|
|
|
|
|
|
|
def AddThumbnailCount( self, service_identifier, count ):
|
|
|
|
if service_identifier == self._service_identifier:
|
|
|
|
self._num_local_thumbs += count
|
|
|
|
self._DisplayNumThumbs()
|
|
|
|
|
|
|
|
def EventServerInitialise( self, event ):
|
|
|
|
service = HC.app.Read( 'service', self._service_identifier )
|
|
|
|
connection = service.GetConnection()
|
|
|
|
connection.Get( 'init' )
|
|
|
|
|
|
def EventServiceRefreshAccount( self, event ):
|
|
|
|
self._refresh.Disable()
|
|
|
|
connection = self._service.GetConnection()
|
|
|
|
connection.Get( 'account' )
|
|
|
|
|
|
def EventServiceReset( self, event ):
|
|
|
|
message = 'This will remove all cached information for ' + self._service_identifier.GetName() + ' from the database. It will take a minute for the database to finish the operation, during which time the gui may freeze.'
|
|
|
|
with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_YES:
|
|
|
|
with wx.BusyCursor(): HC.app.Write( 'reset_service', self._service_identifier )
|
|
|
|
|
|
|
|
|
|
def EventTimerUpdates( self, event ):
|
|
|
|
now = HC.GetNow()
|
|
|
|
first_begin = self._service.GetFirstBegin()
|
|
next_begin = self._service.GetNextBegin()
|
|
|
|
if first_begin == 0:
|
|
|
|
num_updates = 0
|
|
num_updates_downloaded = 0
|
|
|
|
else:
|
|
|
|
num_updates = ( now - first_begin ) / HC.UPDATE_DURATION
|
|
num_updates_downloaded = ( next_begin - first_begin ) / HC.UPDATE_DURATION
|
|
|
|
|
|
self._updates_text.SetLabel( HC.ConvertIntToPrettyString( num_updates_downloaded ) + '/' + HC.ConvertIntToPrettyString( num_updates ) + ' - ' + self._service.GetUpdateStatus() )
|
|
|
|
|
|
def ProcessServiceUpdates( self, service_identifiers_to_service_updates ):
|
|
|
|
for ( service_identifier, service_updates ) in service_identifiers_to_service_updates.items():
|
|
|
|
for service_update in service_updates:
|
|
|
|
if service_identifier == self._service_identifier:
|
|
|
|
( action, row ) = service_update.ToTuple()
|
|
|
|
if action == HC.SERVICE_UPDATE_RESET: self._service_identifier = row
|
|
|
|
if action in ( HC.SERVICE_UPDATE_ACCOUNT, HC.SERVICE_UPDATE_REQUEST_MADE ): wx.CallLater( 600, self._DisplayAccountInfo )
|
|
else:
|
|
wx.CallLater( 200, self._DisplayService )
|
|
wx.CallLater( 400, self.Layout ) # ugly hack, but it works for now
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FrameSplash( ClientGUICommon.Frame ):
|
|
|
|
def __init__( self ):
|
|
|
|
wx.Frame.__init__( self, None, style = wx.FRAME_NO_TASKBAR | wx.FRAME_SHAPED, title = 'hydrus client' )
|
|
|
|
self.SetIcon( wx.Icon( HC.STATIC_DIR + os.path.sep + 'hydrus.ico', wx.BITMAP_TYPE_ICO ) )
|
|
|
|
self._bmp = wx.EmptyBitmap( 154, 220, 32 ) # 32 bit for transparency?
|
|
|
|
self.SetSize( ( 154, 220 ) )
|
|
|
|
self.Center()
|
|
|
|
# this is 124 x 166
|
|
self._hydrus = wx.Image( HC.STATIC_DIR + os.path.sep + 'hydrus_splash.png', type=wx.BITMAP_TYPE_PNG ).ConvertToBitmap()
|
|
|
|
dc = wx.BufferedDC( wx.ClientDC( self ), self._bmp )
|
|
|
|
dc.SetBackground( wx.Brush( wx.WHITE ) )
|
|
|
|
dc.Clear()
|
|
|
|
dc.DrawBitmap( self._hydrus, 15, 15 )
|
|
|
|
self.Bind( wx.EVT_PAINT, self.EventPaint )
|
|
|
|
self.Show( True )
|
|
|
|
self.Bind( wx.EVT_MOUSE_EVENTS, self.OnMouseEvents )
|
|
|
|
|
|
def EventPaint( self, event ): wx.BufferedPaintDC( self, self._bmp )
|
|
|
|
def OnMouseEvents( self, event ): pass
|
|
|
|
def SetText( self, text ):
|
|
|
|
dc = wx.BufferedDC( wx.ClientDC( self ), self._bmp )
|
|
|
|
dc.SetBackground( wx.Brush( wx.WHITE ) )
|
|
|
|
dc.Clear()
|
|
|
|
dc.DrawBitmap( self._hydrus, 15, 15 )
|
|
|
|
dc.SetFont( wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT ) )
|
|
|
|
( width, height ) = dc.GetTextExtent( text )
|
|
|
|
x = ( 154 - width ) / 2
|
|
|
|
dc.DrawText( text, x, 200 )
|
|
|
|
|