924 lines
30 KiB
Python
924 lines
30 KiB
Python
import os
|
|
import re
|
|
|
|
from qtpy import QtCore as QC
|
|
from qtpy import QtWidgets as QW
|
|
from qtpy import QtGui as QG
|
|
|
|
from hydrus.core import HydrusConstants as HC
|
|
from hydrus.core import HydrusData
|
|
from hydrus.core import HydrusGlobals as HG
|
|
|
|
from hydrus.client import ClientConstants as CC
|
|
from hydrus.client.gui import ClientGUIFrames
|
|
from hydrus.client.gui import ClientGUIFunctions
|
|
from hydrus.client.gui import ClientGUIShortcuts
|
|
from hydrus.client.gui import QtPorting as QP
|
|
from hydrus.client.gui.lists import ClientGUIListBoxes
|
|
from hydrus.client.gui.search import ClientGUIACDropdown
|
|
from hydrus.client.gui.widgets import ClientGUICommon
|
|
|
|
class Dialog( QP.Dialog ):
|
|
|
|
def __init__( self, parent, title, style = QC.Qt.Dialog, position = 'topleft' ):
|
|
|
|
QP.Dialog.__init__( self, parent )
|
|
|
|
self.setWindowFlags( style )
|
|
|
|
self.setWindowTitle( title )
|
|
|
|
if parent is not None and position == 'topleft':
|
|
|
|
parent_tlw = self.parentWidget().window()
|
|
|
|
pos = parent_tlw.pos() + QC.QPoint( 50, 50 )
|
|
|
|
self.move( pos )
|
|
|
|
|
|
self.setWindowFlag( QC.Qt.WindowContextHelpButtonHint, on = False )
|
|
|
|
self._new_options = HG.client_controller.new_options
|
|
|
|
self.setWindowIcon( QG.QIcon( HG.client_controller.frame_icon_pixmap ) )
|
|
|
|
self._widget_event_filter = QP.WidgetEventFilter( self )
|
|
|
|
if parent is not None and position == 'center':
|
|
|
|
QP.CallAfter( QP.CenterOnWindow, parent, self )
|
|
|
|
|
|
HG.client_controller.ResetIdleTimer()
|
|
|
|
|
|
def keyPressEvent( self, event ):
|
|
|
|
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
|
|
|
|
if key == QC.Qt.Key_Escape:
|
|
|
|
self.done( QW.QDialog.Rejected )
|
|
|
|
else:
|
|
|
|
QP.Dialog.keyPressEvent( self, event )
|
|
|
|
|
|
|
|
def SetInitialSize( self, size: QC.QSize ):
|
|
|
|
display_size = ClientGUIFunctions.GetDisplaySize( self )
|
|
|
|
width = min( display_size.width(), size.width() )
|
|
height = min( display_size.height(), size.height() )
|
|
|
|
self.resize( QC.QSize( width, height ) )
|
|
|
|
min_width = min( 240, width )
|
|
min_height = min( 240, height )
|
|
|
|
self.setMinimumSize( QC.QSize( min_width, min_height ) )
|
|
|
|
|
|
class DialogChooseNewServiceMethod( Dialog ):
|
|
|
|
def __init__( self, parent ):
|
|
|
|
Dialog.__init__( self, parent, 'how to set up the account?', position = 'center' )
|
|
|
|
register_message = 'I want to initialise a new account with the server. I have a registration token (a hexadecimal key starting with \'r\').'
|
|
|
|
self._register = QW.QPushButton( register_message, self )
|
|
self._register.clicked.connect( self.EventRegister )
|
|
|
|
setup_message = 'The account is already initialised; I just want to add it to this client. I have a normal access key.'
|
|
|
|
self._setup = QW.QPushButton( setup_message, self )
|
|
self._setup.clicked.connect( self.accept )
|
|
|
|
vbox = QP.VBoxLayout()
|
|
|
|
QP.AddToLayout( vbox, self._register, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
st = ClientGUICommon.BetterStaticText( self, '-or-' )
|
|
|
|
st.setAlignment( QC.Qt.AlignCenter )
|
|
|
|
QP.AddToLayout( vbox, st, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
QP.AddToLayout( vbox, self._setup, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
self.setLayout( vbox )
|
|
|
|
size_hint = self.sizeHint()
|
|
|
|
QP.SetInitialSize( self, size_hint )
|
|
|
|
self._should_register = False
|
|
|
|
ClientGUIFunctions.SetFocusLater( self._register )
|
|
|
|
|
|
def EventRegister( self ):
|
|
|
|
self._should_register = True
|
|
|
|
self.done( QW.QDialog.Accepted )
|
|
|
|
|
|
def GetRegister( self ):
|
|
|
|
return self._should_register
|
|
|
|
|
|
class DialogGenerateNewAccounts( Dialog ):
|
|
|
|
def __init__( self, parent, service_key ):
|
|
|
|
Dialog.__init__( self, parent, 'configure new accounts' )
|
|
|
|
self._service_key = service_key
|
|
|
|
self._num = ClientGUICommon.BetterSpinBox( self, min=1, max=10000, width = 80 )
|
|
|
|
self._account_types = ClientGUICommon.BetterChoice( self )
|
|
|
|
self._lifetime = ClientGUICommon.BetterChoice( self )
|
|
|
|
self._ok = QW.QPushButton( 'OK', self )
|
|
self._ok.clicked.connect( self.EventOK )
|
|
self._ok.setObjectName( 'HydrusAccept' )
|
|
|
|
self._cancel = QW.QPushButton( 'Cancel', self )
|
|
self._cancel.clicked.connect( self.reject )
|
|
self._cancel.setObjectName( 'HydrusCancel' )
|
|
|
|
#
|
|
|
|
self._num.setValue( 1 )
|
|
|
|
service = HG.client_controller.services_manager.GetService( service_key )
|
|
|
|
response = service.Request( HC.GET, 'account_types' )
|
|
|
|
account_types = response[ 'account_types' ]
|
|
|
|
for account_type in account_types:
|
|
|
|
self._account_types.addItem( account_type.GetTitle(), account_type )
|
|
|
|
|
|
self._account_types.setCurrentIndex( 0 )
|
|
|
|
for ( s, value ) in HC.lifetimes:
|
|
|
|
self._lifetime.addItem( s, value )
|
|
|
|
|
|
self._lifetime.setCurrentIndex( 3 ) # one year
|
|
|
|
#
|
|
|
|
ctrl_box = QP.HBoxLayout()
|
|
|
|
QP.AddToLayout( ctrl_box, ClientGUICommon.BetterStaticText(self,'generate'), CC.FLAGS_CENTER_PERPENDICULAR )
|
|
QP.AddToLayout( ctrl_box, self._num, CC.FLAGS_CENTER_PERPENDICULAR )
|
|
QP.AddToLayout( ctrl_box, self._account_types, CC.FLAGS_CENTER_PERPENDICULAR )
|
|
QP.AddToLayout( ctrl_box, ClientGUICommon.BetterStaticText(self,'accounts, to expire in'), CC.FLAGS_CENTER_PERPENDICULAR )
|
|
QP.AddToLayout( ctrl_box, self._lifetime, CC.FLAGS_CENTER_PERPENDICULAR )
|
|
|
|
b_box = QP.HBoxLayout()
|
|
QP.AddToLayout( b_box, self._ok, CC.FLAGS_CENTER_PERPENDICULAR )
|
|
QP.AddToLayout( b_box, self._cancel, CC.FLAGS_CENTER_PERPENDICULAR )
|
|
|
|
vbox = QP.VBoxLayout()
|
|
|
|
QP.AddToLayout( vbox, ctrl_box, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
QP.AddToLayout( vbox, b_box, CC.FLAGS_ON_RIGHT )
|
|
|
|
self.setLayout( vbox )
|
|
|
|
size_hint = self.sizeHint()
|
|
|
|
QP.SetInitialSize( self, size_hint )
|
|
|
|
ClientGUIFunctions.SetFocusLater( self._ok )
|
|
|
|
|
|
def EventOK( self ):
|
|
|
|
num = self._num.value()
|
|
|
|
account_type = self._account_types.GetValue()
|
|
|
|
account_type_key = account_type.GetAccountTypeKey()
|
|
|
|
lifetime = self._lifetime.GetValue()
|
|
|
|
if lifetime is None:
|
|
|
|
expires = None
|
|
|
|
else:
|
|
|
|
expires = HydrusData.GetNow() + lifetime
|
|
|
|
|
|
service = HG.client_controller.services_manager.GetService( self._service_key )
|
|
|
|
try:
|
|
|
|
request_args = { 'num' : num, 'account_type_key' : account_type_key }
|
|
|
|
if expires is not None:
|
|
|
|
request_args[ 'expires' ] = expires
|
|
|
|
|
|
response = service.Request( HC.GET, 'registration_keys', request_args )
|
|
|
|
registration_keys = response[ 'registration_keys' ]
|
|
|
|
ClientGUIFrames.ShowKeys( 'registration', registration_keys )
|
|
|
|
finally:
|
|
|
|
self.done( QW.QDialog.Accepted )
|
|
|
|
|
|
|
|
class DialogInputLocalBooruShare( Dialog ):
|
|
|
|
def __init__( self, parent, share_key, name, text, timeout, hashes, new_share = False ):
|
|
|
|
Dialog.__init__( self, parent, 'configure local booru share' )
|
|
|
|
self._name = QW.QLineEdit( self )
|
|
|
|
self._text = QW.QPlainTextEdit( self )
|
|
self._text.setMinimumHeight( 100 )
|
|
|
|
message = 'expires in'
|
|
|
|
self._timeout_number = ClientGUICommon.NoneableSpinCtrl( self, message, none_phrase = 'no expiration', max = 1000000, multiplier = 1 )
|
|
|
|
self._timeout_multiplier = ClientGUICommon.BetterChoice( self )
|
|
self._timeout_multiplier.addItem( 'minutes', 60 )
|
|
self._timeout_multiplier.addItem( 'hours', 60 * 60 )
|
|
self._timeout_multiplier.addItem( 'days', 60 * 60 * 24 )
|
|
|
|
self._copy_internal_share_link = QW.QPushButton( 'copy internal share link', self )
|
|
self._copy_internal_share_link.clicked.connect( self.EventCopyInternalShareURL )
|
|
|
|
self._copy_external_share_link = QW.QPushButton( 'copy external share link', self )
|
|
self._copy_external_share_link.clicked.connect( self.EventCopyExternalShareURL )
|
|
|
|
self._ok = QW.QPushButton( 'ok', self )
|
|
self._ok.clicked.connect( self.accept )
|
|
self._ok.setObjectName( 'HydrusAccept' )
|
|
|
|
self._cancel = QW.QPushButton( 'cancel', self )
|
|
self._cancel.clicked.connect( self.reject )
|
|
self._cancel.setObjectName( 'HydrusCancel' )
|
|
|
|
#
|
|
|
|
self._share_key = share_key
|
|
self._name.setText( name )
|
|
self._text.setPlainText( text )
|
|
|
|
if timeout is None:
|
|
|
|
self._timeout_number.SetValue( None )
|
|
|
|
self._timeout_multiplier.SetValue( 60 )
|
|
|
|
else:
|
|
|
|
time_left = HydrusData.GetTimeDeltaUntilTime( timeout )
|
|
|
|
if time_left < 60 * 60 * 12: time_value = 60
|
|
elif time_left < 60 * 60 * 24 * 7: time_value = 60 * 60
|
|
else: time_value = 60 * 60 * 24
|
|
|
|
self._timeout_number.SetValue( time_left // time_value )
|
|
|
|
self._timeout_multiplier.SetValue( time_value )
|
|
|
|
|
|
self._hashes = hashes
|
|
|
|
self._service = HG.client_controller.services_manager.GetService( CC.LOCAL_BOORU_SERVICE_KEY )
|
|
|
|
internal_port = self._service.GetPort()
|
|
|
|
if internal_port is None:
|
|
|
|
self._copy_internal_share_link.setEnabled( False )
|
|
self._copy_external_share_link.setEnabled( False )
|
|
|
|
|
|
#
|
|
|
|
rows = []
|
|
|
|
rows.append( ( 'share name: ', self._name ) )
|
|
rows.append( ( 'share text: ', self._text ) )
|
|
|
|
gridbox = ClientGUICommon.WrapInGrid( self, rows )
|
|
|
|
timeout_box = QP.HBoxLayout()
|
|
QP.AddToLayout( timeout_box, self._timeout_number, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
QP.AddToLayout( timeout_box, self._timeout_multiplier, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
link_box = QP.HBoxLayout()
|
|
QP.AddToLayout( link_box, self._copy_internal_share_link, CC.FLAGS_CENTER_PERPENDICULAR )
|
|
QP.AddToLayout( link_box, self._copy_external_share_link, CC.FLAGS_CENTER_PERPENDICULAR )
|
|
|
|
b_box = QP.HBoxLayout()
|
|
QP.AddToLayout( b_box, self._ok, CC.FLAGS_CENTER_PERPENDICULAR )
|
|
QP.AddToLayout( b_box, self._cancel, CC.FLAGS_CENTER_PERPENDICULAR )
|
|
|
|
vbox = QP.VBoxLayout()
|
|
|
|
intro = 'Sharing ' + HydrusData.ToHumanInt( len( self._hashes ) ) + ' files.'
|
|
intro += os.linesep + 'Title and text are optional.'
|
|
|
|
if new_share: intro += os.linesep + 'The link will not work until you ok this dialog.'
|
|
|
|
QP.AddToLayout( vbox, ClientGUICommon.BetterStaticText(self,intro), CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
QP.AddToLayout( vbox, gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
QP.AddToLayout( vbox, timeout_box, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
QP.AddToLayout( vbox, link_box, CC.FLAGS_ON_RIGHT )
|
|
QP.AddToLayout( vbox, b_box, CC.FLAGS_ON_RIGHT )
|
|
|
|
self.setLayout( vbox )
|
|
|
|
size_hint = self.sizeHint()
|
|
|
|
size_hint.setWidth( max( size_hint.width(), 350 ) )
|
|
|
|
QP.SetInitialSize( self, size_hint )
|
|
|
|
ClientGUIFunctions.SetFocusLater( self._ok )
|
|
|
|
|
|
def EventCopyExternalShareURL( self ):
|
|
|
|
internal_port = self._service.GetPort()
|
|
|
|
if internal_port is None:
|
|
|
|
QW.QMessageBox.warning( self, 'Warning', 'The local booru is not currently running!' )
|
|
|
|
|
|
try:
|
|
|
|
url = self._service.GetExternalShareURL( self._share_key )
|
|
|
|
except Exception as e:
|
|
|
|
HydrusData.ShowException( e )
|
|
|
|
QW.QMessageBox.critical( self, 'Error', 'Unfortunately, could not generate an external URL: {}'.format(e) )
|
|
|
|
return
|
|
|
|
|
|
HG.client_controller.pub( 'clipboard', 'text', url )
|
|
|
|
|
|
def EventCopyInternalShareURL( self ):
|
|
|
|
self._service = HG.client_controller.services_manager.GetService( CC.LOCAL_BOORU_SERVICE_KEY )
|
|
|
|
url = self._service.GetInternalShareURL( self._share_key )
|
|
|
|
HG.client_controller.pub( 'clipboard', 'text', url )
|
|
|
|
|
|
def GetInfo( self ):
|
|
|
|
name = self._name.text()
|
|
|
|
text = self._text.toPlainText()
|
|
|
|
timeout = self._timeout_number.GetValue()
|
|
|
|
if timeout is not None: timeout = timeout * self._timeout_multiplier.GetValue() + HydrusData.GetNow()
|
|
|
|
return ( self._share_key, name, text, timeout, self._hashes )
|
|
|
|
|
|
class DialogInputNamespaceRegex( Dialog ):
|
|
|
|
def __init__( self, parent, namespace = '', regex = '' ):
|
|
|
|
Dialog.__init__( self, parent, 'configure quick namespace' )
|
|
|
|
self._namespace = QW.QLineEdit( self )
|
|
|
|
self._regex = QW.QLineEdit( self )
|
|
|
|
self._shortcuts = ClientGUICommon.RegexButton( self )
|
|
|
|
self._regex_intro_link = ClientGUICommon.BetterHyperLink( self, 'a good regex introduction', 'https://www.aivosto.com/vbtips/regex.html' )
|
|
self._regex_practise_link = ClientGUICommon.BetterHyperLink( self, 'regex practice', 'https://regexr.com/3cvmf' )
|
|
|
|
self._ok = QW.QPushButton( 'OK', self )
|
|
self._ok.clicked.connect( self.EventOK )
|
|
self._ok.setObjectName( 'HydrusAccept' )
|
|
|
|
self._cancel = QW.QPushButton( 'Cancel', self )
|
|
self._cancel.clicked.connect( self.reject )
|
|
self._cancel.setObjectName( 'HydrusCancel' )
|
|
|
|
#
|
|
|
|
self._namespace.setText( namespace )
|
|
self._regex.setText( regex )
|
|
|
|
#
|
|
|
|
control_box = QP.HBoxLayout()
|
|
|
|
QP.AddToLayout( control_box, self._namespace, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
QP.AddToLayout( control_box, ClientGUICommon.BetterStaticText(self,':'), CC.FLAGS_CENTER_PERPENDICULAR )
|
|
QP.AddToLayout( control_box, self._regex, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
b_box = QP.HBoxLayout()
|
|
QP.AddToLayout( b_box, self._ok, CC.FLAGS_CENTER_PERPENDICULAR )
|
|
QP.AddToLayout( b_box, self._cancel, CC.FLAGS_CENTER_PERPENDICULAR )
|
|
|
|
vbox = QP.VBoxLayout()
|
|
|
|
intro = r'Put the namespace (e.g. page) on the left.' + os.linesep + r'Put the regex (e.g. [1-9]+\d*(?=.{4}$)) on the right.' + os.linesep + r'All files will be tagged with "namespace:regex".'
|
|
|
|
QP.AddToLayout( vbox, ClientGUICommon.BetterStaticText(self,intro), CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
QP.AddToLayout( vbox, control_box, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
QP.AddToLayout( vbox, self._shortcuts, CC.FLAGS_ON_RIGHT )
|
|
QP.AddToLayout( vbox, self._regex_intro_link, CC.FLAGS_ON_RIGHT )
|
|
QP.AddToLayout( vbox, self._regex_practise_link, CC.FLAGS_ON_RIGHT )
|
|
QP.AddToLayout( vbox, b_box, CC.FLAGS_ON_RIGHT )
|
|
|
|
self.setLayout( vbox )
|
|
|
|
size_hint = self.sizeHint()
|
|
|
|
QP.SetInitialSize( self, size_hint )
|
|
|
|
ClientGUIFunctions.SetFocusLater( self._ok )
|
|
|
|
|
|
def EventOK( self ):
|
|
|
|
( namespace, regex ) = self.GetInfo()
|
|
|
|
if namespace == '':
|
|
|
|
QW.QMessageBox.warning( self, 'Warning', 'Please enter something for the namespace.' )
|
|
|
|
return
|
|
|
|
|
|
try:
|
|
|
|
re.compile( regex )
|
|
|
|
except Exception as e:
|
|
|
|
text = 'That regex would not compile!'
|
|
text += os.linesep * 2
|
|
text += str( e )
|
|
|
|
QW.QMessageBox.critical( self, 'Error', text )
|
|
|
|
return
|
|
|
|
|
|
self.done( QW.QDialog.Accepted )
|
|
|
|
|
|
def GetInfo( self ):
|
|
|
|
namespace = self._namespace.text()
|
|
|
|
regex = self._regex.text()
|
|
|
|
return ( namespace, regex )
|
|
|
|
|
|
class DialogInputTags( Dialog ):
|
|
|
|
def __init__( self, parent, service_key, tag_display_type, tags, message = '' ):
|
|
|
|
Dialog.__init__( self, parent, 'input tags' )
|
|
|
|
self._service_key = service_key
|
|
|
|
self._tags = ClientGUIListBoxes.ListBoxTagsStringsAddRemove( self, service_key, tag_display_type )
|
|
|
|
default_location_context = HG.client_controller.new_options.GetDefaultLocalLocationContext()
|
|
|
|
self._tag_autocomplete = ClientGUIACDropdown.AutoCompleteDropdownTagsWrite( self, self.EnterTags, default_location_context, service_key, show_paste_button = True )
|
|
|
|
self._tag_autocomplete.nullEntered.connect( self.OK )
|
|
|
|
self._ok = ClientGUICommon.BetterButton( self, 'OK', self.done, QW.QDialog.Accepted )
|
|
self._ok.setObjectName( 'HydrusAccept' )
|
|
|
|
self._cancel = QW.QPushButton( 'Cancel', self )
|
|
self._cancel.clicked.connect( self.reject )
|
|
self._cancel.setObjectName( 'HydrusCancel' )
|
|
|
|
#
|
|
|
|
self._tags.SetTags( tags )
|
|
|
|
#
|
|
|
|
b_box = QP.HBoxLayout()
|
|
|
|
QP.AddToLayout( b_box, self._ok, CC.FLAGS_CENTER_PERPENDICULAR )
|
|
QP.AddToLayout( b_box, self._cancel, CC.FLAGS_CENTER_PERPENDICULAR )
|
|
|
|
vbox = QP.VBoxLayout()
|
|
|
|
if message != '':
|
|
|
|
st = ClientGUICommon.BetterStaticText( self, message )
|
|
|
|
st.setWordWrap( True )
|
|
|
|
QP.AddToLayout( vbox, st, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
|
|
QP.AddToLayout( vbox, self._tags, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
QP.AddToLayout( vbox, self._tag_autocomplete )
|
|
QP.AddToLayout( vbox, b_box, CC.FLAGS_ON_RIGHT )
|
|
|
|
self.setLayout( vbox )
|
|
|
|
size_hint = self.sizeHint()
|
|
|
|
size_hint.setWidth( max( size_hint.width(), 300 ) )
|
|
|
|
QP.SetInitialSize( self, size_hint )
|
|
|
|
ClientGUIFunctions.SetFocusLater( self._tag_autocomplete )
|
|
|
|
|
|
def EnterTags( self, tags ):
|
|
|
|
if len( tags ) > 0:
|
|
|
|
self._tags.EnterTags( tags )
|
|
|
|
|
|
|
|
def GetTags( self ):
|
|
|
|
return self._tags.GetTags()
|
|
|
|
|
|
def OK( self ):
|
|
|
|
self.done( QW.QDialog.Accepted )
|
|
|
|
|
|
class DialogInputUPnPMapping( Dialog ):
|
|
|
|
def __init__( self, parent, external_port, protocol_type, internal_port, description, duration ):
|
|
|
|
Dialog.__init__( self, parent, 'configure upnp mapping' )
|
|
|
|
self._external_port = ClientGUICommon.BetterSpinBox( self, min=0, max=65535 )
|
|
|
|
self._protocol_type = ClientGUICommon.BetterChoice( self )
|
|
self._protocol_type.addItem( 'TCP', 'TCP' )
|
|
self._protocol_type.addItem( 'UDP', 'UDP' )
|
|
|
|
self._internal_port = ClientGUICommon.BetterSpinBox( self, min=0, max=65535 )
|
|
self._description = QW.QLineEdit( self )
|
|
self._duration = ClientGUICommon.BetterSpinBox( self, min=0, max=86400 )
|
|
|
|
self._ok = ClientGUICommon.BetterButton( self, 'OK', self.done, QW.QDialog.Accepted )
|
|
self._ok.setObjectName( 'HydrusAccept' )
|
|
|
|
self._cancel = QW.QPushButton( 'Cancel', self )
|
|
self._cancel.clicked.connect( self.reject )
|
|
self._cancel.setObjectName( 'HydrusCancel' )
|
|
|
|
#
|
|
|
|
self._external_port.setValue( external_port )
|
|
|
|
if protocol_type == 'TCP': self._protocol_type.setCurrentIndex( 0 )
|
|
elif protocol_type == 'UDP': self._protocol_type.setCurrentIndex( 1 )
|
|
|
|
self._internal_port.setValue( internal_port )
|
|
self._description.setText( description )
|
|
self._duration.setValue( duration )
|
|
|
|
#
|
|
|
|
rows = []
|
|
|
|
rows.append( ( 'external port: ', self._external_port ) )
|
|
rows.append( ( 'protocol type: ', self._protocol_type ) )
|
|
rows.append( ( 'internal port: ', self._internal_port ) )
|
|
rows.append( ( 'description: ', self._description ) )
|
|
rows.append( ( 'duration (0 = indefinite): ', self._duration ) )
|
|
|
|
gridbox = ClientGUICommon.WrapInGrid( self, rows )
|
|
|
|
b_box = QP.HBoxLayout()
|
|
QP.AddToLayout( b_box, self._ok, CC.FLAGS_CENTER_PERPENDICULAR )
|
|
QP.AddToLayout( b_box, self._cancel, CC.FLAGS_CENTER_PERPENDICULAR )
|
|
|
|
vbox = QP.VBoxLayout()
|
|
|
|
QP.AddToLayout( vbox, gridbox, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
|
|
QP.AddToLayout( vbox, b_box, CC.FLAGS_ON_RIGHT )
|
|
|
|
self.setLayout( vbox )
|
|
|
|
size_hint = self.sizeHint()
|
|
|
|
QP.SetInitialSize( self, size_hint )
|
|
|
|
ClientGUIFunctions.SetFocusLater( self._ok )
|
|
|
|
|
|
def GetInfo( self ):
|
|
|
|
external_port = self._external_port.value()
|
|
protocol_type = self._protocol_type.GetValue()
|
|
internal_port = self._internal_port.value()
|
|
description = self._description.text()
|
|
duration = self._duration.value()
|
|
|
|
return ( external_port, protocol_type, internal_port, description, duration )
|
|
|
|
|
|
class DialogSelectFromURLTree( Dialog ):
|
|
|
|
def __init__( self, parent, url_tree ):
|
|
|
|
Dialog.__init__( self, parent, 'select items' )
|
|
|
|
self._tree = QP.TreeWidgetWithInheritedCheckState( self )
|
|
|
|
self._ok = ClientGUICommon.BetterButton( self, 'OK', self.done, QW.QDialog.Accepted )
|
|
self._ok.setObjectName( 'HydrusAccept' )
|
|
|
|
self._cancel = QW.QPushButton( 'Cancel', self )
|
|
self._cancel.clicked.connect( self.reject )
|
|
self._cancel.setObjectName( 'HydrusCancel' )
|
|
|
|
#
|
|
|
|
( text_gumpf, name, size, children ) = url_tree
|
|
|
|
root_name = self._RenderItemName( name, size )
|
|
|
|
root_item = QW.QTreeWidgetItem()
|
|
root_item.setText( 0, root_name )
|
|
root_item.setCheckState( 0, QC.Qt.Checked )
|
|
self._tree.addTopLevelItem( root_item )
|
|
|
|
self._AddDirectory( root_item, children )
|
|
|
|
root_item.setExpanded( True )
|
|
|
|
#
|
|
|
|
button_hbox = QP.HBoxLayout()
|
|
|
|
QP.AddToLayout( button_hbox, self._ok, CC.FLAGS_CENTER_PERPENDICULAR )
|
|
QP.AddToLayout( button_hbox, self._cancel, CC.FLAGS_CENTER_PERPENDICULAR )
|
|
|
|
vbox = QP.VBoxLayout()
|
|
|
|
QP.AddToLayout( vbox, self._tree, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
QP.AddToLayout( vbox, button_hbox, CC.FLAGS_ON_RIGHT )
|
|
|
|
self.setLayout( vbox )
|
|
|
|
size_hint = self.sizeHint()
|
|
|
|
size_hint.setWidth( max( size_hint.width(), 640 ) )
|
|
size_hint.setHeight( max( size_hint.height(), 640 ) )
|
|
|
|
QP.SetInitialSize( self, size_hint )
|
|
|
|
|
|
def _AddDirectory( self, root, children ):
|
|
|
|
for ( child_type, name, size, data ) in children:
|
|
|
|
item_name = self._RenderItemName( name, size )
|
|
|
|
if child_type == 'file':
|
|
|
|
item = QW.QTreeWidgetItem()
|
|
item.setText( 0, item_name )
|
|
item.setCheckState( 0, root.checkState( 0 ) )
|
|
item.setData( 0, QC.Qt.UserRole, data )
|
|
root.addChild( item )
|
|
|
|
else:
|
|
|
|
subroot = QW.QTreeWidgetItem()
|
|
subroot.setText( 0, item_name )
|
|
subroot.setCheckState( 0, root.checkState( 0 ) )
|
|
root.addChild( subroot )
|
|
|
|
self._AddDirectory( subroot, data )
|
|
|
|
|
|
|
|
|
|
def _GetSelectedChildrenData( self, parent_item ):
|
|
|
|
result = []
|
|
|
|
for i in range( parent_item.childCount() ):
|
|
|
|
child_item = parent_item.child( i )
|
|
|
|
if child_item.checkState( 0 ) == QC.Qt.Checked:
|
|
|
|
data = child_item.data( 0, QC.Qt.UserRole )
|
|
|
|
if data is not None:
|
|
|
|
result.append( data )
|
|
|
|
|
|
|
|
result.extend( self._GetSelectedChildrenData( child_item ) )
|
|
|
|
|
|
return result
|
|
|
|
|
|
def _RenderItemName( self, name, size ):
|
|
|
|
return name + ' - ' + HydrusData.ToHumanBytes( size )
|
|
|
|
|
|
def GetURLs( self ):
|
|
|
|
root_item = self._tree.topLevelItem( 0 )
|
|
|
|
urls = self._GetSelectedChildrenData( root_item )
|
|
|
|
return urls
|
|
|
|
|
|
class DialogTextEntry( Dialog ):
|
|
|
|
def __init__( self, parent, message, default = '', placeholder = None, allow_blank = False, suggestions = None, max_chars = None, password_entry = False, min_char_width = 72 ):
|
|
|
|
if suggestions is None:
|
|
|
|
suggestions = []
|
|
|
|
|
|
Dialog.__init__( self, parent, 'enter text', position = 'center' )
|
|
|
|
self._chosen_suggestion = None
|
|
self._allow_blank = allow_blank
|
|
self._max_chars = max_chars
|
|
|
|
button_choices = []
|
|
|
|
for text in suggestions:
|
|
|
|
button_choices.append( ClientGUICommon.BetterButton( self, text, self.ButtonChoice, text ) )
|
|
|
|
|
|
self._text = QW.QLineEdit( self )
|
|
self._text.textChanged.connect( self.EventText )
|
|
self._text.installEventFilter( ClientGUICommon.TextCatchEnterEventFilter( self._text, self.EnterText ) )
|
|
|
|
width = ClientGUIFunctions.ConvertTextToPixelWidth( self._text, min_char_width )
|
|
|
|
self._text.setMinimumWidth( width )
|
|
|
|
if password_entry:
|
|
|
|
self._text.setEchoMode( QW.QLineEdit.Password )
|
|
|
|
|
|
if self._max_chars is not None:
|
|
|
|
self._text.setMaxLength( self._max_chars )
|
|
|
|
|
|
self._ok = ClientGUICommon.BetterButton( self, 'ok', self.done, QW.QDialog.Accepted )
|
|
self._ok.setObjectName( 'HydrusAccept' )
|
|
|
|
self._cancel = QW.QPushButton( 'cancel', self )
|
|
self._cancel.clicked.connect( self.reject )
|
|
self._cancel.setObjectName( 'HydrusCancel' )
|
|
|
|
#
|
|
|
|
self._text.setText( default )
|
|
|
|
if placeholder is not None:
|
|
|
|
self._text.setPlaceholderText( placeholder )
|
|
|
|
|
|
if len( default ) > 0:
|
|
|
|
self._text.setSelection( 0, len( default ) )
|
|
|
|
|
|
self._CheckText()
|
|
|
|
#
|
|
|
|
hbox = QP.HBoxLayout()
|
|
|
|
QP.AddToLayout( hbox, self._ok, CC.FLAGS_CENTER_PERPENDICULAR )
|
|
QP.AddToLayout( hbox, self._cancel, CC.FLAGS_CENTER_PERPENDICULAR )
|
|
|
|
st_message = ClientGUICommon.BetterStaticText( self, message )
|
|
st_message.setWordWrap( True )
|
|
|
|
vbox = QP.VBoxLayout()
|
|
|
|
QP.AddToLayout( vbox, st_message, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
for button in button_choices:
|
|
|
|
QP.AddToLayout( vbox, button, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
|
|
QP.AddToLayout( vbox, self._text, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
QP.AddToLayout( vbox, hbox, CC.FLAGS_ON_RIGHT )
|
|
|
|
self.setLayout( vbox )
|
|
|
|
size_hint = self.sizeHint()
|
|
|
|
size_hint.setWidth( max( size_hint.width(), 250 ) )
|
|
|
|
QP.SetInitialSize( self, size_hint )
|
|
|
|
self._text.setFocus( QC.Qt.OtherFocusReason )
|
|
|
|
|
|
def _CheckText( self ):
|
|
|
|
if not self._allow_blank:
|
|
|
|
if self._text.text() == '':
|
|
|
|
self._ok.setEnabled( False )
|
|
|
|
else:
|
|
|
|
self._ok.setEnabled( True )
|
|
|
|
|
|
|
|
|
|
def ButtonChoice( self, text ):
|
|
|
|
self._chosen_suggestion = text
|
|
|
|
self.done( QW.QDialog.Accepted )
|
|
|
|
|
|
def EventText( self, text ):
|
|
|
|
QP.CallAfter( self._CheckText )
|
|
|
|
|
|
def EnterText( self ):
|
|
|
|
if self._ok.isEnabled():
|
|
|
|
self.done( QW.QDialog.Accepted )
|
|
|
|
|
|
|
|
def GetValue( self ):
|
|
|
|
if self._chosen_suggestion is None:
|
|
|
|
return self._text.text()
|
|
|
|
else:
|
|
|
|
return self._chosen_suggestion
|
|
|
|
|
|
|