From 9e7e278f0b3ba037e5ccf62008717e7a5404eace Mon Sep 17 00:00:00 2001 From: Ivan Romanov Date: Sun, 19 May 2019 17:23:18 +0500 Subject: [PATCH] Add sources --- CMakeLists.txt | 19 +++++ dialog.cpp | 184 +++++++++++++++++++++++++++++++++++++++++++++++++ dialog.h | 57 +++++++++++++++ dialog.ui | 78 +++++++++++++++++++++ main.cpp | 32 +++++++++ 5 files changed, 370 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 dialog.cpp create mode 100644 dialog.h create mode 100644 dialog.ui create mode 100644 main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..575638d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.0) + +project(xcursor-viewer CXX) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +find_package(Qt5Core REQUIRED) +find_package(Qt5Gui REQUIRED) +find_package(Qt5Widgets REQUIRED) + +set(SOURCES main.cpp dialog.cpp) +set(HEADERS dialog.h) +set(FORMS dialog.ui) + +add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS} ${FORMS}) +target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Gui Qt5::Widgets) diff --git a/dialog.cpp b/dialog.cpp new file mode 100644 index 0000000..3960dda --- /dev/null +++ b/dialog.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2019 Ivan Romanov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "dialog.h" +#include + +#include +#include +#include +#include +#include +#include + +Dialog::Dialog(QWidget *parent) + : QDialog(parent) + , ui(new Ui::Dialog) +{ + ui->setupUi(this); + + connect(ui->lwCursors, &QListWidget::currentTextChanged, this, &Dialog::showCursor); + connect(ui->pbOpenFolder, &QPushButton::clicked, this, &Dialog::openFolder); +} + +Dialog::~Dialog() +{ + delete ui; +} + +void Dialog::openFolder() +{ + QString dirPath = QFileDialog::getExistingDirectory(this); + + if (dirPath.isEmpty()) { + return; + } + + QDir dir(dirPath); + + QStringList fileList = dir.entryList(QDir::Filter::Files); + + _cursorFileList.clear(); + + for (const QString &fileName: fileList) { + QFile file(dir.absoluteFilePath(fileName)); + if (!file.open(QIODevice::OpenModeFlag::ReadOnly)) { + continue; + } + + QDataStream stream(&file); + stream.setByteOrder(QDataStream::ByteOrder::LittleEndian); + + quint32 magic; + quint32 header; + quint32 version; + quint32 ntoc; + + stream >> magic >> header >> version >> ntoc; + + if (magic != 0x72756358 /* Xcur */ && header != 16) { + continue; + } + + CursorFile cursorFile; + + cursorFile.name = fileName; + + for (quint32 i = 0; i < ntoc; ++i) { + quint32 type; + quint32 subtype; + quint32 position; + + stream >> type >> subtype >> position; + qint64 tocPos = file.pos(); // position in table of contents entries + + if (type == 0xfffd0002) { + file.seek(position); + + quint32 imgHeader; + quint32 imgType; + quint32 imgSubtype; + quint32 imgVersion; + quint32 imgWidth; + quint32 imgHeight; + quint32 imgYhot; + quint32 imgXhot; + quint32 imgDelay; + + stream >> imgHeader + >> imgType + >> imgSubtype + >> imgVersion + >> imgWidth + >> imgHeight + >> imgYhot + >> imgXhot + >> imgDelay; + + if (imgHeader != 36 || imgType != type || imgSubtype != subtype || imgVersion != 1) { + continue; + } + + QByteArray imgData = file.read((imgWidth * imgHeight) * 4); + + Cursor cursor; + cursor.image = QImage(reinterpret_cast(imgData.data()), static_cast(imgWidth), static_cast(imgHeight), QImage::Format::Format_ARGB32).copy(); + cursor.hotSpot = QPoint(static_cast(imgXhot), static_cast(imgYhot)); + + QString key = QStringLiteral("%1").arg(static_cast(subtype), 3, 10, QLatin1Char('0')); + cursorFile.cursorMap.insertMulti(key, cursor); + + file.seek(tocPos); + } + } + + _cursorFileList << cursorFile; + } + + ui->lwCursors->clear(); + + QStringList nameList; + + for (const CursorFile &cursorFile: _cursorFileList) { + nameList << cursorFile.name; + } + + ui->lwCursors->addItems(nameList); +} + +void Dialog::showCursor(const QString &fileName) +{ + CursorFile foundCursorFile; + + for (const CursorFile &cursorFile: _cursorFileList) { + if (cursorFile.name == fileName) { + foundCursorFile = cursorFile; + break; + } + } + + if(foundCursorFile.name.isEmpty()) { + ui->teCursorInfo->clear(); + return; + } + + QString msg = ""; + + QStringList keys = foundCursorFile.cursorMap.keys(); + keys.removeDuplicates(); + keys.sort(); + + for (const QString &key: keys) { + QList cursorList = foundCursorFile.cursorMap.values(key); + msg += "

"; + for (const Cursor &cursor: cursorList) { + QByteArray imgBa; + QBuffer buffer(&imgBa); + cursor.image.save(&buffer, "PNG"); + + imgBa = imgBa.toBase64(); + + msg += ""; + } + msg += "

"; + } + + msg += ""; + + ui->teCursorInfo->setHtml(msg); +} diff --git a/dialog.h b/dialog.h new file mode 100644 index 0000000..1e231bc --- /dev/null +++ b/dialog.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2019 Ivan Romanov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#pragma once + +#include + +#include +#include +#include +#include + +namespace Ui { class Dialog; } + +struct Cursor +{ + QImage image; + QPoint hotSpot; +}; + +struct CursorFile +{ + QString name; + QMap cursorMap; +}; + +class Dialog : public QDialog +{ + Q_OBJECT + +public: + explicit Dialog(QWidget *parent = nullptr); + ~Dialog(); + + void openFolder(); + void showCursor(const QString &fileName); + +private: + Ui::Dialog *ui; + QList _cursorFileList; +}; + diff --git a/dialog.ui b/dialog.ui new file mode 100644 index 0000000..4fb4d53 --- /dev/null +++ b/dialog.ui @@ -0,0 +1,78 @@ + + + Dialog + + + + 0 + 0 + 513 + 425 + + + + Xcursor Viewer + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Open folder + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + + + true + + + + + + + + + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..f06aa46 --- /dev/null +++ b/main.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 Ivan Romanov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "dialog.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + Dialog dlg; + dlg.show(); + app.exec(); + + return 0; +}