diff --git a/help/changelog.html b/help/changelog.html
index cfb950b7..245be468 100755
--- a/help/changelog.html
+++ b/help/changelog.html
@@ -8,6 +8,34 @@
changelog
+
+ version 269
+ - nested pages now supported!
+ - moved all page management (session load/save, new/close page, page navigation, page name maintenance, etc...) code from the main gui to the new PagesNotebook object
+ - expanded the session object to hold nested page information
+ - added a 'page of pages' page to the 'special' new page entry!
+ - numerous other gui-notebook page-related event fixes and improvements
+ - figured out cross-platform menu and other mouse event support for nested notebooks, but there may still be holes--please let me know if your new pages ever appear in the wrong tier!
+ - ways to move pages up and down rows will come in the coming weeks!
+ - main gui status bar now shows total bandwidth this session as well as current speed
+ - added a cog button to network job controls that allow for manual- and auto-bandwidth-override
+ - added a 'blocked?' column to the review bandwidth panel to quickly see which network contexts are currently available to do work
+ - added a button to reset 'default' and 'global' bandwidth rules to review bandwidth panel
+ - merged the network request start test and consumption code into one transaction, stopping some accidental overconsumption when the engine was under heavy load
+ - did some logic work to make sure unusual network context rule/usage situations are visible on the review bandwidth panel for editing
+ - network jobs now report more information as they get ready to start, including while they are held up waiting for a download slot
+ - added a simple 'network profile mode' to the debug menu that atm just prints a summary of new jobs
+ - fixed an error that could sometimes be a crash when 'review services' was opened while no pages were open in the main gui
+ - fixed the pixiv login test button with a hacky workaround--I will make extensive proper login testing gui when I move to the login engine
+ - fixed an issue in the youtube downloader
+ - if the bandwidth or session managers are missing on boot, empty defaults will be created in their stead
+ - bumped the max page limit up to 150--I expect to increase it more in the coming months as I rejigger how some gui stuff is laid out
+ - renamed 'sort by age' to 'time imported'
+ - fixed and improved some test code
+ - cleaned up some shutdown code
+ - should have fixed a rare unicode conversion issue when printing to log
+ - misc improvements
+
version 268
- split the sort dropdown into two, splitting the sort type and sort order
diff --git a/include/ClientCaches.py b/include/ClientCaches.py
index 961d4a15..7b480dbc 100644
--- a/include/ClientCaches.py
+++ b/include/ClientCaches.py
@@ -3362,7 +3362,7 @@ class WebSessionManagerClient( object ):
if 'pixivAccount.postKey' not in j:
- raise HydrusExceptions.ForbiddenException( 'When trying to log into Pixiv, I could not find the POST key!' )
+ raise HydrusExceptions.ForbiddenException( 'When trying to log into Pixiv, I could not find the POST key! This is a problem with hydrus\'s pixiv parsing, not your login! Please contact hydrus dev!' )
post_key = j[ 'pixivAccount.postKey' ]
@@ -3386,3 +3386,80 @@ class WebSessionManagerClient( object ):
r = session.post( 'https://accounts.pixiv.net/api/login?lang=en', data = form_fields, headers = headers )
+ def TestPixiv( self, pixiv_id, password ):
+
+ # this is just an ugly copy, but fuck it for the minute
+ # we'll figure out a proper testing engine later with the login engine and tie the manage gui into it as well
+
+ session = requests.Session()
+
+ response = session.get( 'https://accounts.pixiv.net/login' )
+
+ soup = ClientDownloading.GetSoup( response.content )
+
+ # some whocking 20kb bit of json tucked inside a hidden form input wew lad
+ i = soup.find( 'input', id = 'init-config' )
+
+ raw_json = i['value']
+
+ j = json.loads( raw_json )
+
+ if 'pixivAccount.postKey' not in j:
+
+ return ( False, 'When trying to log into Pixiv, I could not find the POST key! This is a problem with hydrus\'s pixiv parsing, not your login! Please contact hydrus dev!' )
+
+
+ post_key = j[ 'pixivAccount.postKey' ]
+
+ form_fields = {}
+
+ form_fields[ 'pixiv_id' ] = pixiv_id
+ form_fields[ 'password' ] = password
+ form_fields[ 'captcha' ] = ''
+ form_fields[ 'g_recaptcha_response' ] = ''
+ form_fields[ 'return_to' ] = 'https://www.pixiv.net'
+ form_fields[ 'lang' ] = 'en'
+ form_fields[ 'post_key' ] = post_key
+ form_fields[ 'source' ] = 'pc'
+
+ headers = {}
+
+ headers[ 'referer' ] = "https://accounts.pixiv.net/login?lang=en^source=pc&view_type=page&ref=wwwtop_accounts_index"
+ headers[ 'origin' ] = "https://accounts.pixiv.net"
+
+ r = session.post( 'https://accounts.pixiv.net/api/login?lang=en', data = form_fields, headers = headers )
+
+ if not r.ok:
+
+ HydrusData.ShowText( r.content )
+
+ return ( False, 'Login request failed! Info printed to log.' )
+
+
+ cookies = session.cookies
+
+ cookies.clear_expired_cookies()
+
+ domains = cookies.list_domains()
+
+ for domain in domains:
+
+ if domain.endswith( 'pixiv.net' ):
+
+ d = cookies.get_dict( domain )
+
+ if 'PHPSESSID' not in d:
+
+ HydrusData.ShowText( r.content )
+
+ return ( False, 'Pixiv login failed to establish session! Info printed to log.' )
+
+
+ return ( True, '' )
+
+
+
+ HydrusData.ShowText( r.content )
+
+ return ( False, 'Pixiv login failed to establish session! Info printed to log.' )
+
diff --git a/include/ClientConstants.py b/include/ClientConstants.py
index 04d4180d..e3ed8113 100755
--- a/include/ClientConstants.py
+++ b/include/ClientConstants.py
@@ -29,7 +29,7 @@ COLOUR_UNSELECTED = wx.Colour( 223, 227, 230 )
COLOUR_MESSAGE = wx.Colour( 230, 246, 255 )
-SHORTCUT_HELP = '''You can set up many custom shortcuts in file->options->shortcuts. Please check that to see your current mapping.
+SHORTCUT_HELP = '''You can set up many custom shortcuts in file->shortcuts. Please check that to see your current mapping.
Some shortcuts remain hardcoded, however:
diff --git a/include/ClientController.py b/include/ClientController.py
index 063126f4..39c2ae8d 100755
--- a/include/ClientController.py
+++ b/include/ClientController.py
@@ -577,7 +577,25 @@ class Controller( HydrusController.HydrusController ):
#
bandwidth_manager = self.Read( 'serialisable', HydrusSerialisable.SERIALISABLE_TYPE_NETWORK_BANDWIDTH_MANAGER )
+
+ if bandwidth_manager is None:
+
+ bandwidth_manager = ClientNetworking.NetworkBandwidthManager()
+
+ ClientDefaults.SetDefaultBandwidthManagerRules( bandwidth_manager )
+
+ wx.MessageBox( 'Your bandwidth manager was missing on boot! I have recreated a new empty one with default rules. Please check that your hard drive and client are ok and let the hydrus dev know the details if there is a mystery.' )
+
+
session_manager = self.Read( 'serialisable', HydrusSerialisable.SERIALISABLE_TYPE_NETWORK_SESSION_MANAGER )
+
+ if session_manager is None:
+
+ session_manager = ClientNetworking.NetworkSessionManager()
+
+ wx.MessageBox( 'Your session manager was missing on boot! I have recreated a new empty one. Please check that your hard drive and client are ok and let the hydrus dev know the details if there is a mystery.' )
+
+
login_manager = ClientNetworking.NetworkLoginManager()
self.network_engine = ClientNetworking.NetworkEngine( self, bandwidth_manager, session_manager, login_manager )
diff --git a/include/ClientDB.py b/include/ClientDB.py
index 9459eff5..233dec3d 100755
--- a/include/ClientDB.py
+++ b/include/ClientDB.py
@@ -2846,7 +2846,9 @@ class DB( HydrusDB.HydrusDB ):
self._SetJSONDump( shortcuts )
- bandwidth_manager = ClientDefaults.GetDefaultBandwidthManager()
+ bandwidth_manager = ClientNetworking.NetworkBandwidthManager()
+
+ ClientDefaults.SetDefaultBandwidthManagerRules( bandwidth_manager )
self._SetJSONDump( bandwidth_manager )
@@ -4896,13 +4898,21 @@ class DB( HydrusDB.HydrusDB ):
def _GetJSONDump( self, dump_type ):
- ( version, dump ) = self._c.execute( 'SELECT version, dump FROM json_dumps WHERE dump_type = ?;', ( dump_type, ) ).fetchone()
+ result = self._c.execute( 'SELECT version, dump FROM json_dumps WHERE dump_type = ?;', ( dump_type, ) ).fetchone()
- serialisable_info = json.loads( dump )
+ if result is None:
+
+ return result
+
+ else:
+
+ ( version, dump ) = result
+
+ serialisable_info = json.loads( dump )
+
+ return HydrusSerialisable.CreateFromSerialisableTuple( ( dump_type, version, serialisable_info ) )
+
- return HydrusSerialisable.CreateFromSerialisableTuple( ( dump_type, version, serialisable_info ) )
-
-
def _GetJSONDumpNamed( self, dump_type, dump_name = None ):
@@ -9610,7 +9620,9 @@ class DB( HydrusDB.HydrusDB ):
#
- bandwidth_manager = ClientDefaults.GetDefaultBandwidthManager()
+ bandwidth_manager = ClientNetworking.NetworkBandwidthManager()
+
+ ClientDefaults.SetDefaultBandwidthManagerRules( bandwidth_manager )
self._SetJSONDump( bandwidth_manager )
@@ -9683,7 +9695,9 @@ class DB( HydrusDB.HydrusDB ):
if version == 264:
- default_bandwidth_manager = ClientDefaults.GetDefaultBandwidthManager()
+ default_bandwidth_manager = ClientNetworking.NetworkBandwidthManager()
+
+ ClientDefaults.SetDefaultBandwidthManagerRules( default_bandwidth_manager )
bandwidth_manager = self._GetJSONDump( HydrusSerialisable.SERIALISABLE_TYPE_NETWORK_BANDWIDTH_MANAGER )
diff --git a/include/ClientDefaults.py b/include/ClientDefaults.py
index 5c9d53ae..27dd9048 100644
--- a/include/ClientDefaults.py
+++ b/include/ClientDefaults.py
@@ -7,14 +7,12 @@ import HydrusNetworking
import os
import wx
-def GetDefaultBandwidthManager():
+def SetDefaultBandwidthManagerRules( bandwidth_manager ):
KB = 1024
MB = 1024 ** 2
GB = 1024 ** 3
- bandwidth_manager = ClientNetworking.NetworkBandwidthManager()
-
#
rules = HydrusNetworking.BandwidthRules()
@@ -82,10 +80,6 @@ def GetDefaultBandwidthManager():
bandwidth_manager.SetRules( ClientNetworking.NetworkContext( CC.NETWORK_CONTEXT_THREAD_WATCHER_THREAD ), rules )
- #
-
- return bandwidth_manager
-
def GetClientDefaultOptions():
options = {}
diff --git a/include/ClientGUI.py b/include/ClientGUI.py
index 04c5c246..b664436f 100755
--- a/include/ClientGUI.py
+++ b/include/ClientGUI.py
@@ -73,7 +73,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
self.SetDropTarget( ClientDragDrop.FileDropTarget( self.ImportFiles, self.ImportURL ) )
- bandwidth_width = ClientData.ConvertTextToPixelWidth( self, 9 )
+ 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 )
@@ -86,17 +86,11 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
self._focus_holder = wx.Window( self, size = ( 0, 0 ) )
- self._media_status_override = None
self._closed_pages = []
self._deleted_page_keys = set()
- self._next_new_page_index = None
self._lock = threading.Lock()
- self._notebook = wx.Notebook( self )
- self._notebook.Bind( wx.EVT_LEFT_DCLICK, self.EventNotebookLeftDoubleClick )
- self._notebook.Bind( wx.EVT_MIDDLE_DOWN, self.EventNotebookMiddleClick )
- self._notebook.Bind( wx.EVT_RIGHT_DOWN, self.EventNotebookMenu )
- self.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGED, self.EventNotebookPageChanged )
+ self._notebook = ClientGUIPages.PagesNotebook( self, self._controller, 'top page notebook' )
wx.GetApp().SetTopWindow( self )
@@ -112,24 +106,15 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
self._controller.sub( self, 'AddModalMessage', 'modal_message' )
self._controller.sub( self, 'ClearClosedPages', 'clear_closed_pages' )
- self._controller.sub( self, 'NewCompose', 'new_compose_frame' )
- self._controller.sub( self, 'NewPageDuplicateFilter', 'new_duplicate_filter' )
- self._controller.sub( self, 'NewPageImportBooru', 'new_import_booru' )
- self._controller.sub( self, 'NewPageImportGallery', 'new_import_gallery' )
self._controller.sub( self, 'NewPageImportHDD', 'new_hdd_import' )
- self._controller.sub( self, 'NewPageImportPageOfImages', 'new_page_import_page_of_images' )
- self._controller.sub( self, 'NewPageImportThreadWatcher', 'new_page_import_thread_watcher' )
- self._controller.sub( self, 'NewPageImportURLs', 'new_page_import_urls' )
- self._controller.sub( self, 'NewPagePetitions', 'new_page_petitions' )
self._controller.sub( self, 'NewPageQuery', 'new_page_query' )
- self._controller.sub( self, 'NewSimilarTo', 'new_similar_to' )
+ self._controller.sub( self, 'NotifyClosedPage', 'notify_closed_page' )
self._controller.sub( self, 'NotifyNewOptions', 'notify_new_options' )
self._controller.sub( self, 'NotifyNewPending', 'notify_new_pending' )
self._controller.sub( self, 'NotifyNewPermissions', 'notify_new_permissions' )
self._controller.sub( self, 'NotifyNewServices', 'notify_new_services_gui' )
self._controller.sub( self, 'NotifyNewSessions', 'notify_new_sessions' )
self._controller.sub( self, 'NotifyNewUndo', 'notify_new_undo' )
- self._controller.sub( self, 'RefreshPageName', 'refresh_page_name' )
self._controller.sub( self._statusbar_thread_updater, 'Update', 'refresh_status' )
self._controller.sub( self, 'SetDBLockedStatus', 'db_locked_status' )
self._controller.sub( self, 'SetMediaFocus', 'set_media_focus' )
@@ -154,9 +139,9 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
wx.CallAfter( self._InitialiseSession ) # do this in callafter as some pages want to talk to controller.gui, which doesn't exist yet!
- self._move_hide_timer = wx.Timer( self, id = ID_TIMER_GUI_BANDWIDTH )
+ self._bandwidth_timer = wx.Timer( self, id = ID_TIMER_GUI_BANDWIDTH )
- self._move_hide_timer.Start( 1000, wx.TIMER_CONTINUOUS )
+ self._bandwidth_timer.Start( 1000, wx.TIMER_CONTINUOUS )
def _AboutWindow( self ):
@@ -252,48 +237,6 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
- def _AppendGUISession( self, name ):
-
- try:
-
- session = self._controller.Read( 'serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_GUI_SESSION, name )
-
- except Exception as e:
-
- HydrusData.ShowText( 'While trying to load session ' + name + ', this error happened:' )
- HydrusData.ShowException( e )
-
- self._NewPageQuery( CC.LOCAL_FILE_SERVICE_KEY )
-
- return
-
-
- starting_index = self._GetDefaultPageInsertionIndex()
-
- try:
-
- forced_insertion_index = starting_index
-
- for ( management_controller, initial_hashes ) in session.IteratePages():
-
- try:
-
- self._NewPage( management_controller, initial_hashes = initial_hashes, forced_insertion_index = forced_insertion_index )
-
- forced_insertion_index += 1
-
- except Exception as e:
-
- HydrusData.ShowException( e )
-
-
-
- finally:
-
- self._media_status_override = None
-
-
-
def _AutoRepoSetup( self ):
def do_it():
@@ -571,7 +514,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
if dlg_yn.ShowModal() == wx.ID_YES:
- self._SaveGUISession( 'last session' )
+ self._notebook.SaveGUISession( 'last session' )
# session save causes a db read in the menu refresh, so let's put this off just a bit
wx.CallLater( 1500, self._controller.Write, 'backup', path )
@@ -683,16 +626,6 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
- def _ChooseNewPage( self, insertion_index = None ):
-
- self._next_new_page_index = insertion_index
-
- with ClientGUIDialogs.DialogPageChooser( self ) as dlg:
-
- dlg.ShowModal()
-
-
-
def _ClearOrphans( self ):
text = 'This will iterate through every file in your database\'s file storage, removing any it does not expect to be there. It may take some time.'
@@ -732,97 +665,6 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
- def _CloseCurrentPage( self, polite = True ):
-
- selection = self._notebook.GetSelection()
-
- if selection != wx.NOT_FOUND:
-
- self._ClosePage( selection, polite = polite )
-
-
-
- def _CloseLeftPages( self, from_index ):
-
- closees = [ index for index in range( self._notebook.GetPageCount() ) if index < from_index ]
-
- self._ClosePages( closees )
-
-
- def _CloseOtherPages( self, except_index ):
-
- closees = [ index for index in range( self._notebook.GetPageCount() ) if index != except_index ]
-
- self._ClosePages( closees )
-
-
- def _ClosePage( self, index, polite = True ):
-
- self._controller.ResetIdleTimer()
- self._controller.ResetPageChangeTimer()
-
- if index == -1 or index > self._notebook.GetPageCount() - 1:
-
- return False
-
-
- name = self._notebook.GetPageText( index )
-
- page = self._notebook.GetPage( index )
-
- if polite:
-
- try:
-
- page.TestAbleToClose()
-
- except HydrusExceptions.PermissionException:
-
- return False
-
-
-
- page.PrepareToHide()
-
- with self._lock:
-
- self._closed_pages.append( ( HydrusData.GetNow(), index, name, page ) )
-
-
- self._notebook.RemovePage( index )
-
- if self._notebook.GetPageCount() == 0:
-
- self._focus_holder.SetFocus()
-
-
- self._controller.pub( 'notify_new_undo' )
-
- return True
-
-
- def _ClosePages( self, indices ):
-
- indices.reverse() # so we are closing from the end first
-
- for index in indices:
-
- successful = self._ClosePage( index )
-
- if not successful:
-
- break
-
-
-
-
- def _CloseRightPages( self, from_index ):
-
- closees = [ index for index in range( self._notebook.GetPageCount() ) if index > from_index ]
-
- self._ClosePages( closees )
-
-
def _DebugMakeSomePopups( self ):
for i in range( 1, 7 ):
@@ -911,22 +753,6 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
HydrusData.Print( 'uncollectable garbage: ' + HydrusData.ToUnicode( gc.garbage ) )
- def _DeleteAllClosedPages( self ):
-
- with self._lock:
-
- deletee_pages = [ page for ( time_closed, selection, name, page ) in self._closed_pages ]
-
- self._closed_pages = []
-
-
- self._DestroyPages( deletee_pages )
-
- self._focus_holder.SetFocus()
-
- self._controller.pub( 'notify_new_undo' )
-
-
def _DeleteGUISession( self, name ):
message = 'Delete session "' + name + '"?'
@@ -1081,7 +907,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
undo_pages = wx.Menu()
- ClientGUIMenus.AppendMenuItem( self, undo_pages, 'clear all', 'Remove all closed pages from memory.', self._DeleteAllClosedPages )
+ ClientGUIMenus.AppendMenuItem( self, undo_pages, 'clear all', 'Remove all closed pages from memory.', self.DeleteAllClosedPages )
undo_pages.AppendSeparator()
@@ -1089,7 +915,9 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
with self._lock:
- for ( i, ( time_closed, index, name, page ) ) in enumerate( self._closed_pages ):
+ for ( i, ( time_closed, page ) ) in enumerate( self._closed_pages ):
+
+ name = page.GetName()
args.append( ( i, name + ' - ' + page.GetPrettyStatus() ) )
@@ -1130,7 +958,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
for name in gui_session_names:
- ClientGUIMenus.AppendMenuItem( self, load, name, 'Close all other pages and load this session.', self._LoadGUISession, name )
+ ClientGUIMenus.AppendMenuItem( self, load, name, 'Close all other pages and load this session.', self._notebook.LoadGUISession, name )
ClientGUIMenus.AppendMenu( sessions, load, 'load' )
@@ -1139,13 +967,13 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
for name in gui_session_names:
- ClientGUIMenus.AppendMenuItem( self, append, name, 'Append this session to whatever pages are already open.', self._AppendGUISession, name )
+ ClientGUIMenus.AppendMenuItem( self, append, name, 'Append this session to whatever pages are already open.', self._notebook.AppendGUISession, name )
ClientGUIMenus.AppendMenu( sessions, append, 'append' )
- ClientGUIMenus.AppendMenuItem( self, sessions, 'save current', 'Save the existing open pages as a session.', self._SaveGUISession )
+ ClientGUIMenus.AppendMenuItem( self, sessions, 'save current', 'Save the existing open pages as a session.', self._notebook.SaveGUISession )
if len( gui_session_names ) > 0 and gui_session_names != [ 'last session' ]:
@@ -1166,7 +994,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
ClientGUIMenus.AppendSeparator( menu )
- ClientGUIMenus.AppendMenuItem( self, menu, 'pick a new page', 'Choose a new page to open.', self._ChooseNewPage )
+ ClientGUIMenus.AppendMenuItem( self, menu, 'pick a new page', 'Choose a new page to open.', self._notebook.ChooseNewPageForDeepestNotebook )
#
@@ -1182,12 +1010,12 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
petition_resolvable_repositories = [ repository for repository in repositories if True in ( repository.HasPermission( content_type, action ) for ( content_type, action ) in petition_permissions ) ]
- ClientGUIMenus.AppendMenuItem( self, search_menu, 'my files', 'Open a new search tab for your files.', self._NewPageQuery, CC.LOCAL_FILE_SERVICE_KEY )
- ClientGUIMenus.AppendMenuItem( self, search_menu, 'trash', 'Open a new search tab for your recently deleted files.', self._NewPageQuery, CC.TRASH_SERVICE_KEY )
+ ClientGUIMenus.AppendMenuItem( self, search_menu, 'my files', 'Open a new search tab for your files.', self._notebook.NewPageQuery, CC.LOCAL_FILE_SERVICE_KEY )
+ ClientGUIMenus.AppendMenuItem( self, search_menu, 'trash', 'Open a new search tab for your recently deleted files.', self._notebook.NewPageQuery, CC.TRASH_SERVICE_KEY )
for service in file_repositories:
- ClientGUIMenus.AppendMenuItem( self, search_menu, service.GetName(), 'Open a new search tab for ' + service.GetName() + '.', self._NewPageQuery, service.GetServiceKey() )
+ ClientGUIMenus.AppendMenuItem( self, search_menu, service.GetName(), 'Open a new search tab for ' + service.GetName() + '.', self._notebook.NewPageQuery, service.GetServiceKey() )
ClientGUIMenus.AppendMenu( menu, search_menu, 'new search page' )
@@ -1200,7 +1028,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
for service in petition_resolvable_repositories:
- ClientGUIMenus.AppendMenuItem( self, petition_menu, service.GetName(), 'Open a new petition page for ' + service.GetName() + '.', self._NewPagePetitions, service.GetServiceKey() )
+ ClientGUIMenus.AppendMenuItem( self, petition_menu, service.GetName(), 'Open a new petition page for ' + service.GetName() + '.', self._notebook.NewPagePetitions, service.GetServiceKey() )
ClientGUIMenus.AppendMenu( menu, petition_menu, 'new petition page' )
@@ -1210,23 +1038,23 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
download_menu = wx.Menu()
- ClientGUIMenus.AppendMenuItem( self, download_menu, 'url download', 'Open a new tab to download some raw urls.', self._NewPageImportURLs )
- ClientGUIMenus.AppendMenuItem( self, download_menu, 'thread watcher', 'Open a new tab to watch a thread.', self._NewPageImportThreadWatcher )
- ClientGUIMenus.AppendMenuItem( self, download_menu, 'webpage of images', 'Open a new tab to download files from generic galleries or threads.', self._NewPageImportPageOfImages )
+ ClientGUIMenus.AppendMenuItem( self, download_menu, 'url download', 'Open a new tab to download some raw urls.', self._notebook.NewPageImportURLs )
+ ClientGUIMenus.AppendMenuItem( self, download_menu, 'thread watcher', 'Open a new tab to watch a thread.', self._notebook.NewPageImportThreadWatcher )
+ ClientGUIMenus.AppendMenuItem( self, download_menu, 'webpage of images', 'Open a new tab to download files from generic galleries or threads.', self._notebook.NewPageImportPageOfImages )
gallery_menu = wx.Menu()
- ClientGUIMenus.AppendMenuItem( self, gallery_menu, 'booru', 'Open a new tab to download files from a booru.', self._NewPageImportBooru )
- ClientGUIMenus.AppendMenuItem( self, gallery_menu, 'deviant art', 'Open a new tab to download files from Deviant Art.', self._NewPageImportGallery, ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_DEVIANT_ART ) )
+ ClientGUIMenus.AppendMenuItem( self, gallery_menu, 'booru', 'Open a new tab to download files from a booru.', self._notebook.NewPageImportBooru )
+ ClientGUIMenus.AppendMenuItem( self, gallery_menu, 'deviant art', 'Open a new tab to download files from Deviant Art.', self._notebook.NewPageImportGallery, ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_DEVIANT_ART ) )
hf_submenu = wx.Menu()
- ClientGUIMenus.AppendMenuItem( self, hf_submenu, 'by artist', 'Open a new tab to download files from Hentai Foundry.', self._NewPageImportGallery, ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_HENTAI_FOUNDRY_ARTIST ) )
- ClientGUIMenus.AppendMenuItem( self, hf_submenu, 'by tags', 'Open a new tab to download files from Hentai Foundry.', self._NewPageImportGallery, ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_HENTAI_FOUNDRY_TAGS ) )
+ ClientGUIMenus.AppendMenuItem( self, hf_submenu, 'by artist', 'Open a new tab to download files from Hentai Foundry.', self._notebook.NewPageImportGallery, ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_HENTAI_FOUNDRY_ARTIST ) )
+ ClientGUIMenus.AppendMenuItem( self, hf_submenu, 'by tags', 'Open a new tab to download files from Hentai Foundry.', self._notebook.NewPageImportGallery, ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_HENTAI_FOUNDRY_TAGS ) )
ClientGUIMenus.AppendMenu( gallery_menu, hf_submenu, 'hentai foundry' )
- ClientGUIMenus.AppendMenuItem( self, gallery_menu, 'newgrounds', 'Open a new tab to download files from Newgrounds.', self._NewPageImportGallery, ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_NEWGROUNDS ) )
+ ClientGUIMenus.AppendMenuItem( self, gallery_menu, 'newgrounds', 'Open a new tab to download files from Newgrounds.', self._notebook.NewPageImportGallery, ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_NEWGROUNDS ) )
result = self._controller.Read( 'serialisable_simple', 'pixiv_account' )
@@ -1234,13 +1062,13 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
pixiv_submenu = wx.Menu()
- ClientGUIMenus.AppendMenuItem( self, pixiv_submenu, 'by artist id', 'Open a new tab to download files from Pixiv.', self._NewPageImportGallery, ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_PIXIV_ARTIST_ID ) )
- ClientGUIMenus.AppendMenuItem( self, pixiv_submenu, 'by tag', 'Open a new tab to download files from Pixiv.', self._NewPageImportGallery, ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_PIXIV_TAG ) )
+ ClientGUIMenus.AppendMenuItem( self, pixiv_submenu, 'by artist id', 'Open a new tab to download files from Pixiv.', self._notebook.NewPageImportGallery, ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_PIXIV_ARTIST_ID ) )
+ ClientGUIMenus.AppendMenuItem( self, pixiv_submenu, 'by tag', 'Open a new tab to download files from Pixiv.', self._notebook.NewPageImportGallery, ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_PIXIV_TAG ) )
ClientGUIMenus.AppendMenu( gallery_menu, pixiv_submenu, 'pixiv' )
- ClientGUIMenus.AppendMenuItem( self, gallery_menu, 'tumblr', 'Open a new tab to download files from tumblr.', self._NewPageImportGallery, ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_TUMBLR ) )
+ ClientGUIMenus.AppendMenuItem( self, gallery_menu, 'tumblr', 'Open a new tab to download files from tumblr.', self._notebook.NewPageImportGallery, ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_TUMBLR ) )
ClientGUIMenus.AppendMenu( download_menu, gallery_menu, 'gallery' )
ClientGUIMenus.AppendMenu( menu, download_menu, 'new download page' )
@@ -1264,7 +1092,8 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
special_menu = wx.Menu()
- ClientGUIMenus.AppendMenuItem( self, special_menu, 'duplicates processing', 'Open a new tab to discover and filter duplicate files.', self._NewPageDuplicateFilter )
+ ClientGUIMenus.AppendMenuItem( self, special_menu, 'page of pages', 'Open a new tab that can hold more tabs.', self._notebook.NewPagesNotebook )
+ ClientGUIMenus.AppendMenuItem( self, special_menu, 'duplicates processing', 'Open a new tab to discover and filter duplicate files.', self._notebook.NewPageDuplicateFilter )
ClientGUIMenus.AppendMenu( menu, special_menu, 'new special page' )
@@ -1585,6 +1414,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
ClientGUIMenus.AppendMenuCheckItem( self, debug_modes, 'db report mode', 'Have the db report query information, where supported.', HG.db_report_mode, self._SwitchBoolean, 'db_report_mode' )
ClientGUIMenus.AppendMenuCheckItem( self, debug_modes, 'db profile mode', 'Run detailed \'profiles\' on every database query and dump this information to the log (this is very useful for hydrus dev to have, if something is running slow for you!).', HG.db_profile_mode, self._SwitchBoolean, 'db_profile_mode' )
ClientGUIMenus.AppendMenuCheckItem( self, debug_modes, 'gui report mode', 'Have the gui report inside information, where supported.', HG.gui_report_mode, self._SwitchBoolean, 'gui_report_mode' )
+ ClientGUIMenus.AppendMenuCheckItem( self, debug_modes, 'network report mode', 'Have the network engine report new jobs.', HG.network_report_mode, self._SwitchBoolean, 'network_report_mode' )
ClientGUIMenus.AppendMenuCheckItem( self, debug_modes, 'pubsub profile mode', 'Run detailed \'profiles\' on every internal publisher/subscriber message and dump this information to the log. This can hammer your log with dozens of large dumps every second. Don\'t run it unless you know you need to.', HG.pubsub_profile_mode, self._SwitchBoolean, 'pubsub_profile_mode' )
ClientGUIMenus.AppendMenuCheckItem( self, debug_modes, 'force idle mode', 'Make the client consider itself idle and fire all maintenance routines right now. This may hang the gui for a while.', HG.force_idle_mode, self._SwitchBoolean, 'force_idle_mode' )
@@ -1598,7 +1428,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
ClientGUIMenus.AppendMenuItem( self, debug, 'clear db service info cache', 'Delete all cached service info like total number of mappings or files, in case it has become desynchronised. Some parts of the gui may be laggy immediately after this as these numbers are recalculated.', self._DeleteServiceInfo )
ClientGUIMenus.AppendMenuItem( self, debug, 'load whole db in disk cache', 'Contiguously read as much of the db as will fit into memory. This will massively speed up any subsequent big job.', self._controller.CallToThread, self._controller.Read, 'load_into_disk_cache' )
ClientGUIMenus.AppendMenuItem( self, debug, 'run and initialise server for testing', 'This will try to boot the server in your install folder and initialise it. This is mostly here for testing purposes.', self._AutoServerSetup )
- ClientGUIMenus.AppendMenuItem( self, debug, 'save \'last session\' gui session', 'Make an immediate save of the \'last session\' gui session. Mostly for testing crashes, where last session is not saved correctly.', self._SaveGUISession, 'last session' )
+ ClientGUIMenus.AppendMenuItem( self, debug, 'save \'last session\' gui session', 'Make an immediate save of the \'last session\' gui session. Mostly for testing crashes, where last session is not saved correctly.', self._notebook.SaveGUISession, 'last session' )
ClientGUIMenus.AppendMenu( menu, debug, 'debug' )
@@ -1624,87 +1454,6 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
with ClientGUIDialogs.DialogGenerateNewAccounts( self, service_key ) as dlg: dlg.ShowModal()
- def _GetDefaultPageInsertionIndex( self ):
-
- new_page_goes = self._new_options.GetInteger( 'default_new_page_goes' )
-
- current_index = self._notebook.GetSelection()
-
- if current_index == wx.NOT_FOUND:
-
- new_page_goes = CC.NEW_PAGE_GOES_FAR_LEFT
-
-
- if new_page_goes == CC.NEW_PAGE_GOES_FAR_LEFT:
-
- insertion_index = 0
-
- elif new_page_goes == CC.NEW_PAGE_GOES_LEFT_OF_CURRENT:
-
- insertion_index = current_index
-
- elif new_page_goes == CC.NEW_PAGE_GOES_RIGHT_OF_CURRENT:
-
- insertion_index = current_index + 1
-
- elif new_page_goes == CC.NEW_PAGE_GOES_FAR_RIGHT:
-
- insertion_index = self._notebook.GetPageCount()
-
-
- return insertion_index
-
-
- def _GetCurrentMediaPage( self ):
-
- page = self._notebook.GetCurrentPage()
-
- if page is not None:
-
- while isinstance( page, wx.Notebook ):
-
- page = page.GetCurrentPage()
-
-
- return page
-
-
-
- def _GetMediaPages( self ):
-
- results = []
-
- for page in self._GetPages():
-
- if isinstance( page, wx.Notebook ):
-
- results.extend( page.GetMediaPages() )
-
- else:
-
- results.append( page )
-
-
-
-
- def _GetPageAndIndex( self, page_key ):
-
- for ( page, index ) in ( ( self._notebook.GetPage( index ), index ) for index in range( self._notebook.GetPageCount() ) ):
-
- if page.GetPageKey() == page_key:
-
- return ( page, index )
-
-
-
- raise HydrusExceptions.DataMissing()
-
-
- def _GetPages( self ):
-
- return [ self._notebook.GetPage( i ) for i in range( self._notebook.GetPageCount() ) ]
-
-
def _ImportFiles( self, paths = None ):
if paths is None: paths = []
@@ -1888,38 +1637,16 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
if load_a_blank_page:
- self._NewPageQuery( CC.LOCAL_FILE_SERVICE_KEY )
+ self._notebook.NewPageQuery( CC.LOCAL_FILE_SERVICE_KEY )
else:
- self._LoadGUISession( default_gui_session )
+ self._notebook.LoadGUISession( default_gui_session )
wx.CallLater( 5 * 60 * 1000, self.SaveLastSession )
- def _LoadGUISession( self, name ):
-
- for page in self._GetPages():
-
- try:
-
- page.TestAbleToClose()
-
- except HydrusExceptions.PermissionException:
-
- return
-
-
-
- while self._notebook.GetPageCount() > 0:
-
- self._CloseCurrentPage( polite = False )
-
-
- self._AppendGUISession( name )
-
-
def _ManageAccountTypes( self, service_key ):
title = 'manage account types'
@@ -1965,8 +1692,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
self._controller.pub( 'wake_daemons' )
self._controller.pubimmediate( 'refresh_status' )
-
- wx.CallAfter( self.RefreshPageName )
+ self._controller.pub( 'refresh_page_name' )
def _ManageParsingScripts( self ):
@@ -2129,183 +1855,6 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
- def _MovePage( self, page_index, delta = None, new_index = None ):
-
- new_page_index = page_index
-
- if delta is not None:
-
- new_page_index = page_index + delta
-
-
- if new_index is not None:
-
- new_page_index = new_index
-
-
- if new_page_index == page_index:
-
- return
-
-
- if 0 <= new_page_index and new_page_index <= self._notebook.GetPageCount() - 1:
-
- page_is_selected = self._notebook.GetSelection() == page_index
-
- page = self._notebook.GetPage( page_index )
- name = self._notebook.GetPageText( page_index )
-
- self._notebook.RemovePage( page_index )
-
- self._notebook.InsertPage( new_page_index, page, name, page_is_selected )
-
-
-
- def _NewPage( self, management_controller, initial_hashes = None, forced_insertion_index = None ):
-
- if self._notebook.GetPageCount() + len( self._closed_pages ) >= 128:
-
- self._DeleteAllClosedPages()
-
-
- if self._notebook.GetPageCount() >= 128:
-
- HydrusData.ShowText( 'The client cannot have more than 128 pages open! For system stability reasons, please close some now!' )
-
- return
-
-
- if self._notebook.GetPageCount() == 120:
-
- HydrusData.ShowText( 'You have 120 pages open! You can only open a few more before system stability is affected! Please close some now!' )
-
-
- self._controller.ResetIdleTimer()
- self._controller.ResetPageChangeTimer()
-
- if initial_hashes is None:
-
- initial_hashes = []
-
-
- page = ClientGUIPages.Page( self._notebook, self._controller, management_controller, initial_hashes )
-
- if forced_insertion_index is None:
-
- if self._next_new_page_index is None:
-
- insertion_index = self._GetDefaultPageInsertionIndex()
-
- else:
-
- insertion_index = self._next_new_page_index
-
- self._next_new_page_index = None
-
-
- else:
-
- insertion_index = forced_insertion_index
-
-
- page_name = 'page'
-
- self._notebook.InsertPage( insertion_index, page, page_name, select = True )
-
- wx.CallAfter( page.SetSearchFocus )
- wx.CallAfter( self.RefreshPageName, page.GetPageKey() )
-
-
- def _NewPageDuplicateFilter( self ):
-
- management_controller = ClientGUIManagement.CreateManagementControllerDuplicateFilter()
-
- self._NewPage( management_controller )
-
-
- def _NewPageImportBooru( self ):
-
- with ClientGUIDialogs.DialogSelectBooru( self ) as dlg:
-
- if dlg.ShowModal() == wx.ID_OK:
-
- gallery_identifier = dlg.GetGalleryIdentifier()
-
- self._NewPageImportGallery( gallery_identifier )
-
-
-
-
- def _NewPageImportGallery( self, gallery_identifier ):
-
- management_controller = ClientGUIManagement.CreateManagementControllerImportGallery( gallery_identifier )
-
- self._NewPage( management_controller )
-
-
- def _NewPageImportPageOfImages( self ):
-
- management_controller = ClientGUIManagement.CreateManagementControllerImportPageOfImages()
-
- self._NewPage( management_controller )
-
-
- def _NewPageImportThreadWatcher( self, thread_url = None ):
-
- management_controller = ClientGUIManagement.CreateManagementControllerImportThreadWatcher( thread_url )
-
- self._NewPage( management_controller )
-
-
- def _NewPageImportURLs( self ):
-
- management_controller = ClientGUIManagement.CreateManagementControllerImportURLs()
-
- self._NewPage( management_controller )
-
-
- def _NewPagePetitions( self, service_key ):
-
- management_controller = ClientGUIManagement.CreateManagementControllerPetitions( service_key )
-
- self._NewPage( management_controller )
-
-
- def _NewPageQuery( self, file_service_key, initial_hashes = None, initial_predicates = None, page_name = None ):
-
- if initial_hashes is None:
-
- initial_hashes = []
-
-
- if initial_predicates is None:
-
- initial_predicates = []
-
-
- if page_name is None:
-
- page_name = 'files'
-
-
- search_enabled = len( initial_hashes ) == 0
-
- new_options = self._controller.GetNewOptions()
-
- tag_service_key = new_options.GetKey( 'default_tag_service_search_page' )
-
- if not self._controller.services_manager.ServiceExists( tag_service_key ):
-
- tag_service_key = CC.COMBINED_TAG_SERVICE_KEY
-
-
- file_search_context = ClientSearch.FileSearchContext( file_service_key = file_service_key, tag_service_key = tag_service_key, predicates = initial_predicates )
-
- management_controller = ClientGUIManagement.CreateManagementControllerQuery( page_name, file_service_key, file_search_context, search_enabled )
-
- self._NewPage( management_controller, initial_hashes = initial_hashes )
-
-
def _OpenDBFolder( self ):
HydrusPaths.LaunchDirectory( self._controller.GetDBDir() )
@@ -2370,11 +1919,11 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
elif action == 'new_page':
- self._ChooseNewPage()
+ self._notebook.ChooseNewPageForDeepestNotebook()
if action == 'close_page':
- self._CloseCurrentPage()
+ self._notebook.CloseCurrentPage()
elif action == 'unclose_page':
@@ -2438,7 +1987,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
def _Refresh( self ):
- page = self._GetCurrentMediaPage()
+ page = self._notebook.GetCurrentMediaPage()
if page is not None:
@@ -2448,22 +1997,20 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
def _RefreshStatusBar( self ):
- if self._media_status_override is not None:
+ if not self._notebook or not self._statusbar:
- media_status = self._media_status_override
+ return
+
+
+ page = self._notebook.GetCurrentMediaPage()
+
+ if page is None:
+
+ media_status = ''
else:
- page = self._GetCurrentMediaPage()
-
- if page is None:
-
- media_status = ''
-
- else:
-
- media_status = page.GetPrettyStatus()
-
+ media_status = page.GetPrettyStatus()
if self._controller.CurrentlyIdle():
@@ -2557,34 +2104,6 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
- def _RenamePage( self, selection ):
-
- if selection == -1 or selection > self._notebook.GetPageCount() - 1:
-
- return
-
-
- page = self._notebook.GetPage( selection )
-
- management_controller = page.GetManagementController()
-
- current_name = management_controller.GetPageName()
-
- with ClientGUIDialogs.DialogTextEntry( self, 'Enter the new name.', default = current_name, allow_blank = False ) as dlg:
-
- if dlg.ShowModal() == wx.ID_OK:
-
- new_name = dlg.GetValue()
-
- new_name = self._notebook.EscapeMnemonics( new_name )
-
- management_controller.SetPageName( new_name )
-
- self.RefreshPageName( page.GetPageKey() )
-
-
-
-
def _ReviewBandwidth( self ):
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, 'review bandwidth' )
@@ -2603,66 +2122,6 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
frame.SetPanel( panel )
- def _SaveGUISession( self, name = None ):
-
- if name is None:
-
- while True:
-
- with ClientGUIDialogs.DialogTextEntry( self, 'Enter a name for the new session.' ) as dlg:
-
- if dlg.ShowModal() == wx.ID_OK:
-
- name = dlg.GetValue()
-
- if name in ( 'just a blank page', 'last session' ):
-
- wx.MessageBox( 'Sorry, you cannot have that name! Try another.' )
-
- else:
-
- existing_session_names = self._controller.Read( 'serialisable_names', HydrusSerialisable.SERIALISABLE_TYPE_GUI_SESSION )
-
- if name in existing_session_names:
-
- message = 'Session \'' + name + '\' already exists! Do you want to overwrite it?'
-
- with ClientGUIDialogs.DialogYesNo( self, message, title = 'Overwrite existing session?', yes_label = 'yes, overwrite', no_label = 'no, choose another name' ) as yn_dlg:
-
- if yn_dlg.ShowModal() != wx.ID_YES:
-
- continue
-
-
-
-
- break
-
-
- else:
-
- return
-
-
-
-
-
- session = ClientGUIPages.GUISession( name )
-
- for page in self._GetPages():
-
- management_controller = page.GetManagementController()
-
- hashes = list( page.GetHashes() )
-
- session.AddPage( management_controller, hashes )
-
-
- self._controller.Write( 'serialisable', session )
-
- self._controller.pub( 'notify_new_sessions' )
-
-
def _SetPassword( self ):
message = '''You can set a password to be asked for whenever the client starts.
@@ -2688,7 +2147,7 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
def _SetMediaFocus( self ):
- page = self._GetCurrentMediaPage()
+ page = self._notebook.GetCurrentMediaPage()
if page is not None:
@@ -2698,7 +2157,7 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
def _SetSearchFocus( self ):
- page = self._GetCurrentMediaPage()
+ page = self._notebook.GetCurrentMediaPage()
if page is not None:
@@ -2708,7 +2167,7 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
def _SetSynchronisedWait( self ):
- page = self._GetCurrentMediaPage()
+ page = self._notebook.GetCurrentMediaPage()
if page is not None:
@@ -2801,7 +2260,7 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
def _ShowHideSplitters( self ):
- page = self._GetCurrentMediaPage()
+ page = self._notebook.GetCurrentMediaPage()
if page is not None:
@@ -2809,27 +2268,6 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
- def _ShowPage( self, showee ):
-
- for ( i, page ) in enumerate( self._GetPages() ):
-
- if isinstance( page, wx.Notebook ) and page.HasPage( page ):
-
- self._notebook.SetSelection( i )
-
- page.ShowPage( page )
-
- break
-
- elif page == showee:
-
- self._notebook.SetSelection( i )
-
- break
-
-
-
-
def _StartIPFSDownload( self ):
ipfs_services = self._controller.services_manager.GetServices( ( HC.IPFS, ) )
@@ -2906,6 +2344,10 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
HG.gui_report_mode = not HG.gui_report_mode
+ elif name == 'network_report_mode':
+
+ HG.network_report_mode = not HG.network_report_mode
+
elif name == 'pubsub_profile_mode':
HG.pubsub_profile_mode = not HG.pubsub_profile_mode
@@ -2930,14 +2372,10 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
with self._lock:
- ( time_closed, index, name, page ) = self._closed_pages.pop( closed_page_index )
+ ( time_closed, page ) = self._closed_pages.pop( closed_page_index )
- page.Show()
-
- index = min( index, self._notebook.GetPageCount() )
-
- self._notebook.InsertPage( index, page, name, True )
+ self._controller.pub( 'notify_page_unclosed', page )
self._controller.pub( 'notify_new_undo' )
@@ -3285,13 +2723,22 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
self._closed_pages = []
- for ( time_closed, index, name, page ) in old_closed_pages:
+ for ( time_closed, page ) in old_closed_pages:
- if time_closed + timeout < now: deletee_pages.append( page )
- else: self._closed_pages.append( ( time_closed, index, name, page ) )
+ if time_closed + timeout < now:
+
+ deletee_pages.append( page )
+
+ else:
+
+ self._closed_pages.append( ( time_closed, page ) )
+
- if len( old_closed_pages ) != len( self._closed_pages ): self._controller.pub( 'notify_new_undo' )
+ if len( old_closed_pages ) != len( self._closed_pages ):
+
+ self._controller.pub( 'notify_new_undo' )
+
self._DestroyPages( deletee_pages )
@@ -3302,6 +2749,22 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
return False
+ def DeleteAllClosedPages( self ):
+
+ with self._lock:
+
+ deletee_pages = [ page for ( time_closed, page ) in self._closed_pages ]
+
+ self._closed_pages = []
+
+
+ self._DestroyPages( deletee_pages )
+
+ self._focus_holder.SetFocus()
+
+ self._controller.pub( 'notify_new_undo' )
+
+
def EventCharHook( self, event ):
if ClientGUIShortcuts.IShouldCatchCharHook( self ):
@@ -3339,7 +2802,7 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
def EventFocus( self, event ):
- page = self._GetCurrentMediaPage()
+ page = self._notebook.GetCurrentMediaPage()
if page is not None:
@@ -3349,156 +2812,37 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
def EventFrameNewPage( self, event ):
- ( tab_index, flags ) = self._notebook.HitTest( ( event.GetX(), event.GetY() ) )
+ screen_position = self.ClientToScreen( event.GetPosition() )
+ new_position = self._notebook.ScreenToClient( screen_position )
- if flags == wx.NB_HITTEST_NOWHERE:
-
- self._ChooseNewPage()
-
+ self._notebook.EventNewPageFromMousePosition( new_position )
def EventFrameNotebookMenu( self, event ):
- ( tab_index, flags ) = self._notebook.HitTest( ( event.GetX(), event.GetY() ) )
+ screen_position = self.ClientToScreen( event.GetPosition() )
+ new_position = self._notebook.ScreenToClient( screen_position )
- if flags == wx.NB_HITTEST_NOWHERE:
-
- self.EventNotebookMenu( event )
-
-
-
- def EventNotebookLeftDoubleClick( self, event ):
-
- ( tab_index, flags ) = self._notebook.HitTest( ( event.GetX(), event.GetY() ) )
-
- if tab_index == wx.NOT_FOUND:
-
- self._ChooseNewPage()
-
-
-
- def EventNotebookMenu( self, event ):
-
- num_pages = self._notebook.GetPageCount()
-
- ( tab_index, flags ) = self._notebook.HitTest( ( event.GetX(), event.GetY() ) )
-
- click_over_tab = tab_index != -1
-
- end_index = num_pages - 1
-
- menu = wx.Menu()
-
- if tab_index != -1:
-
- ClientGUIMenus.AppendMenuItem( self, menu, 'close page', 'Close this page.', self._ClosePage, tab_index )
-
- if num_pages > 1:
-
- can_close_left = tab_index > 0
- can_close_right = tab_index < end_index
-
- ClientGUIMenus.AppendMenuItem( self, menu, 'close other pages', 'Close all pages but this one.', self._CloseOtherPages, tab_index )
-
- if can_close_left:
-
- ClientGUIMenus.AppendMenuItem( self, menu, 'close pages to the left', 'Close all pages to the left of this one.', self._CloseLeftPages, tab_index )
-
-
- if can_close_right:
-
- ClientGUIMenus.AppendMenuItem( self, menu, 'close pages to the right', 'Close all pages to the right of this one.', self._CloseRightPages, tab_index )
-
-
-
- ClientGUIMenus.AppendSeparator( menu )
-
- ClientGUIMenus.AppendMenuItem( self, menu, 'rename page', 'Rename this page.', self._RenamePage, tab_index )
-
- more_than_one_tab = num_pages > 1
-
- ClientGUIMenus.AppendSeparator( menu )
-
-
- ClientGUIMenus.AppendMenuItem( self, menu, 'new page', 'Choose a new page.', self._ChooseNewPage )
-
- if click_over_tab:
-
- if more_than_one_tab:
-
- can_home = tab_index > 1
- can_move_left = tab_index > 0
- can_move_right = tab_index < end_index
- can_end = tab_index < end_index - 1
-
- ClientGUIMenus.AppendMenuItem( self, menu, 'new page here', 'Choose a new page.', self._ChooseNewPage, tab_index )
-
- ClientGUIMenus.AppendSeparator( menu )
-
- if can_home:
-
- ClientGUIMenus.AppendMenuItem( self, menu, 'move to left end', 'Move this page all the way to the left.', self._MovePage, tab_index, new_index = 0 )
-
-
- if can_move_left:
-
- ClientGUIMenus.AppendMenuItem( self, menu, 'move left', 'Move this page one to the left.', self._MovePage, tab_index, delta = -1 )
-
-
- if can_move_right:
-
- ClientGUIMenus.AppendMenuItem( self, menu, 'move right', 'Move this page one to the right.', self._MovePage, tab_index, 1 )
-
-
- if can_end:
-
- ClientGUIMenus.AppendMenuItem( self, menu, 'move to right end', 'Move this page all the way to the right.', self._MovePage, tab_index, new_index = end_index )
-
-
-
-
- self._controller.PopupMenu( self, menu )
-
-
- def EventNotebookMiddleClick( self, event ):
-
- ( tab_index, flags ) = self._notebook.HitTest( ( event.GetX(), event.GetY() ) )
-
- if tab_index == wx.NOT_FOUND:
-
- self._ChooseNewPage()
-
- else:
-
- self._ClosePage( tab_index )
-
-
-
- def EventNotebookPageChanged( self, event ):
-
- old_selection = event.GetOldSelection()
- selection = event.GetSelection()
-
- if old_selection != -1: self._notebook.GetPage( old_selection ).PageHidden()
-
- if selection != -1: self._notebook.GetPage( selection ).PageShown()
-
- self._RefreshStatusBar()
-
- event.Skip( True )
+ self._notebook.EventMenuFromMousePosition( new_position )
def TIMEREventBandwidth( self, event ):
- current_usage = self._controller.network_engine.bandwidth_manager.GetTracker( ClientNetworking.GLOBAL_NETWORK_CONTEXT ).GetUsage( HC.BANDWIDTH_TYPE_DATA, 1 )
+ global_tracker = self._controller.network_engine.bandwidth_manager.GetTracker( ClientNetworking.GLOBAL_NETWORK_CONTEXT )
- if current_usage == 0:
+ boot_time = self._controller.GetBootTime()
+
+ time_since_boot = max( 1, HydrusData.GetNow() - boot_time )
+
+ usage_since_boot = global_tracker.GetUsage( HC.BANDWIDTH_TYPE_DATA, time_since_boot )
+
+ bandwidth_status = HydrusData.ConvertIntToBytes( usage_since_boot )
+
+ current_usage = global_tracker.GetUsage( HC.BANDWIDTH_TYPE_DATA, 1 )
+
+ if current_usage > 0:
- bandwidth_status = ''
-
- else:
-
- bandwidth_status = HydrusData.ConvertIntToBytes( current_usage ) + '/s'
+ bandwidth_status += ' (' + HydrusData.ConvertIntToBytes( current_usage ) + '/s)'
self._statusbar.SetStatusText( bandwidth_status, number = 1 )
@@ -3536,10 +2880,7 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
try:
- for page in self._GetPages():
-
- page.TestAbleToClose()
-
+ self._notebook.TestAbleToClose()
except HydrusExceptions.PermissionException:
@@ -3554,18 +2895,15 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
try:
- self._SaveGUISession( 'last session' )
+ self._notebook.SaveGUISession( 'last session' )
self._message_manager.CleanBeforeDestroy()
self._message_manager.Hide()
- for page in self._GetPages():
-
- page.CleanBeforeDestroy()
-
+ self._notebook.CleanBeforeDestroy()
- page = self._GetCurrentMediaPage()
+ page = self._notebook.GetCurrentMediaPage()
if page is not None:
@@ -3638,7 +2976,16 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
def GetCurrentPage( self ):
- return self._GetCurrentMediaPage()
+ return self._notebook.GetCurrentMediaPage()
+
+
+ def GetTotalPageCounts( self ):
+
+ total_active_page_count = self._notebook.GetNumPages()
+
+ total_closed_page_count = len( self._closed_pages )
+
+ return ( total_active_page_count, total_closed_page_count )
def IShouldRegularlyUpdate( self, window ):
@@ -3673,34 +3020,26 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
# change this when thread watchers can support multiple sub-pages etc...
- self._NewPageImportThreadWatcher( url )
+ self._notebook.NewPageImportThreadWatcher( url )
else:
- if True not in ( page.IsURLImportPage() for page in self._GetMediaPages() ):
-
- self._NewPageImportURLs()
-
+ page = self._notebook.GetOrMakeURLImportPage()
- for page in self._GetMediaPages():
+ if page is not None:
- if page.IsURLImportPage():
-
- self._ShowPage( page )
-
- page_key = page.GetPageKey()
-
- HG.client_controller.pub( 'set_page_url_input', page_key, url )
-
- break
-
+ self._notebook.ShowPage( page )
+
+ page_key = page.GetPageKey()
+
+ HG.client_controller.pub( 'set_page_url_input', page_key, url )
def IsCurrentPage( self, page_key ):
- result = self._GetCurrentMediaPage()
+ result = self._notebook.GetCurrentMediaPage()
if result is None:
@@ -3712,38 +3051,13 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
- def NewPageDuplicateFilter( self ):
-
- self._NewPageDuplicateFilter()
-
-
- def NewPageImportBooru( self ):
-
- self._NewPageImportBooru()
-
-
- def NewPageImportGallery( self, site_type ):
-
- gallery_identifier = ClientDownloading.GalleryIdentifier( site_type )
-
- self._NewPageImportGallery( gallery_identifier )
-
-
def NewPageImportHDD( self, paths, import_file_options, paths_to_tags, delete_after_success ):
management_controller = ClientGUIManagement.CreateManagementControllerImportHDD( paths, import_file_options, paths_to_tags, delete_after_success )
- self._NewPage( management_controller )
+ self._notebook.NewPage( management_controller )
- def NewPageImportPageOfImages( self ): self._NewPageImportPageOfImages()
-
- def NewPageImportThreadWatcher( self ): self._NewPageImportThreadWatcher()
-
- def NewPageImportURLs( self ): self._NewPageImportURLs()
-
- def NewPagePetitions( self, service_key ): self._NewPagePetitions( service_key )
-
def NewPageQuery( self, service_key, initial_hashes = None, initial_predicates = None, page_name = None ):
if initial_hashes is None:
@@ -3756,14 +3070,19 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
initial_predicates = []
- self._NewPageQuery( service_key, initial_hashes = initial_hashes, initial_predicates = initial_predicates, page_name = page_name )
+ self._notebook.NewPageQuery( service_key, initial_hashes = initial_hashes, initial_predicates = initial_predicates, page_name = page_name )
- def NewSimilarTo( self, file_service_key, hash, hamming_distance ):
+ def NotifyClosedPage( self, page ):
- initial_predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_SIMILAR_TO, ( hash, hamming_distance ) ) ]
+ close_time = HydrusData.GetNow()
- self._NewPageQuery( file_service_key, initial_predicates = initial_predicates )
+ self._closed_pages.append( ( close_time, page ) )
+
+ if self._notebook.GetNumPages() == 0:
+
+ self._focus_holder.SetFocus()
+
def NotifyNewOptions( self ):
@@ -3822,13 +3141,14 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
with self._lock:
- for ( time_closed, index, name, page ) in self._closed_pages:
+ for ( time_closed, page ) in self._closed_pages:
try:
if page.GetPageKey() == page_key:
return True
+
except wx.PyDeadObjectError:
@@ -3907,53 +3227,6 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
self._dirty_menus = set()
- def RefreshPageName( self, page_key = None ):
-
- if page_key is None:
-
- pages_and_indices = [ ( self._notebook.GetPage( index ), index ) for index in range( self._notebook.GetPageCount() ) ]
-
- else:
-
- try:
-
- pages_and_indices = [ self._GetPageAndIndex( page_key ) ]
-
- except HydrusExceptions.DataMissing:
-
- return
-
-
-
- for ( page, index ) in pages_and_indices:
-
- management_controller = page.GetManagementController()
-
- page_name = management_controller.GetPageName()
-
- max_page_name_chars = self._new_options.GetInteger( 'max_page_name_chars' )
-
- if len( page_name ) > max_page_name_chars:
-
- page_name = page_name[ : max_page_name_chars ] + u'\u2026'
-
-
- page_file_count_display = self._new_options.GetInteger( 'page_file_count_display' )
-
- if page_file_count_display == CC.PAGE_FILE_COUNT_DISPLAY_ALL or ( page_file_count_display == CC.PAGE_FILE_COUNT_DISPLAY_ONLY_IMPORTERS and management_controller.IsImporter() ):
-
- num_files = page.GetNumFiles()
-
- page_name += ' (' + HydrusData.ConvertIntToPrettyString( num_files ) + ')'
-
-
- if self._notebook.GetPageText( index ) != page_name:
-
- self._notebook.SetPageText( index, page_name )
-
-
-
-
def RefreshStatusBar( self ):
self._RefreshStatusBar()
@@ -3963,7 +3236,7 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
if HC.options[ 'default_gui_session' ] == 'last session':
- self._SaveGUISession( 'last session' )
+ self._notebook.SaveGUISession( 'last session' )
wx.CallLater( 5 * 60 * 1000, self.SaveLastSession )
diff --git a/include/ClientGUICommon.py b/include/ClientGUICommon.py
index 20717fa1..8dacada6 100755
--- a/include/ClientGUICommon.py
+++ b/include/ClientGUICommon.py
@@ -1435,7 +1435,10 @@ class MenuBitmapButton( BetterBitmapButton ):
current_value = check_manager.GetCurrentValue()
func = check_manager.Invert
- ClientGUIMenus.AppendMenuCheckItem( self, menu, title, description, current_value, func )
+ if current_value is not None:
+
+ ClientGUIMenus.AppendMenuCheckItem( self, menu, title, description, current_value, func )
+
elif item_type == 'separator':
@@ -1475,6 +1478,7 @@ class MenuButton( BetterButton ):
ClientGUIMenus.AppendMenuCheckItem( self, menu, title, description, initial_value, check_manager.Invert )
+
elif item_type == 'separator':
ClientGUIMenus.AppendSeparator( menu )
diff --git a/include/ClientGUIControls.py b/include/ClientGUIControls.py
index ab4be61a..d75bd82b 100644
--- a/include/ClientGUIControls.py
+++ b/include/ClientGUIControls.py
@@ -350,6 +350,8 @@ class NetworkJobControl( wx.Panel ):
self._network_job = None
self._download_started = False
+ self._auto_override_bandwidth_rules = False
+
self._left_text = ClientGUICommon.BetterStaticText( self )
self._right_text = ClientGUICommon.BetterStaticText( self, style = wx.ALIGN_RIGHT )
@@ -360,6 +362,25 @@ class NetworkJobControl( wx.Panel ):
self._gauge = ClientGUICommon.Gauge( self )
+ menu_items = []
+
+ invert_call = self.FlipOverrideBandwidthForCurrentJob
+ value_call = self.CurrentJobOverridesBandwidth
+
+ check_manager = ClientGUICommon.CheckboxManagerCalls( invert_call, value_call )
+
+ menu_items.append( ( 'check', 'override bandwidth rules for this job', 'Tell the current job to ignore existing bandwidth rules and go ahead anyway.', check_manager ) )
+
+ menu_items.append( ( 'separator', 0, 0, 0 ) )
+
+ invert_call = self.FlipAutoOverrideBandwidth
+ value_call = self.AutoOverrideBandwidth
+
+ check_manager = ClientGUICommon.CheckboxManagerCalls( invert_call, value_call )
+
+ menu_items.append( ( 'check', 'auto-override bandwidth rules for all jobs here after five seconds', 'Ignore existing bandwidth rules for all jobs under this control, instead waiting a flat five seconds.', check_manager ) )
+
+ self._cog_button = ClientGUICommon.MenuBitmapButton( self, CC.GlobalBMPs.cog, menu_items )
self._cancel_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.stop, self.Cancel )
#
@@ -381,6 +402,7 @@ class NetworkJobControl( wx.Panel ):
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( left_vbox, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
+ hbox.AddF( self._cog_button, CC.FLAGS_VCENTER )
hbox.AddF( self._cancel_button, CC.FLAGS_VCENTER )
self.SetSizer( hbox )
@@ -405,6 +427,11 @@ class NetworkJobControl( wx.Panel ):
else:
+ if self._auto_override_bandwidth_rules and HydrusData.TimeHasPassed( self._network_job.GetCreationTime() + 5 ):
+
+ self._network_job.OverrideBandwidth()
+
+
if self._network_job.IsDone():
can_cancel = False
@@ -471,6 +498,11 @@ class NetworkJobControl( wx.Panel ):
+ def AutoOverrideBandwidth( self ):
+
+ return self._auto_override_bandwidth_rules
+
+
def Cancel( self ):
if self._network_job is not None:
@@ -491,6 +523,31 @@ class NetworkJobControl( wx.Panel ):
+ def CurrentJobOverridesBandwidth( self ):
+
+ if self._network_job is None:
+
+ return None
+
+ else:
+
+ return not self._network_job.ObeysBandwidth()
+
+
+
+ def FlipAutoOverrideBandwidth( self ):
+
+ self._auto_override_bandwidth_rules = not self._auto_override_bandwidth_rules
+
+
+ def FlipOverrideBandwidthForCurrentJob( self ):
+
+ if self._network_job is not None:
+
+ self._network_job.OverrideBandwidth()
+
+
+
def SetNetworkJob( self, network_job ):
if self and self._network_job != network_job:
diff --git a/include/ClientGUIDialogs.py b/include/ClientGUIDialogs.py
index 1a0c9ff6..21648083 100755
--- a/include/ClientGUIDialogs.py
+++ b/include/ClientGUIDialogs.py
@@ -1811,313 +1811,6 @@ class DialogModifyAccounts( Dialog ):
-class DialogPageChooser( Dialog ):
-
- def __init__( self, parent ):
-
- Dialog.__init__( self, parent, 'new page', position = 'center' )
-
- # spawn in this order, so focus precipitates from the graphical top
-
- self._button_7 = wx.Button( self, label = '', id = 7 )
- self._button_8 = wx.Button( self, label = '', id = 8 )
- self._button_9 = wx.Button( self, label = '', id = 9 )
- self._button_4 = wx.Button( self, label = '', id = 4 )
- self._button_5 = wx.Button( self, label = '', id = 5 )
- self._button_6 = wx.Button( self, label = '', id = 6 )
- self._button_1 = wx.Button( self, label = '', id = 1 )
- self._button_2 = wx.Button( self, label = '', id = 2 )
- self._button_3 = wx.Button( self, label = '', id = 3 )
-
- gridbox = wx.GridSizer( 0, 3 )
-
- gridbox.AddF( self._button_7, CC.FLAGS_EXPAND_BOTH_WAYS )
- gridbox.AddF( self._button_8, CC.FLAGS_EXPAND_BOTH_WAYS )
- gridbox.AddF( self._button_9, CC.FLAGS_EXPAND_BOTH_WAYS )
- gridbox.AddF( self._button_4, CC.FLAGS_EXPAND_BOTH_WAYS )
- gridbox.AddF( self._button_5, CC.FLAGS_EXPAND_BOTH_WAYS )
- gridbox.AddF( self._button_6, CC.FLAGS_EXPAND_BOTH_WAYS )
- gridbox.AddF( self._button_1, CC.FLAGS_EXPAND_BOTH_WAYS )
- gridbox.AddF( self._button_2, CC.FLAGS_EXPAND_BOTH_WAYS )
- gridbox.AddF( self._button_3, CC.FLAGS_EXPAND_BOTH_WAYS )
-
- self.SetSizer( gridbox )
-
- self.SetInitialSize( ( 420, 210 ) )
-
- self._services = HG.client_controller.services_manager.GetServices()
-
- repository_petition_permissions = [ ( content_type, HC.PERMISSION_ACTION_OVERRULE ) for content_type in HC.REPOSITORY_CONTENT_TYPES ]
-
- self._petition_service_keys = [ service.GetServiceKey() for service in self._services if service.GetServiceType() in HC.REPOSITORIES and True in ( service.HasPermission( content_type, action ) for ( content_type, action ) in repository_petition_permissions ) ]
-
- self._InitButtons( 'home' )
-
- self.Bind( wx.EVT_CHAR_HOOK, self.EventCharHook )
- self.Bind( wx.EVT_BUTTON, self.EventButton )
-
- #
-
- self.Show( True )
-
-
- def _AddEntry( self, button, entry ):
-
- id = button.GetId()
-
- self._command_dict[ id ] = entry
-
- ( entry_type, obj ) = entry
-
- if entry_type == 'menu':
-
- button.SetLabelText( obj )
-
- elif entry_type == 'page_duplicate_filter':
-
- button.SetLabelText( 'duplicates processing' )
-
- elif entry_type in ( 'page_query', 'page_petitions' ):
-
- name = HG.client_controller.services_manager.GetService( obj ).GetName()
-
- button.SetLabelText( name )
-
- elif entry_type == 'page_import_booru':
-
- button.SetLabelText( 'booru' )
-
- elif entry_type == 'page_import_gallery':
-
- site_type = obj
-
- text = HC.site_type_string_lookup[ site_type ]
-
- button.SetLabelText( text )
-
- elif entry_type == 'page_import_page_of_images':
-
- button.SetLabelText( 'page of images' )
-
- elif entry_type == 'page_import_thread_watcher':
-
- button.SetLabelText( 'thread watcher' )
-
- elif entry_type == 'page_import_urls':
-
- button.SetLabelText( 'raw urls' )
-
-
- button.Show()
-
-
- def _HitButton( self, id ):
-
- if id in self._command_dict:
-
- ( entry_type, obj ) = self._command_dict[ id ]
-
- if entry_type == 'menu':
-
- self._InitButtons( obj )
-
- else:
-
- if entry_type == 'page_query':
-
- HG.client_controller.pub( 'new_page_query', obj )
-
- elif entry_type == 'page_duplicate_filter':
-
- HG.client_controller.pub( 'new_duplicate_filter' )
-
- elif entry_type == 'page_import_booru':
-
- HG.client_controller.pub( 'new_import_booru' )
-
- elif entry_type == 'page_import_gallery':
-
- site_type = obj
-
- HG.client_controller.pub( 'new_import_gallery', site_type )
-
- elif entry_type == 'page_import_page_of_images':
-
- HG.client_controller.pub( 'new_page_import_page_of_images' )
-
- elif entry_type == 'page_import_thread_watcher':
-
- HG.client_controller.pub( 'new_page_import_thread_watcher' )
-
- elif entry_type == 'page_import_urls':
-
- HG.client_controller.pub( 'new_page_import_urls' )
-
- elif entry_type == 'page_petitions':
-
- HG.client_controller.pub( 'new_page_petitions', obj )
-
-
- self.EndModal( wx.ID_OK )
-
-
-
-
- def _InitButtons( self, menu_keyword ):
-
- self._command_dict = {}
-
- entries = []
-
- if menu_keyword == 'home':
-
- entries.append( ( 'menu', 'files' ) )
- entries.append( ( 'menu', 'download' ) )
-
- if len( self._petition_service_keys ) > 0:
-
- entries.append( ( 'menu', 'petitions' ) )
-
-
- entries.append( ( 'menu', 'special' ) )
-
- elif menu_keyword == 'files':
-
- file_repos = [ ( 'page_query', service_key ) for service_key in [ service.GetServiceKey() for service in self._services if service.GetServiceType() == HC.FILE_REPOSITORY ] ]
-
- entries.append( ( 'page_query', CC.LOCAL_FILE_SERVICE_KEY ) )
- entries.append( ( 'page_query', CC.TRASH_SERVICE_KEY ) )
- entries.append( ( 'page_query', CC.COMBINED_LOCAL_FILE_SERVICE_KEY ) )
-
- for service in self._services:
-
- if service.GetServiceType() == HC.FILE_REPOSITORY:
-
- entries.append( ( 'page_query', service.GetServiceKey() ) )
-
-
-
- elif menu_keyword == 'download':
-
- entries.append( ( 'page_import_urls', None ) )
- entries.append( ( 'page_import_thread_watcher', None ) )
- entries.append( ( 'menu', 'gallery' ) )
- entries.append( ( 'page_import_page_of_images', None ) )
-
- elif menu_keyword == 'gallery':
-
- entries.append( ( 'page_import_booru', None ) )
- entries.append( ( 'page_import_gallery', HC.SITE_TYPE_DEVIANT_ART ) )
- entries.append( ( 'menu', 'hentai foundry' ) )
- entries.append( ( 'page_import_gallery', HC.SITE_TYPE_NEWGROUNDS ) )
-
- result = HG.client_controller.Read( 'serialisable_simple', 'pixiv_account' )
-
- if result is not None:
-
- entries.append( ( 'menu', 'pixiv' ) )
-
-
- entries.append( ( 'page_import_gallery', HC.SITE_TYPE_TUMBLR ) )
-
- elif menu_keyword == 'hentai foundry':
-
- entries.append( ( 'page_import_gallery', HC.SITE_TYPE_HENTAI_FOUNDRY_ARTIST ) )
- entries.append( ( 'page_import_gallery', HC.SITE_TYPE_HENTAI_FOUNDRY_TAGS ) )
-
- elif menu_keyword == 'pixiv':
-
- entries.append( ( 'page_import_gallery', HC.SITE_TYPE_PIXIV_ARTIST_ID ) )
- entries.append( ( 'page_import_gallery', HC.SITE_TYPE_PIXIV_TAG ) )
-
- elif menu_keyword == 'petitions':
-
- entries = [ ( 'page_petitions', service_key ) for service_key in self._petition_service_keys ]
-
- elif menu_keyword == 'special':
-
- entries.append( ( 'page_duplicate_filter', None ) )
-
-
- if len( entries ) <= 4:
-
- self._button_1.Hide()
- self._button_3.Hide()
- self._button_5.Hide()
- self._button_7.Hide()
- self._button_9.Hide()
-
- potential_buttons = [ self._button_8, self._button_4, self._button_6, self._button_2 ]
-
- elif len( entries ) <= 9:
-
- potential_buttons = [ self._button_7, self._button_8, self._button_9, self._button_4, self._button_5, self._button_6, self._button_1, self._button_2, self._button_3 ]
-
- else:
-
- # sort out a multi-page solution? maybe only if this becomes a big thing; the person can always select from the menus, yeah?
-
- potential_buttons = [ self._button_7, self._button_8, self._button_9, self._button_4, self._button_5, self._button_6, self._button_1, self._button_2, self._button_3 ]
- entries = entries[:9]
-
-
- for entry in entries: self._AddEntry( potential_buttons.pop( 0 ), entry )
-
- unused_buttons = potential_buttons
-
- for button in unused_buttons: button.Hide()
-
-
- def EventButton( self, event ):
-
- id = event.GetId()
-
- if id == wx.ID_CANCEL:
-
- self.EndModal( wx.ID_CANCEL )
-
- else:
-
- self._HitButton( id )
-
-
-
- def EventCharHook( self, event ):
-
- id = None
-
- ( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
-
- if key == wx.WXK_UP: id = 8
- elif key == wx.WXK_LEFT: id = 4
- elif key == wx.WXK_RIGHT: id = 6
- elif key == wx.WXK_DOWN: id = 2
- elif key == wx.WXK_NUMPAD1: id = 1
- elif key == wx.WXK_NUMPAD2: id = 2
- elif key == wx.WXK_NUMPAD3: id = 3
- elif key == wx.WXK_NUMPAD4: id = 4
- elif key == wx.WXK_NUMPAD5: id = 5
- elif key == wx.WXK_NUMPAD6: id = 6
- elif key == wx.WXK_NUMPAD7: id = 7
- elif key == wx.WXK_NUMPAD8: id = 8
- elif key == wx.WXK_NUMPAD9: id = 9
- elif key == wx.WXK_ESCAPE:
-
- self.EndModal( wx.ID_CANCEL )
-
- return
-
- else:
-
- event.Skip()
-
-
- if id is not None:
-
- self._HitButton( id )
-
-
-
class DialogPathsToTags( Dialog ):
def __init__( self, parent, paths ):
@@ -3352,9 +3045,11 @@ class DialogSelectYoutubeURL( Dialog ):
#
- self._info.sort()
+ keys = list( self._info.keys() )
- for ( extension, resolution ) in self._info:
+ keys.sort()
+
+ for ( extension, resolution ) in keys:
self._urls.Append( ( extension, resolution ), ( extension, resolution ) )
diff --git a/include/ClientGUIDialogsManage.py b/include/ClientGUIDialogsManage.py
index 0fa48352..1025221c 100644
--- a/include/ClientGUIDialogsManage.py
+++ b/include/ClientGUIDialogsManage.py
@@ -2889,11 +2889,18 @@ class DialogManagePixivAccount( ClientGUIDialogs.Dialog ):
manager = HG.client_controller.GetManager( 'web_sessions' )
- cookies = manager.GetPixivCookies( pixiv_id, password )
+ ( result, message ) = manager.TestPixiv( pixiv_id, password )
- self._status.SetLabelText( 'OK!' )
-
- wx.CallLater( 5000, self._status.SetLabel, '' )
+ if result:
+
+ self._status.SetLabelText( 'OK!' )
+
+ wx.CallLater( 5000, self._status.SetLabel, '' )
+
+ else:
+
+ self._status.SetLabelText( message )
+
except HydrusExceptions.ForbiddenException as e:
diff --git a/include/ClientGUIMedia.py b/include/ClientGUIMedia.py
index d80b887c..96496f6d 100755
--- a/include/ClientGUIMedia.py
+++ b/include/ClientGUIMedia.py
@@ -13,6 +13,7 @@ import ClientGUIScrolledPanelsManagement
import ClientGUIShortcuts
import ClientGUITopLevelWindows
import ClientMedia
+import ClientSearch
import ClientTags
import collections
import HydrusExceptions
@@ -694,7 +695,9 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
hash = self._focussed_media.GetDisplayMedia().GetHash()
- HG.client_controller.pub( 'new_similar_to', CC.LOCAL_FILE_SERVICE_KEY, hash, max_hamming )
+ initial_predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_SIMILAR_TO, ( hash, max_hamming ) ) ]
+
+ HG.client_controller.pub( 'new_page_query', CC.LOCAL_FILE_SERVICE_KEY, initial_predicates = initial_predicates )
diff --git a/include/ClientGUIPages.py b/include/ClientGUIPages.py
index 9a6f0ae9..94da219b 100755
--- a/include/ClientGUIPages.py
+++ b/include/ClientGUIPages.py
@@ -1,12 +1,16 @@
import HydrusConstants as HC
import ClientConstants as CC
+import ClientData
import ClientGUICommon
import ClientGUIDialogs
import ClientGUIManagement
import ClientGUIMedia
+import ClientGUIMenus
import ClientGUICanvas
import ClientDownloading
+import ClientSearch
import HydrusData
+import HydrusExceptions
import HydrusSerialisable
import HydrusThreading
import inspect
@@ -17,6 +21,362 @@ import traceback
import wx
import HydrusGlobals as HG
+class DialogPageChooser( ClientGUIDialogs.Dialog ):
+
+ def __init__( self, parent, controller ):
+
+ ClientGUIDialogs.Dialog.__init__( self, parent, 'new page', position = 'center' )
+
+ self._controller = controller
+
+ self._result = None
+
+ # spawn in this order, so focus precipitates from the graphical top
+
+ self._button_7 = wx.Button( self, label = '', id = 7 )
+ self._button_8 = wx.Button( self, label = '', id = 8 )
+ self._button_9 = wx.Button( self, label = '', id = 9 )
+ self._button_4 = wx.Button( self, label = '', id = 4 )
+ self._button_5 = wx.Button( self, label = '', id = 5 )
+ self._button_6 = wx.Button( self, label = '', id = 6 )
+ self._button_1 = wx.Button( self, label = '', id = 1 )
+ self._button_2 = wx.Button( self, label = '', id = 2 )
+ self._button_3 = wx.Button( self, label = '', id = 3 )
+
+ gridbox = wx.GridSizer( 0, 3 )
+
+ gridbox.AddF( self._button_7, CC.FLAGS_EXPAND_BOTH_WAYS )
+ gridbox.AddF( self._button_8, CC.FLAGS_EXPAND_BOTH_WAYS )
+ gridbox.AddF( self._button_9, CC.FLAGS_EXPAND_BOTH_WAYS )
+ gridbox.AddF( self._button_4, CC.FLAGS_EXPAND_BOTH_WAYS )
+ gridbox.AddF( self._button_5, CC.FLAGS_EXPAND_BOTH_WAYS )
+ gridbox.AddF( self._button_6, CC.FLAGS_EXPAND_BOTH_WAYS )
+ gridbox.AddF( self._button_1, CC.FLAGS_EXPAND_BOTH_WAYS )
+ gridbox.AddF( self._button_2, CC.FLAGS_EXPAND_BOTH_WAYS )
+ gridbox.AddF( self._button_3, CC.FLAGS_EXPAND_BOTH_WAYS )
+
+ self.SetSizer( gridbox )
+
+ self.SetInitialSize( ( 420, 210 ) )
+
+ self._services = HG.client_controller.services_manager.GetServices()
+
+ repository_petition_permissions = [ ( content_type, HC.PERMISSION_ACTION_OVERRULE ) for content_type in HC.REPOSITORY_CONTENT_TYPES ]
+
+ self._petition_service_keys = [ service.GetServiceKey() for service in self._services if service.GetServiceType() in HC.REPOSITORIES and True in ( service.HasPermission( content_type, action ) for ( content_type, action ) in repository_petition_permissions ) ]
+
+ self._InitButtons( 'home' )
+
+ self.Bind( wx.EVT_CHAR_HOOK, self.EventCharHook )
+ self.Bind( wx.EVT_BUTTON, self.EventButton )
+
+ #
+
+ self.Show( True )
+
+
+ def _AddEntry( self, button, entry ):
+
+ id = button.GetId()
+
+ self._command_dict[ id ] = entry
+
+ ( entry_type, obj ) = entry
+
+ if entry_type == 'menu':
+
+ button.SetLabelText( obj )
+
+ elif entry_type == 'page_duplicate_filter':
+
+ button.SetLabelText( 'duplicates processing' )
+
+ elif entry_type == 'pages_notebook':
+
+ button.SetLabelText( 'page of pages' )
+
+ elif entry_type in ( 'page_query', 'page_petitions' ):
+
+ name = HG.client_controller.services_manager.GetService( obj ).GetName()
+
+ button.SetLabelText( name )
+
+ elif entry_type == 'page_import_booru':
+
+ button.SetLabelText( 'booru' )
+
+ elif entry_type == 'page_import_gallery':
+
+ site_type = obj
+
+ text = HC.site_type_string_lookup[ site_type ]
+
+ button.SetLabelText( text )
+
+ elif entry_type == 'page_import_page_of_images':
+
+ button.SetLabelText( 'page of images' )
+
+ elif entry_type == 'page_import_thread_watcher':
+
+ button.SetLabelText( 'thread watcher' )
+
+ elif entry_type == 'page_import_urls':
+
+ button.SetLabelText( 'raw urls' )
+
+
+ button.Show()
+
+
+ def _HitButton( self, id ):
+
+ if id in self._command_dict:
+
+ ( entry_type, obj ) = self._command_dict[ id ]
+
+ if entry_type == 'menu':
+
+ self._InitButtons( obj )
+
+ else:
+
+ if entry_type == 'page_query':
+
+ file_service_key = obj
+
+ page_name = 'files'
+
+ search_enabled = True
+
+ new_options = self._controller.GetNewOptions()
+
+ tag_service_key = new_options.GetKey( 'default_tag_service_search_page' )
+
+ if not self._controller.services_manager.ServiceExists( tag_service_key ):
+
+ tag_service_key = CC.COMBINED_TAG_SERVICE_KEY
+
+
+ file_search_context = ClientSearch.FileSearchContext( file_service_key = file_service_key, tag_service_key = tag_service_key )
+
+ self._result = ( 'page', ClientGUIManagement.CreateManagementControllerQuery( page_name, file_service_key, file_search_context, search_enabled ) )
+
+ elif entry_type == 'page_duplicate_filter':
+
+ self._result = ( 'page', ClientGUIManagement.CreateManagementControllerDuplicateFilter() )
+
+ elif entry_type == 'pages_notebook':
+
+ self._result = ( 'pages', None )
+
+ elif entry_type == 'page_import_booru':
+
+ with ClientGUIDialogs.DialogSelectBooru( self ) as dlg:
+
+ if dlg.ShowModal() == wx.ID_OK:
+
+ gallery_identifier = dlg.GetGalleryIdentifier()
+
+ self._result = ( 'page', ClientGUIManagement.CreateManagementControllerImportGallery( gallery_identifier ) )
+
+ else:
+
+ self.EndModal( wx.ID_CANCEL )
+
+
+
+ elif entry_type == 'page_import_gallery':
+
+ site_type = obj
+
+ gallery_identifier = ClientDownloading.GalleryIdentifier( site_type )
+
+ self._result = ( 'page', ClientGUIManagement.CreateManagementControllerImportGallery( gallery_identifier ) )
+
+ elif entry_type == 'page_import_page_of_images':
+
+ self._result = ( 'page', ClientGUIManagement.CreateManagementControllerImportPageOfImages() )
+
+ elif entry_type == 'page_import_thread_watcher':
+
+ self._result = ( 'page', ClientGUIManagement.CreateManagementControllerImportThreadWatcher() )
+
+ elif entry_type == 'page_import_urls':
+
+ self._result = ( 'page', ClientGUIManagement.CreateManagementControllerImportURLs() )
+
+ elif entry_type == 'page_petitions':
+
+ petition_service_key = obj
+
+ self._result = ( 'page', ClientGUIManagement.CreateManagementControllerPetitions( petition_service_key ) )
+
+
+ self.EndModal( wx.ID_OK )
+
+
+
+
+ def _InitButtons( self, menu_keyword ):
+
+ self._command_dict = {}
+
+ entries = []
+
+ if menu_keyword == 'home':
+
+ entries.append( ( 'menu', 'files' ) )
+ entries.append( ( 'menu', 'download' ) )
+
+ if len( self._petition_service_keys ) > 0:
+
+ entries.append( ( 'menu', 'petitions' ) )
+
+
+ entries.append( ( 'menu', 'special' ) )
+
+ elif menu_keyword == 'files':
+
+ entries.append( ( 'page_query', CC.LOCAL_FILE_SERVICE_KEY ) )
+ entries.append( ( 'page_query', CC.TRASH_SERVICE_KEY ) )
+ entries.append( ( 'page_query', CC.COMBINED_LOCAL_FILE_SERVICE_KEY ) )
+
+ for service in self._services:
+
+ if service.GetServiceType() == HC.FILE_REPOSITORY:
+
+ entries.append( ( 'page_query', service.GetServiceKey() ) )
+
+
+
+ elif menu_keyword == 'download':
+
+ entries.append( ( 'page_import_urls', None ) )
+ entries.append( ( 'page_import_thread_watcher', None ) )
+ entries.append( ( 'menu', 'gallery' ) )
+ entries.append( ( 'page_import_page_of_images', None ) )
+
+ elif menu_keyword == 'gallery':
+
+ entries.append( ( 'page_import_booru', None ) )
+ entries.append( ( 'page_import_gallery', HC.SITE_TYPE_DEVIANT_ART ) )
+ entries.append( ( 'menu', 'hentai foundry' ) )
+ entries.append( ( 'page_import_gallery', HC.SITE_TYPE_NEWGROUNDS ) )
+
+ result = HG.client_controller.Read( 'serialisable_simple', 'pixiv_account' )
+
+ if result is not None:
+
+ entries.append( ( 'menu', 'pixiv' ) )
+
+
+ entries.append( ( 'page_import_gallery', HC.SITE_TYPE_TUMBLR ) )
+
+ elif menu_keyword == 'hentai foundry':
+
+ entries.append( ( 'page_import_gallery', HC.SITE_TYPE_HENTAI_FOUNDRY_ARTIST ) )
+ entries.append( ( 'page_import_gallery', HC.SITE_TYPE_HENTAI_FOUNDRY_TAGS ) )
+
+ elif menu_keyword == 'pixiv':
+
+ entries.append( ( 'page_import_gallery', HC.SITE_TYPE_PIXIV_ARTIST_ID ) )
+ entries.append( ( 'page_import_gallery', HC.SITE_TYPE_PIXIV_TAG ) )
+
+ elif menu_keyword == 'petitions':
+
+ entries = [ ( 'page_petitions', service_key ) for service_key in self._petition_service_keys ]
+
+ elif menu_keyword == 'special':
+
+ entries.append( ( 'pages_notebook', None ) )
+ entries.append( ( 'page_duplicate_filter', None ) )
+
+
+ if len( entries ) <= 4:
+
+ self._button_1.Hide()
+ self._button_3.Hide()
+ self._button_5.Hide()
+ self._button_7.Hide()
+ self._button_9.Hide()
+
+ potential_buttons = [ self._button_8, self._button_4, self._button_6, self._button_2 ]
+
+ elif len( entries ) <= 9:
+
+ potential_buttons = [ self._button_7, self._button_8, self._button_9, self._button_4, self._button_5, self._button_6, self._button_1, self._button_2, self._button_3 ]
+
+ else:
+
+ # sort out a multi-page solution? maybe only if this becomes a big thing; the person can always select from the menus, yeah?
+
+ potential_buttons = [ self._button_7, self._button_8, self._button_9, self._button_4, self._button_5, self._button_6, self._button_1, self._button_2, self._button_3 ]
+ entries = entries[:9]
+
+
+ for entry in entries: self._AddEntry( potential_buttons.pop( 0 ), entry )
+
+ unused_buttons = potential_buttons
+
+ for button in unused_buttons: button.Hide()
+
+
+ def EventButton( self, event ):
+
+ id = event.GetId()
+
+ if id == wx.ID_CANCEL:
+
+ self.EndModal( wx.ID_CANCEL )
+
+ else:
+
+ self._HitButton( id )
+
+
+
+ def EventCharHook( self, event ):
+
+ id = None
+
+ ( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
+
+ if key == wx.WXK_UP: id = 8
+ elif key == wx.WXK_LEFT: id = 4
+ elif key == wx.WXK_RIGHT: id = 6
+ elif key == wx.WXK_DOWN: id = 2
+ elif key == wx.WXK_NUMPAD1: id = 1
+ elif key == wx.WXK_NUMPAD2: id = 2
+ elif key == wx.WXK_NUMPAD3: id = 3
+ elif key == wx.WXK_NUMPAD4: id = 4
+ elif key == wx.WXK_NUMPAD5: id = 5
+ elif key == wx.WXK_NUMPAD6: id = 6
+ elif key == wx.WXK_NUMPAD7: id = 7
+ elif key == wx.WXK_NUMPAD8: id = 8
+ elif key == wx.WXK_NUMPAD9: id = 9
+ elif key == wx.WXK_ESCAPE:
+
+ self.EndModal( wx.ID_CANCEL )
+
+ return
+
+ else:
+
+ event.Skip()
+
+
+ if id is not None:
+
+ self._HitButton( id )
+
+
+
+ def GetValue( self ):
+
+ return self._result
+
+
class Page( wx.SplitterWindow ):
def __init__( self, parent, controller, management_controller, initial_hashes ):
@@ -33,6 +393,8 @@ class Page( wx.SplitterWindow ):
self._management_controller.SetKey( 'page', self._page_key )
+ self._initialised = False
+
self._pretty_status = ''
self.SetMinimumPaneSize( 120 )
@@ -67,19 +429,6 @@ class Page( wx.SplitterWindow ):
self._controller.sub( self, 'SetPrettyStatus', 'new_page_status' )
self._controller.sub( self, 'SwapMediaPanel', 'swap_media_panel' )
- if initial_hashes is not None and len( initial_hashes ) > 0:
-
- self._initialised = False
-
- self._controller.CallToThread( self.THREADLoadInitialMediaResults )
-
- else:
-
- self._initialised = True
-
- self._management_panel.Start()
-
-
def _SetPrettyStatus( self, status ):
@@ -155,6 +504,11 @@ class Page( wx.SplitterWindow ):
return self._media_panel.GetSortedMedia()
+ def GetName( self ):
+
+ return self._management_controller.GetPageName()
+
+
def GetNumFiles( self ):
if self._initialised:
@@ -208,6 +562,11 @@ class Page( wx.SplitterWindow ):
return ( x, y )
+ def IsImporter( self ):
+
+ return self._management_controller.IsImporter()
+
+
def IsURLImportPage( self ):
return self._management_controller.GetType() == ClientGUIManagement.MANAGEMENT_TYPE_IMPORT_URLS
@@ -271,6 +630,11 @@ class Page( wx.SplitterWindow ):
self._management_panel.Start()
+ def SetName( self, name ):
+
+ return self._management_controller.SetPageName( name )
+
+
def SetPrettyStatus( self, page_key, status ):
if page_key == self._page_key:
@@ -292,6 +656,20 @@ class Page( wx.SplitterWindow ):
self._controller.pub( 'synchronised_wait_switch', self._page_key )
+ def Start( self ):
+
+ if self._initial_hashes is not None and len( self._initial_hashes ) > 0:
+
+ self._controller.CallToThread( self.THREADLoadInitialMediaResults )
+
+ else:
+
+ self._initialised = True
+
+ self._management_panel.Start()
+
+
+
def SwapMediaPanel( self, page_key, new_panel ):
if page_key == self._page_key:
@@ -331,24 +709,203 @@ class Page( wx.SplitterWindow ):
class PagesNotebook( wx.Notebook ):
- def __init__( self, parent, controller ):
-
- # bring the gui's current _notebook into here and merge all the rename_page, new_page, and other stuff into this
+ def __init__( self, parent, controller, name ):
wx.Notebook.__init__( self, parent )
self._controller = controller
+ self._name = name
+
+ self._next_new_page_index = None
+
+ self._closed_pages = []
+
+ self._page_key = HydrusData.GenerateKey()
+
+ self._controller.sub( self, 'RefreshPageName', 'refresh_page_name' )
+ self._controller.sub( self, 'NotifyPageUnclosed', 'notify_page_unclosed' )
+
+ self.Bind( wx.EVT_LEFT_DCLICK, self.EventLeftDoubleClick )
+ self.Bind( wx.EVT_MIDDLE_DOWN, self.EventMiddleClick )
+ self.Bind( wx.EVT_RIGHT_DOWN, self.EventMenu )
+ self.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGED, self.EventPageChanged )
+
- def _GetMediaPages( self ):
+ def _ChooseNewPage( self, insertion_index = None ):
+
+ self._next_new_page_index = insertion_index
+
+ with DialogPageChooser( self, self._controller ) as dlg:
+
+ if dlg.ShowModal() == wx.ID_OK:
+
+ ( page_type, page_data ) = dlg.GetValue()
+
+ if page_type == 'pages':
+
+ self.NewPagesNotebook()
+
+ elif page_type == 'page':
+
+ management_controller = page_data
+
+ self.NewPage( management_controller )
+
+
+
+
+
+ def _CloseAllPages( self, polite = True ):
+
+ closees = [ index for index in range( self.GetPageCount() ) ]
+
+ self._ClosePages( closees, polite )
+
+
+ def _CloseLeftPages( self, from_index ):
+
+ message = 'Close all pages to the left?'
+
+ with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
+
+ if dlg.ShowModal() == wx.ID_YES:
+
+ closees = [ index for index in range( self.GetPageCount() ) if index < from_index ]
+
+ self._ClosePages( closees )
+
+
+
+
+ def _CloseOtherPages( self, except_index ):
+
+ message = 'Close all other pages?'
+
+ with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
+
+ if dlg.ShowModal() == wx.ID_YES:
+
+ closees = [ index for index in range( self.GetPageCount() ) if index != except_index ]
+
+ self._ClosePages( closees )
+
+
+
+
+ def _ClosePage( self, index, polite = True ):
+
+ self._controller.ResetIdleTimer()
+ self._controller.ResetPageChangeTimer()
+
+ if index == -1 or index > self.GetPageCount() - 1:
+
+ return False
+
+
+ page = self.GetPage( index )
+
+ if polite:
+
+ try:
+
+ page.TestAbleToClose()
+
+ except HydrusExceptions.PermissionException:
+
+ return False
+
+
+
+ page.PrepareToHide()
+
+ self._closed_pages.append( ( index, page.GetPageKey() ) )
+
+ self.RemovePage( index )
+
+ self._controller.pub( 'notify_closed_page', page )
+ self._controller.pub( 'notify_new_undo' )
+
+ return True
+
+
+ def _ClosePages( self, indices, polite = True ):
+
+ indices = list( indices )
+
+ indices.sort( reverse = True ) # so we are closing from the end first
+
+ for index in indices:
+
+ successful = self._ClosePage( index, polite )
+
+ if not successful:
+
+ break
+
+
+
+
+ def _CloseRightPages( self, from_index ):
+
+ message = 'Close all pages to the right?'
+
+ with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
+
+ if dlg.ShowModal() == wx.ID_YES:
+
+ closees = [ index for index in range( self.GetPageCount() ) if index > from_index ]
+
+ self._ClosePages( closees )
+
+
+
+
+ def _GetDefaultPageInsertionIndex( self ):
+
+ new_options = self._controller.GetNewOptions()
+
+ new_page_goes = new_options.GetInteger( 'default_new_page_goes' )
+
+ current_index = self.GetSelection()
+
+ if current_index == wx.NOT_FOUND:
+
+ new_page_goes = CC.NEW_PAGE_GOES_FAR_LEFT
+
+
+ if new_page_goes == CC.NEW_PAGE_GOES_FAR_LEFT:
+
+ insertion_index = 0
+
+ elif new_page_goes == CC.NEW_PAGE_GOES_LEFT_OF_CURRENT:
+
+ insertion_index = current_index
+
+ elif new_page_goes == CC.NEW_PAGE_GOES_RIGHT_OF_CURRENT:
+
+ insertion_index = current_index + 1
+
+ elif new_page_goes == CC.NEW_PAGE_GOES_FAR_RIGHT:
+
+ insertion_index = self.GetPageCount()
+
+
+ return insertion_index
+
+
+ def _GetMediaPages( self, only_my_level ):
results = []
for page in self._GetPages():
- if isinstance( page, wx.Notebook ):
+ if isinstance( page, PagesNotebook ):
- results.extend( page.GetMediaPages() )
+ if not only_my_level:
+
+ results.extend( page.GetMediaPages() )
+
else:
@@ -359,11 +916,314 @@ class PagesNotebook( wx.Notebook ):
return results
+ def _GetNotebookFromMousePosition( self, position ):
+
+ current_page = self.GetCurrentPage()
+
+ if current_page is None or not isinstance( current_page, PagesNotebook ):
+
+ return ( self, position )
+
+ else:
+
+ ( tab_index, flags ) = self.HitTest( position )
+
+ if tab_index != wx.NOT_FOUND:
+
+ return ( self, position )
+
+
+ if flags & wx.NB_HITTEST_NOWHERE and flags & wx.NB_HITTEST_ONPAGE: # not on a label but inside my client area
+
+ screen_position = self.ClientToScreen( position )
+ new_position = current_page.ScreenToClient( screen_position )
+
+ return current_page._GetNotebookFromMousePosition( new_position )
+
+
+
+ return ( self, position )
+
+
+ def _GetIndex( self, page_key ):
+
+ for ( page, index ) in ( ( self.GetPage( index ), index ) for index in range( self.GetPageCount() ) ):
+
+ if page.GetPageKey() == page_key:
+
+ return index
+
+
+
+ raise HydrusExceptions.DataMissing()
+
+
def _GetPages( self ):
return [ self.GetPage( i ) for i in range( self.GetPageCount() ) ]
+ def _MovePage( self, page_index, delta = None, new_index = None ):
+
+ new_page_index = page_index
+
+ if delta is not None:
+
+ new_page_index = page_index + delta
+
+
+ if new_index is not None:
+
+ new_page_index = new_index
+
+
+ if new_page_index == page_index:
+
+ return
+
+
+ if 0 <= new_page_index and new_page_index <= self.GetPageCount() - 1:
+
+ page_is_selected = self.GetSelection() == page_index
+
+ page = self.GetPage( page_index )
+ name = self.GetPageText( page_index )
+
+ self.RemovePage( page_index )
+
+ self.InsertPage( new_page_index, page, name, page_is_selected )
+
+
+
+ def _RefreshPageName( self, index ):
+
+ if index == -1 or index > self.GetPageCount() - 1:
+
+ return
+
+
+ new_options = self._controller.GetNewOptions()
+
+ max_page_name_chars = new_options.GetInteger( 'max_page_name_chars' )
+
+ page_file_count_display = new_options.GetInteger( 'page_file_count_display' )
+
+ page = self.GetPage( index )
+
+ page_name = page.GetName()
+
+ if len( page_name ) > max_page_name_chars:
+
+ page_name = page_name[ : max_page_name_chars ] + u'\u2026'
+
+
+ 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 = page.GetNumFiles()
+
+ page_name += ' (' + HydrusData.ConvertIntToPrettyString( num_files ) + ')'
+
+
+ if self.GetPageText( index ) != page_name:
+
+ self.SetPageText( index, page_name )
+
+
+
+ def _RenamePage( self, index ):
+
+ if index == -1 or index > self.GetPageCount() - 1:
+
+ return
+
+
+ page = self.GetPage( index )
+
+ current_name = page.GetName()
+
+ with ClientGUIDialogs.DialogTextEntry( self, 'Enter the new name.', default = current_name, allow_blank = False ) as dlg:
+
+ if dlg.ShowModal() == wx.ID_OK:
+
+ new_name = dlg.GetValue()
+
+ new_name = self.EscapeMnemonics( new_name )
+
+ page.SetName( new_name )
+
+ self._controller.pub( 'refresh_page_name', page.GetPageKey() )
+
+
+
+
+ def _ShowMenu( self, position ):
+
+ ( tab_index, flags ) = self.HitTest( position )
+
+ num_pages = self.GetPageCount()
+
+ click_over_tab = tab_index != -1
+
+ end_index = num_pages - 1
+
+ menu = wx.Menu()
+
+ if tab_index != -1:
+
+ ClientGUIMenus.AppendMenuItem( self, menu, 'close page', 'Close this page.', self._ClosePage, tab_index )
+
+ if num_pages > 1:
+
+ can_close_left = tab_index > 0
+ can_close_right = tab_index < end_index
+
+ ClientGUIMenus.AppendMenuItem( self, menu, 'close other pages', 'Close all pages but this one.', self._CloseOtherPages, tab_index )
+
+ if can_close_left:
+
+ ClientGUIMenus.AppendMenuItem( self, menu, 'close pages to the left', 'Close all pages to the left of this one.', self._CloseLeftPages, tab_index )
+
+
+ if can_close_right:
+
+ ClientGUIMenus.AppendMenuItem( self, menu, 'close pages to the right', 'Close all pages to the right of this one.', self._CloseRightPages, tab_index )
+
+
+
+ ClientGUIMenus.AppendSeparator( menu )
+
+ ClientGUIMenus.AppendMenuItem( self, menu, 'rename page', 'Rename this page.', self._RenamePage, tab_index )
+
+ more_than_one_tab = num_pages > 1
+
+ ClientGUIMenus.AppendSeparator( menu )
+
+
+ ClientGUIMenus.AppendMenuItem( self, menu, 'new page', 'Choose a new page.', self._ChooseNewPage )
+
+ if click_over_tab:
+
+ if more_than_one_tab:
+
+ can_home = tab_index > 1
+ can_move_left = tab_index > 0
+ can_move_right = tab_index < end_index
+ can_end = tab_index < end_index - 1
+
+ ClientGUIMenus.AppendMenuItem( self, menu, 'new page here', 'Choose a new page.', self._ChooseNewPage, tab_index )
+
+ ClientGUIMenus.AppendSeparator( menu )
+
+ if can_home:
+
+ ClientGUIMenus.AppendMenuItem( self, menu, 'move to left end', 'Move this page all the way to the left.', self._MovePage, tab_index, new_index = 0 )
+
+
+ if can_move_left:
+
+ ClientGUIMenus.AppendMenuItem( self, menu, 'move left', 'Move this page one to the left.', self._MovePage, tab_index, delta = -1 )
+
+
+ if can_move_right:
+
+ ClientGUIMenus.AppendMenuItem( self, menu, 'move right', 'Move this page one to the right.', self._MovePage, tab_index, 1 )
+
+
+ if can_end:
+
+ ClientGUIMenus.AppendMenuItem( self, menu, 'move to right end', 'Move this page all the way to the right.', self._MovePage, tab_index, new_index = end_index )
+
+
+
+
+ self._controller.PopupMenu( self, menu )
+
+
+ def AppendGUISession( self, name ):
+
+ try:
+
+ session = self._controller.Read( 'serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_GUI_SESSION, name )
+
+ except Exception as e:
+
+ HydrusData.ShowText( 'While trying to load session ' + name + ', this error happened:' )
+ HydrusData.ShowException( e )
+
+ self.NewPageQuery( CC.LOCAL_FILE_SERVICE_KEY )
+
+ return
+
+
+ page_tuples = session.GetPages()
+
+ self.AppendSessionPageTuples( page_tuples )
+
+
+ def AppendSessionPageTuples( self, page_tuples ):
+
+ starting_index = self._GetDefaultPageInsertionIndex()
+
+ forced_insertion_index = starting_index
+
+ for page_tuple in page_tuples:
+
+ ( page_type, page_data ) = page_tuple
+
+ if page_type == 'pages':
+
+ ( name, subpage_tuples ) = page_data
+
+ try:
+
+ page = self.NewPagesNotebook( name, forced_insertion_index )
+
+ page.AppendSessionPageTuples( subpage_tuples )
+
+ except Exception as e:
+
+ 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 )
+
+ except Exception as e:
+
+ HydrusData.ShowException( e )
+
+
+
+ forced_insertion_index += 1
+
+
+
+ def ChooseNewPage( self ):
+
+ self._ChooseNewPage()
+
+
+ def ChooseNewPageForDeepestNotebook( self ):
+
+ current_page = self.GetCurrentPage()
+
+ if isinstance( current_page, PagesNotebook ):
+
+ current_page.ChooseNewPageForDeepestNotebook()
+
+ else:
+
+ self._ChooseNewPage()
+
+
+
def CleanBeforeDestroy( self ):
for page in self._GetPages():
@@ -372,14 +1232,201 @@ class PagesNotebook( wx.Notebook ):
+ def CloseCurrentPage( self, polite = True ):
+
+ selection = self.GetSelection()
+
+ if selection != wx.NOT_FOUND:
+
+ page = self.GetPage( selection )
+
+ if isinstance( page, PagesNotebook ):
+
+ if page.GetNumPages() > 0:
+
+ page.CloseCurrentPage( polite )
+
+ else:
+
+ self._ClosePage( selection, polite = polite )
+
+
+ else:
+
+ self._ClosePage( selection, polite = polite )
+
+
+
+
+ def EventLeftDoubleClick( self, event ):
+
+ position = event.GetPosition()
+
+ ( tab_index, flags ) = self.HitTest( position )
+
+ if tab_index == wx.NOT_FOUND:
+
+ if flags & wx.NB_HITTEST_NOWHERE and flags & wx.NB_HITTEST_ONPAGE:
+
+ ( notebook, new_position ) = self._GetNotebookFromMousePosition( position )
+
+ notebook.EventNewPageMousePosition( new_position )
+
+ else:
+
+ self.ChooseNewPage()
+
+
+
+
+ def EventMenu( self, event ):
+
+ self._ShowMenu( event.GetPosition() )
+
+
+ def EventMenuFromMousePosition( self, position ):
+
+ ( notebook, new_position ) = self._GetNotebookFromMousePosition( position )
+
+ notebook._ShowMenu( position )
+
+
+ def EventMiddleClick( self, event ):
+
+ ( tab_index, flags ) = self.HitTest( event.GetPosition() )
+
+ if tab_index == wx.NOT_FOUND:
+
+ if flags & wx.NB_HITTEST_NOWHERE and flags & wx.NB_HITTEST_ONPAGE:
+
+ ( notebook, new_position ) = self._GetNotebookFromMousePosition( event.GetPosition() )
+
+ notebook.EventNewPageFromMousePosition( new_position )
+
+ else:
+
+ self.ChooseNewPage()
+
+
+ else:
+
+ self._ClosePage( tab_index )
+
+
+
+ def EventNewPageFromMousePosition( self, position ):
+
+ ( notebook, new_position ) = self._GetNotebookFromMousePosition( position )
+
+ notebook._ChooseNewPage()
+
+
+ def EventPageChanged( self, event ):
+
+ if event.EventObject == self: # because OS X wants to bump this up to parent notebooks
+
+ old_selection = event.GetOldSelection()
+ selection = event.GetSelection()
+
+ if old_selection != wx.NOT_FOUND:
+
+ self.GetPage( old_selection ).PageHidden()
+
+
+ if selection != wx.NOT_FOUND:
+
+ self.GetPage( selection ).PageShown()
+
+
+ self._controller.gui.RefreshStatusBar()
+
+
+ if HC.PLATFORM_OSX:
+
+ event.Skip() # need this or OS X spergs out and never .Show()s new page, wew
+
+
+
+ def GetCurrentMediaPage( self ):
+
+ page = self.GetCurrentPage()
+
+ if isinstance( page, PagesNotebook ):
+
+ return page.GetCurrentMediaPage()
+
+ else:
+
+ return page # this can be None
+
+
+
+ def GetMediaPages( self, only_my_level = False ):
+
+ return self._GetMediaPages( only_my_level )
+
+
+ def GetName( self ):
+
+ return self._name
+
+
def GetNumFiles( self ):
return sum( page.GetNumFiles() for page in self._GetPages() )
- def GetMediaPages( self ):
+ def GetNumPages( self, only_my_level = False ):
- return self._GetMediaPages()
+ if only_my_level:
+
+ return self.GetPageCount()
+
+ else:
+
+ total = 0
+
+ for page in self._GetPages():
+
+ if isinstance( page, PagesNotebook ):
+
+ total += page.GetNumPages( False )
+
+ else:
+
+ total += 1
+
+
+
+ return total
+
+
+
+ def GetOrMakeURLImportPage( self ):
+
+ for page in self._GetPages():
+
+ if isinstance( page, PagesNotebook ):
+
+ if page.HasURLImportPage():
+
+ return page.GetOrMakeURLImportPage()
+
+
+ elif page.IsURLImportPage():
+
+ return page
+
+
+
+ # import page does not exist
+
+ return self.NewPageImportURLs()
+
+
+ def GetPageKey( self ):
+
+ return self._page_key
def GetPages( self ):
@@ -389,21 +1436,300 @@ class PagesNotebook( wx.Notebook ):
def GetPrettyStatus( self ):
- current_page = self.GetCurrentPage()
-
- if current_page is None:
-
- return ''
-
- else:
-
- return current_page.GetPrettyStatus()
-
+ return HydrusData.ConvertIntToPrettyString( self.GetPageCount() ) + ' pages, ' + HydrusData.ConvertIntToPrettyString( self.GetNumFiles() ) + ' files'
def HasPage( self, page ):
- return page in self._GetMediaPages()
+ return self.HasPageKey( page.GetPageKey() )
+
+
+ def HasPageKey( self, page_key ):
+
+ for page in self._GetPages():
+
+ if page.GetPageKey() == page_key:
+
+ return True
+
+ elif isinstance( page, PagesNotebook ) and page.HasPageKey( page_key ):
+
+ return True
+
+
+
+ return False
+
+
+ def HasURLImportPage( self ):
+
+ for page in self._GetPages():
+
+ if isinstance( page, PagesNotebook ):
+
+ if page.HasURLImportPage():
+
+ return True
+
+
+ else:
+
+ if page.IsURLImportPage():
+
+ return True
+
+
+
+
+ return False
+
+
+ def LoadGUISession( self, name ):
+
+ try:
+
+ self.TestAbleToClose()
+
+ except HydrusExceptions.PermissionException:
+
+ return
+
+
+ self._CloseAllPages( polite = False )
+
+ self.AppendGUISession( name )
+
+
+ def NewPage( self, management_controller, initial_hashes = None, forced_insertion_index = None ):
+
+ MAX_TOTAL_PAGES = 150
+
+ ( total_active_page_count, total_closed_page_count ) = self._controller.gui.GetTotalPageCounts()
+
+ if total_active_page_count + total_closed_page_count >= MAX_TOTAL_PAGES:
+
+ self._controller.gui.DeleteAllClosedPages()
+
+
+ if total_active_page_count >= MAX_TOTAL_PAGES:
+
+ HydrusData.ShowText( 'The client cannot have more than ' + str( MAX_TOTAL_PAGES ) + ' pages open! For system stability reasons, please close some now!' )
+
+ return
+
+
+ if total_active_page_count == MAX_TOTAL_PAGES - 5:
+
+ HydrusData.ShowText( 'You have ' + str( total_active_page_count ) + ' pages open! You can only open a few more before system stability is affected! Please close some now!' )
+
+
+ self._controller.ResetIdleTimer()
+ self._controller.ResetPageChangeTimer()
+
+ if initial_hashes is None:
+
+ initial_hashes = []
+
+
+ page = Page( self, self._controller, management_controller, initial_hashes )
+
+ if forced_insertion_index is None:
+
+ if self._next_new_page_index is None:
+
+ insertion_index = self._GetDefaultPageInsertionIndex()
+
+ else:
+
+ insertion_index = self._next_new_page_index
+
+ self._next_new_page_index = None
+
+
+ else:
+
+ insertion_index = forced_insertion_index
+
+
+ page_name = 'page'
+
+ self.InsertPage( insertion_index, page, page_name, select = True )
+
+ self._controller.pub( 'refresh_page_name', page.GetPageKey() )
+
+ wx.CallAfter( page.Start )
+ wx.CallAfter( page.SetSearchFocus )
+
+ return page
+
+
+ def NewPageDuplicateFilter( self ):
+
+ management_controller = ClientGUIManagement.CreateManagementControllerDuplicateFilter()
+
+ return self.NewPage( management_controller )
+
+
+ def NewPageImportBooru( self ):
+
+ with ClientGUIDialogs.DialogSelectBooru( self ) as dlg:
+
+ if dlg.ShowModal() == wx.ID_OK:
+
+ gallery_identifier = dlg.GetGalleryIdentifier()
+
+ return self.NewPageImportGallery( gallery_identifier )
+
+
+
+
+ def NewPageImportGallery( self, gallery_identifier ):
+
+ management_controller = ClientGUIManagement.CreateManagementControllerImportGallery( gallery_identifier )
+
+ return self.NewPage( management_controller )
+
+
+ def NewPageImportPageOfImages( self ):
+
+ management_controller = ClientGUIManagement.CreateManagementControllerImportPageOfImages()
+
+ return self.NewPage( management_controller )
+
+
+ def NewPageImportThreadWatcher( self, thread_url = None ):
+
+ management_controller = ClientGUIManagement.CreateManagementControllerImportThreadWatcher( thread_url )
+
+ return self.NewPage( management_controller )
+
+
+ def NewPageImportURLs( self ):
+
+ management_controller = ClientGUIManagement.CreateManagementControllerImportURLs()
+
+ return self.NewPage( management_controller )
+
+
+ def NewPagePetitions( self, service_key ):
+
+ management_controller = ClientGUIManagement.CreateManagementControllerPetitions( service_key )
+
+ return self.NewPage( management_controller )
+
+
+ def NewPageQuery( self, file_service_key, initial_hashes = None, initial_predicates = None, page_name = None ):
+
+ if initial_hashes is None:
+
+ initial_hashes = []
+
+
+ if initial_predicates is None:
+
+ initial_predicates = []
+
+
+ if page_name is None:
+
+ page_name = 'files'
+
+
+ search_enabled = len( initial_hashes ) == 0
+
+ new_options = self._controller.GetNewOptions()
+
+ tag_service_key = new_options.GetKey( 'default_tag_service_search_page' )
+
+ if not self._controller.services_manager.ServiceExists( tag_service_key ):
+
+ tag_service_key = CC.COMBINED_TAG_SERVICE_KEY
+
+
+ file_search_context = ClientSearch.FileSearchContext( file_service_key = file_service_key, tag_service_key = tag_service_key, predicates = initial_predicates )
+
+ management_controller = ClientGUIManagement.CreateManagementControllerQuery( page_name, file_service_key, file_search_context, search_enabled )
+
+ return self.NewPage( management_controller, initial_hashes = initial_hashes )
+
+
+ def NewPagesNotebook( self, name = 'pages', forced_insertion_index = None ):
+
+ self._controller.ResetIdleTimer()
+ self._controller.ResetPageChangeTimer()
+
+ page = PagesNotebook( self, self._controller, name )
+
+ if forced_insertion_index is None:
+
+ if self._next_new_page_index is None:
+
+ insertion_index = self._GetDefaultPageInsertionIndex()
+
+ else:
+
+ insertion_index = self._next_new_page_index
+
+ self._next_new_page_index = None
+
+
+ else:
+
+ insertion_index = forced_insertion_index
+
+
+ page_name = 'pages'
+
+ self.InsertPage( insertion_index, page, page_name, select = True )
+
+ self._controller.pub( 'refresh_page_name', page.GetPageKey() )
+
+ return page
+
+
+ def NotifyPageUnclosed( self, page ):
+
+ page_key = page.GetPageKey()
+
+ for ( index, closed_page_key ) in self._closed_pages:
+
+ if page_key == closed_page_key:
+
+ page.Show()
+
+ index = min( index, self.GetPageCount() )
+
+ name = page.GetName()
+
+ self.InsertPage( index, page, name, True )
+
+ self._controller.pub( 'refresh_page_name', page.GetPageKey() )
+
+ self._closed_pages.remove( ( index, closed_page_key ) )
+
+ break
+
+
+
+
+ def PageHidden( self ):
+
+ result = self.GetCurrentPage()
+
+ if result is not None:
+
+ result.PageHidden()
+
+
+
+ def PageShown( self ):
+
+ result = self.GetCurrentPage()
+
+ if result is not None:
+
+ result.PageShown()
+
def PrepareToHide( self ):
@@ -414,6 +1740,101 @@ class PagesNotebook( wx.Notebook ):
+ def RefreshPageName( self, page_key = None ):
+
+ if page_key is None:
+
+ for index in range( self.GetPageCount() ):
+
+ self._RefreshPageName( index )
+
+
+ else:
+
+ for ( index, page ) in enumerate( self._GetPages() ):
+
+ do_it = False
+
+ if page.GetPageKey() == page_key:
+
+ do_it = True
+
+ elif isinstance( page, PagesNotebook ) and page.HasPageKey( page_key ):
+
+ do_it = True
+
+
+ if do_it:
+
+ self._RefreshPageName( index )
+
+ break
+
+
+
+
+
+ def SaveGUISession( self, name = None ):
+
+ if name is None:
+
+ while True:
+
+ with ClientGUIDialogs.DialogTextEntry( self, 'Enter a name for the new session.' ) as dlg:
+
+ if dlg.ShowModal() == wx.ID_OK:
+
+ name = dlg.GetValue()
+
+ if name in ( 'just a blank page', 'last session' ):
+
+ wx.MessageBox( 'Sorry, you cannot have that name! Try another.' )
+
+ else:
+
+ existing_session_names = self._controller.Read( 'serialisable_names', HydrusSerialisable.SERIALISABLE_TYPE_GUI_SESSION )
+
+ if name in existing_session_names:
+
+ message = 'Session \'' + name + '\' already exists! Do you want to overwrite it?'
+
+ with ClientGUIDialogs.DialogYesNo( self, message, title = 'Overwrite existing session?', yes_label = 'yes, overwrite', no_label = 'no, choose another name' ) as yn_dlg:
+
+ if yn_dlg.ShowModal() != wx.ID_YES:
+
+ continue
+
+
+
+
+ break
+
+
+ else:
+
+ return
+
+
+
+
+
+ session = GUISession( name )
+
+ for page in self._GetPages():
+
+ session.AddPage( page )
+
+
+ self._controller.Write( 'serialisable', session )
+
+ self._controller.pub( 'notify_new_sessions' )
+
+
+ def SetName( self, name ):
+
+ self._name = name
+
+
def ShowPage( self, showee ):
for ( i, page ) in enumerate( self._GetPages() ):
@@ -446,7 +1867,7 @@ class PagesNotebook( wx.Notebook ):
class GUISession( HydrusSerialisable.SerialisableBaseNamed ):
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_GUI_SESSION
- SERIALISABLE_VERSION = 2
+ SERIALISABLE_VERSION = 3
def __init__( self, name ):
@@ -455,17 +1876,63 @@ class GUISession( HydrusSerialisable.SerialisableBaseNamed ):
self._pages = []
+ def _GetPageTuple( self, page ):
+
+ if isinstance( page, PagesNotebook ):
+
+ name = page.GetName()
+
+ page_tuples = [ self._GetPageTuple( subpage ) for subpage in page.GetPages() ]
+
+ return ( 'pages', ( name, page_tuples ) )
+
+ else:
+
+ management_controller = page.GetManagementController()
+
+ hashes = list( page.GetHashes() )
+
+ return ( 'page', ( management_controller, hashes ) )
+
+
+
def _GetSerialisableInfo( self ):
+ def GetSerialisablePageTuple( page_tuple ):
+
+ ( page_type, page_data ) = page_tuple
+
+ if page_type == 'pages':
+
+ ( name, page_tuples ) = page_data
+
+ serialisable_page_tuples = [ GetSerialisablePageTuple( pt ) for pt in page_tuples ]
+
+ serialisable_page_data = ( name, serialisable_page_tuples )
+
+ elif page_type == 'page':
+
+ ( management_controller, hashes ) = page_data
+
+ serialisable_management_controller = management_controller.GetSerialisableTuple()
+
+ serialisable_hashes = [ hash.encode( 'hex' ) for hash in hashes ]
+
+ serialisable_page_data = ( serialisable_management_controller, serialisable_hashes )
+
+
+ serialisable_tuple = ( page_type, serialisable_page_data )
+
+ return serialisable_tuple
+
+
serialisable_info = []
- for ( management_controller, hashes ) in self._pages:
+ for page_tuple in self._pages:
- serialisable_management_controller = management_controller.GetSerialisableTuple()
+ serialisable_page_tuple = GetSerialisablePageTuple( page_tuple )
- serialisable_hashes = [ hash.encode( 'hex' ) for hash in hashes ]
-
- serialisable_info.append( ( serialisable_management_controller, serialisable_hashes ) )
+ serialisable_info.append( serialisable_page_tuple )
return serialisable_info
@@ -473,13 +1940,39 @@ class GUISession( HydrusSerialisable.SerialisableBaseNamed ):
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
- for ( serialisable_management_controller, serialisable_hashes ) in serialisable_info:
+ def GetPageTuple( serialisable_page_tuple ):
- management_controller = HydrusSerialisable.CreateFromSerialisableTuple( serialisable_management_controller )
+ ( page_type, serialisable_page_data ) = serialisable_page_tuple
- hashes = [ hash.decode( 'hex' ) for hash in serialisable_hashes ]
+ if page_type == 'pages':
+
+ ( name, serialisable_page_tuples ) = serialisable_page_data
+
+ page_tuples = [ GetPageTuple( spt ) for spt in serialisable_page_tuples ]
+
+ page_data = ( name, page_tuples )
+
+ elif page_type == 'page':
+
+ ( serialisable_management_controller, serialisable_hashes ) = serialisable_page_data
+
+ management_controller = HydrusSerialisable.CreateFromSerialisableTuple( serialisable_management_controller )
+
+ hashes = [ hash.decode( 'hex' ) for hash in serialisable_hashes ]
+
+ page_data = ( management_controller, hashes )
+
- self._pages.append( ( management_controller, hashes ) )
+ page_tuple = ( page_type, page_data )
+
+ return page_tuple
+
+
+ for serialisable_page_tuple in serialisable_info:
+
+ page_tuple = GetPageTuple( serialisable_page_tuple )
+
+ self._pages.append( page_tuple )
@@ -503,18 +1996,29 @@ class GUISession( HydrusSerialisable.SerialisableBaseNamed ):
return ( 2, new_serialisable_info )
-
- def AddPage( self, management_controller, hashes ):
-
- self._pages.append( ( management_controller, hashes ) )
+ if version == 2:
+
+ new_serialisable_info = []
+
+ for ( serialisable_management_controller, serialisable_hashes ) in old_serialisable_info:
+
+ new_serialisable_info.append( ( 'page', ( serialisable_management_controller, serialisable_hashes ) ) )
+
+
+ return ( 3, new_serialisable_info )
+
- def IteratePages( self ):
+ def AddPage( self, page ):
- for page_tuple in self._pages:
-
- yield page_tuple
-
+ page_tuple = self._GetPageTuple( page )
+
+ self._pages.append( page_tuple )
+
+
+ def GetPages( self ):
+
+ return self._pages
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_GUI_SESSION ] = GUISession
diff --git a/include/ClientGUIPanels.py b/include/ClientGUIPanels.py
index 972193bf..0a2c72f7 100644
--- a/include/ClientGUIPanels.py
+++ b/include/ClientGUIPanels.py
@@ -450,7 +450,7 @@ class ReviewServicePanel( wx.Panel ):
def __init__( self, parent, service ):
- ClientGUICommon.StaticBox.__init__( self, parent, 'clientside network' )
+ ClientGUICommon.StaticBox.__init__( self, parent, 'this client\'s network use' )
self._service = service
@@ -530,7 +530,7 @@ class ReviewServicePanel( wx.Panel ):
def __init__( self, parent, service ):
- ClientGUICommon.StaticBox.__init__( self, parent, 'serverside hydrus account' )
+ ClientGUICommon.StaticBox.__init__( self, parent, 'hydrus service account' )
self._service = service
diff --git a/include/ClientGUIScrolledPanelsManagement.py b/include/ClientGUIScrolledPanelsManagement.py
index 8996b3bc..559ccb6d 100644
--- a/include/ClientGUIScrolledPanelsManagement.py
+++ b/include/ClientGUIScrolledPanelsManagement.py
@@ -615,7 +615,7 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
def __init__( self, parent, service_type, dictionary ):
- ClientGUICommon.StaticBox.__init__( self, parent, 'clientside network' )
+ ClientGUICommon.StaticBox.__init__( self, parent, 'network connection' )
self._service_type = service_type
diff --git a/include/ClientGUIScrolledPanelsReview.py b/include/ClientGUIScrolledPanelsReview.py
index efcceeb3..2da0ba45 100644
--- a/include/ClientGUIScrolledPanelsReview.py
+++ b/include/ClientGUIScrolledPanelsReview.py
@@ -1,5 +1,6 @@
import ClientConstants as CC
import ClientData
+import ClientDefaults
import ClientGUICommon
import ClientGUIDialogs
import ClientGUIFrames
@@ -313,10 +314,12 @@ class ReviewAllBandwidthPanel( ClientGUIScrolledPanels.ReviewPanel ):
self._history_time_delta_none = wx.CheckBox( self, label = 'show all' )
self._history_time_delta_none.Bind( wx.EVT_CHECKBOX, self.EventTimeDeltaChanged )
- self._bandwidths = ClientGUIListCtrl.BetterListCtrl( self, 'bandwidth review', 20, 30, [ ( 'name', -1 ), ( 'type', 14 ), ( 'current usage', 14 ), ( 'past 24 hours', 14 ), ( 'this month', 14 ), ( 'has specific rules', 20 ) ], self._ConvertNetworkContextsToListCtrlTuples, activation_callback = self.ShowNetworkContext )
+ self._bandwidths = ClientGUIListCtrl.BetterListCtrl( self, 'bandwidth review', 20, 30, [ ( 'name', -1 ), ( 'type', 14 ), ( 'current usage', 14 ), ( 'past 24 hours', 15 ), ( 'this month', 12 ), ( 'has specific rules', 18 ), ( 'blocked?', 10 ) ], self._ConvertNetworkContextsToListCtrlTuples, activation_callback = self.ShowNetworkContext )
self._edit_default_bandwidth_rules_button = ClientGUICommon.BetterButton( self, 'edit default bandwidth rules', self._EditDefaultBandwidthRules )
+ self._reset_default_bandwidth_rules_button = ClientGUICommon.BetterButton( self, 'reset default bandwidth rules', self._ResetDefaultBandwidthRules )
+
default_rules_help_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.help, self._ShowDefaultRulesHelp )
default_rules_help_button.SetToolTipString( 'Show help regarding default bandwidth rules.' )
@@ -347,6 +350,7 @@ class ReviewAllBandwidthPanel( ClientGUIScrolledPanels.ReviewPanel ):
button_hbox = wx.BoxSizer( wx.HORIZONTAL )
button_hbox.AddF( self._edit_default_bandwidth_rules_button, CC.FLAGS_VCENTER )
+ button_hbox.AddF( self._reset_default_bandwidth_rules_button, CC.FLAGS_VCENTER )
button_hbox.AddF( default_rules_help_button, CC.FLAGS_VCENTER )
button_hbox.AddF( self._delete_record_button, CC.FLAGS_VCENTER )
@@ -393,7 +397,18 @@ class ReviewAllBandwidthPanel( ClientGUIScrolledPanels.ReviewPanel ):
pretty_has_rules = ''
- return ( ( pretty_network_context, pretty_context_type, pretty_current_usage, pretty_day_usage, pretty_month_usage, pretty_has_rules ), ( sortable_network_context, sortable_context_type, current_usage, day_usage, month_usage, has_rules ) )
+ blocked = not self._controller.network_engine.bandwidth_manager.CanDoWork( [ network_context ] )
+
+ if blocked:
+
+ pretty_blocked = 'yes'
+
+ else:
+
+ pretty_blocked = ''
+
+
+ return ( ( pretty_network_context, pretty_context_type, pretty_current_usage, pretty_day_usage, pretty_month_usage, pretty_has_rules, pretty_blocked ), ( sortable_network_context, sortable_context_type, current_usage, day_usage, month_usage, has_rules, blocked ) )
def _DeleteNetworkContexts( self ):
@@ -442,6 +457,19 @@ class ReviewAllBandwidthPanel( ClientGUIScrolledPanels.ReviewPanel ):
+ def _ResetDefaultBandwidthRules( self ):
+
+ message = 'Reset your \'default\' and \'global\' bandwidth rules to default?'
+
+ with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
+
+ if dlg.ShowModal() == wx.ID_YES:
+
+ ClientDefaults.SetDefaultBandwidthManagerRules( self._controller.network_engine.bandwidth_manager )
+
+
+
+
def _ShowDefaultRulesHelp( self ):
help = 'Network requests act in multiple contexts. Most use the \'global\' and \'web domain\' network contexts, but a hydrus server request, for instance, will add its own service-specific context, and a subscription will add both itself and its downloader.'
diff --git a/include/ClientMedia.py b/include/ClientMedia.py
index 63907323..98fea131 100644
--- a/include/ClientMedia.py
+++ b/include/ClientMedia.py
@@ -2105,7 +2105,7 @@ class MediaSort( HydrusSerialisable.SerialisableBase ):
sort_string_lookup[ CC.SORT_FILES_BY_FILESIZE ] = 'filesize'
sort_string_lookup[ CC.SORT_FILES_BY_DURATION ] = 'duration'
- sort_string_lookup[ CC.SORT_FILES_BY_IMPORT_TIME ] = 'age'
+ sort_string_lookup[ CC.SORT_FILES_BY_IMPORT_TIME ] = 'time imported'
sort_string_lookup[ CC.SORT_FILES_BY_MIME ] = 'mime'
sort_string_lookup[ CC.SORT_FILES_BY_RANDOM ] = 'random'
sort_string_lookup[ CC.SORT_FILES_BY_WIDTH ] = 'width'
diff --git a/include/ClientNetworking.py b/include/ClientNetworking.py
index b278a729..4ab465c9 100644
--- a/include/ClientNetworking.py
+++ b/include/ClientNetworking.py
@@ -1112,6 +1112,23 @@ class NetworkBandwidthManager( HydrusSerialisable.SerialisableBase ):
+ def _CanStartRequest( self, network_contexts ):
+
+ for network_context in network_contexts:
+
+ bandwidth_rules = self._GetRules( network_context )
+
+ bandwidth_tracker = self._network_contexts_to_bandwidth_trackers[ network_context ]
+
+ if not bandwidth_rules.CanStartRequest( bandwidth_tracker ):
+
+ return False
+
+
+
+ return True
+
+
def _GetRules( self, network_context ):
if network_context not in self._network_contexts_to_bandwidth_rules:
@@ -1152,6 +1169,16 @@ class NetworkBandwidthManager( HydrusSerialisable.SerialisableBase ):
+ def _ReportRequestUsed( self, network_contexts ):
+
+ for network_context in network_contexts:
+
+ self._network_contexts_to_bandwidth_trackers[ network_context ].ReportRequestUsed()
+
+
+ self._SetDirty()
+
+
def _SetDirty( self ):
self._dirty = True
@@ -1177,7 +1204,7 @@ class NetworkBandwidthManager( HydrusSerialisable.SerialisableBase ):
- def CanDoWork( self, network_contexts, expected_requests, expected_bytes ):
+ def CanDoWork( self, network_contexts, expected_requests = 3, expected_bytes = 1048576 ):
with self._lock:
@@ -1201,19 +1228,7 @@ class NetworkBandwidthManager( HydrusSerialisable.SerialisableBase ):
with self._lock:
- for network_context in network_contexts:
-
- bandwidth_rules = self._GetRules( network_context )
-
- bandwidth_tracker = self._network_contexts_to_bandwidth_trackers[ network_context ]
-
- if not bandwidth_rules.CanStartRequest( bandwidth_tracker ):
-
- return False
-
-
-
- return True
+ return self._CanStartRequest( network_contexts )
@@ -1281,7 +1296,23 @@ class NetworkBandwidthManager( HydrusSerialisable.SerialisableBase ):
with self._lock:
- result = []
+ result = set()
+
+ for ( network_context, bandwidth_rules ) in self._network_contexts_to_bandwidth_rules.items():
+
+ if network_context.IsDefault() or network_context.IsEphemeral():
+
+ continue
+
+
+ # if a context has rules but no activity, list it so the user can edit the rules if needed
+ # in case they set too restrictive rules on an old context and now can't get it up again with activity because of the rules!
+
+ if network_context not in self._network_contexts_to_bandwidth_trackers or self._network_contexts_to_bandwidth_trackers[ network_context ].GetUsage( HC.BANDWIDTH_TYPE_REQUESTS, None ) == 0:
+
+ result.add( network_context )
+
+
for ( network_context, bandwidth_tracker ) in self._network_contexts_to_bandwidth_trackers.items():
@@ -1298,7 +1329,7 @@ class NetworkBandwidthManager( HydrusSerialisable.SerialisableBase ):
- result.append( network_context )
+ result.add( network_context )
return result
@@ -1379,12 +1410,7 @@ class NetworkBandwidthManager( HydrusSerialisable.SerialisableBase ):
with self._lock:
- for network_context in network_contexts:
-
- self._network_contexts_to_bandwidth_trackers[ network_context ].ReportRequestUsed()
-
-
- self._SetDirty()
+ self._ReportRequestUsed( network_contexts )
@@ -1416,6 +1442,23 @@ class NetworkBandwidthManager( HydrusSerialisable.SerialisableBase ):
+ def TryToStartRequest( self, network_contexts ):
+
+ # this wraps canstart and reportrequest in one transaction to stop 5/1 rq/s happening due to race condition
+
+ with self._lock:
+
+ if not self._CanStartRequest( network_contexts ):
+
+ return False
+
+
+ self._ReportRequestUsed( network_contexts )
+
+ return True
+
+
+
def UsesDefaultRules( self, network_context ):
with self._lock:
@@ -1715,6 +1758,8 @@ class NetworkEngine( object ):
else:
+ job.SetStatus( u'waiting for download slot\u2026' )
+
return True
@@ -1771,6 +1816,11 @@ class NetworkJob( object ):
def __init__( self, method, url, body = None, referral_url = None, temp_path = None, for_login = False ):
+ if HG.network_report_mode:
+
+ HydrusData.ShowText( 'Network Job: ' + method + ' ' + url )
+
+
self.engine = None
self._lock = threading.Lock()
@@ -1782,6 +1832,8 @@ class NetworkJob( object ):
self._temp_path = temp_path
self._for_login = for_login
+ self._creation_time = HydrusData.GetNow()
+
self._bandwidth_tracker = HydrusNetworking.BandwidthTracker()
self._wake_time = 0
@@ -1845,8 +1897,6 @@ class NetworkJob( object ):
with self._lock:
- self._ReportRequestUsed()
-
self._status_text = u'sending request\u2026'
@@ -1984,13 +2034,6 @@ class NetworkJob( object ):
self.engine.bandwidth_manager.ReportDataUsed( self._network_contexts, num_bytes )
- def _ReportRequestUsed( self ):
-
- self._bandwidth_tracker.ReportRequestUsed()
-
- self.engine.bandwidth_manager.ReportRequestUsed( self._network_contexts )
-
-
def _SetCancelled( self ):
self._is_cancelled = True
@@ -2030,15 +2073,19 @@ class NetworkJob( object ):
if self._ObeysBandwidth():
- result = self.engine.bandwidth_manager.CanStartRequest( self._network_contexts )
+ result = self.engine.bandwidth_manager.TryToStartRequest( self._network_contexts )
- if not result:
+ if result:
+
+ self._bandwidth_tracker.ReportRequestUsed()
+
+ else:
waiting_duration = self.engine.bandwidth_manager.GetWaitingEstimate( self._network_contexts )
- if waiting_duration <= 1:
+ if waiting_duration < 1:
- self._status_text = ''
+ self._status_text = u'bandwidth free imminently\u2026'
else:
@@ -2067,6 +2114,10 @@ class NetworkJob( object ):
else:
+ self._bandwidth_tracker.ReportRequestUsed()
+
+ self.engine.bandwidth_manager.ReportRequestUsed( self._network_contexts )
+
return True
@@ -2122,6 +2173,14 @@ class NetworkJob( object ):
+ def GetCreationTime( self ):
+
+ with self._lock:
+
+ return self._creation_time
+
+
+
def GetErrorException( self ):
with self._lock:
@@ -2213,6 +2272,11 @@ class NetworkJob( object ):
return self.engine is None
+ def ObeysBandwidth( self ):
+
+ return self._ObeysBandwidth()
+
+
def OverrideBandwidth( self ):
with self._lock:
diff --git a/include/HydrusConstants.py b/include/HydrusConstants.py
index b60b1eb9..e24668d1 100755
--- a/include/HydrusConstants.py
+++ b/include/HydrusConstants.py
@@ -49,7 +49,7 @@ options = {}
# Misc
NETWORK_VERSION = 18
-SOFTWARE_VERSION = 268
+SOFTWARE_VERSION = 269
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
diff --git a/include/HydrusController.py b/include/HydrusController.py
index e0436148..c8da1ffb 100644
--- a/include/HydrusController.py
+++ b/include/HydrusController.py
@@ -243,6 +243,11 @@ class HydrusController( object ):
+ def GetBootTime( self ):
+
+ return self._timestamps[ 'boot' ]
+
+
def GetDBDir( self ):
return self.db_dir
diff --git a/include/HydrusData.py b/include/HydrusData.py
index c92853aa..8ce51f3e 100644
--- a/include/HydrusData.py
+++ b/include/HydrusData.py
@@ -1165,7 +1165,7 @@ def ToUnicode( text_producing_object ):
except:
- text = repr( text ).decode( 'utf-8' )
+ text = unicode( repr( text ) )
diff --git a/include/HydrusGlobals.py b/include/HydrusGlobals.py
index 09f5fd97..31d5837f 100644
--- a/include/HydrusGlobals.py
+++ b/include/HydrusGlobals.py
@@ -11,6 +11,7 @@ callto_report_mode = False
db_report_mode = False
db_profile_mode = False
gui_report_mode = False
+network_report_mode = False
pubsub_profile_mode = False
force_idle_mode = False
server_busy = False
diff --git a/include/HydrusThreading.py b/include/HydrusThreading.py
index 88167afb..08fbcef4 100644
--- a/include/HydrusThreading.py
+++ b/include/HydrusThreading.py
@@ -237,6 +237,8 @@ class THREADCallToThread( DAEMON ):
except Exception as e:
+ HydrusData.Print( traceback.format_exc() )
+
HydrusData.ShowException( e )
finally:
diff --git a/include/TestClientNetworking.py b/include/TestClientNetworking.py
index 266ef842..bf871f79 100644
--- a/include/TestClientNetworking.py
+++ b/include/TestClientNetworking.py
@@ -452,6 +452,8 @@ class TestNetworkingJob( unittest.TestCase ):
job = self._GetJob()
+ job.BandwidthOK()
+
job.Start()
bm = job.engine.bandwidth_manager
diff --git a/include/TestDB.py b/include/TestDB.py
index 3ca2d215..7f486abc 100644
--- a/include/TestDB.py
+++ b/include/TestDB.py
@@ -624,74 +624,132 @@ class TestClientDB( unittest.TestCase ):
def test_gui_sessions( self ):
- session = ClientGUIPages.GUISession( 'test_session' )
+ test_frame = wx.Frame( None )
- gallery_identifier = ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_HENTAI_FOUNDRY_ARTIST )
-
- management_controller = ClientGUIManagement.CreateManagementControllerImportGallery( gallery_identifier )
-
- session.AddPage( management_controller, [] )
-
- service_keys_to_tags = { HydrusData.GenerateKey() : [ 'some', 'tags' ] }
-
- management_controller = ClientGUIManagement.CreateManagementControllerImportHDD( [ 'some', 'paths' ], ClientData.ImportFileOptions(), { 'paths' : service_keys_to_tags }, True )
-
- session.AddPage( management_controller, [] )
-
- management_controller = ClientGUIManagement.CreateManagementControllerImportThreadWatcher()
-
- session.AddPage( management_controller, [] )
-
- management_controller = ClientGUIManagement.CreateManagementControllerImportPageOfImages()
-
- session.AddPage( management_controller, [] )
-
- management_controller = ClientGUIManagement.CreateManagementControllerPetitions( CC.LOCAL_TAG_SERVICE_KEY ) # local because the controller wants to look up the service
-
- session.AddPage( management_controller, [] )
-
- fsc = ClientSearch.FileSearchContext( file_service_key = HydrusData.GenerateKey(), predicates = [] )
-
- management_controller = ClientGUIManagement.CreateManagementControllerQuery( 'search', HydrusData.GenerateKey(), fsc, True )
-
- session.AddPage( management_controller, [] )
-
- fsc = ClientSearch.FileSearchContext( file_service_key = HydrusData.GenerateKey(), tag_service_key = HydrusData.GenerateKey(), predicates = [] )
-
- management_controller = ClientGUIManagement.CreateManagementControllerQuery( 'search', HydrusData.GenerateKey(), fsc, False )
-
- session.AddPage( management_controller, [ HydrusData.GenerateKey() for i in range( 200 ) ] )
-
- fsc = ClientSearch.FileSearchContext( file_service_key = HydrusData.GenerateKey(), predicates = [ ClientSearch.SYSTEM_PREDICATE_ARCHIVE ] )
-
- management_controller = ClientGUIManagement.CreateManagementControllerQuery( 'files', HydrusData.GenerateKey(), fsc, True )
-
- session.AddPage( management_controller, [] )
-
- fsc = ClientSearch.FileSearchContext( file_service_key = HydrusData.GenerateKey(), predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', min_current_count = 1, min_pending_count = 3 ) ] )
-
- management_controller = ClientGUIManagement.CreateManagementControllerQuery( 'wew lad', HydrusData.GenerateKey(), fsc, True )
-
- session.AddPage( management_controller, [] )
-
- fsc = ClientSearch.FileSearchContext( file_service_key = HydrusData.GenerateKey(), predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_RATING, ( '>', 0.2, HydrusData.GenerateKey() ) ), ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_FILE_SERVICE, ( True, HC.CONTENT_STATUS_CURRENT, HydrusData.GenerateKey() ) ) ] )
-
- management_controller = ClientGUIManagement.CreateManagementControllerQuery( 'files', HydrusData.GenerateKey(), fsc, True )
-
- session.AddPage( management_controller, [] )
-
- self._write( 'serialisable', session )
-
- result = self._read( 'serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_GUI_SESSION, 'test_session' )
-
- page_names = []
-
- for ( management_controller, initial_hashes ) in result.IteratePages():
+ try:
- page_names.append( management_controller.GetPageName() )
+ session = ClientGUIPages.GUISession( 'test_session' )
+
+ #
+
+ gallery_identifier = ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_HENTAI_FOUNDRY_ARTIST )
+
+ management_controller = ClientGUIManagement.CreateManagementControllerImportGallery( gallery_identifier )
+
+ page = ClientGUIPages.Page( test_frame, HG.test_controller, management_controller, [] )
+
+ session.AddPage( page )
+
+ #
+
+ service_keys_to_tags = { HydrusData.GenerateKey() : [ 'some', 'tags' ] }
+
+ management_controller = ClientGUIManagement.CreateManagementControllerImportHDD( [ 'some', 'paths' ], ClientData.ImportFileOptions(), { 'paths' : service_keys_to_tags }, True )
+
+ management_controller.GetVariable( 'hdd_import' ).PausePlay() # to stop trying to import 'some' 'paths'
+
+ page = ClientGUIPages.Page( test_frame, HG.test_controller, management_controller, [] )
+
+ session.AddPage( page )
+
+ #
+
+ management_controller = ClientGUIManagement.CreateManagementControllerImportThreadWatcher()
+
+ page = ClientGUIPages.Page( test_frame, HG.test_controller, management_controller, [] )
+
+ session.AddPage( page )
+
+ #
+
+ management_controller = ClientGUIManagement.CreateManagementControllerImportPageOfImages()
+
+ page = ClientGUIPages.Page( test_frame, HG.test_controller, management_controller, [] )
+
+ session.AddPage( page )
+
+ #
+
+ management_controller = ClientGUIManagement.CreateManagementControllerPetitions( HG.test_controller.example_tag_repo_service_key )
+
+ page = ClientGUIPages.Page( test_frame, HG.test_controller, management_controller, [] )
+
+ session.AddPage( page )
+
+ #
+
+ fsc = ClientSearch.FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY, predicates = [] )
+
+ management_controller = ClientGUIManagement.CreateManagementControllerQuery( 'search', CC.LOCAL_FILE_SERVICE_KEY, fsc, True )
+
+ page = ClientGUIPages.Page( test_frame, HG.test_controller, management_controller, [] )
+
+ session.AddPage( page )
+
+ #
+
+ fsc = ClientSearch.FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY, tag_service_key = CC.LOCAL_TAG_SERVICE_KEY, predicates = [] )
+
+ management_controller = ClientGUIManagement.CreateManagementControllerQuery( 'search', CC.LOCAL_FILE_SERVICE_KEY, fsc, False )
+
+ page = ClientGUIPages.Page( test_frame, HG.test_controller, management_controller, [ HydrusData.GenerateKey() for i in range( 200 ) ] )
+
+ session.AddPage( page )
+
+ #
+
+ fsc = ClientSearch.FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY, predicates = [ ClientSearch.SYSTEM_PREDICATE_ARCHIVE ] )
+
+ management_controller = ClientGUIManagement.CreateManagementControllerQuery( 'files', CC.LOCAL_FILE_SERVICE_KEY, fsc, True )
+
+ page = ClientGUIPages.Page( test_frame, HG.test_controller, management_controller, [] )
+
+ session.AddPage( page )
+
+ #
+
+ fsc = ClientSearch.FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY, predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', min_current_count = 1, min_pending_count = 3 ) ] )
+
+ management_controller = ClientGUIManagement.CreateManagementControllerQuery( 'wew lad', CC.LOCAL_FILE_SERVICE_KEY, fsc, True )
+
+ page = ClientGUIPages.Page( test_frame, HG.test_controller, management_controller, [] )
+
+ session.AddPage( page )
+
+ #
+
+ fsc = ClientSearch.FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY, predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_RATING, ( '>', 0.2, TestConstants.LOCAL_RATING_NUMERICAL_SERVICE_KEY ) ), ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_FILE_SERVICE, ( True, HC.CONTENT_STATUS_CURRENT, CC.LOCAL_FILE_SERVICE_KEY ) ) ] )
+
+ management_controller = ClientGUIManagement.CreateManagementControllerQuery( 'files', CC.LOCAL_FILE_SERVICE_KEY, fsc, True )
+
+ page = ClientGUIPages.Page( test_frame, HG.test_controller, management_controller, [] )
+
+ session.AddPage( page )
+
+ #
+
+ self._write( 'serialisable', session )
+
+ result = self._read( 'serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_GUI_SESSION, 'test_session' )
+
+ page_names = []
+
+ for ( page_type, page_data ) in result.GetPages():
+
+ if page_type == 'page':
+
+ ( management_controller, initial_hashes ) = page_data
+
+ page_names.append( management_controller.GetPageName() )
+
+
+
+ self.assertEqual( page_names, [ u'hentai foundry artist', u'import', u'thread watcher', u'page download', u'example tag repo petitions', u'search', u'search', u'files', u'wew lad', u'files' ] )
+
+ finally:
+
+ test_frame.Destroy()
-
- self.assertEqual( page_names, [ u'hentai foundry artist', u'import', u'thread watcher', u'page download', u'local tags petitions', u'search', u'search', u'files', u'wew lad', u'files' ] )
def test_import( self ):
diff --git a/test.py b/test.py
index 8dec1b33..5bbb5d16 100644
--- a/test.py
+++ b/test.py
@@ -92,6 +92,10 @@ class Controller( object ):
self._reads[ 'messaging_sessions' ] = []
self._reads[ 'tag_censorship' ] = []
self._reads[ 'options' ] = ClientDefaults.GetClientDefaultOptions()
+ self._reads[ 'file_system_predicates' ] = []
+ self._reads[ 'media_results' ] = []
+
+ self.example_tag_repo_service_key = HydrusData.GenerateKey()
services = []
@@ -100,6 +104,8 @@ class Controller( object ):
services.append( ClientServices.GenerateService( CC.LOCAL_FILE_SERVICE_KEY, HC.LOCAL_FILE_DOMAIN, CC.LOCAL_FILE_SERVICE_KEY ) )
services.append( ClientServices.GenerateService( CC.TRASH_SERVICE_KEY, HC.LOCAL_FILE_TRASH_DOMAIN, CC.LOCAL_FILE_SERVICE_KEY ) )
services.append( ClientServices.GenerateService( CC.LOCAL_TAG_SERVICE_KEY, HC.LOCAL_TAG, CC.LOCAL_TAG_SERVICE_KEY ) )
+ services.append( ClientServices.GenerateService( self.example_tag_repo_service_key, HC.TAG_REPOSITORY, 'example tag repo' ) )
+ services.append( ClientServices.GenerateService( CC.COMBINED_TAG_SERVICE_KEY, HC.COMBINED_TAG, CC.COMBINED_TAG_SERVICE_KEY ) )
services.append( ClientServices.GenerateService( TestConstants.LOCAL_RATING_LIKE_SERVICE_KEY, HC.LOCAL_RATING_LIKE, 'example local rating like service' ) )
services.append( ClientServices.GenerateService( TestConstants.LOCAL_RATING_NUMERICAL_SERVICE_KEY, HC.LOCAL_RATING_NUMERICAL, 'example local rating numerical service' ) )
@@ -197,6 +203,11 @@ class Controller( object ):
CallToThreadLongRunning = CallToThread
+ def DBCurrentlyDoingJob( self ):
+
+ return False
+
+
def DoHTTP( self, *args, **kwargs ): return self._http.Request( *args, **kwargs )
def GetClientSessionManager( self ):
@@ -245,6 +256,11 @@ class Controller( object ):
return True
+ def IsCurrentPage( self, page_key ):
+
+ return False
+
+
def IsFirstStart( self ):
return True
@@ -260,6 +276,16 @@ class Controller( object ):
return HG.model_shutdown
+ def PageCompletelyDestroyed( self, page_key ):
+
+ return False
+
+
+ def PageClosedButNotDestroyed( self, page_key ):
+
+ return False
+
+
def Read( self, name, *args, **kwargs ):
return self._reads[ name ]
@@ -313,11 +339,25 @@ class Controller( object ):
runner.run( suite )
- def SetHTTP( self, http ): self._http = http
+ def SetHTTP( self, http ):
+
+ self._http = http
+
- def SetRead( self, name, value ): self._reads[ name ] = value
+ def SetRead( self, name, value ):
+
+ self._reads[ name ] = value
+
- def SetWebCookies( self, name, value ): self._cookies[ name ] = value
+ def SetWebCookies( self, name, value ):
+
+ self._cookies[ name ] = value
+
+
+ def StartFileQuery( self, page_key, job_key, search_context ):
+
+ pass
+
def TidyUp( self ):
@@ -331,6 +371,11 @@ class Controller( object ):
return HG.view_shutdown
+ def WaitUntilPubSubsEmpty( self ):
+
+ pass
+
+
def Write( self, name, *args, **kwargs ):
self._writes[ name ].append( ( args, kwargs ) )