Version 307

This commit is contained in:
Hydrus Network Developer 2018-05-16 15:09:50 -05:00
parent 3b0f013357
commit 7f39f3312c
62 changed files with 4462 additions and 3393 deletions

View File

@ -9,16 +9,18 @@
<h3>first off</h3>
<p>I have purposely not pre-baked this into the client's install. You have to put it in yourself. The client won't connect anywhere until you tell it to.</p>
<h3>access keys</h3>
<p><b><i>If this stuff be-fuzzles you, you can go </i>help->i don't know what I am doing->just set up some repositories for me, please<i> and you _should_ be all set up automatically.</i></b></p>
<p>I run a public, objective tag repository that <a href="tagging_schema.html">you are welcome to contribute</a> to. I also run a read-only file repository that you can search and download from to get a feel for the interface. The files on my file repo are appropriately tagged in my tag repo.</p>
<p><b><i>If you don't understand this, you can just go </i>help->i don't know what I am doing->just set up some repositories for me, please<i> and you _should_ be all set up automatically.</i></b></p>
<p>I run a public tag repository that <a href="tagging_schema.html">you are welcome to contribute</a> to. I also run a read-only file repository, which is mostly just an old thing for fun, but you are welcome to search and download from it to get a feel for the interface. The files on my file repo are appropriately tagged in my tag repo.</p>
<p>To add or edit a new repository, hit <i>services->manage services</i> and click the add or edit button:</p>
<p><img src="edit_repos_public_tag_repo.png" /></p>
<p><img src="edit_repos_file_repo.png" /></p>
<p>It is worth testing the address and access key just to make sure your firewall and key is all correct.</p>
<p>Here's the info so you can copy it:</p>
<ul>
<li>public tag repository: 4a285629721ca442541ef2c15ea17d1f7f7578b0c3f4f5f2a05f8f0ab297786f@hydrus.no-ip.org:45871</li>
<li>read-only file repository: 8f8a3685abc19e78a92ba61d84a0482b1cfac176fd853f46d93fe437a95e40a5@hydrus.no-ip.org:45872</li>
<li>public tag repository: 4a285629721ca442541ef2c15ea17d1f7f7578b0c3f4f5f2a05f8f0ab297786f@<a href="https://hydrus.no-ip.org:45871">hydrus.no-ip.org:45871</a></li>
<li>read-only file repository: 8f8a3685abc19e78a92ba61d84a0482b1cfac176fd853f46d93fe437a95e40a5@<a href="https://hydrus.no-ip.org:45872">hydrus.no-ip.org:45872</a></li>
</ul>
<p><b>Tags are rich, cpu-intensive metadata. My repository has millions of mappings, and your client will eventually download and store them all. It will take a few hundred MB and some <i>hours</i> total processing time to fully synchronise. It will mostly happen in the background, without you noticing--for most users, it takes a week or more to quietly catch up. If you interrupt its processing maintenance, it will continue where it left off the next time it runs.</b></p>
<p><b>Tags are rich, cpu-intensive metadata. My repository has hundreds of millions of mappings, and your client will eventually download and index them all. It will take a few hundred MB and <i>hours</i> of total processing time to fully synchronise. It is best left to work on this in small pieces in the background, either during idle time or shutdown time, so unless you are an advanced user, just leave it to download and process on its own--it usually takes a couple of weeks to quietly catch up.</b></p>
<p>If you are an advanced user, you can manually export (<i>services->review services->export updates</i> if <i>advanced mode</i> is on) and import (<i>services->import repository update files</i>) the update files a repository gives your client to save you redownloading them elsewhere. If you want to set up a PTR client a bit quicker than usual (and you understand the processing routine and the delays it can expeience if it encounters thousands of new update files at once), check the update archive <a href="https://cuddlebear92.github.io/Quicksync/">here</a>.</p>
</div>
</body>
</html>

View File

