Compare commits
36 Commits
Author | SHA1 | Date | |
---|---|---|---|
8c152dc862 | |||
1066fa45ea | |||
71c38c8e96 | |||
95fd0f881c | |||
0a9ea1982b | |||
0dfbb45238 | |||
7e7825760a | |||
203274a522 | |||
54c54e2adf | |||
6da9c16c4f | |||
5ace4e219c | |||
c78eb1a272 | |||
7c39668eb6 | |||
4c3e370f70 | |||
f7aaa76985 | |||
5ff49f4885 | |||
36c7eb1891 | |||
58210691ed | |||
0eb9886394 | |||
16ccec0d12 | |||
e2fb1813f1 | |||
7245a4e212 | |||
9f67be61fb | |||
761f5e064c | |||
84e100c174 | |||
d64e9cf276 | |||
dc82115e1f | |||
3b94eecde2 | |||
f3b3ad7b8a | |||
1a511ddb02 | |||
1cb67b48f4 | |||
90d0869b5e | |||
7004e74165 | |||
a2adb0e1d4 | |||
6709c21d70 | |||
31fae2cc8c |
6
.github/workflows/ubuntu.yml
vendored
6
.github/workflows/ubuntu.yml
vendored
@ -17,4 +17,8 @@ jobs:
|
||||
cd build
|
||||
cmake ../
|
||||
make
|
||||
sudo cpack
|
||||
cpack -G DEB
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ubuntu-20.04-deb-package
|
||||
path: build/*.deb
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,6 @@
|
||||
# User files
|
||||
*.user
|
||||
*.user.*
|
||||
|
||||
# Translation files
|
||||
*.qm
|
||||
|
@ -3,6 +3,7 @@ project (pineapple-pictures)
|
||||
cmake_minimum_required (VERSION 3.9.5)
|
||||
|
||||
include (GNUInstallDirs)
|
||||
include (FeatureSummary)
|
||||
|
||||
set (CMAKE_AUTOMOC ON)
|
||||
set (CMAKE_AUTORCC ON)
|
||||
@ -11,24 +12,34 @@ set (QT_MINIMUM_VERSION "5.10")
|
||||
find_package(Qt5 ${QT_MINIMUM_VERSION} CONFIG REQUIRED Widgets Svg LinguistTools)
|
||||
|
||||
set (PPIC_CPP_FILES
|
||||
main.cpp
|
||||
mainwindow.cpp
|
||||
graphicsview.cpp
|
||||
graphicsscene.cpp
|
||||
bottombuttongroup.cpp
|
||||
navigatorview.cpp
|
||||
opacityhelper.cpp
|
||||
toolbutton.cpp
|
||||
app/main.cpp
|
||||
app/mainwindow.cpp
|
||||
app/graphicsview.cpp
|
||||
app/graphicsscene.cpp
|
||||
app/bottombuttongroup.cpp
|
||||
app/navigatorview.cpp
|
||||
app/opacityhelper.cpp
|
||||
app/toolbutton.cpp
|
||||
app/settings.cpp
|
||||
app/settingsdialog.cpp
|
||||
app/aboutdialog.cpp
|
||||
app/metadatamodel.cpp
|
||||
app/metadatadialog.cpp
|
||||
)
|
||||
|
||||
set (PPIC_HEADER_FILES
|
||||
mainwindow.h
|
||||
graphicsview.h
|
||||
graphicsscene.h
|
||||
bottombuttongroup.h
|
||||
navigatorview.h
|
||||
opacityhelper.h
|
||||
toolbutton.h
|
||||
app/mainwindow.h
|
||||
app/graphicsview.h
|
||||
app/graphicsscene.h
|
||||
app/bottombuttongroup.h
|
||||
app/navigatorview.h
|
||||
app/opacityhelper.h
|
||||
app/toolbutton.h
|
||||
app/settings.h
|
||||
app/settingsdialog.h
|
||||
app/aboutdialog.h
|
||||
app/metadatamodel.h
|
||||
app/metadatadialog.h
|
||||
)
|
||||
|
||||
set (PPIC_QRC_FILES
|
||||
@ -42,7 +53,7 @@ set (PPIC_RC_FILES
|
||||
set (EXE_NAME ppic)
|
||||
|
||||
# Translation
|
||||
file (GLOB PPIC_TS_FILES languages/*.ts)
|
||||
file (GLOB PPIC_TS_FILES translations/*.ts)
|
||||
set (PPIC_CPP_FILES_FOR_I18N ${PPIC_CPP_FILES})
|
||||
|
||||
qt5_create_translation(PPIC_QM_FILES ${PPIC_CPP_FILES_FOR_I18N} ${PPIC_TS_FILES})
|
||||
@ -67,6 +78,52 @@ if (WIN32)
|
||||
TARGET ${EXE_NAME}
|
||||
PROPERTY WIN32_EXECUTABLE true
|
||||
)
|
||||
|
||||
target_compile_definitions(${EXE_NAME} PRIVATE
|
||||
FLAG_PORTABLE_MODE_SUPPORT=1
|
||||
)
|
||||
endif ()
|
||||
|
||||
# Helper macros for parsing and setting project version from `git describe --long` result
|
||||
macro (ppic_set_version_via_describe _describe_long)
|
||||
string (
|
||||
REGEX REPLACE
|
||||
"^([0-9a-z.]*)-[0-9]+-g[0-9a-f]*$"
|
||||
"\\1"
|
||||
_tag_parts
|
||||
"${_describe_long}"
|
||||
)
|
||||
list (GET _tag_parts 0 _matched_tag_version)
|
||||
if ("${_matched_tag_version}" MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+$")
|
||||
string (
|
||||
REGEX REPLACE
|
||||
"^([0-9]+)\\.([0-9]+)\\.([0-9]+).*$"
|
||||
"\\1;\\2;\\3"
|
||||
_ver_parts
|
||||
"${_matched_tag_version}"
|
||||
)
|
||||
list (GET _ver_parts 0 CPACK_PACKAGE_VERSION_MAJOR)
|
||||
list (GET _ver_parts 1 CPACK_PACKAGE_VERSION_MINOR)
|
||||
list (GET _ver_parts 2 CPACK_PACKAGE_VERSION_PATCH)
|
||||
endif ()
|
||||
endmacro ()
|
||||
|
||||
# Version setup
|
||||
if (EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
find_package(Git)
|
||||
set_package_properties(Git PROPERTIES TYPE OPTIONAL PURPOSE "Determine exact build version.")
|
||||
if (GIT_FOUND)
|
||||
execute_process (
|
||||
COMMAND ${GIT_EXECUTABLE} describe --tags --always --long
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE _git_describe_long
|
||||
)
|
||||
string (REGEX REPLACE "\n" "" _git_describe_long "${_git_describe_long}")
|
||||
ppic_set_version_via_describe(${_git_describe_long})
|
||||
target_compile_definitions(${EXE_NAME} PRIVATE
|
||||
GIT_DESCRIBE_VERSION_STRING="${_git_describe_long}"
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# Helper macros for install settings
|
||||
@ -101,9 +158,15 @@ elseif (UNIX)
|
||||
|
||||
# install shortcut
|
||||
install (
|
||||
FILES pineapple-pictures.desktop
|
||||
FILES dist/net.blumia.pineapple-pictures.desktop
|
||||
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications"
|
||||
)
|
||||
|
||||
# install app metadata file for appstream (and some other stuff using this metadata like snapcraft)
|
||||
install (
|
||||
FILES dist/appstream/net.blumia.pineapple-pictures.appdata.xml
|
||||
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo"
|
||||
)
|
||||
endif()
|
||||
|
||||
set (INSTALL_TARGETS_DEFAULT_ARGS
|
||||
@ -136,7 +199,7 @@ set (CPACK_GENERATOR "TBZ2")
|
||||
set (CPACK_PACKAGE_NAME "pineapple-pictures")
|
||||
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Yet another image viewer")
|
||||
set (CPACK_PACKAGE_VENDOR "Gary Wang")
|
||||
set (CPACK_PACKAGE_CONTACT "https://github.com/BLumia/PineapplePictures/issues/")
|
||||
set (CPACK_PACKAGE_CONTACT "https://github.com/BLumia/pineapple-pictures/issues/")
|
||||
if (WIN32)
|
||||
# ...
|
||||
elseif (APPLE)
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 BLumia
|
||||
Copyright (c) 2020 BLumia
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,60 +0,0 @@
|
||||
#-------------------------------------------------
|
||||
#
|
||||
# Project created by QtCreator 2019-09-26T23:36:07
|
||||
#
|
||||
#-------------------------------------------------
|
||||
|
||||
QT += core gui svg
|
||||
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
|
||||
TARGET = PineapplePictures
|
||||
TEMPLATE = app
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
# any feature of Qt which has been marked as deprecated (the exact warnings
|
||||
# depend on your compiler). Please consult the documentation of the
|
||||
# deprecated API in order to know how to port your code away from it.
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
# You can also make your code fail to compile if you use deprecated APIs.
|
||||
# In order to do so, uncomment the following line.
|
||||
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||
|
||||
CONFIG += c++11 lrelease embed_translations
|
||||
|
||||
SOURCES += \
|
||||
main.cpp \
|
||||
mainwindow.cpp \
|
||||
graphicsview.cpp \
|
||||
bottombuttongroup.cpp \
|
||||
graphicsscene.cpp \
|
||||
navigatorview.cpp \
|
||||
opacityhelper.cpp \
|
||||
toolbutton.cpp
|
||||
|
||||
HEADERS += \
|
||||
mainwindow.h \
|
||||
graphicsview.h \
|
||||
bottombuttongroup.h \
|
||||
graphicsscene.h \
|
||||
navigatorview.h \
|
||||
opacityhelper.h \
|
||||
toolbutton.h
|
||||
|
||||
TRANSLATIONS = \
|
||||
languages/PineapplePictures.ts \
|
||||
languages/PineapplePictures_zh_CN.ts
|
||||
|
||||
# Default rules for deployment.
|
||||
qnx: target.path = /tmp/$${TARGET}/bin
|
||||
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||
!isEmpty(target.path): INSTALLS += target
|
||||
|
||||
RESOURCES += \
|
||||
resources.qrc
|
||||
|
||||
# Generate fron svg:
|
||||
# magick convert -background none app-icon.svg -define icon:auto-resize="16,32,48,64,128,256" app-icon.ico
|
||||
RC_ICONS = icons/app-icon.ico
|
59
README.md
59
README.md
@ -3,29 +3,64 @@ Yet another image viewer.
|
||||
|CI|Build Status|
|
||||
|---|---|
|
||||
|Windows Build|[](https://ci.appveyor.com/project/BLumia/pineapplepictures/branch/master)|
|
||||
|macOS Build||
|
||||
|Ubuntu 20.04 Build||
|
||||
|macOS Build||
|
||||
|Ubuntu 20.04 Build||
|
||||
|
||||

|
||||
|
||||
## Get it!
|
||||
|
||||
- [GitHub Release Page](https://github.com/BLumia/PineapplePictures/releases)
|
||||
- [GitHub Release Page](https://github.com/BLumia/pineapple-pictures/releases)
|
||||
- Archlinux AUR: [pineapple-pictures-git](https://aur.archlinux.org/packages/pineapple-pictures-git/)
|
||||
|
||||
## Build it manually:
|
||||
|
||||
Current state, we need:
|
||||
|
||||
- `cmake`: as the build system.
|
||||
- `qt5` with `qt5-svg` and `qt5-tools`: since the app is using Qt.
|
||||
|
||||
Then we can build it with any proper c++ compiler like g++ or msvc.
|
||||
|
||||
Building it just requires normal cmake building steps:
|
||||
|
||||
``` bash
|
||||
$ mkdir build && cd build
|
||||
$ cmake ..
|
||||
$ cmake --build . # or simply using `make` if you are using Makefile as the cmake generator.
|
||||
```
|
||||
|
||||
After that, a `ppic` executable file will be available to use. You can also optionally install it by using the target `install` (or simply `make install` in case you are using Makefile). After the build process, you can also use `cpack` to make a package.
|
||||
|
||||
Image formats supports rely on Qt's imageformats plugins, just get the plugins you need from your distro's package manager will be fine. For Windows user, you may need build and install the imageformats plugin manually, read the content below.
|
||||
|
||||
### Linux
|
||||
|
||||
Just normal build process as other program will be fine. Nothing special ;)
|
||||
|
||||
For Archlinux there are also a [PKGBUILD](https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=pineapple-pictures-git) you can use.
|
||||
|
||||
For packaging to debian-based distro, the `CMakeLists.txt` provides some cpack configurations for generating a `.deb` package. After the build process, use `cpack -G DEB` to generate the package. You can also take `.github/workflows/ubuntu.yml` as a reference.
|
||||
|
||||
For this project, `DEB` is the only supported cpack generator in current state, feel free to submit a PR if you like improving `cpack` support for this project.
|
||||
|
||||
### Windows
|
||||
|
||||
The normal build steps for Linux is also applied to Windows, but since Windows doesn't have a decent package manager, so if you need any other image formats support other than the supported formats which Qt provided, you need to get and build these imageformats plugins manually and vendor it. It's optional and can be skipped if you don't need extra image formats support.
|
||||
|
||||
For the Windows binary I provided, kimageformats plugin is used (for formats like kra, xcf, psd and etc.). You can take `appveyor.yml` as a reference to learn what I did when building the Windows binary.
|
||||
|
||||
[KDE Craft](https://community.kde.org/Craft) environment also can be used to build and package this program. I did also created a blueprint for building this project, but since I don't have a CI to run KDE Craft build, the blueprint repo are not provided here. Maybe sometimes later.
|
||||
|
||||
### macOS
|
||||
|
||||
I don't have a mac, so no support at all. There is also a GitHub Action (see `.github/workflows/macos.yml`) running macOS build though so at least it can build. Feel free to submit a PR if you would like to give some love to the macOS build ;P
|
||||
|
||||
## Help Translation!
|
||||
|
||||
[Translate this project on Transifex!](https://www.transifex.com/blumia/pineapple-pictures/)
|
||||
|
||||
Feel free to open up an issue to request an new language to translate.
|
||||
|
||||
## Uncleaned shits inside(TM):
|
||||
|
||||
- Mixed `CR LF` and `LF`.
|
||||
- Ugly action icons.
|
||||
- Ugly implementations.
|
||||
- For windows build, win32 APIs are used.
|
||||
- No drag-window-border-to-resize support under non-windows platforms (I use <kbd>Meta+Drag</kbd> to resize window under my x11 desktop).
|
||||
Feel free to open up an issue to request a new language to translate.
|
||||
|
||||
## License
|
||||
|
||||
|
67
README.zh_CN.md
Normal file
67
README.zh_CN.md
Normal file
@ -0,0 +1,67 @@
|
||||
简单轻量的跨平台看图工具。
|
||||
|
||||
|CI|构建状态|
|
||||
|---|---|
|
||||
|Windows Build|[](https://ci.appveyor.com/project/BLumia/pineapplepictures/branch/master)|
|
||||
|macOS Build||
|
||||
|Ubuntu 20.04 Build||
|
||||
|
||||

|
||||
|
||||
## 立即获取!
|
||||
|
||||
- [GitHub Release 页面](https://github.com/BLumia/pineapple-pictures/releases) | [gitee 发布页面](https://gitee.com/blumia/pineapple-pictures/releases)
|
||||
- Archlinux AUR: [pineapple-pictures-git](https://aur.archlinux.org/packages/pineapple-pictures-git/)
|
||||
|
||||
## 手动构建步骤:
|
||||
|
||||
当前状态,我们需要先确保如下依赖可用:
|
||||
|
||||
- `cmake`: 我们所使用的构建系统
|
||||
- 包含 `qt5-svg` 与 `qt5-tools` 组件的 `qt5`: 此应用基于 Qt
|
||||
|
||||
然后我们就可以使用任何常规的 c++ 编译器如 g++ 或 msvc 来进行构建了
|
||||
|
||||
构建过程就是常规的 CMake 应用构建过程:
|
||||
|
||||
``` bash
|
||||
$ mkdir build && cd build
|
||||
$ cmake ..
|
||||
$ cmake --build . # 如果你使用 Makefile 作为 CMake 生成器,也可以直接简单的使用 `make`
|
||||
```
|
||||
|
||||
完毕后,一个名为 `ppic` 的可执行程序即会被生成以供使用。您也可以选择通过使用 CMake 生成的 `install` 目标继续将其安装到您的设备上(假设您使用 Makefile,即可执行 `make install` 来进行安装)。构建步骤完毕后,您也可以使用 `cpack` 来对应用程序进行打包。
|
||||
|
||||
此应用的图片格式支持依赖于 Qt 的 imageformats 插件,直接从您所用的发行版获取对应的图像格式插件即可。对于 Windows 用户,您可能需要手动构建和使用图像格式插件。下方给出了进一步的说明。
|
||||
|
||||
### Linux
|
||||
|
||||
常规的构建步骤即可完成构建,不需要额外的处理步骤 ;)
|
||||
|
||||
对于 Archlinux 发行版的用户,这里还有一个 [PKGBUILD](https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=pineapple-pictures-git) 可供使用和参考。
|
||||
|
||||
对于在基于 debian 的发行版中进行打包的需求, `CMakeLists.txt` 已经提供了一些基本的 cpack 配置以便生成一个有效的 `.deb` 软件包。在构建步骤完毕后,使用 `cpack -G DEB` 即可生成 DEB 软件包。您也可以参考 `.github/workflows/ubuntu.yml` 来查看当前正在使用的 CI 配置是如何进行打包的。
|
||||
|
||||
目前,`DEB` 是当前唯一受到直接支持的 cpack 生成目标。若希望为此项目添加其它的 cpack 目标支持,欢迎发起合并请求。
|
||||
|
||||
### Windows
|
||||
|
||||
上述的构建步骤在 Windows 中也适用,但由于 Windows 中不具备类如大多 Linux 发行版中所提供的方便的软件包管理机制,故如果您需要任何 Qt 官方支持之外的图像格式例如 psd,xcf,kra 等格式的支持,你就可能需要自行获取并构建对应的 imageformats 插件,并在您最终生成的可执行文件中一并提供这些插件。若您不需要这些额外的图像格式支持,这个步骤也可以直接跳过。
|
||||
|
||||
我们所提供的预编译好的 Windows 程序包含了 kimageformats 插件来提供额外(kra, xcf, psd 等)格式的支持。您可以参考 `appveyor.yml` 来查看我们是如何构建并打包 Windows 可执行程序的。
|
||||
|
||||
[KDE Craft](https://community.kde.org/Craft) 环境也可以被用来构建此应用程序。我也创建了一个蓝图来进行此项目的构建和打包。但由于暂时并未配置 CI 部署此环境来进行 KDE Craft 环境下的构建,故对应的蓝图仓库也尚未公开提供,或许后续会开放出来。
|
||||
|
||||
### macOS
|
||||
|
||||
由于我没有 mac 设备,故 macOS 暂时不受任何支持。不过我们目前有一个 GitHub Action 来执行 macOS 环境下的构建(见 `.github/workflows/macos.yml`)所以至少 macOS 下是可以顺利进行构建的。如果您想完善对 macOS 的支持,也欢迎您创建合并请求 ;P
|
||||
|
||||
## 帮助翻译!
|
||||
|
||||
[在 Transifex 上帮助此项目翻译到更多语言!](https://www.transifex.com/blumia/pineapple-pictures/)
|
||||
|
||||
若 Transifex 上没有您所希望进行翻译的语言,请通过 Issue 的形式告诉我。
|
||||
|
||||
## 许可协议
|
||||
|
||||
此项目使用 MIT 协议进行发布。
|
157
app/aboutdialog.cpp
Normal file
157
app/aboutdialog.cpp
Normal file
@ -0,0 +1,157 @@
|
||||
#include "aboutdialog.h"
|
||||
|
||||
#include <QAbstractButton>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QTextBrowser>
|
||||
#include <QTabWidget>
|
||||
#include <QVBoxLayout>
|
||||
#include <QApplication>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
|
||||
AboutDialog::AboutDialog(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, m_tabWidget(new QTabWidget)
|
||||
, m_buttonBox(new QDialogButtonBox)
|
||||
, m_helpTextEdit(new QTextBrowser)
|
||||
, m_aboutTextEdit(new QTextBrowser)
|
||||
, m_specialThanksTextEdit(new QTextBrowser)
|
||||
, m_licenseTextEdit(new QTextBrowser)
|
||||
, m_3rdPartyLibsTextEdit(new QTextBrowser)
|
||||
{
|
||||
this->setWindowTitle(tr("About"));
|
||||
|
||||
QStringList helpStr {
|
||||
QStringLiteral("<p>%1</p>").arg(tr("Launch application with image file path as argument to load the file.")),
|
||||
QStringLiteral("<p>%1</p>").arg(tr("Drag and drop image file onto the window is also supported.")),
|
||||
QStringLiteral("<p>%1</p>").arg(tr("Context menu option explanation:")),
|
||||
QStringLiteral("<ul>"),
|
||||
QStringLiteral("<li><b>%1</b>:<br/>%2</li>").arg(
|
||||
QCoreApplication::translate("MainWindow", "Stay on top"),
|
||||
this->tr("Make window stay on top of all other windows.")
|
||||
),
|
||||
QStringLiteral("<li><b>%1</b>:<br/>%2</li>").arg(
|
||||
QCoreApplication::translate("MainWindow", "Protected mode"),
|
||||
this->tr("Avoid close window accidentally. (eg. by double clicking the window)")
|
||||
),
|
||||
QStringLiteral("</ul>")
|
||||
};
|
||||
|
||||
QStringList aboutStr {
|
||||
QStringLiteral("<center><img width='128' height='128' src=':/icons/app-icon.svg'/><br/>"),
|
||||
qApp->applicationDisplayName(),
|
||||
#ifdef GIT_DESCRIBE_VERSION_STRING
|
||||
(QStringLiteral("<br/>") + tr("Version: %1").arg(GIT_DESCRIBE_VERSION_STRING)),
|
||||
#endif // GIT_DESCRIBE_VERSION_STRING
|
||||
QStringLiteral("<hr/>"),
|
||||
tr("Copyright (c) 2020 %1").arg(QStringLiteral("<a href='https://github.com/BLumia'>@BLumia</a>")),
|
||||
QStringLiteral("<br/>"),
|
||||
tr("Logo designed by %1").arg(QStringLiteral("<a href='https://github.com/Lovelyblack'>@Lovelyblack</a>")),
|
||||
QStringLiteral("<hr/>"),
|
||||
tr("Built with Qt %1 (%2)").arg(QT_VERSION_STR, QSysInfo::buildCpuArchitecture()),
|
||||
QStringLiteral("<br/><a href='%1'>%2</a>").arg("https://github.com/BLumia/pineapple-pictures", tr("Source code")),
|
||||
QStringLiteral("</center>")
|
||||
};
|
||||
|
||||
QStringList specialThanksStr {
|
||||
QStringLiteral("<h1 align='center'>%1</h1><a href='%2'>%3</a><p>%4</p>").arg(
|
||||
tr("Contributors"),
|
||||
QStringLiteral("https://github.com/BLumia/pineapple-pictures/graphs/contributors"),
|
||||
tr("List of contributors on GitHub"),
|
||||
tr("Thanks to all people who contributed to this project.")
|
||||
),
|
||||
#if 0
|
||||
QStringLiteral("<h1 align='center'>%1</h1><p>%2</p>").arg(
|
||||
tr("Translators"),
|
||||
tr("I would like to thank the following people who volunteered to translate this application.")
|
||||
),
|
||||
#endif
|
||||
};
|
||||
|
||||
QStringList licenseStr {
|
||||
QStringLiteral("<h1 align='center'><b>%1</b></h1>").arg(tr("Your Rights")),
|
||||
QStringLiteral("<p>%1</p><p>%2</p><ul><li>%3</li><li>%4</li><li>%5</li><li>%6</li></ul>").arg(
|
||||
tr("%1 is released under the MIT License."), // %1
|
||||
tr("This license grants people a number of freedoms:"), // %2
|
||||
tr("You are free to use %1, for any purpose"), // %3
|
||||
tr("You are free to distribute %1"), // %4
|
||||
tr("You can study how %1 works and change it"), // %5
|
||||
tr("You can distribute changed versions of %1") // %6
|
||||
).arg(QStringLiteral("<i>%1</i>")),
|
||||
QStringLiteral("<p>%1</p>").arg(tr("The MIT license guarantees you this freedom. Nobody is ever permitted to take it away.")),
|
||||
QStringLiteral("<hr/><pre>%2</pre>")
|
||||
};
|
||||
|
||||
QString mitLicense(QStringLiteral(R"(Expat/MIT License
|
||||
|
||||
Copyright (c) 2020 BLumia
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
)"));
|
||||
|
||||
QStringList thirdPartyLibsStr {
|
||||
QStringLiteral("<h1 align='center'><b>%1</b></h1>").arg(tr("Third-party Libraries used by %1")),
|
||||
tr("%1 is built on the following free software libraries:"),
|
||||
QStringLiteral("<ul>"),
|
||||
QStringLiteral("<li><a href='%1'>%2</a>: %3</li>").arg("https://www.qt.io/", "Qt", "GPLv2 + GPLv3 + LGPLv2.1 + LGPLv3"),
|
||||
QStringLiteral("</ul>")
|
||||
};
|
||||
|
||||
m_helpTextEdit->setText(helpStr.join('\n'));
|
||||
|
||||
m_aboutTextEdit->setText(aboutStr.join('\n'));
|
||||
m_aboutTextEdit->setOpenExternalLinks(true);
|
||||
|
||||
m_specialThanksTextEdit->setText(specialThanksStr.join('\n'));
|
||||
m_specialThanksTextEdit->setOpenExternalLinks(true);
|
||||
|
||||
m_licenseTextEdit->setText(licenseStr.join('\n').arg(qApp->applicationDisplayName(), mitLicense));
|
||||
|
||||
m_3rdPartyLibsTextEdit->setText(thirdPartyLibsStr.join('\n').arg(QStringLiteral("<i>%1</i>")).arg(qApp->applicationDisplayName()));
|
||||
m_3rdPartyLibsTextEdit->setOpenExternalLinks(true);
|
||||
|
||||
m_tabWidget->addTab(m_helpTextEdit, tr("&Help"));
|
||||
m_tabWidget->addTab(m_aboutTextEdit, tr("&About"));
|
||||
m_tabWidget->addTab(m_specialThanksTextEdit, tr("&Special Thanks"));
|
||||
m_tabWidget->addTab(m_licenseTextEdit, tr("&License"));
|
||||
m_tabWidget->addTab(m_3rdPartyLibsTextEdit, tr("&Third-party Libraries"));
|
||||
|
||||
m_buttonBox->setStandardButtons(QDialogButtonBox::Close);
|
||||
connect(m_buttonBox, QOverload<QAbstractButton *>::of(&QDialogButtonBox::clicked), this, [this](){
|
||||
this->close();
|
||||
});
|
||||
|
||||
setLayout(new QVBoxLayout);
|
||||
|
||||
layout()->addWidget(m_tabWidget);
|
||||
layout()->addWidget(m_buttonBox);
|
||||
|
||||
setMinimumSize(361, 161); // not sure why it complain "Unable to set geometry"
|
||||
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||
}
|
||||
|
||||
AboutDialog::~AboutDialog()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QSize AboutDialog::sizeHint() const
|
||||
{
|
||||
return QSize(520, 350);
|
||||
}
|
32
app/aboutdialog.h
Normal file
32
app/aboutdialog.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef ABOUTDIALOG_H
|
||||
#define ABOUTDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTextBrowser;
|
||||
class QTabWidget;
|
||||
class QDialogButtonBox;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class AboutDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AboutDialog(QWidget *parent = nullptr);
|
||||
~AboutDialog() override;
|
||||
|
||||
QSize sizeHint() const override;
|
||||
|
||||
private:
|
||||
QTabWidget * m_tabWidget = nullptr;
|
||||
QDialogButtonBox * m_buttonBox = nullptr;
|
||||
|
||||
QTextBrowser * m_helpTextEdit = nullptr;
|
||||
QTextBrowser * m_aboutTextEdit = nullptr;
|
||||
QTextBrowser * m_specialThanksTextEdit = nullptr;
|
||||
QTextBrowser * m_licenseTextEdit = nullptr;
|
||||
QTextBrowser * m_3rdPartyLibsTextEdit = nullptr;
|
||||
};
|
||||
|
||||
#endif // ABOUTDIALOG_H
|
@ -33,7 +33,7 @@ BottomButtonGroup::BottomButtonGroup(QWidget *parent)
|
||||
QPushButton * btn = new QPushButton(QIcon(QStringLiteral(":/icons/") + text), "");
|
||||
btn->setIconSize(QSize(40, 40));
|
||||
btn->setFixedSize(40, 40);
|
||||
connect(btn, &QAbstractButton::clicked, btn, func);
|
||||
QObject::connect(btn, &QAbstractButton::clicked, btn, func);
|
||||
return btn;
|
||||
};
|
||||
addButton(newBtn("zoom-original", [this]() {
|
@ -24,7 +24,9 @@ GraphicsScene::~GraphicsScene()
|
||||
void GraphicsScene::showImage(const QPixmap &pixmap)
|
||||
{
|
||||
this->clear();
|
||||
m_theThing = this->addPixmap(pixmap);
|
||||
QGraphicsPixmapItem * pixmapItem = this->addPixmap(pixmap);
|
||||
pixmapItem->setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
|
||||
m_theThing = pixmapItem;
|
||||
this->setSceneRect(m_theThing->boundingRect());
|
||||
}
|
||||
|
@ -31,6 +31,13 @@ void GraphicsView::showFileFromUrl(const QUrl &url, bool doRequestGallery)
|
||||
|
||||
QString filePath(url.toLocalFile());
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
// TODO: remove this workaround when M$ change the "wsl$" hostname.
|
||||
if (Q_UNLIKELY(url.scheme() == QStringLiteral("qtbug-86277"))) {
|
||||
filePath = url.path();
|
||||
}
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
if (filePath.endsWith(".svg")) {
|
||||
showSvg(filePath);
|
||||
} else if (filePath.endsWith(".gif")) {
|
||||
@ -44,8 +51,15 @@ void GraphicsView::showFileFromUrl(const QUrl &url, bool doRequestGallery)
|
||||
// QImage::Format imageFormat = imageReader.imageFormat();
|
||||
if (imageReader.format().isEmpty()) {
|
||||
showText(tr("File is not a valid image"));
|
||||
} else if (!imageReader.supportsAnimation() && !imageReader.canRead()) {
|
||||
showText(tr("Image data is invalid or currently unsupported"));
|
||||
} else {
|
||||
showImage(QPixmap::fromImageReader(&imageReader));
|
||||
const QPixmap & pixmap = QPixmap::fromImageReader(&imageReader);
|
||||
if (pixmap.isNull()) {
|
||||
showText(tr("Image data is invalid or currently unsupported"));
|
||||
} else {
|
||||
showImage(pixmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,12 +156,16 @@ void GraphicsView::fitInView(const QRectF &rect, Qt::AspectRatioMode aspectRadio
|
||||
applyTransformationModeByScaleFactor();
|
||||
}
|
||||
|
||||
void GraphicsView::checkAndDoFitInView()
|
||||
void GraphicsView::checkAndDoFitInView(bool markItOnAnyway)
|
||||
{
|
||||
if (!isThingSmallerThanWindowWith(transform())) {
|
||||
m_enableFitInView = true;
|
||||
fitInView(sceneRect(), Qt::KeepAspectRatio);
|
||||
}
|
||||
|
||||
if (markItOnAnyway) {
|
||||
m_enableFitInView = true;
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsView::toggleCheckerboard()
|
@ -30,7 +30,7 @@ public:
|
||||
void rotateView(qreal rotateAngel);
|
||||
void fitInView(const QRectF &rect, Qt::AspectRatioMode aspectRadioMode = Qt::IgnoreAspectRatio);
|
||||
|
||||
void checkAndDoFitInView();
|
||||
void checkAndDoFitInView(bool markItOnAnyway = true);
|
||||
|
||||
signals:
|
||||
void navigatorViewRequired(bool required, qreal angle);
|
@ -37,6 +37,14 @@ int main(int argc, char *argv[])
|
||||
QList<QUrl> urlList;
|
||||
for (const QString & str : urlStrList) {
|
||||
QUrl url = QUrl::fromLocalFile(str);
|
||||
#ifdef Q_OS_WIN
|
||||
// TODO: remove this workaround when M$ change the "wsl$" hostname.
|
||||
if (Q_UNLIKELY(str.startsWith(R"(\\wsl$\)"))) {
|
||||
url.clear();
|
||||
url.setScheme(QStringLiteral("qtbug-86277"));
|
||||
url.setPath(str);
|
||||
}
|
||||
#endif // Q_OS_WIN
|
||||
if (url.isValid()) {
|
||||
urlList.append(url);
|
||||
}
|
@ -1,10 +1,15 @@
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include "settings.h"
|
||||
#include "toolbutton.h"
|
||||
#include "bottombuttongroup.h"
|
||||
#include "graphicsview.h"
|
||||
#include "navigatorview.h"
|
||||
#include "graphicsscene.h"
|
||||
#include "settingsdialog.h"
|
||||
#include "aboutdialog.h"
|
||||
#include "metadatamodel.h"
|
||||
#include "metadatadialog.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QMovie>
|
||||
@ -19,6 +24,7 @@
|
||||
#include <QCollator>
|
||||
#include <QClipboard>
|
||||
#include <QMimeData>
|
||||
#include <QWindow>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
@ -27,9 +33,14 @@
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow(parent)
|
||||
{
|
||||
this->setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
|
||||
if (Settings::instance()->stayOnTop()) {
|
||||
this->setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowMinimizeButtonHint | Qt::WindowStaysOnTopHint);
|
||||
} else {
|
||||
this->setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowMinimizeButtonHint);
|
||||
}
|
||||
|
||||
this->setAttribute(Qt::WA_TranslucentBackground, true);
|
||||
this->setMinimumSize(710, 530);
|
||||
this->setMinimumSize(350, 330);
|
||||
this->setWindowIcon(QIcon(":/icons/app-icon.svg"));
|
||||
this->setMouseTracking(true);
|
||||
|
||||
@ -101,13 +112,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
connect(m_bottomButtonGroup, &BottomButtonGroup::resetToOriginalBtnClicked,
|
||||
this, [ = ](){ m_graphicsView->resetScale(); });
|
||||
connect(m_bottomButtonGroup, &BottomButtonGroup::toggleWindowMaximum,
|
||||
this, [ = ](){
|
||||
if (isMaximized()) {
|
||||
showNormal();
|
||||
} else {
|
||||
showMaximized();
|
||||
}
|
||||
});
|
||||
this, &MainWindow::toggleMaximize);
|
||||
connect(m_bottomButtonGroup, &BottomButtonGroup::zoomInBtnClicked,
|
||||
this, [ = ](){ m_graphicsView->zoomView(1.25); });
|
||||
connect(m_bottomButtonGroup, &BottomButtonGroup::zoomOutBtnClicked,
|
||||
@ -187,11 +192,11 @@ void MainWindow::adjustWindowSizeBySceneRect()
|
||||
QSize screenSize = qApp->screenAt(QCursor::pos())->availableSize();
|
||||
if (screenSize.expandedTo(sceneSize) == screenSize) {
|
||||
// we can show the picture by increase the window size.
|
||||
if (screenSize.expandedTo(sceneSizeWithMargins) == screenSize) {
|
||||
this->resize(sceneSizeWithMargins);
|
||||
} else {
|
||||
this->resize(screenSize);
|
||||
}
|
||||
QSize finalSize = (screenSize.expandedTo(sceneSizeWithMargins) == screenSize) ?
|
||||
sceneSizeWithMargins : screenSize;
|
||||
// We have a very reasonable sizeHint() value ;P
|
||||
this->resize(finalSize.expandedTo(this->sizeHint()));
|
||||
|
||||
// We're sure the window can display the whole thing with 1:1 scale.
|
||||
// The old window size may cause fitInView call from resize() and the
|
||||
// above resize() call won't reset the scale back to 1:1, so we
|
||||
@ -237,9 +242,20 @@ void MainWindow::loadGalleryBySingleLocalFile(const QString &path)
|
||||
clearGallery();
|
||||
|
||||
for (int i = 0; i < entryList.count(); i++) {
|
||||
const QString & oneEntry = entryList.at(i);
|
||||
m_files.append(QUrl::fromLocalFile(dir.absoluteFilePath(oneEntry)));
|
||||
if (oneEntry == currentFileName) {
|
||||
const QString & fileName = entryList.at(i);
|
||||
const QString & oneEntry = dir.absoluteFilePath(fileName);
|
||||
QUrl url = QUrl::fromLocalFile(oneEntry);
|
||||
#ifdef Q_OS_WIN
|
||||
// TODO: remove this workaround when M$ change the "wsl$" hostname.
|
||||
// Qt will convert path "\\wsl$\" to "//wsl$/"...
|
||||
if (Q_UNLIKELY(oneEntry.startsWith(R"(//wsl$/)"))) {
|
||||
url.clear();
|
||||
url.setScheme(QStringLiteral("qtbug-86277"));
|
||||
url.setPath(oneEntry);
|
||||
}
|
||||
#endif // Q_OS_WIN
|
||||
m_files.append(url);
|
||||
if (fileName == currentFileName) {
|
||||
m_currentFileIndex = i;
|
||||
}
|
||||
}
|
||||
@ -325,8 +341,14 @@ void MainWindow::mousePressEvent(QMouseEvent *event)
|
||||
|
||||
void MainWindow::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->buttons() & Qt::LeftButton && m_clickedOnWindow) {
|
||||
if (event->buttons() & Qt::LeftButton && m_clickedOnWindow && !isMaximized()) {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||
if (!window()->windowHandle()->startSystemMove()) {
|
||||
move(event->globalPos() - m_oldMousePos);
|
||||
}
|
||||
#else
|
||||
move(event->globalPos() - m_oldMousePos);
|
||||
#endif // QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||
event->accept();
|
||||
}
|
||||
|
||||
@ -342,9 +364,22 @@ void MainWindow::mouseReleaseEvent(QMouseEvent *event)
|
||||
|
||||
void MainWindow::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
{
|
||||
switch (Settings::instance()->doubleClickBehavior()) {
|
||||
case ActionCloseWindow:
|
||||
quitAppAction();
|
||||
event->accept();
|
||||
break;
|
||||
case ActionMaximizeWindow:
|
||||
toggleMaximize();
|
||||
event->accept();
|
||||
break;
|
||||
case ActionDoNothing:
|
||||
break;
|
||||
}
|
||||
|
||||
return QMainWindow::mouseDoubleClickEvent(event);
|
||||
// blumia: don't call parent constructor here, seems it will cause mouse move
|
||||
// event get called even if we set event->accept();
|
||||
// return QMainWindow::mouseDoubleClickEvent(event);
|
||||
}
|
||||
|
||||
void MainWindow::wheelEvent(QWheelEvent *event)
|
||||
@ -432,23 +467,37 @@ void MainWindow::contextMenuEvent(QContextMenuEvent *event)
|
||||
});
|
||||
stayOnTopMode->setCheckable(true);
|
||||
stayOnTopMode->setChecked(stayOnTop());
|
||||
|
||||
QAction * protectedMode = new QAction(tr("Protected mode"));
|
||||
connect(protectedMode, &QAction::triggered, this, [ = ](){
|
||||
toggleProtectedMode();
|
||||
});
|
||||
protectedMode->setCheckable(true);
|
||||
protectedMode->setChecked(m_protectedMode);
|
||||
|
||||
QAction * toggleSettings = new QAction(tr("Configure..."));
|
||||
connect(toggleSettings, &QAction::triggered, this, [ = ](){
|
||||
SettingsDialog * sd = new SettingsDialog(this);
|
||||
sd->exec();
|
||||
sd->deleteLater();
|
||||
});
|
||||
|
||||
QAction * helpAction = new QAction(tr("Help"));
|
||||
connect(helpAction, &QAction::triggered, this, [ = ](){
|
||||
QStringList sl {
|
||||
tr("Launch application with image file path as argument to load the file."),
|
||||
tr("Drag and drop image file onto the window is also supported."),
|
||||
"",
|
||||
tr("Context menu option explanation:"),
|
||||
(tr("Stay on top") + " : " + tr("Make window stay on top of all other windows.")),
|
||||
(tr("Protected mode") + " : " + tr("Avoid close window accidentally. (eg. by double clicking the window)"))
|
||||
};
|
||||
m_graphicsView->showText(sl.join('\n'));
|
||||
AboutDialog * ad = new AboutDialog(this);
|
||||
ad->exec();
|
||||
ad->deleteLater();
|
||||
});
|
||||
|
||||
QAction * propertiesAction = new QAction(tr("Properties"));
|
||||
connect(propertiesAction, &QAction::triggered, this, [ = ](){
|
||||
MetadataModel * md = new MetadataModel();
|
||||
md->setFile(currentFileUrl.toLocalFile());
|
||||
|
||||
MetadataDialog * ad = new MetadataDialog(this);
|
||||
ad->setMetadataModel(md);
|
||||
ad->exec();
|
||||
ad->deleteLater();
|
||||
});
|
||||
|
||||
if (copyMenu->actions().count() == 1) {
|
||||
@ -465,7 +514,12 @@ void MainWindow::contextMenuEvent(QContextMenuEvent *event)
|
||||
menu->addAction(stayOnTopMode);
|
||||
menu->addAction(protectedMode);
|
||||
menu->addSeparator();
|
||||
menu->addAction(toggleSettings);
|
||||
menu->addAction(helpAction);
|
||||
if (currentFileUrl.isValid()) {
|
||||
menu->addSeparator();
|
||||
menu->addAction(propertiesAction);
|
||||
}
|
||||
menu->exec(mapToGlobal(event->pos()));
|
||||
menu->deleteLater();
|
||||
|
||||
@ -559,6 +613,11 @@ bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *r
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
QSize MainWindow::sizeHint() const
|
||||
{
|
||||
return QSize(710, 530);
|
||||
}
|
||||
|
||||
void MainWindow::centerWindow()
|
||||
{
|
||||
this->setGeometry(
|
||||
@ -626,3 +685,12 @@ void MainWindow::toggleFullscreen()
|
||||
showFullScreen();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::toggleMaximize()
|
||||
{
|
||||
if (isMaximized()) {
|
||||
showNormal();
|
||||
} else {
|
||||
showMaximized();
|
||||
}
|
||||
}
|
@ -50,6 +50,8 @@ protected slots:
|
||||
|
||||
bool nativeEvent(const QByteArray& eventType, void* message, long* result) override;
|
||||
|
||||
QSize sizeHint() const override;
|
||||
|
||||
void centerWindow();
|
||||
void closeWindow();
|
||||
void updateWidgetsPosition();
|
||||
@ -58,6 +60,7 @@ protected slots:
|
||||
bool stayOnTop();
|
||||
void quitAppAction(bool force = false);
|
||||
void toggleFullscreen();
|
||||
void toggleMaximize();
|
||||
|
||||
private:
|
||||
QPoint m_oldMousePos;
|
101
app/metadatadialog.cpp
Normal file
101
app/metadatadialog.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
#include "metadatadialog.h"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QPainter>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QTreeView>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHeaderView>
|
||||
|
||||
#include "metadatamodel.h"
|
||||
|
||||
class PropertyTreeView : public QTreeView
|
||||
{
|
||||
public:
|
||||
explicit PropertyTreeView(QWidget* parent) : QTreeView(parent) {}
|
||||
~PropertyTreeView() {}
|
||||
|
||||
protected:
|
||||
void rowsInserted(const QModelIndex& parent, int start, int end) override
|
||||
{
|
||||
QTreeView::rowsInserted(parent, start, end);
|
||||
if (!parent.isValid()) {
|
||||
for (int row = start; row <= end; ++row) {
|
||||
setupSection(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reset() override
|
||||
{
|
||||
QTreeView::reset();
|
||||
if (model()) {
|
||||
for (int row = 0; row < model()->rowCount(); ++row) {
|
||||
setupSection(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void setupSection(int row)
|
||||
{
|
||||
expand(model()->index(row, 0));
|
||||
setFirstColumnSpanned(row, QModelIndex(), true);
|
||||
}
|
||||
};
|
||||
|
||||
class PropertyTreeItemDelegate : public QStyledItemDelegate
|
||||
{
|
||||
public:
|
||||
PropertyTreeItemDelegate(QObject* parent)
|
||||
: QStyledItemDelegate(parent)
|
||||
{}
|
||||
|
||||
protected:
|
||||
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override
|
||||
{
|
||||
QStyleOptionViewItem opt = option;
|
||||
if (!index.parent().isValid()) {
|
||||
opt.font.setBold(true);
|
||||
opt.features.setFlag(QStyleOptionViewItem::Alternate);
|
||||
}
|
||||
QStyledItemDelegate::paint(painter, opt, index);
|
||||
}
|
||||
};
|
||||
|
||||
MetadataDialog::MetadataDialog(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, m_treeView(new PropertyTreeView(this))
|
||||
{
|
||||
m_treeView->setRootIsDecorated(false);
|
||||
m_treeView->setIndentation(0);
|
||||
m_treeView->setItemDelegate(new PropertyTreeItemDelegate(m_treeView));
|
||||
m_treeView->header()->resizeSection(0, sizeHint().width() / 2);
|
||||
|
||||
setWindowTitle(tr("Image Metadata"));
|
||||
|
||||
QDialogButtonBox * buttonBox = new QDialogButtonBox(QDialogButtonBox::Close);
|
||||
|
||||
setLayout(new QVBoxLayout);
|
||||
layout()->addWidget(m_treeView);
|
||||
layout()->addWidget(buttonBox);
|
||||
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::close);
|
||||
|
||||
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||
}
|
||||
|
||||
MetadataDialog::~MetadataDialog()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MetadataDialog::setMetadataModel(MetadataModel * model)
|
||||
{
|
||||
m_treeView->setModel(model);
|
||||
}
|
||||
|
||||
QSize MetadataDialog::sizeHint() const
|
||||
{
|
||||
return QSize(520, 350);
|
||||
}
|
26
app/metadatadialog.h
Normal file
26
app/metadatadialog.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef METADATADIALOG_H
|
||||
#define METADATADIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTreeView;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class MetadataModel;
|
||||
class MetadataDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MetadataDialog(QWidget * parent);
|
||||
~MetadataDialog() override;
|
||||
|
||||
void setMetadataModel(MetadataModel * model);
|
||||
|
||||
QSize sizeHint() const override;
|
||||
|
||||
private:
|
||||
QTreeView * m_treeView = nullptr;
|
||||
};
|
||||
|
||||
#endif // METADATADIALOG_H
|
205
app/metadatamodel.cpp
Normal file
205
app/metadatamodel.cpp
Normal file
@ -0,0 +1,205 @@
|
||||
#include "metadatamodel.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDateTime>
|
||||
#include <QFileInfo>
|
||||
#include <QImageReader>
|
||||
|
||||
MetadataModel::MetadataModel(QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
MetadataModel::~MetadataModel()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MetadataModel::setFile(const QString &imageFilePath)
|
||||
{
|
||||
QFileInfo fileInfo(imageFilePath);
|
||||
// It'll be fine if we don't re-use the image reader we pass to the graphics scene for now.
|
||||
QImageReader imgReader(imageFilePath);
|
||||
imgReader.setAutoTransform(true);
|
||||
imgReader.setDecideFormatFromContent(true);
|
||||
|
||||
const QString & itemTypeString = tr("%1 File").arg(QString(imgReader.format().toUpper()));
|
||||
const QString & sizeString = QLocale().formattedDataSize(fileInfo.size());
|
||||
const QString & birthTimeString = QLocale().toString(fileInfo.birthTime(), QLocale::LongFormat);
|
||||
const QString & lastModifiedTimeString = QLocale().toString(fileInfo.lastModified(), QLocale::LongFormat);
|
||||
const QString & imageDimensionsString = imageSize(imgReader.size());
|
||||
const QString & imageRatioString = imageSizeRatio(imgReader.size());
|
||||
|
||||
#if 0
|
||||
appendSection(QStringLiteral("Description"), tr("Description", "Section name."));
|
||||
appendSection(QStringLiteral("Origin"), tr("Origin", "Section name."));
|
||||
#endif // 0
|
||||
appendSection(QStringLiteral("Image"), tr("Image", "Section name."));
|
||||
#if 0
|
||||
appendSection(QStringLiteral("Camera"), tr("Camera", "Section name."));
|
||||
appendSection(QStringLiteral("AdvancedPhoto"), tr("Advanced photo", "Section name."));
|
||||
appendSection(QStringLiteral("GPS"), tr("GPS", "Section name."));
|
||||
#endif // 0
|
||||
appendSection(QStringLiteral("File"), tr("File", "Section name."));
|
||||
|
||||
appendProperty(QStringLiteral("Image"), QStringLiteral("Image.Dimensions"),
|
||||
tr("Dimensions"), imageDimensionsString);
|
||||
appendProperty(QStringLiteral("Image"), QStringLiteral("Image.SizeRatio"),
|
||||
tr("Aspect Ratio"), imageRatioString);
|
||||
|
||||
appendProperty(QStringLiteral("File"), QStringLiteral("File.Name"),
|
||||
tr("Name"), fileInfo.fileName());
|
||||
appendProperty(QStringLiteral("File"), QStringLiteral("File.ItemType"),
|
||||
tr("Item type"), itemTypeString);
|
||||
appendProperty(QStringLiteral("File"), QStringLiteral("File.Path"),
|
||||
tr("Folder path"), fileInfo.path());
|
||||
appendProperty(QStringLiteral("File"), QStringLiteral("File.Size"),
|
||||
tr("Size"), sizeString);
|
||||
appendProperty(QStringLiteral("File"), QStringLiteral("File.CreatedTime"),
|
||||
tr("Date Created"), birthTimeString);
|
||||
appendProperty(QStringLiteral("File"), QStringLiteral("File.LastModified"),
|
||||
tr("Date Modified"), lastModifiedTimeString);
|
||||
}
|
||||
|
||||
QString MetadataModel::imageSize(const QSize &size)
|
||||
{
|
||||
QString imageSize;
|
||||
|
||||
if (size.isValid()) {
|
||||
imageSize = tr("%1 x %2").arg(QString::number(size.width()), QString::number(size.height()));
|
||||
} else {
|
||||
imageSize = QLatin1Char('-');
|
||||
}
|
||||
|
||||
return imageSize;
|
||||
}
|
||||
|
||||
int simplegcd(int a, int b) {
|
||||
return b == 0 ? a : simplegcd(b, a % b);
|
||||
}
|
||||
|
||||
QString MetadataModel::imageSizeRatio(const QSize &size)
|
||||
{
|
||||
if (!size.isValid()) {
|
||||
return QStringLiteral("-");
|
||||
}
|
||||
int gcd = simplegcd(size.width(), size.height());
|
||||
return tr("%1 : %2").arg(QString::number(size.width() / gcd), QString::number(size.height() / gcd));
|
||||
}
|
||||
|
||||
bool MetadataModel::appendSection(const QString §ionKey, const QString §ionDisplayName)
|
||||
{
|
||||
if (m_sections.contains(sectionKey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_sections.append(sectionKey);
|
||||
m_sectionProperties[sectionKey] = qMakePair<QString, QList<QString> >(sectionDisplayName, {});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MetadataModel::appendProperty(const QString §ionKey, const QString &propertyKey, const QString &propertyDisplayName, const QString &propertyValue)
|
||||
{
|
||||
if (!m_sections.contains(sectionKey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QList<QString> & propertyList = m_sectionProperties[sectionKey].second;
|
||||
if (!propertyList.contains(propertyKey)) {
|
||||
propertyList.append(propertyKey);
|
||||
}
|
||||
|
||||
m_properties[propertyKey] = qMakePair<QString, QString>(propertyDisplayName, propertyValue);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MetadataModel::updateProperty(const QString &propertyKey, const QString &propertyValue)
|
||||
{
|
||||
if (m_properties.contains(propertyKey)) {
|
||||
m_properties[propertyKey].second = propertyValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QModelIndex MetadataModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
if (!hasIndex(row, column, parent)) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
if (!parent.isValid()) {
|
||||
return createIndex(row, column, RowType::SectionRow);
|
||||
} else {
|
||||
// internalid param: row means nth section it belongs to.
|
||||
return createIndex(row, column, RowType::PropertyRow + parent.row());
|
||||
}
|
||||
}
|
||||
|
||||
QModelIndex MetadataModel::parent(const QModelIndex &child) const
|
||||
{
|
||||
if (!child.isValid()) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
if (child.internalId() == RowType::SectionRow) {
|
||||
return QModelIndex();
|
||||
} else {
|
||||
return createIndex(child.internalId() - RowType::PropertyRow, 0, SectionRow);
|
||||
}
|
||||
}
|
||||
|
||||
int MetadataModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (!parent.isValid()) {
|
||||
return m_sections.count();
|
||||
}
|
||||
|
||||
if (parent.internalId() == RowType::SectionRow) {
|
||||
const QString & sectionKey = m_sections[parent.row()];
|
||||
return m_sectionProperties[sectionKey].second.count();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MetadataModel::columnCount(const QModelIndex &) const
|
||||
{
|
||||
// Always key(display name) and value.
|
||||
return 2;
|
||||
}
|
||||
|
||||
QVariant MetadataModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if (role != Qt::DisplayRole) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if (index.internalId() == RowType::SectionRow) {
|
||||
return (index.column() == 0) ? m_sectionProperties[m_sections[index.row()]].first
|
||||
: QVariant();
|
||||
} else {
|
||||
int sectionIndex = index.internalId() - RowType::PropertyRow;
|
||||
const QString & sectionKey = m_sections[sectionIndex];
|
||||
const QList<QString> & propertyList = m_sectionProperties[sectionKey].second;
|
||||
return (index.column() == 0) ? m_properties[propertyList[index.row()]].first
|
||||
: m_properties[propertyList[index.row()]].second;
|
||||
}
|
||||
}
|
||||
|
||||
QVariant MetadataModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (orientation == Qt::Vertical || role != Qt::DisplayRole) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
return section == 0 ? tr("Property") : tr("Value");
|
||||
}
|
43
app/metadatamodel.h
Normal file
43
app/metadatamodel.h
Normal file
@ -0,0 +1,43 @@
|
||||
#ifndef METADATAMODEL_H
|
||||
#define METADATAMODEL_H
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
|
||||
class MetadataModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MetadataModel(QObject *parent = nullptr);
|
||||
~MetadataModel();
|
||||
|
||||
void setFile(const QString & imageFilePath);
|
||||
static QString imageSize(const QSize &size);
|
||||
static QString imageSizeRatio(const QSize &size);
|
||||
bool appendSection(const QString & sectionKey, const QString & sectionDisplayName);
|
||||
bool appendProperty(const QString & sectionKey, const QString & propertyKey,
|
||||
const QString & propertyDisplayName, const QString & propertyValue = QString());
|
||||
bool updateProperty(const QString & propertyKey, const QString & propertyValue);
|
||||
|
||||
private:
|
||||
enum RowType : quintptr {
|
||||
SectionRow,
|
||||
PropertyRow,
|
||||
};
|
||||
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
|
||||
QModelIndex parent(const QModelIndex &child) const override;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex & = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
|
||||
// [SECTION_KEY]
|
||||
QList<QString> m_sections;
|
||||
// {SECTION_KEY: (SECTION_DISPLAY_NAME, [PROPERTY_KEY])}
|
||||
QMap<QString, QPair<QString, QList<QString> > > m_sectionProperties;
|
||||
// {PROPERTY_KEY: (PROPERTY_DISPLAY_NAME, PROPERTY_VALUE)}
|
||||
QMap<QString, QPair<QString, QString> > m_properties;
|
||||
};
|
||||
|
||||
#endif // METADATAMODEL_H
|
86
app/settings.cpp
Normal file
86
app/settings.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
#include "settings.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QStandardPaths>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
|
||||
Settings *Settings::m_settings_instance = nullptr;
|
||||
|
||||
Settings *Settings::instance()
|
||||
{
|
||||
if (!m_settings_instance) {
|
||||
m_settings_instance = new Settings;
|
||||
}
|
||||
|
||||
return m_settings_instance;
|
||||
}
|
||||
|
||||
bool Settings::stayOnTop()
|
||||
{
|
||||
return m_qsettings->value("stay_on_top", true).toBool();
|
||||
}
|
||||
|
||||
DoubleClickBehavior Settings::doubleClickBehavior()
|
||||
{
|
||||
QString result = m_qsettings->value("double_click_behavior", "close").toString().toLower();
|
||||
|
||||
return stringToDoubleClickBehavior(result);
|
||||
}
|
||||
|
||||
void Settings::setStayOnTop(bool on)
|
||||
{
|
||||
m_qsettings->setValue("stay_on_top", on);
|
||||
m_qsettings->sync();
|
||||
}
|
||||
|
||||
void Settings::setDoubleClickBehavior(DoubleClickBehavior dcb)
|
||||
{
|
||||
m_qsettings->setValue("double_click_behavior", doubleClickBehaviorToString(dcb));
|
||||
m_qsettings->sync();
|
||||
}
|
||||
|
||||
QString Settings::doubleClickBehaviorToString(DoubleClickBehavior dcb)
|
||||
{
|
||||
static QMap<DoubleClickBehavior, QString> _map {
|
||||
{ActionCloseWindow, "close"},
|
||||
{ActionMaximizeWindow, "maximize"},
|
||||
{ActionDoNothing, "ignore"}
|
||||
};
|
||||
|
||||
return _map.value(dcb, "close");
|
||||
}
|
||||
|
||||
DoubleClickBehavior Settings::stringToDoubleClickBehavior(QString str)
|
||||
{
|
||||
static QMap<QString, DoubleClickBehavior> _map {
|
||||
{"close", ActionCloseWindow},
|
||||
{"maximize", ActionMaximizeWindow},
|
||||
{"ignore", ActionDoNothing}
|
||||
};
|
||||
|
||||
return _map.value(str, ActionCloseWindow);
|
||||
}
|
||||
|
||||
Settings::Settings()
|
||||
: QObject(qApp)
|
||||
{
|
||||
QString configPath;
|
||||
|
||||
#ifdef FLAG_PORTABLE_MODE_SUPPORT
|
||||
QString portableConfigDirPath = QDir(QCoreApplication::applicationDirPath()).absoluteFilePath("data");
|
||||
QFileInfo portableConfigDirInfo(portableConfigDirPath);
|
||||
if (portableConfigDirInfo.exists() && portableConfigDirInfo.isDir() && portableConfigDirInfo.isWritable()) {
|
||||
// we can use it.
|
||||
configPath = portableConfigDirPath;
|
||||
}
|
||||
#endif // FLAG_PORTABLE_MODE_SUPPORT
|
||||
|
||||
// %LOCALAPPDATA% under Windows.
|
||||
if (configPath.isEmpty()) {
|
||||
configPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
|
||||
}
|
||||
|
||||
m_qsettings = new QSettings(QDir(configPath).absoluteFilePath("config.ini"), QSettings::IniFormat, this);
|
||||
}
|
||||
|
41
app/settings.h
Normal file
41
app/settings.h
Normal file
@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QSettings>
|
||||
|
||||
enum DoubleClickBehavior {
|
||||
ActionDoNothing,
|
||||
ActionCloseWindow,
|
||||
ActionMaximizeWindow,
|
||||
|
||||
ActionStart = ActionDoNothing,
|
||||
ActionEnd = ActionMaximizeWindow
|
||||
};
|
||||
|
||||
class Settings : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static Settings *instance();
|
||||
|
||||
bool stayOnTop();
|
||||
DoubleClickBehavior doubleClickBehavior();
|
||||
|
||||
void setStayOnTop(bool on);
|
||||
void setDoubleClickBehavior(DoubleClickBehavior dcb);
|
||||
|
||||
static QString doubleClickBehaviorToString(DoubleClickBehavior dcb);
|
||||
static DoubleClickBehavior stringToDoubleClickBehavior(QString str);
|
||||
|
||||
private:
|
||||
Settings();
|
||||
|
||||
static Settings *m_settings_instance;
|
||||
|
||||
QSettings *m_qsettings;
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
};
|
||||
|
53
app/settingsdialog.cpp
Normal file
53
app/settingsdialog.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
#include "settingsdialog.h"
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QFormLayout>
|
||||
#include <QStringListModel>
|
||||
|
||||
SettingsDialog::SettingsDialog(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, m_stayOnTop(new QCheckBox)
|
||||
, m_doubleClickBehavior(new QComboBox)
|
||||
{
|
||||
this->setWindowTitle(tr("Settings"));
|
||||
|
||||
QFormLayout * settingsForm = new QFormLayout(this);
|
||||
|
||||
static QMap<DoubleClickBehavior, QString> _map {
|
||||
{ ActionDoNothing, tr("Do nothing") },
|
||||
{ ActionCloseWindow, tr("Close the window") },
|
||||
{ ActionMaximizeWindow, tr("Toggle maximize") }
|
||||
};
|
||||
|
||||
QStringList dropDown;
|
||||
for (int dcb = ActionStart; dcb <= ActionEnd; dcb++) {
|
||||
dropDown.append(_map.value(static_cast<DoubleClickBehavior>(dcb)));
|
||||
}
|
||||
|
||||
settingsForm->addRow(tr("Stay on top when start-up"), m_stayOnTop);
|
||||
settingsForm->addRow(tr("Double-click behavior"), m_doubleClickBehavior);
|
||||
|
||||
m_stayOnTop->setChecked(Settings::instance()->stayOnTop());
|
||||
m_doubleClickBehavior->setModel(new QStringListModel(dropDown));
|
||||
DoubleClickBehavior dcb = Settings::instance()->doubleClickBehavior();
|
||||
m_doubleClickBehavior->setCurrentIndex(static_cast<int>(dcb));
|
||||
|
||||
connect(m_stayOnTop, &QCheckBox::stateChanged, this, [ = ](int state){
|
||||
Settings::instance()->setStayOnTop(state == Qt::Checked);
|
||||
});
|
||||
|
||||
connect(m_doubleClickBehavior, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [ = ](int index){
|
||||
Settings::instance()->setDoubleClickBehavior(static_cast<DoubleClickBehavior>(index));
|
||||
});
|
||||
|
||||
this->setMinimumSize(300, 61); // not sure why it complain "Unable to set geometry"
|
||||
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||
}
|
||||
|
||||
SettingsDialog::~SettingsDialog()
|
||||
{
|
||||
|
||||
}
|
25
app/settingsdialog.h
Normal file
25
app/settingsdialog.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef SETTINGSDIALOG_H
|
||||
#define SETTINGSDIALOG_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QDialog>
|
||||
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class SettingsDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SettingsDialog(QWidget *parent = nullptr);
|
||||
~SettingsDialog();
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
private:
|
||||
QCheckBox * m_stayOnTop = nullptr;
|
||||
QComboBox * m_doubleClickBehavior = nullptr;
|
||||
};
|
||||
|
||||
#endif // SETTINGSDIALOG_H
|
@ -59,6 +59,7 @@ build_script:
|
||||
- mingw32-make install
|
||||
# fixme: I don't know how to NOT make the binary installed to the ./bin/ folder...
|
||||
- cd bin
|
||||
- copy %APPVEYOR_BUILD_FOLDER%\LICENSE .
|
||||
- copy C:\projects\cmake\bin\libKF5Archive.dll .
|
||||
- windeployqt --verbose=2 --no-quick-import --no-translations --no-opengl-sw --no-angle --no-system-d3d-compiler --release .\ppic.exe
|
||||
# for debug..
|
||||
|
19
dist/appstream/net.blumia.pineapple-pictures.appdata.xml
vendored
Normal file
19
dist/appstream/net.blumia.pineapple-pictures.appdata.xml
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<component type="desktop-application">
|
||||
<id>net.blumia.pineapple-pictures</id>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>MIT</project_license>
|
||||
<name>Pineapple Pictures</name>
|
||||
<name xml:lang="zh-CN">菠萝看图</name>
|
||||
<summary>Image Viewer</summary>
|
||||
<summary xml:lang="zh-CN">图像查看器</summary>
|
||||
<description>
|
||||
<p>Pineapple Pictures is a lightweight and easy to use image viewer, comes with a handy navigation thumbnail when zoom-in, and doesn't contains any image management support.</p>
|
||||
<p xml:lang="zh-CN">菠萝看图是一个轻量级易用的图像查看器,在图片放大时提供了方便的鸟瞰导航功能,且不包含任何图片管理功能。</p>
|
||||
</description>
|
||||
<url type="homepage">https://github.com/BLumia/pineapple-pictures</url>
|
||||
<url type="bugtracker">https://github.com/BLumia/pineapple-pictures/issues</url>
|
||||
<provides>
|
||||
<binary>ppic</binary>
|
||||
</provides>
|
||||
</component>
|
@ -1,12 +1,15 @@
|
||||
[Desktop Entry]
|
||||
Categories=Graphics;
|
||||
Comment=Pineapple Pictures Image Viewer.
|
||||
Comment=Pineapple Pictures is a lightweight image viewer
|
||||
Comment[zh_CN]=菠萝看图是一个轻量级的图像查看器
|
||||
Exec=ppic %F
|
||||
GenericName=Pictures
|
||||
GenericName=Image Viewer
|
||||
GenericName[zh_CN]=图像查看器
|
||||
Icon=pineapple-pictures
|
||||
Keywords=Picture;Image;Viewer;Jpg;Jpeg;Png;
|
||||
MimeType=image/bmp;image/bmp24;image/jpg;image/jpe;image/jpeg;image/jpeg24;image/jng;image/pcd;image/pcx;image/png;image/tif;image/tiff;image/tiff24;image/dds;image/gif;image/sgi;image/j2k;image/jp2;image/pct;image/wdp;image/arw;image/icb;image/dng;image/vda;image/vst;image/svg;image/ptif;image/mef;image/xbm;image/svg+xml;
|
||||
Name=Pineapple Pictures
|
||||
Name[zh_CN]=菠萝看图
|
||||
StartupNotify=false
|
||||
Type=Application
|
||||
Terminal=false
|
1680
icons/app-icon.svg
1680
icons/app-icon.svg
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 44 KiB |
@ -1,123 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1">
|
||||
<context>
|
||||
<name>GraphicsScene</name>
|
||||
<message>
|
||||
<location filename="../graphicsscene.cpp" line="16"/>
|
||||
<source>Drag image here</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GraphicsView</name>
|
||||
<message>
|
||||
<location filename="../graphicsview.cpp" line="243"/>
|
||||
<source>File url list is empty</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../graphicsview.cpp" line="46"/>
|
||||
<source>File is not a valid image</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../graphicsview.cpp" line="251"/>
|
||||
<source>Image data is invalid</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../graphicsview.cpp" line="258"/>
|
||||
<source>Not supported mimedata: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindow</name>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="173"/>
|
||||
<source>File url list is empty</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="382"/>
|
||||
<source>&Copy</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="403"/>
|
||||
<source>Copy P&ixmap</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="408"/>
|
||||
<source>Copy &File Path</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="418"/>
|
||||
<source>&Paste Image</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="424"/>
|
||||
<source>&Paste Image File</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="429"/>
|
||||
<location filename="../mainwindow.cpp" line="448"/>
|
||||
<source>Stay on top</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="435"/>
|
||||
<location filename="../mainwindow.cpp" line="449"/>
|
||||
<source>Protected mode</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="441"/>
|
||||
<source>Help</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="444"/>
|
||||
<source>Launch application with image file path as argument to load the file.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="445"/>
|
||||
<source>Drag and drop image file onto the window is also supported.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="447"/>
|
||||
<source>Context menu option explanation:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="448"/>
|
||||
<source>Make window stay on top of all other windows.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="449"/>
|
||||
<source>Avoid close window accidentally. (eg. by double clicking the window)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>main</name>
|
||||
<message>
|
||||
<location filename="../main.cpp" line="27"/>
|
||||
<source>Pineapple Pictures</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../main.cpp" line="31"/>
|
||||
<source>File list.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
@ -1,127 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="zh_CN">
|
||||
<context>
|
||||
<name>GraphicsScene</name>
|
||||
<message>
|
||||
<location filename="../graphicsscene.cpp" line="16"/>
|
||||
<source>Drag image here</source>
|
||||
<translation>拖放图片至此</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GraphicsView</name>
|
||||
<message>
|
||||
<location filename="../graphicsview.cpp" line="243"/>
|
||||
<source>File url list is empty</source>
|
||||
<translation>文件 URL 列表为空</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../graphicsview.cpp" line="46"/>
|
||||
<source>File is not a valid image</source>
|
||||
<translation>文件不是有效的图片文件</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../graphicsview.cpp" line="251"/>
|
||||
<source>Image data is invalid</source>
|
||||
<translation>图片数据无效</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../graphicsview.cpp" line="258"/>
|
||||
<source>Not supported mimedata: %1</source>
|
||||
<translation>不受支持的 MimeData 格式:%1</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindow</name>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="173"/>
|
||||
<source>File url list is empty</source>
|
||||
<translation>文件 URL 列表为空</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="382"/>
|
||||
<source>&Copy</source>
|
||||
<translation>复制(&C)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy &Pixmap</source>
|
||||
<translation type="vanished">复制位图(&P)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="403"/>
|
||||
<source>Copy P&ixmap</source>
|
||||
<translation>复制位图(&I)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="408"/>
|
||||
<source>Copy &File Path</source>
|
||||
<translation>复制文件路径(&F)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="418"/>
|
||||
<source>&Paste Image</source>
|
||||
<translation>粘贴图像(&P)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="424"/>
|
||||
<source>&Paste Image File</source>
|
||||
<translation>粘贴图像文件(&P)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="429"/>
|
||||
<location filename="../mainwindow.cpp" line="448"/>
|
||||
<source>Stay on top</source>
|
||||
<translation>总在最前</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="435"/>
|
||||
<location filename="../mainwindow.cpp" line="449"/>
|
||||
<source>Protected mode</source>
|
||||
<translation>保护模式</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="441"/>
|
||||
<source>Help</source>
|
||||
<translation>帮助</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="444"/>
|
||||
<source>Launch application with image file path as argument to load the file.</source>
|
||||
<translation>以图片文件的路径作为参数运行程序即可直接打开图片文件。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="445"/>
|
||||
<source>Drag and drop image file onto the window is also supported.</source>
|
||||
<translation>也支持拖放图片文件到窗口内来加载图片。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="447"/>
|
||||
<source>Context menu option explanation:</source>
|
||||
<translation>菜单项说明:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="448"/>
|
||||
<source>Make window stay on top of all other windows.</source>
|
||||
<translation>使窗口始终至于其它非置顶窗口上方。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="449"/>
|
||||
<source>Avoid close window accidentally. (eg. by double clicking the window)</source>
|
||||
<translation>避免窗口意外关闭。(如:不小心双击了窗口触发了关闭窗口行为)</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>main</name>
|
||||
<message>
|
||||
<location filename="../main.cpp" line="27"/>
|
||||
<source>Pineapple Pictures</source>
|
||||
<translation>菠萝看图</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../main.cpp" line="31"/>
|
||||
<source>File list.</source>
|
||||
<translation>文件列表。</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
67
pineapple-pictures.pro
Normal file
67
pineapple-pictures.pro
Normal file
@ -0,0 +1,67 @@
|
||||
QT += core widgets gui svg
|
||||
|
||||
TARGET = ppic
|
||||
TEMPLATE = app
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
# any feature of Qt which has been marked as deprecated (the exact warnings
|
||||
# depend on your compiler). Please consult the documentation of the
|
||||
# deprecated API in order to know how to port your code away from it.
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
# You can also make your code fail to compile if you use deprecated APIs.
|
||||
# In order to do so, uncomment the following line.
|
||||
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||
|
||||
CONFIG += c++11 lrelease embed_translations
|
||||
|
||||
SOURCES += \
|
||||
app/aboutdialog.cpp \
|
||||
app/main.cpp \
|
||||
app/mainwindow.cpp \
|
||||
app/graphicsview.cpp \
|
||||
app/bottombuttongroup.cpp \
|
||||
app/graphicsscene.cpp \
|
||||
app/navigatorview.cpp \
|
||||
app/opacityhelper.cpp \
|
||||
app/toolbutton.cpp \
|
||||
app/settings.cpp \
|
||||
app/settingsdialog.cpp \
|
||||
app/metadatamodel.cpp \
|
||||
app/metadatadialog.cpp
|
||||
|
||||
HEADERS += \
|
||||
app/aboutdialog.h \
|
||||
app/mainwindow.h \
|
||||
app/graphicsview.h \
|
||||
app/bottombuttongroup.h \
|
||||
app/graphicsscene.h \
|
||||
app/navigatorview.h \
|
||||
app/opacityhelper.h \
|
||||
app/toolbutton.h \
|
||||
app/settings.h \
|
||||
app/settingsdialog.h \
|
||||
app/metadatamodel.h \
|
||||
app/metadatadialog.h
|
||||
|
||||
TRANSLATIONS = \
|
||||
translations/PineapplePictures.ts \
|
||||
translations/PineapplePictures_zh_CN.ts
|
||||
|
||||
# Default rules for deployment.
|
||||
qnx: target.path = /tmp/$${TARGET}/bin
|
||||
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||
!isEmpty(target.path): INSTALLS += target
|
||||
|
||||
RESOURCES += \
|
||||
resources.qrc
|
||||
|
||||
# Generate from svg:
|
||||
# magick convert -background none app-icon.svg -define icon:auto-resize="16,32,48,64,128,256" app-icon.ico
|
||||
RC_ICONS = icons/app-icon.ico
|
||||
|
||||
# Windows only, for rc file (we're not going to use the .rc file in this repo)
|
||||
QMAKE_TARGET_PRODUCT = Pineapple Pictures
|
||||
QMAKE_TARGET_DESCRIPTION = Pineapple Pictures - Image Viewer
|
||||
QMAKE_TARGET_COPYRIGHT = MIT/Expat License - Copyright (C) 2020 Gary Wang
|
423
translations/PineapplePictures.ts
Normal file
423
translations/PineapplePictures.ts
Normal file
@ -0,0 +1,423 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1">
|
||||
<context>
|
||||
<name>AboutDialog</name>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="22"/>
|
||||
<source>About</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="25"/>
|
||||
<source>Launch application with image file path as argument to load the file.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="26"/>
|
||||
<source>Drag and drop image file onto the window is also supported.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="27"/>
|
||||
<source>Context menu option explanation:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="31"/>
|
||||
<source>Make window stay on top of all other windows.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="35"/>
|
||||
<source>Avoid close window accidentally. (eg. by double clicking the window)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="44"/>
|
||||
<source>Version: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="47"/>
|
||||
<source>Copyright (c) 2020 %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="49"/>
|
||||
<source>Logo designed by %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="51"/>
|
||||
<source>Built with Qt %1 (%2)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="52"/>
|
||||
<source>Source code</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="58"/>
|
||||
<source>Contributors</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="60"/>
|
||||
<source>List of contributors on GitHub</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="61"/>
|
||||
<source>Thanks to all people who contributed to this project.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="65"/>
|
||||
<source>Translators</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="66"/>
|
||||
<source>I would like to thank the following people who volunteered to translate this application.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="131"/>
|
||||
<source>&Special Thanks</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="133"/>
|
||||
<source>&Third-party Libraries</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="72"/>
|
||||
<source>Your Rights</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="74"/>
|
||||
<source>%1 is released under the MIT License.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="75"/>
|
||||
<source>This license grants people a number of freedoms:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="76"/>
|
||||
<source>You are free to use %1, for any purpose</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="77"/>
|
||||
<source>You are free to distribute %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="78"/>
|
||||
<source>You can study how %1 works and change it</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="79"/>
|
||||
<source>You can distribute changed versions of %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="81"/>
|
||||
<source>The MIT license guarantees you this freedom. Nobody is ever permitted to take it away.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="109"/>
|
||||
<source>Third-party Libraries used by %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="110"/>
|
||||
<source>%1 is built on the following free software libraries:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="129"/>
|
||||
<source>&Help</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="130"/>
|
||||
<source>&About</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="132"/>
|
||||
<source>&License</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GraphicsScene</name>
|
||||
<message>
|
||||
<location filename="../app/graphicsscene.cpp" line="16"/>
|
||||
<source>Drag image here</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GraphicsView</name>
|
||||
<message>
|
||||
<location filename="../app/graphicsview.cpp" line="261"/>
|
||||
<source>File url list is empty</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/graphicsview.cpp" line="53"/>
|
||||
<source>File is not a valid image</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/graphicsview.cpp" line="55"/>
|
||||
<location filename="../app/graphicsview.cpp" line="59"/>
|
||||
<source>Image data is invalid or currently unsupported</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/graphicsview.cpp" line="269"/>
|
||||
<source>Image data is invalid</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/graphicsview.cpp" line="276"/>
|
||||
<source>Not supported mimedata: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindow</name>
|
||||
<message>
|
||||
<location filename="../app/mainwindow.cpp" line="178"/>
|
||||
<source>File url list is empty</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/mainwindow.cpp" line="417"/>
|
||||
<source>&Copy</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/mainwindow.cpp" line="438"/>
|
||||
<source>Copy P&ixmap</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/mainwindow.cpp" line="443"/>
|
||||
<source>Copy &File Path</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/mainwindow.cpp" line="453"/>
|
||||
<source>&Paste Image</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/mainwindow.cpp" line="459"/>
|
||||
<source>&Paste Image File</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/mainwindow.cpp" line="492"/>
|
||||
<source>Properties</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="30"/>
|
||||
<location filename="../app/mainwindow.cpp" line="464"/>
|
||||
<source>Stay on top</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="34"/>
|
||||
<location filename="../app/mainwindow.cpp" line="471"/>
|
||||
<source>Protected mode</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/mainwindow.cpp" line="478"/>
|
||||
<source>Configure...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/mainwindow.cpp" line="485"/>
|
||||
<source>Help</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MetadataDialog</name>
|
||||
<message>
|
||||
<location filename="../app/metadatadialog.cpp" line="75"/>
|
||||
<source>Image Metadata</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MetadataModel</name>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="36"/>
|
||||
<source>Origin</source>
|
||||
<comment>Section name.</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="38"/>
|
||||
<source>Image</source>
|
||||
<comment>Section name.</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="44"/>
|
||||
<source>File</source>
|
||||
<comment>Section name.</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="40"/>
|
||||
<source>Camera</source>
|
||||
<comment>Section name.</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="27"/>
|
||||
<source>%1 File</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="35"/>
|
||||
<source>Description</source>
|
||||
<comment>Section name.</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="41"/>
|
||||
<source>Advanced photo</source>
|
||||
<comment>Section name.</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="42"/>
|
||||
<source>GPS</source>
|
||||
<comment>Section name.</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="47"/>
|
||||
<source>Dimensions</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="49"/>
|
||||
<source>Aspect Ratio</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="52"/>
|
||||
<source>Name</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="54"/>
|
||||
<source>Item type</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="56"/>
|
||||
<source>Folder path</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="58"/>
|
||||
<source>Size</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="60"/>
|
||||
<source>Date Created</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="62"/>
|
||||
<source>Date Modified</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="70"/>
|
||||
<source>%1 x %2</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="88"/>
|
||||
<source>%1 : %2</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="204"/>
|
||||
<source>Property</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="204"/>
|
||||
<source>Value</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsDialog</name>
|
||||
<message>
|
||||
<location filename="../app/settingsdialog.cpp" line="15"/>
|
||||
<source>Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/settingsdialog.cpp" line="20"/>
|
||||
<source>Do nothing</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/settingsdialog.cpp" line="21"/>
|
||||
<source>Close the window</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/settingsdialog.cpp" line="22"/>
|
||||
<source>Toggle maximize</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/settingsdialog.cpp" line="30"/>
|
||||
<source>Stay on top when start-up</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/settingsdialog.cpp" line="31"/>
|
||||
<source>Double-click behavior</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>main</name>
|
||||
<message>
|
||||
<location filename="../app/main.cpp" line="27"/>
|
||||
<source>Pineapple Pictures</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/main.cpp" line="31"/>
|
||||
<source>File list.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
423
translations/PineapplePictures_zh_CN.ts
Normal file
423
translations/PineapplePictures_zh_CN.ts
Normal file
@ -0,0 +1,423 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="zh_CN">
|
||||
<context>
|
||||
<name>AboutDialog</name>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="22"/>
|
||||
<source>About</source>
|
||||
<translation>关于</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="25"/>
|
||||
<source>Launch application with image file path as argument to load the file.</source>
|
||||
<translation>以图片文件的路径作为参数运行程序即可直接打开图片文件。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="26"/>
|
||||
<source>Drag and drop image file onto the window is also supported.</source>
|
||||
<translation>也支持拖放图片文件到窗口内来加载图片。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="27"/>
|
||||
<source>Context menu option explanation:</source>
|
||||
<translation>菜单项说明:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="31"/>
|
||||
<source>Make window stay on top of all other windows.</source>
|
||||
<translation>使窗口始终至于其它非置顶窗口上方。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="35"/>
|
||||
<source>Avoid close window accidentally. (eg. by double clicking the window)</source>
|
||||
<translation>避免窗口意外关闭。(如:不小心双击了窗口触发了关闭窗口行为)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="44"/>
|
||||
<source>Version: %1</source>
|
||||
<translation>版本: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="47"/>
|
||||
<source>Copyright (c) 2020 %1</source>
|
||||
<translation>版权所有 (c) 2020 %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="49"/>
|
||||
<source>Logo designed by %1</source>
|
||||
<translation>Logo 由 %1 设计</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="51"/>
|
||||
<source>Built with Qt %1 (%2)</source>
|
||||
<translation>使用 Qt %1 (%2) 进行构建</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="52"/>
|
||||
<source>Source code</source>
|
||||
<translation>源代码</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="58"/>
|
||||
<source>Contributors</source>
|
||||
<translation>贡献者</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="60"/>
|
||||
<source>List of contributors on GitHub</source>
|
||||
<translation>GitHub 上的贡献者列表</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="61"/>
|
||||
<source>Thanks to all people who contributed to this project.</source>
|
||||
<translation>感谢所有参与此项目的朋友。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="65"/>
|
||||
<source>Translators</source>
|
||||
<translation>翻译者</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="66"/>
|
||||
<source>I would like to thank the following people who volunteered to translate this application.</source>
|
||||
<translation>我想要感谢下列自愿参与翻译此应用程序的朋友。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="131"/>
|
||||
<source>&Special Thanks</source>
|
||||
<translation>致谢(&S)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="133"/>
|
||||
<source>&Third-party Libraries</source>
|
||||
<translation>第三方程序库(&T)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="72"/>
|
||||
<source>Your Rights</source>
|
||||
<translation>用户的权利</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="74"/>
|
||||
<source>%1 is released under the MIT License.</source>
|
||||
<translation>%1 是在 MIT 许可协议下发布的。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="75"/>
|
||||
<source>This license grants people a number of freedoms:</source>
|
||||
<translation>此许可证赋予人们以下自由的权利:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="76"/>
|
||||
<source>You are free to use %1, for any purpose</source>
|
||||
<translation>任何人都可以为了任何目的自由地使用 %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="77"/>
|
||||
<source>You are free to distribute %1</source>
|
||||
<translation>任何人都可以自由地分发 %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="78"/>
|
||||
<source>You can study how %1 works and change it</source>
|
||||
<translation>任何人都可以自由地研究 %1 的工作原理并对其进行修改</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="79"/>
|
||||
<source>You can distribute changed versions of %1</source>
|
||||
<translation>任何人都可以自由地分发修改过的 %1 版本</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="81"/>
|
||||
<source>The MIT license guarantees you this freedom. Nobody is ever permitted to take it away.</source>
|
||||
<translation>此软件通过 MIT 许可证赋予用户上述自由,任何人无权剥夺。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="109"/>
|
||||
<source>Third-party Libraries used by %1</source>
|
||||
<translation>%1 使用的第三方程序库</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="110"/>
|
||||
<source>%1 is built on the following free software libraries:</source>
|
||||
<translation>%1 采用了下列自由软件程序库进行构建:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="129"/>
|
||||
<source>&Help</source>
|
||||
<translation>帮助(&H)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="130"/>
|
||||
<source>&About</source>
|
||||
<translation>关于(&A)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="132"/>
|
||||
<source>&License</source>
|
||||
<translation>软件许可证(&L)</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GraphicsScene</name>
|
||||
<message>
|
||||
<location filename="../app/graphicsscene.cpp" line="16"/>
|
||||
<source>Drag image here</source>
|
||||
<translation>拖放图片至此</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GraphicsView</name>
|
||||
<message>
|
||||
<location filename="../app/graphicsview.cpp" line="261"/>
|
||||
<source>File url list is empty</source>
|
||||
<translation>文件 URL 列表为空</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/graphicsview.cpp" line="53"/>
|
||||
<source>File is not a valid image</source>
|
||||
<translation>文件不是有效的图片文件</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/graphicsview.cpp" line="55"/>
|
||||
<location filename="../app/graphicsview.cpp" line="59"/>
|
||||
<source>Image data is invalid or currently unsupported</source>
|
||||
<translation>图像数据无效或暂未支持</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/graphicsview.cpp" line="269"/>
|
||||
<source>Image data is invalid</source>
|
||||
<translation>图片数据无效</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/graphicsview.cpp" line="276"/>
|
||||
<source>Not supported mimedata: %1</source>
|
||||
<translation>不受支持的 MimeData 格式:%1</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindow</name>
|
||||
<message>
|
||||
<location filename="../app/mainwindow.cpp" line="178"/>
|
||||
<source>File url list is empty</source>
|
||||
<translation>文件 URL 列表为空</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/mainwindow.cpp" line="417"/>
|
||||
<source>&Copy</source>
|
||||
<translation>复制(&C)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/mainwindow.cpp" line="438"/>
|
||||
<source>Copy P&ixmap</source>
|
||||
<translation>复制位图(&I)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/mainwindow.cpp" line="443"/>
|
||||
<source>Copy &File Path</source>
|
||||
<translation>复制文件路径(&F)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/mainwindow.cpp" line="453"/>
|
||||
<source>&Paste Image</source>
|
||||
<translation>粘贴图像(&P)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/mainwindow.cpp" line="459"/>
|
||||
<source>&Paste Image File</source>
|
||||
<translation>粘贴图像文件(&P)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/mainwindow.cpp" line="492"/>
|
||||
<source>Properties</source>
|
||||
<translation>属性</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="30"/>
|
||||
<location filename="../app/mainwindow.cpp" line="464"/>
|
||||
<source>Stay on top</source>
|
||||
<translation>总在最前</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/aboutdialog.cpp" line="34"/>
|
||||
<location filename="../app/mainwindow.cpp" line="471"/>
|
||||
<source>Protected mode</source>
|
||||
<translation>保护模式</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/mainwindow.cpp" line="478"/>
|
||||
<source>Configure...</source>
|
||||
<translation>设置...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/mainwindow.cpp" line="485"/>
|
||||
<source>Help</source>
|
||||
<translation>帮助</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MetadataDialog</name>
|
||||
<message>
|
||||
<location filename="../app/metadatadialog.cpp" line="75"/>
|
||||
<source>Image Metadata</source>
|
||||
<translation>图像元信息</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MetadataModel</name>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="36"/>
|
||||
<source>Origin</source>
|
||||
<comment>Section name.</comment>
|
||||
<translation>来源</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="38"/>
|
||||
<source>Image</source>
|
||||
<comment>Section name.</comment>
|
||||
<translation>图像</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="44"/>
|
||||
<source>File</source>
|
||||
<comment>Section name.</comment>
|
||||
<translation>文件</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="40"/>
|
||||
<source>Camera</source>
|
||||
<comment>Section name.</comment>
|
||||
<translation>照相机</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="27"/>
|
||||
<source>%1 File</source>
|
||||
<translation>%1 文件</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="35"/>
|
||||
<source>Description</source>
|
||||
<comment>Section name.</comment>
|
||||
<translation>说明</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="41"/>
|
||||
<source>Advanced photo</source>
|
||||
<comment>Section name.</comment>
|
||||
<translation>高级照片</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="42"/>
|
||||
<source>GPS</source>
|
||||
<comment>Section name.</comment>
|
||||
<translation>GPS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="47"/>
|
||||
<source>Dimensions</source>
|
||||
<translation>分辨率</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="49"/>
|
||||
<source>Aspect Ratio</source>
|
||||
<translation>纵横比</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="52"/>
|
||||
<source>Name</source>
|
||||
<translation>名称</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="54"/>
|
||||
<source>Item type</source>
|
||||
<translation>项目类型</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="56"/>
|
||||
<source>Folder path</source>
|
||||
<translation>文件夹路径</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="58"/>
|
||||
<source>Size</source>
|
||||
<translation>大小</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="60"/>
|
||||
<source>Date Created</source>
|
||||
<translation>创建日期</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="62"/>
|
||||
<source>Date Modified</source>
|
||||
<translation>修改日期</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="70"/>
|
||||
<source>%1 x %2</source>
|
||||
<translation>%1 x %2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="88"/>
|
||||
<source>%1 : %2</source>
|
||||
<translation>%1 : %2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="204"/>
|
||||
<source>Property</source>
|
||||
<translation>属性</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/metadatamodel.cpp" line="204"/>
|
||||
<source>Value</source>
|
||||
<translation>值</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsDialog</name>
|
||||
<message>
|
||||
<location filename="../app/settingsdialog.cpp" line="15"/>
|
||||
<source>Settings</source>
|
||||
<translation>设定</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/settingsdialog.cpp" line="20"/>
|
||||
<source>Do nothing</source>
|
||||
<translation>什么也不做</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/settingsdialog.cpp" line="21"/>
|
||||
<source>Close the window</source>
|
||||
<translation>关闭窗口</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/settingsdialog.cpp" line="22"/>
|
||||
<source>Toggle maximize</source>
|
||||
<translation>最大化窗口</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/settingsdialog.cpp" line="30"/>
|
||||
<source>Stay on top when start-up</source>
|
||||
<translation>启动时保持窗口总在最前</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/settingsdialog.cpp" line="31"/>
|
||||
<source>Double-click behavior</source>
|
||||
<translation>双击时的行为</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>main</name>
|
||||
<message>
|
||||
<location filename="../app/main.cpp" line="27"/>
|
||||
<source>Pineapple Pictures</source>
|
||||
<translation>菠萝看图</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../app/main.cpp" line="31"/>
|
||||
<source>File list.</source>
|
||||
<translation>文件列表。</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
Reference in New Issue
Block a user