1896 lines
76 KiB
Python
1896 lines
76 KiB
Python
![]() |
import HydrusConstants as HC
|
||
|
import ClientConstants as CC
|
||
|
import ClientGUICommon
|
||
|
import ClientGUIDialogs
|
||
|
import ClientGUICanvas
|
||
|
import ClientGUIMixins
|
||
|
import itertools
|
||
|
import os
|
||
|
import random
|
||
|
import threading
|
||
|
import traceback
|
||
|
import wx
|
||
|
|
||
|
# Option Enums
|
||
|
|
||
|
ID_TIMER_WATERFALL = wx.NewId()
|
||
|
ID_TIMER_ANIMATION = 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_BUTTON_SIZERS = wx.SizerFlags( 0 ).Align( wx.ALIGN_RIGHT )
|
||
|
FLAGS_LONE_BUTTON = wx.SizerFlags( 0 ).Border( wx.ALL, 2 ).Align( wx.ALIGN_RIGHT )
|
||
|
|
||
|
FLAGS_MIXED = wx.SizerFlags( 0 ).Border( wx.ALL, 2 ).Align( wx.ALIGN_CENTER_VERTICAL )
|
||
|
|
||
|
def AddFileServiceIdentifiersToMenu( menu, file_service_identifiers, phrase, action ):
|
||
|
|
||
|
if len( file_service_identifiers ) == 1:
|
||
|
|
||
|
( s_i, ) = file_service_identifiers
|
||
|
|
||
|
if action == CC.ID_NULL: id = CC.ID_NULL
|
||
|
else: id = CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( action, s_i )
|
||
|
|
||
|
menu.Append( id, phrase + ' ' + s_i.GetName() )
|
||
|
|
||
|
else:
|
||
|
|
||
|
submenu = wx.Menu()
|
||
|
|
||
|
for s_i in file_service_identifiers:
|
||
|
|
||
|
if action == CC.ID_NULL: id = CC.ID_NULL
|
||
|
else: id = CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( action, s_i )
|
||
|
|
||
|
submenu.Append( id, s_i.GetName() )
|
||
|
|
||
|
|
||
|
menu.AppendMenu( CC.ID_NULL, phrase + u'\u2026', submenu )
|
||
|
|
||
|
|
||
|
class MediaPanel( ClientGUIMixins.ListeningMediaList, wx.ScrolledWindow ):
|
||
|
|
||
|
def __init__( self, parent, page_key, file_service_identifier, predicates, file_query_result ):
|
||
|
|
||
|
wx.ScrolledWindow.__init__( self, parent, size = ( 0, 0 ) )
|
||
|
ClientGUIMixins.ListeningMediaList.__init__( self, file_service_identifier, predicates, file_query_result )
|
||
|
|
||
|
self.SetDoubleBuffered( True )
|
||
|
|
||
|
self._options = wx.GetApp().Read( 'options' )
|
||
|
|
||
|
self.SetScrollRate( 0, 50 )
|
||
|
|
||
|
self._page_key = page_key
|
||
|
|
||
|
self._focussed_media = None
|
||
|
self._shift_focussed_media = None
|
||
|
|
||
|
self._selected_media = set()
|
||
|
|
||
|
HC.pubsub.sub( self, 'AddMediaResult', 'add_media_result' )
|
||
|
HC.pubsub.sub( self, 'SetFocussedMedia', 'set_focus' )
|
||
|
HC.pubsub.sub( self, 'PageHidden', 'page_hidden' )
|
||
|
HC.pubsub.sub( self, 'PageShown', 'page_shown' )
|
||
|
HC.pubsub.sub( self, 'Collect', 'collect_media' )
|
||
|
HC.pubsub.sub( self, 'Sort', 'sort_media' )
|
||
|
HC.pubsub.sub( self, 'FileDumped', 'file_dumped' )
|
||
|
|
||
|
self._PublishSelectionChange()
|
||
|
|
||
|
|
||
|
def _Archive( self ):
|
||
|
|
||
|
hashes = self._GetSelectedHashes( CC.DISCRIMINANT_INBOX )
|
||
|
|
||
|
if len( hashes ) > 0: wx.GetApp().Write( 'content_updates', [ CC.ContentUpdate( CC.CONTENT_UPDATE_ARCHIVE, CC.LOCAL_FILE_SERVICE_IDENTIFIER, hashes ) ] )
|
||
|
|
||
|
|
||
|
def _CopyHashToClipboard( self ):
|
||
|
|
||
|
if wx.TheClipboard.Open():
|
||
|
|
||
|
data = wx.TextDataObject( self._focussed_media.GetDisplayMedia().GetHash().encode( 'hex' ) )
|
||
|
|
||
|
wx.TheClipboard.SetData( data )
|
||
|
|
||
|
wx.TheClipboard.Close()
|
||
|
|
||
|
else: wx.MessageBox( 'I could not get permission to access the clipboard.' )
|
||
|
|
||
|
|
||
|
def _CopyHashesToClipboard( self ):
|
||
|
|
||
|
if wx.TheClipboard.Open():
|
||
|
|
||
|
data = wx.TextDataObject( os.linesep.join( [ hash.encode( 'hex' ) for hash in self._GetSelectedHashes() ] ) )
|
||
|
|
||
|
wx.TheClipboard.SetData( data )
|
||
|
|
||
|
wx.TheClipboard.Close()
|
||
|
|
||
|
else: wx.MessageBox( 'I could not get permission to access the clipboard.' )
|
||
|
|
||
|
|
||
|
def _CopyLocalUrlToClipboard( self ):
|
||
|
|
||
|
if wx.TheClipboard.Open():
|
||
|
|
||
|
data = wx.TextDataObject( 'http://127.0.0.1:45865/file?hash=' + self._focussed_media.GetDisplayMedia().GetHash().encode( 'hex' ) )
|
||
|
|
||
|
wx.TheClipboard.SetData( data )
|
||
|
|
||
|
wx.TheClipboard.Close()
|
||
|
|
||
|
else: wx.MessageBox( 'I could not get permission to access the clipboard.' )
|
||
|
|
||
|
|
||
|
def _CopyPathToClipboard( self ):
|
||
|
|
||
|
if wx.TheClipboard.Open():
|
||
|
|
||
|
data = wx.TextDataObject( HC.CLIENT_FILES_DIR + os.path.sep + self._focussed_media.GetDisplayMedia().GetHash().encode( 'hex' ) )
|
||
|
|
||
|
wx.TheClipboard.SetData( data )
|
||
|
|
||
|
wx.TheClipboard.Close()
|
||
|
|
||
|
else: wx.MessageBox( 'I could not get permission to access the clipboard.' )
|
||
|
|
||
|
|
||
|
def _CustomFilter( self ):
|
||
|
|
||
|
with ClientGUIDialogs.DialogSetupCustomFilterActions( self ) as dlg:
|
||
|
|
||
|
if dlg.ShowModal() == wx.ID_OK:
|
||
|
|
||
|
actions = dlg.GetActions()
|
||
|
|
||
|
media_results = self.GenerateMediaResults( discriminant = CC.DISCRIMINANT_LOCAL, selected_media = set( self._selected_media ) )
|
||
|
|
||
|
if len( media_results ) > 0:
|
||
|
|
||
|
try: ClientGUICanvas.CanvasFullscreenMediaListCustomFilter( self.GetTopLevelParent(), self._page_key, self._file_service_identifier, self._predicates, media_results, actions )
|
||
|
except: wx.MessageBox( traceback.format_exc() )
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
def _Delete( self, file_service_identifier ):
|
||
|
|
||
|
if file_service_identifier.GetType() == HC.LOCAL_FILE:
|
||
|
|
||
|
hashes = self._GetSelectedHashes( CC.DISCRIMINANT_LOCAL )
|
||
|
|
||
|
num_to_delete = len( hashes )
|
||
|
|
||
|
if num_to_delete:
|
||
|
|
||
|
if num_to_delete == 1: message = 'Are you sure you want to delete this file?'
|
||
|
else: message = 'Are you sure you want to delete these ' + HC.ConvertIntToPrettyString( num_to_delete ) + ' files?'
|
||
|
|
||
|
with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
|
||
|
|
||
|
if dlg.ShowModal() == wx.ID_YES:
|
||
|
|
||
|
try: wx.GetApp().Write( 'content_updates', [ CC.ContentUpdate( CC.CONTENT_UPDATE_DELETE, file_service_identifier, hashes ) ] )
|
||
|
except: wx.MessageBox( traceback.format_exc() )
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
else:
|
||
|
|
||
|
hashes = self._GetSelectedHashes()
|
||
|
|
||
|
wx.GetApp().Write( 'petition_files', file_service_identifier, hashes, 'admin' )
|
||
|
|
||
|
|
||
|
|
||
|
def _DeselectAll( self ):
|
||
|
|
||
|
if len( self._selected_media ) > 0:
|
||
|
|
||
|
for m in self._selected_media: m.Deselect()
|
||
|
|
||
|
self._ReblitMedia( self._selected_media )
|
||
|
|
||
|
self._selected_media = set()
|
||
|
|
||
|
self._SetFocussedMedia( None )
|
||
|
self._shift_focussed_media = None
|
||
|
|
||
|
with wx.FrozenWindow( self ): self._ReblitCanvas()
|
||
|
|
||
|
self._PublishSelectionChange()
|
||
|
|
||
|
|
||
|
|
||
|
def _DeselectSelect( self, media_to_deselect, media_to_select ):
|
||
|
|
||
|
if len( media_to_deselect ) > 0:
|
||
|
|
||
|
for m in media_to_deselect: m.Deselect()
|
||
|
|
||
|
self._ReblitMedia( media_to_deselect )
|
||
|
|
||
|
self._selected_media.difference_update( media_to_deselect )
|
||
|
|
||
|
|
||
|
if len( media_to_select ) > 0:
|
||
|
|
||
|
for m in media_to_select: m.Select()
|
||
|
|
||
|
self._ReblitMedia( media_to_select )
|
||
|
|
||
|
self._selected_media.update( media_to_select )
|
||
|
|
||
|
|
||
|
self._PublishSelectionChange()
|
||
|
|
||
|
|
||
|
def _FullScreen( self, first_media = None ):
|
||
|
|
||
|
media_results = self.GenerateMediaResults( discriminant = CC.DISCRIMINANT_LOCAL )
|
||
|
|
||
|
if len( media_results ) > 0:
|
||
|
|
||
|
if first_media is None and self._focussed_media is not None: first_media = self._focussed_media
|
||
|
|
||
|
if first_media is not None and first_media.GetFileServiceIdentifiersCDPP().HasLocal(): first_hash = first_media.GetDisplayMedia().GetHash()
|
||
|
else: first_hash = None
|
||
|
|
||
|
ClientGUICanvas.CanvasFullscreenMediaListBrowser( self.GetTopLevelParent(), self._page_key, self._file_service_identifier, self._predicates, media_results, first_hash )
|
||
|
|
||
|
|
||
|
|
||
|
def _Filter( self ):
|
||
|
|
||
|
media_results = self.GenerateMediaResults( discriminant = CC.DISCRIMINANT_LOCAL, selected_media = set( self._selected_media ) )
|
||
|
|
||
|
if len( media_results ) > 0:
|
||
|
|
||
|
try: ClientGUICanvas.CanvasFullscreenMediaListFilter( self.GetTopLevelParent(), self._page_key, self._file_service_identifier, self._predicates, media_results )
|
||
|
except: wx.MessageBox( traceback.format_exc() )
|
||
|
|
||
|
|
||
|
|
||
|
def _GetHashes( self ): return HC.IntelligentMassUnion( [ media.GetHashes() for media in self._sorted_media ] )
|
||
|
|
||
|
def _GetNumSelected( self ): return sum( [ media.GetNumFiles() for media in self._selected_media ] )
|
||
|
|
||
|
def _GetPrettyStatus( self ):
|
||
|
|
||
|
num_files = sum( [ media.GetNumFiles() for media in self._sorted_media ] )
|
||
|
|
||
|
num_selected = self._GetNumSelected()
|
||
|
|
||
|
pretty_total_size = self._GetPrettyTotalSelectedSize()
|
||
|
|
||
|
if num_selected == 0:
|
||
|
|
||
|
if num_files == 1: return '1 file'
|
||
|
else: return HC.ConvertIntToPrettyString( num_files ) + ' files'
|
||
|
|
||
|
elif num_selected == 1: return '1 of ' + HC.ConvertIntToPrettyString( num_files ) + ' files selected, ' + pretty_total_size
|
||
|
else: return HC.ConvertIntToPrettyString( num_selected ) + ' of ' + HC.ConvertIntToPrettyString( num_files ) + ' files selected, totalling ' + pretty_total_size
|
||
|
|
||
|
|
||
|
def _GetPrettyTotalSelectedSize( self ):
|
||
|
|
||
|
total_size = sum( [ media.GetSize() for media in self._selected_media ] )
|
||
|
|
||
|
unknown_size = False in ( media.IsSizeDefinite() for media in self._selected_media )
|
||
|
|
||
|
if total_size == 0:
|
||
|
|
||
|
if unknown_size: return 'unknown size'
|
||
|
else: return HC.ConvertIntToBytes( 0 )
|
||
|
|
||
|
else:
|
||
|
|
||
|
if unknown_size: return HC.ConvertIntToBytes( total_size ) + ' + some unknown size'
|
||
|
else: return HC.ConvertIntToBytes( total_size )
|
||
|
|
||
|
|
||
|
|
||
|
def _GetSelectedHashes( self, discriminant = None, not_uploaded_to = None ): return HC.IntelligentMassUnion( ( media.GetHashes( discriminant, not_uploaded_to ) for media in self._selected_media ) )
|
||
|
|
||
|
def _GetSimilarTo( self ):
|
||
|
|
||
|
if self._focussed_media is not None:
|
||
|
|
||
|
hash = self._focussed_media.GetDisplayMedia().GetHash()
|
||
|
|
||
|
HC.pubsub.pub( 'new_similar_to', self._file_service_identifier, hash )
|
||
|
|
||
|
|
||
|
|
||
|
def _HitMedia( self, media, ctrl, shift ):
|
||
|
|
||
|
if media is None:
|
||
|
|
||
|
if not ctrl and not shift: self._DeselectAll()
|
||
|
|
||
|
else:
|
||
|
|
||
|
if ctrl:
|
||
|
|
||
|
if media.IsSelected():
|
||
|
|
||
|
self._DeselectSelect( ( media, ), () )
|
||
|
|
||
|
if self._focussed_media == media: self._SetFocussedMedia( None )
|
||
|
|
||
|
else:
|
||
|
|
||
|
self._DeselectSelect( (), ( media, ) )
|
||
|
|
||
|
if self._focussed_media is None: self._SetFocussedMedia( media )
|
||
|
|
||
|
|
||
|
self._shift_focussed_media = None
|
||
|
|
||
|
elif shift and self._focussed_media is not None:
|
||
|
|
||
|
if self._shift_focussed_media is None: self._shift_focussed_media = self._focussed_media
|
||
|
|
||
|
start_index = self._sorted_media_to_indices[ self._shift_focussed_media ]
|
||
|
|
||
|
end_index = self._sorted_media_to_indices[ media ]
|
||
|
|
||
|
if start_index < end_index: media_i_want_selected_at_the_end = set( self._sorted_media[ start_index : end_index + 1 ] )
|
||
|
else: media_i_want_selected_at_the_end = set( self._sorted_media[ end_index : start_index + 1 ] )
|
||
|
|
||
|
self._DeselectSelect( self._selected_media - media_i_want_selected_at_the_end, media_i_want_selected_at_the_end - self._selected_media )
|
||
|
|
||
|
self._SetFocussedMedia( media )
|
||
|
|
||
|
else:
|
||
|
|
||
|
if not media.IsSelected(): self._DeselectSelect( self._selected_media, ( media, ) )
|
||
|
else: self._PublishSelectionChange()
|
||
|
|
||
|
self._SetFocussedMedia( media )
|
||
|
self._shift_focussed_media = None
|
||
|
|
||
|
|
||
|
|
||
|
if self._focussed_media is not None:
|
||
|
|
||
|
( x, y ) = self._GetMediaCoordinates( self._focussed_media )
|
||
|
|
||
|
( start_x, start_y ) = self.GetViewStart()
|
||
|
|
||
|
( x_unit, y_unit ) = self.GetScrollPixelsPerUnit()
|
||
|
|
||
|
( width, height ) = self.GetClientSize()
|
||
|
|
||
|
if y < start_y * y_unit:
|
||
|
|
||
|
y_to_scroll_to = y / y_unit
|
||
|
|
||
|
self.Scroll( -1, y_to_scroll_to )
|
||
|
|
||
|
wx.PostEvent( self, wx.ScrollWinEvent( wx.wxEVT_SCROLLWIN_THUMBRELEASE ) )
|
||
|
|
||
|
elif y > ( start_y * y_unit ) + height:
|
||
|
|
||
|
y_to_scroll_to = ( y - height ) / y_unit
|
||
|
|
||
|
self.Scroll( -1, y_to_scroll_to + 3 )
|
||
|
|
||
|
wx.PostEvent( self, wx.ScrollWinEvent( wx.wxEVT_SCROLLWIN_THUMBRELEASE ) )
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
def _ManageRatings( self ):
|
||
|
|
||
|
if len( self._selected_media ) > 0:
|
||
|
|
||
|
service_identifiers = wx.GetApp().Read( 'service_identifiers', HC.RATINGS_SERVICES )
|
||
|
|
||
|
if len( service_identifiers ) > 0:
|
||
|
|
||
|
try:
|
||
|
|
||
|
with ClientGUIDialogs.DialogManageRatings( None, self._selected_media ) as dlg: dlg.ShowModal()
|
||
|
|
||
|
self.SetFocus()
|
||
|
|
||
|
except: wx.MessageBox( traceback.format_exc() )
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
def _ManageTags( self ):
|
||
|
|
||
|
if len( self._selected_media ) > 0:
|
||
|
|
||
|
try:
|
||
|
|
||
|
with ClientGUIDialogs.DialogManageTags( None, self._file_service_identifier, self._selected_media ) as dlg: dlg.ShowModal()
|
||
|
|
||
|
self.SetFocus()
|
||
|
|
||
|
except: wx.MessageBox( traceback.format_exc() )
|
||
|
|
||
|
|
||
|
|
||
|
def _ModifyUploaders( self, file_service_identifier ):
|
||
|
|
||
|
hashes = self._GetSelectedHashes()
|
||
|
|
||
|
if hashes is not None and len( hashes ) > 0:
|
||
|
|
||
|
with ClientGUIDialogs.DialogModifyAccounts( self, file_service_identifier, [ HC.AccountIdentifier( hash = hash ) for hash in hashes ] ) as dlg: dlg.ShowModal()
|
||
|
|
||
|
self.SetFocus()
|
||
|
|
||
|
|
||
|
|
||
|
def _NewThreadDumper( self ):
|
||
|
|
||
|
# can't do normal _getselectedhashes because we want to keep order!
|
||
|
|
||
|
args = [ media.GetHashes( CC.DISCRIMINANT_LOCAL ) for media in self._sorted_media if media in self._selected_media ]
|
||
|
|
||
|
hashes = [ h for h in itertools.chain( *args ) ]
|
||
|
|
||
|
if len( hashes ) > 0: HC.pubsub.pub( 'new_thread_dumper', hashes )
|
||
|
|
||
|
|
||
|
def _PetitionFiles( self, file_service_identifier ):
|
||
|
|
||
|
hashes = self._GetSelectedHashes()
|
||
|
|
||
|
if hashes is not None and len( hashes ) > 0:
|
||
|
|
||
|
if len( hashes ) == 1: message = 'Enter a reason for this file to be removed from ' + file_service_identifier.GetName() + '.'
|
||
|
else: message = 'Enter a reason for these ' + HC.ConvertIntToPrettyString( len( hashes ) ) + ' files to be removed from ' + file_service_identifier.GetName() + '.'
|
||
|
|
||
|
with wx.TextEntryDialog( self, message ) as dlg:
|
||
|
|
||
|
if dlg.ShowModal() == wx.ID_OK: wx.GetApp().Write( 'petition_files', file_service_identifier, hashes, dlg.GetValue() )
|
||
|
|
||
|
|
||
|
self.SetFocus()
|
||
|
|
||
|
|
||
|
|
||
|
def _PublishSelectionChange( self ):
|
||
|
|
||
|
if len( self._selected_media ) == 0: tags_media = self._sorted_media
|
||
|
else: tags_media = self._selected_media
|
||
|
|
||
|
HC.pubsub.pub( 'new_tags_selection', self._page_key, tags_media )
|
||
|
HC.pubsub.pub( 'new_page_status', self._page_key, self._GetPrettyStatus() )
|
||
|
|
||
|
|
||
|
def _RatingsFilter( self, service_identifier ):
|
||
|
|
||
|
if service_identifier is None:
|
||
|
|
||
|
service_identifier = ClientGUIDialogs.SelectServiceIdentifier( service_types = ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ) )
|
||
|
|
||
|
if service_identifier is None: return
|
||
|
|
||
|
|
||
|
media_results = self.GenerateMediaResults( discriminant = CC.DISCRIMINANT_LOCAL, selected_media = set( self._selected_media ), unrated = service_identifier )
|
||
|
|
||
|
if len( media_results ) > 0:
|
||
|
|
||
|
try: ClientGUICanvas.RatingsFilterFrame( self.GetTopLevelParent(), self._page_key, service_identifier, media_results )
|
||
|
except: wx.MessageBox( traceback.format_exc() )
|
||
|
|
||
|
|
||
|
|
||
|
def _ReblitMedia( self, media ): pass
|
||
|
|
||
|
def _ReblitCanvas( self ): pass
|
||
|
|
||
|
def _RefitCanvas( self ): pass
|
||
|
|
||
|
def _RemoveMedia( self, singleton_media, collected_media ):
|
||
|
|
||
|
ClientGUIMixins.ListeningMediaList._RemoveMedia( self, singleton_media, collected_media )
|
||
|
|
||
|
self._selected_media.difference_update( singleton_media )
|
||
|
self._selected_media.difference_update( collected_media )
|
||
|
|
||
|
if self._focussed_media not in self._selected_media: self._SetFocussedMedia( None )
|
||
|
|
||
|
self._shift_focussed_media = None
|
||
|
|
||
|
with wx.FrozenWindow( self ):
|
||
|
|
||
|
self._RefitCanvas()
|
||
|
|
||
|
self._ReblitCanvas()
|
||
|
|
||
|
|
||
|
self._PublishSelectionChange()
|
||
|
|
||
|
HC.pubsub.pub( 'sorted_media_pulse', self._page_key, self.GenerateMediaResults() )
|
||
|
|
||
|
|
||
|
def _ScrollEnd( self ):
|
||
|
|
||
|
if len( self._sorted_media ) > 0: self._HitMedia( self._sorted_media[ -1 ], False, False )
|
||
|
|
||
|
|
||
|
def _ScrollHome( self ):
|
||
|
|
||
|
if len( self._sorted_media ) > 0: self._HitMedia( self._sorted_media[ 0 ], False, False )
|
||
|
|
||
|
|
||
|
def _SelectAll( self ):
|
||
|
|
||
|
for media in self._sorted_media: media.Select()
|
||
|
|
||
|
self._selected_media = set( self._sorted_media )
|
||
|
|
||
|
with wx.FrozenWindow( self ): self._ReblitCanvas()
|
||
|
|
||
|
self._PublishSelectionChange()
|
||
|
|
||
|
|
||
|
def _SetFocussedMedia( self, media ):
|
||
|
|
||
|
self._focussed_media = media
|
||
|
|
||
|
HC.pubsub.pub( 'focus_changed', self._page_key, media )
|
||
|
|
||
|
|
||
|
def _UploadFiles( self, file_service_identifier ):
|
||
|
|
||
|
hashes = self._GetSelectedHashes( not_uploaded_to = file_service_identifier )
|
||
|
|
||
|
if hashes is not None and len( hashes ) > 0:
|
||
|
|
||
|
try: wx.GetApp().Write( 'add_uploads', file_service_identifier, hashes )
|
||
|
except Exception as e: wx.MessageBox( unicode( e ) )
|
||
|
|
||
|
|
||
|
|
||
|
def AddMediaResult( self, page_key, media_result ):
|
||
|
|
||
|
if page_key == self._page_key: return ClientGUIMixins.ListeningMediaList.AddMediaResult( self, media_result )
|
||
|
|
||
|
|
||
|
def Archive( self, hashes ):
|
||
|
|
||
|
ClientGUIMixins.ListeningMediaList.Archive( self, hashes )
|
||
|
|
||
|
affected_media = self._GetMedia( hashes )
|
||
|
|
||
|
if len( affected_media ) > 0: self._ReblitMedia( affected_media )
|
||
|
|
||
|
self._PublishSelectionChange()
|
||
|
|
||
|
if self._focussed_media is not None: self._HitMedia( self._focussed_media, False, False )
|
||
|
|
||
|
|
||
|
def Collect( self, page_key, collect_by ):
|
||
|
|
||
|
if page_key == self._page_key:
|
||
|
|
||
|
ClientGUIMixins.ListeningMediaList.Collect( self, collect_by )
|
||
|
|
||
|
self._DeselectAll()
|
||
|
|
||
|
with wx.FrozenWindow( self ): self._RefitCanvas()
|
||
|
|
||
|
# no refresh needed since the sort call that always comes after will do it
|
||
|
|
||
|
|
||
|
|
||
|
def FileDumped( self, page_key, hash, status ):
|
||
|
|
||
|
if page_key == self._page_key:
|
||
|
|
||
|
media = self._GetMedia( { hash } )
|
||
|
|
||
|
for m in media: m.Dumped( status )
|
||
|
|
||
|
self._ReblitMedia( media )
|
||
|
|
||
|
|
||
|
|
||
|
def PageHidden( self, page_key ):
|
||
|
|
||
|
if page_key == self._page_key: HC.pubsub.pub( 'focus_changed', self._page_key, None )
|
||
|
|
||
|
|
||
|
def PageShown( self, page_key ):
|
||
|
|
||
|
if page_key == self._page_key:
|
||
|
|
||
|
HC.pubsub.pub( 'focus_changed', self._page_key, self._focussed_media )
|
||
|
|
||
|
self._PublishSelectionChange()
|
||
|
|
||
|
|
||
|
|
||
|
def ProcessContentUpdates( self, content_updates ):
|
||
|
|
||
|
ClientGUIMixins.ListeningMediaList.ProcessContentUpdates( self, content_updates )
|
||
|
|
||
|
for content_update in content_updates:
|
||
|
|
||
|
service_identifier = content_update.GetServiceIdentifier()
|
||
|
|
||
|
service_type = service_identifier.GetType()
|
||
|
|
||
|
hashes = content_update.GetHashes()
|
||
|
|
||
|
affected_media = self._GetMedia( hashes )
|
||
|
|
||
|
action = content_update.GetAction()
|
||
|
|
||
|
if action == CC.CONTENT_UPDATE_DELETE and service_type in ( HC.FILE_REPOSITORY, HC.LOCAL_FILE ) and self._focussed_media in affected_media: self._SetFocussedMedia( None )
|
||
|
|
||
|
if len( affected_media ) > 0: self._ReblitMedia( affected_media )
|
||
|
|
||
|
|
||
|
self._PublishSelectionChange()
|
||
|
|
||
|
if self._focussed_media is not None: self._HitMedia( self._focussed_media, False, False )
|
||
|
|
||
|
|
||
|
def ProcessServiceUpdate( self, update ):
|
||
|
|
||
|
ClientGUIMixins.ListeningMediaList.ProcessServiceUpdate( self, update )
|
||
|
|
||
|
action = update.GetAction()
|
||
|
|
||
|
service_identifier = update.GetServiceIdentifier()
|
||
|
|
||
|
if action in ( CC.SERVICE_UPDATE_DELETE_PENDING, CC.SERVICE_UPDATE_RESET ):
|
||
|
|
||
|
with wx.FrozenWindow( self ):
|
||
|
|
||
|
self._RefitCanvas()
|
||
|
|
||
|
self._ReblitCanvas()
|
||
|
|
||
|
|
||
|
|
||
|
self._PublishSelectionChange()
|
||
|
|
||
|
|
||
|
def SetFocussedMedia( self, page_key, media ):
|
||
|
|
||
|
if page_key == self._page_key:
|
||
|
|
||
|
if media is None: self._SetFocussedMedia( None )
|
||
|
else:
|
||
|
|
||
|
try:
|
||
|
|
||
|
my_media = self._GetMedia( media.GetHashes() )[0]
|
||
|
|
||
|
self._HitMedia( my_media, False, False )
|
||
|
|
||
|
except: pass
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
def Sort( self, page_key, sort_by ):
|
||
|
|
||
|
if page_key == self._page_key:
|
||
|
|
||
|
ClientGUIMixins.ListeningMediaList.Sort( self, sort_by )
|
||
|
|
||
|
with wx.FrozenWindow( self ): self._ReblitCanvas()
|
||
|
|
||
|
|
||
|
HC.pubsub.pub( 'sorted_media_pulse', self._page_key, self.GenerateMediaResults() )
|
||
|
|
||
|
|
||
|
class MediaPanelNoQuery( MediaPanel ):
|
||
|
|
||
|
def __init__( self, parent, page_key, file_service_identifier ): MediaPanel.__init__( self, parent, page_key, file_service_identifier, [], CC.FileQueryResult( file_service_identifier, [], [] ) )
|
||
|
|
||
|
def _GetPrettyStatus( self ): return 'No query'
|
||
|
|
||
|
def GetSortedMedia( self ): return None
|
||
|
|
||
|
class MediaPanelLoading( MediaPanel ):
|
||
|
|
||
|
def __init__( self, parent, page_key, file_service_identifier ): MediaPanel.__init__( self, parent, page_key, file_service_identifier, [], CC.FileQueryResult( file_service_identifier, [], [] ) )
|
||
|
|
||
|
def _GetPrettyStatus( self ): return u'Loading\u2026'
|
||
|
|
||
|
def GetSortedMedia( self ): return None
|
||
|
|
||
|
class MediaPanelThumbnails( MediaPanel ):
|
||
|
|
||
|
def __init__( self, parent, page_key, file_service_identifier, predicates, file_query_result ):
|
||
|
|
||
|
MediaPanel.__init__( self, parent, page_key, file_service_identifier, predicates, file_query_result )
|
||
|
|
||
|
self._num_columns = 1
|
||
|
self._num_rows_in_client_height = 0
|
||
|
self._last_visible_row = 0
|
||
|
|
||
|
self._timer_waterfall = wx.Timer( self, ID_TIMER_WATERFALL )
|
||
|
self._thumbnails_to_waterfall = []
|
||
|
|
||
|
self._timer_animation = wx.Timer( self, ID_TIMER_ANIMATION )
|
||
|
self._thumbnails_being_faded_in = {}
|
||
|
|
||
|
self._drawn_up_to = 0
|
||
|
|
||
|
self._thumbnail_span_dimensions = CC.AddPaddingToDimensions( wx.GetApp().Read( 'options' )[ 'thumbnail_dimensions' ], ( CC.THUMBNAIL_BORDER + CC.THUMBNAIL_MARGIN ) * 2 )
|
||
|
|
||
|
( thumbnail_span_width, thumbnail_span_height ) = self._thumbnail_span_dimensions
|
||
|
|
||
|
self.SetScrollRate( 0, thumbnail_span_height )
|
||
|
|
||
|
self._canvas_bmp = wx.EmptyBitmap( 0, 0 )
|
||
|
|
||
|
self.Bind( wx.EVT_SCROLLWIN, self.EventScroll )
|
||
|
self.Bind( wx.EVT_LEFT_DOWN, self.EventSelection )
|
||
|
self.Bind( wx.EVT_RIGHT_UP, self.EventShowMenu )
|
||
|
self.Bind( wx.EVT_LEFT_DCLICK, self.EventMouseFullScreen )
|
||
|
self.Bind( wx.EVT_MIDDLE_DOWN, self.EventMouseFullScreen )
|
||
|
self.Bind( wx.EVT_PAINT, self.EventPaint )
|
||
|
self.Bind( wx.EVT_SIZE, self.EventResize )
|
||
|
self.Bind( wx.EVT_TIMER, self.EventTimerWaterfall, id = ID_TIMER_WATERFALL )
|
||
|
self.Bind( wx.EVT_TIMER, self.EventTimerAnimation, id = ID_TIMER_ANIMATION )
|
||
|
|
||
|
self.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
|
||
|
|
||
|
self.Bind( wx.EVT_MENU, self.EventMenu )
|
||
|
|
||
|
self.RefreshAcceleratorTable()
|
||
|
|
||
|
HC.pubsub.sub( self, 'NewThumbnails', 'new_thumbnails' )
|
||
|
HC.pubsub.sub( self, 'ThumbnailsResized', 'thumbnail_resize' )
|
||
|
HC.pubsub.sub( self, 'RefreshAcceleratorTable', 'options_updated' )
|
||
|
|
||
|
|
||
|
def _BlitThumbnail( self, thumbnail ):
|
||
|
|
||
|
( x, y ) = self._GetMediaCoordinates( thumbnail )
|
||
|
|
||
|
if ( x, y ) != ( -1, -1 ):
|
||
|
|
||
|
self._thumbnails_being_faded_in[ ( thumbnail.GetBmp(), x, y ) ] = 0.0
|
||
|
|
||
|
if not self._timer_animation.IsRunning(): self._timer_animation.Start( 0, wx.TIMER_ONE_SHOT )
|
||
|
|
||
|
|
||
|
|
||
|
def _CalculateLastVisibleRow( self ):
|
||
|
|
||
|
( x, y ) = self.GetViewStart()
|
||
|
|
||
|
( xUnit, yUnit ) = self.GetScrollPixelsPerUnit()
|
||
|
|
||
|
y_offset = y * yUnit
|
||
|
|
||
|
( my_client_width, my_client_height ) = self.GetClientSize()
|
||
|
|
||
|
y_end = y_offset + my_client_height
|
||
|
|
||
|
( thumbnail_span_width, thumbnail_span_height ) = self._thumbnail_span_dimensions
|
||
|
|
||
|
total_thumbs_to_end = ( y_end / thumbnail_span_height ) + 1
|
||
|
|
||
|
return total_thumbs_to_end
|
||
|
|
||
|
|
||
|
def _ExportFiles( self ):
|
||
|
|
||
|
job_key = os.urandom( 32 )
|
||
|
|
||
|
cancel_event = threading.Event()
|
||
|
|
||
|
with ClientGUIDialogs.DialogProgress( self, job_key, cancel_event ) as dlg:
|
||
|
|
||
|
wx.GetApp().Write( 'export_files', job_key, self._GetSelectedHashes( CC.DISCRIMINANT_LOCAL ), cancel_event )
|
||
|
|
||
|
dlg.ShowModal()
|
||
|
|
||
|
|
||
|
|
||
|
def _GenerateMediaCollection( self, media_results ): return ThumbnailMediaCollection( self._file_service_identifier, self._predicates, media_results )
|
||
|
|
||
|
def _GenerateMediaSingleton( self, media_result ): return ThumbnailMediaSingleton( self._file_service_identifier, media_result )
|
||
|
|
||
|
def _GetMediaCoordinates( self, media ):
|
||
|
|
||
|
try: index = self._sorted_media_to_indices[ media ]
|
||
|
except: return ( -1, -1 )
|
||
|
|
||
|
row = index / self._num_columns
|
||
|
column = index % self._num_columns
|
||
|
|
||
|
( thumbnail_span_width, thumbnail_span_height ) = self._thumbnail_span_dimensions
|
||
|
|
||
|
( x, y ) = ( column * thumbnail_span_width + CC.THUMBNAIL_MARGIN, row * thumbnail_span_height + CC.THUMBNAIL_MARGIN )
|
||
|
|
||
|
return ( x, y )
|
||
|
|
||
|
|
||
|
def _GetScrolledDC( self ):
|
||
|
|
||
|
cdc = wx.ClientDC( self )
|
||
|
|
||
|
self.DoPrepareDC( cdc ) # because this is a scrolled window
|
||
|
|
||
|
return wx.BufferedDC( cdc, self._canvas_bmp )
|
||
|
|
||
|
|
||
|
def _GetThumbnailUnderMouse( self, mouse_event ):
|
||
|
|
||
|
( xUnit, yUnit ) = self.GetScrollPixelsPerUnit()
|
||
|
|
||
|
( x_scroll, y_scroll ) = self.GetViewStart()
|
||
|
|
||
|
y_offset = y_scroll * yUnit
|
||
|
|
||
|
x = mouse_event.GetX()
|
||
|
y = mouse_event.GetY() + y_offset
|
||
|
|
||
|
( t_span_x, t_span_y ) = self._thumbnail_span_dimensions
|
||
|
|
||
|
x_mod = x % t_span_x
|
||
|
y_mod = y % t_span_y
|
||
|
|
||
|
if x_mod <= CC.THUMBNAIL_MARGIN or y_mod <= CC.THUMBNAIL_MARGIN or x_mod > t_span_x - CC.THUMBNAIL_MARGIN or y_mod > t_span_y - CC.THUMBNAIL_MARGIN: return None
|
||
|
|
||
|
column_index = ( x / t_span_x )
|
||
|
row_index = ( y / t_span_y )
|
||
|
|
||
|
if column_index >= self._num_columns: return None
|
||
|
|
||
|
thumbnail_index = self._num_columns * row_index + column_index
|
||
|
|
||
|
if thumbnail_index >= len( self._sorted_media ): return None
|
||
|
|
||
|
return self._sorted_media[ thumbnail_index ]
|
||
|
|
||
|
|
||
|
def _MoveFocussedThumbnail( self, rows, columns, shift ):
|
||
|
|
||
|
if self._focussed_media is not None:
|
||
|
|
||
|
current_position = self._sorted_media_to_indices[ self._focussed_media ]
|
||
|
|
||
|
new_position = current_position + columns + ( self._num_columns * rows )
|
||
|
|
||
|
if new_position < 0: new_position = 0
|
||
|
elif new_position > len( self._sorted_media ) - 1: new_position = len( self._sorted_media ) - 1
|
||
|
|
||
|
self._HitMedia( self._sorted_media[ new_position ], False, shift )
|
||
|
|
||
|
|
||
|
|
||
|
def _ReblitMedia( self, thumbnails ): [ self._BlitThumbnail( t ) for t in thumbnails if t.IsLoaded() ]
|
||
|
|
||
|
def _ReblitCanvas( self ):
|
||
|
|
||
|
( canvas_width, canvas_height ) = self._canvas_bmp.GetSize()
|
||
|
|
||
|
( thumbnail_width, thumbnail_height ) = self._thumbnail_span_dimensions
|
||
|
|
||
|
to_row = canvas_height / thumbnail_height
|
||
|
|
||
|
if to_row > 0:
|
||
|
|
||
|
dc = self._GetScrolledDC()
|
||
|
|
||
|
from_row = 0
|
||
|
|
||
|
num_rows_to_draw = ( to_row - from_row ) + 1 # +1 because caller assumes it is inclusive
|
||
|
|
||
|
( thumbnail_span_width, thumbnail_span_height ) = self._thumbnail_span_dimensions
|
||
|
|
||
|
( my_width, my_height ) = self._canvas_bmp.GetSize()
|
||
|
|
||
|
dc.SetBrush( wx.Brush( wx.WHITE ) )
|
||
|
|
||
|
dc.SetPen( wx.TRANSPARENT_PEN )
|
||
|
|
||
|
begin_white_y = ( from_row ) * ( thumbnail_span_height + CC.THUMBNAIL_MARGIN )
|
||
|
height_white_y = num_rows_to_draw * ( thumbnail_span_height + CC.THUMBNAIL_MARGIN )
|
||
|
|
||
|
dc.DrawRectangle( 0, begin_white_y, my_width, height_white_y ) # this incremental clear is so we don't have to do a potentially _very_ expensive (0.4s or more!) dc.Clear()
|
||
|
|
||
|
thumbnails_to_render_later = []
|
||
|
|
||
|
first_index = from_row * self._num_columns
|
||
|
|
||
|
last_index = first_index + ( num_rows_to_draw * self._num_columns )
|
||
|
|
||
|
for ( sub_index, thumbnail ) in enumerate( self._sorted_media[ first_index : last_index ] ):
|
||
|
|
||
|
current_row = from_row + ( sub_index / self._num_columns )
|
||
|
|
||
|
current_col = sub_index % self._num_columns
|
||
|
|
||
|
if thumbnail.IsLoaded(): dc.DrawBitmap( thumbnail.GetBmp(), current_col * thumbnail_span_width + CC.THUMBNAIL_MARGIN, current_row * thumbnail_span_height + CC.THUMBNAIL_MARGIN )
|
||
|
else: thumbnails_to_render_later.append( thumbnail )
|
||
|
|
||
|
|
||
|
self._last_visible_row = to_row
|
||
|
|
||
|
self._thumbnails_to_waterfall = thumbnails_to_render_later
|
||
|
self._thumbnails_being_faded_in = {}
|
||
|
|
||
|
random.shuffle( self._thumbnails_to_waterfall )
|
||
|
|
||
|
if not self._timer_waterfall.IsRunning(): self._timer_waterfall.Start( 20, wx.TIMER_ONE_SHOT )
|
||
|
|
||
|
|
||
|
|
||
|
def _RefitCanvas( self ):
|
||
|
|
||
|
( client_width, client_height ) = self.GetClientSize()
|
||
|
|
||
|
if client_width > 0 and client_height > 0:
|
||
|
|
||
|
( thumbnail_width, thumbnail_height ) = self._thumbnail_span_dimensions
|
||
|
|
||
|
num_media = len( self._sorted_media )
|
||
|
|
||
|
num_rows = num_media / self._num_columns
|
||
|
|
||
|
if num_media % self._num_columns > 0: num_rows += 1
|
||
|
|
||
|
last_visible_row = min( int( self._CalculateLastVisibleRow() * 1.5 ), num_rows )
|
||
|
|
||
|
canvas_width = client_width
|
||
|
|
||
|
canvas_height = max( last_visible_row * thumbnail_height, client_height )
|
||
|
|
||
|
if ( canvas_width, canvas_height ) != self._canvas_bmp.GetSize(): self._canvas_bmp = wx.EmptyBitmap( canvas_width, canvas_height, 24 )
|
||
|
|
||
|
virtual_width = client_width
|
||
|
|
||
|
virtual_height = max( num_rows * thumbnail_height, client_height )
|
||
|
|
||
|
if ( virtual_width, virtual_height ) != self.GetVirtualSize(): self.SetVirtualSize( ( virtual_width, virtual_height ) )
|
||
|
|
||
|
|
||
|
|
||
|
def AddMediaResult( self, page_key, media_result ):
|
||
|
|
||
|
if page_key == self._page_key:
|
||
|
|
||
|
num_media = len( self._sorted_media )
|
||
|
|
||
|
old_num_rows = num_media / self._num_columns
|
||
|
|
||
|
if num_media % self._num_columns > 0: old_num_rows += 1
|
||
|
|
||
|
media = MediaPanel.AddMediaResult( self, page_key, media_result )
|
||
|
|
||
|
num_media = len( self._sorted_media )
|
||
|
|
||
|
num_rows = num_media / self._num_columns
|
||
|
|
||
|
if num_media % self._num_columns > 0: num_rows += 1
|
||
|
|
||
|
if old_num_rows != num_rows:
|
||
|
|
||
|
with wx.FrozenWindow( self ):
|
||
|
|
||
|
self._RefitCanvas()
|
||
|
|
||
|
self._ReblitCanvas()
|
||
|
|
||
|
|
||
|
|
||
|
if num_rows < self._last_visible_row: self._BlitThumbnail( media )
|
||
|
|
||
|
self._PublishSelectionChange()
|
||
|
|
||
|
|
||
|
|
||
|
def EventKeyDown( self, event ):
|
||
|
|
||
|
# accelerator tables can't handle escape key in windows, gg
|
||
|
|
||
|
if event.GetKeyCode() == wx.WXK_ESCAPE: self._DeselectAll()
|
||
|
else: event.Skip()
|
||
|
|
||
|
|
||
|
def EventMenu( self, event ):
|
||
|
|
||
|
action = CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
|
||
|
|
||
|
if action is not None:
|
||
|
|
||
|
try:
|
||
|
|
||
|
( command, data ) = action
|
||
|
|
||
|
if command == 'archive': self._Archive()
|
||
|
elif command == 'copy_files':
|
||
|
with wx.BusyCursor(): wx.GetApp().Write( 'copy_files', self._GetSelectedHashes( CC.DISCRIMINANT_LOCAL ) )
|
||
|
elif command == 'copy_hash': self._CopyHashToClipboard()
|
||
|
elif command == 'copy_hashes': self._CopyHashesToClipboard()
|
||
|
elif command == 'copy_local_url': self._CopyLocalUrlToClipboard()
|
||
|
elif command == 'copy_path': self._CopyPathToClipboard()
|
||
|
elif command == 'ctrl-space':
|
||
|
|
||
|
if self._focussed_media is not None: self._HitMedia( self._focussed_media, True, False )
|
||
|
|
||
|
elif command == 'custom_filter': self._CustomFilter()
|
||
|
elif command == 'delete': self._Delete( data )
|
||
|
elif command == 'deselect': self._DeselectAll()
|
||
|
elif command == 'download': wx.GetApp().Write( 'add_downloads', data, self._GetSelectedHashes( CC.DISCRIMINANT_NOT_LOCAL ) )
|
||
|
elif command == 'export': self._ExportFiles()
|
||
|
elif command == 'filter': self._Filter()
|
||
|
elif command == 'fullscreen': self._FullScreen()
|
||
|
elif command == 'get_similar_to': self._GetSimilarTo()
|
||
|
elif command == 'manage_ratings': self._ManageRatings()
|
||
|
elif command == 'manage_tags': self._ManageTags()
|
||
|
elif command == 'modify_account': self._ModifyUploaders( data )
|
||
|
elif command == 'new_thread_dumper': self._NewThreadDumper()
|
||
|
elif command == 'petition': self._PetitionFiles( data )
|
||
|
elif command == 'ratings_filter': self._RatingsFilter( data )
|
||
|
elif command == 'scroll_end': self._ScrollEnd()
|
||
|
elif command == 'scroll_home': self._ScrollHome()
|
||
|
elif command == 'select_all': self._SelectAll()
|
||
|
elif command == 'upload': self._UploadFiles( data )
|
||
|
elif command == 'key_up': self._MoveFocussedThumbnail( -1, 0, False )
|
||
|
elif command == 'key_down': self._MoveFocussedThumbnail( 1, 0, False )
|
||
|
elif command == 'key_left': self._MoveFocussedThumbnail( 0, -1, False )
|
||
|
elif command == 'key_right': self._MoveFocussedThumbnail( 0, 1, False )
|
||
|
elif command == 'key_shift_up': self._MoveFocussedThumbnail( -1, 0, True )
|
||
|
elif command == 'key_shift_down': self._MoveFocussedThumbnail( 1, 0, True )
|
||
|
elif command == 'key_shift_left': self._MoveFocussedThumbnail( 0, -1, True )
|
||
|
elif command == 'key_shift_right': self._MoveFocussedThumbnail( 0, 1, True )
|
||
|
else: event.Skip()
|
||
|
|
||
|
except Exception as e:
|
||
|
|
||
|
wx.MessageBox( unicode( e ) )
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
def EventMouseFullScreen( self, event ):
|
||
|
|
||
|
t = self._GetThumbnailUnderMouse( event )
|
||
|
|
||
|
if t is not None:
|
||
|
|
||
|
if t.GetFileServiceIdentifiersCDPP().HasLocal(): self._FullScreen( t )
|
||
|
elif self._file_service_identifier != CC.NULL_SERVICE_IDENTIFIER: wx.GetApp().Write( 'add_downloads', self._file_service_identifier, t.GetHashes() )
|
||
|
|
||
|
|
||
|
|
||
|
def EventPaint( self, event ): wx.BufferedPaintDC( self, self._canvas_bmp, wx.BUFFER_VIRTUAL_AREA )
|
||
|
|
||
|
def EventResize( self, event ):
|
||
|
|
||
|
old_numcols = self._num_columns
|
||
|
old_numclientrows = self._num_rows_in_client_height
|
||
|
|
||
|
( client_width, client_height ) = self.GetClientSize()
|
||
|
|
||
|
( thumbnail_width, thumbnail_height ) = self._thumbnail_span_dimensions
|
||
|
|
||
|
self._num_columns = client_width / thumbnail_width
|
||
|
|
||
|
if self._num_columns == 0: self._num_columns = 1
|
||
|
|
||
|
num_media = len( self._sorted_media )
|
||
|
|
||
|
num_rows = num_media / self._num_columns
|
||
|
|
||
|
if num_media % self._num_columns > 0: num_rows += 1
|
||
|
|
||
|
if self._num_columns != old_numcols:
|
||
|
|
||
|
with wx.FrozenWindow( self ):
|
||
|
|
||
|
self._RefitCanvas()
|
||
|
|
||
|
self._ReblitCanvas()
|
||
|
|
||
|
|
||
|
else:
|
||
|
|
||
|
# the client is the actual window, remember, not the scrollable virtual bmp
|
||
|
|
||
|
self._num_rows_in_client_height = client_height / thumbnail_height
|
||
|
|
||
|
if client_height % thumbnail_height > 0: self._num_rows_in_client_height += 1
|
||
|
|
||
|
if self._num_rows_in_client_height > old_numclientrows:
|
||
|
|
||
|
with wx.FrozenWindow( self ): self._ReblitCanvas()
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
def EventSelection( self, event ):
|
||
|
|
||
|
self._HitMedia( self._GetThumbnailUnderMouse( event ), event.CmdDown(), event.ShiftDown() )
|
||
|
|
||
|
event.Skip()
|
||
|
|
||
|
|
||
|
def EventShowMenu( self, event ):
|
||
|
|
||
|
thumbnail = self._GetThumbnailUnderMouse( event )
|
||
|
|
||
|
menu = wx.Menu()
|
||
|
|
||
|
if thumbnail is None: menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'select_all' ), 'select all' )
|
||
|
else:
|
||
|
|
||
|
self._HitMedia( thumbnail, event.CmdDown(), event.ShiftDown() )
|
||
|
|
||
|
if self._focussed_media is not None:
|
||
|
|
||
|
# variables
|
||
|
|
||
|
num_selected = self._GetNumSelected()
|
||
|
|
||
|
multiple_selected = num_selected > 1
|
||
|
|
||
|
services = wx.GetApp().Read( 'services' )
|
||
|
|
||
|
tag_repositories = [ service for service in services if service.GetServiceIdentifier().GetType() == HC.TAG_REPOSITORY ]
|
||
|
|
||
|
file_repositories = [ service for service in services if service.GetServiceIdentifier().GetType() == HC.FILE_REPOSITORY ]
|
||
|
|
||
|
local_ratings_services = [ service for service in services if service.GetServiceIdentifier().GetType() in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ) ]
|
||
|
|
||
|
i_can_post_ratings = len( local_ratings_services ) > 0
|
||
|
|
||
|
downloadable_file_service_identifiers = { repository.GetServiceIdentifier() for repository in file_repositories if repository.GetAccount().HasPermission( HC.GET_DATA ) }
|
||
|
uploadable_file_service_identifiers = { repository.GetServiceIdentifier() for repository in file_repositories if repository.GetAccount().HasPermission( HC.POST_DATA ) }
|
||
|
petition_resolvable_file_service_identifiers = { repository.GetServiceIdentifier() for repository in file_repositories if repository.GetAccount().HasPermission( HC.RESOLVE_PETITIONS ) }
|
||
|
petitionable_file_service_identifiers = { repository.GetServiceIdentifier() for repository in file_repositories if repository.GetAccount().HasPermission( HC.POST_PETITIONS ) } - petition_resolvable_file_service_identifiers
|
||
|
user_manageable_file_service_identifiers = { repository.GetServiceIdentifier() for repository in file_repositories if repository.GetAccount().HasPermission( HC.MANAGE_USERS ) }
|
||
|
admin_file_service_identifiers = { repository.GetServiceIdentifier() for repository in file_repositories if repository.GetAccount().HasPermission( HC.GENERAL_ADMIN ) }
|
||
|
|
||
|
all_service_identifiers = [ media.GetFileServiceIdentifiersCDPP() for media in self._selected_media ]
|
||
|
|
||
|
selection_has_local = True in ( s_is.HasLocal() for s_is in all_service_identifiers )
|
||
|
selection_has_inbox = True in ( media.HasInbox() for media in self._selected_media )
|
||
|
|
||
|
if multiple_selected:
|
||
|
|
||
|
uploaded_phrase = 'all uploaded to'
|
||
|
pending_phrase = 'all pending to'
|
||
|
petitioned_phrase = 'all petitioned from'
|
||
|
deleted_phrase = 'all deleted from'
|
||
|
|
||
|
download_phrase = 'download all possible from'
|
||
|
upload_phrase = 'upload all possible to'
|
||
|
petition_phrase = 'petition all possible for removal from'
|
||
|
remote_delete_phrase = 'delete all possible from'
|
||
|
modify_account_phrase = 'modify the accounts that uploaded these to'
|
||
|
|
||
|
manage_tags_phrase = 'manage tags for all'
|
||
|
manage_ratings_phrase = 'manage ratings for all'
|
||
|
|
||
|
archive_phrase = 'archive all'
|
||
|
local_delete_phrase = 'delete all'
|
||
|
dump_phrase = 'dump all'
|
||
|
export_phrase = 'export all'
|
||
|
copy_phrase = 'files'
|
||
|
|
||
|
else:
|
||
|
|
||
|
uploaded_phrase = 'uploaded to'
|
||
|
pending_phrase = 'pending to'
|
||
|
petitioned_phrase = 'petitioned from'
|
||
|
deleted_phrase = 'deleted from'
|
||
|
|
||
|
download_phrase = 'download from'
|
||
|
upload_phrase = 'upload to'
|
||
|
petition_phrase = 'petition for removal from'
|
||
|
remote_delete_phrase = 'delete from'
|
||
|
modify_account_phrase = 'modify the account that uploaded this to'
|
||
|
|
||
|
manage_tags_phrase = 'manage tags'
|
||
|
manage_ratings_phrase = 'manage ratings'
|
||
|
|
||
|
archive_phrase = 'archive'
|
||
|
local_delete_phrase = 'delete'
|
||
|
dump_phrase = 'dump'
|
||
|
export_phrase = 'export'
|
||
|
copy_phrase = 'file'
|
||
|
|
||
|
|
||
|
# info about the files
|
||
|
|
||
|
all_current_file_service_identifiers = [ service_identifiers.GetCurrentRemote() for service_identifiers in all_service_identifiers ]
|
||
|
|
||
|
current_file_service_identifiers = HC.IntelligentMassIntersect( all_current_file_service_identifiers )
|
||
|
|
||
|
some_current_file_service_identifiers = HC.IntelligentMassUnion( all_current_file_service_identifiers ) - current_file_service_identifiers
|
||
|
|
||
|
all_pending_file_service_identifiers = [ service_identifiers.GetPendingRemote() for service_identifiers in all_service_identifiers ]
|
||
|
|
||
|
pending_file_service_identifiers = HC.IntelligentMassIntersect( all_pending_file_service_identifiers )
|
||
|
|
||
|
some_pending_file_service_identifiers = HC.IntelligentMassUnion( all_pending_file_service_identifiers ) - pending_file_service_identifiers
|
||
|
|
||
|
all_petitioned_file_service_identifiers = [ service_identifiers.GetPetitionedRemote() for service_identifiers in all_service_identifiers ]
|
||
|
|
||
|
petitioned_file_service_identifiers = HC.IntelligentMassIntersect( all_petitioned_file_service_identifiers )
|
||
|
|
||
|
some_petitioned_file_service_identifiers = HC.IntelligentMassUnion( all_petitioned_file_service_identifiers ) - petitioned_file_service_identifiers
|
||
|
|
||
|
all_deleted_file_service_identifiers = [ service_identifiers.GetDeletedRemote() for service_identifiers in all_service_identifiers ]
|
||
|
|
||
|
deleted_file_service_identifiers = HC.IntelligentMassIntersect( all_deleted_file_service_identifiers )
|
||
|
|
||
|
some_deleted_file_service_identifiers = HC.IntelligentMassUnion( all_deleted_file_service_identifiers ) - deleted_file_service_identifiers
|
||
|
|
||
|
# valid commands for the files
|
||
|
|
||
|
selection_uploadable_file_service_identifiers = set()
|
||
|
|
||
|
for s_is in all_service_identifiers:
|
||
|
|
||
|
# we can upload (set pending) to a repo_id when we have permission, a file is local, not current, not pending, and either ( not deleted or admin )
|
||
|
|
||
|
if s_is.HasLocal(): selection_uploadable_file_service_identifiers.update( uploadable_file_service_identifiers - s_is.GetCurrentRemote() - s_is.GetPendingRemote() - ( s_is.GetDeletedRemote() - admin_file_service_identifiers ) )
|
||
|
|
||
|
|
||
|
selection_downloadable_file_service_identifiers = set()
|
||
|
|
||
|
for s_is in all_service_identifiers:
|
||
|
|
||
|
# we can download (set pending to local) when we have permission, a file is not local and not already downloading and current
|
||
|
|
||
|
if not s_is.HasLocal() and not s_is.HasDownloading(): selection_downloadable_file_service_identifiers.update( downloadable_file_service_identifiers & s_is.GetCurrentRemote() )
|
||
|
|
||
|
|
||
|
selection_petitionable_file_service_identifiers = set()
|
||
|
|
||
|
for s_is in all_service_identifiers:
|
||
|
|
||
|
# we can petition when we have permission and a file is current
|
||
|
# we can re-petition an already petitioned file
|
||
|
|
||
|
selection_petitionable_file_service_identifiers.update( petitionable_file_service_identifiers & s_is.GetCurrentRemote() )
|
||
|
|
||
|
|
||
|
selection_deletable_file_service_identifiers = set()
|
||
|
|
||
|
for s_is in all_service_identifiers:
|
||
|
|
||
|
# we can delete remote when we have permission and a file is current and it is not already petitioned
|
||
|
|
||
|
selection_deletable_file_service_identifiers.update( ( petition_resolvable_file_service_identifiers & s_is.GetCurrentRemote() ) - s_is.GetPetitionedRemote() )
|
||
|
|
||
|
|
||
|
selection_modifyable_file_service_identifiers = set()
|
||
|
|
||
|
for s_is in all_service_identifiers:
|
||
|
|
||
|
# we can modify users when we have permission and the file is current or deleted
|
||
|
|
||
|
selection_modifyable_file_service_identifiers.update( user_manageable_file_service_identifiers & ( s_is.GetCurrentRemote() | s_is.GetDeletedRemote() ) )
|
||
|
|
||
|
|
||
|
# do the actual menu
|
||
|
|
||
|
if multiple_selected: menu.Append( CC.ID_NULL, HC.ConvertIntToPrettyString( num_selected ) + ' files, ' + self._GetPrettyTotalSelectedSize() )
|
||
|
else:
|
||
|
|
||
|
menu.Append( CC.ID_NULL, thumbnail.GetPrettyInfo() )
|
||
|
menu.Append( CC.ID_NULL, thumbnail.GetPrettyAge() )
|
||
|
|
||
|
|
||
|
if len( some_current_file_service_identifiers ) > 0: AddFileServiceIdentifiersToMenu( menu, some_current_file_service_identifiers, 'some uploaded to', CC.ID_NULL )
|
||
|
|
||
|
if len( current_file_service_identifiers ) > 0: AddFileServiceIdentifiersToMenu( menu, current_file_service_identifiers, uploaded_phrase, CC.ID_NULL )
|
||
|
|
||
|
if len( some_pending_file_service_identifiers ) > 0: AddFileServiceIdentifiersToMenu( menu, some_pending_file_service_identifiers, 'some pending to', CC.ID_NULL )
|
||
|
|
||
|
if len( pending_file_service_identifiers ) > 0: AddFileServiceIdentifiersToMenu( menu, pending_file_service_identifiers, pending_phrase, CC.ID_NULL )
|
||
|
|
||
|
if len( some_petitioned_file_service_identifiers ) > 0: AddFileServiceIdentifiersToMenu( menu, some_petitioned_file_service_identifiers, 'some petitioned from', CC.ID_NULL )
|
||
|
|
||
|
if len( petitioned_file_service_identifiers ) > 0: AddFileServiceIdentifiersToMenu( menu, petitioned_file_service_identifiers, petitioned_phrase, CC.ID_NULL )
|
||
|
|
||
|
if len( some_deleted_file_service_identifiers ) > 0: AddFileServiceIdentifiersToMenu( menu, some_deleted_file_service_identifiers, 'some deleted from', CC.ID_NULL )
|
||
|
|
||
|
if len( deleted_file_service_identifiers ) > 0: AddFileServiceIdentifiersToMenu( menu, deleted_file_service_identifiers, deleted_phrase, CC.ID_NULL )
|
||
|
|
||
|
menu.AppendSeparator()
|
||
|
|
||
|
if len( selection_downloadable_file_service_identifiers ) > 0 or len( selection_uploadable_file_service_identifiers ) > 0 or len( selection_petitionable_file_service_identifiers ) > 0 or len( selection_deletable_file_service_identifiers ) > 0 or len( selection_modifyable_file_service_identifiers ) > 0:
|
||
|
|
||
|
if len( selection_downloadable_file_service_identifiers ) > 0: AddFileServiceIdentifiersToMenu( menu, selection_downloadable_file_service_identifiers, download_phrase, 'download' )
|
||
|
|
||
|
if len( selection_uploadable_file_service_identifiers ) > 0: AddFileServiceIdentifiersToMenu( menu, selection_uploadable_file_service_identifiers, upload_phrase, 'upload' )
|
||
|
|
||
|
if len( selection_petitionable_file_service_identifiers ) > 0: AddFileServiceIdentifiersToMenu( menu, selection_petitionable_file_service_identifiers, petition_phrase, 'petition' )
|
||
|
|
||
|
if len( selection_deletable_file_service_identifiers ) > 0: AddFileServiceIdentifiersToMenu( menu, selection_deletable_file_service_identifiers, remote_delete_phrase, 'delete' )
|
||
|
|
||
|
if len( selection_modifyable_file_service_identifiers ) > 0: AddFileServiceIdentifiersToMenu( menu, selection_modifyable_file_service_identifiers, modify_account_phrase, 'modify_account' )
|
||
|
|
||
|
menu.AppendSeparator()
|
||
|
|
||
|
|
||
|
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_tags' ), manage_tags_phrase )
|
||
|
|
||
|
if i_can_post_ratings: menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_ratings' ), manage_ratings_phrase )
|
||
|
|
||
|
menu.AppendSeparator()
|
||
|
|
||
|
if selection_has_local:
|
||
|
|
||
|
if multiple_selected: menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'filter' ), 'filter' )
|
||
|
|
||
|
if i_can_post_ratings:
|
||
|
|
||
|
ratings_filter_menu = wx.Menu()
|
||
|
|
||
|
for service in local_ratings_services: ratings_filter_menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'ratings_filter', service.GetServiceIdentifier() ), service.GetServiceIdentifier().GetName() )
|
||
|
|
||
|
menu.AppendMenu( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'ratings_filter' ), 'ratings filter', ratings_filter_menu )
|
||
|
|
||
|
|
||
|
if multiple_selected: menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'custom_filter' ), 'custom filter' )
|
||
|
|
||
|
if multiple_selected or i_can_post_ratings: menu.AppendSeparator()
|
||
|
|
||
|
if selection_has_inbox: menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'archive' ), archive_phrase )
|
||
|
|
||
|
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete', CC.LOCAL_FILE_SERVICE_IDENTIFIER ), local_delete_phrase )
|
||
|
|
||
|
|
||
|
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'export' ), export_phrase )
|
||
|
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'new_thread_dumper' ), dump_phrase )
|
||
|
|
||
|
menu.AppendSeparator()
|
||
|
|
||
|
copy_menu = wx.Menu()
|
||
|
|
||
|
copy_menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_files' ), copy_phrase )
|
||
|
copy_menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_hash' ) , 'hash' )
|
||
|
if multiple_selected: copy_menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_hashes' ) , 'hashes' )
|
||
|
copy_menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_path' ) , 'path' )
|
||
|
copy_menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_local_url' ) , 'local url' )
|
||
|
|
||
|
menu.AppendMenu( CC.ID_NULL, 'copy', copy_menu )
|
||
|
|
||
|
if self._focussed_media.HasImages():
|
||
|
|
||
|
menu.AppendSeparator()
|
||
|
|
||
|
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'get_similar_to' ) , 'find very similar images' )
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
self.PopupMenu( menu )
|
||
|
|
||
|
menu.Destroy()
|
||
|
|
||
|
event.Skip()
|
||
|
|
||
|
|
||
|
def EventScroll( self, event ):
|
||
|
|
||
|
num_media = len( self._sorted_media )
|
||
|
|
||
|
num_rows = num_media / self._num_columns
|
||
|
|
||
|
if num_media % self._num_columns > 0: num_rows += 1
|
||
|
|
||
|
current_last_visible_row = min( int( self._CalculateLastVisibleRow() * 1.25 ), num_rows )
|
||
|
|
||
|
if current_last_visible_row > num_rows: current_last_visible_row = num_rows
|
||
|
|
||
|
if current_last_visible_row > self._last_visible_row:
|
||
|
|
||
|
with wx.FrozenWindow( self ):
|
||
|
|
||
|
self._RefitCanvas()
|
||
|
|
||
|
self._ReblitCanvas()
|
||
|
|
||
|
|
||
|
|
||
|
event.Skip()
|
||
|
|
||
|
|
||
|
def EventTimerAnimation( self, event ):
|
||
|
|
||
|
dc = self._GetScrolledDC()
|
||
|
|
||
|
all_info = self._thumbnails_being_faded_in.items()
|
||
|
|
||
|
for ( key, current_alpha ) in all_info:
|
||
|
|
||
|
( bmp, x, y ) = key
|
||
|
|
||
|
current_alpha += 0.25
|
||
|
|
||
|
if current_alpha < 1.0:
|
||
|
|
||
|
image = bmp.ConvertToImage()
|
||
|
|
||
|
image.InitAlpha()
|
||
|
|
||
|
image = image.AdjustChannels( 1, 1, 1, current_alpha )
|
||
|
|
||
|
bmp_to_use = wx.BitmapFromImage( image, 32 )
|
||
|
|
||
|
self._thumbnails_being_faded_in[ key ] = current_alpha
|
||
|
|
||
|
else:
|
||
|
|
||
|
bmp_to_use = bmp
|
||
|
|
||
|
del self._thumbnails_being_faded_in[ key ]
|
||
|
|
||
|
|
||
|
dc.DrawBitmap( bmp_to_use, x, y, True )
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
if len( self._thumbnails_being_faded_in ) > 0: self._timer_animation.Start( 32, wx.TIMER_ONE_SHOT )
|
||
|
|
||
|
|
||
|
def EventTimerWaterfall( self, event ):
|
||
|
|
||
|
how_many = random.randint( 6, 12 )
|
||
|
|
||
|
for thumbnail in self._thumbnails_to_waterfall[-how_many:]: self._BlitThumbnail( thumbnail )
|
||
|
|
||
|
self._thumbnails_to_waterfall = self._thumbnails_to_waterfall[:-how_many]
|
||
|
|
||
|
if len( self._thumbnails_to_waterfall ) > 0: self._timer_waterfall.Start( 5, wx.TIMER_ONE_SHOT )
|
||
|
|
||
|
|
||
|
def NewThumbnails( self, hashes ):
|
||
|
|
||
|
affected_thumbnails = self._GetMedia( hashes )
|
||
|
|
||
|
if len( affected_thumbnails ) > 0:
|
||
|
|
||
|
for t in affected_thumbnails: t.ReloadFromDB()
|
||
|
|
||
|
self._ReblitMedia( affected_thumbnails )
|
||
|
|
||
|
|
||
|
|
||
|
def RefreshAcceleratorTable( self ):
|
||
|
|
||
|
entries = [
|
||
|
( wx.ACCEL_NORMAL, wx.WXK_HOME, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'scroll_home' ) ),
|
||
|
( wx.ACCEL_NORMAL, wx.WXK_NUMPAD_HOME, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'scroll_home' ) ),
|
||
|
( wx.ACCEL_NORMAL, wx.WXK_END, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'scroll_end' ) ),
|
||
|
( wx.ACCEL_NORMAL, wx.WXK_NUMPAD_END, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'scroll_end' ) ),
|
||
|
( wx.ACCEL_NORMAL, wx.WXK_DELETE, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete', CC.LOCAL_FILE_SERVICE_IDENTIFIER ) ),
|
||
|
( wx.ACCEL_NORMAL, wx.WXK_NUMPAD_DELETE, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete', CC.LOCAL_FILE_SERVICE_IDENTIFIER ) ),
|
||
|
( wx.ACCEL_NORMAL, wx.WXK_RETURN, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'fullscreen' ) ),
|
||
|
( wx.ACCEL_NORMAL, wx.WXK_NUMPAD_ENTER, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'fullscreen' ) ),
|
||
|
( wx.ACCEL_NORMAL, wx.WXK_UP, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'key_up' ) ),
|
||
|
( wx.ACCEL_NORMAL, wx.WXK_NUMPAD_UP, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'key_up' ) ),
|
||
|
( wx.ACCEL_NORMAL, wx.WXK_DOWN, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'key_down' ) ),
|
||
|
( wx.ACCEL_NORMAL, wx.WXK_NUMPAD_DOWN, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'key_down' ) ),
|
||
|
( wx.ACCEL_NORMAL, wx.WXK_LEFT, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'key_left' ) ),
|
||
|
( wx.ACCEL_NORMAL, wx.WXK_NUMPAD_LEFT, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'key_left' ) ),
|
||
|
( wx.ACCEL_NORMAL, wx.WXK_RIGHT, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'key_right' ) ),
|
||
|
( wx.ACCEL_NORMAL, wx.WXK_NUMPAD_RIGHT, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'key_right' ) ),
|
||
|
( wx.ACCEL_SHIFT, wx.WXK_UP, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'key_shift_up' ) ),
|
||
|
( wx.ACCEL_SHIFT, wx.WXK_NUMPAD_UP, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'key_shift_up' ) ),
|
||
|
( wx.ACCEL_SHIFT, wx.WXK_DOWN, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'key_shift_down' ) ),
|
||
|
( wx.ACCEL_SHIFT, wx.WXK_NUMPAD_DOWN, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'key_shift_down' ) ),
|
||
|
( wx.ACCEL_SHIFT, wx.WXK_LEFT, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'key_shift_left' ) ),
|
||
|
( wx.ACCEL_SHIFT, wx.WXK_NUMPAD_LEFT, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'key_shift_left' ) ),
|
||
|
( wx.ACCEL_SHIFT, wx.WXK_RIGHT, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'key_shift_right' ) ),
|
||
|
( wx.ACCEL_SHIFT, wx.WXK_NUMPAD_RIGHT, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'key_shift_right' ) ),
|
||
|
( wx.ACCEL_CMD, ord( 'A' ), CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'select_all' ) ),
|
||
|
( wx.ACCEL_CTRL, ord( 'c' ), CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_files' ) ),
|
||
|
( wx.ACCEL_CTRL, wx.WXK_SPACE, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'ctrl-space' ) )
|
||
|
]
|
||
|
|
||
|
for ( modifier, key_dict ) in self._options[ 'shortcuts' ].items(): entries.extend( [ ( modifier, key, CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( action ) ) for ( key, action ) in key_dict.items() ] )
|
||
|
|
||
|
self.SetAcceleratorTable( wx.AcceleratorTable( entries ) )
|
||
|
|
||
|
|
||
|
def Sort( self, page_key, sort_by ):
|
||
|
|
||
|
MediaPanel.Sort( self, page_key, sort_by )
|
||
|
|
||
|
for thumbnail in self._collected_media:
|
||
|
|
||
|
thumbnail.ReloadFromDB()
|
||
|
|
||
|
|
||
|
self._ReblitMedia( self._collected_media )
|
||
|
|
||
|
|
||
|
def ThumbnailsResized( self ):
|
||
|
|
||
|
self._thumbnail_span_dimensions = CC.AddPaddingToDimensions( wx.GetApp().Read( 'options' )[ 'thumbnail_dimensions' ], ( CC.THUMBNAIL_BORDER + CC.THUMBNAIL_MARGIN ) * 2 )
|
||
|
|
||
|
( thumbnail_span_width, thumbnail_span_height ) = self._thumbnail_span_dimensions
|
||
|
|
||
|
( client_width, client_height ) = self.GetClientSize()
|
||
|
|
||
|
self._num_columns = client_width / thumbnail_span_width
|
||
|
|
||
|
if self._num_columns == 0: self._num_columns = 1
|
||
|
|
||
|
self.SetScrollRate( 0, thumbnail_span_height )
|
||
|
|
||
|
for t in self._sorted_media: t.ReloadFromDBLater()
|
||
|
|
||
|
with wx.FrozenWindow( self ):
|
||
|
|
||
|
self._RefitCanvas()
|
||
|
|
||
|
self._ReblitCanvas()
|
||
|
|
||
|
|
||
|
|
||
|
class Selectable():
|
||
|
|
||
|
def __init__( self ): self._selected = False
|
||
|
|
||
|
def Deselect( self ): self._selected = False
|
||
|
|
||
|
def IsLoaded( self ): return False
|
||
|
|
||
|
def IsSelected( self ): return self._selected
|
||
|
|
||
|
def Select( self ): self._selected = True
|
||
|
|
||
|
# keep this around, just for reference
|
||
|
class ThumbGridSizer( wx.PySizer ):
|
||
|
|
||
|
def __init__( self, parent_container ):
|
||
|
|
||
|
wx.PySizer.__init__( self )
|
||
|
|
||
|
self._parent_container = parent_container
|
||
|
|
||
|
self._thumbnails = []
|
||
|
|
||
|
self._options = wx.GetApp().Read( 'options' )
|
||
|
|
||
|
|
||
|
def _GetThumbnailDimensions( self ): return CC.AddPaddingToDimensions( self._options[ 'thumbnail_dimensions' ], ( CC.THUMBNAIL_MARGIN + CC.THUMBNAIL_BORDER ) * 2 )
|
||
|
|
||
|
def AddThumbnail( self, thumbnail ): self._thumbnails.append( thumbnail )
|
||
|
|
||
|
def CalcMin( self ):
|
||
|
|
||
|
( width, height ) = self._parent_container.GetClientSize()
|
||
|
|
||
|
( thumbnail_width, thumbnail_height ) = self._GetThumbnailDimensions()
|
||
|
|
||
|
self._num_columns = width / thumbnail_width
|
||
|
|
||
|
if self._num_columns == 0: self._num_columns = 1
|
||
|
|
||
|
num_items = len( self._parent_container )
|
||
|
|
||
|
my_min_height = num_items / self._num_columns
|
||
|
|
||
|
if num_items % self._num_columns > 0: my_min_height += 1
|
||
|
|
||
|
my_min_height *= thumbnail_height
|
||
|
|
||
|
return wx.Size( width, my_min_height )
|
||
|
|
||
|
|
||
|
def RecalcSizes( self ):
|
||
|
|
||
|
w = self.GetContainingWindow()
|
||
|
|
||
|
( xUnit, yUnit ) = w.GetScrollPixelsPerUnit()
|
||
|
|
||
|
( x, y ) = w.GetViewStart()
|
||
|
|
||
|
y_offset = y * yUnit
|
||
|
|
||
|
( thumbnail_width, thumbnail_height ) = self._GetThumbnailDimensions()
|
||
|
|
||
|
for ( index, thumbnail ) in enumerate( self.GetChildren() ):
|
||
|
|
||
|
current_col = index % self._num_columns
|
||
|
current_row = index / self._num_columns
|
||
|
|
||
|
thumbnail.SetDimension( ( current_col * thumbnail_width, current_row * thumbnail_height - y_offset ), ( thumbnail_width, thumbnail_height ) )
|
||
|
|
||
|
|
||
|
|
||
|
class Thumbnail( Selectable ):
|
||
|
|
||
|
def __init__( self, file_service_identifier ):
|
||
|
|
||
|
Selectable.__init__( self )
|
||
|
|
||
|
self._dump_status = CC.DUMPER_NOT_DUMPED
|
||
|
self._hydrus_bmp = None
|
||
|
self._file_service_identifier = file_service_identifier
|
||
|
|
||
|
self._my_dimensions = CC.AddPaddingToDimensions( wx.GetApp().Read( 'options' )[ 'thumbnail_dimensions' ], CC.THUMBNAIL_BORDER * 2 )
|
||
|
|
||
|
|
||
|
def _LoadFromDB( self ):
|
||
|
|
||
|
display_hash = self.GetDisplayMedia().GetHash()
|
||
|
|
||
|
mime = self.GetDisplayMedia().GetMime()
|
||
|
|
||
|
if mime in HC.IMAGES:
|
||
|
|
||
|
my_file_service_identifiers = self.GetFileServiceIdentifiersCDPP().GetCurrent()
|
||
|
|
||
|
if CC.LOCAL_FILE_SERVICE_IDENTIFIER in my_file_service_identifiers: thumbnail_file_service_identifier = CC.LOCAL_FILE_SERVICE_IDENTIFIER
|
||
|
elif len( my_file_service_identifiers ) > 0: thumbnail_file_service_identifier = list( my_file_service_identifiers )[0]
|
||
|
else: thumbnail_file_service_identifier = self._file_service_identifier
|
||
|
|
||
|
self._hydrus_bmp = wx.GetApp().GetThumbnailCache().GetThumbnail( thumbnail_file_service_identifier, display_hash )
|
||
|
|
||
|
elif mime == HC.APPLICATION_FLASH: self._hydrus_bmp = wx.GetApp().GetThumbnailCache().GetFlashThumbnail()
|
||
|
elif mime == HC.VIDEO_FLV: self._hydrus_bmp = wx.GetApp().GetThumbnailCache().GetFLVThumbnail()
|
||
|
else: self._hydrus_bmp = wx.GetApp().GetThumbnailCache().GetNotFoundThumbnail()
|
||
|
|
||
|
|
||
|
def GetBmp( self ):
|
||
|
|
||
|
inbox = self.HasInbox()
|
||
|
|
||
|
local = self.GetFileServiceIdentifiersCDPP().HasLocal()
|
||
|
|
||
|
( creators, series, titles, volumes, chapters, pages ) = self.GetTags().GetCSTVCP()
|
||
|
|
||
|
if self._hydrus_bmp is None: self._LoadFromDB()
|
||
|
|
||
|
( width, height ) = self._my_dimensions
|
||
|
|
||
|
bmp = wx.EmptyBitmap( width, height, 24 )
|
||
|
|
||
|
dc = wx.MemoryDC( bmp )
|
||
|
|
||
|
if not local:
|
||
|
|
||
|
if self._selected: dc.SetBackground( wx.Brush( wx.Colour( 64, 64, 72 ) ) ) # Payne's Gray
|
||
|
else: dc.SetBackground( wx.Brush( wx.Colour( 32, 32, 36 ) ) ) # 50% Payne's Gray
|
||
|
|
||
|
else:
|
||
|
|
||
|
if self._selected: dc.SetBackground( wx.Brush( CC.COLOUR_SELECTED ) )
|
||
|
else: dc.SetBackground( wx.Brush( wx.WHITE ) )
|
||
|
|
||
|
|
||
|
dc.Clear()
|
||
|
|
||
|
( thumb_width, thumb_height ) = self._hydrus_bmp.GetSize()
|
||
|
|
||
|
x_offset = ( width - thumb_width ) / 2
|
||
|
|
||
|
y_offset = ( height - thumb_height ) / 2
|
||
|
|
||
|
hydrus_bmp = self._hydrus_bmp.CreateWxBmp()
|
||
|
|
||
|
dc.DrawBitmap( hydrus_bmp, x_offset, y_offset )
|
||
|
|
||
|
hydrus_bmp.Destroy()
|
||
|
|
||
|
collections_string = ''
|
||
|
|
||
|
if len( volumes ) > 0:
|
||
|
|
||
|
if len( volumes ) == 1:
|
||
|
|
||
|
( volume, ) = volumes
|
||
|
|
||
|
collections_string = 'v' + str( volume )
|
||
|
|
||
|
else: collections_string = 'v' + str( min( volumes ) ) + '-' + str( max( volumes ) )
|
||
|
|
||
|
|
||
|
if len( chapters ) > 0:
|
||
|
|
||
|
if len( chapters ) == 1:
|
||
|
|
||
|
( chapter, ) = chapters
|
||
|
|
||
|
collections_string_append = 'c' + str( chapter )
|
||
|
|
||
|
else: collections_string_append = 'c' + str( min( chapters ) ) + '-' + str( max( chapters ) )
|
||
|
|
||
|
if len( collections_string ) > 0: collections_string += '-' + collections_string_append
|
||
|
else: collections_string = collections_string_append
|
||
|
|
||
|
|
||
|
if len( pages ) > 0:
|
||
|
|
||
|
if len( pages ) == 1:
|
||
|
|
||
|
( page, ) = pages
|
||
|
|
||
|
collections_string_append = 'p' + str( page )
|
||
|
|
||
|
else: collections_string_append = 'p' + str( min( pages ) ) + '-' + str( max( pages ) )
|
||
|
|
||
|
if len( collections_string ) > 0: collections_string += '-' + collections_string_append
|
||
|
else: collections_string = collections_string_append
|
||
|
|
||
|
|
||
|
if len( collections_string ) > 0:
|
||
|
|
||
|
dc.SetFont( wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT ) )
|
||
|
|
||
|
( text_x, text_y ) = dc.GetTextExtent( collections_string )
|
||
|
|
||
|
top_left_x = width - text_x - CC.THUMBNAIL_BORDER
|
||
|
top_left_y = height - text_y - CC.THUMBNAIL_BORDER
|
||
|
|
||
|
dc.SetBrush( wx.Brush( CC.COLOUR_UNSELECTED ) )
|
||
|
|
||
|
dc.SetTextForeground( CC.COLOUR_SELECTED_DARK )
|
||
|
|
||
|
dc.SetPen( wx.TRANSPARENT_PEN )
|
||
|
|
||
|
dc.DrawRectangle( top_left_x - 1, top_left_y - 1, text_x + 2, text_y + 2 )
|
||
|
|
||
|
dc.DrawText( collections_string, top_left_x, top_left_y )
|
||
|
|
||
|
|
||
|
if len( creators ) > 0: upper_info_string = ', '.join( creators )
|
||
|
elif len( series ) > 0: upper_info_string = ', '.join( series )
|
||
|
elif len( titles ) > 0: upper_info_string = ', '.join( titles )
|
||
|
else: upper_info_string = ''
|
||
|
|
||
|
if len( upper_info_string ) > 0:
|
||
|
|
||
|
dc.SetFont( wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT ) )
|
||
|
|
||
|
( text_x, text_y ) = dc.GetTextExtent( upper_info_string )
|
||
|
|
||
|
top_left_x = int( ( width - text_x ) / 2 )
|
||
|
top_left_y = CC.THUMBNAIL_BORDER
|
||
|
|
||
|
dc.SetBrush( wx.Brush( CC.COLOUR_UNSELECTED ) )
|
||
|
|
||
|
dc.SetTextForeground( CC.COLOUR_SELECTED_DARK )
|
||
|
|
||
|
dc.SetPen( wx.TRANSPARENT_PEN )
|
||
|
|
||
|
dc.DrawRectangle( 0, top_left_y - 1, width, text_y + 2 )
|
||
|
|
||
|
dc.DrawText( upper_info_string, top_left_x, top_left_y )
|
||
|
|
||
|
|
||
|
dc.SetBrush( wx.TRANSPARENT_BRUSH )
|
||
|
|
||
|
if not local:
|
||
|
|
||
|
if self._selected: colour = wx.Colour( 227, 66, 52 ) # Vermillion, lol
|
||
|
else: colour = wx.Colour( 248, 208, 204 ) # 25% Vermillion, 75% White
|
||
|
|
||
|
else:
|
||
|
|
||
|
if self._selected: colour = CC.COLOUR_SELECTED_DARK
|
||
|
else: colour = CC.COLOUR_UNSELECTED
|
||
|
|
||
|
|
||
|
dc.SetPen( wx.Pen( colour, style=wx.SOLID ) )
|
||
|
|
||
|
dc.DrawRectangle( 0, 0, width, height )
|
||
|
|
||
|
file_service_identifiers = self.GetFileServiceIdentifiersCDPP()
|
||
|
|
||
|
if inbox: dc.DrawBitmap( CC.GlobalBMPs.inbox_bmp, width - 18, 0 )
|
||
|
elif CC.LOCAL_FILE_SERVICE_IDENTIFIER in file_service_identifiers.GetPending(): dc.DrawBitmap( CC.GlobalBMPs.downloading_bmp, width - 18, 0 )
|
||
|
|
||
|
if self._dump_status == CC.DUMPER_DUMPED_OK: dc.DrawBitmap( CC.GlobalBMPs.dump_ok, width - 18, 18 )
|
||
|
elif self._dump_status == CC.DUMPER_RECOVERABLE_ERROR: dc.DrawBitmap( CC.GlobalBMPs.dump_recoverable, width - 18, 18 )
|
||
|
elif self._dump_status == CC.DUMPER_UNRECOVERABLE_ERROR: dc.DrawBitmap( CC.GlobalBMPs.dump_fail, width - 18, 18 )
|
||
|
|
||
|
if self.IsCollection():
|
||
|
|
||
|
dc.DrawBitmap( CC.GlobalBMPs.collection_bmp, 1, height - 17 )
|
||
|
|
||
|
num_files_str = str( len( self._hashes ) )
|
||
|
|
||
|
dc.SetFont( wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT ) )
|
||
|
|
||
|
( text_x, text_y ) = dc.GetTextExtent( num_files_str )
|
||
|
|
||
|
dc.SetBrush( wx.Brush( CC.COLOUR_UNSELECTED ) )
|
||
|
|
||
|
dc.SetTextForeground( CC.COLOUR_SELECTED_DARK )
|
||
|
|
||
|
dc.SetPen( wx.TRANSPARENT_PEN )
|
||
|
|
||
|
dc.DrawRectangle( 17, height - text_y - 3, text_x + 2, text_y + 2 )
|
||
|
|
||
|
dc.DrawText( num_files_str, 18, height - text_y - 2 )
|
||
|
|
||
|
|
||
|
if self._file_service_identifier.GetType() == HC.LOCAL_FILE:
|
||
|
|
||
|
if len( file_service_identifiers.GetPendingRemote() ) > 0: dc.DrawBitmap( CC.GlobalBMPs.file_repository_pending_bmp, 0, 0 )
|
||
|
elif len( file_service_identifiers.GetCurrentRemote() ) > 0: dc.DrawBitmap( CC.GlobalBMPs.file_repository_bmp, 0, 0 )
|
||
|
|
||
|
elif self._file_service_identifier in file_service_identifiers.GetCurrentRemote():
|
||
|
|
||
|
if self._file_service_identifier in file_service_identifiers.GetPetitionedRemote(): dc.DrawBitmap( CC.GlobalBMPs.file_repository_petitioned_bmp, 0, 0 )
|
||
|
|
||
|
|
||
|
return bmp
|
||
|
|
||
|
|
||
|
def Dumped( self, dump_status ): self._dump_status = dump_status
|
||
|
|
||
|
def IsLoaded( self ): return self._hydrus_bmp is not None
|
||
|
|
||
|
def ReloadFromDB( self ):
|
||
|
|
||
|
self._my_dimensions = CC.AddPaddingToDimensions( wx.GetApp().Read( 'options' )[ 'thumbnail_dimensions' ], CC.THUMBNAIL_BORDER * 2 )
|
||
|
|
||
|
if self._hydrus_bmp is not None: self._LoadFromDB()
|
||
|
|
||
|
|
||
|
def ReloadFromDBLater( self ):
|
||
|
|
||
|
self._my_dimensions = CC.AddPaddingToDimensions( wx.GetApp().Read( 'options' )[ 'thumbnail_dimensions' ], CC.THUMBNAIL_BORDER * 2 )
|
||
|
|
||
|
self._hydrus_bmp = None
|
||
|
|
||
|
|
||
|
class ThumbnailMediaCollection( Thumbnail, ClientGUIMixins.MediaCollection ):
|
||
|
|
||
|
def __init__( self, file_service_identifier, predicates, media_results ):
|
||
|
|
||
|
ClientGUIMixins.MediaCollection.__init__( self, file_service_identifier, predicates, media_results )
|
||
|
Thumbnail.__init__( self, file_service_identifier )
|
||
|
|
||
|
|
||
|
def ProcessContentUpdate( self, content_update ):
|
||
|
|
||
|
ClientGUIMixins.MediaCollection.ProcessContentUpdate( self, content_update )
|
||
|
|
||
|
if content_update.GetAction() == CC.CONTENT_UPDATE_ADD and content_update.GetServiceIdentifier() == CC.LOCAL_FILE_SERVICE_IDENTIFIER:
|
||
|
|
||
|
if self.GetDisplayMedia() in self._GetMedia( content_update.GetHashes() ): self.ReloadFromDB()
|
||
|
|
||
|
|
||
|
|
||
|
class ThumbnailMediaSingleton( Thumbnail, ClientGUIMixins.MediaSingleton ):
|
||
|
|
||
|
def __init__( self, file_service_identifier, media_result ):
|
||
|
|
||
|
ClientGUIMixins.MediaSingleton.__init__( self, media_result )
|
||
|
Thumbnail.__init__( self, file_service_identifier )
|
||
|
|
||
|
|
||
|
def ProcessContentUpdate( self, content_update ):
|
||
|
|
||
|
ClientGUIMixins.MediaSingleton.ProcessContentUpdate( self, content_update )
|
||
|
|
||
|
if content_update.GetAction() == CC.CONTENT_UPDATE_ADD and content_update.GetServiceIdentifier() == CC.LOCAL_FILE_SERVICE_IDENTIFIER: self.ReloadFromDB()
|
||
|
|
||
|
|