@ -8,6 +8,45 @@
<div class="content">
<h3>changelog</h3>
<ul>
<li><h3>version 307</h3></li>
<ul>
<li>wrote a gelbooru 0.2.5 (which matches gelbooru itself) parser in the new system. it now has some more redundancy and produces md5 hash and source urls</li>
<li>fixed the e621 parser for flash files</li>
<li>manage tags is now a notebook rather than a listbook!</li>
<li>a problem where OS X was displaying the wrong label in the manage tags listbook is now fixed (as it no longer uses the buggy old listbook)</li>
<li>improved autocomplete focus setting in OS X in general</li>
<li>moved all paged importer loops to the new job scheduling system, which will massively cut down on idle time thread count (and some idle CPU usage) on clients with a bunch of import pages open</li>
<li>the main thread pool (which the job scheduling system uses) can now temporarily grow much larger (200 threads) when it gets hammered</li>
<li>recalibrated some thread watcher code to deal with being embedded in a larger object better (and sharing a page with other watcher importers)</li>
<li>finished the first version of a multiple watcher. it is ugly and only for advanced users for now--please check release post for more information</li>
<li>added 'use the multiple watcher on DnD events' to options->downloading, which will send thread url drag and drops straight to a new/existing multiple watcher!</li>
<li>gui session load now 'shows' the first page but loads everything to the right in the background, which saves some CPU and memory for large sessions. this means it now starts focused on the leftmost page rather than the right--see if you like it or not</li>
<li>'clear and load session' now fully deletes old pages and takes a little break between the clear and load step to make sure old large sessions are deleted tidily before loading the new stuff</li>
<li>cleaned up the new leaner popup message display code to react better to (and re-layout) sub-changes in its popups (so now when a subscription popup spawns a new progress gauge, it _should_ be positioned in the right place immediately)</li>
<li>if an import folder/subscription publishes files to a page while the main gui is minimised (and the action would result in a new page creation), the page add will be delayed until the gui is no longer minimised (new page layout in this case was broken due to minimised parent frame)</li>
<li>the edit subscription query panel will now spawn with the query text input focused</li>
<li>added a patch to ensure new booru import pages get query input focus after a second if the focus attempt on init failed (this will be fixed better in the new downloader system)</li>
<li>restored the 'clear deleted files record' button to the 'combined local files' service on _review services_, which was previously hidden during a significant service rewrite.</li>
<li>if critical master tables are missing on boot, the client will warn and provide info and then abandon the boot</li>
<li>if cache similar files tables are missing on boot, the client will warn and provide info and offer to recreate them empty and try to boot the client anyway (this replaces some old repair code that wasn't always kicking in at a good time)</li>
<li>if cache autocomplete tables are missing on boot, the client will warn and provide info and offer to recreate and repopulate them</li>
<li>if mappings tables are missing on boot, the client will warn and provide info and offer to recreate them empty and try to boot the client anyway</li>
<li>the emergency 'repair missing file locations' panel now runs in a more stable way</li>
<li>this emergency dialog will now also recognise 'I fixed all the file paths but cannot do the thumbnail paths' situations and will present the same 'ok, make sure you run regen thumbs after boot' dialog as if all the missing paths were thumbnail related</li>
<li>you can now turn off x/y page name import progress under options->gui</li>
<li>fixed an issue where tags that begin with ':' like ':p' were not getting through the new importing system</li>
<li>fixed an issue where tags that begin with ':' like ':p' could sometimes get prepended extra escape characters and end up looking like ':::p'</li>
<li>fixed sibling tag searching (e.g. if a->b exists and you search for b, results with a but not b should also appear), which the recent tag search optimisation accidentally disabled</li>
<li>whole bunch of refactoring and cleanup of ClientData and ClientGUICommon</li>
<li>cleaned up some spammy splash text status setting that was flooding some debug info with useless garbage</li>
<li>gave getting_started_tags.html's tag repo section and access_keys.html a pass, updating the ancient screenshot and linking advanced users to the latest QuickSync location</li>
<li>some misc help updates</li>
<li>misc autocomplete logic improvements</li>
<li>misc ui display fixes</li>
<li>misc wx destroy code stability improvement</li>
<li>updated ffmpeg for windows builds to the new 4.0 release</li>
<li>updated sqlite for windows builds</li>
</ul>
<li><h3>version 306</h3></li>
<ul>
<li>the file import status list now has 'open selected import files in a new page', which should show up where it is possible. this is a bit prototype and ugly--it'll show _all_ files, including in-trash and permanently deleted (which will show up with the hydrus thumbnail)</li>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 41 KiB

View File

@ -37,12 +37,13 @@
<h3>tag repositories</h3>
<p>It can take a long time to tag even this small number of files well, so I created <i>tag repositories</i> so people can share the work.</p>
<p>Tag repos store many file->tag relationships. Anyone who has an access key to the repository can sync with it and hence download all these relationships. If any of their own files match up, they will get those tags. Access keys will also usually have permission to upload new tags and ask for existing ones to be deleted.</p>
<p>Anyone can run a tag repository, but it is a bit complicated for new users. I run a public tag repository that you are very welcome to access and contribute to. To connect with it, go <i>services->manage services->remote->tag repositories</i> and hit the 'add' button. You'll be asked if you have a <i>registration key</i> or an <i>access key</i>; you have an access key.</p>
<p>Anyone can run a tag repository, but it is a bit complicated for new users. I run a public tag repository that you are <a href="tagging_schema.html">very welcome to access and contribute to</a>. To connect with it, go <i>services->manage services</i> and hit the 'add' button. Fill it in something like this:</p>
<p><img src="edit_repos_public_tag_repo.png" /></p>
<p>Here's the info so you can copy it:</p>
<ul><li>4a285629721ca442541ef2c15ea17d1f7f7578b0c3f4f5f2a05f8f0ab297786f@hydrus.no-ip.org:45871</li></ul>
<ul><li>4a285629721ca442541ef2c15ea17d1f7f7578b0c3f4f5f2a05f8f0ab297786f@<a href="https://hydrus.no-ip.org:45871">hydrus.no-ip.org:45871</a></li></ul>
<p>It is worth checking the test address and access key buttons just to double-check your firewall and key are all correct.</p>
<p>Over time, usually when it is idle, your client will download updates from the repository until it is fully synchronised. You can customise when this happens in <i>file->options->maintenance and processing</i>. As the repository synchronises, you should see some new tags appear, particularly on famous files that lots of people have.</p>
<p><b>Tags are rich, cpu-intensive metadata. My repository has millions of mappings, and your client will download and store them all. It will take a few hundred MB and several <i>hours</i> total processing time to fully synchronise. It will mostly happen in the background, without you noticing.</b></p>
<p><b>Tags are rich, cpu-intensive metadata. My repository has hundreds of millions of mappings, and your client will eventually download and index them all. It will take a few hundred MB and <i>hours</i> of total processing time to fully synchronise. It is best left to work on this in small pieces in the background, either during idle time or shutdown time, so unless you are an advanced user, just leave it to download and process on its own--it usually takes a couple of weeks to quietly catch up.</b></p>
<p>You can watch more detailed synchronisation progress in the <i>services->review services</i> window.</p>
<p><img src="tag_repo_review.png" /></p>
<p>Your new service should now be listed on the left of the manage tags dialog. Adding tags to a repository works very similarly to the local tags service except hitting 'apply' will not immediately confirm your changes--it will put them in a queue to be uploaded. These 'pending' tags will be counted with a plus '+' or minus '-' sign:</p>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -23,8 +23,8 @@
<li>hors d'œuvre</li>
<li>high heels</li>
<li>character:jean-luc picard</li>
<li>person:okita anri</li>
<li>title:the tragical history of hamlet, prince of denmark</li>
<li>roger ebert:4 stars</li>
<li>page:17</li>
</ul>
</li>
@ -74,13 +74,15 @@
<p>Do correct for common plural mistakes. ear->ears, women->female, and so on.</p>
<p>And feel free to replace any 'character (series)' booru artifacts as with the 'anna (frozen)' example above. 'character:anna (frozen)'->'character:princess anna of arendelle' is great wherever it makes sense.</p>
<p>But please <b>do not</b> go 'blah'->'character:blah' unless the name is popular and unique. No one is going to be confused by 'ayanami rei'->'character:ayanami rei', but going 'archer'->'<a href="http://typemoon.wikia.com/wiki/Archer_%28Fate/stay_night%29">character:archer</a>' is going to create a lot of false positives. There's a similar problem with something like 'character:mercy'->'character:angela "mercy" ziegler'--although the left hand side is namespaced, there are still plenty of <i>characters</i> named 'mercy', so a sibling that converts all Mercys to Overwatch's Mercy is not appropriate.</p>
<p>If the character name is the same as the series name, make the unnamespaced version go to the series version. For instance, set 'harry potter'->'series:harry potter', since we don't know which one it is and 'character:harry potter' &sub; 'series:harry potter'. (If a <a href="https://danbooru.donmai.us/posts/1899356">picture of just Hermione</a> that for some reason was not providing namespace information had 'hermione granger' (the character) and 'harry potter' (the series), we wouldn't want to infer 'character:harry potter' by accident.</p>
<p>In general, swap out slang for proper terms. 'lube'->'lubricant', 'series:zelda'->'series:the legend of zelda'.</p>
</li>
<li>
<h3>parents</h3>
<p>Be shy about adding character:blah->series:whatever unless you are certain the character name is unique. 'character:harry potter'->'series:harry potter' seems fairly uncontroversial, for instance.</p>
<p>Be shy about adding character:blah->series:whatever unless you are certain the character name is unique. 'character:harry potter'->'series:harry potter' seems fairly uncontroversial, for instance, but adding specific sub-series just to be completionist, such as 'character:miranda lawson->series:mass effect: redemption' is asking for trouble.</p>
<p>Remember that parents define a relationship that is always true. Don't add 'blonde hair' to 'character:elsa', even though it is true in most files--add 'animal ears' to 'cat ears', as cat ears are always animal ears, no matter what an artist can think up.</p>
<p>I recommend you only put your time into parents that will actually add a lot of tags and be useful when typing tags in future. You can create a complicated tree like the firearms diagram on my parents page, but if it only adds seven tags, you probably wasted your time.</p>
<p>Also, tag parents are only worth something if the parent is useful for searching. Adding 'medium:blue background'->'blue' isn't useful since 'blue' itself is not very valuable, but 'fishnet stockings'->'stockings' is useful as both tags are common and used in searches by plenty of people.</p>
<p>You can create a complicated tree like the firearms diagram on my parents page, but if it only adds seven tags that you probably wouldn't ever use yourself, you probably wasted your time.</p>
</li>
</ul>
</div>

View File

@ -944,7 +944,7 @@ class ClientFilesManager( object ):
continue
ClientData.DeletePath( path )
ClientPaths.DeletePath( path )

View File

@ -22,6 +22,7 @@ import ClientNetworkingBandwidth
import ClientNetworkingDomain
import ClientNetworkingLogin
import ClientNetworkingSessions
import ClientOptions
import ClientPaths
import ClientThreading
import hashlib
@ -69,7 +70,7 @@ class Controller( HydrusController.HydrusController ):
# just to set up some defaults, in case some db update expects something for an odd yaml-loading reason
self.options = ClientDefaults.GetClientDefaultOptions()
self.new_options = ClientData.ClientOptions( self.db_dir )
self.new_options = ClientOptions.ClientOptions( self.db_dir )
HC.options = self.options
@ -91,7 +92,7 @@ class Controller( HydrusController.HydrusController ):
if self._splash is not None:
wx.CallAfter( self._splash.Destroy )
self._splash.DestroyLater()
self._splash = None
@ -170,22 +171,22 @@ class Controller( HydrusController.HydrusController ):
raise HydrusExceptions.ShutdownException()
def CallLaterWXSafe( self, window, delay, func, *args, **kwargs ):
def CallLaterWXSafe( self, window, initial_delay, func, *args, **kwargs ):
call = HydrusData.Call( func, *args, **kwargs )
job = ClientThreading.WXAwareJob( self, self._job_scheduler, window, call, initial_delay = delay )
job = ClientThreading.WXAwareJob( self, self._job_scheduler, window, initial_delay, call )
self._job_scheduler.AddJob( job )
return job
def CallRepeatingWXSafe( self, window, period, delay, func, *args, **kwargs ):
def CallRepeatingWXSafe( self, window, initial_delay, period, func, *args, **kwargs ):
call = HydrusData.Call( func, *args, **kwargs )
job = ClientThreading.WXAwareRepeatingJob( self, self._job_scheduler, window, call, period, initial_delay = delay )
job = ClientThreading.WXAwareRepeatingJob( self, self._job_scheduler, window, initial_delay, period, call )
self._job_scheduler.AddJob( job )
@ -493,11 +494,7 @@ class Controller( HydrusController.HydrusController ):
def InitClientFilesManager( self ):
self.client_files_manager = ClientCaches.ClientFilesManager( self )
missing_locations = self.client_files_manager.GetMissing()
while len( missing_locations ) > 0:
def wx_code( missing_locations ):
with ClientGUITopLevelWindows.DialogManage( None, 'repair file system' ) as dlg:
@ -517,6 +514,17 @@ class Controller( HydrusController.HydrusController ):
return missing_locations
self.client_files_manager = ClientCaches.ClientFilesManager( self )
missing_locations = self.client_files_manager.GetMissing()
while len( missing_locations ) > 0:
missing_locations = self.CallBlockingToWx( wx_code, missing_locations )
def InitModel( self ):

View File

@ -1,11 +1,13 @@
import ClientData
import ClientDefaults
import ClientGUIShortcuts
import ClientImageHandling
import ClientMedia
import ClientNetworkingBandwidth
import ClientNetworkingContexts
import ClientNetworkingDomain
import ClientNetworkingSessions
import ClientOptions
import ClientRatings
import ClientSearch
import ClientServices
@ -293,19 +295,7 @@ class DB( HydrusDB.HydrusDB ):
if service_type in HC.TAG_SERVICES:
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateMappingsTableNames( service_id )
self._c.execute( 'CREATE TABLE IF NOT EXISTS ' + current_mappings_table_name + ' ( tag_id INTEGER, hash_id INTEGER, PRIMARY KEY ( tag_id, hash_id ) ) WITHOUT ROWID;' )
self._CreateIndex( current_mappings_table_name, [ 'hash_id', 'tag_id' ], unique = True )
self._c.execute( 'CREATE TABLE IF NOT EXISTS ' + deleted_mappings_table_name + ' ( tag_id INTEGER, hash_id INTEGER, PRIMARY KEY ( tag_id, hash_id ) ) WITHOUT ROWID;' )
self._CreateIndex( deleted_mappings_table_name, [ 'hash_id', 'tag_id' ], unique = True )
self._c.execute( 'CREATE TABLE IF NOT EXISTS ' + pending_mappings_table_name + ' ( tag_id INTEGER, hash_id INTEGER, PRIMARY KEY ( tag_id, hash_id ) ) WITHOUT ROWID;' )
self._CreateIndex( pending_mappings_table_name, [ 'hash_id', 'tag_id' ], unique = True )
self._c.execute( 'CREATE TABLE IF NOT EXISTS ' + petitioned_mappings_table_name + ' ( tag_id INTEGER, hash_id INTEGER, reason_id INTEGER, PRIMARY KEY ( tag_id, hash_id ) ) WITHOUT ROWID;' )
self._CreateIndex( petitioned_mappings_table_name, [ 'hash_id', 'tag_id' ], unique = True )
self._GenerateMappingsTables( service_id )
#
@ -1488,11 +1478,14 @@ class DB( HydrusDB.HydrusDB ):
gc.collect()
text = 'regenerating similar file metadata - ' + HydrusData.ConvertValueRangeToPrettyString( total_done_previously + i, total_num_hash_ids_in_cache )
HG.client_controller.pub( 'splash_set_status_subtext', text )
job_key.SetVariable( 'popup_text_1', text )
job_key.SetVariable( 'popup_gauge_1', ( total_done_previously + i, total_num_hash_ids_in_cache ) )
if i % 10 == 0:
text = 'regenerating similar file metadata - ' + HydrusData.ConvertValueRangeToPrettyString( total_done_previously + i, total_num_hash_ids_in_cache )
HG.client_controller.pub( 'splash_set_status_subtext', text )
job_key.SetVariable( 'popup_text_1', text )
job_key.SetVariable( 'popup_gauge_1', ( total_done_previously + i, total_num_hash_ids_in_cache ) )
try:
@ -2859,26 +2852,9 @@ class DB( HydrusDB.HydrusDB ):
self._c.execute( 'CREATE TABLE yaml_dumps ( dump_type INTEGER, dump_name TEXT, dump TEXT_YAML, PRIMARY KEY ( dump_type, dump_name ) );' )
# cache
# caches
self._c.execute( 'CREATE TABLE external_caches.shape_perceptual_hashes ( phash_id INTEGER PRIMARY KEY, phash BLOB_BYTES UNIQUE );' )
self._c.execute( 'CREATE TABLE external_caches.shape_perceptual_hash_map ( phash_id INTEGER, hash_id INTEGER, PRIMARY KEY ( phash_id, hash_id ) );' )
self._CreateIndex( 'external_caches.shape_perceptual_hash_map', [ 'hash_id' ] )
self._c.execute( 'CREATE TABLE external_caches.shape_vptree ( phash_id INTEGER PRIMARY KEY, parent_id INTEGER, radius INTEGER, inner_id INTEGER, inner_population INTEGER, outer_id INTEGER, outer_population INTEGER );' )
self._CreateIndex( 'external_caches.shape_vptree', [ 'parent_id' ] )
self._c.execute( 'CREATE TABLE external_caches.shape_maintenance_phash_regen ( hash_id INTEGER PRIMARY KEY );' )
self._c.execute( 'CREATE TABLE external_caches.shape_maintenance_branch_regen ( phash_id INTEGER PRIMARY KEY );' )
self._c.execute( 'CREATE TABLE external_caches.shape_search_cache ( hash_id INTEGER PRIMARY KEY, searched_distance INTEGER );' )
self._c.execute( 'CREATE TABLE external_caches.duplicate_pairs ( smaller_hash_id INTEGER, larger_hash_id INTEGER, duplicate_type INTEGER, PRIMARY KEY ( smaller_hash_id, larger_hash_id ) );' )
self._CreateIndex( 'external_caches.duplicate_pairs', [ 'larger_hash_id', 'smaller_hash_id' ], unique = True )
self._c.execute( 'CREATE TABLE external_caches.integer_subtags ( subtag_id INTEGER PRIMARY KEY, integer_subtag INTEGER );' )
self._CreateIndex( 'external_caches.integer_subtags', [ 'integer_subtag' ] )
self._CreateDBCaches()
# master
@ -2934,7 +2910,7 @@ class DB( HydrusDB.HydrusDB ):
self._c.executemany( 'INSERT INTO yaml_dumps VALUES ( ?, ?, ? );', ( ( YAML_DUMP_ID_IMAGEBOARD, name, imageboards ) for ( name, imageboards ) in ClientDefaults.GetDefaultImageboards() ) )
new_options = ClientData.ClientOptions( self._db_dir )
new_options = ClientOptions.ClientOptions( self._db_dir )
new_options.SetSimpleDownloaderFormulae( ClientDefaults.GetDefaultSimpleDownloaderFormulae() )
@ -2970,6 +2946,28 @@ class DB( HydrusDB.HydrusDB ):
self._c.executemany( 'INSERT INTO json_dumps_named VALUES ( ?, ?, ?, ? );', ClientDefaults.GetDefaultScriptRows() )
def _CreateDBCaches( self ):
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_caches.shape_perceptual_hashes ( phash_id INTEGER PRIMARY KEY, phash BLOB_BYTES UNIQUE );' )
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_caches.shape_perceptual_hash_map ( phash_id INTEGER, hash_id INTEGER, PRIMARY KEY ( phash_id, hash_id ) );' )
self._CreateIndex( 'external_caches.shape_perceptual_hash_map', [ 'hash_id' ] )
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_caches.shape_vptree ( phash_id INTEGER PRIMARY KEY, parent_id INTEGER, radius INTEGER, inner_id INTEGER, inner_population INTEGER, outer_id INTEGER, outer_population INTEGER );' )
self._CreateIndex( 'external_caches.shape_vptree', [ 'parent_id' ] )
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_caches.shape_maintenance_phash_regen ( hash_id INTEGER PRIMARY KEY );' )
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_caches.shape_maintenance_branch_regen ( phash_id INTEGER PRIMARY KEY );' )
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_caches.shape_search_cache ( hash_id INTEGER PRIMARY KEY, searched_distance INTEGER );' )
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_caches.duplicate_pairs ( smaller_hash_id INTEGER, larger_hash_id INTEGER, duplicate_type INTEGER, PRIMARY KEY ( smaller_hash_id, larger_hash_id ) );' )
self._CreateIndex( 'external_caches.duplicate_pairs', [ 'larger_hash_id', 'smaller_hash_id' ], unique = True )
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_caches.integer_subtags ( subtag_id INTEGER PRIMARY KEY, integer_subtag INTEGER );' )
self._CreateIndex( 'external_caches.integer_subtags', [ 'integer_subtag' ] )
def _DeleteFiles( self, service_id, hash_ids ):
# the gui sometimes gets out of sync and sends a DELETE FROM TRASH call before the SEND TO TRASH call
@ -3451,6 +3449,23 @@ class DB( HydrusDB.HydrusDB ):
return hashes_result
def _GenerateMappingsTables( self, service_id ):
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateMappingsTableNames( service_id )
self._c.execute( 'CREATE TABLE IF NOT EXISTS ' + current_mappings_table_name + ' ( tag_id INTEGER, hash_id INTEGER, PRIMARY KEY ( tag_id, hash_id ) ) WITHOUT ROWID;' )
self._CreateIndex( current_mappings_table_name, [ 'hash_id', 'tag_id' ], unique = True )
self._c.execute( 'CREATE TABLE IF NOT EXISTS ' + deleted_mappings_table_name + ' ( tag_id INTEGER, hash_id INTEGER, PRIMARY KEY ( tag_id, hash_id ) ) WITHOUT ROWID;' )
self._CreateIndex( deleted_mappings_table_name, [ 'hash_id', 'tag_id' ], unique = True )
self._c.execute( 'CREATE TABLE IF NOT EXISTS ' + pending_mappings_table_name + ' ( tag_id INTEGER, hash_id INTEGER, PRIMARY KEY ( tag_id, hash_id ) ) WITHOUT ROWID;' )
self._CreateIndex( pending_mappings_table_name, [ 'hash_id', 'tag_id' ], unique = True )
self._c.execute( 'CREATE TABLE IF NOT EXISTS ' + petitioned_mappings_table_name + ' ( tag_id INTEGER, hash_id INTEGER, reason_id INTEGER, PRIMARY KEY ( tag_id, hash_id ) ) WITHOUT ROWID;' )
self._CreateIndex( petitioned_mappings_table_name, [ 'hash_id', 'tag_id' ], unique = True )
def _GetAutocompleteCounts( self, tag_service_id, file_service_id, tag_ids, include_current, include_pending ):
if tag_service_id == self._combined_tag_service_id:
@ -4840,11 +4855,11 @@ class DB( HydrusDB.HydrusDB ):
search_tag_service_ids = [ self._GetServiceId( tag_service_key ) ]
current_selects = []
pending_selects = []
for tag in tags:
current_selects = []
pending_selects = []
( namespace, subtag ) = HydrusTags.SplitTag( tag )
if namespace != '':
@ -6982,6 +6997,8 @@ class DB( HydrusDB.HydrusDB ):
try:
next_stop_time_presentation = 0
paths = [ os.path.join( self._db_dir, filename ) for filename in self._db_filenames.values() ]
paths.sort( key = os.path.getsize )
@ -6994,11 +7011,16 @@ class DB( HydrusDB.HydrusDB ):
if stop_time is not None:
HG.client_controller.pub( 'splash_set_status_subtext', HydrusData.ConvertTimestampToPrettyPending( stop_time, prefix = '' ) )
if HydrusData.TimeHasPassed( stop_time ):
if HydrusData.TimeHasPassed( next_stop_time_presentation ):
return False
HG.client_controller.pub( 'splash_set_status_subtext', HydrusData.ConvertTimestampToPrettyPending( stop_time, prefix = '' ) )
if HydrusData.TimeHasPassed( stop_time ):
return False
next_stop_time_presentation = HydrusData.GetNow() + 1
@ -7183,7 +7205,18 @@ class DB( HydrusDB.HydrusDB ):
if sub_action == 'delete_deleted':
self._c.execute( 'DELETE FROM deleted_files WHERE service_id = ?;', ( service_id, ) )
hashes = sub_row
if hashes is None:
self._c.execute( 'DELETE FROM deleted_files WHERE service_id = ?;', ( service_id, ) )
else:
hash_ids = self._GetHashIds( hashes )
self._c.executemany( 'DELETE FROM deleted_files WHERE service_id = ? AND hash_id = ?;', ( ( service_id, hash_id ) for hash_id in hash_ids ) )
self._c.execute( 'DELETE FROM service_info WHERE service_id = ?;', ( service_id, ) )
@ -7219,7 +7252,7 @@ class DB( HydrusDB.HydrusDB ):
hash_ids = self._GetHashIds( hashes )
self._c.executemany( 'INSERT OR IGNORE INTO file_transfers ( service_id, hash_id ) VALUES ( ?, ? );', [ ( service_id, hash_id ) for hash_id in hash_ids ] )
self._c.executemany( 'INSERT OR IGNORE INTO file_transfers ( service_id, hash_id ) VALUES ( ?, ? );', ( ( service_id, hash_id ) for hash_id in hash_ids ) )
if service_key == CC.COMBINED_LOCAL_FILE_SERVICE_KEY: notify_new_downloads = True
else: notify_new_pending = True
@ -7234,7 +7267,7 @@ class DB( HydrusDB.HydrusDB ):
self._c.execute( 'DELETE FROM file_petitions WHERE service_id = ? AND hash_id IN ' + HydrusData.SplayListForDB( hash_ids ) + ';', ( service_id, ) )
self._c.executemany( 'INSERT OR IGNORE INTO file_petitions ( service_id, hash_id, reason_id ) VALUES ( ?, ?, ? );', [ ( service_id, hash_id, reason_id ) for hash_id in hash_ids ] )
self._c.executemany( 'INSERT OR IGNORE INTO file_petitions ( service_id, hash_id, reason_id ) VALUES ( ?, ?, ? );', ( ( service_id, hash_id, reason_id ) for hash_id in hash_ids ) )
notify_new_pending = True
@ -8491,7 +8524,10 @@ class DB( HydrusDB.HydrusDB ):
HydrusPaths.MakeSureDirectoryExists( full_abs_correct_location )
self._c.execute( 'UPDATE client_files_locations SET location = ? WHERE location = ? AND prefix = ?;', ( portable_correct_location, portable_incorrect_location, prefix ) )
if portable_correct_location != portable_incorrect_location:
self._c.execute( 'UPDATE client_files_locations SET location = ? WHERE location = ? AND prefix = ?;', ( portable_correct_location, portable_incorrect_location, prefix ) )
@ -8499,75 +8535,142 @@ class DB( HydrusDB.HydrusDB ):
HydrusDB.HydrusDB._RepairDB( self )
cache_table_names = self._STS( self._c.execute( 'SELECT name FROM external_caches.sqlite_master WHERE type = "table";' ) )
tag_service_ids = self._GetServiceIds( HC.TAG_SERVICES )
file_service_ids = self._GetServiceIds( HC.AUTOCOMPLETE_CACHE_SPECIFIC_FILE_SERVICES )
expected_table_names = { 'shape_perceptual_hashes', 'shape_perceptual_hash_map', 'shape_vptree', 'shape_maintenance_phash_regen', 'shape_maintenance_branch_regen', 'shape_search_cache', 'duplicate_pairs', 'integer_subtags' }
missing_table_names = expected_table_names.difference( cache_table_names )
repository_service_ids = self._GetServiceIds( HC.REPOSITORIES )
if len( missing_table_names ) == 0:
# master
existing_master_tables = self._STS( self._c.execute( 'SELECT name FROM external_master.sqlite_master WHERE type = ?;', ( 'table', ) ) )
main_master_tables = set()
main_master_tables.add( 'hashes' )
main_master_tables.add( 'local_hashes' )
main_master_tables.add( 'namespaces' )
main_master_tables.add( 'subtags' )
main_master_tables.add( 'tags' )
main_master_tables.add( 'texts' )
missing_main_tables = main_master_tables.difference( existing_master_tables )
if len( missing_main_tables ) > 0:
return
message = 'On boot, some required master tables were missing. This could be due to the entire \'master\' database file being missing or due to some other problem. Critical data is missing, so the client cannot boot! The exact missing tables were:'
message += os.linesep * 2
message += os.linesep.join( missing_main_tables )
message += os.linesep * 2
message += 'The boot will fail once you click ok. If you do not know what happened and how to fix this, please take a screenshot and contact hydrus dev.'
wx.MessageBox( message )
raise Exception( 'Master database was invalid!' )
if 'shape_perceptual_hashes' in missing_table_names:
# mappings
existing_mapping_tables = self._STS( self._c.execute( 'SELECT name FROM external_mappings.sqlite_master WHERE type = ?;', ( 'table', ) ) )
main_mappings_tables = set()
for service_id in tag_service_ids:
self._c.execute( 'CREATE TABLE external_caches.shape_perceptual_hashes ( phash_id INTEGER PRIMARY KEY, phash BLOB_BYTES UNIQUE );' )
main_mappings_tables.update( ( name.split( '.' )[1] for name in GenerateMappingsTableNames( service_id ) ) )
if 'shape_perceptual_hash_map' in missing_table_names:
missing_main_tables = main_mappings_tables.difference( existing_mapping_tables )
if len( missing_main_tables ) > 0:
self._c.execute( 'CREATE TABLE external_caches.shape_perceptual_hash_map ( phash_id INTEGER, hash_id INTEGER, PRIMARY KEY ( phash_id, hash_id ) );' )
self._CreateIndex( 'external_caches.shape_perceptual_hash_map', [ 'hash_id' ] )
missing_main_tables = list( missing_main_tables )
missing_main_tables.sort()
message = 'On boot, some important mappings tables were missing! This could be due to the entire \'mappings\' database file being missing or due to some other problem. The tags in these tables are lost. The exact missing tables were:'
message += os.linesep * 2
message += os.linesep.join( missing_main_tables )
message += os.linesep * 2
message += 'If you wish, click ok on this message and the client will recreate these tables--empty, without data--which should at least let the client boot. If the affected tag service(s) are tag repositories, you will want to reset the processing cache so the client can repopulate the tables from your cached update files. But if you want to solve this problem otherwise, kill the hydrus process now.'
message += os.linesep * 2
message += 'If you do not already know what caused this, it was likely a hard drive fault--either due to a recent abrupt power cut or actual hardware failure. Check \'help my db is broke.txt\' in the install_dir/db directory as soon as you can.'
wx.MessageBox( message )
for service_id in tag_service_ids:
self._GenerateMappingsTables( service_id )
if 'shape_vptree' in missing_table_names:
# caches
existing_cache_tables = self._STS( self._c.execute( 'SELECT name FROM external_caches.sqlite_master WHERE type = ?;', ( 'table', ) ) )
main_cache_tables = set()
main_cache_tables.add( 'shape_perceptual_hashes' )
main_cache_tables.add( 'shape_perceptual_hash_map' )
main_cache_tables.add( 'shape_vptree' )
main_cache_tables.add( 'shape_maintenance_phash_regen' )
main_cache_tables.add( 'shape_maintenance_branch_regen' )
main_cache_tables.add( 'shape_search_cache' )
main_cache_tables.add( 'duplicate_pairs' )
main_cache_tables.add( 'integer_subtags' )
missing_main_tables = main_cache_tables.difference( existing_cache_tables )
if len( missing_main_tables ) > 0:
self._c.execute( 'CREATE TABLE external_caches.shape_vptree ( phash_id INTEGER PRIMARY KEY, parent_id INTEGER, radius INTEGER, inner_id INTEGER, inner_population INTEGER, outer_id INTEGER, outer_population INTEGER );' )
self._CreateIndex( 'external_caches.shape_vptree', [ 'parent_id' ] )
missing_main_tables = list( missing_main_tables )
missing_main_tables.sort()
message = 'On boot, some important caches tables were missing! This could be due to the entire \'caches\' database file being missing or due to some other problem. Data related to duplicate file search may have been lost. The exact missing tables were:'
message += os.linesep * 2
message += os.linesep.join( missing_main_tables )
message += os.linesep * 2
message += 'If you wish, click ok on this message and the client will recreate these tables--empty, without data--which should at least let the client boot. But if you want to solve this problem otherwise, kill the hydrus process now.'
message += os.linesep * 2
message += 'If you do not already know what caused this, it was likely a hard drive fault--either due to a recent abrupt power cut or actual hardware failure. Check \'help my db is broke.txt\' in the install_dir/db directory as soon as you can.'
wx.MessageBox( message )
self._CreateDBCaches()
if 'shape_maintenance_phash_regen' in missing_table_names:
mappings_cache_tables = set()
for ( file_service_id, tag_service_id ) in itertools.product( file_service_ids, tag_service_ids ):
self._c.execute( 'CREATE TABLE external_caches.shape_maintenance_phash_regen ( hash_id INTEGER PRIMARY KEY );' )
mappings_cache_tables.update( ( name.split( '.' )[1] for name in GenerateSpecificMappingsCacheTableNames( file_service_id, tag_service_id ) ) )
if 'shape_maintenance_branch_regen' in missing_table_names:
for tag_service_id in tag_service_ids:
self._c.execute( 'CREATE TABLE external_caches.shape_maintenance_branch_regen ( phash_id INTEGER PRIMARY KEY );' )
mappings_cache_tables.add( GenerateCombinedFilesMappingsCacheTableName( tag_service_id ).split( '.' )[1] )
if 'shape_search_cache' in missing_table_names:
self._c.execute( 'CREATE TABLE external_caches.shape_search_cache ( hash_id INTEGER PRIMARY KEY, searched_distance INTEGER );' )
missing_main_tables = mappings_cache_tables.difference( existing_cache_tables )
if 'duplicate_pairs' in missing_table_names:
if len( missing_main_tables ) > 0:
self._c.execute( 'CREATE TABLE external_caches.duplicate_pairs ( smaller_hash_id INTEGER, larger_hash_id INTEGER, duplicate_type INTEGER, PRIMARY KEY ( smaller_hash_id, larger_hash_id ) );' )
self._CreateIndex( 'external_caches.duplicate_pairs', [ 'larger_hash_id', 'smaller_hash_id' ], unique = True )
missing_main_tables = list( missing_main_tables )
if 'integer_subtags' in missing_table_names:
missing_main_tables.sort()
self._c.execute( 'CREATE TABLE external_caches.integer_subtags ( subtag_id INTEGER PRIMARY KEY, integer_subtag INTEGER );' )
self._CreateIndex( 'external_caches.integer_subtags', [ 'integer_subtag' ] )
message = 'On boot, some mapping caches tables were missing! This could be due to the entire \'caches\' database file being missing or due to some other problem. All of this data can be regenerated. The exact missing tables were:'
message += os.linesep * 2
message += os.linesep.join( missing_main_tables )
message += os.linesep * 2
message += 'If you wish, click ok on this message and the client will recreate and repopulate these tables with the correct data. This may take a few minutes. But if you want to solve this problem otherwise, kill the hydrus process now.'
message += os.linesep * 2
message += 'If you do not already know what caused this, it was likely a hard drive fault--either due to a recent abrupt power cut or actual hardware failure. Check \'help my db is broke.txt\' in the install_dir/db directory as soon as you can.'
wx.MessageBox( message )
self._RegenerateACCache()
missing_table_names = list( missing_table_names )
missing_table_names.sort()
message = 'On boot, some cache tables were missing from the database! This could mean your hard drive has a serious problem!'
message += os.linesep * 2
message += 'The missing tables were:'
message += os.linesep * 2
message += os.linesep.join( missing_table_names )
message += os.linesep * 2
message += 'They have been recreated but are now empty. It may be possible to regenerate their contents through the database maintenance menus.'
message += os.linesep * 2
message += 'Please check that your hard drive and system are otherwise healthy. If you have a working recent backup, you may wish to restore to it as there may be other faults with your database. You might also like to contact hydrus dev or read \'help my db is broke.txt\' in the install_dir/db directory as background reading.'
self.pub_initial_message( message )
def _ReparseFiles( self, hashes ):
@ -8997,116 +9100,116 @@ class DB( HydrusDB.HydrusDB ):
if version == 250:
duplicate_filter = ClientData.Shortcuts( 'duplicate_filter' )
duplicate_filter = ClientGUIShortcuts.Shortcuts( 'duplicate_filter' )
duplicate_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_this_is_better' ) )
duplicate_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_not_dupes' ) )
duplicate_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_RIGHT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_alternates' ) )
duplicate_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_MIDDLE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_exactly_the_same' ) )
duplicate_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_RIGHT, [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_skip' ) )
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_this_is_better' ) )
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_not_dupes' ) )
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_RIGHT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_alternates' ) )
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_MIDDLE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_exactly_the_same' ) )
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_RIGHT, [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_skip' ) )
duplicate_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_SPACE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_this_is_better' ) )
duplicate_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_skip' ) )
duplicate_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_skip' ) )
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_SPACE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_this_is_better' ) )
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_skip' ) )
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_skip' ) )
self._SetJSONDump( duplicate_filter )
if version == 251:
duplicate_filter = ClientData.Shortcuts( 'duplicate_filter' )
duplicate_filter = ClientGUIShortcuts.Shortcuts( 'duplicate_filter' )
duplicate_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_this_is_better' ) )
duplicate_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_RIGHT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_alternates' ) )
duplicate_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_MIDDLE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_back' ) )
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_this_is_better' ) )
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_RIGHT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_alternates' ) )
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_MIDDLE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_back' ) )
duplicate_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_SPACE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_this_is_better' ) )
duplicate_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_skip' ) )
duplicate_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_skip' ) )
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_SPACE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_this_is_better' ) )
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_skip' ) )
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_skip' ) )
self._SetJSONDump( duplicate_filter )
media = ClientData.Shortcuts( 'media' )
media = ClientGUIShortcuts.Shortcuts( 'media' )
media.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F4, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'manage_file_ratings' ) )
media.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F3, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'manage_file_tags' ) )
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F4, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'manage_file_ratings' ) )
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F3, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'manage_file_tags' ) )
media.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F7, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_file' ) )
media.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F7, [ CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'inbox_file' ) )
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F7, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_file' ) )
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F7, [ CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'inbox_file' ) )
media.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'E' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'open_file_in_external_program' ) )
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'E' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'open_file_in_external_program' ) )
media.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'R' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'remove_file_from_view' ) )
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'R' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'remove_file_from_view' ) )
media.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F12, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'launch_the_archive_delete_filter' ) )
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F12, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'launch_the_archive_delete_filter' ) )
self._SetJSONDump( media )
main_gui = ClientData.Shortcuts( 'main_gui' )
main_gui = ClientGUIShortcuts.Shortcuts( 'main_gui' )
main_gui.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F5, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'refresh' ) )
main_gui.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F9, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'new_page' ) )
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F5, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'refresh' ) )
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F9, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'new_page' ) )
main_gui.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'I' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'synchronised_wait_switch' ) )
main_gui.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'M' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'set_media_focus' ) )
main_gui.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'R' ), [ CC.SHORTCUT_MODIFIER_CTRL, CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'show_hide_splitters' ) )
main_gui.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'S' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'set_search_focus' ) )
main_gui.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'T' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'new_page' ) )
main_gui.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'U' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'unclose_page' ) )
main_gui.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'W' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'close_page' ) )
main_gui.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'Y' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'redo' ) )
main_gui.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'Z' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'undo' ) )
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'I' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'synchronised_wait_switch' ) )
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'M' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'set_media_focus' ) )
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'R' ), [ CC.SHORTCUT_MODIFIER_CTRL, CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'show_hide_splitters' ) )
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'S' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'set_search_focus' ) )
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'T' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'new_page' ) )
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'U' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'unclose_page' ) )
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'W' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'close_page' ) )
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'Y' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'redo' ) )
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'Z' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'undo' ) )
self._SetJSONDump( main_gui )
media_viewer_browser = ClientData.Shortcuts( 'media_viewer_browser' )
media_viewer_browser = ClientGUIShortcuts.Shortcuts( 'media_viewer_browser' )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_LEFT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_LEFT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_PAGEUP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_PAGEUP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_LEFT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_LEFT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_PAGEUP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_PAGEUP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_SCROLL_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_SCROLL_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_DOWN, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_RIGHT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_RIGHT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_DOWN, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_PAGEDOWN, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_PAGEDOWN, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_DOWN, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_RIGHT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_RIGHT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_DOWN, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_PAGEDOWN, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_PAGEDOWN, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_SCROLL_DOWN, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_SCROLL_DOWN, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_HOME, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_first' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_HOME, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_first' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_HOME, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_first' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_HOME, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_first' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_END, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_last' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_END, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_last' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_END, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_last' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_END, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_last' ) )
self._SetJSONDump( media_viewer_browser )
media_viewer = ClientData.Shortcuts( 'media_viewer' )
media_viewer = ClientGUIShortcuts.Shortcuts( 'media_viewer' )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'B' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'move_animation_to_previous_frame' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'N' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'move_animation_to_next_frame' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'B' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'move_animation_to_previous_frame' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'N' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'move_animation_to_next_frame' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'F' ), [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'switch_between_fullscreen_borderless_and_regular_framed_window' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'F' ), [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'switch_between_fullscreen_borderless_and_regular_framed_window' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'Z' ), [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'switch_between_100_percent_and_canvas_zoom' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_ADD, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_in' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_ADD, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_in' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_SUBTRACT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_out' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_SUBTRACT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_out' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'Z' ), [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'switch_between_100_percent_and_canvas_zoom' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_ADD, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_in' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_ADD, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_in' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_SUBTRACT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_out' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_SUBTRACT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_out' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_SCROLL_UP, [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_in' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_SCROLL_DOWN, [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_out' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_SCROLL_UP, [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_in' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_SCROLL_DOWN, [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_out' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_UP, [ CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'pan_up' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_DOWN, [ CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'pan_down' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_LEFT, [ CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'pan_left' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_RIGHT, [ CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'pan_right' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_UP, [ CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'pan_up' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_DOWN, [ CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'pan_down' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_LEFT, [ CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'pan_left' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_RIGHT, [ CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'pan_right' ) )
self._SetJSONDump( media_viewer )
@ -9151,19 +9254,19 @@ class DB( HydrusDB.HydrusDB ):
if version == 254:
archive_delete_filter = ClientData.Shortcuts( 'archive_delete_filter' )
archive_delete_filter = ClientGUIShortcuts.Shortcuts( 'archive_delete_filter' )
archive_delete_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_keep' ) )
archive_delete_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_RIGHT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_delete' ) )
archive_delete_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_MIDDLE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_back' ) )
archive_delete_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_keep' ) )
archive_delete_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_RIGHT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_delete' ) )
archive_delete_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_MIDDLE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_back' ) )
archive_delete_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_SPACE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_keep' ) )
archive_delete_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F7, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_keep' ) )
archive_delete_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_DELETE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_delete' ) )
archive_delete_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_DELETE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_delete' ) )
archive_delete_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_BACK, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_back' ) )
archive_delete_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_skip' ) )
archive_delete_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_skip' ) )
archive_delete_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_SPACE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_keep' ) )
archive_delete_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F7, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_keep' ) )
archive_delete_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_DELETE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_delete' ) )
archive_delete_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_DELETE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_delete' ) )
archive_delete_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_BACK, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_back' ) )
archive_delete_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_skip' ) )
archive_delete_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_skip' ) )
self._SetJSONDump( archive_delete_filter )
@ -10169,6 +10272,36 @@ class DB( HydrusDB.HydrusDB ):
self.pub_initial_message( message )
if version == 306:
try:
domain_manager = self._GetJSONDump( HydrusSerialisable.SERIALISABLE_TYPE_NETWORK_DOMAIN_MANAGER )
domain_manager.Initialise()
#
domain_manager.OverwriteDefaultParsers( ( 'e621 file page parser', 'gelbooru 0.2.5 file page parser' ) )
#
domain_manager.TryToLinkURLMatchesAndParsers()
#
self._SetJSONDump( domain_manager )
except Exception as e:
HydrusData.PrintException( e )
message = 'Trying to update some parsers failed! Please let hydrus dev know!'
self.pub_initial_message( message )
self._controller.pub( 'splash_set_title_text', 'updated db to v' + str( version + 1 ) )
self._c.execute( 'UPDATE version SET version = ?;', ( version + 1, ) )

View File

@ -1,4 +1,3 @@
import ClientData
import ClientImporting
import ClientImportOptions
import ClientPaths

File diff suppressed because it is too large Load Diff

View File

@ -646,119 +646,121 @@ def GetDefaultScriptRows():
def GetDefaultShortcuts():
import ClientGUIShortcuts
shortcuts = []
archive_delete_filter = ClientData.Shortcuts( 'archive_delete_filter' )
archive_delete_filter = ClientGUIShortcuts.Shortcuts( 'archive_delete_filter' )
archive_delete_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_keep' ) )
archive_delete_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_RIGHT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_delete' ) )
archive_delete_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_MIDDLE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_back' ) )
archive_delete_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_keep' ) )
archive_delete_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_RIGHT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_delete' ) )
archive_delete_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_MIDDLE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_back' ) )
archive_delete_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_SPACE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_keep' ) )
archive_delete_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F7, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_keep' ) )
archive_delete_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_DELETE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_delete' ) )
archive_delete_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_DELETE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_delete' ) )
archive_delete_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_BACK, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_back' ) )
archive_delete_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_skip' ) )
archive_delete_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_skip' ) )
archive_delete_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_SPACE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_keep' ) )
archive_delete_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F7, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_keep' ) )
archive_delete_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_DELETE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_delete' ) )
archive_delete_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_DELETE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_delete' ) )
archive_delete_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_BACK, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_back' ) )
archive_delete_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_skip' ) )
archive_delete_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_skip' ) )
shortcuts.append( archive_delete_filter )
duplicate_filter = ClientData.Shortcuts( 'duplicate_filter' )
duplicate_filter = ClientGUIShortcuts.Shortcuts( 'duplicate_filter' )
duplicate_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_this_is_better' ) )
duplicate_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_RIGHT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_alternates' ) )
duplicate_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_MIDDLE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_back' ) )
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_this_is_better' ) )
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_RIGHT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_alternates' ) )
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_MIDDLE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_back' ) )
duplicate_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_SPACE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_this_is_better' ) )
duplicate_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_skip' ) )
duplicate_filter.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_skip' ) )
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_SPACE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_this_is_better' ) )
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_skip' ) )
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_skip' ) )
shortcuts.append( duplicate_filter )
media = ClientData.Shortcuts( 'media' )
media = ClientGUIShortcuts.Shortcuts( 'media' )
media.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F4, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'manage_file_ratings' ) )
media.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F3, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'manage_file_tags' ) )
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F4, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'manage_file_ratings' ) )
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F3, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'manage_file_tags' ) )
media.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F7, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_file' ) )
media.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F7, [ CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'inbox_file' ) )
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F7, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_file' ) )
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F7, [ CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'inbox_file' ) )
media.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'E' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'open_file_in_external_program' ) )
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'E' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'open_file_in_external_program' ) )
media.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'R' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'remove_file_from_view' ) )
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'R' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'remove_file_from_view' ) )
media.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F12, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'launch_the_archive_delete_filter' ) )
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F12, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'launch_the_archive_delete_filter' ) )
media.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'C' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'copy_file' ) )
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'C' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'copy_file' ) )
shortcuts.append( media )
main_gui = ClientData.Shortcuts( 'main_gui' )
main_gui = ClientGUIShortcuts.Shortcuts( 'main_gui' )
main_gui.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F5, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'refresh' ) )
main_gui.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F9, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'new_page' ) )
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F5, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'refresh' ) )
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F9, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'new_page' ) )
main_gui.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'I' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'synchronised_wait_switch' ) )
main_gui.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'M' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'set_media_focus' ) )
main_gui.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'R' ), [ CC.SHORTCUT_MODIFIER_CTRL, CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'show_hide_splitters' ) )
main_gui.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'S' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'set_search_focus' ) )
main_gui.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'T' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'new_page' ) )
main_gui.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'U' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'unclose_page' ) )
main_gui.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'W' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'close_page' ) )
main_gui.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'Y' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'redo' ) )
main_gui.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'Z' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'undo' ) )
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'I' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'synchronised_wait_switch' ) )
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'M' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'set_media_focus' ) )
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'R' ), [ CC.SHORTCUT_MODIFIER_CTRL, CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'show_hide_splitters' ) )
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'S' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'set_search_focus' ) )
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'T' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'new_page' ) )
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'U' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'unclose_page' ) )
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'W' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'close_page' ) )
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'Y' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'redo' ) )
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'Z' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'undo' ) )
shortcuts.append( main_gui )
media_viewer_browser = ClientData.Shortcuts( 'media_viewer_browser' )
media_viewer_browser = ClientGUIShortcuts.Shortcuts( 'media_viewer_browser' )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_LEFT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_LEFT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_PAGEUP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_PAGEUP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_LEFT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_LEFT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_PAGEUP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_PAGEUP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_SCROLL_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_SCROLL_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_DOWN, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_RIGHT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_RIGHT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_DOWN, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_PAGEDOWN, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_PAGEDOWN, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_DOWN, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_RIGHT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_RIGHT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_DOWN, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_PAGEDOWN, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_PAGEDOWN, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_SCROLL_DOWN, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_SCROLL_DOWN, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_HOME, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_first' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_HOME, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_first' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_HOME, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_first' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_HOME, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_first' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_END, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_last' ) )
media_viewer_browser.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_END, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_last' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_END, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_last' ) )
media_viewer_browser.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_END, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_last' ) )
shortcuts.append( media_viewer_browser )
media_viewer = ClientData.Shortcuts( 'media_viewer' )
media_viewer = ClientGUIShortcuts.Shortcuts( 'media_viewer' )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'B' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'move_animation_to_previous_frame' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'N' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'move_animation_to_next_frame' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'B' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'move_animation_to_previous_frame' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'N' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'move_animation_to_next_frame' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'F' ), [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'switch_between_fullscreen_borderless_and_regular_framed_window' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'F' ), [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'switch_between_fullscreen_borderless_and_regular_framed_window' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'Z' ), [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'switch_between_100_percent_and_canvas_zoom' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_ADD, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_in' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_ADD, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_in' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_SUBTRACT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_out' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_SUBTRACT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_out' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'Z' ), [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'switch_between_100_percent_and_canvas_zoom' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_ADD, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_in' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_ADD, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_in' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_SUBTRACT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_out' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_SUBTRACT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_out' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_SCROLL_UP, [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_in' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_SCROLL_DOWN, [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_out' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_SCROLL_UP, [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_in' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_SCROLL_DOWN, [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_out' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_UP, [ CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'pan_up' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_DOWN, [ CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'pan_down' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_LEFT, [ CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'pan_left' ) )
media_viewer.SetCommand( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_RIGHT, [ CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'pan_right' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_UP, [ CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'pan_up' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_DOWN, [ CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'pan_down' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_LEFT, [ CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'pan_left' ) )
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_RIGHT, [ CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'pan_right' ) )
shortcuts.append( media_viewer )

View File

@ -1,5 +1,4 @@
import bs4
import ClientData
import ClientNetworkingDomain
import ClientNetworkingJobs
import ClientParsing

393
include/ClientDuplicates.py Normal file
View File

@ -0,0 +1,393 @@
import ClientConstants as CC
import HydrusConstants as HC
import HydrusData
import HydrusExceptions
import HydrusGlobals as HG
import HydrusSerialisable
class DuplicateActionOptions( HydrusSerialisable.SerialisableBase ):
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_DUPLICATE_ACTION_OPTIONS
SERIALISABLE_NAME = 'Duplicate Action Options'
SERIALISABLE_VERSION = 3
def __init__( self, tag_service_actions = None, rating_service_actions = None, delete_second_file = False, sync_archive = False, delete_both_files = False, sync_urls_action = None ):
if tag_service_actions is None:
tag_service_actions = []
if rating_service_actions is None:
rating_service_actions = []
HydrusSerialisable.SerialisableBase.__init__( self )
self._tag_service_actions = tag_service_actions
self._rating_service_actions = rating_service_actions
self._delete_second_file = delete_second_file
self._sync_archive = sync_archive
self._delete_both_files = delete_both_files
self._sync_urls_action = sync_urls_action
def _GetSerialisableInfo( self ):
if HG.client_controller.IsBooted():
services_manager = HG.client_controller.services_manager
self._tag_service_actions = [ ( service_key, action, tag_filter ) for ( service_key, action, tag_filter ) in self._tag_service_actions if services_manager.ServiceExists( service_key ) and services_manager.GetServiceType( service_key ) in ( HC.LOCAL_TAG, HC.TAG_REPOSITORY ) ]
self._rating_service_actions = [ ( service_key, action ) for ( service_key, action ) in self._rating_service_actions if services_manager.ServiceExists( service_key ) and services_manager.GetServiceType( service_key ) in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ) ]
serialisable_tag_service_actions = [ ( service_key.encode( 'hex' ), action, tag_filter.GetSerialisableTuple() ) for ( service_key, action, tag_filter ) in self._tag_service_actions ]
serialisable_rating_service_actions = [ ( service_key.encode( 'hex' ), action ) for ( service_key, action ) in self._rating_service_actions ]
return ( serialisable_tag_service_actions, serialisable_rating_service_actions, self._delete_second_file, self._sync_archive, self._delete_both_files, self._sync_urls_action )
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
( serialisable_tag_service_actions, serialisable_rating_service_actions, self._delete_second_file, self._sync_archive, self._delete_both_files, self._sync_urls_action ) = serialisable_info
self._tag_service_actions = [ ( serialisable_service_key.decode( 'hex' ), action, HydrusSerialisable.CreateFromSerialisableTuple( serialisable_tag_filter ) ) for ( serialisable_service_key, action, serialisable_tag_filter ) in serialisable_tag_service_actions ]
self._rating_service_actions = [ ( serialisable_service_key.decode( 'hex' ), action ) for ( serialisable_service_key, action ) in serialisable_rating_service_actions ]
def _UpdateSerialisableInfo( self, version, old_serialisable_info ):
if version == 1:
( serialisable_service_actions, delete_second_file ) = old_serialisable_info
tag_service_actions = []
rating_service_actions = []
# As the client isn't booted when this is loaded in options, there isn't a good way to figure out tag from rating
# So, let's just dupe and purge later on, in serialisation
for ( service_key_encoded, action ) in serialisable_service_actions:
service_key = service_key_encoded.decode( 'hex' )
import ClientTags
tag_filter = ClientTags.TagFilter()
tag_service_actions.append( ( service_key, action, tag_filter ) )
rating_service_actions.append( ( service_key, action ) )
serialisable_tag_service_actions = [ ( service_key.encode( 'hex' ), action, tag_filter.GetSerialisableTuple() ) for ( service_key, action, tag_filter ) in tag_service_actions ]
serialisable_rating_service_actions = [ ( service_key.encode( 'hex' ), action ) for ( service_key, action ) in rating_service_actions ]
sync_archive = delete_second_file
delete_both_files = False
new_serialisable_info = ( serialisable_tag_service_actions, serialisable_rating_service_actions, delete_second_file, sync_archive, delete_both_files )
return ( 2, new_serialisable_info )
if version == 2:
( serialisable_tag_service_actions, serialisable_rating_service_actions, delete_second_file, sync_archive, delete_both_files ) = old_serialisable_info
sync_urls_action = None
new_serialisable_info = ( serialisable_tag_service_actions, serialisable_rating_service_actions, delete_second_file, sync_archive, delete_both_files, sync_urls_action )
return ( 3, new_serialisable_info )
def GetDeletedHashes( self, first_media, second_media ):
first_hashes = first_media.GetHashes()
second_hashes = second_media.GetHashes()
if self._delete_second_file:
return second_hashes
elif self._delete_both_files:
return first_hashes.union( second_hashes )
else:
return set()
def SetTuple( self, tag_service_actions, rating_service_actions, delete_second_file, sync_archive, delete_both_files, sync_urls_action ):
self._tag_service_actions = tag_service_actions
self._rating_service_actions = rating_service_actions
self._delete_second_file = delete_second_file
self._sync_archive = sync_archive
self._delete_both_files = delete_both_files
self._sync_urls_action = sync_urls_action
def ToTuple( self ):
return ( self._tag_service_actions, self._rating_service_actions, self._delete_second_file, self._sync_archive, self._delete_both_files, self._sync_urls_action )
def ProcessPairIntoContentUpdates( self, first_media, second_media ):
list_of_service_keys_to_content_updates = []
first_hashes = first_media.GetHashes()
second_hashes = second_media.GetHashes()
#
service_keys_to_content_updates = {}
services_manager = HG.client_controller.services_manager
for ( service_key, action, tag_filter ) in self._tag_service_actions:
content_updates = []
try:
service = services_manager.GetService( service_key )
except HydrusExceptions.DataMissing:
continue
service_type = service.GetServiceType()
if service_type == HC.LOCAL_TAG:
add_content_action = HC.CONTENT_UPDATE_ADD
elif service_type == HC.TAG_REPOSITORY:
add_content_action = HC.CONTENT_UPDATE_PEND
first_current_tags = first_media.GetTagsManager().GetCurrent( service_key )
second_current_tags = second_media.GetTagsManager().GetCurrent( service_key )
first_current_tags = tag_filter.Filter( first_current_tags )
second_current_tags = tag_filter.Filter( second_current_tags )
if action == HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE:
first_needs = second_current_tags.difference( first_current_tags )
second_needs = first_current_tags.difference( second_current_tags )
content_updates.extend( ( HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, add_content_action, ( tag, first_hashes ) ) for tag in first_needs ) )
content_updates.extend( ( HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, add_content_action, ( tag, second_hashes ) ) for tag in second_needs ) )
elif action == HC.CONTENT_MERGE_ACTION_COPY:
first_needs = second_current_tags.difference( first_current_tags )
content_updates.extend( ( HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, add_content_action, ( tag, first_hashes ) ) for tag in first_needs ) )
elif service_type == HC.LOCAL_TAG and action == HC.CONTENT_MERGE_ACTION_MOVE:
first_needs = second_current_tags.difference( first_current_tags )
content_updates.extend( ( HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, add_content_action, ( tag, first_hashes ) ) for tag in first_needs ) )
content_updates.extend( ( HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_DELETE, ( tag, second_hashes ) ) for tag in second_current_tags ) )
if len( content_updates ) > 0:
service_keys_to_content_updates[ service_key ] = content_updates
for ( service_key, action ) in self._rating_service_actions:
content_updates = []
try:
service = services_manager.GetService( service_key )
except HydrusExceptions.DataMissing:
continue
first_current_value = first_media.GetRatingsManager().GetRating( service_key )
second_current_value = second_media.GetRatingsManager().GetRating( service_key )
if action == HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE:
if first_current_value == second_current_value:
continue
if first_current_value is None and second_current_value is not None:
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( second_current_value, first_hashes ) ) )
elif first_current_value is not None and second_current_value is None:
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( first_current_value, second_hashes ) ) )
elif action == HC.CONTENT_MERGE_ACTION_COPY:
if first_current_value == second_current_value:
continue
if first_current_value is None and second_current_value is not None:
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( second_current_value, first_hashes ) ) )
elif action == HC.CONTENT_MERGE_ACTION_MOVE:
if second_current_value is not None:
if first_current_value is None:
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( second_current_value, first_hashes ) ) )
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( None, second_hashes ) ) )
if len( content_updates ) > 0:
service_keys_to_content_updates[ service_key ] = content_updates
if len( service_keys_to_content_updates ) > 0:
list_of_service_keys_to_content_updates.append( service_keys_to_content_updates )
#
service_keys_to_content_updates = {}
if self._sync_archive:
if first_media.HasInbox() and second_media.HasArchive():
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_ARCHIVE, first_hashes )
service_keys_to_content_updates[ CC.COMBINED_LOCAL_FILE_SERVICE_KEY ] = [ content_update ]
elif first_media.HasArchive() and second_media.HasInbox():
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_ARCHIVE, second_hashes )
service_keys_to_content_updates[ CC.COMBINED_LOCAL_FILE_SERVICE_KEY ] = [ content_update ]
if len( service_keys_to_content_updates ) > 0:
list_of_service_keys_to_content_updates.append( service_keys_to_content_updates )
#
if self._sync_urls_action is not None:
first_urls = set( first_media.GetLocationsManager().GetURLs() )
second_urls = set( second_media.GetLocationsManager().GetURLs() )
content_updates = []
if self._sync_urls_action == HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE:
first_needs = second_urls.difference( first_urls )
second_needs = first_urls.difference( second_urls )
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_URLS, HC.CONTENT_UPDATE_ADD, ( first_needs, first_hashes ) ) )
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_URLS, HC.CONTENT_UPDATE_ADD, ( second_needs, second_hashes ) ) )
elif self._sync_urls_action == HC.CONTENT_MERGE_ACTION_COPY:
first_needs = second_urls.difference( first_urls )
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_URLS, HC.CONTENT_UPDATE_ADD, ( first_needs, first_hashes ) ) )
if len( content_updates ) > 0:
service_keys_to_content_updates = { CC.COMBINED_LOCAL_FILE_SERVICE_KEY : content_updates }
list_of_service_keys_to_content_updates.append( service_keys_to_content_updates )
#
service_keys_to_content_updates = {}
deletee_media = []
if self._delete_second_file or self._delete_both_files:
if self._delete_both_files:
deletee_media.append( first_media )
deletee_media.append( second_media )
for media in deletee_media:
current_locations = media.GetLocationsManager().GetCurrent()
if CC.LOCAL_FILE_SERVICE_KEY in current_locations:
deletee_service_key = CC.LOCAL_FILE_SERVICE_KEY
elif CC.TRASH_SERVICE_KEY in current_locations:
deletee_service_key = CC.TRASH_SERVICE_KEY
else:
deletee_service_key = None
if deletee_service_key is not None:
if deletee_service_key not in service_keys_to_content_updates:
service_keys_to_content_updates[ deletee_service_key ] = []
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_DELETE, media.GetHashes() )
service_keys_to_content_updates[ deletee_service_key ].append( content_update )
if len( service_keys_to_content_updates ) > 0:
list_of_service_keys_to_content_updates.append( service_keys_to_content_updates )
#
return list_of_service_keys_to_content_updates
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_DUPLICATE_ACTION_OPTIONS ] = DuplicateActionOptions

