2025-07-23 21:20:34 +08:00
// SPDX-FileCopyrightText: 2022 Gary Wang <wzc782970009@gmail.com>
//
// SPDX-License-Identifier: MIT
# include "actionmanager.h"
2026-07-12 21:00:11 +08:00
# include "settings.h"
2025-07-23 21:20:34 +08:00
# include "mainwindow.h"
2026-07-12 21:00:11 +08:00
# include "playlistmanager.h"
# include "graphicsview.h"
# include "graphicsscene.h"
2025-07-23 21:20:34 +08:00
# include <QGuiApplication>
# include <QSvgRenderer>
# include <QPainter>
2026-07-03 15:37:20 +08:00
static QIcon loadHidpiIcon ( const QString & resp , QSize sz = QSize ( 32 , 32 ) )
2025-07-23 21:20:34 +08:00
{
QSvgRenderer r ( resp ) ;
QPixmap pm = QPixmap ( sz * qApp - > devicePixelRatio ( ) ) ;
pm . fill ( Qt : : transparent ) ;
QPainter p ( & pm ) ;
r . render ( & p ) ;
pm . setDevicePixelRatio ( qApp - > devicePixelRatio ( ) ) ;
return QIcon ( pm ) ;
}
void ActionManager : : setupAction ( MainWindow * mainWindow )
{
2026-07-03 15:12:15 +08:00
// region: Action and Its Icon Creation
2026-07-12 21:00:11 +08:00
// Declare some convenient macros and lambdas.
2026-07-03 15:12:15 +08:00
# define ICON_NAME(name)\
QStringLiteral ( " :/icons/ " # name " .svg " )
# define ACTION_NAME(s) QStringLiteral(STRIFY(s))
# define STRIFY(s) #s
2025-07-23 21:20:34 +08:00
auto create_action = [ ] ( QWidget * w , QAction * * a , QString i , QString an , bool iconFromTheme = false ) {
* a = new QAction ( w ) ;
if ( ! i . isNull ( ) )
2026-07-03 15:37:20 +08:00
( * a ) - > setIcon ( iconFromTheme ? QIcon : : fromTheme ( i ) : loadHidpiIcon ( i ) ) ;
2025-07-23 21:20:34 +08:00
( * a ) - > setObjectName ( an ) ;
w - > addAction ( * a ) ;
} ;
2026-07-03 15:12:15 +08:00
2026-07-12 21:00:11 +08:00
/// This macro for register normal action.
# define CREATE_NEW_ACTION(w, a) create_action(w, &a, QString(), ACTION_NAME(a))
/// This macro for register Qt theme action.
# define CREATE_NEW_THEMEICON_ACTION(w, a, i) create_action(w, &a, QLatin1String(STRIFY(i)), ACTION_NAME(a), true)
/// This macro for register action with our custom icon.
# define CREATE_NEW_ICON_ACTION(w, a, i) create_action(w, &a, ICON_NAME(i), ACTION_NAME(a), false)
// TODO: We may replace all Qt theme icons to our custom icon for visual consistency.
// TODO: Some icons are invalid.
CREATE_NEW_THEMEICON_ACTION ( mainWindow , actionCopyPixmap , edit - copy ) ;
CREATE_NEW_ACTION ( mainWindow , actionCopyFilePath ) ;
CREATE_NEW_ICON_ACTION ( mainWindow , actionPrevPicture , go - previous ) ;
CREATE_NEW_ICON_ACTION ( mainWindow , actionNextPicture , go - next ) ;
2025-07-23 21:20:34 +08:00
CREATE_NEW_ICON_ACTION ( mainWindow , actionActualSize , zoom - original ) ;
2026-07-12 21:00:11 +08:00
CREATE_NEW_ICON_ACTION ( mainWindow , actionFitToScreen , app - icon ) ;
CREATE_NEW_ICON_ACTION ( mainWindow , actionFitByWidth , app - icon ) ;
CREATE_NEW_ICON_ACTION ( mainWindow , actionFitByHeight , app - icon ) ;
2025-07-23 21:20:34 +08:00
CREATE_NEW_ICON_ACTION ( mainWindow , actionZoomIn , zoom - in ) ;
CREATE_NEW_ICON_ACTION ( mainWindow , actionZoomOut , zoom - out ) ;
2026-07-12 21:00:11 +08:00
CREATE_NEW_ICON_ACTION ( mainWindow , actionFlipHorizontal , app - icon ) ;
CREATE_NEW_ICON_ACTION ( mainWindow , actionFlipVertical , app - icon ) ;
CREATE_NEW_ICON_ACTION ( mainWindow , actionRotateClockwise , object - rotate - right ) ;
CREATE_NEW_ICON_ACTION ( mainWindow , actionRotateCounterClockwise , app - icon ) ;
2025-07-23 21:20:34 +08:00
2026-07-12 21:00:11 +08:00
CREATE_NEW_ACTION ( mainWindow , actionToggleAvoidResetTransform ) ;
CREATE_NEW_ICON_ACTION ( mainWindow , actionToggleCheckerboard , view - background - checkerboard ) ;
2025-07-23 21:20:34 +08:00
CREATE_NEW_ACTION ( mainWindow , actionTogglePauseAnimation ) ;
CREATE_NEW_ACTION ( mainWindow , actionAnimationNextFrame ) ;
CREATE_NEW_THEMEICON_ACTION ( mainWindow , actionTrash , edit - delete ) ;
CREATE_NEW_THEMEICON_ACTION ( mainWindow , actionLocateInFileManager , system - file - manager ) ;
CREATE_NEW_THEMEICON_ACTION ( mainWindow , actionProperties , document - properties ) ;
2026-07-12 21:00:11 +08:00
CREATE_NEW_ACTION ( mainWindow , actionSettings ) ;
CREATE_NEW_THEMEICON_ACTION ( mainWindow , actionHelp , system - help ) ;
// Remove convenient macros
# undef CREATE_NEW_ICON_ACTION
2025-07-23 21:20:34 +08:00
# undef CREATE_NEW_THEMEICON_ACTION
2026-07-12 21:00:11 +08:00
# undef CREATE_NEW_ACTION
2025-07-23 21:20:34 +08:00
2026-07-03 15:12:15 +08:00
# undef STRIFY
# undef ACTION_NAME
# undef ICON_NAME
2025-07-23 21:20:34 +08:00
2026-07-03 15:12:15 +08:00
// endregion
2025-07-23 21:20:34 +08:00
2026-07-12 21:00:11 +08:00
// region: Setup Checkable Action
actionToggleAvoidResetTransform - > setCheckable ( true ) ;
actionToggleCheckerboard - > setCheckable ( true ) ;
actionTogglePauseAnimation - > setCheckable ( true ) ;
// endregion
2026-07-03 15:12:15 +08:00
// region: Action Translatable Text Setup
2025-07-23 21:20:34 +08:00
2026-07-12 21:00:11 +08:00
actionCopyPixmap - > setText ( QCoreApplication : : translate ( " MainWindow " , " Copy P&ixmap " , nullptr ) ) ;
actionCopyFilePath - > setText ( QCoreApplication : : translate ( " MainWindow " , " Copy &File Path " , nullptr ) ) ;
actionPrevPicture - > setText ( QCoreApplication : : translate ( " MainWindow " , " Previous image " , nullptr ) ) ;
actionNextPicture - > setText ( QCoreApplication : : translate ( " MainWindow " , " Next image " , nullptr ) ) ;
2025-07-23 21:20:34 +08:00
actionActualSize - > setText ( QCoreApplication : : translate ( " MainWindow " , " Actual size " , nullptr ) ) ;
2026-07-12 21:00:11 +08:00
actionFitToScreen - > setText ( QCoreApplication : : translate ( " MainWindow " , " Fit to screen " , nullptr ) ) ;
actionFitByWidth - > setText ( QCoreApplication : : translate ( " MainWindow " , " Fit by width " , nullptr ) ) ;
actionFitByHeight - > setText ( QCoreApplication : : translate ( " MainWindow " , " Fit by height " , nullptr ) ) ;
2025-07-23 21:20:34 +08:00
actionZoomIn - > setText ( QCoreApplication : : translate ( " MainWindow " , " Zoom in " , nullptr ) ) ;
actionZoomOut - > setText ( QCoreApplication : : translate ( " MainWindow " , " Zoom out " , nullptr ) ) ;
2026-07-12 21:00:11 +08:00
actionFlipHorizontal - > setText ( QCoreApplication : : translate ( " MainWindow " , " Flip &Horizontally " , nullptr ) ) ;
actionFlipVertical - > setText ( QCoreApplication : : translate ( " MainWindow " , " Flip &Vertically " , nullptr ) ) ;
2025-07-23 21:20:34 +08:00
actionRotateClockwise - > setText ( QCoreApplication : : translate ( " MainWindow " , " Rotate right " , nullptr ) ) ;
actionRotateCounterClockwise - > setText ( QCoreApplication : : translate ( " MainWindow " , " Rotate left " , nullptr ) ) ;
2026-07-12 21:00:11 +08:00
actionToggleAvoidResetTransform - > setText ( QCoreApplication : : translate ( " MainWindow " , " Keep transformation " , " The 'transformation' means the flip/rotation status that currently applied to the image view " ) ) ;
actionToggleCheckerboard - > setText ( QCoreApplication : : translate ( " MainWindow " , " Toggle Checkerboard " , nullptr ) ) ;
2025-07-23 21:20:34 +08:00
actionTogglePauseAnimation - > setText ( QCoreApplication : : translate ( " MainWindow " , " Pause/Resume Animation " , nullptr ) ) ;
actionAnimationNextFrame - > setText ( QCoreApplication : : translate ( " MainWindow " , " Animation Go to Next Frame " , nullptr ) ) ;
actionTrash - > setText ( QCoreApplication : : translate ( " MainWindow " , " Move to Trash " , nullptr ) ) ;
# ifdef Q_OS_WIN
actionLocateInFileManager - > setText (
QCoreApplication : : translate (
" MainWindow " , " Show in File Explorer " ,
" File Explorer is the name of explorer.exe under Windows "
2026-07-03 15:12:15 +08:00
)
) ;
2025-07-23 21:20:34 +08:00
# else
actionLocateInFileManager - > setText ( QCoreApplication : : translate ( " MainWindow " , " Show in directory " , nullptr ) ) ;
# endif // Q_OS_WIN
actionProperties - > setText ( QCoreApplication : : translate ( " MainWindow " , " Properties " , nullptr ) ) ;
2026-07-03 15:12:15 +08:00
2026-07-12 21:00:11 +08:00
actionSettings - > setText ( QCoreApplication : : translate ( " MainWindow " , " Configure... " , nullptr ) ) ;
actionHelp - > setText ( QCoreApplication : : translate ( " MainWindow " , " Help " , nullptr ) ) ;
2026-07-03 15:12:15 +08:00
// endregion
QMetaObject : : connectSlotsByName ( mainWindow ) ;
2025-07-23 21:20:34 +08:00
}
void ActionManager : : setupShortcuts ( )
{
2026-07-12 21:00:11 +08:00
actionCopyPixmap - > setShortcut ( QKeySequence : : Copy ) ;
2025-07-23 21:20:34 +08:00
actionPrevPicture - > setShortcuts ( {
QKeySequence ( Qt : : Key_Left ) ,
2026-07-12 21:00:11 +08:00
QKeySequence ( Qt : : Key_PageUp ) ,
2025-07-23 21:20:34 +08:00
} ) ;
actionNextPicture - > setShortcuts ( {
QKeySequence ( Qt : : Key_Right ) ,
2026-07-12 21:00:11 +08:00
QKeySequence ( Qt : : Key_PageDown ) ,
2025-07-23 21:20:34 +08:00
} ) ;
2026-07-12 21:00:11 +08:00
actionActualSize - > setShortcut ( QKeySequence ( Qt : : CTRL | Qt : : Key_0 ) ) ;
actionZoomIn - > setShortcut ( QKeySequence : : ZoomIn ) ;
actionZoomOut - > setShortcut ( QKeySequence : : ZoomOut ) ;
2025-07-23 21:20:34 +08:00
actionTrash - > setShortcut ( QKeySequence : : Delete ) ;
actionProperties - > setShortcut ( QKeySequence ( Qt : : CTRL | Qt : : Key_I ) ) ;
2026-07-12 21:00:11 +08:00
actionSettings - > setShortcut ( QKeySequence : : Preferences ) ;
actionHelp - > setShortcut ( QKeySequence : : HelpContents ) ;
2025-07-23 21:20:34 +08:00
}
2026-07-12 21:00:11 +08:00
void ActionManager : : updateActionState ( PlaylistManager * pm , GraphicsView * gv )
2026-07-03 14:56:54 +08:00
{
2026-07-12 21:00:11 +08:00
const auto * gs = gv - > scene ( ) ;
const auto * settings = Settings : : instance ( ) ;
2026-07-13 15:40:59 +08:00
// Update copy actions
bool canCopy = pm - > currentIndex ( ) . has_value ( ) ;
actionCopyPixmap - > setEnabled ( canCopy ) ;
actionCopyFilePath - > setEnabled ( canCopy ) ;
2026-07-12 21:00:11 +08:00
// Update Prev/Next Picture State
const int galleryFileCount = pm - > totalCount ( ) ;
const bool loopGallery = settings - > loopGallery ( ) ;
actionPrevPicture - > setEnabled ( galleryFileCount > 1 & & ( loopGallery | | ! pm - > isFirstIndex ( ) ) ) ;
actionNextPicture - > setEnabled ( galleryFileCount > 1 & & ( loopGallery | | ! pm - > isLastIndex ( ) ) ) ;
// Update Avoid Reset Transform
2026-07-13 15:40:59 +08:00
actionToggleAvoidResetTransform - > setChecked ( gv - > isAvoidResetTransform ( ) ) ;
2026-07-12 21:00:11 +08:00
// Update Checkerboard
2026-07-13 15:40:59 +08:00
actionToggleCheckerboard - > setChecked ( gv - > isCheckerboard ( ) ) ;
2026-07-12 21:00:11 +08:00
// Update Animation Graphics Action
bool canPauseAnimation = gs - > canPauseAnimation ( ) ;
actionTogglePauseAnimation - > setEnabled ( canPauseAnimation ) ;
if ( canPauseAnimation ) {
actionTogglePauseAnimation - > setChecked ( gs - > isPauseAnimation ( ) ) ;
} else {
actionTogglePauseAnimation - > setChecked ( false ) ;
}
bool canSkipAnimation = gs - > canSkipAnimationFrame ( ) ;
actionAnimationNextFrame - > setEnabled ( canSkipAnimation ) ;
2026-07-13 15:40:59 +08:00
// Update trash, located in explorer and properties.
bool hasLoadedFile = pm - > currentIndex ( ) . has_value ( ) ;
actionTrash - > setEnabled ( hasLoadedFile ) ;
actionLocateInFileManager - > setEnabled ( hasLoadedFile ) ;
actionProperties - > setEnabled ( hasLoadedFile ) ;
2026-07-03 14:56:54 +08:00
}