hydrus/hydrus/client/gui/ClientGUIDragDrop.py

339 lines
9.5 KiB
Python
Raw Normal View History

2018-03-28 21:55:58 +00:00
import os
2020-04-22 21:00:35 +00:00
2019-11-14 03:56:30 +00:00
from qtpy import QtCore as QC
from qtpy import QtGui as QG
from qtpy import QtWidgets as QW
2020-04-22 21:00:35 +00:00
2020-12-09 22:18:48 +00:00
from hydrus.core import HydrusConstants as HC
2020-04-22 21:00:35 +00:00
from hydrus.core import HydrusGlobals as HG
from hydrus.core import HydrusPaths
2021-07-14 20:42:19 +00:00
from hydrus.core import HydrusText
2020-07-29 20:52:44 +00:00
2020-12-09 22:18:48 +00:00
from hydrus.client import ClientExporting
2020-04-22 21:00:35 +00:00
from hydrus.client.gui import ClientGUIFunctions
from hydrus.client.gui import QtPorting as QP
2015-07-29 19:11:35 +00:00
2019-11-20 23:10:46 +00:00
# we do this because some programs like discord will disallow exports with additional custom mimetypes (like 'application/hydrus-files')
# as this is only ever an internal transfer, and as the python mimedata object is preserved through the dnd, we can just tack this info on with a subclass and python variables
class QMimeDataHydrusFiles( QC.QMimeData ):
def __init__( self ):
QC.QMimeData.__init__( self )
self._hydrus_files = None
def hydrusFiles( self ):
return self._hydrus_files
def setHydrusFiles( self, page_key, hashes ):
self._hydrus_files = ( page_key, hashes )
2019-02-13 22:26:43 +00:00
def DoFileExportDragDrop( window, page_key, media, alt_down ):
2018-03-28 21:55:58 +00:00
2019-11-14 03:56:30 +00:00
drop_source = QG.QDrag( window )
2018-03-28 21:55:58 +00:00
2019-11-20 23:10:46 +00:00
data_object = QMimeDataHydrusFiles()
2018-03-28 21:55:58 +00:00
#
2019-11-14 03:56:30 +00:00
new_options = HG.client_controller.new_options
2018-03-28 21:55:58 +00:00
2019-11-14 03:56:30 +00:00
do_secret_discord_dnd_fix = new_options.GetBoolean( 'secret_discord_dnd_fix' ) and alt_down
2018-03-28 21:55:58 +00:00
#
client_files_manager = HG.client_controller.client_files_manager
original_paths = []
2020-12-09 22:18:48 +00:00
media_and_original_paths = []
2018-03-28 21:55:58 +00:00
2019-02-13 22:26:43 +00:00
total_size = 0
2018-03-28 21:55:58 +00:00
for m in media:
hash = m.GetHash()
mime = m.GetMime()
2019-02-13 22:26:43 +00:00
total_size += m.GetSize()
2018-09-12 21:36:26 +00:00
original_path = client_files_manager.GetFilePath( hash, mime, check_file_exists = False )
2018-03-28 21:55:58 +00:00
original_paths.append( original_path )
2020-12-09 22:18:48 +00:00
media_and_original_paths.append( ( m, original_path ) )
2018-03-28 21:55:58 +00:00
#
2019-02-13 22:26:43 +00:00
discord_dnd_fix_possible = new_options.GetBoolean( 'discord_dnd_fix' ) and len( original_paths ) <= 50 and total_size < 200 * 1048576
2018-03-28 21:55:58 +00:00
temp_dir = HG.client_controller.temp_dir
2019-11-14 03:56:30 +00:00
if do_secret_discord_dnd_fix:
2019-02-13 22:26:43 +00:00
dnd_paths = original_paths
2019-11-14 03:56:30 +00:00
flags = QC.Qt.MoveAction
2019-02-13 22:26:43 +00:00
elif discord_dnd_fix_possible and os.path.exists( temp_dir ):
2018-03-28 21:55:58 +00:00
2020-12-09 22:18:48 +00:00
fallback_filename_terms = ClientExporting.ParseExportPhrase( '{hash}' )
try:
filename_pattern = new_options.GetString( 'discord_dnd_filename_pattern' )
filename_terms = ClientExporting.ParseExportPhrase( filename_pattern )
if len( filename_terms ) == 0:
raise Exception()
except:
filename_terms = fallback_filename_terms
2018-03-28 21:55:58 +00:00
dnd_paths = []
2020-12-09 22:18:48 +00:00
for ( m, original_path ) in media_and_original_paths:
2018-03-28 21:55:58 +00:00
2020-12-09 22:18:48 +00:00
filename = ClientExporting.GenerateExportFilename( temp_dir, m, filename_terms )
if filename == HC.mime_ext_lookup[ m.GetMime() ]:
filename = ClientExporting.GenerateExportFilename( temp_dir, m, fallback_filename_terms )
2018-03-28 21:55:58 +00:00
dnd_path = os.path.join( temp_dir, filename )
if not os.path.exists( dnd_path ):
HydrusPaths.MirrorFile( original_path, dnd_path )
dnd_paths.append( dnd_path )
2019-11-14 03:56:30 +00:00
flags = QC.Qt.MoveAction | QC.Qt.CopyAction
2018-03-28 21:55:58 +00:00
else:
dnd_paths = original_paths
2019-11-14 03:56:30 +00:00
flags = QC.Qt.CopyAction
2018-03-28 21:55:58 +00:00
2019-11-14 03:56:30 +00:00
uri_list = []
2018-03-28 21:55:58 +00:00
for path in dnd_paths:
2019-11-14 03:56:30 +00:00
uri_list.append( QC.QUrl.fromLocalFile( path ) )
2018-03-28 21:55:58 +00:00
2019-11-14 03:56:30 +00:00
data_object.setUrls( uri_list )
2018-03-28 21:55:58 +00:00
#
2019-11-14 03:56:30 +00:00
hashes = [ m.GetHash() for m in media ]
2019-12-05 05:29:32 +00:00
data_object.setHydrusFiles( page_key, hashes )
2019-11-20 23:10:46 +00:00
# old way of doing this that makes some external programs (discord) reject it
'''
2019-11-14 03:56:30 +00:00
if page_key is None:
encoded_page_key = None
else:
encoded_page_key = page_key.hex()
data_obj = ( encoded_page_key, [ hash.hex() for hash in hashes ] )
2018-03-28 21:55:58 +00:00
2019-11-14 03:56:30 +00:00
data_str = json.dumps( data_obj )
data_bytes = bytes( data_str, 'utf-8' )
data_object.setData( 'application/hydrus-media', data_bytes )
2019-11-20 23:10:46 +00:00
'''
2019-11-14 03:56:30 +00:00
#
drop_source.setMimeData( data_object )
result = drop_source.exec_( flags, QC.Qt.CopyAction )
2018-03-28 21:55:58 +00:00
return result
2019-11-14 03:56:30 +00:00
class FileDropTarget( QC.QObject ):
2015-07-29 19:11:35 +00:00
2019-11-14 03:56:30 +00:00
def __init__( self, parent, filenames_callable = None, url_callable = None, media_callable = None ):
2015-07-29 19:11:35 +00:00
2019-11-14 03:56:30 +00:00
QC.QObject.__init__( self, parent )
2015-07-29 19:11:35 +00:00
2017-11-22 21:03:07 +00:00
self._parent = parent
2019-11-14 03:56:30 +00:00
if parent:
parent.setAcceptDrops( True )
2017-05-17 21:53:02 +00:00
self._filenames_callable = filenames_callable
self._url_callable = url_callable
2017-12-20 22:55:48 +00:00
self._media_callable = media_callable
2015-07-29 19:11:35 +00:00
2019-11-14 03:56:30 +00:00
def eventFilter( self, object, event ):
2015-07-29 19:11:35 +00:00
2019-11-14 03:56:30 +00:00
if event.type() == QC.QEvent.Drop:
2015-07-29 19:11:35 +00:00
2019-11-14 03:56:30 +00:00
if self.OnDrop( event.pos().x(), event.pos().y() ):
2019-12-05 05:29:32 +00:00
2019-11-14 03:56:30 +00:00
event.setDropAction( self.OnData( event.mimeData(), event.proposedAction() ) )
2020-02-12 22:50:37 +00:00
2019-11-14 03:56:30 +00:00
event.accept()
2015-07-29 19:11:35 +00:00
2019-11-14 03:56:30 +00:00
elif event.type() == QC.QEvent.DragEnter:
2017-05-17 21:53:02 +00:00
2019-11-14 03:56:30 +00:00
event.accept()
return False
def OnData( self, mime_data, result ):
2019-12-05 05:29:32 +00:00
media_dnd = isinstance( mime_data, QMimeDataHydrusFiles )
urls_dnd = mime_data.hasUrls()
text_dnd = mime_data.hasText()
if media_dnd and self._media_callable is not None:
result = mime_data.hydrusFiles()
2019-11-20 23:10:46 +00:00
2019-12-05 05:29:32 +00:00
if result is not None:
2019-11-20 23:10:46 +00:00
2019-12-05 05:29:32 +00:00
( page_key, hashes ) = result
2019-11-20 23:10:46 +00:00
2019-12-05 05:29:32 +00:00
if page_key is not None:
2019-11-20 23:10:46 +00:00
2019-12-05 05:29:32 +00:00
QP.CallAfter( self._media_callable, page_key, hashes ) # callafter so we can terminate dnd event now
2019-11-20 23:10:46 +00:00
2019-12-05 05:29:32 +00:00
result = QC.Qt.MoveAction
# old way of doing it that messed up discord et al
'''
elif mime_data.formats().count( 'application/hydrus-media' ) and self._media_callable is not None:
mview = mime_data.data( 'application/hydrus-media' )
2019-11-14 03:56:30 +00:00
2019-12-05 05:29:32 +00:00
data_bytes = mview.data()
2019-11-14 03:56:30 +00:00
2019-12-05 05:29:32 +00:00
data_str = str( data_bytes, 'utf-8' )
2019-11-14 03:56:30 +00:00
2019-12-05 05:29:32 +00:00
(encoded_page_key, encoded_hashes) = json.loads( data_str )
2019-11-14 03:56:30 +00:00
2019-12-05 05:29:32 +00:00
if encoded_page_key is not None:
2019-11-14 03:56:30 +00:00
2019-12-05 05:29:32 +00:00
page_key = bytes.fromhex( encoded_page_key )
hashes = [ bytes.fromhex( encoded_hash ) for encoded_hash in encoded_hashes ]
QP.CallAfter( self._media_callable, page_key, hashes ) # callafter so we can terminate dnd event now
2019-11-14 03:56:30 +00:00
2019-12-05 05:29:32 +00:00
result = QC.Qt.MoveAction
'''
2021-07-14 20:42:19 +00:00
elif urls_dnd or text_dnd:
2019-12-05 05:29:32 +00:00
paths = []
urls = []
2021-07-14 20:42:19 +00:00
if urls_dnd:
2015-07-29 19:11:35 +00:00
2021-07-14 20:42:19 +00:00
dnd_items = mime_data.urls()
for dnd_item in dnd_items:
2018-01-03 22:37:30 +00:00
2021-07-14 20:42:19 +00:00
if dnd_item.isLocalFile():
paths.append( os.path.normpath( dnd_item.toLocalFile() ) )
else:
urls.append( dnd_item.url() )
2017-08-23 21:34:25 +00:00
2021-07-14 20:42:19 +00:00
else:
text = mime_data.text()
text_lines = HydrusText.DeserialiseNewlinedTexts( text )
for text_line in text_lines:
2017-09-27 21:52:54 +00:00
2021-07-14 20:42:19 +00:00
if text_line.startswith( 'http' ):
urls.append( text_line )
# ignore 'paths'
2017-08-23 21:34:25 +00:00
2019-12-05 05:29:32 +00:00
2021-07-14 20:42:19 +00:00
if self._filenames_callable is not None:
2019-11-14 03:56:30 +00:00
2021-07-14 20:42:19 +00:00
if len( paths ) > 0:
QP.CallAfter( self._filenames_callable, paths ) # callafter to terminate dnd event now
2019-11-14 03:56:30 +00:00
2019-12-05 05:29:32 +00:00
2021-07-14 20:42:19 +00:00
if self._url_callable is not None:
2019-11-14 03:56:30 +00:00
2021-07-14 20:42:19 +00:00
if len( urls ) > 0:
2019-12-05 05:29:32 +00:00
2021-07-14 20:42:19 +00:00
for url in urls:
QP.CallAfter( self._url_callable, url ) # callafter to terminate dnd event now
2019-12-05 05:29:32 +00:00
2019-11-14 03:56:30 +00:00
2015-07-29 19:11:35 +00:00
2019-12-05 05:29:32 +00:00
result = QC.Qt.IgnoreAction
else:
result = QC.Qt.IgnoreAction
2015-07-29 19:11:35 +00:00
return result
2017-11-22 21:03:07 +00:00
def OnDrop( self, x, y ):
2020-02-26 22:28:52 +00:00
screen_position = ClientGUIFunctions.ClientToScreen( self._parent, QC.QPoint( x, y ) )
2017-11-29 21:48:23 +00:00
2019-11-20 23:10:46 +00:00
drop_tlw = QW.QApplication.topLevelAt( screen_position )
my_tlw = self._parent.window()
2017-11-22 21:03:07 +00:00
2019-11-20 23:10:46 +00:00
if drop_tlw == my_tlw:
2017-11-22 21:03:07 +00:00
return True
else:
return False
2017-09-27 21:52:54 +00:00
# setting OnDragOver to return copy gives Linux trouble with page tab drops with shift held down