View File

@ -1,5 +1,5 @@
import ClientConstants as CC
import ClientData
import ClientPaths
import ClientSearch
import HydrusConstants as HC
import HydrusData
@ -357,7 +357,7 @@ class ExportFolder( HydrusSerialisable.SerialisableBaseNamed ):
deletee_path = os.path.join( folder_path, deletee_filename )
ClientData.DeletePath( deletee_path )
ClientPaths.DeletePath( deletee_path )
if len( deletee_filenames ) > 0:

View File

@ -77,10 +77,10 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
ClientGUITopLevelWindows.FrameThatResizes.__init__( self, None, title, 'main_gui', float_on_parent = False )
bandwidth_width = ClientData.ConvertTextToPixelWidth( self, 17 )
idle_width = ClientData.ConvertTextToPixelWidth( self, 6 )
system_busy_width = ClientData.ConvertTextToPixelWidth( self, 13 )
db_width = ClientData.ConvertTextToPixelWidth( self, 14 )
bandwidth_width = ClientGUICommon.ConvertTextToPixelWidth( self, 17 )
idle_width = ClientGUICommon.ConvertTextToPixelWidth( self, 6 )
system_busy_width = ClientGUICommon.ConvertTextToPixelWidth( self, 13 )
db_width = ClientGUICommon.ConvertTextToPixelWidth( self, 14 )
stb_style = wx.STB_SIZEGRIP | wx.STB_ELLIPSIZE_END | wx.FULL_REPAINT_ON_RESIZE
@ -126,6 +126,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
self._controller.sub( self, 'NewPageImportHDD', 'new_hdd_import' )
self._controller.sub( self, 'NewPageQuery', 'new_page_query' )
self._controller.sub( self, 'NotifyClosedPage', 'notify_closed_page' )
self._controller.sub( self, 'NotifyDeletedPage', 'notify_deleted_page' )
self._controller.sub( self, 'NotifyNewImportFolders', 'notify_new_import_folders' )
self._controller.sub( self, 'NotifyNewOptions', 'notify_new_options' )
self._controller.sub( self, 'NotifyNewPages', 'notify_new_pages' )
@ -971,7 +972,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
page.CleanBeforeDestroy()
page.Destroy()
page.DestroyLater()
@ -1499,7 +1500,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
ClientGUIMenus.AppendMenuItem( self, submenu, 'autocomplete cache', 'Delete and recreate the tag autocomplete cache, fixing any miscounts.', self._RegenerateACCache )
ClientGUIMenus.AppendMenuItem( self, submenu, 'similar files search metadata', 'Delete and recreate the similar files search phashes.', self._RegenerateSimilarFilesPhashes )
ClientGUIMenus.AppendMenuItem( self, submenu, 'similar files search tree', 'Delete and recreate the similar files search tree.', self._RegenerateSimilarFilesTree )
ClientGUIMenus.AppendMenuItem( self, submenu, 'all thumbnails', 'Delete all thumbnails and regenerate them from their original files.', self._RegenerateThumbnails )
ClientGUIMenus.AppendMenuItem( self, submenu, 'missing/all thumbnails', 'Delete missing/all thumbnails and regenerate them from their original files.', self._RegenerateThumbnails )
ClientGUIMenus.AppendMenu( menu, submenu, 'regenerate' )
@ -1849,6 +1850,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
ClientGUIMenus.AppendMenuItem( self, debug, 'make some popups', 'Throw some varied popups at the message manager, just to check it is working.', self._DebugMakeSomePopups )
ClientGUIMenus.AppendMenuItem( self, debug, 'make a popup in five seconds', 'Throw a delayed popup at the message manager, giving you time to minimise or otherwise alter the client before it arrives.', self._controller.CallLater, 5, HydrusData.ShowText, 'This is a delayed popup message.' )
ClientGUIMenus.AppendMenuItem( self, debug, 'make a modal popup in five seconds', 'Throw up a delayed modal popup to test with. It will stay alive for five seconds.', self._DebugMakeDelayedModalPopup )
ClientGUIMenus.AppendMenuItem( self, debug, 'make a new page in five seconds', 'Throw a delayed page at the main notebook, giving you time to minimise or otherwise alter the client before it arrives.', self._controller.CallLater, 5, self._controller.pub, 'new_page_query', CC.LOCAL_FILE_SERVICE_KEY )
ClientGUIMenus.AppendMenuItem( self, debug, 'make a parentless text ctrl dialog', 'Make a parentless text control in a dialog to test some character event catching.', self._DebugMakeParentlessTextCtrl )
ClientGUIMenus.AppendMenuItem( self, debug, 'force a gui layout now', 'Tell the gui to relayout--useful to test some gui bootup layout issues.', self.Layout )
ClientGUIMenus.AppendMenuItem( self, debug, 'force a layout for all non-gui tlws now', 'Tell all sub-frames to relayout--useful to test some layout issues.', self._ForceLayoutAllNonGUITLWs )
@ -3833,14 +3835,12 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
else:
self._controller.CallLaterWXSafe( self, 2, self.Destroy )
self._controller.CreateSplash()
wx.CallAfter( self._controller.Exit )
self.Destroy()
self.DestroyLater()
return True
@ -3985,9 +3985,23 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
if url_type == HC.URL_TYPE_WATCHABLE:
self._notebook.NewPageImportThreadWatcher( url, on_deepest_notebook = True )
return
if self._new_options.GetBoolean( 'use_multiple_watcher_for_drag_and_drops' ):
page = self._notebook.GetOrMakeMultipleWatcherPage()
if page is not None:
self._notebook.ShowPage( page )
page_key = page.GetPageKey()
HG.client_controller.pub( 'pend_url', page_key, url )
else:
self._notebook.NewPageImportThreadWatcher( url, on_deepest_notebook = True )
@ -4048,6 +4062,20 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
self._menu_updater.Update()
def NotifyDeletedPage( self, page ):
if self._notebook.GetNumPages() == 0:
self._focus_holder.SetFocus()
self._DestroyPages( ( page, ) )
self._DirtyMenu( 'pages' )
self._menu_updater.Update()
def NotifyNewImportFolders( self ):
self._DirtyMenu( 'file' )
@ -4129,6 +4157,18 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
def PresentImportedFilesToPage( self, hashes, page_name ):
tlp = ClientGUICommon.GetTLP( self )
if tlp.IsIconized(): # initial layout is bugged if the new page is added during minimised, so let's delay it here
if not self._notebook.HasMediaPageName( page_name ):
self._controller.CallLaterWXSafe( self, 5.0, self.PresentImportedFilesToPage, hashes, page_name )
return
dest_page = self._notebook.PresentImportedFilesToPage( hashes, page_name )

