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
2022-06-29 20:52:53 +00:00
from hydrus . core import HydrusExceptions
2020-04-22 21:00:35 +00:00
from hydrus . core import HydrusGlobals as HG
from hydrus . core import HydrusSerialisable
2023-04-19 20:38:13 +00:00
from hydrus . core import HydrusTime
2020-07-29 20:52:44 +00:00
2020-07-08 22:00:33 +00:00
from hydrus . client import ClientApplicationCommand as CAC
2020-04-22 21:00:35 +00:00
from hydrus . client import ClientData
from hydrus . client . gui import ClientGUICore as CGC
from hydrus . client . gui import ClientGUIFunctions
2022-08-10 21:32:27 +00:00
from hydrus . client . gui import QtPorting as QP
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
2022-03-02 21:44:08 +00:00
shortcut_press_type_str_lookup = {
SHORTCUT_PRESS_TYPE_PRESS : ' press ' ,
SHORTCUT_PRESS_TYPE_RELEASE : ' release ' ,
SHORTCUT_PRESS_TYPE_DOUBLE_CLICK : ' double ' ,
SHORTCUT_PRESS_TYPE_DRAG : ' drag '
}
2020-02-19 21:48:36 +00:00
SHORTCUT_MODIFIER_CTRL = 0
SHORTCUT_MODIFIER_ALT = 1
SHORTCUT_MODIFIER_SHIFT = 2
SHORTCUT_MODIFIER_KEYPAD = 3
SHORTCUT_MODIFIER_GROUP_SWITCH = 4
2020-11-11 22:20:16 +00:00
SHORTCUT_MODIFIER_META = 5 # This is 'Control' in macOS, which is for system level stuff. They use 'Command' for Control stuff, which is helpfully mapped to Control in Qt. Just name nonsense
2020-02-19 21:48:36 +00:00
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
2023-05-03 20:34:44 +00:00
SHORTCUT_KEY_SPECIAL_MEDIA_PLAY_PAUSE = 29
SHORTCUT_KEY_SPECIAL_MEDIA_PREVIOUS = 30
SHORTCUT_KEY_SPECIAL_MEDIA_NEXT = 31
SHORTCUT_KEY_SPECIAL_MEDIA_VOLUME_DOWN = 32
SHORTCUT_KEY_SPECIAL_MEDIA_VOLUME_UP = 33
SHORTCUT_KEY_SPECIAL_MEDIA_VOLUME_MUTE_UNMUTE = 32
2020-02-19 21:48:36 +00:00
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
2022-03-02 21:44:08 +00:00
special_key_shortcut_enum_lookup = {
QC . Qt . Key_Space : SHORTCUT_KEY_SPECIAL_SPACE ,
QC . Qt . Key_Backspace : SHORTCUT_KEY_SPECIAL_BACKSPACE ,
QC . Qt . Key_Tab : SHORTCUT_KEY_SPECIAL_TAB ,
QC . Qt . Key_Return : SHORTCUT_KEY_SPECIAL_RETURN ,
QC . Qt . Key_Enter : SHORTCUT_KEY_SPECIAL_ENTER ,
QC . Qt . Key_Pause : SHORTCUT_KEY_SPECIAL_PAUSE ,
QC . Qt . Key_Escape : SHORTCUT_KEY_SPECIAL_ESCAPE ,
QC . Qt . Key_Insert : SHORTCUT_KEY_SPECIAL_INSERT ,
QC . Qt . Key_Delete : SHORTCUT_KEY_SPECIAL_DELETE ,
QC . Qt . Key_Up : SHORTCUT_KEY_SPECIAL_UP ,
QC . Qt . Key_Down : SHORTCUT_KEY_SPECIAL_DOWN ,
QC . Qt . Key_Left : SHORTCUT_KEY_SPECIAL_LEFT ,
QC . Qt . Key_Right : SHORTCUT_KEY_SPECIAL_RIGHT ,
QC . Qt . Key_Home : SHORTCUT_KEY_SPECIAL_HOME ,
QC . Qt . Key_End : SHORTCUT_KEY_SPECIAL_END ,
QC . Qt . Key_PageUp : SHORTCUT_KEY_SPECIAL_PAGE_UP ,
QC . Qt . Key_PageDown : SHORTCUT_KEY_SPECIAL_PAGE_DOWN ,
QC . Qt . Key_F1 : SHORTCUT_KEY_SPECIAL_F1 ,
QC . Qt . Key_F2 : SHORTCUT_KEY_SPECIAL_F2 ,
QC . Qt . Key_F3 : SHORTCUT_KEY_SPECIAL_F3 ,
QC . Qt . Key_F4 : SHORTCUT_KEY_SPECIAL_F4 ,
QC . Qt . Key_F5 : SHORTCUT_KEY_SPECIAL_F5 ,
QC . Qt . Key_F6 : SHORTCUT_KEY_SPECIAL_F6 ,
QC . Qt . Key_F7 : SHORTCUT_KEY_SPECIAL_F7 ,
QC . Qt . Key_F8 : SHORTCUT_KEY_SPECIAL_F8 ,
QC . Qt . Key_F9 : SHORTCUT_KEY_SPECIAL_F9 ,
QC . Qt . Key_F10 : SHORTCUT_KEY_SPECIAL_F10 ,
QC . Qt . Key_F11 : SHORTCUT_KEY_SPECIAL_F11 ,
2023-05-03 20:34:44 +00:00
QC . Qt . Key_F12 : SHORTCUT_KEY_SPECIAL_F12 ,
QC . Qt . Key_MediaTogglePlayPause : SHORTCUT_KEY_SPECIAL_MEDIA_PLAY_PAUSE ,
QC . Qt . Key_MediaPlay : SHORTCUT_KEY_SPECIAL_MEDIA_PLAY_PAUSE ,
QC . Qt . Key_MediaPause : SHORTCUT_KEY_SPECIAL_MEDIA_PLAY_PAUSE ,
QC . Qt . Key_MediaPrevious : SHORTCUT_KEY_SPECIAL_MEDIA_PREVIOUS ,
QC . Qt . Key_MediaNext : SHORTCUT_KEY_SPECIAL_MEDIA_NEXT ,
QC . Qt . Key_VolumeDown : SHORTCUT_KEY_SPECIAL_MEDIA_VOLUME_DOWN ,
QC . Qt . Key_VolumeUp : SHORTCUT_KEY_SPECIAL_MEDIA_VOLUME_UP ,
QC . Qt . Key_VolumeMute : SHORTCUT_KEY_SPECIAL_MEDIA_VOLUME_MUTE_UNMUTE
2022-03-02 21:44:08 +00:00
}
2020-02-19 21:48:36 +00:00
2022-03-02 21:44:08 +00:00
special_key_shortcut_str_lookup = {
SHORTCUT_KEY_SPECIAL_SPACE : ' space ' ,
SHORTCUT_KEY_SPECIAL_BACKSPACE : ' backspace ' ,
SHORTCUT_KEY_SPECIAL_TAB : ' tab ' ,
SHORTCUT_KEY_SPECIAL_RETURN : ' return ' ,
SHORTCUT_KEY_SPECIAL_ENTER : ' enter ' ,
SHORTCUT_KEY_SPECIAL_PAUSE : ' pause ' ,
SHORTCUT_KEY_SPECIAL_ESCAPE : ' escape ' ,
SHORTCUT_KEY_SPECIAL_INSERT : ' insert ' ,
SHORTCUT_KEY_SPECIAL_DELETE : ' delete ' ,
SHORTCUT_KEY_SPECIAL_UP : ' up ' ,
SHORTCUT_KEY_SPECIAL_DOWN : ' down ' ,
SHORTCUT_KEY_SPECIAL_LEFT : ' left ' ,
SHORTCUT_KEY_SPECIAL_RIGHT : ' right ' ,
SHORTCUT_KEY_SPECIAL_HOME : ' home ' ,
SHORTCUT_KEY_SPECIAL_END : ' end ' ,
SHORTCUT_KEY_SPECIAL_PAGE_DOWN : ' page down ' ,
SHORTCUT_KEY_SPECIAL_PAGE_UP : ' page up ' ,
SHORTCUT_KEY_SPECIAL_F1 : ' f1 ' ,
SHORTCUT_KEY_SPECIAL_F2 : ' f2 ' ,
SHORTCUT_KEY_SPECIAL_F3 : ' f3 ' ,
SHORTCUT_KEY_SPECIAL_F4 : ' f4 ' ,
SHORTCUT_KEY_SPECIAL_F5 : ' f5 ' ,
SHORTCUT_KEY_SPECIAL_F6 : ' f6 ' ,
SHORTCUT_KEY_SPECIAL_F7 : ' f7 ' ,
SHORTCUT_KEY_SPECIAL_F8 : ' f8 ' ,
SHORTCUT_KEY_SPECIAL_F9 : ' f9 ' ,
SHORTCUT_KEY_SPECIAL_F10 : ' f10 ' ,
SHORTCUT_KEY_SPECIAL_F11 : ' f11 ' ,
2023-05-03 20:34:44 +00:00
SHORTCUT_KEY_SPECIAL_F12 : ' f12 ' ,
SHORTCUT_KEY_SPECIAL_MEDIA_PLAY_PAUSE : ' media: play/pause ' ,
SHORTCUT_KEY_SPECIAL_MEDIA_PREVIOUS : ' media: previous ' ,
SHORTCUT_KEY_SPECIAL_MEDIA_NEXT : ' media: next ' ,
SHORTCUT_KEY_SPECIAL_MEDIA_VOLUME_DOWN : ' volume down ' ,
SHORTCUT_KEY_SPECIAL_MEDIA_VOLUME_UP : ' volume up ' ,
SHORTCUT_KEY_SPECIAL_MEDIA_VOLUME_MUTE_UNMUTE : ' volume mute/unmute '
2022-03-02 21:44:08 +00:00
}
2020-02-19 21:48:36 +00:00
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
2023-05-03 20:34:44 +00:00
SHORTCUT_MOUSE_TASK = 9
2020-04-29 21:44:12 +00:00
2023-05-03 20:34:44 +00:00
SHORTCUT_MOUSE_CLICKS = { SHORTCUT_MOUSE_LEFT , SHORTCUT_MOUSE_MIDDLE , SHORTCUT_MOUSE_RIGHT , SHORTCUT_MOUSE_BACK , SHORTCUT_MOUSE_FORWARD , SHORTCUT_MOUSE_TASK }
2020-04-29 21:44:12 +00:00
2022-03-02 21:44:08 +00:00
qt_mouse_buttons_to_hydrus_mouse_buttons = {
QC . Qt . LeftButton : SHORTCUT_MOUSE_LEFT ,
QC . Qt . MiddleButton : SHORTCUT_MOUSE_MIDDLE ,
QC . Qt . RightButton : SHORTCUT_MOUSE_RIGHT ,
QC . Qt . BackButton : SHORTCUT_MOUSE_BACK ,
2023-05-03 20:34:44 +00:00
QC . Qt . ForwardButton : SHORTCUT_MOUSE_FORWARD ,
QC . Qt . TaskButton : SHORTCUT_MOUSE_TASK
2022-03-02 21:44:08 +00:00
}
2020-02-19 21:48:36 +00:00
2022-03-02 21:44:08 +00:00
shortcut_mouse_string_lookup = {
SHORTCUT_MOUSE_LEFT : ' left-click ' ,
SHORTCUT_MOUSE_RIGHT : ' right-click ' ,
SHORTCUT_MOUSE_MIDDLE : ' middle-click ' ,
SHORTCUT_MOUSE_BACK : ' back ' ,
SHORTCUT_MOUSE_FORWARD : ' forward ' ,
2023-05-03 20:34:44 +00:00
SHORTCUT_MOUSE_TASK : ' task button ' ,
2022-03-02 21:44:08 +00:00
SHORTCUT_MOUSE_SCROLL_UP : ' scroll up ' ,
SHORTCUT_MOUSE_SCROLL_DOWN : ' scroll down ' ,
SHORTCUT_MOUSE_SCROLL_LEFT : ' scroll left ' ,
SHORTCUT_MOUSE_SCROLL_RIGHT : ' scroll right '
}
2020-02-19 21:48:36 +00:00
2022-02-16 22:10:11 +00:00
def SetMouseLabels ( call_mouse_buttons_primary_secondary ) :
if call_mouse_buttons_primary_secondary :
shortcut_mouse_string_lookup [ SHORTCUT_MOUSE_LEFT ] = ' primary-click '
shortcut_mouse_string_lookup [ SHORTCUT_MOUSE_RIGHT ] = ' secondary-click '
else :
shortcut_mouse_string_lookup [ SHORTCUT_MOUSE_LEFT ] = ' left-click '
shortcut_mouse_string_lookup [ SHORTCUT_MOUSE_RIGHT ] = ' right-click '
2023-11-22 22:29:14 +00:00
2022-03-02 21:44:08 +00:00
shortcut_names_to_pretty_names = {
' global ' : ' global ' ,
' main_gui ' : ' the main window ' ,
' tags_autocomplete ' : ' tag autocomplete ' ,
' media ' : ' media actions, either thumbnails or the viewer ' ,
2023-10-18 20:31:50 +00:00
' thumbnails ' : ' thumbnails ' ,
2022-03-02 21:44:08 +00:00
' media_viewer ' : ' media viewers - all ' ,
' media_viewer_browser ' : ' media viewers - \' normal \' browser ' ,
' archive_delete_filter ' : ' media viewers - archive/delete filter ' ,
' duplicate_filter ' : ' media viewers - duplicate filter ' ,
' preview_media_window ' : ' the actual media in a preview window (mouse only) ' ,
2023-10-18 20:31:50 +00:00
' media_viewer_media_window ' : ' the actual media in a media viewer (mouse only) ' ,
2022-03-02 21:44:08 +00:00
}
2020-05-06 21:31:41 +00:00
2021-01-27 22:14:03 +00:00
shortcut_names_sorted = [
' global ' ,
' main_gui ' ,
' tags_autocomplete ' ,
' media ' ,
2023-10-18 20:31:50 +00:00
' thumbnails ' ,
2021-01-27 22:14:03 +00:00
' media_viewer ' ,
' media_viewer_browser ' ,
' archive_delete_filter ' ,
' duplicate_filter ' ,
' preview_media_window ' ,
' media_viewer_media_window '
]
2020-05-06 21:31:41 +00:00
2022-03-02 21:44:08 +00:00
shortcut_names_to_descriptions = {
' global ' : ' Actions for the whole program. Should work in the main gui or a media viewer. ' ,
' archive_delete_filter ' : ' Navigation actions for the media viewer during an archive/delete filter. Mouse shortcuts should work. ' ,
' duplicate_filter ' : ' Navigation actions for the media viewer during a duplicate filter. Mouse shortcuts should work. ' ,
' media ' : ' Actions to alter metadata for media in the media viewer or the thumbnail grid. ' ,
2023-10-18 20:31:50 +00:00
' thumbnails ' : ' Actions that interact with the grid of thumbnails in a normal file page. ' ,
2022-03-02 21:44:08 +00:00
' main_gui ' : ' Actions to control pages in the main window of the program. ' ,
' tags_autocomplete ' : ' Actions to control tag autocomplete when its input text box is focused. ' ,
' media_viewer_browser ' : ' Navigation actions for the regular browsable media viewer. ' ,
' media_viewer ' : ' Zoom and pan and player actions for any media viewer. ' ,
' media_viewer_media_window ' : ' Actions for any video or audio player in a media viewer window. Mouse only! ' ,
' preview_media_window ' : ' Actions for any video or audio player in a preview window. Mouse only! '
}
2020-02-19 21:48:36 +00:00
# shortcut commands
2023-10-18 20:31:50 +00:00
SHORTCUTS_RESERVED_NAMES = [ ' global ' , ' archive_delete_filter ' , ' duplicate_filter ' , ' media ' , ' thumbnails ' , ' tags_autocomplete ' , ' main_gui ' , ' media_viewer_browser ' , ' media_viewer ' , ' media_viewer_media_window ' , ' preview_media_window ' ]
2020-02-19 21:48:36 +00:00
2023-05-24 20:44:12 +00:00
SHORTCUTS_GLOBAL_ACTIONS = [
CAC . SIMPLE_GLOBAL_AUDIO_MUTE ,
CAC . SIMPLE_GLOBAL_AUDIO_UNMUTE ,
CAC . SIMPLE_GLOBAL_AUDIO_MUTE_FLIP ,
CAC . SIMPLE_EXIT_APPLICATION ,
CAC . SIMPLE_EXIT_APPLICATION_FORCE_MAINTENANCE ,
CAC . SIMPLE_RESTART_APPLICATION ,
CAC . SIMPLE_HIDE_TO_SYSTEM_TRAY ,
CAC . SIMPLE_GLOBAL_PROFILE_MODE_FLIP ,
CAC . SIMPLE_GLOBAL_FORCE_ANIMATION_SCANBAR_SHOW
]
SHORTCUTS_MEDIA_ACTIONS = [
CAC . SIMPLE_MANAGE_FILE_TAGS ,
CAC . SIMPLE_MANAGE_FILE_RATINGS ,
CAC . SIMPLE_MANAGE_FILE_URLS ,
CAC . SIMPLE_MANAGE_FILE_NOTES ,
CAC . SIMPLE_MANAGE_FILE_TIMESTAMPS ,
CAC . SIMPLE_ARCHIVE_FILE ,
CAC . SIMPLE_INBOX_FILE ,
CAC . SIMPLE_DELETE_FILE ,
CAC . SIMPLE_UNDELETE_FILE ,
CAC . SIMPLE_EXPORT_FILES ,
CAC . SIMPLE_EXPORT_FILES_QUICK_AUTO_EXPORT ,
CAC . SIMPLE_REMOVE_FILE_FROM_VIEW ,
CAC . SIMPLE_OPEN_FILE_IN_EXTERNAL_PROGRAM ,
CAC . SIMPLE_OPEN_FILE_IN_FILE_EXPLORER ,
CAC . SIMPLE_OPEN_SELECTION_IN_NEW_PAGE ,
CAC . SIMPLE_LAUNCH_THE_ARCHIVE_DELETE_FILTER ,
CAC . SIMPLE_COPY_BMP ,
CAC . SIMPLE_COPY_BMP_OR_FILE_IF_NOT_BMPABLE ,
2023-07-05 20:52:58 +00:00
CAC . SIMPLE_COPY_LITTLE_BMP ,
2023-05-24 20:44:12 +00:00
CAC . SIMPLE_COPY_FILE ,
CAC . SIMPLE_COPY_PATH ,
CAC . SIMPLE_COPY_SHA256_HASH ,
CAC . SIMPLE_COPY_MD5_HASH ,
CAC . SIMPLE_COPY_SHA1_HASH ,
CAC . SIMPLE_COPY_SHA512_HASH ,
CAC . SIMPLE_GET_SIMILAR_TO_EXACT ,
CAC . SIMPLE_GET_SIMILAR_TO_VERY_SIMILAR ,
CAC . SIMPLE_GET_SIMILAR_TO_SIMILAR ,
CAC . SIMPLE_GET_SIMILAR_TO_SPECULATIVE ,
CAC . SIMPLE_DUPLICATE_MEDIA_SET_ALTERNATE ,
CAC . SIMPLE_DUPLICATE_MEDIA_SET_ALTERNATE_COLLECTIONS ,
CAC . SIMPLE_DUPLICATE_MEDIA_SET_CUSTOM ,
CAC . SIMPLE_DUPLICATE_MEDIA_SET_FOCUSED_BETTER ,
CAC . SIMPLE_DUPLICATE_MEDIA_SET_FOCUSED_KING ,
CAC . SIMPLE_DUPLICATE_MEDIA_SET_SAME_QUALITY ,
CAC . SIMPLE_DUPLICATE_MEDIA_SET_POTENTIAL ,
CAC . SIMPLE_SHOW_DUPLICATES ,
CAC . SIMPLE_OPEN_KNOWN_URL
]
SHORTCUTS_MEDIA_VIEWER_ACTIONS = [
CAC . SIMPLE_PAUSE_MEDIA ,
CAC . SIMPLE_PAUSE_PLAY_MEDIA ,
CAC . SIMPLE_MEDIA_SEEK_DELTA ,
CAC . SIMPLE_MOVE_ANIMATION_TO_PREVIOUS_FRAME ,
CAC . SIMPLE_MOVE_ANIMATION_TO_NEXT_FRAME ,
CAC . SIMPLE_SWITCH_BETWEEN_FULLSCREEN_BORDERLESS_AND_REGULAR_FRAMED_WINDOW ,
CAC . SIMPLE_PAN_UP ,
CAC . SIMPLE_PAN_DOWN ,
CAC . SIMPLE_PAN_LEFT ,
CAC . SIMPLE_PAN_RIGHT ,
CAC . SIMPLE_PAN_TOP_EDGE ,
CAC . SIMPLE_PAN_BOTTOM_EDGE ,
CAC . SIMPLE_PAN_LEFT_EDGE ,
CAC . SIMPLE_PAN_RIGHT_EDGE ,
CAC . SIMPLE_PAN_VERTICAL_CENTER ,
CAC . SIMPLE_PAN_HORIZONTAL_CENTER ,
CAC . SIMPLE_ZOOM_IN ,
CAC . SIMPLE_ZOOM_OUT ,
CAC . SIMPLE_SWITCH_BETWEEN_100_PERCENT_AND_CANVAS_ZOOM ,
CAC . SIMPLE_SWITCH_BETWEEN_100_PERCENT_AND_MAX_ZOOM ,
CAC . SIMPLE_SWITCH_BETWEEN_CANVAS_AND_MAX_ZOOM ,
CAC . SIMPLE_ZOOM_100 ,
CAC . SIMPLE_ZOOM_CANVAS ,
CAC . SIMPLE_ZOOM_DEFAULT ,
CAC . SIMPLE_ZOOM_MAX ,
CAC . SIMPLE_FLIP_DARKMODE ,
CAC . SIMPLE_CLOSE_MEDIA_VIEWER
]
SHORTCUTS_MEDIA_VIEWER_BROWSER_ACTIONS = [
CAC . SIMPLE_VIEW_NEXT ,
CAC . SIMPLE_VIEW_FIRST ,
CAC . SIMPLE_VIEW_LAST ,
CAC . SIMPLE_VIEW_PREVIOUS ,
CAC . SIMPLE_PAUSE_PLAY_SLIDESHOW ,
CAC . SIMPLE_SHOW_MENU ,
CAC . SIMPLE_CLOSE_MEDIA_VIEWER
]
SHORTCUTS_MAIN_GUI_ACTIONS = [
CAC . SIMPLE_REFRESH ,
CAC . SIMPLE_REFRESH_ALL_PAGES ,
CAC . SIMPLE_REFRESH_PAGE_OF_PAGES_PAGES ,
CAC . SIMPLE_NEW_PAGE ,
CAC . SIMPLE_NEW_PAGE_OF_PAGES ,
CAC . SIMPLE_NEW_DUPLICATE_FILTER_PAGE ,
CAC . SIMPLE_NEW_GALLERY_DOWNLOADER_PAGE ,
CAC . SIMPLE_NEW_URL_DOWNLOADER_PAGE ,
CAC . SIMPLE_NEW_SIMPLE_DOWNLOADER_PAGE ,
CAC . SIMPLE_NEW_WATCHER_DOWNLOADER_PAGE ,
CAC . SIMPLE_SET_MEDIA_FOCUS ,
CAC . SIMPLE_SHOW_HIDE_SPLITTERS ,
CAC . SIMPLE_SET_SEARCH_FOCUS ,
CAC . SIMPLE_UNCLOSE_PAGE ,
CAC . SIMPLE_CLOSE_PAGE ,
CAC . SIMPLE_REDO ,
CAC . SIMPLE_UNDO ,
CAC . SIMPLE_FLIP_DARKMODE ,
CAC . SIMPLE_RUN_ALL_EXPORT_FOLDERS ,
CAC . SIMPLE_CHECK_ALL_IMPORT_FOLDERS ,
CAC . SIMPLE_FLIP_DEBUG_FORCE_IDLE_MODE_DO_NOT_SET_THIS ,
CAC . SIMPLE_SHOW_AND_FOCUS_MANAGE_TAGS_FAVOURITE_TAGS ,
CAC . SIMPLE_SHOW_AND_FOCUS_MANAGE_TAGS_RELATED_TAGS ,
CAC . SIMPLE_REFRESH_RELATED_TAGS ,
CAC . SIMPLE_SHOW_AND_FOCUS_MANAGE_TAGS_FILE_LOOKUP_SCRIPT_TAGS ,
CAC . SIMPLE_SHOW_AND_FOCUS_MANAGE_TAGS_RECENT_TAGS ,
CAC . SIMPLE_FOCUS_MEDIA_VIEWER ,
CAC . SIMPLE_MOVE_PAGES_SELECTION_LEFT ,
CAC . SIMPLE_MOVE_PAGES_SELECTION_RIGHT ,
CAC . SIMPLE_MOVE_PAGES_SELECTION_HOME ,
CAC . SIMPLE_MOVE_PAGES_SELECTION_END ,
CAC . SIMPLE_OPEN_COMMAND_PALETTE
]
SHORTCUTS_TAGS_AUTOCOMPLETE_ACTIONS = [
CAC . SIMPLE_SYNCHRONISED_WAIT_SWITCH ,
CAC . SIMPLE_AUTOCOMPLETE_FORCE_FETCH ,
CAC . SIMPLE_AUTOCOMPLETE_IME_MODE
]
SHORTCUTS_DUPLICATE_FILTER_ACTIONS = [
CAC . SIMPLE_DUPLICATE_FILTER_THIS_IS_BETTER_AND_DELETE_OTHER ,
CAC . SIMPLE_DUPLICATE_FILTER_THIS_IS_BETTER_BUT_KEEP_BOTH ,
CAC . SIMPLE_DUPLICATE_FILTER_EXACTLY_THE_SAME ,
CAC . SIMPLE_DUPLICATE_FILTER_ALTERNATES ,
CAC . SIMPLE_DUPLICATE_FILTER_FALSE_POSITIVE ,
CAC . SIMPLE_DUPLICATE_FILTER_CUSTOM_ACTION ,
CAC . SIMPLE_DUPLICATE_FILTER_SKIP ,
CAC . SIMPLE_DUPLICATE_FILTER_BACK ,
CAC . SIMPLE_CLOSE_MEDIA_VIEWER ,
CAC . SIMPLE_VIEW_NEXT
]
SHORTCUTS_ARCHIVE_DELETE_FILTER_ACTIONS = [
CAC . SIMPLE_ARCHIVE_DELETE_FILTER_KEEP ,
CAC . SIMPLE_ARCHIVE_DELETE_FILTER_DELETE ,
CAC . SIMPLE_ARCHIVE_DELETE_FILTER_SKIP ,
CAC . SIMPLE_ARCHIVE_DELETE_FILTER_BACK ,
CAC . SIMPLE_CLOSE_MEDIA_VIEWER
]
SHORTCUTS_MEDIA_VIEWER_VIDEO_AUDIO_PLAYER_ACTIONS = [
CAC . SIMPLE_PAUSE_MEDIA ,
CAC . SIMPLE_PAUSE_PLAY_MEDIA ,
CAC . SIMPLE_MEDIA_SEEK_DELTA ,
CAC . SIMPLE_OPEN_FILE_IN_EXTERNAL_PROGRAM ,
CAC . SIMPLE_OPEN_FILE_IN_FILE_EXPLORER ,
CAC . SIMPLE_CLOSE_MEDIA_VIEWER
]
SHORTCUTS_PREVIEW_VIDEO_AUDIO_PLAYER_ACTIONS = [
CAC . SIMPLE_PAUSE_MEDIA ,
CAC . SIMPLE_PAUSE_PLAY_MEDIA ,
CAC . SIMPLE_MEDIA_SEEK_DELTA ,
CAC . SIMPLE_OPEN_FILE_IN_EXTERNAL_PROGRAM ,
CAC . SIMPLE_OPEN_FILE_IN_FILE_EXPLORER ,
CAC . SIMPLE_LAUNCH_MEDIA_VIEWER
]
2020-02-19 21:48:36 +00:00
2023-10-18 20:31:50 +00:00
SHORTCUTS_THUMBNAILS_ACTIONS = [
CAC . SIMPLE_LAUNCH_MEDIA_VIEWER ,
CAC . SIMPLE_LAUNCH_THE_ARCHIVE_DELETE_FILTER ,
CAC . SIMPLE_OPEN_FILE_IN_EXTERNAL_PROGRAM ,
CAC . SIMPLE_OPEN_FILE_IN_FILE_EXPLORER ,
CAC . SIMPLE_MOVE_THUMBNAIL_FOCUS ,
CAC . SIMPLE_SELECT_FILES
]
2022-03-02 21:44:08 +00:00
simple_shortcut_name_to_action_lookup = {
' global ' : SHORTCUTS_GLOBAL_ACTIONS ,
' media ' : SHORTCUTS_MEDIA_ACTIONS ,
' media_viewer ' : SHORTCUTS_MEDIA_VIEWER_ACTIONS ,
' media_viewer_browser ' : SHORTCUTS_MEDIA_VIEWER_BROWSER_ACTIONS ,
' main_gui ' : SHORTCUTS_MAIN_GUI_ACTIONS ,
' tags_autocomplete ' : SHORTCUTS_TAGS_AUTOCOMPLETE_ACTIONS ,
' duplicate_filter ' : SHORTCUTS_DUPLICATE_FILTER_ACTIONS + SHORTCUTS_MEDIA_ACTIONS + SHORTCUTS_MEDIA_VIEWER_ACTIONS ,
' archive_delete_filter ' : SHORTCUTS_ARCHIVE_DELETE_FILTER_ACTIONS ,
' media_viewer_media_window ' : SHORTCUTS_MEDIA_VIEWER_VIDEO_AUDIO_PLAYER_ACTIONS ,
' preview_media_window ' : SHORTCUTS_PREVIEW_VIDEO_AUDIO_PLAYER_ACTIONS ,
2023-10-18 20:31:50 +00:00
' thumbnails ' : SHORTCUTS_THUMBNAILS_ACTIONS ,
2022-03-02 21:44:08 +00:00
' custom ' : SHORTCUTS_MEDIA_ACTIONS + SHORTCUTS_MEDIA_VIEWER_ACTIONS
}
2020-02-19 21:48:36 +00:00
2022-09-07 21:16:25 +00:00
simple_shortcut_name_to_action_lookup = { key : HydrusData . DedupeList ( value ) for ( key , value ) in simple_shortcut_name_to_action_lookup . items ( ) }
2022-02-02 22:14:01 +00:00
CUMULATIVE_MOUSEWARP_MANHATTAN_LENGTH = 0
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 :
2022-03-02 21:44:08 +00:00
key_ord = int ( key_qt )
2019-12-11 23:18:37 +00:00
try :
2019-11-14 03:56:30 +00:00
2021-03-03 22:23:35 +00:00
if key_ord == 0 :
raise Exception ( ' Shortcut caught a null key ' ) # I think this is like 'release alt' or some weird code
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
2020-11-11 22:20:16 +00:00
if event . modifiers ( ) & QC . Qt . ControlModifier :
2019-11-14 03:56:30 +00:00
2020-11-11 22:20:16 +00:00
modifiers . append ( SHORTCUT_MODIFIER_CTRL )
2019-11-14 03:56:30 +00:00
2020-11-11 22:20:16 +00:00
if event . modifiers ( ) & QC . Qt . MetaModifier :
2018-05-16 20:09:50 +00:00
2020-11-11 22:20:16 +00:00
modifiers . append ( SHORTCUT_MODIFIER_META )
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
2022-03-02 21:44:08 +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
elif event . modifiers ( ) & QC . Qt . MetaModifier :
modifier = QC . Qt . MetaModifier
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 )
2023-05-03 20:34:44 +00:00
2020-11-11 22:20:16 +00:00
GLOBAL_MOUSE_SCROLL_DELTA_FOR_TRACKPADS = 0
2023-05-03 20:34:44 +00:00
ONE_TICK_ON_A_NORMAL_MOUSE_IN_EIGHTS_OF_A_DEGREE = 15 * 8 # fifteen degrees, in eighths of a degree
2020-11-11 22:20:16 +00:00
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-11-11 22:20:16 +00:00
angle_delta_point = event . angleDelta ( )
if angle_delta_point is None :
return None
angle_delta = angle_delta_point . y ( )
2023-05-03 20:34:44 +00:00
# we used to do QP.WheelEventIsSynthesised here, but it seems some normal mice produce a billion small wheel events for smoothscroll gubbins or something, lfg
# so let's just try this tech for everyone
if abs ( angle_delta ) < ONE_TICK_ON_A_NORMAL_MOUSE_IN_EIGHTS_OF_A_DEGREE :
# likely using a trackpad to generate artificial wheel events
global GLOBAL_MOUSE_SCROLL_DELTA_FOR_TRACKPADS
GLOBAL_MOUSE_SCROLL_DELTA_FOR_TRACKPADS + = angle_delta
2020-11-11 22:20:16 +00:00
2023-05-03 20:34:44 +00:00
if abs ( GLOBAL_MOUSE_SCROLL_DELTA_FOR_TRACKPADS ) > ONE_TICK_ON_A_NORMAL_MOUSE_IN_EIGHTS_OF_A_DEGREE :
2020-11-11 22:20:16 +00:00
2023-05-03 20:34:44 +00:00
angle_delta = GLOBAL_MOUSE_SCROLL_DELTA_FOR_TRACKPADS
2020-11-11 22:20:16 +00:00
2023-05-03 20:34:44 +00:00
GLOBAL_MOUSE_SCROLL_DELTA_FOR_TRACKPADS = 0
2020-11-11 22:20:16 +00:00
2023-05-03 20:34:44 +00:00
else :
2020-11-11 22:20:16 +00:00
2023-05-03 20:34:44 +00:00
return None
2020-11-11 22:20:16 +00:00
if angle_delta > 0 :
2020-02-12 22:50:37 +00:00
2020-02-19 21:48:36 +00:00
key = SHORTCUT_MOUSE_SCROLL_UP
2020-02-12 22:50:37 +00:00
2020-11-11 22:20:16 +00:00
elif angle_delta < 0 :
2020-02-12 22:50:37 +00:00
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
2020-11-11 22:20:16 +00:00
if event . modifiers ( ) & QC . Qt . MetaModifier :
modifiers . append ( SHORTCUT_MODIFIER_META )
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-07-08 22:00:33 +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 . Collection [ 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-07-15 20:52:09 +00:00
do_focus_test = False # lmao, why this here? I guess it got turned off
2020-05-27 21:27:52 +00:00
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-11-11 22:20:16 +00:00
if SHORTCUT_MODIFIER_META in self . modifiers :
components . append ( ' control ' )
2020-02-19 21:48:36 +00:00
if SHORTCUT_MODIFIER_CTRL in self . modifiers :
2018-05-16 20:09:50 +00:00
2020-11-11 22:20:16 +00:00
if HC . PLATFORM_MACOS :
components . append ( ' command ' )
else :
components . append ( ' ctrl ' )
2018-05-16 20:09:50 +00:00
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
2022-06-29 20:52:53 +00:00
def TryToIncrementKey ( self ) :
if self . shortcut_type == SHORTCUT_TYPE_KEYBOARD_CHARACTER :
new_shortcut_key = self . shortcut_key + 1
if ClientData . OrdIsAlphaLower ( new_shortcut_key ) or ClientData . OrdIsNumber ( new_shortcut_key ) :
self . shortcut_key = new_shortcut_key
return
elif self . shortcut_type == SHORTCUT_TYPE_KEYBOARD_SPECIAL :
new_shortcut_key = self . shortcut_key + 1
if new_shortcut_key in special_key_shortcut_str_lookup :
self . shortcut_key = new_shortcut_key
return
raise HydrusExceptions . VetoException ( ' Sorry, cannot increment that shortcut! ' )
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 :
2021-08-25 21:59:05 +00:00
command = CAC . ApplicationCommand . STATICCreateSimpleCommand ( data )
2018-05-16 20:09:50 +00:00
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
2020-07-08 22:00:33 +00:00
command = CAC . ApplicationCommand ( CAC . APPLICATION_COMMAND_TYPE_CONTENT , ( service_key , content_type , action , value ) )
2018-05-16 20:09:50 +00:00
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 )
2021-01-27 22:14:03 +00:00
def DeleteShortcut ( self , shortcut ) :
if shortcut in self . _shortcuts_to_commands :
del self . _shortcuts_to_commands [ shortcut ]
2018-05-16 20:09:50 +00:00
def GetCommand ( self , shortcut ) :
if shortcut in self . _shortcuts_to_commands :
return self . _shortcuts_to_commands [ shortcut ]
else :
return None
2021-08-25 21:59:05 +00:00
def GetShortcuts ( self , simple_action : int ) :
2019-06-05 19:42:39 +00:00
shortcuts = [ ]
for ( shortcut , command ) in self . _shortcuts_to_commands . items ( ) :
2021-08-25 21:59:05 +00:00
if command . IsSimpleCommand ( ) and command . GetSimpleAction ( ) == simple_action :
2019-06-05 19:42:39 +00:00
shortcuts . append ( shortcut )
return shortcuts
2022-06-29 20:52:53 +00:00
def GetShortcutsAndCommands ( self ) :
return list ( self )
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
2021-01-27 22:14:03 +00:00
def __init__ ( self , parent : QW . QWidget , initial_shortcuts_names : typing . Collection [ str ] , alternate_filter_target = 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
2022-01-26 21:57:04 +00:00
self . _last_click_down_position = QC . QPoint ( 0 , 0 )
2021-01-27 22:14:03 +00:00
filter_target = parent
if alternate_filter_target is not None :
2017-03-29 19:39:34 +00:00
2021-01-27 22:14:03 +00:00
filter_target = alternate_filter_target
2017-03-29 19:39:34 +00:00
2021-01-27 22:14:03 +00:00
self . _filter_target = filter_target
2017-03-29 19:39:34 +00:00
self . _parent = parent
2021-01-27 22:14:03 +00:00
self . _filter_target . 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
2022-08-17 20:54:59 +00:00
self . _activating_wait_job = None
2020-03-04 22:12:53 +00:00
self . _frame_activated_time = 0.0
2022-08-17 20:54:59 +00:00
self . _parent_currently_activated = True
2020-03-04 22:12:53 +00:00
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 :
2021-01-27 22:14:03 +00:00
message = ' Shortcut " {} " matched to command " {} " on {} . ' . format ( shortcut . ToString ( ) , command . ToString ( ) , repr ( self . _parent ) )
2018-03-22 00:03:33 +00:00
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
2023-06-21 19:50:13 +00:00
try :
2019-11-14 03:56:30 +00:00
2023-06-21 19:50:13 +00:00
if event . type ( ) == QC . QEvent . KeyPress :
2018-03-22 00:03:33 +00:00
2023-06-21 19:50:13 +00:00
i_should_catch_shortcut_event = IShouldCatchShortcutEvent ( self . _filter_target , watched , event = event )
shortcut = ConvertKeyEventToShortcut ( event )
if shortcut is not None :
2018-03-22 00:03:33 +00:00
2023-06-21 19:50:13 +00:00
if HG . shortcut_report_mode :
2019-11-14 03:56:30 +00:00
2023-06-21 19:50:13 +00:00
message = ' Key shortcut " {} " passing through {} . ' . format ( shortcut . ToString ( ) , repr ( self . _parent ) )
2019-11-14 03:56:30 +00:00
2023-06-21 19:50:13 +00:00
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. '
2019-11-14 03:56:30 +00:00
2023-06-21 19:50:13 +00:00
HydrusData . ShowText ( message )
2019-11-14 03:56:30 +00:00
2018-03-22 00:03:33 +00:00
2023-06-21 19:50:13 +00:00
if i_should_catch_shortcut_event :
2019-11-14 03:56:30 +00:00
2023-06-21 19:50:13 +00:00
shortcut_processed = self . _ProcessShortcut ( shortcut )
2020-02-12 22:50:37 +00:00
2023-06-21 19:50:13 +00:00
if shortcut_processed :
event . accept ( )
return True
2019-11-14 03:56:30 +00:00
2018-03-14 21:01:02 +00:00
2023-06-21 19:50:13 +00:00
elif self . _catch_mouse :
2022-03-02 21:44:08 +00:00
2023-06-21 19:50:13 +00:00
if event . type ( ) in ( QC . QEvent . MouseButtonPress , QC . QEvent . MouseButtonRelease , QC . QEvent . MouseButtonDblClick , QC . QEvent . Wheel ) :
2022-01-26 21:57:04 +00:00
2023-06-21 19:50:13 +00:00
global CUMULATIVE_MOUSEWARP_MANHATTAN_LENGTH
2020-03-04 22:12:53 +00:00
2023-06-21 19:50:13 +00:00
if event . type ( ) == QC . QEvent . MouseButtonPress :
2020-03-04 22:12:53 +00:00
2023-06-21 19:50:13 +00:00
self . _last_click_down_position = event . globalPosition ( ) . toPoint ( )
2020-03-04 22:12:53 +00:00
2023-06-21 19:50:13 +00:00
CUMULATIVE_MOUSEWARP_MANHATTAN_LENGTH = 0
2020-03-04 22:12:53 +00:00
2023-06-21 19:50:13 +00:00
#if event.type() != QC.QEvent.Wheel and self._ignore_activating_mouse_click and not HydrusTime.TimeHasPassedPrecise( self._frame_activated_time + 0.017 ):
if event . type ( ) != QC . QEvent . Wheel and self . _ignore_activating_mouse_click and not self . _parent_currently_activated :
if event . type ( ) == QC . QEvent . MouseButtonRelease and self . _activating_wait_job is not None :
# first completed click in the time window sets us active instantly
self . _activating_wait_job . Cancel ( )
self . _parent_currently_activated = True
2022-01-26 21:57:04 +00:00
return False
2023-06-21 19:50:13 +00:00
if event . type ( ) == QC . QEvent . MouseButtonRelease :
2020-02-12 22:50:37 +00:00
2023-06-21 19:50:13 +00:00
release_press_pos = event . globalPosition ( ) . toPoint ( )
2020-02-12 22:50:37 +00:00
2023-06-21 19:50:13 +00:00
delta = release_press_pos - self . _last_click_down_position
approx_distance = delta . manhattanLength ( ) + CUMULATIVE_MOUSEWARP_MANHATTAN_LENGTH
# if mouse release is some distance from mouse down (i.e. we are ending a drag), then don't fire off a release command
if approx_distance > 20 :
2020-02-12 22:50:37 +00:00
2023-06-21 19:50:13 +00:00
return False
2020-02-12 22:50:37 +00:00
2023-06-21 19:50:13 +00:00
i_should_catch_shortcut_event = IShouldCatchShortcutEvent ( self . _filter_target , watched , event = event )
shortcut = ConvertMouseEventToShortcut ( event )
if shortcut is not None :
2020-02-12 22:50:37 +00:00
2023-06-21 19:50:13 +00:00
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 )
2020-02-12 22:50:37 +00:00
2023-06-21 19:50:13 +00:00
if i_should_catch_shortcut_event :
2020-02-12 22:50:37 +00:00
2023-06-21 19:50:13 +00:00
shortcut_processed = self . _ProcessShortcut ( shortcut )
2020-02-12 22:50:37 +00:00
2023-06-21 19:50:13 +00:00
if shortcut_processed :
event . accept ( )
return True
2020-02-12 22:50:37 +00:00
2023-06-21 19:50:13 +00:00
except Exception as e :
HydrusData . ShowException ( e )
return True
2020-02-12 22:50:37 +00:00
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
2022-08-17 20:54:59 +00:00
def FrameActivated ( self ) :
def do_it ( ) :
self . _parent_currently_activated = True
self . _activating_wait_job = HG . client_controller . CallLater ( 0.2 , do_it )
def FrameDeactivated ( self ) :
if self . _activating_wait_job is not None :
self . _activating_wait_job . Cancel ( )
self . _parent_currently_activated = False
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 )
2022-08-17 20:54:59 +00:00
2020-03-04 22:12:53 +00:00
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 ) :
2023-06-21 19:50:13 +00:00
try :
if event . type ( ) == QC . QEvent . WindowActivate :
self . _shortcuts_handler . FrameActivated ( )
elif event . type ( ) == QC . QEvent . WindowDeactivate :
self . _shortcuts_handler . FrameDeactivated ( )
2020-03-04 22:12:53 +00:00
2023-06-21 19:50:13 +00:00
except Exception as e :
2020-03-04 22:12:53 +00:00
2023-06-21 19:50:13 +00:00
HydrusData . ShowException ( e )
2022-08-17 20:54:59 +00:00
2023-06-21 19:50:13 +00:00
return True
2022-08-17 20:54:59 +00:00
2020-03-04 22:12:53 +00:00
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-07-08 22:00:33 +00:00
def GetNamesToShortcuts ( self , simple_command : int ) :
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
2022-03-02 21:44:08 +00:00
def shortcuts_manager_initialised ( ) :
return ShortcutsManager . my_instance is not None