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