View File

@ -4,6 +4,7 @@ import ClientData
import ClientGUICommon
import ClientGUIListBoxes
import ClientGUIMenus
import ClientGUIShortcuts
import ClientSearch
import collections
import HydrusConstants as HC
@ -281,7 +282,7 @@ class AutoCompleteDropdown( wx.Panel ):
if self._move_hide_job is None:
self._move_hide_job = HG.client_controller.CallRepeatingWXSafe( self._dropdown_window, 0.25, 0.0, self._DropdownHideShow )
self._move_hide_job = HG.client_controller.CallRepeatingWXSafe( self._dropdown_window, 0.0, 0.25, self._DropdownHideShow )
self._move_hide_job.Delay( 0.25 )
@ -298,7 +299,7 @@ class AutoCompleteDropdown( wx.Panel ):
if self._refresh_list_job is not None and delay == 0.0:
self._refresh_list_job.MoveNextWorkTimeToNow()
self._refresh_list_job.Wake()
else:
@ -427,7 +428,7 @@ class AutoCompleteDropdown( wx.Panel ):
HG.client_controller.ResetIdleTimer()
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
if key in ( wx.WXK_INSERT, wx.WXK_NUMPAD_INSERT ):
@ -696,6 +697,18 @@ class AutoCompleteDropdown( wx.Panel ):
self._favourites_list.SetPredicates( predicates )
def SetFocus( self ):
if HC.PLATFORM_OSX:
wx.CallAfter( self._text_ctrl.SetFocus )
else:
self._text_ctrl.SetFocus()
class AutoCompleteDropdownTags( AutoCompleteDropdown ):
def __init__( self, parent, file_service_key, tag_service_key ):
@ -772,15 +785,6 @@ class AutoCompleteDropdownTags( AutoCompleteDropdown ):
self._current_matches = matches
num_chars = len( self._text_ctrl.GetValue() )
if num_chars == 0:
# refresh system preds after five mins
self._ScheduleListRefresh( 300 )
def FileButtonHit( self ):
@ -1039,8 +1043,6 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
matches = []
else:
( namespace, half_complete_subtag ) = HydrusTags.SplitTag( search_text )
@ -1215,6 +1217,20 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
self._BroadcastCurrentText()
def _UpdateSearchResultsList( self ):
AutoCompleteDropdownTags._UpdateSearchResultsList( self )
num_chars = len( self._text_ctrl.GetValue() )
if num_chars == 0:
# refresh system preds after five mins
self._ScheduleListRefresh( 300 )
def GetFileSearchContext( self ):
return self._file_search_context

View File

@ -1012,7 +1012,7 @@ class CanvasFrame( ClientGUITopLevelWindows.FrameThatResizes ):
self.ShowFullScreen( False, wx.FULLSCREEN_ALL )
self.Destroy()
self.DestroyLater()
def FullscreenSwitch( self ):
@ -1532,7 +1532,7 @@ class Canvas( wx.Window ):
control = wx.TextCtrl( panel, style = wx.TE_MULTILINE )
size = ClientData.ConvertTextToPixels( control, ( 80, 14 ) )
size = ClientGUICommon.ConvertTextToPixels( control, ( 80, 14 ) )
control.SetInitialSize( size )
@ -2041,7 +2041,7 @@ class Canvas( wx.Window ):
if self._CanProcessInput() and not self._FocusIsElsewhere(): # focus is likely on a tag manager frame in this case
shortcut = ClientData.ConvertKeyEventToShortcut( event )
shortcut = ClientGUIShortcuts.ConvertKeyEventToShortcut( event )
if shortcut is not None:
@ -2647,7 +2647,7 @@ class CanvasWithDetails( Canvas ):
tags_i_want_to_display = list( tags_i_want_to_display )
ClientData.SortTagsList( tags_i_want_to_display, HC.options[ 'default_tag_sort' ] )
ClientTags.SortTagsList( tags_i_want_to_display, HC.options[ 'default_tag_sort' ] )
current_y = 3
@ -3568,7 +3568,7 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
if self._CanProcessInput() and not self._FocusIsElsewhere():
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
if key in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER, wx.WXK_ESCAPE ):
@ -3576,7 +3576,7 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
else:
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
if modifier == wx.ACCEL_NORMAL and key in CC.DELETE_KEYS:
@ -3634,7 +3634,7 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
shortcut = ClientData.ConvertMouseEventToShortcut( event )
shortcut = ClientGUIShortcuts.ConvertMouseEventToShortcut( event )
if shortcut is not None:
@ -4255,7 +4255,7 @@ class CanvasMediaListFilterArchiveDelete( CanvasMediaList ):
if self._CanProcessInput() and not self._FocusIsElsewhere():
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
if modifier == wx.ACCEL_NORMAL and key in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER, wx.WXK_ESCAPE ): self._Close()
else:
@ -4312,7 +4312,7 @@ class CanvasMediaListFilterArchiveDelete( CanvasMediaList ):
shortcut = ClientData.ConvertMouseEventToShortcut( event )
shortcut = ClientGUIShortcuts.ConvertMouseEventToShortcut( event )
if shortcut is not None:
@ -4693,7 +4693,7 @@ class CanvasMediaListBrowser( CanvasMediaListNavigable ):
if self._CanProcessInput() and not self._FocusIsElsewhere():
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
if modifier == wx.ACCEL_NORMAL and key in CC.DELETE_KEYS: self._Delete()
elif modifier == wx.ACCEL_SHIFT and key in CC.DELETE_KEYS: self._Undelete()
@ -4959,21 +4959,9 @@ class MediaContainer( wx.Window ):
def _DestroyThisMediaWindow( self, media_window ):
def wx_destroy( media_window ):
if not media_window:
return
media_window.Destroy()
if media_window is not None:
media_window.Hide()
wx.CallAfter( wx_destroy, media_window )
media_window.DestroyLater()

View File

@ -227,6 +227,18 @@ def ApplyContentApplicationCommandToMedia( parent, command, media ):
return True
def ConvertTextToPixels( window, ( char_cols, char_rows ) ):
dialog_units = ( char_cols * 4, char_rows * 8 )
return tuple( window.ConvertDialogToPixels( dialog_units ) ) # convert from _Point_ to a tuple that size methods can deal with
def ConvertTextToPixelWidth( window, char_cols ):
( width, height ) = ConvertTextToPixels( window, ( char_cols, 1 ) )
return width
def GetFocusTLP():
focus = wx.Window.FindFocus()
@ -565,6 +577,38 @@ class BetterChoice( wx.Choice ):
class BetterNotebook( wx.Notebook ):
def _ShiftSelection( self, delta ):
existing_selection = self.GetSelection()
if existing_selection != wx.NOT_FOUND:
new_selection = ( existing_selection + delta ) % self.GetPageCount()
if new_selection != existing_selection:
self.SetSelection( new_selection )
def GetPages( self ):
return [ self.GetPage( i ) for i in range( self.GetPageCount() ) ]
def SelectLeft( self ):
self._ShiftSelection( -1 )
def SelectRight( self ):
self._ShiftSelection( 1 )
class BetterRadioBox( wx.RadioBox ):
def __init__( self, *args, **kwargs ):
@ -960,7 +1004,7 @@ class ChoiceSort( wx.Panel ):
self._sort_type_choice = BetterChoice( self )
self._sort_asc_choice = BetterChoice( self )
asc_width = ClientData.ConvertTextToPixelWidth( self._sort_asc_choice, 15 )
asc_width = ConvertTextToPixelWidth( self._sort_asc_choice, 15 )
self._sort_asc_choice.SetMinSize( ( asc_width, -1 ) )
@ -973,7 +1017,7 @@ class ChoiceSort( wx.Panel ):
self._sort_type_choice.Append( example_sort.GetSortTypeString(), sort_type )
type_width = ClientData.ConvertTextToPixelWidth( self._sort_type_choice, 10 )
type_width = ConvertTextToPixelWidth( self._sort_type_choice, 10 )
self._sort_type_choice.SetMinSize( ( type_width, -1 ) )
@ -1573,7 +1617,7 @@ class ListBook( wx.Panel ):
self._panel_sizer.Detach( page_to_delete )
page_to_delete.Destroy()
page_to_delete.DestroyLater()
del self._keys_to_active_pages[ key_to_delete ]
@ -1881,7 +1925,7 @@ class NoneableSpinCtrl( wx.Panel ):
self._one = wx.SpinCtrl( self, min = min, max = max )
width = ClientData.ConvertTextToPixelWidth( self._one, len( str( max ) ) + 5 )
width = ConvertTextToPixelWidth( self._one, len( str( max ) ) + 5 )
self._one.SetInitialSize( ( width, -1 ) )
@ -1889,7 +1933,7 @@ class NoneableSpinCtrl( wx.Panel ):
self._two = wx.SpinCtrl( self, initial = 0, min = min, max = max )
width = ClientData.ConvertTextToPixelWidth( self._two, len( str( max ) ) + 5 )
width = ConvertTextToPixelWidth( self._two, len( str( max ) ) + 5 )
self._two.SetInitialSize( ( width, -1 ) )
@ -2780,168 +2824,6 @@ class SaneMultilineTextCtrl( wx.TextCtrl ):
class Shortcut( wx.Panel ):
def __init__( self, parent ):
wx.Panel.__init__( self, parent )
self._mouse_radio = wx.RadioButton( self, style = wx.RB_GROUP, label = 'mouse' )
self._mouse_shortcut = ShortcutMouse( self, self._mouse_radio )
self._keyboard_radio = wx.RadioButton( self, label = 'keyboard' )
self._keyboard_shortcut = ShortcutKeyboard( self, self._keyboard_radio )
#
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.Add( BetterStaticText( self, 'Mouse events only work for the duplicate and archive/delete filters atm!' ), CC.FLAGS_EXPAND_PERPENDICULAR )
gridbox = wx.FlexGridSizer( 2 )
gridbox.AddGrowableCol( 1, 1 )
gridbox.Add( self._mouse_radio, CC.FLAGS_VCENTER )
gridbox.Add( self._mouse_shortcut, CC.FLAGS_EXPAND_BOTH_WAYS )
gridbox.Add( self._keyboard_radio, CC.FLAGS_VCENTER )
gridbox.Add( self._keyboard_shortcut, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.Add( gridbox, CC.FLAGS_EXPAND_BOTH_WAYS )
self.SetSizer( vbox )
def GetValue( self ):
if self._mouse_radio.GetValue() == True:
return self._mouse_shortcut.GetValue()
else:
return self._keyboard_shortcut.GetValue()
def SetValue( self, shortcut ):
if shortcut.GetShortcutType() == CC.SHORTCUT_TYPE_MOUSE:
self._mouse_radio.SetValue( True )
self._mouse_shortcut.SetValue( shortcut )
else:
self._keyboard_radio.SetValue( True )
self._keyboard_shortcut.SetValue( shortcut )
class ShortcutKeyboard( wx.TextCtrl ):
def __init__( self, parent, related_radio = None ):
self._shortcut = ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F7, [] )
self._related_radio = related_radio
wx.TextCtrl.__init__( self, parent, style = wx.TE_PROCESS_ENTER )
self.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
self._SetShortcutString()
def _SetShortcutString( self ):
display_string = self._shortcut.ToString()
wx.TextCtrl.SetValue( self, display_string )
def EventKeyDown( self, event ):
shortcut = ClientData.ConvertKeyEventToShortcut( event )
if shortcut is not None:
self._shortcut = shortcut
if self._related_radio is not None:
self._related_radio.SetValue( True )
self._SetShortcutString()
def GetValue( self ):
return self._shortcut
def SetValue( self, shortcut ):
self._shortcut = shortcut
self._SetShortcutString()
class ShortcutMouse( wx.Button ):
def __init__( self, parent, related_radio = None ):
self._shortcut = ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [] )
self._related_radio = related_radio
wx.Button.__init__( self, parent )
self.Bind( wx.EVT_MOUSE_EVENTS, self.EventMouse )
self._SetShortcutString()
def _SetShortcutString( self ):
display_string = self._shortcut.ToString()
self.SetLabel( display_string )
def EventMouse( self, event ):
self.SetFocus()
shortcut = ClientData.ConvertMouseEventToShortcut( event )
if shortcut is not None:
self._shortcut = shortcut
if self._related_radio is not None:
self._related_radio.SetValue( True )
self._SetShortcutString()
def GetValue( self ):
return self._shortcut
def SetValue( self, shortcut ):
self._shortcut = shortcut
self._SetShortcutString()
class StaticBox( wx.Panel ):
def __init__( self, parent, title ):
@ -3112,80 +2994,6 @@ class TextAndGauge( wx.Panel ):
self._gauge.SetValue( value )
class TextAndPasteCtrl( wx.Panel ):
def __init__( self, parent, add_callable ):
self._add_callable = add_callable
wx.Panel.__init__( self, parent )
self._text_input = wx.TextCtrl( self, style = wx.TE_PROCESS_ENTER )
self._text_input.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
self._paste_button = BetterBitmapButton( self, CC.GlobalBMPs.paste, self._Paste )
self._paste_button.SetToolTip( 'Paste multiple inputs from the clipboard. Assumes the texts are newline-separated.' )
#
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.Add( self._text_input, CC.FLAGS_EXPAND_BOTH_WAYS )
hbox.Add( self._paste_button, CC.FLAGS_VCENTER )
self.SetSizer( hbox )
def _Paste( self ):
raw_text = HG.client_controller.GetClipboardText()
try:
texts = [ text for text in HydrusText.DeserialiseNewlinedTexts( raw_text ) if text != '' ]
if len( texts ) > 0:
self._add_callable( texts )
except:
wx.MessageBox( 'I could not understand what was in the clipboard' )
def EventKeyDown( self, event ):
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
if key in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ):
text = self._text_input.GetValue()
if text != '':
self._add_callable( ( text, ) )
self._text_input.SetValue( '' )
else:
event.Skip()
def GetValue( self ):
return self._text_input.GetValue()
def SetValue( self, text ):
self._text_input.SetValue( text )
class ThreadToGUIUpdater( object ):
def __init__( self, win, func ):

View File

@ -6,12 +6,14 @@ import ClientGUIDialogs
import ClientGUIListCtrl
import ClientGUIMenus
import ClientGUIScrolledPanels
import ClientGUIShortcuts
import ClientGUITime
import ClientGUITopLevelWindows
import HydrusConstants as HC
import HydrusData
import HydrusGlobals as HG
import HydrusNetworking
import HydrusText
import os
import wx
@ -239,7 +241,7 @@ class BytesControl( wx.Panel ):
self._spin = wx.SpinCtrl( self, min = 0, max = 1048576 )
width = ClientData.ConvertTextToPixelWidth( self._spin, 12 )
width = ClientGUICommon.ConvertTextToPixelWidth( self._spin, 12 )
self._spin.SetSize( ( width, -1 ) )
@ -542,7 +544,7 @@ class NetworkJobControl( wx.Panel ):
self._right_text = ClientGUICommon.BetterStaticText( self, style = wx.ALIGN_RIGHT )
# 512/768KB - 200KB/s
right_width = ClientData.ConvertTextToPixelWidth( self._right_text, 20 )
right_width = ClientGUICommon.ConvertTextToPixelWidth( self._right_text, 20 )
self._right_text.SetMinSize( ( right_width, -1 ) )
@ -797,3 +799,77 @@ class StringToStringDictButton( ClientGUICommon.BetterButton ):
self._value = value
class TextAndPasteCtrl( wx.Panel ):
def __init__( self, parent, add_callable ):
self._add_callable = add_callable
wx.Panel.__init__( self, parent )
self._text_input = wx.TextCtrl( self, style = wx.TE_PROCESS_ENTER )
self._text_input.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
self._paste_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.paste, self._Paste )
self._paste_button.SetToolTip( 'Paste multiple inputs from the clipboard. Assumes the texts are newline-separated.' )
#
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.Add( self._text_input, CC.FLAGS_EXPAND_BOTH_WAYS )
hbox.Add( self._paste_button, CC.FLAGS_VCENTER )
self.SetSizer( hbox )
def _Paste( self ):
raw_text = HG.client_controller.GetClipboardText()
try:
texts = [ text for text in HydrusText.DeserialiseNewlinedTexts( raw_text ) if text != '' ]
if len( texts ) > 0:
self._add_callable( texts )
except:
wx.MessageBox( 'I could not understand what was in the clipboard' )
def EventKeyDown( self, event ):
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
if key in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ):
text = self._text_input.GetValue()
if text != '':
self._add_callable( ( text, ) )
self._text_input.SetValue( '' )
else:
event.Skip()
def GetValue( self ):
return self._text_input.GetValue()
def SetValue( self, text ):
self._text_input.SetValue( text )

View File

@ -14,6 +14,7 @@ import ClientGUIImport
import ClientGUIListBoxes
import ClientGUIListCtrl
import ClientGUIPredicates
import ClientGUIShortcuts
import ClientGUITime
import ClientGUITopLevelWindows
import ClientImporting
@ -608,7 +609,7 @@ class DialogInputFileSystemPredicates( Dialog ):
def EventCharHook( self, event ):
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
if key in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ):

View File

@ -3475,7 +3475,7 @@ class DialogManageTagCensorship( ClientGUIDialogs.Dialog ):
def EventKeyDownTag( self, event ):
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
if key in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ):

View File

@ -3,6 +3,7 @@ import ClientConstants as CC
import ClientData
import ClientGUICommon
import ClientGUIMenus
import ClientGUIShortcuts
import ClientSearch
import ClientSerialisable
import ClientTags
@ -55,7 +56,7 @@ class AddEditDeleteListBox( wx.Panel ):
#
( width, height ) = ClientData.ConvertTextToPixels( self._listbox, ( 20, height_num_chars ) )
( width, height ) = ClientGUICommon.ConvertTextToPixels( self._listbox, ( 20, height_num_chars ) )
self._listbox.SetInitialSize( ( width, height ) )
@ -574,7 +575,7 @@ class QueueListBox( wx.Panel ):
#
( width, height ) = ClientData.ConvertTextToPixels( self._listbox, ( 20, height_num_chars ) )
( width, height ) = ClientGUICommon.ConvertTextToPixels( self._listbox, ( 20, height_num_chars ) )
self._listbox.SetInitialSize( ( width, height ) )
@ -2518,7 +2519,7 @@ class ListBoxTagsStringsAddRemove( ListBoxTagsStrings ):
def EventCharHook( self, event ):
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
if key in CC.DELETE_KEYS:

View File

@ -2,6 +2,7 @@ import ClientConstants as CC
import ClientData
import ClientGUICommon
import ClientSerialisable
import ClientGUIShortcuts
import HydrusData
import HydrusExceptions
import HydrusGlobals as HG
@ -130,7 +131,7 @@ class SaneListCtrl( wx.ListCtrl, ListCtrlAutoWidthMixin ):
def EventKeyDown( self, event ):
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
if key in CC.DELETE_KEYS:
@ -510,7 +511,7 @@ class BetterListCtrl( wx.ListCtrl, ListCtrlAutoWidthMixin ):
self._indices_to_data_info = {}
self._data_to_indices = {}
( total_width, height ) = ClientData.ConvertTextToPixels( self, ( sizing_column_initial_width_num_chars, height_num_chars ) )
( total_width, height ) = ClientGUICommon.ConvertTextToPixels( self, ( sizing_column_initial_width_num_chars, height_num_chars ) )
resize_column = 1
@ -524,7 +525,7 @@ class BetterListCtrl( wx.ListCtrl, ListCtrlAutoWidthMixin ):
else:
width = ClientData.ConvertTextToPixelWidth( self, width_num_chars )
width = ClientGUICommon.ConvertTextToPixelWidth( self, width_num_chars )
total_width += width
@ -756,7 +757,7 @@ class BetterListCtrl( wx.ListCtrl, ListCtrlAutoWidthMixin ):
def EventKeyDown( self, event ):
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
if key in CC.DELETE_KEYS:
@ -1314,3 +1315,7 @@ class BetterListCtrlPanel( wx.Panel ):
self._listctrl.Bind( wx.EVT_LIST_DELETE_ALL_ITEMS, self.EventContentChanged )
def UpdateButtons( self ):
self._UpdateButtons()

View File

