2020-04-22 21:00:35 +00:00
import typing
2019-11-14 03:56:30 +00:00
from qtpy import QtCore as QC
from qtpy import QtWidgets as QW
from qtpy import QtGui as QG
2020-04-22 21:00:35 +00:00
from hydrus . core import HydrusConstants as HC
from hydrus . core import HydrusData
from hydrus . core import HydrusGlobals as HG
from hydrus . core import HydrusSerialisable
from hydrus . client import ClientConstants as CC
from hydrus . client import ClientData
from hydrus . client . gui import ClientGUICore as CGC
from hydrus . client . gui import ClientGUIFunctions
2019-12-11 23:18:37 +00:00
2020-02-19 21:48:36 +00:00
SHORTCUT_TYPE_KEYBOARD_CHARACTER = 0
SHORTCUT_TYPE_MOUSE = 1
SHORTCUT_TYPE_KEYBOARD_SPECIAL = 2
SHORTCUT_TYPE_NOT_ALLOWED = 3
SHORTCUT_PRESS_TYPE_PRESS = 0
SHORTCUT_PRESS_TYPE_RELEASE = 1
SHORTCUT_PRESS_TYPE_DOUBLE_CLICK = 2
SHORTCUT_PRESS_TYPE_DRAG = 3
shortcut_press_type_str_lookup = { }
shortcut_press_type_str_lookup [ SHORTCUT_PRESS_TYPE_PRESS ] = ' press '
shortcut_press_type_str_lookup [ SHORTCUT_PRESS_TYPE_RELEASE ] = ' release '
shortcut_press_type_str_lookup [ SHORTCUT_PRESS_TYPE_DOUBLE_CLICK ] = ' double '
shortcut_press_type_str_lookup [ SHORTCUT_PRESS_TYPE_DRAG ] = ' drag '
SHORTCUT_MODIFIER_CTRL = 0
SHORTCUT_MODIFIER_ALT = 1
SHORTCUT_MODIFIER_SHIFT = 2
SHORTCUT_MODIFIER_KEYPAD = 3
SHORTCUT_MODIFIER_GROUP_SWITCH = 4
SHORTCUT_KEY_SPECIAL_SPACE = 0
SHORTCUT_KEY_SPECIAL_BACKSPACE = 1
SHORTCUT_KEY_SPECIAL_TAB = 2
SHORTCUT_KEY_SPECIAL_RETURN = 3
SHORTCUT_KEY_SPECIAL_ENTER = 4
SHORTCUT_KEY_SPECIAL_PAUSE = 5
SHORTCUT_KEY_SPECIAL_ESCAPE = 6
SHORTCUT_KEY_SPECIAL_INSERT = 7
SHORTCUT_KEY_SPECIAL_DELETE = 8
SHORTCUT_KEY_SPECIAL_UP = 9
SHORTCUT_KEY_SPECIAL_DOWN = 10
SHORTCUT_KEY_SPECIAL_LEFT = 11
SHORTCUT_KEY_SPECIAL_RIGHT = 12
SHORTCUT_KEY_SPECIAL_HOME = 13
SHORTCUT_KEY_SPECIAL_END = 14
SHORTCUT_KEY_SPECIAL_PAGE_UP = 15
SHORTCUT_KEY_SPECIAL_PAGE_DOWN = 16
SHORTCUT_KEY_SPECIAL_F1 = 17
SHORTCUT_KEY_SPECIAL_F2 = 18
SHORTCUT_KEY_SPECIAL_F3 = 19
SHORTCUT_KEY_SPECIAL_F4 = 20
SHORTCUT_KEY_SPECIAL_F5 = 21
SHORTCUT_KEY_SPECIAL_F6 = 22
SHORTCUT_KEY_SPECIAL_F7 = 23
SHORTCUT_KEY_SPECIAL_F8 = 24
SHORTCUT_KEY_SPECIAL_F9 = 25
SHORTCUT_KEY_SPECIAL_F10 = 26
SHORTCUT_KEY_SPECIAL_F11 = 27
SHORTCUT_KEY_SPECIAL_F12 = 28
if HC . PLATFORM_MACOS :
2020-05-27 21:27:52 +00:00
DELETE_KEYS_QT = ( QC . Qt . Key_Backspace , QC . Qt . Key_Delete )
DELETE_KEYS_HYDRUS = ( SHORTCUT_KEY_SPECIAL_BACKSPACE , SHORTCUT_KEY_SPECIAL_DELETE )
2020-02-19 21:48:36 +00:00
else :
2020-05-27 21:27:52 +00:00
DELETE_KEYS_QT = ( QC . Qt . Key_Delete , )
DELETE_KEYS_HYDRUS = ( SHORTCUT_KEY_SPECIAL_DELETE , )
2020-02-19 21:48:36 +00:00
special_key_shortcut_enum_lookup = { }
special_key_shortcut_enum_lookup [ QC . Qt . Key_Space ] = SHORTCUT_KEY_SPECIAL_SPACE
special_key_shortcut_enum_lookup [ QC . Qt . Key_Backspace ] = SHORTCUT_KEY_SPECIAL_BACKSPACE
special_key_shortcut_enum_lookup [ QC . Qt . Key_Tab ] = SHORTCUT_KEY_SPECIAL_TAB
special_key_shortcut_enum_lookup [ QC . Qt . Key_Return ] = SHORTCUT_KEY_SPECIAL_RETURN
special_key_shortcut_enum_lookup [ QC . Qt . Key_Enter ] = SHORTCUT_KEY_SPECIAL_ENTER
special_key_shortcut_enum_lookup [ QC . Qt . Key_Pause ] = SHORTCUT_KEY_SPECIAL_PAUSE
special_key_shortcut_enum_lookup [ QC . Qt . Key_Escape ] = SHORTCUT_KEY_SPECIAL_ESCAPE
special_key_shortcut_enum_lookup [ QC . Qt . Key_Insert ] = SHORTCUT_KEY_SPECIAL_INSERT
special_key_shortcut_enum_lookup [ QC . Qt . Key_Delete ] = SHORTCUT_KEY_SPECIAL_DELETE
special_key_shortcut_enum_lookup [ QC . Qt . Key_Up ] = SHORTCUT_KEY_SPECIAL_UP
special_key_shortcut_enum_lookup [ QC . Qt . Key_Down ] = SHORTCUT_KEY_SPECIAL_DOWN
special_key_shortcut_enum_lookup [ QC . Qt . Key_Left ] = SHORTCUT_KEY_SPECIAL_LEFT
special_key_shortcut_enum_lookup [ QC . Qt . Key_Right ] = SHORTCUT_KEY_SPECIAL_RIGHT
special_key_shortcut_enum_lookup [ QC . Qt . Key_Home ] = SHORTCUT_KEY_SPECIAL_HOME
special_key_shortcut_enum_lookup [ QC . Qt . Key_End ] = SHORTCUT_KEY_SPECIAL_END
special_key_shortcut_enum_lookup [ QC . Qt . Key_PageUp ] = SHORTCUT_KEY_SPECIAL_PAGE_UP
special_key_shortcut_enum_lookup [ QC . Qt . Key_PageDown ] = SHORTCUT_KEY_SPECIAL_PAGE_DOWN
special_key_shortcut_enum_lookup [ QC . Qt . Key_F1 ] = SHORTCUT_KEY_SPECIAL_F1
special_key_shortcut_enum_lookup [ QC . Qt . Key_F2 ] = SHORTCUT_KEY_SPECIAL_F2
special_key_shortcut_enum_lookup [ QC . Qt . Key_F3 ] = SHORTCUT_KEY_SPECIAL_F3
special_key_shortcut_enum_lookup [ QC . Qt . Key_F4 ] = SHORTCUT_KEY_SPECIAL_F4
special_key_shortcut_enum_lookup [ QC . Qt . Key_F5 ] = SHORTCUT_KEY_SPECIAL_F5
special_key_shortcut_enum_lookup [ QC . Qt . Key_F6 ] = SHORTCUT_KEY_SPECIAL_F6
special_key_shortcut_enum_lookup [ QC . Qt . Key_F7 ] = SHORTCUT_KEY_SPECIAL_F7
special_key_shortcut_enum_lookup [ QC . Qt . Key_F8 ] = SHORTCUT_KEY_SPECIAL_F8
special_key_shortcut_enum_lookup [ QC . Qt . Key_F9 ] = SHORTCUT_KEY_SPECIAL_F9
special_key_shortcut_enum_lookup [ QC . Qt . Key_F10 ] = SHORTCUT_KEY_SPECIAL_F10
special_key_shortcut_enum_lookup [ QC . Qt . Key_F11 ] = SHORTCUT_KEY_SPECIAL_F11
special_key_shortcut_enum_lookup [ QC . Qt . Key_F12 ] = SHORTCUT_KEY_SPECIAL_F12
special_key_shortcut_str_lookup = { }
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_SPACE ] = ' space '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_BACKSPACE ] = ' backspace '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_TAB ] = ' tab '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_RETURN ] = ' return '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_ENTER ] = ' enter '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_PAUSE ] = ' pause '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_ESCAPE ] = ' escape '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_INSERT ] = ' insert '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_DELETE ] = ' delete '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_UP ] = ' up '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_DOWN ] = ' down '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_LEFT ] = ' left '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_RIGHT ] = ' right '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_HOME ] = ' home '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_END ] = ' end '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_PAGE_DOWN ] = ' page down '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_PAGE_UP ] = ' page up '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_F1 ] = ' f1 '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_F2 ] = ' f2 '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_F3 ] = ' f3 '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_F4 ] = ' f4 '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_F5 ] = ' f5 '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_F6 ] = ' f6 '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_F7 ] = ' f7 '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_F8 ] = ' f8 '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_F9 ] = ' f9 '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_F10 ] = ' f10 '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_F11 ] = ' f11 '
special_key_shortcut_str_lookup [ SHORTCUT_KEY_SPECIAL_F12 ] = ' f12 '
SHORTCUT_MOUSE_LEFT = 0
SHORTCUT_MOUSE_RIGHT = 1
SHORTCUT_MOUSE_MIDDLE = 2
SHORTCUT_MOUSE_SCROLL_UP = 3
SHORTCUT_MOUSE_SCROLL_DOWN = 4
SHORTCUT_MOUSE_SCROLL_LEFT = 5
SHORTCUT_MOUSE_SCROLL_RIGHT = 6
2020-04-29 21:44:12 +00:00
SHORTCUT_MOUSE_BACK = 7
SHORTCUT_MOUSE_FORWARD = 8
SHORTCUT_MOUSE_CLICKS = { SHORTCUT_MOUSE_LEFT , SHORTCUT_MOUSE_MIDDLE , SHORTCUT_MOUSE_RIGHT , SHORTCUT_MOUSE_BACK , SHORTCUT_MOUSE_FORWARD }
qt_mouse_buttons_to_hydrus_mouse_buttons = { }
qt_mouse_buttons_to_hydrus_mouse_buttons [ QC . Qt . LeftButton ] = SHORTCUT_MOUSE_LEFT
qt_mouse_buttons_to_hydrus_mouse_buttons [ QC . Qt . MiddleButton ] = SHORTCUT_MOUSE_MIDDLE
qt_mouse_buttons_to_hydrus_mouse_buttons [ QC . Qt . RightButton ] = SHORTCUT_MOUSE_RIGHT
qt_mouse_buttons_to_hydrus_mouse_buttons [ QC . Qt . BackButton ] = SHORTCUT_MOUSE_BACK
qt_mouse_buttons_to_hydrus_mouse_buttons [ QC . Qt . ForwardButton ] = SHORTCUT_MOUSE_FORWARD
2020-02-19 21:48:36 +00:00
shortcut_mouse_string_lookup = { }
shortcut_mouse_string_lookup [ SHORTCUT_MOUSE_LEFT ] = ' left-click '
shortcut_mouse_string_lookup [ SHORTCUT_MOUSE_RIGHT ] = ' right-click '
shortcut_mouse_string_lookup [ SHORTCUT_MOUSE_MIDDLE ] = ' middle-click '
2020-04-29 21:44:12 +00:00
shortcut_mouse_string_lookup [ SHORTCUT_MOUSE_BACK ] = ' back '
shortcut_mouse_string_lookup [ SHORTCUT_MOUSE_FORWARD ] = ' forward '
2020-02-19 21:48:36 +00:00
shortcut_mouse_string_lookup [ SHORTCUT_MOUSE_SCROLL_UP ] = ' scroll up '
shortcut_mouse_string_lookup [ SHORTCUT_MOUSE_SCROLL_DOWN ] = ' scroll down '
shortcut_mouse_string_lookup [ SHORTCUT_MOUSE_SCROLL_LEFT ] = ' scroll left '
shortcut_mouse_string_lookup [ SHORTCUT_MOUSE_SCROLL_RIGHT ] = ' scroll right '
2020-05-06 21:31:41 +00:00
shortcut_names_to_pretty_names = { }
shortcut_names_to_pretty_names [ ' global ' ] = ' global '
shortcut_names_to_pretty_names [ ' main_gui ' ] = ' the main window '
shortcut_names_to_pretty_names [ ' media ' ] = ' media actions, either thumbnails or the viewer '
shortcut_names_to_pretty_names [ ' media_viewer ' ] = ' media viewers - all (zoom and pan) '
shortcut_names_to_pretty_names [ ' media_viewer_browser ' ] = ' media viewer - \' normal \' browser '
shortcut_names_to_pretty_names [ ' archive_delete_filter ' ] = ' media viewer - archive/delete filter '
shortcut_names_to_pretty_names [ ' duplicate_filter ' ] = ' media viewer - duplicate filter '
shortcut_names_to_pretty_names [ ' preview_media_window ' ] = ' media viewer - the preview window '
shortcut_names_to_pretty_names [ ' media_viewer_media_window ' ] = ' the actual media in a media viewer '
shortcut_names_to_sort_order = { }
shortcut_names_to_sort_order [ ' global ' ] = 0
shortcut_names_to_sort_order [ ' main_gui ' ] = 1
shortcut_names_to_sort_order [ ' media ' ] = 2
shortcut_names_to_sort_order [ ' media_viewer ' ] = 3
shortcut_names_to_sort_order [ ' media_viewer_browser ' ] = 4
shortcut_names_to_sort_order [ ' archive_delete_filter ' ] = 5
shortcut_names_to_sort_order [ ' duplicate_filter ' ] = 6
shortcut_names_to_sort_order [ ' preview_media_window ' ] = 7
shortcut_names_to_sort_order [ ' media_viewer_media_window ' ] = 8
2020-02-19 21:48:36 +00:00
shortcut_names_to_descriptions = { }
shortcut_names_to_descriptions [ ' global ' ] = ' Actions for the whole program. Should work in the main gui or a media viewer. '
shortcut_names_to_descriptions [ ' archive_delete_filter ' ] = ' Navigation actions for the media viewer during an archive/delete filter. Mouse shortcuts should work. '
shortcut_names_to_descriptions [ ' duplicate_filter ' ] = ' Navigation actions for the media viewer during a duplicate filter. Mouse shortcuts should work. '
shortcut_names_to_descriptions [ ' media ' ] = ' Actions to alter metadata for media in the media viewer or the thumbnail grid. '
shortcut_names_to_descriptions [ ' main_gui ' ] = ' Actions to control pages in the main window of the program. '
shortcut_names_to_descriptions [ ' media_viewer_browser ' ] = ' Navigation actions for the regular browsable media viewer. '
shortcut_names_to_descriptions [ ' media_viewer ' ] = ' Zoom and pan and player actions for any media viewer. '
shortcut_names_to_descriptions [ ' media_viewer_media_window ' ] = ' Actions for any video or audio player in a media viewer window. '
shortcut_names_to_descriptions [ ' preview_media_window ' ] = ' Actions for any video or audio player in a preview window. '
# shortcut commands
SHORTCUTS_RESERVED_NAMES = [ ' global ' , ' archive_delete_filter ' , ' duplicate_filter ' , ' media ' , ' main_gui ' , ' media_viewer_browser ' , ' media_viewer ' , ' media_viewer_media_window ' , ' preview_media_window ' ]
2020-04-01 21:51:42 +00:00
SHORTCUTS_GLOBAL_ACTIONS = [ ' global_audio_mute ' , ' global_audio_unmute ' , ' global_audio_mute_flip ' , ' exit_application ' , ' exit_application_force_maintenance ' , ' restart_application ' , ' hide_to_system_tray ' ]
2020-06-24 21:25:24 +00:00
SHORTCUTS_MEDIA_ACTIONS = [ ' manage_file_tags ' , ' manage_file_ratings ' , ' manage_file_urls ' , ' manage_file_notes ' , ' archive_file ' , ' inbox_file ' , ' delete_file ' , ' undelete_file ' , ' export_files ' , ' export_files_quick_auto_export ' , ' remove_file_from_view ' , ' open_file_in_external_program ' , ' open_selection_in_new_page ' , ' launch_the_archive_delete_filter ' , ' copy_bmp ' , ' copy_bmp_or_file_if_not_bmpable ' , ' copy_file ' , ' copy_path ' , ' copy_sha256_hash ' , ' copy_md5_hash ' , ' copy_sha1_hash ' , ' copy_sha512_hash ' , ' get_similar_to_exact ' , ' get_similar_to_very_similar ' , ' get_similar_to_similar ' , ' get_similar_to_speculative ' , ' duplicate_media_set_alternate ' , ' duplicate_media_set_alternate_collections ' , ' duplicate_media_set_custom ' , ' duplicate_media_set_focused_better ' , ' duplicate_media_set_focused_king ' , ' duplicate_media_set_same_quality ' , ' open_known_url ' ]
2020-02-19 21:48:36 +00:00
SHORTCUTS_MEDIA_VIEWER_ACTIONS = [ ' pause_media ' , ' pause_play_media ' , ' move_animation_to_previous_frame ' , ' move_animation_to_next_frame ' , ' switch_between_fullscreen_borderless_and_regular_framed_window ' , ' pan_up ' , ' pan_down ' , ' pan_left ' , ' pan_right ' , ' pan_top_edge ' , ' pan_bottom_edge ' , ' pan_left_edge ' , ' pan_right_edge ' , ' pan_vertical_center ' , ' pan_horizontal_center ' , ' zoom_in ' , ' zoom_out ' , ' switch_between_100_percent_and_canvas_zoom ' , ' flip_darkmode ' , ' close_media_viewer ' ]
SHORTCUTS_MEDIA_VIEWER_BROWSER_ACTIONS = [ ' view_next ' , ' view_first ' , ' view_last ' , ' view_previous ' , ' pause_play_slideshow ' , ' show_menu ' , ' close_media_viewer ' ]
SHORTCUTS_MAIN_GUI_ACTIONS = [ ' refresh ' , ' refresh_all_pages ' , ' refresh_page_of_pages_pages ' , ' new_page ' , ' new_page_of_pages ' , ' new_duplicate_filter_page ' , ' new_gallery_downloader_page ' , ' new_url_downloader_page ' , ' new_simple_downloader_page ' , ' new_watcher_downloader_page ' , ' synchronised_wait_switch ' , ' set_media_focus ' , ' show_hide_splitters ' , ' set_search_focus ' , ' unclose_page ' , ' close_page ' , ' redo ' , ' undo ' , ' flip_darkmode ' , ' check_all_import_folders ' , ' flip_debug_force_idle_mode_do_not_set_this ' , ' show_and_focus_manage_tags_favourite_tags ' , ' show_and_focus_manage_tags_related_tags ' , ' show_and_focus_manage_tags_file_lookup_script_tags ' , ' show_and_focus_manage_tags_recent_tags ' , ' focus_media_viewer ' ]
SHORTCUTS_DUPLICATE_FILTER_ACTIONS = [ ' duplicate_filter_this_is_better_and_delete_other ' , ' duplicate_filter_this_is_better_but_keep_both ' , ' duplicate_filter_exactly_the_same ' , ' duplicate_filter_alternates ' , ' duplicate_filter_false_positive ' , ' duplicate_filter_custom_action ' , ' duplicate_filter_skip ' , ' duplicate_filter_back ' , ' close_media_viewer ' ]
SHORTCUTS_ARCHIVE_DELETE_FILTER_ACTIONS = [ ' archive_delete_filter_keep ' , ' archive_delete_filter_delete ' , ' archive_delete_filter_skip ' , ' archive_delete_filter_back ' , ' close_media_viewer ' ]
SHORTCUTS_MEDIA_VIEWER_VIDEO_AUDIO_PLAYER_ACTIONS = [ ' pause_media ' , ' pause_play_media ' , ' open_file_in_external_program ' , ' close_media_viewer ' ]
SHORTCUTS_PREVIEW_VIDEO_AUDIO_PLAYER_ACTIONS = [ ' pause_media ' , ' pause_play_media ' , ' open_file_in_external_program ' , ' launch_media_viewer ' ]
simple_shortcut_name_to_action_lookup = { }
simple_shortcut_name_to_action_lookup [ ' global ' ] = SHORTCUTS_GLOBAL_ACTIONS
simple_shortcut_name_to_action_lookup [ ' media ' ] = SHORTCUTS_MEDIA_ACTIONS
simple_shortcut_name_to_action_lookup [ ' media_viewer ' ] = SHORTCUTS_MEDIA_VIEWER_ACTIONS
simple_shortcut_name_to_action_lookup [ ' media_viewer_browser ' ] = SHORTCUTS_MEDIA_VIEWER_BROWSER_ACTIONS
simple_shortcut_name_to_action_lookup [ ' main_gui ' ] = SHORTCUTS_MAIN_GUI_ACTIONS
simple_shortcut_name_to_action_lookup [ ' duplicate_filter ' ] = SHORTCUTS_DUPLICATE_FILTER_ACTIONS + SHORTCUTS_MEDIA_ACTIONS + SHORTCUTS_MEDIA_VIEWER_ACTIONS
simple_shortcut_name_to_action_lookup [ ' archive_delete_filter ' ] = SHORTCUTS_ARCHIVE_DELETE_FILTER_ACTIONS
simple_shortcut_name_to_action_lookup [ ' media_viewer_media_window ' ] = SHORTCUTS_MEDIA_VIEWER_VIDEO_AUDIO_PLAYER_ACTIONS
simple_shortcut_name_to_action_lookup [ ' preview_media_window ' ] = SHORTCUTS_PREVIEW_VIDEO_AUDIO_PLAYER_ACTIONS
simple_shortcut_name_to_action_lookup [ ' custom ' ] = SHORTCUTS_MEDIA_ACTIONS + SHORTCUTS_MEDIA_VIEWER_ACTIONS
2019-12-11 23:18:37 +00:00
# ok, the problem here is that I get key codes that are converted, so if someone does shift+1 on a US keyboard, this ends up with Shift+! same with ctrl+alt+ to get accented characters
# it isn't really a big deal since everything still lines up, but the QGuiApplicationPrivate::platformIntegration()->possibleKeys(e) to get some variant of 'yeah this is just !' seems unavailable for python
# it is basically a display bug, but it'd be nice to have it working right
def ConvertQtKeyToShortcutKey ( key_qt ) :
2017-05-31 21:50:53 +00:00
2020-02-19 21:48:36 +00:00
if key_qt in special_key_shortcut_enum_lookup :
2019-11-14 03:56:30 +00:00
2020-02-19 21:48:36 +00:00
key_ord = special_key_shortcut_enum_lookup [ key_qt ]
2019-12-11 23:18:37 +00:00
2020-02-19 21:48:36 +00:00
return ( SHORTCUT_TYPE_KEYBOARD_SPECIAL , key_ord )
2019-12-11 23:18:37 +00:00
else :
try :
2019-11-14 03:56:30 +00:00
2019-12-11 23:18:37 +00:00
key_ord = int ( key_qt )
2019-11-14 03:56:30 +00:00
2019-12-11 23:18:37 +00:00
key_chr = chr ( key_ord )
2019-11-14 03:56:30 +00:00
2019-12-11 23:18:37 +00:00
# this is turbo lower() that converts Scharfes S (beta) to 'ss'
key_chr = key_chr . casefold ( ) [ 0 ]
2019-11-14 03:56:30 +00:00
2019-12-11 23:18:37 +00:00
casefold_key_ord = ord ( key_chr )
2020-02-19 21:48:36 +00:00
return ( SHORTCUT_TYPE_KEYBOARD_CHARACTER , casefold_key_ord )
2019-12-11 23:18:37 +00:00
except :
2020-02-19 21:48:36 +00:00
return ( SHORTCUT_TYPE_NOT_ALLOWED , key_ord )
2019-12-11 23:18:37 +00:00
def ConvertKeyEventToShortcut ( event ) :
key_qt = event . key ( )
( shortcut_type , key_ord ) = ConvertQtKeyToShortcutKey ( key_qt )
2020-02-19 21:48:36 +00:00
if shortcut_type != SHORTCUT_TYPE_NOT_ALLOWED :
2018-05-16 20:09:50 +00:00
modifiers = [ ]
2019-11-14 03:56:30 +00:00
if event . modifiers ( ) & QC . Qt . AltModifier :
2018-05-16 20:09:50 +00:00
2020-02-19 21:48:36 +00:00
modifiers . append ( SHORTCUT_MODIFIER_ALT )
2018-05-16 20:09:50 +00:00
2019-11-20 23:10:46 +00:00
if HC . PLATFORM_MACOS :
2019-11-14 03:56:30 +00:00
ctrl = QC . Qt . MetaModifier
else :
ctrl = QC . Qt . ControlModifier
if event . modifiers ( ) & ctrl :
2018-05-16 20:09:50 +00:00
2020-02-19 21:48:36 +00:00
modifiers . append ( SHORTCUT_MODIFIER_CTRL )
2018-05-16 20:09:50 +00:00
2019-11-14 03:56:30 +00:00
if event . modifiers ( ) & QC . Qt . ShiftModifier :
2018-05-16 20:09:50 +00:00
2020-02-19 21:48:36 +00:00
modifiers . append ( SHORTCUT_MODIFIER_SHIFT )
2018-05-16 20:09:50 +00:00
2019-11-14 03:56:30 +00:00
if event . modifiers ( ) & QC . Qt . GroupSwitchModifier :
2020-02-19 21:48:36 +00:00
modifiers . append ( SHORTCUT_MODIFIER_GROUP_SWITCH )
2019-11-14 03:56:30 +00:00
if event . modifiers ( ) & QC . Qt . KeypadModifier :
2020-02-19 21:48:36 +00:00
modifiers . append ( SHORTCUT_MODIFIER_KEYPAD )
2019-11-14 03:56:30 +00:00
2020-02-19 21:48:36 +00:00
shortcut_press_type = SHORTCUT_PRESS_TYPE_PRESS
2020-02-12 22:50:37 +00:00
shortcut = Shortcut ( shortcut_type , key_ord , shortcut_press_type , modifiers )
2018-05-16 20:09:50 +00:00
if HG . gui_report_mode :
HydrusData . ShowText ( ' key event caught: ' + repr ( shortcut ) )
return shortcut
return None
def ConvertKeyEventToSimpleTuple ( event ) :
2019-11-14 03:56:30 +00:00
modifier = QC . Qt . NoModifier
2018-05-16 20:09:50 +00:00
2019-11-14 03:56:30 +00:00
if event . modifiers ( ) & QC . Qt . AltModifier : modifier = QC . Qt . AltModifier
elif event . modifiers ( ) & QC . Qt . ControlModifier : modifier = QC . Qt . ControlModifier
elif event . modifiers ( ) & QC . Qt . ShiftModifier : modifier = QC . Qt . ShiftModifier
elif event . modifiers ( ) & QC . Qt . KeypadModifier : modifier = QC . Qt . KeypadModifier
elif event . modifiers ( ) & QC . Qt . GroupSwitchModifier : modifier = QC . Qt . GroupSwitchModifier
2018-05-16 20:09:50 +00:00
2019-11-14 03:56:30 +00:00
key = event . key ( )
2018-05-16 20:09:50 +00:00
return ( modifier , key )
2020-04-29 21:44:12 +00:00
def ConvertMouseEventToShortcut ( event : QG . QMouseEvent ) :
2018-05-16 20:09:50 +00:00
key = None
2020-02-19 21:48:36 +00:00
shortcut_press_type = SHORTCUT_PRESS_TYPE_PRESS
2020-02-12 22:50:37 +00:00
if event . type ( ) == QC . QEvent . MouseButtonPress :
2018-05-16 20:09:50 +00:00
2020-04-29 21:44:12 +00:00
for ( qt_button , hydrus_button ) in qt_mouse_buttons_to_hydrus_mouse_buttons . items ( ) :
2020-02-12 22:50:37 +00:00
2020-04-29 21:44:12 +00:00
if event . buttons ( ) & qt_button :
key = hydrus_button
break
2020-02-12 22:50:37 +00:00
2018-05-16 20:09:50 +00:00
2020-02-12 22:50:37 +00:00
elif event . type ( ) in ( QC . QEvent . MouseButtonDblClick , QC . QEvent . MouseButtonRelease ) :
2018-05-16 20:09:50 +00:00
2020-02-12 22:50:37 +00:00
if event . type ( ) == QC . QEvent . MouseButtonRelease :
2020-02-19 21:48:36 +00:00
shortcut_press_type = SHORTCUT_PRESS_TYPE_RELEASE
2020-02-12 22:50:37 +00:00
elif event . type ( ) == QC . QEvent . MouseButtonDblClick :
2020-02-19 21:48:36 +00:00
shortcut_press_type = SHORTCUT_PRESS_TYPE_DOUBLE_CLICK
2020-02-12 22:50:37 +00:00
2018-05-16 20:09:50 +00:00
2020-04-29 21:44:12 +00:00
for ( qt_button , hydrus_button ) in qt_mouse_buttons_to_hydrus_mouse_buttons . items ( ) :
2020-02-12 22:50:37 +00:00
2020-04-29 21:44:12 +00:00
if event . button ( ) == qt_button :
key = hydrus_button
break
2020-02-12 22:50:37 +00:00
2018-05-16 20:09:50 +00:00
2020-02-12 22:50:37 +00:00
elif event . type ( ) == QC . QEvent . Wheel :
2018-05-16 20:09:50 +00:00
2020-02-12 22:50:37 +00:00
if event . angleDelta ( ) . y ( ) > 0 :
2020-02-19 21:48:36 +00:00
key = SHORTCUT_MOUSE_SCROLL_UP
2020-02-12 22:50:37 +00:00
elif event . angleDelta ( ) . y ( ) < 0 :
2020-02-19 21:48:36 +00:00
key = SHORTCUT_MOUSE_SCROLL_DOWN
2020-02-12 22:50:37 +00:00
2018-05-16 20:09:50 +00:00
if key is not None :
modifiers = [ ]
2019-11-14 03:56:30 +00:00
if event . modifiers ( ) & QC . Qt . AltModifier :
2018-05-16 20:09:50 +00:00
2020-02-19 21:48:36 +00:00
modifiers . append ( SHORTCUT_MODIFIER_ALT )
2018-05-16 20:09:50 +00:00
2019-11-14 03:56:30 +00:00
if event . modifiers ( ) & QC . Qt . ControlModifier :
2018-05-16 20:09:50 +00:00
2020-02-19 21:48:36 +00:00
modifiers . append ( SHORTCUT_MODIFIER_CTRL )
2018-05-16 20:09:50 +00:00
2019-11-14 03:56:30 +00:00
if event . modifiers ( ) & QC . Qt . ShiftModifier :
2018-05-16 20:09:50 +00:00
2020-02-19 21:48:36 +00:00
modifiers . append ( SHORTCUT_MODIFIER_SHIFT )
2018-05-16 20:09:50 +00:00
2019-11-14 03:56:30 +00:00
if event . modifiers ( ) & QC . Qt . GroupSwitchModifier :
2020-02-19 21:48:36 +00:00
modifiers . append ( SHORTCUT_MODIFIER_GROUP_SWITCH )
2019-11-14 03:56:30 +00:00
if event . modifiers ( ) & QC . Qt . KeypadModifier :
2020-02-19 21:48:36 +00:00
modifiers . append ( SHORTCUT_MODIFIER_KEYPAD )
2019-11-14 03:56:30 +00:00
2020-02-19 21:48:36 +00:00
shortcut = Shortcut ( SHORTCUT_TYPE_MOUSE , key , shortcut_press_type , modifiers )
2018-05-16 20:09:50 +00:00
if HG . gui_report_mode :
HydrusData . ShowText ( ' mouse event caught: ' + repr ( shortcut ) )
return shortcut
return None
2020-02-19 21:48:36 +00:00
def AncestorShortcutsHandlers ( widget : QW . QWidget ) :
shortcuts_handlers = [ ]
window = widget . window ( )
if window == widget :
return shortcuts_handlers
2020-04-08 21:10:11 +00:00
widget = widget . parentWidget ( )
if widget is None :
return shortcuts_handlers
2020-02-19 21:48:36 +00:00
while True :
child_shortcuts_handlers = [ child for child in widget . children ( ) if isinstance ( child , ShortcutsHandler ) ]
shortcuts_handlers . extend ( child_shortcuts_handlers )
if widget == window :
break
2020-04-08 21:10:11 +00:00
widget = widget . parentWidget ( )
2020-02-19 21:48:36 +00:00
if widget is None :
break
return shortcuts_handlers
2020-05-27 21:27:52 +00:00
def IShouldCatchShortcutEvent ( event_handler_owner : QC . QObject , event_catcher : QW . QWidget , event : typing . Optional [ QC . QEvent ] = None , child_tlw_classes_who_can_pass_up : typing . Optional [ typing . Iterable [ type ] ] = None ) :
2017-04-19 20:58:30 +00:00
2019-07-03 22:49:27 +00:00
do_focus_test = True
2020-05-27 21:27:52 +00:00
if event is not None :
2019-11-20 23:10:46 +00:00
2020-05-27 21:27:52 +00:00
# the event happened to somewhere else, most likely a hover window of a media viewer
# should we intercept that event that happened somewhere else?
if event_handler_owner != event_catcher :
# don't pass clicks up
if event . type ( ) in ( QC . QEvent . MouseButtonPress , QC . QEvent . MouseButtonRelease , QC . QEvent . MouseButtonDblClick ) :
return False
# don't pass wheels that happen to legit controls that want to eat it, like a list, when the catcher is a window
if event . type ( ) == QC . QEvent . Wheel :
widget_under_mouse = event_catcher . childAt ( event_catcher . mapFromGlobal ( QG . QCursor . pos ( ) ) )
if widget_under_mouse is not None :
mouse_scroll_over_window_greyspace = widget_under_mouse == event_catcher and event_catcher . isWindow ( )
if not mouse_scroll_over_window_greyspace :
return False
2019-11-20 23:10:46 +00:00
2020-05-27 21:27:52 +00:00
if event . type ( ) == QC . QEvent . Wheel :
do_focus_test = False
2019-07-03 22:49:27 +00:00
2020-05-27 21:27:52 +00:00
do_focus_test = False
2019-07-03 22:49:27 +00:00
if do_focus_test :
2020-05-27 21:27:52 +00:00
if not ClientGUIFunctions . TLWIsActive ( event_handler_owner ) :
2019-06-26 21:27:18 +00:00
2019-12-11 23:18:37 +00:00
if child_tlw_classes_who_can_pass_up is not None :
2019-07-03 22:49:27 +00:00
2020-05-27 21:27:52 +00:00
child_tlw_has_focus = ClientGUIFunctions . WidgetOrAnyTLWChildHasFocus ( event_handler_owner ) and isinstance ( QW . QApplication . activeWindow ( ) , child_tlw_classes_who_can_pass_up )
2019-07-03 22:49:27 +00:00
2019-12-11 23:18:37 +00:00
if not child_tlw_has_focus :
2019-07-03 22:49:27 +00:00
return False
else :
2019-06-26 21:27:18 +00:00
return False
2017-04-19 20:58:30 +00:00
return True
2018-05-16 20:09:50 +00:00
class Shortcut ( HydrusSerialisable . SerialisableBase ) :
SERIALISABLE_TYPE = HydrusSerialisable . SERIALISABLE_TYPE_SHORTCUT
SERIALISABLE_NAME = ' Shortcut '
2020-02-12 22:50:37 +00:00
SERIALISABLE_VERSION = 3
2018-05-16 20:09:50 +00:00
2020-02-12 22:50:37 +00:00
def __init__ ( self , shortcut_type = None , shortcut_key = None , shortcut_press_type = None , modifiers = None ) :
2018-05-16 20:09:50 +00:00
if shortcut_type is None :
2020-02-19 21:48:36 +00:00
shortcut_type = SHORTCUT_TYPE_KEYBOARD_SPECIAL
2018-05-16 20:09:50 +00:00
if shortcut_key is None :
2020-02-19 21:48:36 +00:00
shortcut_key = SHORTCUT_KEY_SPECIAL_F7
2018-05-16 20:09:50 +00:00
2020-02-12 22:50:37 +00:00
if shortcut_press_type is None :
2020-02-19 21:48:36 +00:00
shortcut_press_type = SHORTCUT_PRESS_TYPE_PRESS
2020-02-12 22:50:37 +00:00
2018-05-16 20:09:50 +00:00
if modifiers is None :
modifiers = [ ]
2020-02-19 21:48:36 +00:00
if shortcut_type == SHORTCUT_TYPE_KEYBOARD_CHARACTER and ClientData . OrdIsAlphaUpper ( shortcut_key ) :
2019-11-14 03:56:30 +00:00
shortcut_key + = 32 # convert A to a
2020-05-13 19:03:16 +00:00
modifiers = sorted ( modifiers )
2018-05-16 20:09:50 +00:00
HydrusSerialisable . SerialisableBase . __init__ ( self )
2020-02-12 22:50:37 +00:00
self . shortcut_type = shortcut_type
self . shortcut_key = shortcut_key
self . shortcut_press_type = shortcut_press_type
self . modifiers = modifiers
2018-05-16 20:09:50 +00:00
def __eq__ ( self , other ) :
2020-01-22 21:04:43 +00:00
if isinstance ( other , Shortcut ) :
return self . __hash__ ( ) == other . __hash__ ( )
return NotImplemented
2018-05-16 20:09:50 +00:00
def __hash__ ( self ) :
2020-02-12 22:50:37 +00:00
return ( self . shortcut_type , self . shortcut_key , self . shortcut_press_type , tuple ( self . modifiers ) ) . __hash__ ( )
2018-05-16 20:09:50 +00:00
def __repr__ ( self ) :
return ' Shortcut: ' + self . ToString ( )
def _GetSerialisableInfo ( self ) :
2020-02-12 22:50:37 +00:00
return ( self . shortcut_type , self . shortcut_key , self . shortcut_press_type , self . modifiers )
2018-05-16 20:09:50 +00:00
def _InitialiseFromSerialisableInfo ( self , serialisable_info ) :
2020-02-12 22:50:37 +00:00
( self . shortcut_type , self . shortcut_key , self . shortcut_press_type , self . modifiers ) = serialisable_info
2018-05-16 20:09:50 +00:00
2019-11-14 03:56:30 +00:00
def _UpdateSerialisableInfo ( self , version , old_serialisable_info ) :
if version == 1 :
# these are dicts that convert fixed wx enums to new stuff
wx_to_qt_flat_conversion = {
2020-02-19 21:48:36 +00:00
32 : SHORTCUT_KEY_SPECIAL_SPACE ,
8 : SHORTCUT_KEY_SPECIAL_BACKSPACE ,
9 : SHORTCUT_KEY_SPECIAL_TAB ,
13 : SHORTCUT_KEY_SPECIAL_RETURN ,
310 : SHORTCUT_KEY_SPECIAL_PAUSE ,
27 : SHORTCUT_KEY_SPECIAL_ESCAPE ,
322 : SHORTCUT_KEY_SPECIAL_INSERT ,
127 : SHORTCUT_KEY_SPECIAL_DELETE ,
315 : SHORTCUT_KEY_SPECIAL_UP ,
317 : SHORTCUT_KEY_SPECIAL_DOWN ,
314 : SHORTCUT_KEY_SPECIAL_LEFT ,
316 : SHORTCUT_KEY_SPECIAL_RIGHT ,
313 : SHORTCUT_KEY_SPECIAL_HOME ,
312 : SHORTCUT_KEY_SPECIAL_END ,
367 : SHORTCUT_KEY_SPECIAL_PAGE_DOWN ,
366 : SHORTCUT_KEY_SPECIAL_PAGE_UP ,
340 : SHORTCUT_KEY_SPECIAL_F1 ,
341 : SHORTCUT_KEY_SPECIAL_F2 ,
342 : SHORTCUT_KEY_SPECIAL_F3 ,
343 : SHORTCUT_KEY_SPECIAL_F4 ,
344 : SHORTCUT_KEY_SPECIAL_F5 ,
345 : SHORTCUT_KEY_SPECIAL_F6 ,
346 : SHORTCUT_KEY_SPECIAL_F7 ,
347 : SHORTCUT_KEY_SPECIAL_F8 ,
348 : SHORTCUT_KEY_SPECIAL_F9 ,
349 : SHORTCUT_KEY_SPECIAL_F10 ,
350 : SHORTCUT_KEY_SPECIAL_F11 ,
351 : SHORTCUT_KEY_SPECIAL_F12
2019-11-14 03:56:30 +00:00
}
# regular keys, but numpad, that are tracked in wx by combined unique enum
wx_to_qt_numpad_ascii_conversion = {
324 : ord ( ' 0 ' ) ,
325 : ord ( ' 1 ' ) ,
326 : ord ( ' 2 ' ) ,
327 : ord ( ' 3 ' ) ,
328 : ord ( ' 4 ' ) ,
329 : ord ( ' 5 ' ) ,
330 : ord ( ' 6 ' ) ,
331 : ord ( ' 7 ' ) ,
332 : ord ( ' 8 ' ) ,
2019-12-11 23:18:37 +00:00
333 : ord ( ' 9 ' ) ,
2019-11-14 03:56:30 +00:00
388 : ord ( ' + ' ) ,
392 : ord ( ' / ' ) ,
390 : ord ( ' - ' ) ,
387 : ord ( ' * ' ) ,
391 : ord ( ' . ' )
}
wx_to_qt_numpad_conversion = {
2020-02-19 21:48:36 +00:00
377 : SHORTCUT_KEY_SPECIAL_UP ,
379 : SHORTCUT_KEY_SPECIAL_DOWN ,
376 : SHORTCUT_KEY_SPECIAL_LEFT ,
378 : SHORTCUT_KEY_SPECIAL_RIGHT ,
375 : SHORTCUT_KEY_SPECIAL_HOME ,
382 : SHORTCUT_KEY_SPECIAL_END ,
381 : SHORTCUT_KEY_SPECIAL_PAGE_DOWN ,
380 : SHORTCUT_KEY_SPECIAL_PAGE_UP ,
385 : SHORTCUT_KEY_SPECIAL_DELETE ,
370 : SHORTCUT_KEY_SPECIAL_ENTER
2019-11-14 03:56:30 +00:00
}
( shortcut_type , shortcut_key , modifiers ) = old_serialisable_info
2020-02-19 21:48:36 +00:00
if shortcut_type == SHORTCUT_TYPE_KEYBOARD_CHARACTER :
2019-11-14 03:56:30 +00:00
if shortcut_key in wx_to_qt_flat_conversion :
2020-02-19 21:48:36 +00:00
shortcut_type = SHORTCUT_TYPE_KEYBOARD_SPECIAL
2019-11-14 03:56:30 +00:00
shortcut_key = wx_to_qt_flat_conversion [ shortcut_key ]
elif shortcut_key in wx_to_qt_numpad_ascii_conversion :
shortcut_key = wx_to_qt_numpad_ascii_conversion [ shortcut_key ]
modifiers = list ( modifiers )
2020-02-19 21:48:36 +00:00
modifiers . append ( SHORTCUT_MODIFIER_KEYPAD )
2019-11-14 03:56:30 +00:00
modifiers . sort ( )
elif shortcut_key in wx_to_qt_numpad_conversion :
2020-02-19 21:48:36 +00:00
shortcut_type = SHORTCUT_TYPE_KEYBOARD_SPECIAL
2019-11-14 03:56:30 +00:00
shortcut_key = wx_to_qt_numpad_conversion [ shortcut_key ]
modifiers = list ( modifiers )
2020-02-19 21:48:36 +00:00
modifiers . append ( SHORTCUT_MODIFIER_KEYPAD )
2019-11-14 03:56:30 +00:00
modifiers . sort ( )
2020-02-19 21:48:36 +00:00
if shortcut_type == SHORTCUT_TYPE_KEYBOARD_CHARACTER :
2019-11-14 03:56:30 +00:00
if ClientData . OrdIsAlphaUpper ( shortcut_key ) :
shortcut_key + = 32 # convert 'A' to 'a'
new_serialisable_info = ( shortcut_type , shortcut_key , modifiers )
return ( 2 , new_serialisable_info )
2020-02-12 22:50:37 +00:00
if version == 2 :
( shortcut_type , shortcut_key , modifiers ) = old_serialisable_info
2020-02-19 21:48:36 +00:00
shortcut_press_type = SHORTCUT_PRESS_TYPE_PRESS
2020-02-12 22:50:37 +00:00
new_serialisable_info = ( shortcut_type , shortcut_key , shortcut_press_type , modifiers )
return ( 3 , new_serialisable_info )
2019-11-14 03:56:30 +00:00
2020-02-19 21:48:36 +00:00
def ConvertToSingleClick ( self ) :
if self . IsDoubleClick ( ) :
new_shortcut = self . Duplicate ( )
new_shortcut . shortcut_press_type = SHORTCUT_PRESS_TYPE_PRESS
return new_shortcut
return self
2018-05-16 20:09:50 +00:00
def GetShortcutType ( self ) :
2020-02-12 22:50:37 +00:00
return self . shortcut_type
2018-05-16 20:09:50 +00:00
2020-06-24 21:25:24 +00:00
def IsAppropriateForPressRelease ( self ) :
return self . shortcut_key in SHORTCUT_MOUSE_CLICKS and self . shortcut_press_type != SHORTCUT_PRESS_TYPE_DOUBLE_CLICK
2020-02-19 21:48:36 +00:00
def IsDoubleClick ( self ) :
return self . shortcut_type == SHORTCUT_TYPE_MOUSE and self . shortcut_press_type == SHORTCUT_PRESS_TYPE_DOUBLE_CLICK
2018-05-16 20:09:50 +00:00
def ToString ( self ) :
components = [ ]
2020-02-19 21:48:36 +00:00
if SHORTCUT_MODIFIER_CTRL in self . modifiers :
2018-05-16 20:09:50 +00:00
components . append ( ' ctrl ' )
2020-02-19 21:48:36 +00:00
if SHORTCUT_MODIFIER_ALT in self . modifiers :
2018-05-16 20:09:50 +00:00
components . append ( ' alt ' )
2020-02-19 21:48:36 +00:00
if SHORTCUT_MODIFIER_SHIFT in self . modifiers :
2018-05-16 20:09:50 +00:00
components . append ( ' shift ' )
2020-02-19 21:48:36 +00:00
if SHORTCUT_MODIFIER_GROUP_SWITCH in self . modifiers :
2018-05-16 20:09:50 +00:00
2019-11-14 03:56:30 +00:00
components . append ( ' Mode_switch ' )
2020-02-19 21:48:36 +00:00
if self . shortcut_press_type != SHORTCUT_PRESS_TYPE_PRESS :
2020-02-12 22:50:37 +00:00
2020-02-19 21:48:36 +00:00
action_name = ' {} ' . format ( shortcut_press_type_str_lookup [ self . shortcut_press_type ] )
2019-11-14 03:56:30 +00:00
2020-02-12 22:50:37 +00:00
else :
2019-11-14 03:56:30 +00:00
2020-02-12 22:50:37 +00:00
action_name = ' '
2020-02-19 21:48:36 +00:00
if self . shortcut_type == SHORTCUT_TYPE_MOUSE and self . shortcut_key in shortcut_mouse_string_lookup :
2019-11-14 03:56:30 +00:00
2020-02-19 21:48:36 +00:00
action_name + = shortcut_mouse_string_lookup [ self . shortcut_key ]
2019-11-14 03:56:30 +00:00
2020-02-19 21:48:36 +00:00
elif self . shortcut_type == SHORTCUT_TYPE_KEYBOARD_SPECIAL and self . shortcut_key in special_key_shortcut_str_lookup :
2020-02-12 22:50:37 +00:00
2020-02-19 21:48:36 +00:00
action_name + = special_key_shortcut_str_lookup [ self . shortcut_key ]
2020-02-12 22:50:37 +00:00
2020-02-19 21:48:36 +00:00
elif self . shortcut_type == SHORTCUT_TYPE_KEYBOARD_CHARACTER :
2019-11-14 03:56:30 +00:00
2019-12-18 22:06:34 +00:00
try :
2018-05-16 20:09:50 +00:00
2020-02-12 22:50:37 +00:00
if ClientData . OrdIsAlphaUpper ( self . shortcut_key ) :
2019-12-18 22:06:34 +00:00
2020-02-12 22:50:37 +00:00
action_name + = chr ( self . shortcut_key + 32 ) # + 32 for converting ascii A -> a
2019-12-18 22:06:34 +00:00
else :
2020-02-12 22:50:37 +00:00
action_name + = chr ( self . shortcut_key )
2019-12-18 22:06:34 +00:00
2018-05-16 20:09:50 +00:00
2019-12-18 22:06:34 +00:00
except :
2018-05-16 20:09:50 +00:00
2020-02-12 22:50:37 +00:00
action_name + = ' unknown key: {} ' . format ( repr ( self . shortcut_key ) )
2018-05-16 20:09:50 +00:00
2019-11-14 03:56:30 +00:00
else :
2018-05-16 20:09:50 +00:00
2020-02-12 22:50:37 +00:00
action_name + = ' unknown key: {} ' . format ( repr ( self . shortcut_key ) )
2018-05-16 20:09:50 +00:00
2020-02-12 22:50:37 +00:00
components . append ( action_name )
2019-11-14 03:56:30 +00:00
s = ' + ' . join ( components )
2020-02-19 21:48:36 +00:00
if SHORTCUT_MODIFIER_KEYPAD in self . modifiers :
2019-11-14 03:56:30 +00:00
s + = ' (on numpad) '
return s
2018-05-16 20:09:50 +00:00
HydrusSerialisable . SERIALISABLE_TYPES_TO_OBJECT_TYPES [ HydrusSerialisable . SERIALISABLE_TYPE_SHORTCUT ] = Shortcut
2019-06-05 19:42:39 +00:00
class ShortcutSet ( HydrusSerialisable . SerialisableBaseNamed ) :
2018-05-16 20:09:50 +00:00
2019-06-05 19:42:39 +00:00
SERIALISABLE_TYPE = HydrusSerialisable . SERIALISABLE_TYPE_SHORTCUT_SET
SERIALISABLE_NAME = ' Shortcut Set '
2018-05-16 20:09:50 +00:00
SERIALISABLE_VERSION = 2
def __init__ ( self , name ) :
HydrusSerialisable . SerialisableBaseNamed . __init__ ( self , name )
self . _shortcuts_to_commands = { }
def __iter__ ( self ) :
2019-05-22 22:35:06 +00:00
for ( shortcut , command ) in list ( self . _shortcuts_to_commands . items ( ) ) :
2018-05-16 20:09:50 +00:00
yield ( shortcut , command )
def __len__ ( self ) :
return len ( self . _shortcuts_to_commands )
def _GetSerialisableInfo ( self ) :
2019-01-09 22:59:03 +00:00
return [ ( shortcut . GetSerialisableTuple ( ) , command . GetSerialisableTuple ( ) ) for ( shortcut , command ) in list ( self . _shortcuts_to_commands . items ( ) ) ]
2018-05-16 20:09:50 +00:00
def _InitialiseFromSerialisableInfo ( self , serialisable_info ) :
for ( serialisable_shortcut , serialisable_command ) in serialisable_info :
shortcut = HydrusSerialisable . CreateFromSerialisableTuple ( serialisable_shortcut )
command = HydrusSerialisable . CreateFromSerialisableTuple ( serialisable_command )
self . _shortcuts_to_commands [ shortcut ] = command
def _UpdateSerialisableInfo ( self , version , old_serialisable_info ) :
if version == 1 :
( serialisable_mouse_actions , serialisable_keyboard_actions ) = old_serialisable_info
shortcuts_to_commands = { }
# this never stored mouse actions, so skip
services_manager = HG . client_controller . services_manager
for ( modifier , key , ( serialisable_service_key , data ) ) in serialisable_keyboard_actions :
2019-11-14 03:56:30 +00:00
# no longer updating modifier, as that was wx legacy
2018-05-16 20:09:50 +00:00
2019-11-14 03:56:30 +00:00
modifiers = [ ]
2020-02-19 21:48:36 +00:00
shortcut = Shortcut ( SHORTCUT_TYPE_KEYBOARD_CHARACTER , key , SHORTCUT_PRESS_TYPE_PRESS , modifiers )
2018-05-16 20:09:50 +00:00
if serialisable_service_key is None :
command = ClientData . ApplicationCommand ( CC . APPLICATION_COMMAND_TYPE_SIMPLE , data )
else :
2019-01-09 22:59:03 +00:00
service_key = bytes . fromhex ( serialisable_service_key )
2018-05-16 20:09:50 +00:00
if not services_manager . ServiceExists ( service_key ) :
continue
action = HC . CONTENT_UPDATE_FLIP
value = data
service = services_manager . GetService ( service_key )
service_type = service . GetServiceType ( )
2020-03-11 21:52:11 +00:00
if service_type in HC . REAL_TAG_SERVICES :
2018-05-16 20:09:50 +00:00
content_type = HC . CONTENT_TYPE_MAPPINGS
elif service_type in HC . RATINGS_SERVICES :
content_type = HC . CONTENT_TYPE_RATINGS
else :
continue
command = ClientData . ApplicationCommand ( CC . APPLICATION_COMMAND_TYPE_CONTENT , ( service_key , content_type , action , value ) )
shortcuts_to_commands [ shortcut ] = command
2019-01-09 22:59:03 +00:00
new_serialisable_info = ( ( shortcut . GetSerialisableTuple ( ) , command . GetSerialisableTuple ( ) ) for ( shortcut , command ) in list ( shortcuts_to_commands . items ( ) ) )
2018-05-16 20:09:50 +00:00
return ( 2 , new_serialisable_info )
def GetCommand ( self , shortcut ) :
if shortcut in self . _shortcuts_to_commands :
return self . _shortcuts_to_commands [ shortcut ]
else :
return None
2019-06-05 19:42:39 +00:00
def GetShortcuts ( self , simple_command ) :
shortcuts = [ ]
for ( shortcut , command ) in self . _shortcuts_to_commands . items ( ) :
if command . GetCommandType ( ) == CC . APPLICATION_COMMAND_TYPE_SIMPLE and command . GetData ( ) == simple_command :
shortcuts . append ( shortcut )
return shortcuts
2018-05-16 20:09:50 +00:00
def SetCommand ( self , shortcut , command ) :
self . _shortcuts_to_commands [ shortcut ] = command
2019-06-05 19:42:39 +00:00
HydrusSerialisable . SERIALISABLE_TYPES_TO_OBJECT_TYPES [ HydrusSerialisable . SERIALISABLE_TYPE_SHORTCUT_SET ] = ShortcutSet
2018-05-16 20:09:50 +00:00
2019-11-14 03:56:30 +00:00
class ShortcutsHandler ( QC . QObject ) :
2017-03-29 19:39:34 +00:00
2020-03-04 22:12:53 +00:00
def __init__ ( self , parent : QW . QWidget , initial_shortcuts_names = None , catch_mouse = False , ignore_activating_mouse_click = False ) :
2017-03-29 19:39:34 +00:00
2019-11-14 03:56:30 +00:00
QC . QObject . __init__ ( self , parent )
2020-02-12 22:50:37 +00:00
self . _catch_mouse = catch_mouse
2018-03-14 21:01:02 +00:00
if initial_shortcuts_names is None :
2017-03-29 19:39:34 +00:00
2018-03-14 21:01:02 +00:00
initial_shortcuts_names = [ ]
2017-03-29 19:39:34 +00:00
self . _parent = parent
2019-11-14 03:56:30 +00:00
self . _parent . installEventFilter ( self )
2018-03-14 21:01:02 +00:00
self . _shortcuts_names = list ( initial_shortcuts_names )
2017-03-29 19:39:34 +00:00
2020-03-04 22:12:53 +00:00
self . _ignore_activating_mouse_click = ignore_activating_mouse_click
self . _frame_activated_time = 0.0
if self . _catch_mouse and self . _ignore_activating_mouse_click :
self . _deactivation_catcher = ShortcutsDeactivationCatcher ( self , parent )
2018-03-14 21:01:02 +00:00
2020-02-19 21:48:36 +00:00
def _ProcessShortcut ( self , shortcut : Shortcut ) :
2018-03-14 21:01:02 +00:00
shortcut_processed = False
2020-03-25 21:15:57 +00:00
command = shortcuts_manager ( ) . GetCommand ( self . _shortcuts_names , shortcut )
2020-02-19 21:48:36 +00:00
if command is None and shortcut . IsDoubleClick ( ) :
# ok, so user double-clicked
# if a parent wants to catch this (for instance the media viewer when we double-click a video), then we want that parent to have it
# but if no parent wants it, we can try converting it to a single-click to see if that does anything
ancestor_shortcuts_handlers = AncestorShortcutsHandlers ( self . _parent )
all_ancestor_shortcut_names = HydrusData . MassUnion ( [ ancestor_shortcuts_handler . GetShortcutNames ( ) for ancestor_shortcuts_handler in ancestor_shortcuts_handlers ] )
2020-03-25 21:15:57 +00:00
ancestor_command = shortcuts_manager ( ) . GetCommand ( all_ancestor_shortcut_names , shortcut )
2020-02-19 21:48:36 +00:00
if ancestor_command is None :
if HG . shortcut_report_mode :
message = ' Shortcut " ' + shortcut . ToString ( ) + ' " did not match any command. The single click version is now being attempted. '
HydrusData . ShowText ( message )
shortcut = shortcut . ConvertToSingleClick ( )
2020-03-25 21:15:57 +00:00
command = shortcuts_manager ( ) . GetCommand ( self . _shortcuts_names , shortcut )
2020-02-19 21:48:36 +00:00
else :
if HG . shortcut_report_mode :
message = ' Shortcut " ' + shortcut . ToString ( ) + ' " did not match any command. A parent seems to want it, however, so the single click version will not be attempted. '
HydrusData . ShowText ( message )
2018-03-14 21:01:02 +00:00
if command is not None :
command_processed = self . _parent . ProcessApplicationCommand ( command )
2017-03-29 19:39:34 +00:00
2018-03-14 21:01:02 +00:00
if command_processed :
shortcut_processed = True
2017-03-29 19:39:34 +00:00
2018-03-22 00:03:33 +00:00
if HG . shortcut_report_mode :
message = ' Shortcut " ' + shortcut . ToString ( ) + ' " matched to command " ' + command . ToString ( ) + ' " on ' + repr ( self . _parent ) + ' . '
if command_processed :
message + = ' It was processed. '
else :
message + = ' It was not processed. '
HydrusData . ShowText ( message )
2017-03-29 19:39:34 +00:00
2018-03-14 21:01:02 +00:00
return shortcut_processed
2017-03-29 19:39:34 +00:00
2020-04-29 21:44:12 +00:00
def AddWindowToFilter ( self , win : QW . QWidget ) :
win . installEventFilter ( self )
2019-11-14 03:56:30 +00:00
def eventFilter ( self , watched , event ) :
2018-03-22 00:03:33 +00:00
2019-11-14 03:56:30 +00:00
if event . type ( ) == QC . QEvent . KeyPress :
2018-03-14 21:01:02 +00:00
2020-05-27 21:27:52 +00:00
i_should_catch_shortcut_event = IShouldCatchShortcutEvent ( self . _parent , watched , event = event )
2020-02-12 22:50:37 +00:00
2019-11-14 03:56:30 +00:00
shortcut = ConvertKeyEventToShortcut ( event )
if shortcut is not None :
2018-03-22 00:03:33 +00:00
2019-11-14 03:56:30 +00:00
if HG . shortcut_report_mode :
2018-03-22 00:03:33 +00:00
2019-11-14 03:56:30 +00:00
message = ' Key shortcut " ' + shortcut . ToString ( ) + ' " passing through ' + repr ( self . _parent ) + ' . '
2018-03-22 00:03:33 +00:00
2020-02-12 22:50:37 +00:00
if i_should_catch_shortcut_event :
2019-11-14 03:56:30 +00:00
message + = ' I am in a state to catch it. '
else :
message + = ' I am not in a state to catch it. '
2018-03-22 00:03:33 +00:00
2019-11-14 03:56:30 +00:00
HydrusData . ShowText ( message )
2018-03-22 00:03:33 +00:00
2020-02-12 22:50:37 +00:00
if i_should_catch_shortcut_event :
2018-03-14 21:01:02 +00:00
2019-11-14 03:56:30 +00:00
shortcut_processed = self . _ProcessShortcut ( shortcut )
if shortcut_processed :
2020-02-12 22:50:37 +00:00
event . accept ( )
2019-11-14 03:56:30 +00:00
return True
2018-03-14 21:01:02 +00:00
2020-02-12 22:50:37 +00:00
elif self . _catch_mouse :
if event . type ( ) in ( QC . QEvent . MouseButtonPress , QC . QEvent . MouseButtonRelease , QC . QEvent . MouseButtonDblClick , QC . QEvent . Wheel ) :
2020-05-27 21:27:52 +00:00
if event . type ( ) != QC . QEvent . Wheel and self . _ignore_activating_mouse_click and not HydrusData . TimeHasPassedPrecise ( self . _frame_activated_time + 0.017 ) :
2020-03-04 22:12:53 +00:00
if event . type ( ) == QC . QEvent . MouseButtonRelease :
self . _frame_activated_time = 0.0
return False
2020-05-27 21:27:52 +00:00
i_should_catch_shortcut_event = IShouldCatchShortcutEvent ( self . _parent , watched , event = event )
2020-02-12 22:50:37 +00:00
shortcut = ConvertMouseEventToShortcut ( event )
if shortcut is not None :
if HG . shortcut_report_mode :
message = ' Mouse Press shortcut " ' + shortcut . ToString ( ) + ' " passing through ' + repr ( self . _parent ) + ' . '
if i_should_catch_shortcut_event :
message + = ' I am in a state to catch it. '
else :
message + = ' I am not in a state to catch it. '
HydrusData . ShowText ( message )
if i_should_catch_shortcut_event :
shortcut_processed = self . _ProcessShortcut ( shortcut )
if shortcut_processed :
event . accept ( )
return True
2019-11-14 03:56:30 +00:00
return False
2017-03-29 19:39:34 +00:00
2019-09-11 21:51:09 +00:00
def AddShortcuts ( self , shortcut_set_name ) :
2017-03-29 19:39:34 +00:00
2019-09-11 21:51:09 +00:00
if shortcut_set_name not in self . _shortcuts_names :
2018-03-14 21:01:02 +00:00
2020-02-19 21:48:36 +00:00
reserved_names = [ name for name in self . _shortcuts_names if name in SHORTCUTS_RESERVED_NAMES ]
custom_names = [ name for name in self . _shortcuts_names if name not in SHORTCUTS_RESERVED_NAMES ]
if shortcut_set_name in SHORTCUTS_RESERVED_NAMES :
reserved_names . append ( shortcut_set_name )
else :
custom_names . append ( shortcut_set_name )
self . _shortcuts_names = reserved_names + custom_names
def FlipShortcuts ( self , shortcut_set_name ) :
if shortcut_set_name in self . _shortcuts_names :
self . RemoveShortcuts ( shortcut_set_name )
else :
self . AddShortcuts ( shortcut_set_name )
2018-03-14 21:01:02 +00:00
2017-03-29 19:39:34 +00:00
2020-02-19 21:48:36 +00:00
def GetCustomShortcutNames ( self ) :
2020-05-13 19:03:16 +00:00
custom_names = sorted ( ( name for name in self . _shortcuts_names if name not in SHORTCUTS_RESERVED_NAMES ) )
2020-02-19 21:48:36 +00:00
return custom_names
def GetShortcutNames ( self ) :
return list ( self . _shortcuts_names )
def HasShortcuts ( self , shortcut_set_name ) :
return shortcut_set_name in self . _shortcuts_names
def ProcessShortcut ( self , shortcut ) :
return self . _ProcessShortcut ( shortcut )
2019-09-11 21:51:09 +00:00
def RemoveShortcuts ( self , shortcut_set_name ) :
2017-03-29 19:39:34 +00:00
2019-09-11 21:51:09 +00:00
if shortcut_set_name in self . _shortcuts_names :
2017-03-29 19:39:34 +00:00
2019-09-11 21:51:09 +00:00
self . _shortcuts_names . remove ( shortcut_set_name )
2017-03-29 19:39:34 +00:00
2018-05-16 20:09:50 +00:00
2020-02-12 22:50:37 +00:00
def SetShortcuts ( self , shortcut_set_names ) :
self . _shortcuts_names = list ( shortcut_set_names )
2020-03-04 22:12:53 +00:00
def FrameActivated ( self ) :
self . _frame_activated_time = HydrusData . GetNowPrecise ( )
class ShortcutsDeactivationCatcher ( QC . QObject ) :
def __init__ ( self , shortcuts_handler : ShortcutsHandler , widget : QW . QWidget ) :
QC . QObject . __init__ ( self , shortcuts_handler )
self . _shortcuts_handler = shortcuts_handler
widget . window ( ) . installEventFilter ( self )
def eventFilter ( self , watched , event ) :
if event . type ( ) == QC . QEvent . WindowActivate :
self . _shortcuts_handler . FrameActivated ( )
return False
2020-03-25 21:15:57 +00:00
class ShortcutsManager ( QC . QObject ) :
2019-06-05 19:42:39 +00:00
2020-03-25 21:15:57 +00:00
shortcutsChanged = QC . Signal ( )
my_instance = None
def __init__ ( self , shortcut_sets = None ) :
2019-06-05 19:42:39 +00:00
2020-03-25 21:15:57 +00:00
parent = CGC . core ( )
2019-06-05 19:42:39 +00:00
2020-03-25 21:15:57 +00:00
QC . QObject . __init__ ( self , parent )
2019-06-05 19:42:39 +00:00
2020-03-25 21:15:57 +00:00
self . _names_to_shortcut_sets = { }
2019-06-05 19:42:39 +00:00
2020-03-25 21:15:57 +00:00
if shortcut_sets is not None :
self . SetShortcutSets ( shortcut_sets )
2019-06-05 19:42:39 +00:00
2020-03-25 21:15:57 +00:00
ShortcutsManager . my_instance = self
2019-06-05 19:42:39 +00:00
2020-03-25 21:15:57 +00:00
@staticmethod
def instance ( ) - > ' ShortcutsManager ' :
2019-06-05 19:42:39 +00:00
2020-03-25 21:15:57 +00:00
if ShortcutsManager . my_instance is None :
raise Exception ( ' ShortcutsManager is not yet initialised! ' )
else :
2019-06-05 19:42:39 +00:00
2020-03-25 21:15:57 +00:00
return ShortcutsManager . my_instance
2019-06-05 19:42:39 +00:00
2020-04-29 21:44:12 +00:00
def GetCommand ( self , shortcuts_names : typing . Iterable [ str ] , shortcut : Shortcut ) :
2019-06-05 19:42:39 +00:00
2020-02-19 21:48:36 +00:00
# process more specific shortcuts with higher priority
shortcuts_names = list ( shortcuts_names )
shortcuts_names . reverse ( )
2019-06-05 19:42:39 +00:00
for name in shortcuts_names :
2020-03-25 21:15:57 +00:00
if name in self . _names_to_shortcut_sets :
2019-06-05 19:42:39 +00:00
2020-03-25 21:15:57 +00:00
command = self . _names_to_shortcut_sets [ name ] . GetCommand ( shortcut )
2019-06-05 19:42:39 +00:00
if command is not None :
2020-02-19 21:48:36 +00:00
if HG . shortcut_report_mode :
2019-06-05 19:42:39 +00:00
2020-02-19 21:48:36 +00:00
HydrusData . ShowText ( ' Shortcut " {} " matched on " {} " set to " {} " command. ' . format ( shortcut . ToString ( ) , name , repr ( command ) ) )
2019-06-05 19:42:39 +00:00
return command
return None
2020-03-25 21:15:57 +00:00
def GetNamesToShortcuts ( self , simple_command : ClientData . ApplicationCommand ) :
2019-06-05 19:42:39 +00:00
names_to_shortcuts = { }
2020-03-25 21:15:57 +00:00
for ( name , shortcut_set ) in self . _names_to_shortcut_sets . items ( ) :
2019-06-05 19:42:39 +00:00
shortcuts = shortcut_set . GetShortcuts ( simple_command )
if len ( shortcuts ) > 0 :
names_to_shortcuts [ name ] = shortcuts
return names_to_shortcuts
2020-03-25 21:15:57 +00:00
def GetShortcutSets ( self ) - > typing . List [ ShortcutSet ] :
return list ( self . _names_to_shortcut_sets . values ( ) )
2020-04-29 21:44:12 +00:00
def SetShortcutSets ( self , shortcut_sets : typing . Iterable [ ShortcutSet ] ) :
2019-06-05 19:42:39 +00:00
2020-03-25 21:15:57 +00:00
self . _names_to_shortcut_sets = { shortcut_set . GetName ( ) : shortcut_set for shortcut_set in shortcut_sets }
2019-06-05 19:42:39 +00:00
2020-03-25 21:15:57 +00:00
self . shortcutsChanged . emit ( )
2019-06-05 19:42:39 +00:00
2020-03-25 21:15:57 +00:00
shortcuts_manager = ShortcutsManager . instance
shortcuts_manager_initialised = lambda : ShortcutsManager . my_instance is not None