Version 134
This commit is contained in:
parent
8585942d95
commit
5662e56649
|
@ -1,13 +1,12 @@
|
|||
## Hydrus Image Tagging Network (Client and Server)
|
||||
|
||||
A personal booru-style media tagger that can import files and tags from your hard drive and popular websites. Content can be shared with other users via user-run servers.
|
||||
The hydrus network client is an application written for anon and other internet-fluent media nerds who have large image/swf collections. It browses with tags instead of folders, a little like a *booru on your desktop. Tags and files can be anonymously shared through custom servers that any user may run. Everything is free, and the source code is included with the release. It is developed for Windows, but fairly functional builds for Linux and OS X are released at the same time.
|
||||
|
||||
This is the source-only release, meant for people who want to experiment with the code; if you want an executable for Windows, Linux or OS X, please check out the [compiled releases](http://www.mediafire.com/hydrus). All releases include original source code.
|
||||
The software is constantly being improved. I put out a new release every Wednesday at 8pm Eastern.
|
||||
|
||||
The program can do quite a lot! Please check out the help inside the release or [here](http://hydrusnetwork.github.io/hydrus/help).
|
||||
|
||||
* [Homepage](http://hydrusnetwork.github.io/hydrus/)
|
||||
* [Source Code on Github](https://github.com/hydrusnetwork/hydrus)
|
||||
|
||||
## Attribution
|
||||
|
||||
|
|
|
@ -62,5 +62,7 @@ except:
|
|||
|
||||
import traceback
|
||||
|
||||
print( 'Critical error occured! Details written to crash.log!' )
|
||||
|
||||
with open( 'crash.log', 'wb' ) as f: f.write( traceback.format_exc() )
|
||||
|
|
@ -8,6 +8,31 @@
|
|||
<div class="content">
|
||||
<h3>changelog</h3>
|
||||
<ul>
|
||||
<li><h3>version 134</h3></li>
|
||||
<ul>
|
||||
<li>updated to wx 3.0.1.1</li>
|
||||
<li>fixed a critical media scrolling bug due to the wx update</li>
|
||||
<li>improved some bad media scrolling code, sped things up a bit</li>
|
||||
<li>fixed 'top' and 'bottom' media scrolling events</li>
|
||||
<li>fixed a typo that meant the default fullscreen media browsing shortcuts were ctrl+ appended rather than working on their own</li>
|
||||
<li>overly-verbose errors and other text popups are now cropped to 1KB for gui display, with a notice. the full message will be printed to the log as usual</li>
|
||||
<li>improved how severe boot crashes are reported</li>
|
||||
<li>fixed a bit of text-reporting code that wasn't handling non-text very well</li>
|
||||
<li>improved handling of a weird popup message manager error</li>
|
||||
<li>fixed an occasional overhasty cleanup error in the checkimportfolders daemon</li>
|
||||
<li>added options for default values for the thread watcher's number of times to check and check period</li>
|
||||
<li>fixed the thread watcher complaining about closing when the checking was finished</li>
|
||||
<li>optimised some id generating code to stop spamming the id cache, which I think was overloading after a while and causing weird PyAssertionErrors</li>
|
||||
<li>neatened autocomplete dropdown service storage and menu id generation</li>
|
||||
<li>improved menu id generation for tagbox</li>
|
||||
<li>fixed opening new petition page from view menu</li>
|
||||
<li>added a 'this might take ages' warning yes/no dialog when trying to delete a tag service</li>
|
||||
<li>added a little popup message info to report on progress when deleting a tag service</li>
|
||||
<li>added some server db testing</li>
|
||||
<li>fixed an error when double-clicking a tag in a page without search predicates</li>
|
||||
<li>updated some help links from mediafire to the new github releases page</li>
|
||||
<li>fixed a typo bug in server db's account flushing code</li>
|
||||
</ul>
|
||||
<li><h3>version 133</h3></li>
|
||||
<ul>
|
||||
<li>reworked the add file process to correct file repository file counts</li>
|
||||
|
|
|
@ -8,14 +8,15 @@
|
|||
<div class="content">
|
||||
<h3>contact and links</h3>
|
||||
<p>Please send bug reports straight to my email or forum. Your other ideas and comments are always welcome.</p>
|
||||
<p>I don't really do chat, and I don't get caught up in the social stuff on twitter/tumblr. If you want to tell me something, please just send me a mail. I like to spend a day or so to think before replying to non-urgent emails, but I do reply to everything. If I said I was going to work on something you care about and seem to have forgotten about it, please do nudge me.</p>
|
||||
<p>If you have a problem with something on someone's file repository, please, <span class="warning">do not come to me</span>, as I can in no way help with your problem. If your ex-gf's nudes have leaked onto the internet, or you find something terribly offensive, or you just plain hate the free flow of information, I cannot help you at all.</p>
|
||||
<p>I don't really do chat, and I don't get caught up in the social stuff on twitter/tumblr. I like to spend a day or so to think before replying to non-urgent emails, but I do reply to everything.</p>
|
||||
<p>I delete all tweets and resolved email conversations after three months. So, if you think you are waiting for a reply, or I said I was going to work on something you care about and seem to have forgotten, please do nudge me.</p>
|
||||
<p>If you have a problem with something on someone else's server, please, <span class="warning">do not come to me</span>, as I cannot help. If your ex-gf's nudes have leaked onto the internet, or you find something terribly offensive, or you just plain hate the free flow of information, I cannot help you at all.</p>
|
||||
<p>Anyway:</p>
|
||||
<ul>
|
||||
<li><a href="mailto:hydrus.admin@gmail.com">email</a></li>
|
||||
<li><a href="http://hydrus.x10.mx/forum/">forum</a></li>
|
||||
<li><a href="http://hydrus.tumblr.com/">tumblr</a> (<a href="http://hydrus.tumblr.com/rss">rss</a>)</li>
|
||||
<li><a href="http://twitter.com/hydrusnetwork">twitter</a> (<a href="http://api.twitter.com/1/statuses/user_timeline.rss?screen_name=hydrusnetwork">rss</a>)</li>
|
||||
<li><a href="http://twitter.com/hydrusnetwork">twitter</a></li>
|
||||
</ul>
|
||||
<p>If you would like to send me something physical, you can use my PO Box:</p>
|
||||
<ul>
|
||||
|
@ -23,7 +24,6 @@
|
|||
<li>Rockford, IL, 61126</li>
|
||||
<li>UNITED STATES</li>
|
||||
</ul>
|
||||
<p>I try to make my work <a href="http://en.wikipedia.org/wiki/Project_triangle">good and cheap</a>. As a result, I am not very fast. Whenever I say 'x should be done within a week', please don't believe my optimism.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -192,6 +192,7 @@ CLIENT_DEFAULT_OPTIONS[ 'num_autocomplete_chars' ] = 2
|
|||
CLIENT_DEFAULT_OPTIONS[ 'gui_capitalisation' ] = False
|
||||
CLIENT_DEFAULT_OPTIONS[ 'default_gui_session' ] = 'just a blank page'
|
||||
CLIENT_DEFAULT_OPTIONS[ 'ac_timings' ] = ( 3, 500, 250 )
|
||||
CLIENT_DEFAULT_OPTIONS[ 'thread_checker_timings' ] = ( 3, 1200 )
|
||||
|
||||
system_predicates = {}
|
||||
|
||||
|
@ -258,22 +259,22 @@ shortcuts[ wx.ACCEL_CTRL ][ ord( 'I' ) ] = 'synchronised_wait_switch'
|
|||
shortcuts[ wx.ACCEL_CTRL ][ ord( 'Z' ) ] = 'undo'
|
||||
shortcuts[ wx.ACCEL_CTRL ][ ord( 'Y' ) ] = 'redo'
|
||||
|
||||
shortcuts[ wx.ACCEL_CTRL ][ wx.WXK_UP ] = 'previous'
|
||||
shortcuts[ wx.ACCEL_CTRL ][ wx.WXK_LEFT ] = 'previous'
|
||||
shortcuts[ wx.ACCEL_CTRL ][ wx.WXK_NUMPAD_UP ] = 'previous'
|
||||
shortcuts[ wx.ACCEL_CTRL ][ wx.WXK_NUMPAD_LEFT ] = 'previous'
|
||||
shortcuts[ wx.ACCEL_CTRL ][ wx.WXK_PAGEUP ] = 'previous'
|
||||
shortcuts[ wx.ACCEL_CTRL ][ wx.WXK_NUMPAD_PAGEUP ] = 'previous'
|
||||
shortcuts[ wx.ACCEL_CTRL ][ wx.WXK_DOWN ] = 'next'
|
||||
shortcuts[ wx.ACCEL_CTRL ][ wx.WXK_RIGHT ] = 'next'
|
||||
shortcuts[ wx.ACCEL_CTRL ][ wx.WXK_NUMPAD_DOWN ] = 'next'
|
||||
shortcuts[ wx.ACCEL_CTRL ][ wx.WXK_NUMPAD_RIGHT ] = 'next'
|
||||
shortcuts[ wx.ACCEL_CTRL ][ wx.WXK_PAGEDOWN ] = 'next'
|
||||
shortcuts[ wx.ACCEL_CTRL ][ wx.WXK_NUMPAD_PAGEDOWN ] = 'next'
|
||||
shortcuts[ wx.ACCEL_CTRL ][ wx.WXK_HOME ] = 'first'
|
||||
shortcuts[ wx.ACCEL_CTRL ][ wx.WXK_NUMPAD_HOME ] = 'first'
|
||||
shortcuts[ wx.ACCEL_CTRL ][ wx.WXK_END ] = 'last'
|
||||
shortcuts[ wx.ACCEL_CTRL ][ wx.WXK_NUMPAD_END ] = 'last'
|
||||
shortcuts[ wx.ACCEL_NORMAL ][ wx.WXK_UP ] = 'previous'
|
||||
shortcuts[ wx.ACCEL_NORMAL ][ wx.WXK_LEFT ] = 'previous'
|
||||
shortcuts[ wx.ACCEL_NORMAL ][ wx.WXK_NUMPAD_UP ] = 'previous'
|
||||
shortcuts[ wx.ACCEL_NORMAL ][ wx.WXK_NUMPAD_LEFT ] = 'previous'
|
||||
shortcuts[ wx.ACCEL_NORMAL ][ wx.WXK_PAGEUP ] = 'previous'
|
||||
shortcuts[ wx.ACCEL_NORMAL ][ wx.WXK_NUMPAD_PAGEUP ] = 'previous'
|
||||
shortcuts[ wx.ACCEL_NORMAL ][ wx.WXK_DOWN ] = 'next'
|
||||
shortcuts[ wx.ACCEL_NORMAL ][ wx.WXK_RIGHT ] = 'next'
|
||||
shortcuts[ wx.ACCEL_NORMAL ][ wx.WXK_NUMPAD_DOWN ] = 'next'
|
||||
shortcuts[ wx.ACCEL_NORMAL ][ wx.WXK_NUMPAD_RIGHT ] = 'next'
|
||||
shortcuts[ wx.ACCEL_NORMAL ][ wx.WXK_PAGEDOWN ] = 'next'
|
||||
shortcuts[ wx.ACCEL_NORMAL ][ wx.WXK_NUMPAD_PAGEDOWN ] = 'next'
|
||||
shortcuts[ wx.ACCEL_NORMAL ][ wx.WXK_HOME ] = 'first'
|
||||
shortcuts[ wx.ACCEL_NORMAL ][ wx.WXK_NUMPAD_HOME ] = 'first'
|
||||
shortcuts[ wx.ACCEL_NORMAL ][ wx.WXK_END ] = 'last'
|
||||
shortcuts[ wx.ACCEL_NORMAL ][ wx.WXK_NUMPAD_END ] = 'last'
|
||||
|
||||
shortcuts[ wx.ACCEL_SHIFT ][ wx.WXK_UP ] = 'pan_up'
|
||||
shortcuts[ wx.ACCEL_SHIFT ][ wx.WXK_DOWN ] = 'pan_down'
|
||||
|
|
|
@ -277,7 +277,7 @@ The database will be locked while the backup occurs, which may lock up your gui
|
|||
|
||||
|
||||
def OnInit( self ):
|
||||
|
||||
self.SetAssertMode(wx.PYAPP_ASSERT_SUPPRESS)
|
||||
HC.app = self
|
||||
HC.http = HydrusNetworking.HTTPConnectionManager()
|
||||
|
||||
|
|
|
@ -4195,6 +4195,7 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
|
|||
HC.repos_changed = True
|
||||
|
||||
recalc_combined_mappings = False
|
||||
message = None
|
||||
|
||||
for ( action, details ) in edit_log:
|
||||
|
||||
|
@ -4212,6 +4213,18 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
|
|||
|
||||
service = self._GetService( c, service_id )
|
||||
|
||||
if service.GetServiceType() == HC.TAG_REPOSITORY:
|
||||
|
||||
recalc_combined_mappings = True
|
||||
|
||||
if message is None:
|
||||
|
||||
message = HC.Message( HC.MESSAGE_TYPE_TEXT, { 'text' : 'updating services: deleting tag data' } )
|
||||
|
||||
HC.pubsub.pub( 'message', message )
|
||||
|
||||
|
||||
|
||||
c.execute( 'DELETE FROM services WHERE service_id = ?;', ( service_id, ) )
|
||||
|
||||
service_update = HC.ServiceUpdate( HC.SERVICE_UPDATE_RESET )
|
||||
|
@ -4263,7 +4276,14 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
|
|||
|
||||
|
||||
|
||||
if recalc_combined_mappings: self._RecalcCombinedMappings( c )
|
||||
if recalc_combined_mappings:
|
||||
|
||||
message.SetInfo( 'text', 'updating services: recalculating combined tag data' )
|
||||
|
||||
self._RecalcCombinedMappings( c )
|
||||
|
||||
message.SetInfo( 'text', 'updating services: done!' )
|
||||
|
||||
|
||||
self.pub_after_commit( 'notify_new_pending' )
|
||||
|
||||
|
@ -5800,12 +5820,13 @@ def DAEMONCheckImportFolders():
|
|||
|
||||
details[ 'failed_imported_paths' ].add( path )
|
||||
|
||||
HC.ShowText( 'Import folder failed to import ' + path + ':' + os.linesep + traceback.format_exc() )
|
||||
HC.ShowText( 'Import folder failed to import ' + path + ':' + os.linesep * 2 + traceback.format_exc() )
|
||||
|
||||
should_action = False
|
||||
|
||||
|
||||
os.remove( temp_path )
|
||||
try: os.remove( temp_path )
|
||||
except: pass # sometimes this fails, I think due to old handles not being cleaned up fast enough. np--it'll be cleaned up later
|
||||
|
||||
|
||||
if should_action:
|
||||
|
|
|
@ -781,8 +781,8 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
|
|||
if len( petition_resolve_tag_services ) > 0 or len( petition_resolve_file_services ) > 0:
|
||||
|
||||
menu.AppendSeparator()
|
||||
for service in petition_resolve_tag_services: menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'petitions', service ), p( service.GetName() + ' Petitions' ), p( 'Open a petition tab for ' + service.GetName() ) )
|
||||
for service in petition_resolve_file_services: menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'petitions', service ), p( service.GetName() + ' Petitions' ), p( 'Open a petition tab for ' + service.GetName() ) )
|
||||
for service in petition_resolve_tag_services: menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'petitions', service.GetServiceKey() ), p( service.GetName() + ' Petitions' ), p( 'Open a petition tab for ' + service.GetName() ) )
|
||||
for service in petition_resolve_file_services: menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'petitions', service.GetServiceKey() ), p( service.GetName() + ' Petitions' ), p( 'Open a petition tab for ' + service.GetName() ) )
|
||||
|
||||
menu.AppendSeparator()
|
||||
menu.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.' ) )
|
||||
|
@ -2072,7 +2072,8 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
|
||||
self.Hide()
|
||||
|
||||
self._message_manager.CleanBeforeDestroy()
|
||||
try: self._message_manager.CleanBeforeDestroy()
|
||||
except: pass
|
||||
|
||||
for page in [ self._notebook.GetPage( i ) for i in range( self._notebook.GetPageCount() ) ]: page.CleanBeforeDestroy()
|
||||
|
||||
|
|
|
@ -1982,7 +1982,7 @@ class CanvasFullscreenMediaListCustomFilter( CanvasFullscreenMediaList ):
|
|||
|
||||
if self._current_media.HasInbox(): menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'archive' ), '&archive' )
|
||||
if self._current_media.HasArchive(): menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'inbox' ), 'return to &inbox' )
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'remove', HC.LOCAL_FILE_SERVICE_KEY ), '&remove' )
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'remove' ), '&remove' )
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete', HC.LOCAL_FILE_SERVICE_KEY ), '&delete' )
|
||||
|
||||
menu.AppendSeparator()
|
||||
|
|
|
@ -14,6 +14,10 @@ import wx.richtext
|
|||
from wx.lib.mixins.listctrl import ListCtrlAutoWidthMixin
|
||||
from wx.lib.mixins.listctrl import ColumnSorterMixin
|
||||
|
||||
TEXT_CUTOFF = 1024
|
||||
|
||||
#
|
||||
|
||||
ID_TIMER_ANIMATED = wx.NewId()
|
||||
ID_TIMER_SLIDESHOW = wx.NewId()
|
||||
ID_TIMER_MEDIA_INFO_DISPLAY = wx.NewId()
|
||||
|
@ -425,13 +429,16 @@ class AutoCompleteDropdownTags( AutoCompleteDropdown ):
|
|||
self._current_namespace = ''
|
||||
self._current_matches = []
|
||||
|
||||
self._file_service = HC.app.GetManager( 'services' ).GetService( file_service_key )
|
||||
self._tag_service = HC.app.GetManager( 'services' ).GetService( tag_service_key )
|
||||
self._file_service_key = file_service_key
|
||||
self._tag_service_key = tag_service_key
|
||||
|
||||
self._file_repo_button = wx.Button( self._dropdown_window, label = self._file_service.GetName() )
|
||||
file_service = HC.app.GetManager( 'services' ).GetService( self._file_service_key )
|
||||
tag_service = HC.app.GetManager( 'services' ).GetService( self._tag_service_key )
|
||||
|
||||
self._file_repo_button = wx.Button( self._dropdown_window, label = file_service.GetName() )
|
||||
self._file_repo_button.Bind( wx.EVT_BUTTON, self.EventFileButton )
|
||||
|
||||
self._tag_repo_button = wx.Button( self._dropdown_window, label = self._tag_service.GetName() )
|
||||
self._tag_repo_button = wx.Button( self._dropdown_window, label = tag_service.GetName() )
|
||||
self._tag_repo_button.Bind( wx.EVT_BUTTON, self.EventTagButton )
|
||||
|
||||
self.Bind( wx.EVT_MENU, self.EventMenu )
|
||||
|
@ -465,7 +472,7 @@ class AutoCompleteDropdownTags( AutoCompleteDropdown ):
|
|||
|
||||
menu = wx.Menu()
|
||||
|
||||
for service in services: menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'change_file_repository', service ), service.GetName() )
|
||||
for service in services: menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'change_file_repository', service.GetServiceKey() ), service.GetName() )
|
||||
|
||||
self.PopupMenu( menu )
|
||||
|
||||
|
@ -482,27 +489,27 @@ class AutoCompleteDropdownTags( AutoCompleteDropdown ):
|
|||
|
||||
if command == 'change_file_repository':
|
||||
|
||||
service = data
|
||||
self._file_service_key = data
|
||||
|
||||
self._file_service = service
|
||||
file_service = HC.app.GetManager( 'services' ).GetService( self._file_service_key )
|
||||
|
||||
name = service.GetName()
|
||||
name = file_service.GetName()
|
||||
|
||||
self._file_repo_button.SetLabel( name )
|
||||
|
||||
HC.pubsub.pub( 'change_file_repository', self._page_key, service.GetServiceKey() )
|
||||
HC.pubsub.pub( 'change_file_repository', self._page_key, self._file_service_key )
|
||||
|
||||
elif command == 'change_tag_repository':
|
||||
|
||||
service = data
|
||||
self._tag_service_key = data
|
||||
|
||||
self._tag_service = service
|
||||
tag_service = tag_service = HC.app.GetManager( 'services' ).GetService( self._tag_service_key )
|
||||
|
||||
name = service.GetName()
|
||||
name = tag_service.GetName()
|
||||
|
||||
self._tag_repo_button.SetLabel( name )
|
||||
|
||||
HC.pubsub.pub( 'change_tag_repository', self._page_key, service.GetServiceKey() )
|
||||
HC.pubsub.pub( 'change_tag_repository', self._page_key, self._tag_service_key )
|
||||
|
||||
else:
|
||||
|
||||
|
@ -529,7 +536,7 @@ class AutoCompleteDropdownTags( AutoCompleteDropdown ):
|
|||
|
||||
menu = wx.Menu()
|
||||
|
||||
for service in services: menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'change_tag_repository', service ), service.GetName() )
|
||||
for service in services: menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'change_tag_repository', service.GetServiceKey() ), service.GetName() )
|
||||
|
||||
self.PopupMenu( menu )
|
||||
|
||||
|
@ -609,8 +616,8 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
self._first_letters = ''
|
||||
self._current_namespace = ''
|
||||
|
||||
if self._file_service.GetServiceKey() == HC.COMBINED_FILE_SERVICE_KEY: search_service_key = self._tag_service.GetServiceKey()
|
||||
else: search_service_key = self._file_service.GetServiceKey()
|
||||
if self._file_service_key == HC.COMBINED_FILE_SERVICE_KEY: search_service_key = self._tag_service_key
|
||||
else: search_service_key = self._file_service_key
|
||||
|
||||
matches = HC.app.Read( 'file_system_predicates', search_service_key )
|
||||
|
||||
|
@ -653,7 +660,7 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
|
||||
if len( search_text ) < num_first_letters:
|
||||
|
||||
results = HC.app.Read( 'autocomplete_tags', file_service_key = self._file_service.GetServiceKey(), tag_service_key = self._tag_service.GetServiceKey(), tag = search_text, include_current = self._include_current, include_pending = self._include_pending )
|
||||
results = HC.app.Read( 'autocomplete_tags', file_service_key = self._file_service_key, tag_service_key = self._tag_service_key, tag = search_text, include_current = self._include_current, include_pending = self._include_pending )
|
||||
|
||||
matches = results.GetMatches( half_complete_tag )
|
||||
|
||||
|
@ -663,7 +670,7 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
|
||||
self._first_letters = half_complete_tag
|
||||
|
||||
self._cached_results = HC.app.Read( 'autocomplete_tags', file_service_key = self._file_service.GetServiceKey(), tag_service_key = self._tag_service.GetServiceKey(), half_complete_tag = search_text, include_current = self._include_current, include_pending = self._include_pending )
|
||||
self._cached_results = HC.app.Read( 'autocomplete_tags', file_service_key = self._file_service_key, tag_service_key = self._tag_service_key, half_complete_tag = search_text, include_current = self._include_current, include_pending = self._include_pending )
|
||||
|
||||
|
||||
matches = self._cached_results.GetMatches( half_complete_tag )
|
||||
|
@ -682,8 +689,8 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
else: tags_managers.append( m.GetTagsManager() )
|
||||
|
||||
|
||||
lists_of_current_tags = [ list( tags_manager.GetCurrent( self._tag_service.GetServiceKey() ) ) for tags_manager in tags_managers ]
|
||||
lists_of_pending_tags = [ list( tags_manager.GetPending( self._tag_service.GetServiceKey() ) ) for tags_manager in tags_managers ]
|
||||
lists_of_current_tags = [ list( tags_manager.GetCurrent( self._tag_service_key ) ) for tags_manager in tags_managers ]
|
||||
lists_of_pending_tags = [ list( tags_manager.GetPending( self._tag_service_key ) ) for tags_manager in tags_managers ]
|
||||
|
||||
current_tags_flat_iterable = itertools.chain.from_iterable( lists_of_current_tags )
|
||||
pending_tags_flat_iterable = itertools.chain.from_iterable( lists_of_pending_tags )
|
||||
|
@ -705,7 +712,7 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
if self._include_current: tags_to_do.update( current_tags_to_count.keys() )
|
||||
if self._include_pending: tags_to_do.update( pending_tags_to_count.keys() )
|
||||
|
||||
results = CC.AutocompleteMatchesPredicates( self._tag_service.GetServiceKey(), [ HC.Predicate( HC.PREDICATE_TYPE_TAG, ( operator, tag ), { HC.CURRENT : current_tags_to_count[ tag ], HC.PENDING : pending_tags_to_count[ tag ] } ) for tag in tags_to_do ] )
|
||||
results = CC.AutocompleteMatchesPredicates( self._tag_service_key, [ HC.Predicate( HC.PREDICATE_TYPE_TAG, ( operator, tag ), { HC.CURRENT : current_tags_to_count[ tag ], HC.PENDING : pending_tags_to_count[ tag ] } ) for tag in tags_to_do ] )
|
||||
|
||||
matches = results.GetMatches( half_complete_tag )
|
||||
|
||||
|
@ -791,15 +798,15 @@ class AutoCompleteDropdownTagsWrite( AutoCompleteDropdownTags ):
|
|||
|
||||
tag_censorship_manager = HC.app.GetManager( 'tag_censorship' )
|
||||
|
||||
result = tag_censorship_manager.FilterTags( self._tag_service.GetServiceKey(), ( tag, ) )
|
||||
result = tag_censorship_manager.FilterTags( self._tag_service_key, ( tag, ) )
|
||||
|
||||
if len( result ) > 0:
|
||||
|
||||
tag_parents_manager = HC.app.GetManager( 'tag_parents' )
|
||||
|
||||
parents = tag_parents_manager.GetParents( self._tag_service.GetServiceKey(), tag )
|
||||
parents = tag_parents_manager.GetParents( self._tag_service_key, tag )
|
||||
|
||||
parents = tag_censorship_manager.FilterTags( self._tag_service.GetServiceKey(), parents )
|
||||
parents = tag_censorship_manager.FilterTags( self._tag_service_key, parents )
|
||||
|
||||
self._chosen_tag_callable( tag, parents )
|
||||
|
||||
|
@ -845,7 +852,7 @@ class AutoCompleteDropdownTagsWrite( AutoCompleteDropdownTags ):
|
|||
|
||||
if len( search_text ) < num_first_letters:
|
||||
|
||||
results = HC.app.Read( 'autocomplete_tags', file_service_key = self._file_service.GetServiceKey(), tag_service_key = self._tag_service.GetServiceKey(), tag = search_text, collapse = False )
|
||||
results = HC.app.Read( 'autocomplete_tags', file_service_key = self._file_service_key, tag_service_key = self._tag_service_key, tag = search_text, collapse = False )
|
||||
|
||||
matches = results.GetMatches( half_complete_tag )
|
||||
|
||||
|
@ -855,7 +862,7 @@ class AutoCompleteDropdownTagsWrite( AutoCompleteDropdownTags ):
|
|||
|
||||
self._first_letters = half_complete_tag
|
||||
|
||||
self._cached_results = HC.app.Read( 'autocomplete_tags', file_service_key = self._file_service.GetServiceKey(), tag_service_key = self._tag_service.GetServiceKey(), half_complete_tag = search_text, collapse = False )
|
||||
self._cached_results = HC.app.Read( 'autocomplete_tags', file_service_key = self._file_service_key, tag_service_key = self._tag_service_key, half_complete_tag = search_text, collapse = False )
|
||||
|
||||
|
||||
matches = self._cached_results.GetMatches( half_complete_tag )
|
||||
|
@ -905,7 +912,7 @@ class AutoCompleteDropdownTagsWrite( AutoCompleteDropdownTags ):
|
|||
|
||||
parents_manager = HC.app.GetManager( 'tag_parents' )
|
||||
|
||||
raw_parents = parents_manager.GetParents( self._tag_service.GetServiceKey(), tag )
|
||||
raw_parents = parents_manager.GetParents( self._tag_service_key, tag )
|
||||
|
||||
parents = [ HC.Predicate( HC.PREDICATE_TYPE_PARENT, raw_parent ) for raw_parent in raw_parents ]
|
||||
|
||||
|
@ -1859,7 +1866,7 @@ class ListBox( wx.ScrolledWindow ):
|
|||
|
||||
self.DoPrepareDC( cdc ) # because this is a scrolled window
|
||||
|
||||
return wx.BufferedDC( cdc, self._canvas_bmp )
|
||||
return wx.BufferedDC( cdc, self._canvas_bmp, wx.BUFFER_VIRTUAL_AREA )
|
||||
|
||||
|
||||
def _GetTextColour( self, text ): return ( 0, 111, 250 )
|
||||
|
@ -1968,7 +1975,20 @@ class ListBox( wx.ScrolledWindow ):
|
|||
|
||||
( command, data ) = action
|
||||
|
||||
if command == 'copy': HC.pubsub.pub( 'clipboard', 'text', data )
|
||||
if command == 'copy_term':
|
||||
|
||||
term = self._strings_to_terms[ self._ordered_strings[ self._current_selected_index ] ]
|
||||
|
||||
HC.pubsub.pub( 'clipboard', 'text', term )
|
||||
|
||||
elif command == 'copy_sub_term':
|
||||
|
||||
term = self._strings_to_terms[ self._ordered_strings[ self._current_selected_index ] ]
|
||||
|
||||
sub_term = term.split( ':', 1 )[1]
|
||||
|
||||
HC.pubsub.pub( 'clipboard', 'text', sub_term )
|
||||
|
||||
else:
|
||||
|
||||
event.Skip()
|
||||
|
@ -1990,13 +2010,13 @@ class ListBox( wx.ScrolledWindow ):
|
|||
|
||||
term = self._strings_to_terms[ self._ordered_strings[ self._current_selected_index ] ]
|
||||
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy', term ), 'copy ' + term )
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_term' ), 'copy ' + term )
|
||||
|
||||
if ':' in term:
|
||||
|
||||
sub_term = term.split( ':', 1 )[1]
|
||||
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy', sub_term ), 'copy ' + sub_term )
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_sub_term' ), 'copy ' + sub_term )
|
||||
|
||||
|
||||
self.PopupMenu( menu )
|
||||
|
@ -2540,6 +2560,17 @@ class PopupMessageError( PopupMessage ):
|
|||
|
||||
if len( HC.u( value ) ) > 0:
|
||||
|
||||
if len( HC.u( value ) ) > TEXT_CUTOFF:
|
||||
|
||||
new_value = 'An error occured that is too long to display here. Here is the start of it (the rest is printed to the log):'
|
||||
|
||||
new_value += os.linesep * 2
|
||||
|
||||
new_value += value[:TEXT_CUTOFF]
|
||||
|
||||
value = new_value
|
||||
|
||||
|
||||
text = FitResistantStaticText( self, label = HC.u( value ) )
|
||||
text.Wrap( 380 )
|
||||
text.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
|
||||
|
@ -2593,7 +2624,7 @@ class PopupMessageFiles( PopupMessage ):
|
|||
|
||||
PopupMessage.__init__( self, parent, message )
|
||||
|
||||
text = message.GetInfo( 'text' )
|
||||
text = message.GetInfo( 'text' )[:TEXT_CUTOFF]
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
|
@ -2717,7 +2748,7 @@ class PopupMessageGauge( PopupMessage ):
|
|||
def Update( self ):
|
||||
|
||||
mode = self._message.GetInfo( 'mode' )
|
||||
text = self._message.GetInfo( 'text' )
|
||||
text = self._message.GetInfo( 'text' )[:TEXT_CUTOFF]
|
||||
|
||||
if self._job_key.IsPausable() and self._created - HC.GetNow() > 2: self._pause_button.Show()
|
||||
else: self._pause_button.Hide()
|
||||
|
@ -2771,7 +2802,7 @@ class PopupMessageText( PopupMessage ):
|
|||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
text = message.GetInfo( 'text' )
|
||||
text = self._ProcessText( HC.u( message.GetInfo( 'text' ) ) )
|
||||
|
||||
self._text = FitResistantStaticText( self, label = text )
|
||||
self._text.Wrap( 380 )
|
||||
|
@ -2782,9 +2813,25 @@ class PopupMessageText( PopupMessage ):
|
|||
self.SetSizer( vbox )
|
||||
|
||||
|
||||
def _ProcessText( self, text ):
|
||||
|
||||
if len( text ) > TEXT_CUTOFF:
|
||||
|
||||
new_text = 'A text notice occured that is too long to display here. Here is the start of it (the rest is printed to the log):'
|
||||
|
||||
new_text += os.linesep * 2
|
||||
|
||||
new_text += text[:TEXT_CUTOFF]
|
||||
|
||||
text = new_text
|
||||
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def Update( self ):
|
||||
|
||||
text = self._message.GetInfo( 'text' )
|
||||
text = self._ProcessText( self._message.GetInfo( 'text' ) )
|
||||
|
||||
if self._text.GetLabel() != text: self._text.SetLabel( text )
|
||||
|
||||
|
@ -2906,23 +2953,46 @@ class PopupMessageManager( wx.Frame ):
|
|||
|
||||
def _SizeAndPositionAndShow( self ):
|
||||
|
||||
self.Fit()
|
||||
|
||||
parent = self.GetParent()
|
||||
|
||||
( parent_width, parent_height ) = parent.GetClientSize()
|
||||
|
||||
( my_width, my_height ) = self.GetClientSize()
|
||||
|
||||
my_x = ( parent_width - my_width ) - 5
|
||||
my_y = ( parent_height - my_height ) - 15
|
||||
|
||||
self.SetPosition( parent.ClientToScreenXY( my_x, my_y ) )
|
||||
|
||||
num_messages_displayed = self._message_vbox.GetItemCount()
|
||||
|
||||
if num_messages_displayed > 0: self.Show()
|
||||
else: self.Hide()
|
||||
try:
|
||||
|
||||
self.Fit()
|
||||
|
||||
parent = self.GetParent()
|
||||
|
||||
( parent_width, parent_height ) = parent.GetClientSize()
|
||||
|
||||
( my_width, my_height ) = self.GetClientSize()
|
||||
|
||||
my_x = ( parent_width - my_width ) - 5
|
||||
my_y = ( parent_height - my_height ) - 15
|
||||
|
||||
self.SetPosition( parent.ClientToScreenXY( my_x, my_y ) )
|
||||
|
||||
num_messages_displayed = self._message_vbox.GetItemCount()
|
||||
|
||||
if num_messages_displayed > 0: self.Show()
|
||||
else: self.Hide()
|
||||
|
||||
except:
|
||||
|
||||
# I don't understand the error here.
|
||||
# It happened for someone in Fit(), causing 'C++ assertion 'm_hDWP failed at blah ... EndRepositioningChildren Shouldn't be called'
|
||||
# It might be related to an id-cache overflow error I had before, in which case it is fixed
|
||||
|
||||
text = 'The popup message manager experienced a fatal error and will now stop working! Please restart the client as soon as possible! If this keeps happening, please email the details and your client.log to the hydrus developer.'
|
||||
|
||||
print( text )
|
||||
|
||||
print( traceback.format_exc() )
|
||||
|
||||
wx.MessageBox( text )
|
||||
|
||||
self._timer.Stop()
|
||||
|
||||
self.CleanBeforeDestroy()
|
||||
|
||||
self.Destroy()
|
||||
|
||||
|
||||
|
||||
def AddMessage( self, message ):
|
||||
|
@ -4010,12 +4080,25 @@ class TagsBox( ListBox ):
|
|||
|
||||
( command, data ) = action
|
||||
|
||||
if command == 'copy': HC.pubsub.pub( 'clipboard', 'text', data )
|
||||
if command == 'copy_term':
|
||||
|
||||
term = self._strings_to_terms[ self._ordered_strings[ self._current_selected_index ] ]
|
||||
|
||||
HC.pubsub.pub( 'clipboard', 'text', term )
|
||||
|
||||
elif command == 'copy_sub_term':
|
||||
|
||||
term = self._strings_to_terms[ self._ordered_strings[ self._current_selected_index ] ]
|
||||
|
||||
sub_term = term.split( ':', 1 )[1]
|
||||
|
||||
HC.pubsub.pub( 'clipboard', 'text', sub_term )
|
||||
|
||||
elif command == 'copy_all_tags': HC.pubsub.pub( 'clipboard', 'text', os.linesep.join( self._GetAllTagsForClipboard() ) )
|
||||
elif command == 'copy_all_tags_with_counts': HC.pubsub.pub( 'clipboard', 'text', os.linesep.join( self._GetAllTagsForClipboard( with_counts = True ) ) )
|
||||
elif command in ( 'parent', 'sibling' ):
|
||||
|
||||
tag = data
|
||||
tag = self._strings_to_terms[ self._ordered_strings[ self._current_selected_index ] ]
|
||||
|
||||
import ClientGUIDialogsManage
|
||||
|
||||
|
@ -4056,19 +4139,19 @@ class TagsBox( ListBox ):
|
|||
|
||||
if type( term ) in ( str, unicode ):
|
||||
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy', term ), 'copy ' + term )
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_term' ), 'copy ' + term )
|
||||
|
||||
if ':' in term:
|
||||
|
||||
sub_term = term.split( ':', 1 )[1]
|
||||
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy', sub_term ), 'copy ' + sub_term )
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_sub_term' ), 'copy ' + sub_term )
|
||||
|
||||
|
||||
menu.AppendSeparator()
|
||||
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'parent', term ), 'add parent to ' + term )
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'sibling', term ), 'add sibling to ' + term )
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'parent' ), 'add parent to ' + term )
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'sibling' ), 'add sibling to ' + term )
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -3004,6 +3004,19 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
|
|||
|
||||
self._listbook.AddPage( self._shortcuts_page, 'shortcuts' )
|
||||
|
||||
# thread checker
|
||||
|
||||
self._thread_checker_page = wx.Panel( self._listbook )
|
||||
self._thread_checker_page.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
|
||||
|
||||
self._thread_times_to_check = wx.SpinCtrl( self._thread_checker_page, min = 0, max = 100 )
|
||||
self._thread_times_to_check.SetToolTipString( 'how many times the thread checker will check' )
|
||||
|
||||
self._thread_check_period = wx.SpinCtrl( self._thread_checker_page, min = 30, max = 86400 )
|
||||
self._thread_check_period.SetToolTipString( 'how long the checker will wait between checks' )
|
||||
|
||||
self._listbook.AddPage( self._thread_checker_page, 'thread checker' )
|
||||
|
||||
#
|
||||
|
||||
self._ok = wx.Button( self, id = wx.ID_OK, label = 'Save' )
|
||||
|
@ -3193,6 +3206,14 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
|
|||
|
||||
self._SortListCtrl()
|
||||
|
||||
#
|
||||
|
||||
( times_to_check, check_period ) = HC.options[ 'thread_checker_timings' ]
|
||||
|
||||
self._thread_times_to_check.SetValue( times_to_check )
|
||||
|
||||
self._thread_check_period.SetValue( check_period )
|
||||
|
||||
|
||||
def ArrangeControls():
|
||||
|
||||
|
@ -3473,6 +3494,19 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
|
|||
|
||||
#
|
||||
|
||||
gridbox = wx.FlexGridSizer( 0, 2 )
|
||||
|
||||
gridbox.AddGrowableCol( 1, 1 )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self._thread_checker_page, label = 'default number of times to check: ' ), FLAGS_MIXED )
|
||||
gridbox.AddF( self._thread_times_to_check, FLAGS_MIXED )
|
||||
gridbox.AddF( wx.StaticText( self._thread_checker_page, label = 'default wait in seconds between checks: ' ), FLAGS_MIXED )
|
||||
gridbox.AddF( self._thread_check_period, FLAGS_MIXED )
|
||||
|
||||
self._thread_checker_page.SetSizer( gridbox )
|
||||
|
||||
#
|
||||
|
||||
buttons = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
buttons.AddF( self._ok, FLAGS_SMALL_INDENT )
|
||||
|
@ -3791,6 +3825,8 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
|
|||
|
||||
HC.options[ 'local_port' ] = new_local_port
|
||||
|
||||
HC.options[ 'thread_checker_timings' ] = ( self._thread_times_to_check.GetValue(), self._thread_check_period.GetValue() )
|
||||
|
||||
try: HC.app.Write( 'save_options' )
|
||||
except: wx.MessageBox( traceback.format_exc() )
|
||||
|
||||
|
@ -5022,9 +5058,24 @@ class DialogManageServices( ClientGUIDialogs.Dialog ):
|
|||
|
||||
( service_key, service_type, name, info ) = service_panel.GetInfo()
|
||||
|
||||
self._edit_log.append( ( HC.DELETE, service_key ) )
|
||||
|
||||
services_listbook.DeleteCurrentPage()
|
||||
if service_type == HC.TAG_REPOSITORY:
|
||||
|
||||
text = 'Deleting a tag service is a potentially very expensive operation.'
|
||||
text += os.linesep * 2
|
||||
text += 'If you have millions of tags, it could take twenty minutes or more, during which time your database will be locked.'
|
||||
text += os.linesep * 2
|
||||
text += 'Are you sure you want to delete ' + name + '?'
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, text ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
|
||||
self._edit_log.append( ( HC.DELETE, service_key ) )
|
||||
|
||||
services_listbook.DeleteCurrentPage()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1665,13 +1665,15 @@ class ManagementPanelImportThreadWatcher( ManagementPanelImport ):
|
|||
|
||||
self._thread_info = wx.StaticText( self._thread_panel, label = 'enter a 4chan thread url' )
|
||||
|
||||
( times_to_check, check_period ) = HC.options[ 'thread_checker_timings' ]
|
||||
|
||||
self._thread_times_to_check = wx.SpinCtrl( self._thread_panel, size = ( 60, -1 ), min = 0, max = 100 )
|
||||
self._thread_times_to_check.SetValue( 5 )
|
||||
self._thread_times_to_check.SetValue( times_to_check )
|
||||
self._thread_times_to_check.Bind( wx.EVT_SPINCTRL, self.EventThreadVariable )
|
||||
|
||||
self._thread_time = wx.SpinCtrl( self._thread_panel, size = ( 100, -1 ), min = 30, max = 86400 )
|
||||
self._thread_time.SetValue( 180 )
|
||||
self._thread_time.Bind( wx.EVT_SPINCTRL, self.EventThreadVariable )
|
||||
self._thread_check_period = wx.SpinCtrl( self._thread_panel, size = ( 100, -1 ), min = 30, max = 86400 )
|
||||
self._thread_check_period.SetValue( check_period )
|
||||
self._thread_check_period.Bind( wx.EVT_SPINCTRL, self.EventThreadVariable )
|
||||
|
||||
self._thread_input = wx.TextCtrl( self._thread_panel, style = wx.TE_PROCESS_ENTER )
|
||||
self._thread_input.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
|
||||
|
@ -1684,7 +1686,7 @@ class ManagementPanelImportThreadWatcher( ManagementPanelImport ):
|
|||
hbox.AddF( wx.StaticText( self._thread_panel, label = 'check ' ), FLAGS_MIXED )
|
||||
hbox.AddF( self._thread_times_to_check, FLAGS_MIXED )
|
||||
hbox.AddF( wx.StaticText( self._thread_panel, label = ' more times, every ' ), FLAGS_MIXED )
|
||||
hbox.AddF( self._thread_time, FLAGS_MIXED )
|
||||
hbox.AddF( self._thread_check_period, FLAGS_MIXED )
|
||||
hbox.AddF( wx.StaticText( self._thread_panel, label = ' seconds' ), FLAGS_MIXED )
|
||||
|
||||
self._thread_panel.AddF( self._thread_info, FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
@ -1705,7 +1707,7 @@ class ManagementPanelImportThreadWatcher( ManagementPanelImport ):
|
|||
|
||||
import_queue_job_key = self._import_controller.GetJobKey( 'import_queue' )
|
||||
|
||||
thread_time = self._thread_time.GetValue()
|
||||
thread_time = self._thread_check_period.GetValue()
|
||||
thread_times_to_check = self._thread_times_to_check.GetValue()
|
||||
|
||||
import_queue_job_key.SetVariable( 'thread_time', thread_time )
|
||||
|
@ -1858,7 +1860,7 @@ class ManagementPanelImportThreadWatcher( ManagementPanelImport ):
|
|||
|
||||
import_queue_position_job_key = self._import_controller.GetJobKey( 'import_queue' )
|
||||
|
||||
if import_queue_position_job_key.IsWorking() and not import_queue_position_job_key.IsPaused():
|
||||
if self._thread_times_to_check.GetValue() > 0 and import_queue_position_job_key.IsWorking() and not import_queue_position_job_key.IsPaused():
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, 'This page is still importing. Are you sure you want to close it?' ) as dlg:
|
||||
|
||||
|
@ -2148,7 +2150,7 @@ class ManagementPanelQuery( ManagementPanel ):
|
|||
|
||||
def AddPredicate( self, page_key, predicate ):
|
||||
|
||||
if page_key == self._page_key:
|
||||
if self._show_search and page_key == self._page_key:
|
||||
|
||||
if predicate is not None:
|
||||
|
||||
|
|
|
@ -559,8 +559,6 @@ class MediaPanel( ClientGUIMixins.ListeningMediaList, wx.ScrolledWindow ):
|
|||
|
||||
self._shift_focussed_media = None
|
||||
|
||||
self._RefitCanvas()
|
||||
|
||||
self._RedrawCanvas()
|
||||
|
||||
self._PublishSelectionChange()
|
||||
|
@ -614,7 +612,7 @@ class MediaPanel( ClientGUIMixins.ListeningMediaList, wx.ScrolledWindow ):
|
|||
|
||||
def _Select( self, select_type ):
|
||||
|
||||
self._RedrawCanvas()
|
||||
self._RefitCanvas()
|
||||
|
||||
if select_type == 'all': self._DeselectSelect( [], self._sorted_media )
|
||||
else:
|
||||
|
@ -1310,7 +1308,7 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
|
||||
self.DoPrepareDC( cdc ) # because this is a scrolled window
|
||||
|
||||
return wx.BufferedDC( cdc, self._canvas_bmp )
|
||||
return wx.BufferedDC( cdc, self._canvas_bmp, wx.BUFFER_VIRTUAL_AREA )
|
||||
|
||||
|
||||
def _GetThumbnailUnderMouse( self, mouse_event ):
|
||||
|
@ -2037,6 +2035,10 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
# it seems that some scroll events happen after the viewstart has changed, some happen before
|
||||
# so I have to keep track of a manual current_y_start
|
||||
|
||||
( start_x, start_y ) = self.GetViewStart()
|
||||
|
||||
( my_virtual_width, my_virtual_height ) = self.GetVirtualSize()
|
||||
|
||||
( my_width, my_height ) = self.GetClientSize()
|
||||
|
||||
( xUnit, yUnit ) = self.GetScrollPixelsPerUnit()
|
||||
|
@ -2051,6 +2053,8 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
elif event_type == wx.wxEVT_SCROLLWIN_THUMBRELEASE: self._current_y_offset = 0
|
||||
elif event_type == wx.wxEVT_SCROLLWIN_PAGEUP: self._current_y_offset = - page_of_y_units
|
||||
elif event_type == wx.wxEVT_SCROLLWIN_PAGEDOWN: self._current_y_offset = page_of_y_units
|
||||
elif event_type == wx.wxEVT_SCROLLWIN_TOP: self._current_y_offset = - start_y
|
||||
elif event_type == wx.wxEVT_SCROLLWIN_BOTTOM: self._current_y_offset = ( my_virtual_height / yUnit ) - start_y
|
||||
|
||||
self._RefitCanvas()
|
||||
|
||||
|
@ -2140,8 +2144,6 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
|
||||
for t in self._sorted_media: t.ReloadFromDBLater()
|
||||
|
||||
self._RefitCanvas()
|
||||
|
||||
self._RedrawCanvas() # to force redraw
|
||||
|
||||
|
||||
|
|
|
@ -28,16 +28,16 @@ if sys.platform == 'win32': PLATFORM_WINDOWS = True
|
|||
elif sys.platform == 'darwin': PLATFORM_OSX = True
|
||||
elif sys.platform == 'linux2': PLATFORM_LINUX = True
|
||||
|
||||
if PLATFORM_LINUX:
|
||||
|
||||
if not hasattr( sys, 'frozen' ):
|
||||
|
||||
import wxversion
|
||||
|
||||
if not wxversion.checkInstalled( '2.9' ): raise Exception( 'Need wxPython 2.9 on linux!' )
|
||||
|
||||
wxversion.select( '2.9' )
|
||||
|
||||
#if PLATFORM_LINUX:
|
||||
#
|
||||
# if not hasattr( sys, 'frozen' ):
|
||||
#
|
||||
# import wxversion
|
||||
#
|
||||
# if not wxversion.checkInstalled( '2.9' ): raise Exception( 'Need wxPython 2.9 on Linux!' )
|
||||
#
|
||||
# wxversion.select( '2.9' )
|
||||
#
|
||||
|
||||
import wx
|
||||
|
||||
|
@ -64,7 +64,7 @@ options = {}
|
|||
# Misc
|
||||
|
||||
NETWORK_VERSION = 15
|
||||
SOFTWARE_VERSION = 133
|
||||
SOFTWARE_VERSION = 134
|
||||
|
||||
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import HydrusThreading
|
|||
import lz4
|
||||
import numpy
|
||||
import os
|
||||
from PIL import _imaging
|
||||
from PIL import Image as PILImage
|
||||
import shutil
|
||||
import struct
|
||||
|
|
|
@ -1098,7 +1098,7 @@ class ServiceDB( FileDB, MessageDB, TagDB ):
|
|||
|
||||
requests_dict = HC.BuildKeyToListDict( all_requests )
|
||||
|
||||
c.executemany( 'UPDATE account SET used_bytes = used_bytes + ?, used_requests = used_requests + ? WHERE account_key = ?;', [ ( sum( num_bytes_list ), len( num_bytes_list ), sqlite3.Binary( account_key ) ) for ( account_key, num_bytes_list ) in requests_dict.items() ] )
|
||||
c.executemany( 'UPDATE accounts SET used_bytes = used_bytes + ?, used_requests = used_requests + ? WHERE account_key = ?;', [ ( sum( num_bytes_list ), len( num_bytes_list ), sqlite3.Binary( account_key ) ) for ( account_key, num_bytes_list ) in requests_dict.items() ] )
|
||||
|
||||
|
||||
def _GenerateRegistrationKeys( self, c, service_key, num, title, lifetime = None ):
|
||||
|
|
|
@ -1110,7 +1110,7 @@ class TestClientDB( unittest.TestCase ):
|
|||
class TestServerDB( unittest.TestCase ):
|
||||
|
||||
def _read( self, action, *args, **kwargs ): return self._db.Read( action, HC.HIGH_PRIORITY, *args, **kwargs )
|
||||
def _write( self, action, *args, **kwargs ): return self._db.Write( action, HC.HIGH_PRIORITY, True, *args, **kwargs )
|
||||
def _write( self, action, *args, **kwargs ): return self._db.Write( action, HC.HIGH_PRIORITY, *args, **kwargs )
|
||||
|
||||
@classmethod
|
||||
def setUpClass( self ):
|
||||
|
@ -1153,10 +1153,10 @@ class TestServerDB( unittest.TestCase ):
|
|||
HC.SERVER_THUMBNAILS_DIR = self._old_server_thumbnails_dir
|
||||
|
||||
|
||||
# add read and write funcs like with client db
|
||||
|
||||
def _test_account_creation( self ):
|
||||
|
||||
# create new account types
|
||||
# edit the account types
|
||||
# create rkeys
|
||||
# create akeys
|
||||
# test successive rkey fetch gives new akeys
|
||||
|
@ -1177,22 +1177,80 @@ class TestServerDB( unittest.TestCase ):
|
|||
|
||||
def _test_init_server_admin( self ):
|
||||
|
||||
# do it, however it works
|
||||
result = self._read( 'init' ) # an access key
|
||||
|
||||
pass
|
||||
self.assertEqual( type( result ), str )
|
||||
self.assertEqual( len( result ), 32 )
|
||||
|
||||
self._admin_access_key = result
|
||||
|
||||
result = self._read( 'account_key_from_access_key', HC.SERVER_ADMIN_KEY, self._admin_access_key )
|
||||
|
||||
self.assertEqual( type( result ), str )
|
||||
self.assertEqual( len( result ), 32 )
|
||||
|
||||
self._admin_account_key = result
|
||||
|
||||
|
||||
def _test_service_creation( self ):
|
||||
|
||||
# add tag, add file repo
|
||||
self._tag_service_key = os.urandom( 32 )
|
||||
self._file_service_key = os.urandom( 32 )
|
||||
|
||||
# fetch service info or whatever to test
|
||||
edit_log = []
|
||||
|
||||
# change the port
|
||||
t_options = { 'max_monthly_data' : None, 'message' : 'tag repo message', 'port' : 100, 'upnp' : None }
|
||||
f_options = { 'max_monthly_data' : None, 'message' : 'file repo message', 'port' : 101, 'upnp' : None }
|
||||
|
||||
# fetch service info or whatever to test
|
||||
edit_log.append( ( HC.ADD, ( self._tag_service_key, HC.TAG_REPOSITORY, t_options ) ) )
|
||||
edit_log.append( ( HC.ADD, ( self._file_service_key, HC.FILE_REPOSITORY, f_options ) ) )
|
||||
|
||||
pass
|
||||
result = self._write( 'services', self._admin_account_key, edit_log )
|
||||
|
||||
self.assertIn( self._tag_service_key, result )
|
||||
|
||||
self._tag_service_admin_access_key = result[ self._tag_service_key ]
|
||||
|
||||
self.assertEqual( type( self._tag_service_admin_access_key ), str )
|
||||
self.assertEqual( len( self._tag_service_admin_access_key ), 32 )
|
||||
|
||||
self.assertIn( self._file_service_key, result )
|
||||
|
||||
self._file_service_admin_access_key = result[ self._file_service_key ]
|
||||
|
||||
self.assertEqual( type( self._tag_service_admin_access_key ), str )
|
||||
self.assertEqual( len( self._tag_service_admin_access_key ), 32 )
|
||||
|
||||
#
|
||||
|
||||
result = self._read( 'service_keys', HC.REPOSITORIES )
|
||||
|
||||
self.assertEqual( set( result ), { self._tag_service_key, self._file_service_key } )
|
||||
|
||||
#
|
||||
|
||||
result = self._read( 'services_info' )
|
||||
|
||||
services_info = { service_key : ( service_type, options ) for ( service_key, service_type, options ) in result }
|
||||
|
||||
self.assertEqual( services_info[ HC.SERVER_ADMIN_KEY ], ( 99, { 'max_monthly_data' : None, 'message' : 'hydrus server administration service', 'max_storage' : None, 'upnp' : None, 'port' : 45870 } ) )
|
||||
self.assertEqual( services_info[ self._tag_service_key ], ( HC.TAG_REPOSITORY, t_options ) )
|
||||
self.assertEqual( services_info[ self._file_service_key ], ( HC.FILE_REPOSITORY, f_options ) )
|
||||
|
||||
#
|
||||
|
||||
f_options_modified = dict( f_options )
|
||||
f_options_modified[ 'port' ] = 102
|
||||
|
||||
edit_log = [ ( HC.EDIT, ( self._file_service_key, HC.FILE_REPOSITORY, f_options_modified ) ) ]
|
||||
|
||||
self._write( 'services', self._admin_account_key, edit_log )
|
||||
|
||||
result = self._read( 'services_info' )
|
||||
|
||||
services_info = { service_key : ( service_type, options ) for ( service_key, service_type, options ) in result }
|
||||
|
||||
self.assertEqual( services_info[ self._file_service_key ], ( HC.FILE_REPOSITORY, f_options_modified ) )
|
||||
|
||||
|
||||
def test_server( self ):
|
||||
|
|
|
@ -62,5 +62,7 @@ except:
|
|||
|
||||
import traceback
|
||||
|
||||
print( 'Critical error occured! Details written to crash.log!' )
|
||||
|
||||
with open( 'crash.log', 'wb' ) as f: f.write( traceback.format_exc() )
|
||||
|
Loading…
Reference in New Issue