@ -16,12 +16,14 @@ import ClientGUIControls
import ClientGUIDialogs
import ClientGUIImport
import ClientGUIListBoxes
import ClientGUIListCtrl
import ClientGUIMedia
import ClientGUIMenus
import ClientGUIParsing
import ClientGUIScrolledPanels
import ClientGUIScrolledPanelsEdit
import ClientGUISeedCache
import ClientGUIShortcuts
import ClientGUITime
import ClientGUITopLevelWindows
import ClientImporting
@ -61,6 +63,7 @@ MANAGEMENT_TYPE_PETITIONS = 5
MANAGEMENT_TYPE_QUERY = 6
MANAGEMENT_TYPE_IMPORT_URLS = 7
MANAGEMENT_TYPE_DUPLICATE_FILTER = 8
MANAGEMENT_TYPE_IMPORT_MULTIPLE_WATCHER = 9
management_panel_types_to_classes = {}
@ -142,6 +145,16 @@ def CreateManagementControllerImportThreadWatcher( thread_url = None ):
return management_controller
def CreateManagementControllerImportMultipleWatcher( thread_url = None ):
management_controller = CreateManagementController( 'multiple watcher', MANAGEMENT_TYPE_IMPORT_MULTIPLE_WATCHER )
multiple_watcher_import = ClientImporting.MultipleWatcherImport( thread_url = thread_url )
management_controller.SetVariable( 'multiple_watcher_import', multiple_watcher_import )
return management_controller
def CreateManagementControllerImportURLs():
management_controller = CreateManagementController( 'url import', MANAGEMENT_TYPE_IMPORT_URLS )
@ -413,7 +426,7 @@ def GenerateDumpMultipartFormDataCTAndBody( fields ):
def EventKeyDown( self, event ):
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
if key in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ): self.EventReady( None )
else: event.Skip()
@ -720,6 +733,12 @@ class ManagementController( HydrusSerialisable.SerialisableBase ):
return simple_downloader_import.GetValueRange()
elif self._management_type == MANAGEMENT_TYPE_IMPORT_MULTIPLE_WATCHER:
multiple_watcher_import = self._serialisables[ 'multiple_watcher_import' ]
return multiple_watcher_import.GetValueRange()
elif self._management_type == MANAGEMENT_TYPE_IMPORT_THREAD_WATCHER:
thread_watcher_import = self._serialisables[ 'thread_watcher_import' ]
@ -772,7 +791,7 @@ class ManagementController( HydrusSerialisable.SerialisableBase ):
def IsImporter( self ):
return self._management_type in ( MANAGEMENT_TYPE_IMPORT_GALLERY, MANAGEMENT_TYPE_IMPORT_HDD, MANAGEMENT_TYPE_IMPORT_SIMPLE_DOWNLOADER, MANAGEMENT_TYPE_IMPORT_THREAD_WATCHER, MANAGEMENT_TYPE_IMPORT_URLS )
return self._management_type in ( MANAGEMENT_TYPE_IMPORT_GALLERY, MANAGEMENT_TYPE_IMPORT_HDD, MANAGEMENT_TYPE_IMPORT_SIMPLE_DOWNLOADER, MANAGEMENT_TYPE_IMPORT_THREAD_WATCHER, MANAGEMENT_TYPE_IMPORT_MULTIPLE_WATCHER, MANAGEMENT_TYPE_IMPORT_URLS )
def SetKey( self, name, key ):
@ -826,8 +845,6 @@ class ManagementPanel( wx.lib.scrolledpanel.ScrolledPanel ):
self._collect_by = ClientGUICommon.CheckboxCollect( self, self._page_key )
self._controller.sub( self, 'SetSearchFocus', 'set_search_focus' )
def _MakeCurrentSelectionTagsBox( self, sizer ):
@ -860,7 +877,7 @@ class ManagementPanel( wx.lib.scrolledpanel.ScrolledPanel ):
pass
def SetSearchFocus( self, page_key = None ):
def SetSearchFocus( self ):
pass
@ -1433,7 +1450,7 @@ class ManagementPanelImporterGallery( ManagementPanelImporter ):
self._delay_button = wx.Button( self._pending_queries_panel, label = u'\u2193' )
self._delay_button.Bind( wx.EVT_BUTTON, self.EventDelay )
self._query_input = ClientGUICommon.TextAndPasteCtrl( self._pending_queries_panel, self._PendQueries )
self._query_input = ClientGUIControls.TextAndPasteCtrl( self._pending_queries_panel, self._PendQueries )
self._file_limit = ClientGUICommon.NoneableSpinCtrl( self._gallery_downloader_panel, 'stop after this many files', min = 1, none_phrase = 'no limit' )
self._file_limit.Bind( wx.EVT_SPINCTRL, self.EventFileLimit )
@ -1699,12 +1716,7 @@ class ManagementPanelImporterGallery( ManagementPanelImporter ):
self._UpdateStatus()
def SetSearchFocus( self, page_key = None ):
if page_key is not None and page_key != self._page_key:
return
def SetSearchFocus( self ):
wx.CallAfter( self._query_input.SetFocus )
@ -1859,7 +1871,7 @@ class ManagementPanelImporterSimpleDownloader( ManagementPanelImporter ):
self._delay_button = wx.Button( self._pending_jobs_panel, label = u'\u2193' )
self._delay_button.Bind( wx.EVT_BUTTON, self.EventDelay )
self._page_url_input = ClientGUICommon.TextAndPasteCtrl( self._pending_jobs_panel, self._PendPageURLs )
self._page_url_input = ClientGUIControls.TextAndPasteCtrl( self._pending_jobs_panel, self._PendPageURLs )
self._formulae = ClientGUICommon.BetterChoice( self._pending_jobs_panel )
@ -2232,12 +2244,7 @@ class ManagementPanelImporterSimpleDownloader( ManagementPanelImporter ):
self._UpdateStatus()
def SetSearchFocus( self, page_key = None ):
if page_key is not None and page_key != self._page_key:
return
def SetSearchFocus( self ):
wx.CallAfter( self._page_url_input.SetFocus )
@ -2529,7 +2536,7 @@ class ManagementPanelImporterThreadWatcher( ManagementPanelImporter ):
def EventKeyDown( self, event ):
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
if key in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ):
@ -2544,7 +2551,9 @@ class ManagementPanelImporterThreadWatcher( ManagementPanelImporter ):
self._thread_watcher_import.SetThreadURL( thread_url )
self._thread_watcher_import.Start( self._page_key )
publish_to_page = True
self._thread_watcher_import.Start( self._page_key, publish_to_page )
else:
@ -2552,12 +2561,7 @@ class ManagementPanelImporterThreadWatcher( ManagementPanelImporter ):
def SetSearchFocus( self, page_key = None ):
if page_key is not None and page_key != self._page_key:
return
def SetSearchFocus( self ):
wx.CallAfter( self._thread_input.SetFocus )
@ -2566,12 +2570,281 @@ class ManagementPanelImporterThreadWatcher( ManagementPanelImporter ):
if self._thread_watcher_import.HasThread():
self._thread_watcher_import.Start( self._page_key )
publish_to_page = True
self._thread_watcher_import.Start( self._page_key, publish_to_page )
management_panel_types_to_classes[ MANAGEMENT_TYPE_IMPORT_THREAD_WATCHER ] = ManagementPanelImporterThreadWatcher
class ManagementPanelImporterMultipleWatcher( ManagementPanelImporter ):
def __init__( self, parent, page, controller, management_controller ):
ManagementPanelImporter.__init__( self, parent, page, controller, management_controller )
self._last_thread_keys = set()
self._next_update_time = 0
self._highlit_watcher = None
#
self._watchers_panel = ClientGUICommon.StaticBox( self, 'watchers' )
self._watchers_listctrl_panel = ClientGUIListCtrl.BetterListCtrlPanel( self._watchers_panel )
self._watchers_listctrl = ClientGUIListCtrl.BetterListCtrl( self._watchers_listctrl_panel, 'watchers', 6, 12, [ ( 'subject', -1 ), ( 'status', 8 ), ( 'progress', 15 ) ], self._ConvertDataToListCtrlTuples, delete_key_callback = self._RemoveWatchers, activation_callback = self._HighlightWatcher )
self._watchers_listctrl_panel.SetListCtrl( self._watchers_listctrl )
self._watchers_listctrl_panel.AddButton( 'clear highlight', self._ClearExistingHighlightAndPanel, enabled_check_func = self._CanClearHighlight )
self._watchers_listctrl_panel.AddButton( 'highlight', self._HighlightWatcher, enabled_check_func = self._CanHighlight )
self._watchers_listctrl_panel.AddButton( 'remove', self._RemoveWatchers, enabled_only_on_selection = True )
self._watcher_url_input = ClientGUIControls.TextAndPasteCtrl( self._watchers_panel, self._AddURLs )
self._watchers_listctrl.Sort( 0 )
# suck up thread watchers from elsewhere in the program (presents a checklistboxdialog)
#
self._multiple_watcher_import = self._management_controller.GetVariable( 'multiple_watcher_import' )
self._watchers_panel.Add( self._watchers_listctrl_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
self._watchers_panel.Add( self._watcher_url_input, CC.FLAGS_EXPAND_PERPENDICULAR )
#
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.Add( self._sort_by, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
self._collect_by.Hide()
vbox.Add( self._watchers_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
self._MakeCurrentSelectionTagsBox( vbox )
self.SetSizer( vbox )
#
self._UpdateStatus()
HG.client_controller.sub( self, 'PendURL', 'pend_url' )
def _AddURLs( self, urls ):
for url in urls:
self._multiple_watcher_import.AddURL( url )
def _CanClearHighlight( self ):
return self._highlit_watcher is not None
def _CanHighlight( self ):
num_selected = len( self._watchers_listctrl.GetData( only_selected = True ) )
return num_selected == 1
def _ClearExistingHighlight( self ):
if self._highlit_watcher is not None:
publish_to_page = False
self._highlit_watcher.Repage( self._page_key, publish_to_page )
self._highlit_watcher = None
self._watchers_listctrl_panel.UpdateButtons()
def _ClearExistingHighlightAndPanel( self ):
self._ClearExistingHighlight()
media_results = []
panel = ClientGUIMedia.MediaPanelThumbnails( self._page, self._page_key, CC.LOCAL_FILE_SERVICE_KEY, media_results )
self._page.SwapMediaPanel( panel )
self._watchers_listctrl.UpdateDatas()
def _ConvertDataToListCtrlTuples( self, watcher ):
subject = watcher.GetSubject()
if watcher == self._highlit_watcher:
subject = '* ' + subject
status = watcher.GetSimpleStatus()
( value, range ) = watcher.GetValueRange()
progress = ( range, value )
pretty_subject = subject
pretty_status = status
pretty_progress = HydrusData.ConvertValueRangeToPrettyString( value, range )
display_tuple = ( pretty_subject, pretty_status, pretty_progress )
sort_tuple = ( subject, status, progress )
return ( display_tuple, sort_tuple )
def _HighlightWatcher( self ):
selected = self._watchers_listctrl.GetData( only_selected = True )
if len( selected ) == 1:
new_highlight = selected[0]
if new_highlight == self._highlit_watcher:
self._ClearExistingHighlightAndPanel()
else:
self._ClearExistingHighlight()
self._highlit_watcher = selected[0]
hashes = self._highlit_watcher.GetPresentedHashes()
media_results = HG.client_controller.Read( 'media_results', hashes )
hashes_to_media_results = { media_result.GetHash() : media_result for media_result in media_results }
sorted_media_results = [ hashes_to_media_results[ hash ] for hash in hashes ]
panel = ClientGUIMedia.MediaPanelThumbnails( self._page, self._page_key, CC.LOCAL_FILE_SERVICE_KEY, sorted_media_results )
self._page.SwapMediaPanel( panel )
publish_to_page = True
self._highlit_watcher.Repage( self._page_key, publish_to_page )
self._watchers_listctrl_panel.UpdateButtons()
self._watchers_listctrl.UpdateDatas()
def _RemoveWatchers( self ):
# should prob have some sort of 'three are still importing m8'
message = 'Remove the selected watchers?'
with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
if dlg.ShowModal() == wx.ID_YES:
highlight_was_included = False
for watcher in self._watchers_listctrl.GetData( only_selected = True ):
if watcher == self._highlit_watcher:
highlight_was_included = True
self._multiple_watcher_import.RemoveWatcher( watcher.GetThreadKey() )
if highlight_was_included:
self._ClearExistingHighlightAndPanel()
def _UpdateStatus( self ):
if HydrusData.TimeHasPassed( self._next_update_time ):
self._next_update_time = HydrusData.GetNow() + 1
thread_keys = self._multiple_watcher_import.GetThreadKeys()
if self._last_thread_keys != thread_keys:
self._last_thread_keys = thread_keys
watchers = self._multiple_watcher_import.GetWatchers()
self._watchers_listctrl.SetData( watchers )
self._watchers_listctrl.UpdateDatas()
# something here to push a refreshpagename thing to update value/range values on any change. so maybe cache this number and then check on changes or whatever!
# although I had to write a hook in the seedcachepanel thing so that it would do that even if page was hidden. this is not so available here, so think about it.
pass
def CheckAbleToClose( self ):
num_working = 0
for watcher in self._multiple_watcher_import.GetWatchers():
if watcher.CurrentlyWorking():
num_working += 1
if num_working > 0:
raise HydrusExceptions.VetoException( HydrusData.ConvertIntToPrettyString( num_working ) + ' watchers are still importing.' )
def PendURL( self, page_key, url ):
if page_key == self._page_key:
self._multiple_watcher_import.AddURL( url )
def SetSearchFocus( self ):
wx.CallAfter( self._watcher_url_input.SetFocus )
def Start( self ):
self._multiple_watcher_import.Start( self._page_key )
management_panel_types_to_classes[ MANAGEMENT_TYPE_IMPORT_MULTIPLE_WATCHER ] = ManagementPanelImporterMultipleWatcher
class ManagementPanelImporterURLs( ManagementPanelImporter ):
def __init__( self, parent, page, controller, management_controller ):
@ -2595,7 +2868,7 @@ class ManagementPanelImporterURLs( ManagementPanelImporter ):
# replace all this with a seed cache panel sometime
self._seed_cache_button = ClientGUISeedCache.SeedCacheButton( self._url_panel, self._controller, self._urls_import.GetSeedCache )
self._url_input = ClientGUICommon.TextAndPasteCtrl( self._url_panel, self._PendURLs )
self._url_input = ClientGUIControls.TextAndPasteCtrl( self._url_panel, self._PendURLs )
file_import_options = self._urls_import.GetOptions()
@ -2710,12 +2983,7 @@ class ManagementPanelImporterURLs( ManagementPanelImporter ):
def SetSearchFocus( self, page_key = None ):
if page_key is not None and page_key != self._page_key:
return
def SetSearchFocus( self ):
wx.CallAfter( self._url_input.SetFocus )
@ -3511,12 +3779,7 @@ class ManagementPanelQuery( ManagementPanel ):
def SetSearchFocus( self, page_key = None ):
if page_key is not None and page_key != self._page_key:
return
def SetSearchFocus( self ):
if self._search_enabled:

View File

@ -941,7 +941,7 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
control = wx.TextCtrl( panel, style = wx.TE_MULTILINE )
size = ClientData.ConvertTextToPixels( control, ( 80, 14 ) )
size = ClientGUICommon.ConvertTextToPixels( control, ( 80, 14 ) )
control.SetInitialSize( size )

View File

@ -1,7 +1,6 @@
import ClientConstants as CC
import ClientGUICommon
import ClientCaches
import ClientData
import ClientDefaults
import ClientGUIDialogs
import ClientImporting

View File

@ -9,6 +9,7 @@ import ClientGUIMenus
import ClientGUICanvas
import ClientDownloading
import ClientSearch
import ClientGUIShortcuts
import ClientThreading
import collections
import hashlib
@ -122,6 +123,10 @@ class DialogPageChooser( ClientGUIDialogs.Dialog ):
button.SetLabelText( 'simple downloader' )
elif entry_type == 'page_import_multiple_watcher':
button.SetLabelText( 'multiple watcher' )
elif entry_type == 'page_import_thread_watcher':
button.SetLabelText( 'thread watcher' )
@ -205,6 +210,10 @@ class DialogPageChooser( ClientGUIDialogs.Dialog ):
self._result = ( 'page', ClientGUIManagement.CreateManagementControllerImportSimpleDownloader() )
elif entry_type == 'page_import_multiple_watcher':
self._result = ( 'page', ClientGUIManagement.CreateManagementControllerImportMultipleWatcher() )
elif entry_type == 'page_import_thread_watcher':
self._result = ( 'page', ClientGUIManagement.CreateManagementControllerImportThreadWatcher() )
@ -298,6 +307,7 @@ class DialogPageChooser( ClientGUIDialogs.Dialog ):
elif menu_keyword == 'special':
entries.append( ( 'pages_notebook', None ) )
entries.append( ( 'page_import_multiple_watcher', None ) )
entries.append( ( 'page_duplicate_filter', None ) )
@ -351,7 +361,7 @@ class DialogPageChooser( ClientGUIDialogs.Dialog ):
id = None
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
if key == wx.WXK_UP: id = 8
elif key == wx.WXK_LEFT: id = 4
@ -466,11 +476,15 @@ class Page( wx.SplitterWindow ):
self.ReplaceWindow( self._media_panel, new_panel )
self._media_panel.DestroyLater()
# old messed-up way of doing it
'''
self._media_panel.Hide()
# If this is a CallAfter, OS X segfaults on refresh jej
self._controller.CallLaterWXSafe( self._media_panel, 0.5, self._media_panel.Destroy )
'''
self._media_panel = new_panel
self._controller.pub( 'refresh_page_name', self._page_key )
@ -618,6 +632,11 @@ class Page( wx.SplitterWindow ):
return ( x, y )
def IsMultipleWatcherPage( self ):
return self._management_controller.GetType() == ClientGUIManagement.MANAGEMENT_TYPE_IMPORT_MULTIPLE_WATCHER
def IsImporter( self ):
return self._management_controller.IsImporter()
@ -671,7 +690,7 @@ class Page( wx.SplitterWindow ):
def SetMediaFocus( self ):
wx.CallAfter( self._media_panel.SetFocus )
self._media_panel.SetFocus()
def SetMediaResults( self, media_results ):
@ -887,11 +906,11 @@ class PagesNotebook( wx.Notebook ):
def _CloseAllPages( self, polite = True ):
def _CloseAllPages( self, polite = True, delete_pages = False ):
closees = [ index for index in range( self.GetPageCount() ) ]
self._ClosePages( closees, polite )
self._ClosePages( closees, polite, delete_pages = delete_pages )
def _CloseLeftPages( self, from_index ):
@ -924,7 +943,7 @@ class PagesNotebook( wx.Notebook ):
def _ClosePage( self, index, polite = True ):
def _ClosePage( self, index, polite = True, delete_page = False ):
self._controller.ResetIdleTimer()
self._controller.ResetPageChangeTimer()
@ -958,13 +977,20 @@ class PagesNotebook( wx.Notebook ):
self._controller.pub( 'refresh_page_name', self._page_key )
self._controller.pub( 'notify_closed_page', page )
self._controller.pub( 'notify_new_undo' )
if delete_page:
self._controller.pub( 'notify_deleted_page', page )
else:
self._controller.pub( 'notify_closed_page', page )
self._controller.pub( 'notify_new_undo' )
return True
def _ClosePages( self, indices, polite = True ):
def _ClosePages( self, indices, polite = True, delete_pages = False ):
indices = list( indices )
@ -972,7 +998,7 @@ class PagesNotebook( wx.Notebook ):
for index in indices:
successful = self._ClosePage( index, polite )
successful = self._ClosePage( index, polite, delete_page = delete_pages )
if not successful:
@ -1269,6 +1295,8 @@ class PagesNotebook( wx.Notebook ):
page_file_count_display = new_options.GetInteger( 'page_file_count_display' )
import_page_progress_display = new_options.GetBoolean( 'import_page_progress_display' )
page = self.GetPage( index )
page_name = page.GetDisplayName()
@ -1280,16 +1308,29 @@ class PagesNotebook( wx.Notebook ):
page_name = page_name[ : max_page_name_chars ] + u'\u2026'
num_string = ''
( num_files, ( num_value, num_range ) ) = page.GetNumFileSummary()
if page_file_count_display == CC.PAGE_FILE_COUNT_DISPLAY_ALL or ( page_file_count_display == CC.PAGE_FILE_COUNT_DISPLAY_ONLY_IMPORTERS and page.IsImporter() ):
( num_files, ( num_value, num_range ) ) = page.GetNumFileSummary()
num_string += HydrusData.ConvertIntToPrettyString( num_files )
num_string = HydrusData.ConvertIntToPrettyString( num_files )
if import_page_progress_display:
if num_range > 0 and num_value != num_range:
num_string += ', ' + HydrusData.ConvertValueRangeToPrettyString( num_value, num_range )
if len( num_string ) > 0:
num_string += ', '
num_string += HydrusData.ConvertValueRangeToPrettyString( num_value, num_range )
if len( num_string ) > 0:
page_name += ' (' + num_string + ')'
@ -1554,6 +1595,8 @@ class PagesNotebook( wx.Notebook ):
forced_insertion_index = starting_index
done_first_page = False
for page_tuple in page_tuples:
( page_type, page_data ) = page_tuple
@ -1564,7 +1607,7 @@ class PagesNotebook( wx.Notebook ):
try:
page = self.NewPagesNotebook( name, forced_insertion_index = forced_insertion_index, give_it_a_blank_page = False )
page = self.NewPagesNotebook( name, forced_insertion_index = forced_insertion_index, give_it_a_blank_page = False, select_page = False )
page.AppendSessionPageTuples( subpage_tuples )
@ -1573,15 +1616,17 @@ class PagesNotebook( wx.Notebook ):
HydrusData.ShowException( e )
# append the tuples
elif page_type == 'page':
( management_controller, initial_hashes ) = page_data
try:
self.NewPage( management_controller, initial_hashes = initial_hashes, forced_insertion_index = forced_insertion_index )
select_page = not done_first_page
self.NewPage( management_controller, initial_hashes = initial_hashes, forced_insertion_index = forced_insertion_index, select_page = select_page )
done_first_page = True
except Exception as e:
@ -1928,6 +1973,28 @@ class PagesNotebook( wx.Notebook ):
def GetOrMakeMultipleWatcherPage( self ):
for page in self._GetPages():
if isinstance( page, PagesNotebook ):
if page.HasMultipleWatcherPage():
return page.GetOrMakeMultipleWatcherPage()
elif page.IsMultipleWatcherPage():
return page
# import page does not exist
return self.NewPageImportMultipleWatcher( on_deepest_notebook = True )
def GetOrMakeURLImportPage( self ):
for page in self._GetPages():
@ -1974,6 +2041,21 @@ class PagesNotebook( wx.Notebook ):
return HydrusData.ConvertIntToPrettyString( self.GetPageCount() ) + ' pages, ' + num_string + ' files'
def HasMediaPageName( self, page_name, only_my_level = False ):
media_pages = self._GetMediaPages( only_my_level )
for page in media_pages:
if page.GetName() == page_name:
return True
return False
def HasPage( self, page ):
return self.HasPageKey( page.GetPageKey() )
@ -1993,6 +2075,29 @@ class PagesNotebook( wx.Notebook ):
return False
def HasMultipleWatcherPage( self ):
for page in self._GetPages():
if isinstance( page, PagesNotebook ):
if page.HasMultipleWatcherPage():
return True
else:
if page.IsMultipleWatcherPage():
return True
return False
@ -2042,10 +2147,14 @@ class PagesNotebook( wx.Notebook ):
return
self._CloseAllPages( polite = False )
self._CloseAllPages( polite = False, delete_pages = True )
self._controller.CallLaterWXSafe( self, 1.0, self.AppendGUISession, name, load_in_a_page_of_pages = False )
else:
self.AppendGUISession( name, load_in_a_page_of_pages = False )
self.AppendGUISession( name, load_in_a_page_of_pages = False )
def MediaDragAndDropDropped( self, source_page_key, hashes ):
@ -2221,7 +2330,11 @@ class PagesNotebook( wx.Notebook ):
if select_page:
wx.CallAfter( page.SetSearchFocus )
page.SetSearchFocus()
# this is here for now due to the pagechooser having a double-layer dialog on a booru choice, which messes up some focus inheritance
self._controller.CallLaterWXSafe( self, 0.5, page.SetSearchFocus )
return page
@ -2261,6 +2374,13 @@ class PagesNotebook( wx.Notebook ):
return self.NewPage( management_controller, on_deepest_notebook = on_deepest_notebook )
def NewPageImportMultipleWatcher( self, thread_url = None, on_deepest_notebook = False ):
management_controller = ClientGUIManagement.CreateManagementControllerImportMultipleWatcher( thread_url )
return self.NewPage( management_controller, on_deepest_notebook = on_deepest_notebook )
def NewPageImportThreadWatcher( self, thread_url = None, on_deepest_notebook = False ):
management_controller = ClientGUIManagement.CreateManagementControllerImportThreadWatcher( thread_url )
@ -2324,7 +2444,7 @@ class PagesNotebook( wx.Notebook ):
return page
def NewPagesNotebook( self, name = 'pages', forced_insertion_index = None, on_deepest_notebook = False, give_it_a_blank_page = True ):
def NewPagesNotebook( self, name = 'pages', forced_insertion_index = None, on_deepest_notebook = False, give_it_a_blank_page = True, select_page = True ):
current_page = self.GetCurrentPage()
@ -2360,7 +2480,7 @@ class PagesNotebook( wx.Notebook ):
page_name = page.GetDisplayName()
self.InsertPage( insertion_index, page, page_name, select = True )
self.InsertPage( insertion_index, page, page_name, select = select_page )
self._controller.pub( 'refresh_page_name', page.GetPageKey() )

View File

@ -45,6 +45,11 @@ class ReviewServicePanel( wx.Panel ):
subpanels.append( self._ServiceFilePanel( self, service ) )
if self._service.GetServiceKey() == CC.COMBINED_LOCAL_FILE_SERVICE_KEY:
subpanels.append( self._ServiceCombinedLocalFilesPanel( self, service ) )
if self._service.GetServiceKey() == CC.TRASH_SERVICE_KEY:
subpanels.append( self._ServiceTrashPanel( self, service ) )
@ -112,31 +117,6 @@ class ReviewServicePanel( wx.Panel ):
def EventDeleteLocalDeleted( self, event ):
message = 'This will clear the client\'s memory of which files it has locally deleted, which affects \'exclude previously deleted files\' import tests.'
message += os.linesep * 2
message += 'It will freeze the gui while it works.'
message += os.linesep * 2
message += 'If you do not know what this does, click \'forget it\'.'
with ClientGUIDialogs.DialogYesNo( self, message, yes_label = 'do it', no_label = 'forget it' ) as dlg_add:
result = dlg_add.ShowModal()
if result == wx.ID_YES:
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_ADVANCED, ( 'delete_deleted', None ) )
service_keys_to_content_updates = { self._service_key : [ content_update ] }
HG.client_controller.Write( 'content_updates', service_keys_to_content_updates )
self._DisplayService()
def EventImmediateSync( self, event ):
def do_it():
@ -257,6 +237,45 @@ class ReviewServicePanel( wx.Panel ):
class _ServiceCombinedLocalFilesPanel( ClientGUICommon.StaticBox ):
def __init__( self, parent, service ):
ClientGUICommon.StaticBox.__init__( self, parent, 'combined local files' )
self._service = service
self._clear_deleted_files_record = ClientGUICommon.BetterButton( self, 'clear deleted files record', self._ClearDeletedFilesRecord )
#
self.Add( self._clear_deleted_files_record, CC.FLAGS_LONE_BUTTON )
def _ClearDeletedFilesRecord( self ):
message = 'This will instruct your database to forget its entire record of locally deleted files, meaning that if it ever encounters any of those files again, it will assume they are new and reimport them. This operation cannot be undone.'
with ClientGUIDialogs.DialogYesNo( self, message, yes_label = 'do it', no_label = 'forget it' ) as dlg_add:
result = dlg_add.ShowModal()
if result == wx.ID_YES:
hashes = None
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_ADVANCED, ( 'delete_deleted', hashes ) )
service_keys_to_content_updates = { CC.COMBINED_LOCAL_FILE_SERVICE_KEY : [ content_update ] }
HG.client_controller.Write( 'content_updates', service_keys_to_content_updates )
HG.client_controller.pub( 'service_updated', self._service )
class _ServiceFilePanel( ClientGUICommon.StaticBox ):
def __init__( self, parent, service ):

View File

@ -477,7 +477,7 @@ class EditFormulaPanel( ClientGUIScrolledPanels.EditPanel ):
self._formula_description = ClientGUICommon.SaneMultilineTextCtrl( my_panel )
( width, height ) = ClientData.ConvertTextToPixels( self._formula_description, ( 90, 8 ) )
( width, height ) = ClientGUICommon.ConvertTextToPixels( self._formula_description, ( 90, 8 ) )
self._formula_description.SetInitialSize( ( width, height ) )
@ -4563,7 +4563,7 @@ class TestPanel( wx.Panel ):
self._example_data_preview = ClientGUICommon.SaneMultilineTextCtrl( self, style = wx.TE_READONLY )
size = ClientData.ConvertTextToPixels( self._example_data_preview, ( 80, 12 ) )
size = ClientGUICommon.ConvertTextToPixels( self._example_data_preview, ( 80, 12 ) )
self._example_data_preview.SetInitialSize( size )
@ -4571,7 +4571,7 @@ class TestPanel( wx.Panel ):
self._results = ClientGUICommon.SaneMultilineTextCtrl( self )
size = ClientData.ConvertTextToPixels( self._example_data_preview, ( 80, 12 ) )
size = ClientGUICommon.ConvertTextToPixels( self._example_data_preview, ( 80, 12 ) )
self._results.SetInitialSize( size )

View File

@ -87,7 +87,7 @@ class PopupMessage( PopupWindow ):
popup_message_character_width = HG.client_controller.new_options.GetInteger( 'popup_message_character_width' )
wrap_width = ClientData.ConvertTextToPixelWidth( self._title, popup_message_character_width )
wrap_width = ClientGUICommon.ConvertTextToPixelWidth( self._title, popup_message_character_width )
self._title.Wrap( wrap_width )
self._title.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
@ -319,7 +319,10 @@ class PopupMessage( PopupWindow ):
text = title
if self._title.GetLabelText() != text: self._title.SetLabelText( text )
if self._title.GetLabelText() != text:
self._title.SetLabelText( text )
self._title.Show()
@ -569,7 +572,7 @@ class PopupMessageManager( wx.Frame ):
job_key.SetVariable( 'popup_text_1', u'initialising popup message manager\u2026' )
self._update_job = HG.client_controller.CallRepeatingWXSafe( self, 0.5, 0.25, self.REPEATINGUpdate )
self._update_job = HG.client_controller.CallRepeatingWXSafe( self, 0.25, 0.5, self.REPEATINGUpdate )
HG.client_controller.CallLaterWXSafe( self, 0.5, self.AddMessage, job_key )
@ -578,7 +581,7 @@ class PopupMessageManager( wx.Frame ):
def _CheckPending( self ):
size_and_position_needed = False
force_relayout = False
self._pending_job_keys = [ job_key for job_key in self._pending_job_keys if not job_key.IsDeleted() ]
@ -592,7 +595,7 @@ class PopupMessageManager( wx.Frame ):
self._message_vbox.Add( window, CC.FLAGS_EXPAND_PERPENDICULAR )
size_and_position_needed = True
force_relayout = True
dismiss_shown_before = self._dismiss_all.IsShown()
@ -612,15 +615,15 @@ class PopupMessageManager( wx.Frame ):
if self._dismiss_all.IsShown() != dismiss_shown_before:
size_and_position_needed = True
force_relayout = True
if not self.IsShown():
if force_relayout:
size_and_position_needed = True
self.Layout()
if size_and_position_needed:
if self._NeedsSizeOrShow():
self._SizeAndPositionAndShow()
@ -686,6 +689,39 @@ class PopupMessageManager( wx.Frame ):
def _NeedsSizeOrShow( self ):
num_messages_displayed = self._message_vbox.GetItemCount()
there_is_stuff_to_display = num_messages_displayed > 0
is_shown = self.IsShown()
if there_is_stuff_to_display:
if not is_shown:
return True
best_size = self.GetBestSize()
if best_size != self.GetSize():
return True
else:
if is_shown:
return True
return False
def _SizeAndPositionAndShow( self ):
try:
@ -786,7 +822,7 @@ class PopupMessageManager( wx.Frame ):
self.CleanBeforeDestroy()
self.Destroy()
self.DestroyLater()
@ -875,7 +911,7 @@ class PopupMessageManager( wx.Frame ):
self.CleanBeforeDestroy()
self.Destroy()
self.DestroyLater()
return
@ -890,6 +926,8 @@ class PopupMessageManager( wx.Frame ):
message_window.TryToDismiss()
self.Layout()
break
else:
@ -956,12 +994,11 @@ class PopupMessageManager( wx.Frame ):
self._message_vbox.Detach( window )
# OS X segfaults if this is instant
wx.CallAfter( window.Destroy )
window.DestroyLater()
if self._OKToAlterUI():
self._SizeAndPositionAndShow()
self.Layout()
self._CheckPending()
@ -980,6 +1017,8 @@ class PopupMessageManager( wx.Frame ):
message_window.TryToDismiss()
self.Layout()
self._CheckPending()
@ -997,6 +1036,8 @@ class PopupMessageManager( wx.Frame ):
if self._OKToAlterUI():
self.Layout()
self._SizeAndPositionAndShow()
@ -1044,7 +1085,7 @@ class PopupMessageDialogPanel( ClientGUIScrolledPanels.ReviewPanelVetoable ):
self._message_pubbed = False
self._update_job = HG.client_controller.CallRepeatingWXSafe( self, 0.5, 0.25, self.REPEATINGUpdate )
self._update_job = HG.client_controller.CallRepeatingWXSafe( self, 0.25, 0.5, self.REPEATINGUpdate )
def _HideOtherWindows( self ):

View File

@ -2,6 +2,7 @@ import ClientConstants as CC
import ClientData
import ClientDefaults
import ClientDownloading
import ClientDuplicates
import ClientImporting
import ClientGUICommon
import ClientGUIControls
@ -13,6 +14,7 @@ import ClientGUIParsing
import ClientGUIScrolledPanels
import ClientGUISeedCache
import ClientGUISerialisable
import ClientGUIShortcuts
import ClientGUITime
import ClientGUITopLevelWindows
import ClientImportOptions
@ -1000,7 +1002,7 @@ class EditDuplicateActionOptionsPanel( ClientGUIScrolledPanels.EditPanel ):
delete_both_files = self._delete_both_files.GetValue()
sync_urls_action = self._sync_urls_action.GetChoice()
duplicate_action_options = ClientData.DuplicateActionOptions( tag_service_actions, rating_service_actions, delete_second_file, sync_archive, delete_both_files, sync_urls_action )
duplicate_action_options = ClientDuplicates.DuplicateActionOptions( tag_service_actions, rating_service_actions, delete_second_file, sync_archive, delete_both_files, sync_urls_action )
return duplicate_action_options
@ -1767,7 +1769,7 @@ class EditNetworkContextCustomHeadersPanel( ClientGUIScrolledPanels.EditPanel ):
self._reason = wx.TextCtrl( self )
width = ClientData.ConvertTextToPixelWidth( self._reason, 60 )
width = ClientGUICommon.ConvertTextToPixelWidth( self._reason, 60 )
self._reason.SetMinSize( ( width, -1 ) )
#
@ -2844,7 +2846,7 @@ class EditSubscriptionQueryPanel( ClientGUIScrolledPanels.EditPanel ):
self._status_st = ClientGUICommon.BetterStaticText( self )
st_width = ClientData.ConvertTextToPixelWidth( self._status_st, 50 )
st_width = ClientGUICommon.ConvertTextToPixelWidth( self._status_st, 50 )
self._status_st.SetMinSize( ( st_width, -1 ) )
@ -2892,6 +2894,10 @@ class EditSubscriptionQueryPanel( ClientGUIScrolledPanels.EditPanel ):
self._UpdateStatus()
self._query_text.SetSelection( -1, -1 ) # select all
wx.CallAfter( self._query_text.SetFocus )
def _GetValue( self ):
@ -3771,7 +3777,7 @@ class EditSubscriptionsPanel( ClientGUIScrolledPanels.EditPanel ):
def SetCheckerOptions( self ):
checker_options = ClientData.CheckerOptions( intended_files_per_check = 5, never_faster_than = 86400, never_slower_than = 90 * 86400, death_file_velocity = ( 1, 90 * 86400 ) )
checker_options = ClientImportOptions.CheckerOptions( intended_files_per_check = 5, never_faster_than = 86400, never_slower_than = 90 * 86400, death_file_velocity = ( 1, 90 * 86400 ) )
with ClientGUITopLevelWindows.DialogEdit( self, 'edit check timings' ) as dlg:
@ -4003,7 +4009,7 @@ class EditTagFilterPanel( ClientGUIScrolledPanels.EditPanel ):
def EventKeyDownBlacklist( self, event ):
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
if key in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ):
@ -4017,7 +4023,7 @@ class EditTagFilterPanel( ClientGUIScrolledPanels.EditPanel ):
def EventKeyDownWhitelist( self, event ):
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
if key in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ):
@ -4651,7 +4657,7 @@ class EditURLMatchPanel( ClientGUIScrolledPanels.EditPanel ):
self._example_url.SetValue( example_url )
example_url_width = ClientData.ConvertTextToPixelWidth( self._example_url, 75 )
example_url_width = ClientGUICommon.ConvertTextToPixelWidth( self._example_url, 75 )
self._example_url.SetMinSize( ( example_url_width, -1 ) )

