diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8d76abd..5ce6017 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -23,6 +23,8 @@ set (PPIC_CPP_FILES
settings.cpp
settingsdialog.cpp
aboutdialog.cpp
+ metadatamodel.cpp
+ metadatadialog.cpp
)
set (PPIC_HEADER_FILES
@@ -36,6 +38,8 @@ set (PPIC_HEADER_FILES
settings.h
settingsdialog.h
aboutdialog.h
+ metadatamodel.h
+ metadatadialog.h
)
set (PPIC_QRC_FILES
diff --git a/languages/PineapplePictures.ts b/languages/PineapplePictures.ts
index 9935ee1..8286787 100644
--- a/languages/PineapplePictures.ts
+++ b/languages/PineapplePictures.ts
@@ -193,58 +193,105 @@
MainWindow
-
+
-
+
-
+
-
+
-
+
-
+
+
+
+
+
+
-
+
-
+
-
+
-
+
+
+ MetadataDialog
+
+
+
+
+
+
+
+ MetadataModel
+
+
+
+ General info about the image, section name.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
SettingsDialog
diff --git a/languages/PineapplePictures_zh_CN.ts b/languages/PineapplePictures_zh_CN.ts
index cc1d866..edfdf2b 100644
--- a/languages/PineapplePictures_zh_CN.ts
+++ b/languages/PineapplePictures_zh_CN.ts
@@ -193,58 +193,105 @@
MainWindow
-
+
文件 URL 列表为空
-
+
复制(&C)
-
+
复制位图(&I)
-
+
复制文件路径(&F)
-
+
粘贴图像(&P)
-
+
粘贴图像文件(&P)
+
+
+
+
+
-
+
总在最前
-
+
保护模式
-
+
设置...
-
+
帮助
+
+ MetadataDialog
+
+
+
+
+
+
+
+ MetadataModel
+
+
+
+ General info about the image, section name.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
SettingsDialog
diff --git a/mainwindow.cpp b/mainwindow.cpp
index 28be67a..4b864ed 100644
--- a/mainwindow.cpp
+++ b/mainwindow.cpp
@@ -8,6 +8,8 @@
#include "graphicsscene.h"
#include "settingsdialog.h"
#include "aboutdialog.h"
+#include "metadatamodel.h"
+#include "metadatadialog.h"
#include
#include
@@ -487,6 +489,17 @@ void MainWindow::contextMenuEvent(QContextMenuEvent *event)
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) {
menu->addActions(copyMenu->actions());
} else {
@@ -503,6 +516,10 @@ void MainWindow::contextMenuEvent(QContextMenuEvent *event)
menu->addSeparator();
menu->addAction(toggleSettings);
menu->addAction(helpAction);
+ if (currentFileUrl.isValid()) {
+ menu->addSeparator();
+ menu->addAction(propertiesAction);
+ }
menu->exec(mapToGlobal(event->pos()));
menu->deleteLater();
diff --git a/metadatadialog.cpp b/metadatadialog.cpp
new file mode 100644
index 0000000..bb39199
--- /dev/null
+++ b/metadatadialog.cpp
@@ -0,0 +1,41 @@
+#include "metadatadialog.h"
+
+#include
+#include
+#include
+
+#include "metadatamodel.h"
+
+MetadataDialog::MetadataDialog(QWidget *parent)
+ : QDialog(parent)
+ , m_treeView(new QTreeView(this))
+{
+ m_treeView->setRootIsDecorated(false);
+ m_treeView->setIndentation(0);
+
+ setWindowTitle(tr("Image Metadata"));
+
+ QDialogButtonBox * buttonBox = new QDialogButtonBox(QDialogButtonBox::Close);
+
+ setLayout(new QVBoxLayout);
+ layout()->addWidget(m_treeView);
+ layout()->addWidget(buttonBox);
+
+ connect(buttonBox, &QDialogButtonBox::close, this, &QDialog::close);
+}
+
+MetadataDialog::~MetadataDialog()
+{
+
+}
+
+void MetadataDialog::setMetadataModel(MetadataModel * model)
+{
+ m_treeView->setModel(model);
+ m_treeView->expandAll();
+}
+
+QSize MetadataDialog::sizeHint() const
+{
+ return QSize(520, 350);
+}
diff --git a/metadatadialog.h b/metadatadialog.h
new file mode 100644
index 0000000..8459c95
--- /dev/null
+++ b/metadatadialog.h
@@ -0,0 +1,26 @@
+#ifndef METADATADIALOG_H
+#define METADATADIALOG_H
+
+#include
+
+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
diff --git a/metadatamodel.cpp b/metadatamodel.cpp
new file mode 100644
index 0000000..8f4a514
--- /dev/null
+++ b/metadatamodel.cpp
@@ -0,0 +1,159 @@
+#include "metadatamodel.h"
+
+#include
+#include
+#include
+#include
+
+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);
+
+ const QString & sizeString = QLocale().formattedDataSize(fileInfo.size());
+ const QString & timeString = QLocale().toString(fileInfo.lastModified(), QLocale::LongFormat);
+ const QString & imageSizeString = imageSize(imgReader.size());
+
+ appendSection(QStringLiteral("General"), tr("General", "General info about the image, section name."));
+
+ appendProperty(QStringLiteral("General"), QStringLiteral("General.Name"),
+ tr("File Name"), fileInfo.fileName());
+ appendProperty(QStringLiteral("General"), QStringLiteral("General.Size"),
+ tr("File Size"), sizeString);
+ appendProperty(QStringLiteral("General"), QStringLiteral("General.Time"),
+ tr("Last Modified"), timeString);
+ appendProperty(QStringLiteral("General"), QStringLiteral("General.ImageSize"),
+ tr("Image Size"), imageSizeString);
+}
+
+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;
+}
+
+bool MetadataModel::appendSection(const QString §ionKey, const QString §ionDisplayName)
+{
+ if (m_sections.contains(sectionKey)) {
+ return false;
+ }
+
+ m_sections.append(sectionKey);
+ m_sectionProperties[sectionKey] = qMakePair >(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 & propertyList = m_sectionProperties[sectionKey].second;
+ if (!propertyList.contains(propertyKey)) {
+ propertyList.append(propertyKey);
+ }
+
+ m_properties[propertyKey] = qMakePair(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 & propertyList = m_sectionProperties[sectionKey].second;
+ return (index.column() == 0) ? m_properties[propertyList[index.row()]].first
+ : m_properties[propertyList[index.row()]].second;
+ }
+}
diff --git a/metadatamodel.h b/metadatamodel.h
new file mode 100644
index 0000000..c17e78f
--- /dev/null
+++ b/metadatamodel.h
@@ -0,0 +1,41 @@
+#ifndef METADATAMODEL_H
+#define METADATAMODEL_H
+
+#include
+
+class MetadataModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ explicit MetadataModel(QObject *parent = nullptr);
+ ~MetadataModel();
+
+ void setFile(const QString & imageFilePath);
+ static QString imageSize(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;
+
+ // [SECTION_KEY]
+ QList m_sections;
+ // {SECTION_KEY: (SECTION_DISPLAY_NAME, [PROPERTY_KEY])}
+ QMap > > m_sectionProperties;
+ // {PROPERTY_KEY: (PROPERTY_DISPLAY_NAME, PROPERTY_VALUE)}
+ QMap > m_properties;
+};
+
+#endif // METADATAMODEL_H
diff --git a/pineapple-pictures.pro b/pineapple-pictures.pro
index 0a6dd42..07656f7 100644
--- a/pineapple-pictures.pro
+++ b/pineapple-pictures.pro
@@ -1,9 +1,3 @@
-#-------------------------------------------------
-#
-# Project created by QtCreator 2019-09-26T23:36:07
-#
-#-------------------------------------------------
-
QT += core widgets gui svg
TARGET = ppic
@@ -24,8 +18,8 @@ CONFIG += c++11 lrelease embed_translations
SOURCES += \
aboutdialog.cpp \
- main.cpp \
- mainwindow.cpp \
+ main.cpp \
+ mainwindow.cpp \
graphicsview.cpp \
bottombuttongroup.cpp \
graphicsscene.cpp \
@@ -33,11 +27,13 @@ SOURCES += \
opacityhelper.cpp \
toolbutton.cpp \
settings.cpp \
- settingsdialog.cpp
+ settingsdialog.cpp \
+ metadatamodel.cpp \
+ metadatadialog.cpp
HEADERS += \
aboutdialog.h \
- mainwindow.h \
+ mainwindow.h \
graphicsview.h \
bottombuttongroup.h \
graphicsscene.h \
@@ -45,7 +41,9 @@ HEADERS += \
opacityhelper.h \
toolbutton.h \
settings.h \
- settingsdialog.h
+ settingsdialog.h \
+ metadatamodel.h \
+ metadatadialog.h
TRANSLATIONS = \
languages/PineapplePictures.ts \