View File

@ -1496,7 +1496,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
def EventKeyDownNamespace( self, event ):
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
if key in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ):
@ -1716,6 +1716,8 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._permit_watchers_to_name_their_pages = wx.CheckBox( thread_checker )
self._use_multiple_watcher_for_drag_and_drops = wx.CheckBox( thread_checker )
self._thread_watcher_not_found_page_string = ClientGUICommon.NoneableTextCtrl( thread_checker, none_phrase = 'do not show' )
self._thread_watcher_dead_page_string = ClientGUICommon.NoneableTextCtrl( thread_checker, none_phrase = 'do not show' )
self._thread_watcher_paused_page_string = ClientGUICommon.NoneableTextCtrl( thread_checker, none_phrase = 'do not show' )
@ -1735,6 +1737,8 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._permit_watchers_to_name_their_pages.SetValue( self._new_options.GetBoolean( 'permit_watchers_to_name_their_pages' ) )
self._use_multiple_watcher_for_drag_and_drops.SetValue( self._new_options.GetBoolean( 'use_multiple_watcher_for_drag_and_drops' ) )
self._thread_watcher_not_found_page_string.SetValue( self._new_options.GetNoneableString( 'thread_watcher_not_found_page_string' ) )
self._thread_watcher_dead_page_string.SetValue( self._new_options.GetNoneableString( 'thread_watcher_dead_page_string' ) )
self._thread_watcher_paused_page_string.SetValue( self._new_options.GetNoneableString( 'thread_watcher_paused_page_string' ) )
@ -1768,6 +1772,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
rows = []
rows.append( ( 'Permit thread checkers to name their own pages:', self._permit_watchers_to_name_their_pages ) )
rows.append( ( 'When drag-and-dropping, use the Multiple Watcher (advanced!):', self._use_multiple_watcher_for_drag_and_drops ) )
rows.append( ( 'Prepend 404 thread checker page names with this:', self._thread_watcher_not_found_page_string ) )
rows.append( ( 'Prepend dead thread checker page names with this:', self._thread_watcher_dead_page_string ) )
rows.append( ( 'Prepend paused thread checker page names with this:', self._thread_watcher_paused_page_string ) )
@ -1799,12 +1804,14 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._new_options.SetBoolean( 'permit_watchers_to_name_their_pages', self._permit_watchers_to_name_their_pages.GetValue() )
self._new_options.SetDefaultThreadCheckerOptions( self._thread_checker_options.GetValue() )
self._new_options.SetBoolean( 'use_multiple_watcher_for_drag_and_drops', self._use_multiple_watcher_for_drag_and_drops.GetValue() )
self._new_options.SetNoneableString( 'thread_watcher_not_found_page_string', self._thread_watcher_not_found_page_string.GetValue() )
self._new_options.SetNoneableString( 'thread_watcher_dead_page_string', self._thread_watcher_dead_page_string.GetValue() )
self._new_options.SetNoneableString( 'thread_watcher_paused_page_string', self._thread_watcher_paused_page_string.GetValue() )
self._new_options.SetDefaultThreadCheckerOptions( self._thread_checker_options.GetValue() )
class _ImportingPanel( wx.Panel ):
@ -2504,6 +2511,8 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._page_file_count_display.Append( CC.page_file_count_display_string_lookup[ display_type ], display_type )
self._import_page_progress_display = wx.CheckBox( self )
self._total_pages_warning = wx.SpinCtrl( self, min = 5, max = 200 )
self._reverse_page_shift_drag_behaviour = wx.CheckBox( self )
@ -2582,6 +2591,8 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._page_file_count_display.SelectClientData( self._new_options.GetInteger( 'page_file_count_display' ) )
self._import_page_progress_display.SetValue( self._new_options.GetBoolean( 'import_page_progress_display' ) )
self._total_pages_warning.SetValue( self._new_options.GetInteger( 'total_pages_warning' ) )
self._reverse_page_shift_drag_behaviour.SetValue( self._new_options.GetBoolean( 'reverse_page_shift_drag_behaviour' ) )
@ -2627,6 +2638,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
rows.append( ( 'Confirm sending more than one file to archive or inbox: ', self._confirm_archive ) )
rows.append( ( 'Max characters to display in a page name: ', self._max_page_name_chars ) )
rows.append( ( 'Show page file count after its name: ', self._page_file_count_display ) )
rows.append( ( 'Show import page x/y progress after its name: ', self._import_page_progress_display ) )
rows.append( ( 'Warn at this many total pages: ', self._total_pages_warning ) )
rows.append( ( 'Reverse page tab shift-drag behaviour: ', self._reverse_page_shift_drag_behaviour ) )
rows.append( ( 'Always embed autocomplete dropdown results window: ', self._always_embed_autocompletes ) )
@ -2722,6 +2734,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._new_options.SetInteger( 'max_page_name_chars', self._max_page_name_chars.GetValue() )
self._new_options.SetInteger( 'page_file_count_display', self._page_file_count_display.GetChoice() )
self._new_options.SetBoolean( 'import_page_progress_display', self._import_page_progress_display.GetValue() )
self._new_options.SetInteger( 'total_pages_warning', self._total_pages_warning.GetValue() )
@ -3045,7 +3058,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
def EventKeyDownSortBy( self, event ):
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
if key in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ):
@ -3371,12 +3384,14 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
if self._fetch_ac_results_automatically.GetValue() == True:
self._num_autocomplete_chars.Enable()
self._autocomplete_long_wait.Enable()
self._autocomplete_short_wait_chars.Enable()
self._autocomplete_short_wait.Enable()
else:
self._num_autocomplete_chars.Disable()
self._autocomplete_long_wait.Disable()
self._autocomplete_short_wait_chars.Disable()
self._autocomplete_short_wait.Disable()
@ -4234,7 +4249,7 @@ class ManageShortcutsPanel( ClientGUIScrolledPanels.ManagePanel ):
def _Add( self ):
shortcuts = ClientData.Shortcuts( 'new shortcuts' )
shortcuts = ClientGUIShortcuts.Shortcuts( 'new shortcuts' )
with ClientGUITopLevelWindows.DialogEdit( self, 'edit shortcuts' ) as dlg:
@ -4484,7 +4499,7 @@ class ManageShortcutsPanel( ClientGUIScrolledPanels.ManagePanel ):
def EventAdd( self, event ):
shortcut = ClientData.Shortcut()
shortcut = ClientGUIShortcuts.Shortcut()
command = ClientData.ApplicationCommand()
name = self._name.GetValue()
@ -4526,7 +4541,7 @@ class ManageShortcutsPanel( ClientGUIScrolledPanels.ManagePanel ):
raise HydrusExceptions.VetoException( 'That name is reserved--please pick another!' )
shortcuts = ClientData.Shortcuts( name )
shortcuts = ClientGUIShortcuts.Shortcuts( name )
for ( shortcut, command ) in self._shortcuts.GetClientData():
@ -4562,7 +4577,7 @@ class ManageShortcutsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._shortcut_panel = ClientGUICommon.StaticBox( self, 'shortcut' )
self._shortcut = ClientGUICommon.Shortcut( self._shortcut_panel )
self._shortcut = ClientGUIShortcuts.ShortcutPanel( self._shortcut_panel )
#
@ -5070,12 +5085,13 @@ class ManageTagsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._hashes.update( m.GetHashes() )
self._tag_repositories = ClientGUICommon.ListBook( self )
self._tag_repositories.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGED, self.EventServiceChanged )
self._tag_repositories = ClientGUICommon.BetterNotebook( self )
#
services = HG.client_controller.services_manager.GetServices( HC.TAG_SERVICES )
services = HG.client_controller.services_manager.GetServices( HC.TAG_SERVICES, randomised = False )
default_tag_repository_key = HC.options[ 'default_tag_repository' ]
for service in services:
@ -5084,12 +5100,10 @@ class ManageTagsPanel( ClientGUIScrolledPanels.ManagePanel ):
page = self._Panel( self._tag_repositories, self._file_service_key, service.GetServiceKey(), self._current_media, self._immediate_commit, canvas_key = self._canvas_key )
self._tag_repositories.AddPage( name, service_key, page )
select = service_key == default_tag_repository_key
self._tag_repositories.AddPage( page, name, select = select )
default_tag_repository_key = HC.options[ 'default_tag_repository' ]
self._tag_repositories.Select( default_tag_repository_key )
#
@ -5112,12 +5126,16 @@ class ManageTagsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._my_shortcut_handler = ClientGUIShortcuts.ShortcutsHandler( self, [ 'media', 'main_gui' ] )
self._tag_repositories.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGED, self.EventServiceChanged )
self._SetSearchFocus()
def _GetGroupsOfServiceKeysToContentUpdates( self ):
groups_of_service_keys_to_content_updates = []
for page in self._tag_repositories.GetActivePages():
for page in self._tag_repositories.GetPages():
( service_key, groups_of_content_updates ) = page.GetGroupsOfContentUpdates()
@ -5151,7 +5169,7 @@ class ManageTagsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._current_media = ( new_media_singleton.Duplicate(), )
for page in self._tag_repositories.GetActivePages():
for page in self._tag_repositories.GetPages():
page.SetMedia( self._current_media )
@ -5190,12 +5208,16 @@ class ManageTagsPanel( ClientGUIScrolledPanels.ManagePanel ):
def EventSelectDown( self, event ):
self._tag_repositories.SelectDown()
self._tag_repositories.SelectRight()
self._SetSearchFocus()
def EventSelectUp( self, event ):
self._tag_repositories.SelectUp()
self._tag_repositories.SelectLeft()
self._SetSearchFocus()
def EventShowNext( self, event ):
@ -5223,6 +5245,8 @@ class ManageTagsPanel( ClientGUIScrolledPanels.ManagePanel ):
wx.CallAfter( page.SetTagBoxFocus )
event.Skip()
def ProcessApplicationCommand( self, command ):
@ -5872,7 +5896,7 @@ class ManageURLsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._urls_listbox = wx.ListBox( self, style = wx.LB_SORT | wx.LB_SINGLE )
self._urls_listbox.Bind( wx.EVT_LISTBOX_DCLICK, self.EventListDoubleClick )
( width, height ) = ClientData.ConvertTextToPixels( self._urls_listbox, ( 120, 10 ) )
( width, height ) = ClientGUICommon.ConvertTextToPixels( self._urls_listbox, ( 120, 10 ) )
self._urls_listbox.SetInitialSize( ( width, height ) )
@ -6013,7 +6037,7 @@ class ManageURLsPanel( ClientGUIScrolledPanels.ManagePanel ):
def EventInputCharHook( self, event ):
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
if key in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ):
@ -6089,7 +6113,9 @@ class RepairFileSystemPanel( ClientGUIScrolledPanels.ManagePanel ):
text += os.linesep * 2
text += '1) If you know what these should be (e.g. you recently remapped their external drive to another location), update the paths here manually. For most users, this will likely be a simple ctrl+a->correct, but if you have a more complicated system or store your thumbnails different to your files, make sure you skim the whole list. Check everything reports _ok!_'
text += os.linesep * 2
text += 'Then hit \'apply\', and the client will launch. You should double-check your \'preferred\' file storage locations under database->migrate database immediately.'
text += 'Although it is best if you can find everything, you only _have_ to fix the subdirectories starting with \'f\', which store your original files. Those starting \'t\' and \'r\' are for your thumbnails, which can be regenerated with a bit of work.'
text += os.linesep * 2
text += 'Then hit \'apply\', and the client will launch. You should double-check all your locations under database->migrate database immediately.'
text += os.linesep * 2
text += '2) If the locations are not available, or you do not know what they should be, or you wish to fix this outside of the program, hit \'cancel\' to gracefully cancel client boot. Feel free to contact hydrus dev for help.'
@ -6172,58 +6198,51 @@ class RepairFileSystemPanel( ClientGUIScrolledPanels.ManagePanel ):
correct_rows = []
if self._only_thumbs:
thumb_problems = False
for ( incorrect_location, prefix, correct_location, ok ) in self._locations.GetClientData():
problems = False
for ( incorrect_location, prefix, correct_location, ok ) in self._locations.GetClientData():
if correct_location == '':
if correct_location == '':
if prefix.startswith( 'f' ):
problems = True
correct_location = incorrect_location
elif ok != 'ok!':
problems = True
correct_rows.append( ( incorrect_location, prefix, correct_location ) )
if problems:
message = 'Some or all of your incorrect paths have not been corrected, but they are all thumbnail paths.'
message += os.linesep * 2
message += 'Would you like instead to create new empty folders at the previous (or corrected, if you have entered them) locations?'
message += os.linesep * 2
message += 'You can run database->regenerate->all thumbnails to fill them up again.'
with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
if dlg.ShowModal() != wx.ID_YES:
raise HydrusExceptions.VetoException()
else:
for ( incorrect_location, prefix, correct_location, ok ) in self._locations.GetClientData():
if correct_location == '':
raise HydrusExceptions.VetoException( 'You did not correct all the locations!' )
elif ok != 'ok!':
raise HydrusExceptions.VetoException( 'You did not find all the correct locations!' )
raise HydrusExceptions.VetoException( 'You did not correct all the file locations!' )
else:
correct_rows.append( ( incorrect_location, prefix, correct_location ) )
thumb_problems = True
correct_location = incorrect_location
elif ok != 'ok!':
if prefix.startswith( 'f' ):
raise HydrusExceptions.VetoException( 'You did not find all the correct file locations!' )
else:
thumb_problems = True
correct_rows.append( ( incorrect_location, prefix, correct_location ) )
if thumb_problems:
message = 'Some or all of your incorrect paths have not been corrected, but they are all thumbnail paths.'
message += os.linesep * 2
message += 'Would you like instead to create new empty subdirectories at the previous (or corrected, if you have entered them) locations?'
message += os.linesep * 2
message += 'You can run database->regenerate->thumbnails to fill them up again.'
with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
if dlg.ShowModal() != wx.ID_YES:
raise HydrusExceptions.VetoException()

View File

@ -421,7 +421,7 @@ class MigrateDatabasePanel( ClientGUIScrolledPanels.ReviewPanel ):
self.SetSizer( vbox )
min_width = ClientData.ConvertTextToPixelWidth( self, 100 )
min_width = ClientGUICommon.ConvertTextToPixelWidth( self, 100 )
self.SetMinSize( ( min_width, -1 ) )
@ -1195,7 +1195,7 @@ class ReviewAllBandwidthPanel( ClientGUIScrolledPanels.ReviewPanel ):
self._bandwidths.Sort( 0 )
self._update_job = HG.client_controller.CallRepeatingWXSafe( self, 5.0, 0.0, self._Update )
self._update_job = HG.client_controller.CallRepeatingWXSafe( self, 0.5, 5.0, self._Update )
#
@ -1311,7 +1311,7 @@ class ReviewAllBandwidthPanel( ClientGUIScrolledPanels.ReviewPanel ):
self._controller.network_engine.bandwidth_manager.DeleteHistory( selected_network_contexts )
self._update_job.MoveNextWorkTimeToNow()
self._update_job.Wake()
@ -1411,7 +1411,7 @@ class ReviewAllBandwidthPanel( ClientGUIScrolledPanels.ReviewPanel ):
self._controller.new_options.SetNoneableInteger( 'last_review_bandwidth_search_distance', last_review_bandwidth_search_distance )
self._update_job.MoveNextWorkTimeToNow()
self._update_job.Wake()
def ShowNetworkContext( self ):
@ -1929,9 +1929,9 @@ class ReviewNetworkContextBandwidthPanel( ClientGUIScrolledPanels.ReviewPanel ):
#
self._rules_job = HG.client_controller.CallRepeatingWXSafe( self, 5.0, 0.0, self._UpdateRules )
self._rules_job = HG.client_controller.CallRepeatingWXSafe( self, 0.5, 5.0, self._UpdateRules )
self._update_job = HG.client_controller.CallRepeatingWXSafe( self, 1.0, 0.0, self._Update )
self._update_job = HG.client_controller.CallRepeatingWXSafe( self, 0.5, 1.0, self._Update )
def _EditRules( self ):
@ -2068,7 +2068,7 @@ class ReviewNetworkContextBandwidthPanel( ClientGUIScrolledPanels.ReviewPanel ):
self._controller.network_engine.bandwidth_manager.DeleteRules( self._network_context )
self._rules_job.MoveNextWorkTimeToNow()
self._rules_job.Wake()

View File

@ -1,8 +1,10 @@
import ClientConstants as CC
import ClientData
import ClientGUICommon
import HydrusConstants as HC
import HydrusData
import HydrusGlobals as HG
import HydrusSerialisable
import wx
FLASHWIN_OK = False
@ -20,6 +22,109 @@ if HC.PLATFORM_WINDOWS:
pass
def ConvertKeyEventToShortcut( event ):
key = event.KeyCode
if ClientData.OrdIsSensibleASCII( key ) or key in CC.wxk_code_string_lookup.keys():
modifiers = []
if event.AltDown():
modifiers.append( CC.SHORTCUT_MODIFIER_ALT )
if event.CmdDown():
modifiers.append( CC.SHORTCUT_MODIFIER_CTRL )
if event.ShiftDown():
modifiers.append( CC.SHORTCUT_MODIFIER_SHIFT )
shortcut = Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, key, modifiers )
if HG.gui_report_mode:
HydrusData.ShowText( 'key event caught: ' + repr( shortcut ) )
return shortcut
return None
def ConvertKeyEventToSimpleTuple( event ):
modifier = wx.ACCEL_NORMAL
if event.AltDown(): modifier = wx.ACCEL_ALT
elif event.CmdDown(): modifier = wx.ACCEL_CTRL
elif event.ShiftDown(): modifier = wx.ACCEL_SHIFT
key = event.KeyCode
return ( modifier, key )
def ConvertMouseEventToShortcut( event ):
key = None
if event.LeftDown() or event.LeftDClick():
key = CC.SHORTCUT_MOUSE_LEFT
elif event.MiddleDown() or event.MiddleDClick():
key = CC.SHORTCUT_MOUSE_MIDDLE
elif event.RightDown() or event.RightDClick():
key = CC.SHORTCUT_MOUSE_RIGHT
elif event.GetWheelRotation() > 0:
key = CC.SHORTCUT_MOUSE_SCROLL_UP
elif event.GetWheelRotation() < 0:
key = CC.SHORTCUT_MOUSE_SCROLL_DOWN
if key is not None:
modifiers = []
if event.AltDown():
modifiers.append( CC.SHORTCUT_MODIFIER_ALT )
if event.CmdDown():
modifiers.append( CC.SHORTCUT_MODIFIER_CTRL )
if event.ShiftDown():
modifiers.append( CC.SHORTCUT_MODIFIER_SHIFT )
shortcut = Shortcut( CC.SHORTCUT_TYPE_MOUSE, key, modifiers )
if HG.gui_report_mode:
HydrusData.ShowText( 'mouse event caught: ' + repr( shortcut ) )
return shortcut
return None
def IShouldCatchCharHook( evt_handler ):
if HC.PLATFORM_WINDOWS and FLASHWIN_OK:
@ -44,6 +149,416 @@ def IShouldCatchCharHook( evt_handler ):
return True
class Shortcut( HydrusSerialisable.SerialisableBase ):
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_SHORTCUT
SERIALISABLE_NAME = 'Shortcut'
SERIALISABLE_VERSION = 1
def __init__( self, shortcut_type = None, shortcut_key = None, modifiers = None ):
if shortcut_type is None:
shortcut_type = CC.SHORTCUT_TYPE_KEYBOARD
if shortcut_key is None:
shortcut_key = wx.WXK_F7
if modifiers is None:
modifiers = []
modifiers.sort()
HydrusSerialisable.SerialisableBase.__init__( self )
self._shortcut_type = shortcut_type
self._shortcut_key = shortcut_key
self._modifiers = modifiers
def __cmp__( self, other ):
return cmp( self.ToString(), other.ToString() )
def __eq__( self, other ):
return self.__hash__() == other.__hash__()
def __hash__( self ):
return ( self._shortcut_type, self._shortcut_key, tuple( self._modifiers ) ).__hash__()
def __repr__( self ):
return 'Shortcut: ' + self.ToString()
def _GetSerialisableInfo( self ):
return ( self._shortcut_type, self._shortcut_key, self._modifiers )
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
( self._shortcut_type, self._shortcut_key, self._modifiers ) = serialisable_info
def GetShortcutType( self ):
return self._shortcut_type
def ToString( self ):
components = []
if CC.SHORTCUT_MODIFIER_CTRL in self._modifiers:
components.append( 'ctrl' )
if CC.SHORTCUT_MODIFIER_ALT in self._modifiers:
components.append( 'alt' )
if CC.SHORTCUT_MODIFIER_SHIFT in self._modifiers:
components.append( 'shift' )
if self._shortcut_type == CC.SHORTCUT_TYPE_KEYBOARD:
if self._shortcut_key in CC.wxk_code_string_lookup:
components.append( CC.wxk_code_string_lookup[ self._shortcut_key ] )
elif ClientData.OrdIsAlphaUpper( self._shortcut_key ):
components.append( chr( self._shortcut_key + 32 ) ) # + 32 for converting ascii A -> a
elif ClientData.OrdIsSensibleASCII( self._shortcut_key ):
components.append( chr( self._shortcut_key ) )
else:
components.append( 'unknown key' )
elif self._shortcut_type == CC.SHORTCUT_TYPE_MOUSE:
components.append( CC.shortcut_mouse_string_lookup[ self._shortcut_key ] )
return '+'.join( components )
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_SHORTCUT ] = Shortcut
class ShortcutPanel( wx.Panel ):
def __init__( self, parent ):
wx.Panel.__init__( self, parent )
self._mouse_radio = wx.RadioButton( self, style = wx.RB_GROUP, label = 'mouse' )
self._mouse_shortcut = ShortcutMouse( self, self._mouse_radio )
self._keyboard_radio = wx.RadioButton( self, label = 'keyboard' )
self._keyboard_shortcut = ShortcutKeyboard( self, self._keyboard_radio )
#
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.Add( ClientGUICommon.BetterStaticText( self, 'Mouse events only work for the duplicate and archive/delete filters atm!' ), CC.FLAGS_EXPAND_PERPENDICULAR )
gridbox = wx.FlexGridSizer( 2 )
gridbox.AddGrowableCol( 1, 1 )
gridbox.Add( self._mouse_radio, CC.FLAGS_VCENTER )
gridbox.Add( self._mouse_shortcut, CC.FLAGS_EXPAND_BOTH_WAYS )
gridbox.Add( self._keyboard_radio, CC.FLAGS_VCENTER )
gridbox.Add( self._keyboard_shortcut, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.Add( gridbox, CC.FLAGS_EXPAND_BOTH_WAYS )
self.SetSizer( vbox )
def GetValue( self ):
if self._mouse_radio.GetValue() == True:
return self._mouse_shortcut.GetValue()
else:
return self._keyboard_shortcut.GetValue()
def SetValue( self, shortcut ):
if shortcut.GetShortcutType() == CC.SHORTCUT_TYPE_MOUSE:
self._mouse_radio.SetValue( True )
self._mouse_shortcut.SetValue( shortcut )
else:
self._keyboard_radio.SetValue( True )
self._keyboard_shortcut.SetValue( shortcut )
class ShortcutKeyboard( wx.TextCtrl ):
def __init__( self, parent, related_radio = None ):
self._shortcut = Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_F7, [] )
self._related_radio = related_radio
wx.TextCtrl.__init__( self, parent, style = wx.TE_PROCESS_ENTER )
self.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
self._SetShortcutString()
def _SetShortcutString( self ):
display_string = self._shortcut.ToString()
wx.TextCtrl.SetValue( self, display_string )
def EventKeyDown( self, event ):
shortcut = ConvertKeyEventToShortcut( event )
if shortcut is not None:
self._shortcut = shortcut
if self._related_radio is not None:
self._related_radio.SetValue( True )
self._SetShortcutString()
def GetValue( self ):
return self._shortcut
def SetValue( self, shortcut ):
self._shortcut = shortcut
self._SetShortcutString()
class ShortcutMouse( wx.Button ):
def __init__( self, parent, related_radio = None ):
self._shortcut = Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [] )
self._related_radio = related_radio
wx.Button.__init__( self, parent )
self.Bind( wx.EVT_MOUSE_EVENTS, self.EventMouse )
self._SetShortcutString()
def _SetShortcutString( self ):
display_string = self._shortcut.ToString()
self.SetLabel( display_string )
def EventMouse( self, event ):
self.SetFocus()
shortcut = ConvertMouseEventToShortcut( event )
if shortcut is not None:
self._shortcut = shortcut
if self._related_radio is not None:
self._related_radio.SetValue( True )
self._SetShortcutString()
def GetValue( self ):
return self._shortcut
def SetValue( self, shortcut ):
self._shortcut = shortcut
self._SetShortcutString()
class Shortcuts( HydrusSerialisable.SerialisableBaseNamed ):
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_SHORTCUTS
SERIALISABLE_NAME = 'Shortcuts'
SERIALISABLE_VERSION = 2
def __init__( self, name ):
HydrusSerialisable.SerialisableBaseNamed.__init__( self, name )
self._shortcuts_to_commands = {}
def __iter__( self ):
for ( shortcut, command ) in self._shortcuts_to_commands.items():
yield ( shortcut, command )
def __len__( self ):
return len( self._shortcuts_to_commands )
def _GetSerialisableInfo( self ):
return [ ( shortcut.GetSerialisableTuple(), command.GetSerialisableTuple() ) for ( shortcut, command ) in self._shortcuts_to_commands.items() ]
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
for ( serialisable_shortcut, serialisable_command ) in serialisable_info:
shortcut = HydrusSerialisable.CreateFromSerialisableTuple( serialisable_shortcut )
command = HydrusSerialisable.CreateFromSerialisableTuple( serialisable_command )
self._shortcuts_to_commands[ shortcut ] = command
def _UpdateSerialisableInfo( self, version, old_serialisable_info ):
if version == 1:
( serialisable_mouse_actions, serialisable_keyboard_actions ) = old_serialisable_info
shortcuts_to_commands = {}
# this never stored mouse actions, so skip
services_manager = HG.client_controller.services_manager
for ( modifier, key, ( serialisable_service_key, data ) ) in serialisable_keyboard_actions:
if modifier not in CC.shortcut_wx_to_hydrus_lookup:
modifiers = []
else:
modifiers = [ CC.shortcut_wx_to_hydrus_lookup[ modifier ] ]
shortcut = Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, key, modifiers )
if serialisable_service_key is None:
command = ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, data )
else:
service_key = serialisable_service_key.decode( 'hex' )
if not services_manager.ServiceExists( service_key ):
continue
action = HC.CONTENT_UPDATE_FLIP
value = data
service = services_manager.GetService( service_key )
service_type = service.GetServiceType()
if service_type in HC.TAG_SERVICES:
content_type = HC.CONTENT_TYPE_MAPPINGS
elif service_type in HC.RATINGS_SERVICES:
content_type = HC.CONTENT_TYPE_RATINGS
else:
continue
command = ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_CONTENT, ( service_key, content_type, action, value ) )
shortcuts_to_commands[ shortcut ] = command
new_serialisable_info = ( ( shortcut.GetSerialisableTuple(), command.GetSerialisableTuple() ) for ( shortcut, command ) in shortcuts_to_commands.items() )
return ( 2, new_serialisable_info )
def GetCommand( self, shortcut ):
if shortcut in self._shortcuts_to_commands:
return self._shortcuts_to_commands[ shortcut ]
else:
return None
def SetCommand( self, shortcut, command ):
self._shortcuts_to_commands[ shortcut ] = command
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_SHORTCUTS ] = Shortcuts
class ShortcutsHandler( object ):
def __init__( self, parent, initial_shortcuts_names = None ):
@ -97,7 +612,7 @@ class ShortcutsHandler( object ):
def EventCharHook( self, event ):
shortcut = ClientData.ConvertKeyEventToShortcut( event )
shortcut = ConvertKeyEventToShortcut( event )
if shortcut is not None:
@ -133,7 +648,7 @@ class ShortcutsHandler( object ):
def EventMouse( self, event ):
shortcut = ClientData.ConvertMouseEventToShortcut( event )
shortcut = ConvertMouseEventToShortcut( event )
if shortcut is not None:
@ -163,3 +678,4 @@ class ShortcutsHandler( object ):
self._shortcuts_names.remove( shortcuts_name )

View File

@ -1,8 +1,9 @@
import ClientConstants as CC
import ClientData
import ClientGUICommon
import ClientGUIScrolledPanels
import ClientGUITopLevelWindows
import ClientImporting
import ClientImportOptions
import HydrusData
import os
import wx
@ -78,7 +79,7 @@ class EditCheckerOptions( ClientGUIScrolledPanels.EditPanel ):
never_slower_than = self._never_slower_than.GetValue()
death_file_velocity = self._death_file_velocity.GetValue()
return ClientData.CheckerOptions( intended_files_per_check, never_faster_than, never_slower_than, death_file_velocity )
return ClientImportOptions.CheckerOptions( intended_files_per_check, never_faster_than, never_slower_than, death_file_velocity )
( TimeDeltaEvent, EVT_TIME_DELTA ) = wx.lib.newevent.NewCommandEvent()

View File

@ -1,7 +1,7 @@
import ClientCaches
import ClientConstants as CC
import ClientData
import ClientGUIMenus
import ClientGUIShortcuts
import HydrusConstants as HC
import HydrusData
import HydrusExceptions
@ -798,7 +798,7 @@ class FrameThatTakesScrollablePanel( FrameThatResizes ):
def EventCharHook( self, event ):
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
if key == wx.WXK_ESCAPE:

View File

@ -12,6 +12,168 @@ import HydrusText
import os
import re
class CheckerOptions( HydrusSerialisable.SerialisableBase ):
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_CHECKER_OPTIONS
SERIALISABLE_NAME = 'Checker Timing Options'
SERIALISABLE_VERSION = 1
def __init__( self, intended_files_per_check = 8, never_faster_than = 300, never_slower_than = 86400, death_file_velocity = ( 1, 86400 ) ):
HydrusSerialisable.SerialisableBase.__init__( self )
self._intended_files_per_check = intended_files_per_check
self._never_faster_than = never_faster_than
self._never_slower_than = never_slower_than
self._death_file_velocity = death_file_velocity
def _GetCurrentFilesVelocity( self, seed_cache, last_check_time ):
( death_files_found, death_time_delta ) = self._death_file_velocity
since = last_check_time - death_time_delta
current_files_found = seed_cache.GetNumNewFilesSince( since )
# when a thread is only 30mins old (i.e. first file was posted 30 mins ago), we don't want to calculate based on a longer delete time delta
# we want next check to be like 30mins from now, not 12 hours
# so we'll say "5 files in 30 mins" rather than "5 files in 24 hours"
earliest_source_time = seed_cache.GetEarliestSourceTime()
if earliest_source_time is None:
current_time_delta = death_time_delta
else:
early_time_delta = max( last_check_time - earliest_source_time, 30 )
current_time_delta = min( early_time_delta, death_time_delta )
return ( current_files_found, current_time_delta )
def _GetSerialisableInfo( self ):
return ( self._intended_files_per_check, self._never_faster_than, self._never_slower_than, self._death_file_velocity )
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
( self._intended_files_per_check, self._never_faster_than, self._never_slower_than, self._death_file_velocity ) = serialisable_info
def GetNextCheckTime( self, seed_cache, last_check_time ):
if len( seed_cache ) == 0:
if last_check_time == 0:
return 0 # haven't checked yet, so should check immediately
else:
return HydrusData.GetNow() + self._never_slower_than
else:
( current_files_found, current_time_delta ) = self._GetCurrentFilesVelocity( seed_cache, last_check_time )
if current_files_found == 0:
# this shouldn't typically matter, since a dead checker won't care about next check time
# so let's just have a nice safe value in case this is ever asked legit
check_period = self._never_slower_than
else:
approx_time_per_file = current_time_delta / current_files_found
ideal_check_period = self._intended_files_per_check * approx_time_per_file
# if a thread produced lots of files and then stopped completely for whatever reason, we don't want to keep checking fast
# so, we set a lower limit of time since last file upload, neatly doubling our check period in these situations
latest_source_time = seed_cache.GetLatestSourceTime()
time_since_latest_file = max( last_check_time - latest_source_time, 30 )
never_faster_than = max( self._never_faster_than, time_since_latest_file )
check_period = min( max( never_faster_than, ideal_check_period ), self._never_slower_than )
return last_check_time + check_period
def GetRawCurrentVelocity( self, seed_cache, last_check_time ):
return self._GetCurrentFilesVelocity( seed_cache, last_check_time )
def GetPrettyCurrentVelocity( self, seed_cache, last_check_time, no_prefix = False ):
if len( seed_cache ) == 0:
if last_check_time == 0:
pretty_current_velocity = 'no files yet'
else:
pretty_current_velocity = 'no files, unable to determine velocity'
else:
if no_prefix:
pretty_current_velocity = ''
else:
pretty_current_velocity = 'at last check, found '
( current_files_found, current_time_delta ) = self._GetCurrentFilesVelocity( seed_cache, last_check_time )
pretty_current_velocity += HydrusData.ConvertIntToPrettyString( current_files_found ) + ' files in previous ' + HydrusData.ConvertTimeDeltaToPrettyString( current_time_delta )
return pretty_current_velocity
def IsDead( self, seed_cache, last_check_time ):
if len( seed_cache ) == 0 and last_check_time == 0:
return False
else:
( current_files_found, current_time_delta ) = self._GetCurrentFilesVelocity( seed_cache, last_check_time )
( death_files_found, deleted_time_delta ) = self._death_file_velocity
current_file_velocity_float = current_files_found / float( current_time_delta )
death_file_velocity_float = death_files_found / float( deleted_time_delta )
return current_file_velocity_float < death_file_velocity_float
def ToTuple( self ):
return ( self._intended_files_per_check, self._never_faster_than, self._never_slower_than, self._death_file_velocity )
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_CHECKER_OPTIONS ] = CheckerOptions
class FilenameTaggingOptions( HydrusSerialisable.SerialisableBase ):
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_FILENAME_TAGGING_OPTIONS
@ -704,7 +866,7 @@ class TagImportOptions( HydrusSerialisable.SerialisableBase ):
def GetServiceKeysToContentUpdates( self, hash, tags ):
tags = [ tag for tag in tags if tag is not None ]
tags = HydrusTags.CleanTags( tags )
service_keys_to_tags = collections.defaultdict( set )
@ -713,18 +875,12 @@ class TagImportOptions( HydrusSerialisable.SerialisableBase ):
for ( service_key, namespaces ) in self._service_keys_to_namespaces.items():
tags_to_add_here = []
if len( namespaces ) > 0:
if len( namespaces ) == 0:
for namespace in namespaces:
if namespace == '': tags_to_add_here.extend( [ tag for tag in tags if not ':' in tag ] )
else: tags_to_add_here.extend( [ tag for tag in tags if tag.startswith( namespace + ':' ) ] )
continue
tags_to_add_here = HydrusTags.CleanTags( tags_to_add_here )
tags_to_add_here = [ tag for tag in tags if HydrusTags.SplitTag( tag )[0] in namespaces ]
if len( tags_to_add_here ) > 0:

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,6 @@
import bisect
import collections
import ClientConstants as CC
import ClientData
import ClientFiles
import ClientRatings
import ClientSearch

1211
include/ClientOptions.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,19 @@
import HydrusConstants as HC
import HydrusGlobals as HG
import HydrusPaths
import webbrowser
def DeletePath( path ):
if HC.options[ 'delete_to_recycle_bin' ] == True:
HydrusPaths.RecyclePath( path )
else:
HydrusPaths.DeletePath( path )
def GetCurrentTempDir():
temp_path_override = HG.client_controller.new_options.GetNoneableString( 'temp_path_override' )

View File

@ -263,6 +263,42 @@ def RenderTag( tag, render_for_user ):
return namespace + connector + subtag
def SortTagsList( tags, sort_type ):
if sort_type in ( CC.SORT_BY_LEXICOGRAPHIC_DESC, CC.SORT_BY_LEXICOGRAPHIC_NAMESPACE_DESC ):
reverse = True
else:
reverse = False
if sort_type in ( CC.SORT_BY_LEXICOGRAPHIC_NAMESPACE_ASC, CC.SORT_BY_LEXICOGRAPHIC_NAMESPACE_DESC ):
def key( tag ):
# '{' is above 'z' in ascii, so this works for most situations
( namespace, subtag ) = HydrusTags.SplitTag( tag )
if namespace == '':
return ( '{', subtag )
else:
return ( namespace, subtag )
else:
key = None
tags.sort( key = key, reverse = reverse )
class TagFilter( HydrusSerialisable.SerialisableBase ):
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_TAG_FILTER

View File

@ -363,9 +363,9 @@ class JobKey( object ):
class WXAwareJob( HydrusThreading.SchedulableJob ):
def __init__( self, controller, scheduler, window, work_callable, initial_delay = 0.0 ):
def __init__( self, controller, scheduler, window, initial_delay, work_callable ):
HydrusThreading.SchedulableJob.__init__( self, controller, scheduler, work_callable, initial_delay = initial_delay )
HydrusThreading.SchedulableJob.__init__( self, controller, scheduler, initial_delay, work_callable )
self._window = window
@ -409,9 +409,9 @@ class WXAwareJob( HydrusThreading.SchedulableJob ):
class WXAwareRepeatingJob( HydrusThreading.RepeatingJob ):
def __init__( self, controller, scheduler, window, work_callable, period, initial_delay = 0.0 ):
def __init__( self, controller, scheduler, window, initial_delay, period, work_callable ):
HydrusThreading.RepeatingJob.__init__( self, controller, scheduler, work_callable, period, initial_delay = initial_delay )
HydrusThreading.RepeatingJob.__init__( self, controller, scheduler, initial_delay, period, work_callable )
self._window = window

View File

@ -49,7 +49,7 @@ options = {}
# Misc
NETWORK_VERSION = 18
SOFTWARE_VERSION = 306
SOFTWARE_VERSION = 307
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )

View File

@ -78,7 +78,7 @@ class HydrusController( object ):
calling_from_the_thread_pool = threading.current_thread() in self._call_to_threads
if calling_from_the_thread_pool or len( self._call_to_threads ) < 10:
if calling_from_the_thread_pool or len( self._call_to_threads ) < 200:
call_to_thread = HydrusThreading.THREADCallToThread( self, 'CallToThread' )
@ -207,22 +207,22 @@ class HydrusController( object ):
self._pubsub.sub( object, method_name, topic )
def CallLater( self, delay, func, *args, **kwargs ):
def CallLater( self, initial_delay, func, *args, **kwargs ):
call = HydrusData.Call( func, *args, **kwargs )
job = HydrusThreading.SchedulableJob( self, self._job_scheduler, call, initial_delay = delay )
job = HydrusThreading.SchedulableJob( self, self._job_scheduler, initial_delay, call )
self._job_scheduler.AddJob( job )
return job
def CallRepeating( self, period, delay, func, *args, **kwargs ):
def CallRepeating( self, initial_delay, period, func, *args, **kwargs ):
call = HydrusData.Call( func, *args, **kwargs )
job = HydrusThreading.RepeatingJob( self, self._job_scheduler, call, period, initial_delay = delay )
job = HydrusThreading.RepeatingJob( self, self._job_scheduler, initial_delay, period, call )
self._job_scheduler.AddJob( job )
@ -372,9 +372,9 @@ class HydrusController( object ):
self._daemons.append( HydrusThreading.DAEMONBackgroundWorker( self, 'MaintainDB', HydrusDaemons.DAEMONMaintainDB, period = 300, init_wait = 60 ) )
self.CallRepeating( 120.0, 10.0, self.SleepCheck )
self.CallRepeating( 60.0, 10.0, self.MaintainMemoryFast )
self.CallRepeating( 300.0, 10.0, self.MaintainMemorySlow )
self.CallRepeating( 10.0, 120.0, self.SleepCheck )
self.CallRepeating( 10.0, 60.0, self.MaintainMemoryFast )
self.CallRepeating( 10.0, 300.0, self.MaintainMemorySlow )
def IsFirstStart( self ):

View File

@ -171,6 +171,8 @@ class HydrusDB( object ):
self._InitDB()
self._RepairDB()
( version, ) = self._c.execute( 'SELECT version FROM version;' ).fetchone()
if version < HC.SOFTWARE_VERSION - 50:
@ -225,8 +227,6 @@ class HydrusDB( object ):
( version, ) = self._c.execute( 'SELECT version FROM version;' ).fetchone()
self._RepairDB()
self._CloseDBCursor()
self._controller.CallToThreadLongRunning( self.MainLoop )

View File

@ -78,6 +78,7 @@ SERIALISABLE_TYPE_PARSE_FORMULA_CONTEXT_VARIABLE = 60
SERIALISABLE_TYPE_TAG_SUMMARY_GENERATOR = 61
SERIALISABLE_TYPE_PARSE_RULE_HTML = 62
SERIALISABLE_TYPE_SIMPLE_DOWNLOADER_PARSE_FORMULA = 63
SERIALISABLE_TYPE_MULTIPLE_WATCHER_IMPORT = 64
SERIALISABLE_TYPES_TO_OBJECT_TYPES = {}

View File

@ -193,13 +193,9 @@ def CleanTag( tag ):
tag = HydrusData.ToUnicode( tag )
if tag.startswith( ':' ):
tag = HydrusText.re_leading_single_colon.sub( '::', tag ) # Convert anything starting with one colon to start with two i.e. :D -> ::D
tag = StripTextOfGumpf( tag )
elif ':' in tag:
tag = HydrusText.re_leading_single_colon.sub( '::', tag ) # Convert anything starting with one colon to start with two i.e. :D -> ::D
if ':' in tag:
tag = StripTextOfGumpf( tag ) # need to repeat here to catch 'system:' stuff
@ -257,7 +253,7 @@ def CombineTag( namespace, subtag ):
if namespace == '':
if subtag.startswith( ':' ):
if HydrusText.re_leading_single_colon.search( subtag ) is not None:
return ':' + subtag

View File

@ -514,7 +514,7 @@ class JobScheduler( threading.Thread ):
class SchedulableJob( object ):
def __init__( self, controller, scheduler, work_callable, initial_delay = 0.0 ):
def __init__( self, controller, scheduler, initial_delay, work_callable ):
self._controller = controller
self._scheduler = scheduler
@ -575,13 +575,6 @@ class SchedulableJob( object ):
return HydrusData.TimeHasPassedFloat( self._next_work_time )
def MoveNextWorkTimeToNow( self ):
self._next_work_time = HydrusData.GetNowFloat()
self._scheduler.WorkTimesHaveChanged()
def StartWork( self ):
if self._is_cancelled.is_set():
@ -594,6 +587,13 @@ class SchedulableJob( object ):
self._BootWorker()
def Wake( self ):
self._next_work_time = HydrusData.GetNowFloat()
self._scheduler.WorkTimesHaveChanged()
def Work( self ):
try:
@ -611,9 +611,9 @@ class SchedulableJob( object ):
class RepeatingJob( SchedulableJob ):
def __init__( self, controller, scheduler, work_callable, period, initial_delay = 0.0 ):
def __init__( self, controller, scheduler, initial_delay, period, work_callable ):
SchedulableJob.__init__( self, controller, scheduler, work_callable, initial_delay = initial_delay )
SchedulableJob.__init__( self, controller, scheduler, initial_delay, work_callable )
self._period = period

View File

@ -1,5 +1,4 @@
import ClientConstants as CC
import ClientData
import ClientGUIManagement
import ClientGUIDialogsManage
import ClientCaches

View File

@ -1,5 +1,4 @@
import ClientConstants as CC
import ClientData
import ClientImporting
import ClientImportOptions
import HydrusConstants as HC
@ -11,10 +10,10 @@ class TestData( unittest.TestCase ):
def test_checker_options( self ):
regular_checker_options = ClientData.CheckerOptions( intended_files_per_check = 5, never_faster_than = 30, never_slower_than = 86400, death_file_velocity = ( 1, 86400 ) )
fast_checker_options = ClientData.CheckerOptions( intended_files_per_check = 2, never_faster_than = 30, never_slower_than = 86400, death_file_velocity = ( 1, 86400 ) )
slow_checker_options = ClientData.CheckerOptions( intended_files_per_check = 10, never_faster_than = 30, never_slower_than = 86400, death_file_velocity = ( 1, 86400 ) )
callous_checker_options = ClientData.CheckerOptions( intended_files_per_check = 5, never_faster_than = 30, never_slower_than = 86400, death_file_velocity = ( 1, 60 ) )
regular_checker_options = ClientImportOptions.CheckerOptions( intended_files_per_check = 5, never_faster_than = 30, never_slower_than = 86400, death_file_velocity = ( 1, 86400 ) )
fast_checker_options = ClientImportOptions.CheckerOptions( intended_files_per_check = 2, never_faster_than = 30, never_slower_than = 86400, death_file_velocity = ( 1, 86400 ) )
slow_checker_options = ClientImportOptions.CheckerOptions( intended_files_per_check = 10, never_faster_than = 30, never_slower_than = 86400, death_file_velocity = ( 1, 86400 ) )
callous_checker_options = ClientImportOptions.CheckerOptions( intended_files_per_check = 5, never_faster_than = 30, never_slower_than = 86400, death_file_velocity = ( 1, 60 ) )
empty_seed_cache = ClientImporting.SeedCache()

View File

@ -216,9 +216,7 @@ class TestListBoxes( unittest.TestCase ):
finally:
frame.Hide()
frame.Destroy()
frame.DestroyLater()

View File

@ -1,6 +1,6 @@
import collections
import ClientConstants as CC
import ClientData
import ClientOptions
import HydrusConstants as HC
import HydrusGlobals as HG
import HydrusTags
@ -36,7 +36,7 @@ class MockController( object ):
self.model_is_shutdown = False
self.new_options = ClientData.ClientOptions()
self.new_options = ClientOptions.ClientOptions()
def CallToThread( self, callable, *args, **kwargs ):

View File

@ -749,7 +749,7 @@ class TestClientDB( unittest.TestCase ):
finally:
test_frame.Destroy()
test_frame.DestroyLater()

View File

@ -3,6 +3,8 @@ import ClientConstants as CC
import ClientData
import ClientDefaults
import ClientDownloading
import ClientDuplicates
import ClientGUIShortcuts
import ClientImporting
import ClientImportOptions
import ClientMedia
@ -155,9 +157,9 @@ class TestSerialisables( unittest.TestCase ):
self.assertEqual( obj.ToTuple(), dupe_obj.ToTuple() )
duplicate_action_options_delete_and_move = ClientData.DuplicateActionOptions( [ ( CC.LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_MOVE, ClientTags.TagFilter() ) ], [ ( TC.LOCAL_RATING_LIKE_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_MOVE ), ( TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_MOVE ) ], True )
duplicate_action_options_copy = ClientData.DuplicateActionOptions( [ ( CC.LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_COPY, ClientTags.TagFilter() ) ], [ ( TC.LOCAL_RATING_LIKE_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_COPY ), ( TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_COPY ) ], False )
duplicate_action_options_merge = ClientData.DuplicateActionOptions( [ ( CC.LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE, ClientTags.TagFilter() ) ], [ ( TC.LOCAL_RATING_LIKE_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE ), ( TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE ) ], False )
duplicate_action_options_delete_and_move = ClientDuplicates.DuplicateActionOptions( [ ( CC.LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_MOVE, ClientTags.TagFilter() ) ], [ ( TC.LOCAL_RATING_LIKE_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_MOVE ), ( TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_MOVE ) ], True )
duplicate_action_options_copy = ClientDuplicates.DuplicateActionOptions( [ ( CC.LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_COPY, ClientTags.TagFilter() ) ], [ ( TC.LOCAL_RATING_LIKE_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_COPY ), ( TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_COPY ) ], False )
duplicate_action_options_merge = ClientDuplicates.DuplicateActionOptions( [ ( CC.LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE, ClientTags.TagFilter() ) ], [ ( TC.LOCAL_RATING_LIKE_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE ), ( TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE ) ], False )
inbox = True
size = 40960
@ -395,16 +397,16 @@ class TestSerialisables( unittest.TestCase ):
shortcuts = []
shortcuts.append( ( ClientData.Shortcut(), 'f7' ) )
shortcuts.append( ( ClientGUIShortcuts.Shortcut(), 'f7' ) )
shortcuts.append( ( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_SPACE, [] ), 'space' ) )
shortcuts.append( ( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'a' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), 'ctrl+a' ) )
shortcuts.append( ( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'A' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), 'ctrl+a' ) )
shortcuts.append( ( ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_HOME, [ CC.SHORTCUT_MODIFIER_ALT, CC.SHORTCUT_MODIFIER_CTRL ] ), 'ctrl+alt+home' ) )
shortcuts.append( ( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_SPACE, [] ), 'space' ) )
shortcuts.append( ( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'a' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), 'ctrl+a' ) )
shortcuts.append( ( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'A' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), 'ctrl+a' ) )
shortcuts.append( ( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_HOME, [ CC.SHORTCUT_MODIFIER_ALT, CC.SHORTCUT_MODIFIER_CTRL ] ), 'ctrl+alt+home' ) )
shortcuts.append( ( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [] ), 'left-click' ) )
shortcuts.append( ( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_MIDDLE, [ CC.SHORTCUT_MODIFIER_CTRL ] ), 'ctrl+middle-click' ) )
shortcuts.append( ( ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_SCROLL_DOWN, [ CC.SHORTCUT_MODIFIER_ALT, CC.SHORTCUT_MODIFIER_SHIFT ] ), 'alt+shift+scroll down' ) )
shortcuts.append( ( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [] ), 'left-click' ) )
shortcuts.append( ( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_MIDDLE, [ CC.SHORTCUT_MODIFIER_CTRL ] ), 'ctrl+middle-click' ) )
shortcuts.append( ( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_SCROLL_DOWN, [ CC.SHORTCUT_MODIFIER_ALT, CC.SHORTCUT_MODIFIER_SHIFT ] ), 'alt+shift+scroll down' ) )
for ( shortcut, s ) in shortcuts:
@ -435,16 +437,16 @@ class TestSerialisables( unittest.TestCase ):
command_2 = ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_CONTENT, ( HydrusData.GenerateKey(), HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_FLIP, 'test' ) )
command_3 = ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_CONTENT, ( CC.LOCAL_TAG_SERVICE_KEY, HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_FLIP, 'test' ) )
k_shortcut_1 = ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_SPACE, [] )
k_shortcut_2 = ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'a' ), [ CC.SHORTCUT_MODIFIER_CTRL ] )
k_shortcut_3 = ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'A' ), [ CC.SHORTCUT_MODIFIER_CTRL ] )
k_shortcut_4 = ClientData.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_HOME, [ CC.SHORTCUT_MODIFIER_ALT, CC.SHORTCUT_MODIFIER_CTRL ] )
k_shortcut_1 = ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_SPACE, [] )
k_shortcut_2 = ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'a' ), [ CC.SHORTCUT_MODIFIER_CTRL ] )
k_shortcut_3 = ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, ord( 'A' ), [ CC.SHORTCUT_MODIFIER_CTRL ] )
k_shortcut_4 = ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_HOME, [ CC.SHORTCUT_MODIFIER_ALT, CC.SHORTCUT_MODIFIER_CTRL ] )
m_shortcut_1 = ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [] )
m_shortcut_2 = ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_MIDDLE, [ CC.SHORTCUT_MODIFIER_CTRL ] )
m_shortcut_3 = ClientData.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_SCROLL_DOWN, [ CC.SHORTCUT_MODIFIER_ALT, CC.SHORTCUT_MODIFIER_SHIFT ] )
m_shortcut_1 = ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [] )
m_shortcut_2 = ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_MIDDLE, [ CC.SHORTCUT_MODIFIER_CTRL ] )
m_shortcut_3 = ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_SCROLL_DOWN, [ CC.SHORTCUT_MODIFIER_ALT, CC.SHORTCUT_MODIFIER_SHIFT ] )
shortcuts = ClientData.Shortcuts( 'test' )
shortcuts = ClientGUIShortcuts.Shortcuts( 'test' )
shortcuts.SetCommand( k_shortcut_1, command_1 )
shortcuts.SetCommand( k_shortcut_2, command_2 )
@ -490,7 +492,7 @@ class TestSerialisables( unittest.TestCase ):
gallery_identifier = ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_BOORU, 'gelbooru' )
gallery_stream_identifiers = ClientDownloading.GetGalleryStreamIdentifiers( gallery_identifier )
queries = [ ClientImporting.SubscriptionQuery( 'test query' ), ClientImporting.SubscriptionQuery( 'test query 2' ) ]
checker_options = ClientData.CheckerOptions()
checker_options = ClientImportOptions.CheckerOptions()
initial_file_limit = 100
periodic_file_limit = 50
paused = False

View File

@ -1,6 +1,3 @@
import ClientConstants as CC
import ClientData
import ClientFiles
import ClientLocalServer
import ClientMedia
import ClientRatings
@ -13,18 +10,13 @@ import HydrusNetwork
import HydrusPaths
import HydrusServer
import HydrusServerResources
import HydrusSerialisable
import itertools
import os
import random
import ServerFiles
import ServerServer
import shutil
import ssl
import stat
import TestConstants
import time
import threading
import unittest
from twisted.internet import reactor
from twisted.internet.endpoints import TCP4ClientEndpoint, connectProtocol

View File

@ -7,7 +7,6 @@ import unittest
import HydrusData
import ClientCaches
import ClientConstants as CC
import ClientData
import ClientMedia
import ClientSearch
import HydrusGlobals as HG

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

21
test.py
View File

@ -50,6 +50,7 @@ import wx
from twisted.internet import reactor
from include import ClientCaches
from include import ClientData
from include import ClientOptions
from include import HydrusData
from include import HydrusPaths
@ -83,7 +84,7 @@ class Controller( object ):
self._pubsub = HydrusPubSub.HydrusPubSub( self )
self.new_options = ClientData.ClientOptions( self.db_dir )
self.new_options = ClientOptions.ClientOptions( self.db_dir )
HC.options = ClientDefaults.GetClientDefaultOptions()
@ -288,44 +289,44 @@ class Controller( object ):
CallToThreadLongRunning = CallToThread
def CallLater( self, delay, func, *args, **kwargs ):
def CallLater( self, initial_delay, func, *args, **kwargs ):
call = HydrusData.Call( func, *args, **kwargs )
job = HydrusThreading.SchedulableJob( self, self._job_scheduler, call, initial_delay = delay )
job = HydrusThreading.SchedulableJob( self, self._job_scheduler, initial_delay, call )
self._job_scheduler.AddJob( job )
return job
def CallLaterWXSafe( self, window, delay, func, *args, **kwargs ):
def CallLaterWXSafe( self, window, initial_delay, func, *args, **kwargs ):
call = HydrusData.Call( func, *args, **kwargs )
job = ClientThreading.WXAwareJob( self, self._job_scheduler, window, call, initial_delay = delay )
job = ClientThreading.WXAwareJob( self, self._job_scheduler, window, initial_delay, call )
self._job_scheduler.AddJob( job )
return job
def CallRepeating( self, period, delay, func, *args, **kwargs ):
def CallRepeating( self, initial_delay, period, func, *args, **kwargs ):
call = HydrusData.Call( func, *args, **kwargs )
job = HydrusThreading.RepeatingJob( self, self._job_scheduler, call, period, initial_delay = delay )
job = HydrusThreading.RepeatingJob( self, self._job_scheduler, initial_delay, period, call )
self._job_scheduler.AddJob( job )
return job
def CallRepeatingWXSafe( self, window, period, delay, func, *args, **kwargs ):
def CallRepeatingWXSafe( self, window, initial_delay, period, func, *args, **kwargs ):
call = HydrusData.Call( func, *args, **kwargs )
job = ClientThreading.WXAwareRepeatingJob( self, self._job_scheduler, window, call, period, initial_delay = delay )
job = ClientThreading.WXAwareRepeatingJob( self, self._job_scheduler, window, initial_delay, period, call )
self._job_scheduler.AddJob( job )
@ -566,7 +567,7 @@ if __name__ == '__main__':
controller.Run()
win.Destroy()
win.DestroyLater()
wx.CallAfter( do_it )