Cherry-picking upstream version 0.6.0.
This commit is contained in:
parent
75af31920b
commit
71999b516b
2
AUTHORS
2
AUTHORS
@ -3,7 +3,7 @@ Upstream Authors:
|
|||||||
Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
||||||
|
|
||||||
Copyright:
|
Copyright:
|
||||||
Copyright (c) 2013-2015 LXQt team
|
Copyright (c) 2013-2017 LXQt team
|
||||||
|
|
||||||
License: GPL-2+ and LGPL-2.1+
|
License: GPL-2+ and LGPL-2.1+
|
||||||
The full text of the licenses can be found in the 'COPYING' file.
|
The full text of the licenses can be found in the 'COPYING' file.
|
||||||
|
36
CHANGELOG
36
CHANGELOG
@ -1,7 +1,41 @@
|
|||||||
|
|
||||||
lximage-qt-0.5.1 / 2016-12-21
|
lximage-qt-0.6.0 / 2017-10-21
|
||||||
=============================
|
=============================
|
||||||
|
|
||||||
|
* Add ImageShack upload provider.
|
||||||
|
* Fix warnings issued by GCC and Clang.
|
||||||
|
* Ensure file is closed when upload finishes.
|
||||||
|
* Make image URL read-only.
|
||||||
|
* Update copyright in initial file comment for new additions.
|
||||||
|
* Make QNetworkAccessManager static and fix initialization order in UploadDialog.
|
||||||
|
* Add support for uploading files (fixes #98).
|
||||||
|
* Bump versions
|
||||||
|
* Don't export github templates
|
||||||
|
* Don't use hardcoded install dir
|
||||||
|
* Update CMakeLists.txt
|
||||||
|
* Fix regression in thumbnail view
|
||||||
|
* Add Lithuanian .desktop files
|
||||||
|
* liblxqt don't fit here
|
||||||
|
* Copied issue-template
|
||||||
|
* Drops Qt5Core_VERSION_STRING
|
||||||
|
* set Qt::AA_UseHighDpiPixmaps to true
|
||||||
|
* MainWindow: Fix crash for quick image changes
|
||||||
|
* Use GNUInstallDirs
|
||||||
|
* jobs: Do proper error handling
|
||||||
|
* Adapt to changes in libfm-qt(the c++11 port)
|
||||||
|
* Use the new lxqt-build-tools new FindExif CMake module
|
||||||
|
* Simpler code for ScreenshotSelectAreaGraphicsView class.
|
||||||
|
* Change Screenshot select area green color by actual hightlight color. Use lximage-qt private variables style.
|
||||||
|
* Adapt to C++11 and RAM improvements.
|
||||||
|
* Screenshot captures an area of the screen.
|
||||||
|
* Use const iterators
|
||||||
|
* Bump year
|
||||||
|
* File and folder DND Fixes https://github.com/lxde/lximage-qt/issues/69.
|
||||||
|
|
||||||
|
0.5.1 / 2016-12-21
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Release 0.5.1: Update changelog
|
||||||
* Bump patch version and (#82)
|
* Bump patch version and (#82)
|
||||||
* Create lximage-qt-screenshot_it.desktop (#83)
|
* Create lximage-qt-screenshot_it.desktop (#83)
|
||||||
* Add *da.desktop files
|
* Add *da.desktop files
|
||||||
|
@ -4,37 +4,35 @@ project(lximage-qt)
|
|||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
set(MAJOR_VERSION 0)
|
set(MAJOR_VERSION 0)
|
||||||
set(MINOR_VERSION 5)
|
set(MINOR_VERSION 6)
|
||||||
set(PATCH_VERSION 1)
|
set(PATCH_VERSION 0)
|
||||||
set(LXIMAGE_VERSION ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION})
|
set(LXIMAGE_VERSION ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION})
|
||||||
|
|
||||||
set(LXQTBT_MINIMUM_VERSION "0.3.0")
|
set(LXQTBT_MINIMUM_VERSION "0.4.0")
|
||||||
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
|
|
||||||
find_package(Qt5Widgets REQUIRED)
|
find_package(Qt5Widgets REQUIRED)
|
||||||
|
find_package(Qt5Network REQUIRED)
|
||||||
find_package(Qt5DBus REQUIRED)
|
find_package(Qt5DBus REQUIRED)
|
||||||
find_package(Qt5PrintSupport REQUIRED QUIET)
|
find_package(Qt5PrintSupport REQUIRED)
|
||||||
find_package(Qt5X11Extras REQUIRED QUIET)
|
find_package(Qt5X11Extras REQUIRED)
|
||||||
find_package(Qt5LinguistTools REQUIRED QUIET)
|
find_package(Qt5LinguistTools REQUIRED)
|
||||||
find_package(Qt5Svg REQUIRED QUIET)
|
find_package(Qt5Svg REQUIRED)
|
||||||
find_package(fm-qt REQUIRED QUIET)
|
find_package(fm-qt REQUIRED)
|
||||||
find_package(lxqt-build-tools ${LXQTBT_MINIMUM_VERSION} REQUIRED)
|
find_package(lxqt-build-tools ${LXQTBT_MINIMUM_VERSION} REQUIRED)
|
||||||
message(STATUS "Building with Qt ${Qt5Core_VERSION_STRING}")
|
find_package(Exif REQUIRED)
|
||||||
|
message(STATUS "Building with Qt ${Qt5Core_VERSION}")
|
||||||
|
|
||||||
include(LXQtCompilerSettings NO_POLICY_SCOPE)
|
include(LXQtCompilerSettings NO_POLICY_SCOPE)
|
||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
|
||||||
|
|
||||||
# FIXME: we'll need this to provide detail info for photos in the future
|
|
||||||
pkg_check_modules(EXIF REQUIRED libexif)
|
|
||||||
|
|
||||||
# TODO: make the X11 stuff optional.
|
# TODO: make the X11 stuff optional.
|
||||||
# for screenshot support
|
# for screenshot support
|
||||||
find_package(X11 REQUIRED)
|
find_package(X11 REQUIRED)
|
||||||
|
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
# Xfixes is needed to capture the mouse cursor image
|
# Xfixes is needed to capture the mouse cursor image
|
||||||
pkg_check_modules(XFIXES REQUIRED xfixes)
|
pkg_check_modules(XFIXES REQUIRED xfixes)
|
||||||
|
|
||||||
|
@ -16,11 +16,21 @@ set(lximage-qt_SRCS
|
|||||||
application.cpp
|
application.cpp
|
||||||
imageview.cpp
|
imageview.cpp
|
||||||
modelfilter.cpp
|
modelfilter.cpp
|
||||||
job.cpp
|
|
||||||
loadimagejob.cpp
|
loadimagejob.cpp
|
||||||
saveimagejob.cpp
|
saveimagejob.cpp
|
||||||
screenshotdialog.cpp
|
screenshotdialog.cpp
|
||||||
|
screenshotselectarea.cpp
|
||||||
|
screenshotselectareagraphicsview.cpp
|
||||||
settings.cpp
|
settings.cpp
|
||||||
|
graphicsscene.cpp
|
||||||
|
|
||||||
|
upload/imageshackprovider.cpp
|
||||||
|
upload/imageshackupload.cpp
|
||||||
|
upload/imgurprovider.cpp
|
||||||
|
upload/imgurupload.cpp
|
||||||
|
upload/provider.cpp
|
||||||
|
upload/upload.cpp
|
||||||
|
upload/uploaddialog.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
qt5_add_dbus_adaptor(lximage-qt_SRCS
|
qt5_add_dbus_adaptor(lximage-qt_SRCS
|
||||||
@ -35,6 +45,8 @@ set(lximage-qt_UIS
|
|||||||
mainwindow.ui
|
mainwindow.ui
|
||||||
preferencesdialog.ui
|
preferencesdialog.ui
|
||||||
screenshotdialog.ui
|
screenshotdialog.ui
|
||||||
|
|
||||||
|
upload/uploaddialog.ui
|
||||||
)
|
)
|
||||||
qt5_wrap_ui(lximage-qt_UI_H ${lximage-qt_UIS})
|
qt5_wrap_ui(lximage-qt_UI_H ${lximage-qt_UIS})
|
||||||
|
|
||||||
@ -64,7 +76,7 @@ include(LXQtTranslateDesktop)
|
|||||||
|
|
||||||
file(GLOB desktop_files_in ../data/*.desktop.in)
|
file(GLOB desktop_files_in ../data/*.desktop.in)
|
||||||
lxqt_translate_desktop(desktop_files SOURCES ${desktop_files_in})
|
lxqt_translate_desktop(desktop_files SOURCES ${desktop_files_in})
|
||||||
install(FILES ${desktop_files} DESTINATION share/applications)
|
install(FILES ${desktop_files} DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications")
|
||||||
|
|
||||||
add_executable(lximage-qt
|
add_executable(lximage-qt
|
||||||
${lximage-qt_SRCS}
|
${lximage-qt_SRCS}
|
||||||
@ -74,11 +86,11 @@ add_executable(lximage-qt
|
|||||||
)
|
)
|
||||||
|
|
||||||
add_definitions(
|
add_definitions(
|
||||||
-DLXIMAGE_DATA_DIR="${CMAKE_INSTALL_PREFIX}/share/lximage-qt"
|
-DLXIMAGE_DATA_DIR="${CMAKE_INSTALL_FULL_DATAROOTDIR}/lximage-qt"
|
||||||
-DLXIMAGE_VERSION="${LXIMAGE_VERSION}"
|
-DLXIMAGE_VERSION="${LXIMAGE_VERSION}"
|
||||||
)
|
)
|
||||||
|
|
||||||
set(QT_LIBRARIES Qt5::Widgets Qt5::Core Qt5::DBus Qt5::PrintSupport Qt5::X11Extras Qt5::Svg)
|
set(QT_LIBRARIES Qt5::Widgets Qt5::Network Qt5::Core Qt5::DBus Qt5::PrintSupport Qt5::X11Extras Qt5::Svg)
|
||||||
|
|
||||||
target_link_libraries(lximage-qt
|
target_link_libraries(lximage-qt
|
||||||
fm-qt
|
fm-qt
|
||||||
@ -88,4 +100,4 @@ target_link_libraries(lximage-qt
|
|||||||
${XFIXES_LIBRARIES}
|
${XFIXES_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
install(TARGETS lximage-qt RUNTIME DESTINATION bin)
|
install(TARGETS lximage-qt RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
||||||
|
@ -41,6 +41,11 @@ Application::Application(int& argc, char** argv):
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Application::init(int argc, char** argv) {
|
bool Application::init(int argc, char** argv) {
|
||||||
|
Q_UNUSED(argc)
|
||||||
|
Q_UNUSED(argv)
|
||||||
|
|
||||||
|
setAttribute(Qt::AA_UseHighDpiPixmaps, true);
|
||||||
|
|
||||||
// install the translations built-into Qt itself
|
// install the translations built-into Qt itself
|
||||||
qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
|
qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
|
||||||
installTranslator(&qtTranslator);
|
installTranslator(&qtTranslator);
|
||||||
|
@ -18,45 +18,31 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LXIMAGE_JOB_H
|
#include "graphicsscene.h"
|
||||||
#define LXIMAGE_JOB_H
|
#include <QMimeData>
|
||||||
|
#include <QUrl>
|
||||||
#include <gio/gio.h>
|
|
||||||
|
|
||||||
namespace LxImage {
|
namespace LxImage {
|
||||||
|
|
||||||
class Job {
|
GraphicsScene::GraphicsScene(QObject *parent):
|
||||||
public:
|
QGraphicsScene (parent) {
|
||||||
Job();
|
|
||||||
virtual ~Job();
|
|
||||||
|
|
||||||
void cancel() {
|
|
||||||
g_cancellable_cancel(cancellable_);
|
|
||||||
}
|
|
||||||
void start();
|
|
||||||
|
|
||||||
GError* error() const {
|
|
||||||
return error_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isCancelled() const {
|
|
||||||
return bool(g_cancellable_is_cancelled(cancellable_));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual bool run() = 0;
|
|
||||||
virtual void finish() = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
GCancellable* cancellable_;
|
|
||||||
GError* error_;
|
|
||||||
|
|
||||||
private:
|
|
||||||
static gboolean _jobThread(GIOSchedulerJob* job, GCancellable* cancellable, Job* pThis);
|
|
||||||
static gboolean _finish(Job* pThis);
|
|
||||||
static void _freeMe(Job* pThis);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // LXIMAGE_JOB_H
|
void GraphicsScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event) {
|
||||||
|
if(event->mimeData()->hasUrls())
|
||||||
|
event->acceptProposedAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event) {
|
||||||
|
if(event->mimeData()->hasUrls())
|
||||||
|
event->acceptProposedAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsScene::dropEvent(QGraphicsSceneDragDropEvent* event) {
|
||||||
|
QList<QUrl> urlList = event->mimeData()->urls();
|
||||||
|
if(!urlList.isEmpty())
|
||||||
|
Q_EMIT fileDropped(urlList.first().toLocalFile());
|
||||||
|
event->acceptProposedAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
46
src/graphicsscene.h
Normal file
46
src/graphicsscene.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) 2013 <copyright holder> <email>
|
||||||
|
|
||||||
|
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 2 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, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LXIMAGE_GRAPHICSSCENE_H
|
||||||
|
#define LXIMAGE_GRAPHICSSCENE_H
|
||||||
|
|
||||||
|
#include <QGraphicsScene>
|
||||||
|
#include <QGraphicsSceneDragDropEvent>
|
||||||
|
|
||||||
|
namespace LxImage {
|
||||||
|
|
||||||
|
class GraphicsScene : public QGraphicsScene
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
GraphicsScene(QObject *parent = 0);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
|
||||||
|
virtual void dragMoveEvent(QGraphicsSceneDragDropEvent *event);
|
||||||
|
virtual void dropEvent(QGraphicsSceneDragDropEvent* event);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void fileDropped(const QString file);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // LXIMAGE_GRAPHICSSCENE_H
|
@ -35,7 +35,7 @@ namespace LxImage {
|
|||||||
|
|
||||||
ImageView::ImageView(QWidget* parent):
|
ImageView::ImageView(QWidget* parent):
|
||||||
QGraphicsView(parent),
|
QGraphicsView(parent),
|
||||||
scene_(new QGraphicsScene(this)),
|
scene_(new GraphicsScene(this)),
|
||||||
imageItem_(new QGraphicsRectItem()),
|
imageItem_(new QGraphicsRectItem()),
|
||||||
gifMovie_(nullptr),
|
gifMovie_(nullptr),
|
||||||
cacheTimer_(nullptr),
|
cacheTimer_(nullptr),
|
||||||
@ -49,6 +49,7 @@ ImageView::ImageView(QWidget* parent):
|
|||||||
setLineWidth(0);
|
setLineWidth(0);
|
||||||
|
|
||||||
setScene(scene_);
|
setScene(scene_);
|
||||||
|
connect(scene_, &GraphicsScene::fileDropped, this, &ImageView::onFileDropped);
|
||||||
imageItem_->hide();
|
imageItem_->hide();
|
||||||
imageItem_->setPen(QPen(Qt::NoPen)); // remove the border
|
imageItem_->setPen(QPen(Qt::NoPen)); // remove the border
|
||||||
scene_->addItem(imageItem_);
|
scene_->addItem(imageItem_);
|
||||||
@ -68,6 +69,9 @@ ImageView::~ImageView() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImageView::onFileDropped(const QString file) {
|
||||||
|
Q_EMIT fileDropped(file);
|
||||||
|
}
|
||||||
|
|
||||||
void ImageView::wheelEvent(QWheelEvent* event) {
|
void ImageView::wheelEvent(QWheelEvent* event) {
|
||||||
int delta = event->delta();
|
int delta = event->delta();
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#ifndef LXIMAGE_IMAGEVIEW_H
|
#ifndef LXIMAGE_IMAGEVIEW_H
|
||||||
#define LXIMAGE_IMAGEVIEW_H
|
#define LXIMAGE_IMAGEVIEW_H
|
||||||
|
|
||||||
|
#include "graphicsscene.h"
|
||||||
#include <QGraphicsView>
|
#include <QGraphicsView>
|
||||||
#include <QGraphicsScene>
|
#include <QGraphicsScene>
|
||||||
#include <QGraphicsRectItem>
|
#include <QGraphicsRectItem>
|
||||||
@ -71,6 +72,9 @@ public:
|
|||||||
// if set to true, hides the cursor after 3s of inactivity
|
// if set to true, hides the cursor after 3s of inactivity
|
||||||
void hideCursor(bool enable);
|
void hideCursor(bool enable);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void fileDropped(const QString file);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void wheelEvent(QWheelEvent* event);
|
virtual void wheelEvent(QWheelEvent* event);
|
||||||
virtual void mouseDoubleClickEvent(QMouseEvent* event);
|
virtual void mouseDoubleClickEvent(QMouseEvent* event);
|
||||||
@ -87,11 +91,12 @@ private:
|
|||||||
QRect sceneToViewport(const QRectF& rect);
|
QRect sceneToViewport(const QRectF& rect);
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
|
void onFileDropped(const QString file);
|
||||||
void generateCache();
|
void generateCache();
|
||||||
void blankCursor();
|
void blankCursor();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QGraphicsScene* scene_; // the topmost container of all graphic items
|
GraphicsScene* scene_; // the topmost container of all graphic items
|
||||||
QGraphicsRectItem* imageItem_; // the rect item used to draw the image
|
QGraphicsRectItem* imageItem_; // the rect item used to draw the image
|
||||||
QImage image_; // image to show
|
QImage image_; // image to show
|
||||||
QMovie *gifMovie_; // gif animation to show (should be deleted explicitly)
|
QMovie *gifMovie_; // gif animation to show (should be deleted explicitly)
|
||||||
|
64
src/job.cpp
64
src/job.cpp
@ -1,64 +0,0 @@
|
|||||||
/*
|
|
||||||
* <one line to give the library's name and an idea of what it does.>
|
|
||||||
* Copyright (C) 2014 <copyright holder> <email>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library 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
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "job.h"
|
|
||||||
|
|
||||||
namespace LxImage {
|
|
||||||
|
|
||||||
Job::Job():
|
|
||||||
cancellable_(g_cancellable_new()),
|
|
||||||
error_(NULL) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Job::~Job() {
|
|
||||||
g_object_unref(cancellable_);
|
|
||||||
if(error_)
|
|
||||||
g_error_free(error_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called from the worker thread, not main thread
|
|
||||||
gboolean Job::_jobThread(GIOSchedulerJob* job, GCancellable* cancellable, Job* pThis) {
|
|
||||||
pThis->run();
|
|
||||||
// do final step in the main thread
|
|
||||||
if(!g_cancellable_is_cancelled(pThis->cancellable_))
|
|
||||||
g_io_scheduler_job_send_to_mainloop(job, GSourceFunc(_finish), pThis, NULL);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Job::start() {
|
|
||||||
g_io_scheduler_push_job(GIOSchedulerJobFunc(_jobThread),
|
|
||||||
this, GDestroyNotify(_freeMe),
|
|
||||||
G_PRIORITY_DEFAULT, cancellable_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// this function is called from main thread only
|
|
||||||
gboolean Job::_finish(Job* pThis) {
|
|
||||||
// only do processing if the job is not cancelled
|
|
||||||
if(!g_cancellable_is_cancelled(pThis->cancellable_)) {
|
|
||||||
pThis->finish();
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Job::_freeMe(Job* pThis) {
|
|
||||||
delete pThis;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -21,66 +21,79 @@
|
|||||||
#include "loadimagejob.h"
|
#include "loadimagejob.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include <QImageReader>
|
#include <QImageReader>
|
||||||
#include <QBuffer>
|
#include <QByteArray>
|
||||||
#include <qvarlengtharray.h>
|
#include <qvarlengtharray.h>
|
||||||
#include <libexif/exif-loader.h>
|
#include <libexif/exif-loader.h>
|
||||||
|
|
||||||
using namespace LxImage;
|
using namespace LxImage;
|
||||||
|
|
||||||
LoadImageJob::LoadImageJob(MainWindow* window, FmPath* filePath):
|
LoadImageJob::LoadImageJob(const Fm::FilePath & filePath):
|
||||||
Job(),
|
path_{filePath} {
|
||||||
mainWindow_(window),
|
|
||||||
path_(fm_path_ref(filePath)) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadImageJob::~LoadImageJob() {
|
LoadImageJob::~LoadImageJob() {
|
||||||
fm_path_unref(path_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is called from the worker thread, not main thread
|
// This is called from the worker thread, not main thread
|
||||||
bool LoadImageJob::run() {
|
void LoadImageJob::exec() {
|
||||||
GFile* gfile = fm_path_to_gfile(path_);
|
GFileInputStream* fileStream = nullptr;
|
||||||
GFileInputStream* fileStream = g_file_read(gfile, cancellable_, &error_);
|
Fm::GErrorPtr error;
|
||||||
g_object_unref(gfile);
|
ErrorAction act = ErrorAction::RETRY;
|
||||||
|
QByteArray imageBuffer;
|
||||||
|
while (act == ErrorAction::RETRY && !isCancelled())
|
||||||
|
{
|
||||||
|
error.reset();
|
||||||
|
if (nullptr == (fileStream = g_file_read(path_.gfile().get(), cancellable().get(), &error)))
|
||||||
|
{
|
||||||
|
act = emitError(error);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if(fileStream) { // if the file stream is successfually opened
|
// the file stream is successfully opened
|
||||||
QBuffer imageBuffer;
|
imageBuffer.truncate(0);
|
||||||
GInputStream* inputStream = G_INPUT_STREAM(fileStream);
|
GInputStream* inputStream = G_INPUT_STREAM(fileStream);
|
||||||
while(!g_cancellable_is_cancelled(cancellable_)) {
|
while(!error && !isCancelled()) {
|
||||||
char buffer[4096];
|
char buffer[4096];
|
||||||
|
error.reset();
|
||||||
gssize readSize = g_input_stream_read(inputStream,
|
gssize readSize = g_input_stream_read(inputStream,
|
||||||
buffer, 4096,
|
buffer, 4096,
|
||||||
cancellable_, &error_);
|
cancellable().get(), &error);
|
||||||
if(readSize == -1 || readSize == 0) // error or EOF
|
if(readSize == -1 || readSize == 0) // error or EOF
|
||||||
break;
|
break;
|
||||||
// append the bytes read to the image buffer
|
// append the bytes read to the image buffer
|
||||||
imageBuffer.buffer().append(buffer, readSize);
|
imageBuffer.append(buffer, readSize);
|
||||||
}
|
}
|
||||||
g_input_stream_close(inputStream, NULL, NULL);
|
g_input_stream_close(inputStream, NULL, NULL);
|
||||||
|
|
||||||
|
if (!error)
|
||||||
|
break; // everything read or cancel requested
|
||||||
|
|
||||||
|
act = emitError(error);
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: maybe it's a better idea to implement a GInputStream based QIODevice.
|
// FIXME: maybe it's a better idea to implement a GInputStream based QIODevice.
|
||||||
if(!error_ && !g_cancellable_is_cancelled(cancellable_)) { // load the image from buffer if there are no errors
|
if(!error && !isCancelled()) { // load the image from buffer if there are no errors
|
||||||
image_ = QImage::fromData(imageBuffer.buffer());
|
image_ = QImage::fromData(imageBuffer);
|
||||||
|
|
||||||
if(!image_.isNull()) { // if the image is loaded correctly
|
if(!image_.isNull()) { // if the image is loaded correctly
|
||||||
// check if this file is a jpeg file
|
// check if this file is a jpeg file
|
||||||
// FIXME: can we use FmFileInfo instead if it's available?
|
// FIXME: can we use FmFileInfo instead if it's available?
|
||||||
const char* basename = fm_path_get_basename(path_);
|
const Fm::CStrPtr basename = path_.baseName();
|
||||||
char* mime_type = g_content_type_guess(basename, NULL, 0, NULL);
|
const Fm::CStrPtr mime_type{g_content_type_guess(basename.get(), NULL, 0, NULL)};
|
||||||
if(mime_type && strcmp(mime_type, "image/jpeg") == 0) { // this is a jpeg file
|
if(mime_type && strcmp(mime_type.get(), "image/jpeg") == 0) { // this is a jpeg file
|
||||||
// use libexif to extract additional info embedded in jpeg files
|
// use libexif to extract additional info embedded in jpeg files
|
||||||
ExifLoader *exif_loader = exif_loader_new();
|
std::unique_ptr<ExifLoader, decltype (&exif_loader_unref)> exif_loader{exif_loader_new(), &exif_loader_unref};
|
||||||
// write image data to exif loader
|
// write image data to exif loader
|
||||||
exif_loader_write(exif_loader, (unsigned char*)imageBuffer.data().constData(), (unsigned int)imageBuffer.size());
|
exif_loader_write(exif_loader.get(), reinterpret_cast<unsigned char*>(const_cast<char *>(imageBuffer.constData())), static_cast<unsigned int>(imageBuffer.size()));
|
||||||
ExifData *exif_data = exif_loader_get_data(exif_loader);
|
std::unique_ptr<ExifData, decltype (&exif_data_unref)> exif_data{exif_loader_get_data(exif_loader.get()), &exif_data_unref};
|
||||||
exif_loader_unref(exif_loader);
|
exif_loader.reset();
|
||||||
if(exif_data) {
|
if (exif_data) {
|
||||||
/* reference for EXIF orientation tag:
|
/* reference for EXIF orientation tag:
|
||||||
* http://www.impulseadventure.com/photo/exif-orientation.html */
|
* http://www.impulseadventure.com/photo/exif-orientation.html */
|
||||||
ExifEntry* orient_ent = exif_data_get_entry(exif_data, EXIF_TAG_ORIENTATION);
|
ExifEntry* orient_ent = exif_data_get_entry(exif_data.get(), EXIF_TAG_ORIENTATION);
|
||||||
if(orient_ent) { /* orientation flag found in EXIF */
|
if(orient_ent) { /* orientation flag found in EXIF */
|
||||||
gushort orient;
|
gushort orient;
|
||||||
ExifByteOrder bo = exif_data_get_byte_order(exif_data);
|
ExifByteOrder bo = exif_data_get_byte_order(exif_data.get());
|
||||||
/* bo == EXIF_BYTE_ORDER_INTEL ; */
|
/* bo == EXIF_BYTE_ORDER_INTEL ; */
|
||||||
orient = exif_get_short (orient_ent->data, bo);
|
orient = exif_get_short (orient_ent->data, bo);
|
||||||
qreal rotate_degrees = 0.0;
|
qreal rotate_degrees = 0.0;
|
||||||
@ -105,20 +118,9 @@ bool LoadImageJob::run() {
|
|||||||
}
|
}
|
||||||
// TODO: handle other EXIF tags as well
|
// TODO: handle other EXIF tags as well
|
||||||
}
|
}
|
||||||
exif_data_unref(exif_data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g_free(mime_type);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// this function is called from main thread only
|
|
||||||
void LoadImageJob::finish() {
|
|
||||||
// only do processing if the job is not cancelled
|
|
||||||
if(!g_cancellable_is_cancelled(cancellable_)) {
|
|
||||||
mainWindow_->onImageLoaded(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -21,37 +21,31 @@
|
|||||||
#ifndef LXIMAGE_LOADIMAGEJOB_H
|
#ifndef LXIMAGE_LOADIMAGEJOB_H
|
||||||
#define LXIMAGE_LOADIMAGEJOB_H
|
#define LXIMAGE_LOADIMAGEJOB_H
|
||||||
|
|
||||||
#include <gio/gio.h>
|
#include <libfm-qt/core/filepath.h>
|
||||||
#include <libfm/fm.h>
|
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include "job.h"
|
#include <libfm-qt/core/job.h>
|
||||||
|
|
||||||
namespace LxImage {
|
namespace LxImage {
|
||||||
|
|
||||||
class MainWindow;
|
class LoadImageJob : public Fm::Job {
|
||||||
|
|
||||||
class LoadImageJob : public Job {
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LoadImageJob(MainWindow* window, FmPath* filePath);
|
LoadImageJob(const Fm::FilePath & filePath);
|
||||||
|
|
||||||
QImage image() const {
|
QImage image() const {
|
||||||
return image_;
|
return image_;
|
||||||
}
|
}
|
||||||
|
|
||||||
FmPath* filePath() const {
|
const Fm::FilePath & filePath() const {
|
||||||
return path_;
|
return path_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~LoadImageJob(); // prevent direct deletion
|
~LoadImageJob(); // prevent direct deletion
|
||||||
|
|
||||||
virtual bool run();
|
virtual void exec() override;
|
||||||
virtual void finish();
|
|
||||||
|
|
||||||
public:
|
const Fm::FilePath path_;
|
||||||
MainWindow* mainWindow_;
|
|
||||||
FmPath* path_;
|
|
||||||
QImage image_;
|
QImage image_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -43,6 +43,9 @@
|
|||||||
#include <libfm-qt/folderview.h>
|
#include <libfm-qt/folderview.h>
|
||||||
#include <libfm-qt/filepropsdialog.h>
|
#include <libfm-qt/filepropsdialog.h>
|
||||||
#include <libfm-qt/fileoperation.h>
|
#include <libfm-qt/fileoperation.h>
|
||||||
|
#include <libfm-qt/folderitemdelegate.h>
|
||||||
|
|
||||||
|
#include "upload/uploaddialog.h"
|
||||||
|
|
||||||
using namespace LxImage;
|
using namespace LxImage;
|
||||||
|
|
||||||
@ -51,11 +54,9 @@ MainWindow::MainWindow():
|
|||||||
contextMenu_(new QMenu(this)),
|
contextMenu_(new QMenu(this)),
|
||||||
slideShowTimer_(nullptr),
|
slideShowTimer_(nullptr),
|
||||||
image_(),
|
image_(),
|
||||||
currentFile_(nullptr),
|
|
||||||
// currentFileInfo_(nullptr),
|
// currentFileInfo_(nullptr),
|
||||||
imageModified_(false),
|
imageModified_(false),
|
||||||
folder_(nullptr),
|
folder_(nullptr),
|
||||||
folderPath_(nullptr),
|
|
||||||
folderModel_(new Fm::FolderModel()),
|
folderModel_(new Fm::FolderModel()),
|
||||||
proxyModel_(new Fm::ProxyFolderModel()),
|
proxyModel_(new Fm::ProxyFolderModel()),
|
||||||
modelFilter_(new ModelFilter()),
|
modelFilter_(new ModelFilter()),
|
||||||
@ -83,6 +84,8 @@ MainWindow::MainWindow():
|
|||||||
ui.view->setContextMenuPolicy(Qt::CustomContextMenu);
|
ui.view->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
connect(ui.view, &QWidget::customContextMenuRequested, this, &MainWindow::onContextMenu);
|
connect(ui.view, &QWidget::customContextMenuRequested, this, &MainWindow::onContextMenu);
|
||||||
|
|
||||||
|
connect(ui.view, &ImageView::fileDropped, this, &MainWindow::onFileDropped);
|
||||||
|
|
||||||
// install an event filter on the image view
|
// install an event filter on the image view
|
||||||
ui.view->installEventFilter(this);
|
ui.view->installEventFilter(this);
|
||||||
ui.view->setBackgroundBrush(QBrush(settings.bgColor()));
|
ui.view->setBackgroundBrush(QBrush(settings.bgColor()));
|
||||||
@ -128,16 +131,8 @@ MainWindow::~MainWindow() {
|
|||||||
loadJob_->cancel();
|
loadJob_->cancel();
|
||||||
// we don't need to do delete here. It will be done automatically
|
// we don't need to do delete here. It will be done automatically
|
||||||
}
|
}
|
||||||
if(currentFile_)
|
|
||||||
fm_path_unref(currentFile_);
|
|
||||||
//if(currentFileInfo_)
|
//if(currentFileInfo_)
|
||||||
// fm_file_info_unref(currentFileInfo_);
|
// fm_file_info_unref(currentFileInfo_);
|
||||||
if(folder_) {
|
|
||||||
g_signal_handlers_disconnect_by_func(folder_, gpointer(_onFolderLoaded), this);
|
|
||||||
g_object_unref(folder_);
|
|
||||||
}
|
|
||||||
if(folderPath_)
|
|
||||||
fm_path_unref(folderPath_);
|
|
||||||
delete folderModel_;
|
delete folderModel_;
|
||||||
delete proxyModel_;
|
delete proxyModel_;
|
||||||
delete modelFilter_;
|
delete modelFilter_;
|
||||||
@ -175,7 +170,7 @@ void MainWindow::on_actionZoomOut_triggered() {
|
|||||||
ui.view->zoomOut();
|
ui.view->zoomOut();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onFolderLoaded(FmFolder* folder) {
|
void MainWindow::onFolderLoaded() {
|
||||||
// if currently we're showing a file, get its index in the folder now
|
// if currently we're showing a file, get its index in the folder now
|
||||||
// since the folder is fully loaded.
|
// since the folder is fully loaded.
|
||||||
if(currentFile_ && !currentIndex_.isValid()) {
|
if(currentFile_ && !currentIndex_.isValid()) {
|
||||||
@ -187,22 +182,20 @@ void MainWindow::onFolderLoaded(FmFolder* folder) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// this is used to open the first image of a folder
|
// this is used to open the first image of a folder
|
||||||
else if (currentFile_ == nullptr)
|
else if (!currentFile_)
|
||||||
on_actionFirst_triggered();
|
on_actionFirst_triggered();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::openImageFile(QString fileName) {
|
void MainWindow::openImageFile(QString fileName) {
|
||||||
FmPath* path = fm_path_new_for_str(qPrintable(fileName));
|
const Fm::FilePath path = Fm::FilePath::fromPathStr(qPrintable(fileName));
|
||||||
if(currentFile_ && fm_path_equal(currentFile_, path)) {
|
|
||||||
// the same file! do not load it again
|
// the same file! do not load it again
|
||||||
fm_path_unref(path);
|
if(currentFile_ && currentFile_ == path)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
if (QFileInfo(fileName).isDir()) {
|
if (QFileInfo(fileName).isDir()) {
|
||||||
if(fm_path_equal(path, folderPath_)) {
|
if(path == folderPath_)
|
||||||
fm_path_unref(path);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
QList<QByteArray> formats = QImageReader::supportedImageFormats();
|
QList<QByteArray> formats = QImageReader::supportedImageFormats();
|
||||||
QStringList formatsFilters;
|
QStringList formatsFilters;
|
||||||
for (const QByteArray& format: formats)
|
for (const QByteArray& format: formats)
|
||||||
@ -210,20 +203,16 @@ void MainWindow::openImageFile(QString fileName) {
|
|||||||
QDir dir(fileName);
|
QDir dir(fileName);
|
||||||
dir.setNameFilters(formatsFilters);
|
dir.setNameFilters(formatsFilters);
|
||||||
dir.setFilter(QDir::Files | QDir::NoDotAndDotDot);
|
dir.setFilter(QDir::Files | QDir::NoDotAndDotDot);
|
||||||
if(dir.entryList().isEmpty()) {
|
if(dir.entryList().isEmpty())
|
||||||
fm_path_unref(path);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
if(currentFile_)
|
currentFile_ = Fm::FilePath{};
|
||||||
fm_path_unref(currentFile_);
|
|
||||||
currentFile_ = nullptr;
|
|
||||||
loadFolder(path);
|
loadFolder(path);
|
||||||
} else {
|
} else {
|
||||||
// load the image file asynchronously
|
// load the image file asynchronously
|
||||||
loadImage(path);
|
loadImage(path);
|
||||||
loadFolder(fm_path_get_parent(path));
|
loadFolder(path.parent());
|
||||||
}
|
}
|
||||||
fm_path_unref(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// paste the specified image into the current view,
|
// paste the specified image into the current view,
|
||||||
@ -238,9 +227,7 @@ void MainWindow::pasteImage(QImage newImage) {
|
|||||||
setModified(true);
|
setModified(true);
|
||||||
|
|
||||||
currentIndex_ = QModelIndex(); // invaludate current index since we don't have a folder model now
|
currentIndex_ = QModelIndex(); // invaludate current index since we don't have a folder model now
|
||||||
if(currentFile_)
|
currentFile_ = Fm::FilePath{};
|
||||||
fm_path_unref(currentFile_);
|
|
||||||
currentFile_ = nullptr;
|
|
||||||
|
|
||||||
image_ = newImage;
|
image_ = newImage;
|
||||||
ui.view->setImage(image_);
|
ui.view->setImage(image_);
|
||||||
@ -342,44 +329,36 @@ void MainWindow::on_actionSaveAs_triggered() {
|
|||||||
if(saveJob_) // if we're currently saving another file
|
if(saveJob_) // if we're currently saving another file
|
||||||
return;
|
return;
|
||||||
QString baseName;
|
QString baseName;
|
||||||
if(currentFile_) {
|
if(currentFile_)
|
||||||
char* dispName = fm_path_display_name(currentFile_, false);
|
baseName = QString::fromUtf8(currentFile_.displayName().get());
|
||||||
baseName = QString::fromUtf8(dispName);
|
|
||||||
g_free(dispName);
|
|
||||||
}
|
|
||||||
QString fileName = saveFileName(baseName);
|
QString fileName = saveFileName(baseName);
|
||||||
if(!fileName.isEmpty()) {
|
if(!fileName.isEmpty()) {
|
||||||
FmPath* path = fm_path_new_for_str(qPrintable(fileName));
|
const Fm::FilePath path = Fm::FilePath::fromPathStr(qPrintable(fileName));
|
||||||
// save the image file asynchronously
|
// save the image file asynchronously
|
||||||
saveImage(path);
|
saveImage(path);
|
||||||
|
|
||||||
if(!currentFile_) { // if we haven't load any file yet
|
if(!currentFile_) { // if we haven't load any file yet
|
||||||
currentFile_ = path;
|
currentFile_ = path;
|
||||||
loadFolder(fm_path_get_parent(path));
|
loadFolder(path.parent());
|
||||||
}
|
}
|
||||||
else
|
|
||||||
fm_path_unref(path);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionDelete_triggered() {
|
void MainWindow::on_actionDelete_triggered() {
|
||||||
// delete the current file
|
// delete the current file
|
||||||
if(currentFile_) {
|
if(currentFile_)
|
||||||
FmPathList* paths = fm_path_list_new();
|
Fm::FileOperation::deleteFiles({currentFile_});
|
||||||
fm_path_list_push_tail(paths, currentFile_);
|
|
||||||
Fm::FileOperation::deleteFiles(paths);
|
|
||||||
fm_path_list_unref(paths);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionFileProperties_triggered() {
|
void MainWindow::on_actionFileProperties_triggered() {
|
||||||
if(currentIndex_.isValid()) {
|
if(currentIndex_.isValid()) {
|
||||||
FmFileInfo* file = proxyModel_->fileInfoFromIndex(currentIndex_);
|
const auto file = proxyModel_->fileInfoFromIndex(currentIndex_);
|
||||||
// it's better to use an async job to query the file info since it's
|
// it's better to use an async job to query the file info since it's
|
||||||
// possible that loading of the folder is not finished and the file info is
|
// possible that loading of the folder is not finished and the file info is
|
||||||
// not available yet, but it's overkill for a rarely used function.
|
// not available yet, but it's overkill for a rarely used function.
|
||||||
if(file)
|
if(file)
|
||||||
Fm::FilePropsDialog::showForFile(file);
|
Fm::FilePropsDialog::showForFile(std::move(file));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,11 +375,9 @@ void MainWindow::on_actionNext_triggered() {
|
|||||||
index = proxyModel_->index(currentIndex_.row() + 1, 0);
|
index = proxyModel_->index(currentIndex_.row() + 1, 0);
|
||||||
else
|
else
|
||||||
index = proxyModel_->index(0, 0);
|
index = proxyModel_->index(0, 0);
|
||||||
FmFileInfo* info = proxyModel_->fileInfoFromIndex(index);
|
const auto info = proxyModel_->fileInfoFromIndex(index);
|
||||||
if(info) {
|
if(info)
|
||||||
FmPath* path = fm_file_info_get_path(info);
|
loadImage(info->path(), index);
|
||||||
loadImage(path, index);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,59 +390,56 @@ void MainWindow::on_actionPrevious_triggered() {
|
|||||||
index = proxyModel_->index(currentIndex_.row() - 1, 0);
|
index = proxyModel_->index(currentIndex_.row() - 1, 0);
|
||||||
else
|
else
|
||||||
index = proxyModel_->index(proxyModel_->rowCount() - 1, 0);
|
index = proxyModel_->index(proxyModel_->rowCount() - 1, 0);
|
||||||
FmFileInfo* info = proxyModel_->fileInfoFromIndex(index);
|
const auto info = proxyModel_->fileInfoFromIndex(index);
|
||||||
if(info) {
|
if(info)
|
||||||
FmPath* path = fm_file_info_get_path(info);
|
loadImage(info->path(), index);
|
||||||
loadImage(path, index);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionFirst_triggered() {
|
void MainWindow::on_actionFirst_triggered() {
|
||||||
QModelIndex index = proxyModel_->index(0, 0);
|
QModelIndex index = proxyModel_->index(0, 0);
|
||||||
if(index.isValid()) {
|
if(index.isValid()) {
|
||||||
FmFileInfo* info = proxyModel_->fileInfoFromIndex(index);
|
const auto info = proxyModel_->fileInfoFromIndex(index);
|
||||||
if(info) {
|
if(info)
|
||||||
FmPath* path = fm_file_info_get_path(info);
|
loadImage(info->path(), index);
|
||||||
loadImage(path, index);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionLast_triggered() {
|
void MainWindow::on_actionLast_triggered() {
|
||||||
QModelIndex index = proxyModel_->index(proxyModel_->rowCount() - 1, 0);
|
QModelIndex index = proxyModel_->index(proxyModel_->rowCount() - 1, 0);
|
||||||
if(index.isValid()) {
|
if(index.isValid()) {
|
||||||
FmFileInfo* info = proxyModel_->fileInfoFromIndex(index);
|
const auto info = proxyModel_->fileInfoFromIndex(index);
|
||||||
if(info) {
|
if(info)
|
||||||
FmPath* path = fm_file_info_get_path(info);;
|
loadImage(info->path(), index);
|
||||||
loadImage(path, index);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::loadFolder(FmPath* newFolderPath) {
|
void MainWindow::loadFolder(const Fm::FilePath & newFolderPath) {
|
||||||
if(folder_) { // an folder is already loaded
|
if(folder_) { // an folder is already loaded
|
||||||
if(fm_path_equal(newFolderPath, folderPath_)) // same folder, ignore
|
if(newFolderPath == folderPath_) // same folder, ignore
|
||||||
return;
|
return;
|
||||||
// free current folder
|
disconnect(folder_.get(), nullptr, this, nullptr); // disconnect from all signals
|
||||||
g_signal_handlers_disconnect_by_func(folder_, gpointer(_onFolderLoaded), this);
|
|
||||||
g_object_unref(folder_);
|
|
||||||
fm_path_unref(folderPath_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
folderPath_ = fm_path_ref(newFolderPath);
|
folderPath_ = newFolderPath;
|
||||||
folder_ = fm_folder_from_path(newFolderPath);
|
folder_ = Fm::Folder::fromPath(folderPath_);
|
||||||
g_signal_connect(folder_, "finish-loading", G_CALLBACK(_onFolderLoaded), this);
|
connect(folder_.get(), &Fm::Folder::finishLoading, this, &MainWindow::onFolderLoaded);
|
||||||
|
|
||||||
folderModel_->setFolder(folder_);
|
folderModel_->setFolder(folder_);
|
||||||
currentIndex_ = QModelIndex(); // set current index to invalid
|
currentIndex_ = QModelIndex(); // set current index to invalid
|
||||||
}
|
}
|
||||||
|
|
||||||
// the image is loaded (the method is only called if the loading is not cancelled)
|
// the image is loaded (the method is only called if the loading is not cancelled)
|
||||||
void MainWindow::onImageLoaded(LoadImageJob* job) {
|
void MainWindow::onImageLoaded() {
|
||||||
|
// Note: As the signal finished() is emitted from different thread,
|
||||||
|
// we can get it even after canceling the job (and setting the loadJob_
|
||||||
|
// to nullptr). This simple check should be enough.
|
||||||
|
if (sender() == loadJob_)
|
||||||
|
{
|
||||||
|
image_ = loadJob_->image();
|
||||||
|
|
||||||
loadJob_ = nullptr; // the job object will be freed later automatically
|
loadJob_ = nullptr; // the job object will be freed later automatically
|
||||||
|
|
||||||
image_ = job->image();
|
|
||||||
ui.view->setAutoZoomFit(true);
|
ui.view->setAutoZoomFit(true);
|
||||||
ui.view->setImage(image_);
|
ui.view->setImage(image_);
|
||||||
|
|
||||||
@ -474,18 +448,14 @@ void MainWindow::onImageLoaded(LoadImageJob* job) {
|
|||||||
|
|
||||||
updateUI();
|
updateUI();
|
||||||
|
|
||||||
if(job->error()) {
|
|
||||||
// if there are errors
|
|
||||||
// TODO: show a info bar?
|
|
||||||
}
|
|
||||||
|
|
||||||
/* we resized and moved the window without showing
|
/* we resized and moved the window without showing
|
||||||
it in updateUI(), so we need to show it here */
|
it in updateUI(), so we need to show it here */
|
||||||
show();
|
show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onImageSaved(SaveImageJob* job) {
|
void MainWindow::onImageSaved() {
|
||||||
if(!job->error()) {
|
if(!saveJob_->failed()) {
|
||||||
setModified(false);
|
setModified(false);
|
||||||
}
|
}
|
||||||
saveJob_ = nullptr;
|
saveJob_ = nullptr;
|
||||||
@ -535,16 +505,16 @@ bool MainWindow::eventFilter(QObject* watched, QEvent* event) {
|
|||||||
return QObject::eventFilter(watched, event);
|
return QObject::eventFilter(watched, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex MainWindow::indexFromPath(FmPath* filePath) {
|
QModelIndex MainWindow::indexFromPath(const Fm::FilePath & filePath) {
|
||||||
// if the folder is already loaded, figure out our index
|
// if the folder is already loaded, figure out our index
|
||||||
// otherwise, it will be done again in onFolderLoaded() when the folder is fully loaded.
|
// otherwise, it will be done again in onFolderLoaded() when the folder is fully loaded.
|
||||||
if(folder_ && fm_folder_is_loaded(folder_)) {
|
if(folder_ && folder_->isLoaded()) {
|
||||||
QModelIndex index;
|
QModelIndex index;
|
||||||
int count = proxyModel_->rowCount();
|
int count = proxyModel_->rowCount();
|
||||||
for(int row = 0; row < count; ++row) {
|
for(int row = 0; row < count; ++row) {
|
||||||
index = proxyModel_->index(row, 0);
|
index = proxyModel_->index(row, 0);
|
||||||
FmFileInfo* info = proxyModel_->fileInfoFromIndex(index);
|
const auto info = proxyModel_->fileInfoFromIndex(index);
|
||||||
if(info && fm_path_equal(filePath, fm_file_info_get_path(info))) {
|
if(info && filePath == info->path()) {
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -564,19 +534,19 @@ void MainWindow::updateUI() {
|
|||||||
|
|
||||||
QString title;
|
QString title;
|
||||||
if(currentFile_) {
|
if(currentFile_) {
|
||||||
char* dispName = fm_path_display_basename(currentFile_);
|
const Fm::CStrPtr dispName = currentFile_.displayName();
|
||||||
if(loadJob_) { // if loading is in progress
|
if(loadJob_) { // if loading is in progress
|
||||||
title = tr("[*]%1 (Loading...) - Image Viewer")
|
title = tr("[*]%1 (Loading...) - Image Viewer")
|
||||||
.arg(QString::fromUtf8(dispName));
|
.arg(QString::fromUtf8(dispName.get()));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(image_.isNull()) {
|
if(image_.isNull()) {
|
||||||
title = tr("[*]%1 (Failed to Load) - Image Viewer")
|
title = tr("[*]%1 (Failed to Load) - Image Viewer")
|
||||||
.arg(QString::fromUtf8(dispName));
|
.arg(QString::fromUtf8(dispName.get()));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
title = tr("[*]%1 (%2x%3) - Image Viewer")
|
title = tr("[*]%1 (%2x%3) - Image Viewer")
|
||||||
.arg(QString::fromUtf8(dispName))
|
.arg(QString::fromUtf8(dispName.get()))
|
||||||
.arg(image_.width())
|
.arg(image_.width())
|
||||||
.arg(image_.height());
|
.arg(image_.height());
|
||||||
/* Here we try to implement the following behavior as far as possible:
|
/* Here we try to implement the following behavior as far as possible:
|
||||||
@ -608,7 +578,6 @@ void MainWindow::updateUI() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g_free(dispName);
|
|
||||||
// TODO: update status bar, show current index in the folder
|
// TODO: update status bar, show current index in the folder
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -620,7 +589,7 @@ void MainWindow::updateUI() {
|
|||||||
|
|
||||||
// Load the specified image file asynchronously in a worker thread.
|
// Load the specified image file asynchronously in a worker thread.
|
||||||
// When the loading is finished, onImageLoaded() will be called.
|
// When the loading is finished, onImageLoaded() will be called.
|
||||||
void MainWindow::loadImage(FmPath* filePath, QModelIndex index) {
|
void MainWindow::loadImage(const Fm::FilePath & filePath, QModelIndex index) {
|
||||||
// cancel loading of current image
|
// cancel loading of current image
|
||||||
if(loadJob_) {
|
if(loadJob_) {
|
||||||
loadJob_->cancel(); // the job object will be freed automatically later
|
loadJob_->cancel(); // the job object will be freed automatically later
|
||||||
@ -633,14 +602,12 @@ void MainWindow::loadImage(FmPath* filePath, QModelIndex index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
currentIndex_ = index;
|
currentIndex_ = index;
|
||||||
if(currentFile_)
|
currentFile_ = filePath;
|
||||||
fm_path_unref(currentFile_);
|
|
||||||
currentFile_ = fm_path_ref(filePath);
|
|
||||||
// clear current image, but do not update the view now to prevent flickers
|
// clear current image, but do not update the view now to prevent flickers
|
||||||
image_ = QImage();
|
image_ = QImage();
|
||||||
|
|
||||||
const char* basename = fm_path_get_basename(currentFile_);
|
const Fm::CStrPtr basename = currentFile_.baseName();
|
||||||
char* mime_type = g_content_type_guess(basename, NULL, 0, NULL);
|
char* mime_type = g_content_type_guess(basename.get(), NULL, 0, NULL);
|
||||||
QString mimeType;
|
QString mimeType;
|
||||||
if (mime_type) {
|
if (mime_type) {
|
||||||
mimeType = QString(mime_type);
|
mimeType = QString(mime_type);
|
||||||
@ -648,33 +615,47 @@ void MainWindow::loadImage(FmPath* filePath, QModelIndex index) {
|
|||||||
}
|
}
|
||||||
if(mimeType == "image/gif"
|
if(mimeType == "image/gif"
|
||||||
|| mimeType == "image/svg+xml" || mimeType == "image/svg+xml-compressed") {
|
|| mimeType == "image/svg+xml" || mimeType == "image/svg+xml-compressed") {
|
||||||
char *file_name = fm_path_to_str(currentFile_);
|
const Fm::CStrPtr file_name = currentFile_.toString();
|
||||||
QString fileName(file_name);
|
|
||||||
g_free(file_name);
|
|
||||||
ui.view->setAutoZoomFit(true); // like in onImageLoaded()
|
ui.view->setAutoZoomFit(true); // like in onImageLoaded()
|
||||||
if(mimeType == "image/gif")
|
if(mimeType == "image/gif")
|
||||||
ui.view->setGifAnimation(fileName);
|
ui.view->setGifAnimation(QString{file_name.get()});
|
||||||
else
|
else
|
||||||
ui.view->setSVG(fileName);
|
ui.view->setSVG(QString{file_name.get()});
|
||||||
image_ = ui.view->image();
|
image_ = ui.view->image();
|
||||||
updateUI();
|
updateUI();
|
||||||
show();
|
show();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// start a new gio job to load the specified image
|
// start a new gio job to load the specified image
|
||||||
loadJob_ = new LoadImageJob(this, filePath);
|
loadJob_ = new LoadImageJob(currentFile_);
|
||||||
loadJob_->start();
|
connect(loadJob_, &Fm::Job::finished, this, &MainWindow::onImageLoaded);
|
||||||
|
connect(loadJob_, &Fm::Job::error, this
|
||||||
|
, [] (const Fm::GErrorPtr & err, Fm::Job::ErrorSeverity /*severity*/, Fm::Job::ErrorAction & /*response*/)
|
||||||
|
{
|
||||||
|
// TODO: show a info bar?
|
||||||
|
qWarning().noquote() << "lximage-qt:" << err.message();
|
||||||
|
}
|
||||||
|
, Qt::BlockingQueuedConnection);
|
||||||
|
loadJob_->runAsync();
|
||||||
|
|
||||||
updateUI();
|
updateUI();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::saveImage(FmPath* filePath) {
|
void MainWindow::saveImage(const Fm::FilePath & filePath) {
|
||||||
if(saveJob_) // do not launch a new job if the current one is still in progress
|
if(saveJob_) // do not launch a new job if the current one is still in progress
|
||||||
return;
|
return;
|
||||||
// start a new gio job to save current image to the specified path
|
// start a new gio job to save current image to the specified path
|
||||||
saveJob_ = new SaveImageJob(this, filePath);
|
saveJob_ = new SaveImageJob(image_, filePath);
|
||||||
saveJob_->start();
|
connect(saveJob_, &Fm::Job::finished, this, &MainWindow::onImageSaved);
|
||||||
|
connect(saveJob_, &Fm::Job::error, this
|
||||||
|
, [] (const Fm::GErrorPtr & err, Fm::Job::ErrorSeverity /*severity*/, Fm::Job::ErrorAction & /*response*/)
|
||||||
|
{
|
||||||
|
// TODO: show a info bar?
|
||||||
|
qWarning().noquote() << "lximage-qt:" << err.message();
|
||||||
|
}
|
||||||
|
, Qt::BlockingQueuedConnection);
|
||||||
|
saveJob_->runAsync();
|
||||||
// FIXME: add a cancel button to the UI? update status bar?
|
// FIXME: add a cancel button to the UI? update status bar?
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -753,6 +734,13 @@ void MainWindow::on_actionPaste_triggered() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_actionUpload_triggered()
|
||||||
|
{
|
||||||
|
if (currentFile_.isValid()) {
|
||||||
|
UploadDialog(this, currentFile_.localPath().get()).exec();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionFlipVertical_triggered() {
|
void MainWindow::on_actionFlipVertical_triggered() {
|
||||||
bool hasQGraphicsItem(false);
|
bool hasQGraphicsItem(false);
|
||||||
if(QGraphicsItem *graphItem = getGraphicsItem()) {
|
if(QGraphicsItem *graphItem = getGraphicsItem()) {
|
||||||
@ -871,8 +859,10 @@ void MainWindow::setShowThumbnails(bool show) {
|
|||||||
listView->installEventFilter(this);
|
listView->installEventFilter(this);
|
||||||
// FIXME: optimize the size of the thumbnail view
|
// FIXME: optimize the size of the thumbnail view
|
||||||
// FIXME if the thumbnail view is docked elsewhere, update the settings.
|
// FIXME if the thumbnail view is docked elsewhere, update the settings.
|
||||||
|
if(Fm::FolderItemDelegate* delegate = static_cast<Fm::FolderItemDelegate*>(listView->itemDelegateForColumn(Fm::FolderModel::ColumnFileName))) {
|
||||||
int scrollHeight = style()->pixelMetric(QStyle::PM_ScrollBarExtent);
|
int scrollHeight = style()->pixelMetric(QStyle::PM_ScrollBarExtent);
|
||||||
thumbnailsView_->setFixedHeight(listView->gridSize().height() + scrollHeight);
|
thumbnailsView_->setFixedHeight(delegate->itemSize().height() + scrollHeight);
|
||||||
|
}
|
||||||
thumbnailsView_->setModel(proxyModel_);
|
thumbnailsView_->setModel(proxyModel_);
|
||||||
proxyModel_->setShowThumbnails(true);
|
proxyModel_->setShowThumbnails(true);
|
||||||
if (currentFile_) { // select the loaded image
|
if (currentFile_) { // select the loaded image
|
||||||
@ -978,14 +968,18 @@ void MainWindow::onExitFullscreen() {
|
|||||||
showNormal();
|
showNormal();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onThumbnailSelChanged(const QItemSelection& selected, const QItemSelection& deselected) {
|
void MainWindow::onThumbnailSelChanged(const QItemSelection& selected, const QItemSelection& /*deselected*/) {
|
||||||
// the selected item of thumbnail view is changed
|
// the selected item of thumbnail view is changed
|
||||||
if(!selected.isEmpty()) {
|
if(!selected.isEmpty()) {
|
||||||
QModelIndex index = selected.first().topLeft();
|
QModelIndex index = selected.first().topLeft();
|
||||||
if(index.isValid() && index != currentIndex_) {
|
if(index.isValid() && index != currentIndex_) {
|
||||||
FmFileInfo* file = proxyModel_->fileInfoFromIndex(index);
|
const auto file = proxyModel_->fileInfoFromIndex(index);
|
||||||
if(file)
|
if(file)
|
||||||
loadImage(fm_file_info_get_path(file), index);
|
loadImage(file->path(), index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::onFileDropped(const QString path) {
|
||||||
|
openImageFile(path);
|
||||||
|
}
|
||||||
|
@ -62,7 +62,7 @@ public:
|
|||||||
|
|
||||||
void pasteImage(QImage newImage);
|
void pasteImage(QImage newImage);
|
||||||
|
|
||||||
FmPath* currentFile() const {
|
const Fm::FilePath & currentFile() const {
|
||||||
return currentFile_;
|
return currentFile_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,9 +70,9 @@ public:
|
|||||||
void applySettings();
|
void applySettings();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void loadImage(FmPath* filePath, QModelIndex index = QModelIndex());
|
void loadImage(const Fm::FilePath & filePath, QModelIndex index = QModelIndex());
|
||||||
void saveImage(FmPath* filePath); // save current image to a file
|
void saveImage(const Fm::FilePath & filePath); // save current image to a file
|
||||||
void loadFolder(FmPath* newFolderPath);
|
void loadFolder(const Fm::FilePath & newFolderPath);
|
||||||
QString openFileName();
|
QString openFileName();
|
||||||
QString openDirectory();
|
QString openDirectory();
|
||||||
QString saveFileName(QString defaultName = QString());
|
QString saveFileName(QString defaultName = QString());
|
||||||
@ -80,8 +80,8 @@ protected:
|
|||||||
virtual void resizeEvent(QResizeEvent *event);
|
virtual void resizeEvent(QResizeEvent *event);
|
||||||
virtual void closeEvent(QCloseEvent *event);
|
virtual void closeEvent(QCloseEvent *event);
|
||||||
|
|
||||||
void onImageLoaded(LoadImageJob* job);
|
void onImageLoaded();
|
||||||
void onImageSaved(SaveImageJob* job);
|
void onImageSaved();
|
||||||
|
|
||||||
virtual bool eventFilter(QObject* watched, QEvent* event);
|
virtual bool eventFilter(QObject* watched, QEvent* event);
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
@ -103,6 +103,7 @@ private Q_SLOTS:
|
|||||||
void on_actionFlipHorizontal_triggered();
|
void on_actionFlipHorizontal_triggered();
|
||||||
void on_actionCopy_triggered();
|
void on_actionCopy_triggered();
|
||||||
void on_actionPaste_triggered();
|
void on_actionPaste_triggered();
|
||||||
|
void on_actionUpload_triggered();
|
||||||
|
|
||||||
void on_actionShowThumbnails_triggered(bool checked);
|
void on_actionShowThumbnails_triggered(bool checked);
|
||||||
void on_actionFullScreen_triggered(bool checked);
|
void on_actionFullScreen_triggered(bool checked);
|
||||||
@ -123,31 +124,28 @@ private Q_SLOTS:
|
|||||||
|
|
||||||
void onThumbnailSelChanged(const QItemSelection & selected, const QItemSelection & deselected);
|
void onThumbnailSelChanged(const QItemSelection & selected, const QItemSelection & deselected);
|
||||||
|
|
||||||
|
void onFileDropped(const QString path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onFolderLoaded(FmFolder* folder);
|
void onFolderLoaded();
|
||||||
void updateUI();
|
void updateUI();
|
||||||
void setModified(bool modified);
|
void setModified(bool modified);
|
||||||
QModelIndex indexFromPath(FmPath* filePath);
|
QModelIndex indexFromPath(const Fm::FilePath & filePath);
|
||||||
QGraphicsItem* getGraphicsItem();
|
QGraphicsItem* getGraphicsItem();
|
||||||
|
|
||||||
// GObject related signal handers and callbacks
|
|
||||||
static void _onFolderLoaded(FmFolder* folder, MainWindow* _this) {
|
|
||||||
_this->onFolderLoaded(folder);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::MainWindow ui;
|
Ui::MainWindow ui;
|
||||||
QMenu* contextMenu_;
|
QMenu* contextMenu_;
|
||||||
QTimer* slideShowTimer_;
|
QTimer* slideShowTimer_;
|
||||||
|
|
||||||
QImage image_; // the image currently shown
|
QImage image_; // the image currently shown
|
||||||
FmPath* currentFile_; // path to current image file
|
Fm::FilePath currentFile_; // path to current image file
|
||||||
// FmFileInfo* currentFileInfo_; // info of the current file, can be NULL
|
// FmFileInfo* currentFileInfo_; // info of the current file, can be NULL
|
||||||
bool imageModified_; // the current image is modified by rotation, flip, or others and needs to be saved
|
bool imageModified_; // the current image is modified by rotation, flip, or others and needs to be saved
|
||||||
|
|
||||||
// folder browsing
|
// folder browsing
|
||||||
FmFolder* folder_;
|
std::shared_ptr<Fm::Folder> folder_;
|
||||||
FmPath* folderPath_;
|
Fm::FilePath folderPath_;
|
||||||
Fm::FolderModel* folderModel_;
|
Fm::FolderModel* folderModel_;
|
||||||
Fm::ProxyFolderModel* proxyModel_;
|
Fm::ProxyFolderModel* proxyModel_;
|
||||||
ModelFilter* modelFilter_;
|
ModelFilter* modelFilter_;
|
||||||
@ -161,6 +159,6 @@ private:
|
|||||||
SaveImageJob* saveJob_;
|
SaveImageJob* saveJob_;
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
}
|
||||||
|
|
||||||
#endif // LXIMAGE_MAINWINDOW_H
|
#endif // LXIMAGE_MAINWINDOW_H
|
||||||
|
@ -108,6 +108,8 @@
|
|||||||
<addaction name="actionCopy"/>
|
<addaction name="actionCopy"/>
|
||||||
<addaction name="actionPaste"/>
|
<addaction name="actionPaste"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionUpload"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
<addaction name="actionPreferences"/>
|
<addaction name="actionPreferences"/>
|
||||||
</widget>
|
</widget>
|
||||||
<addaction name="menu_File"/>
|
<addaction name="menu_File"/>
|
||||||
@ -466,6 +468,17 @@
|
|||||||
<string>Ctrl+D</string>
|
<string>Ctrl+D</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionUpload">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="upload-media"/>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Upload</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Upload the image</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
|
@ -30,10 +30,11 @@ ModelFilter::~ModelFilter() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ModelFilter::filterAcceptsRow(const Fm::ProxyFolderModel* model, FmFileInfo* info) const {
|
bool ModelFilter::filterAcceptsRow(const Fm::ProxyFolderModel* model, const std::shared_ptr<const Fm::FileInfo>& info) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(model)
|
||||||
|
|
||||||
// filter out non-image files and formats that we don't support.
|
// filter out non-image files and formats that we don't support.
|
||||||
if(!fm_file_info_is_image(info))
|
return info && info->isImage();
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ namespace LxImage {
|
|||||||
class ModelFilter: public Fm::ProxyFolderModelFilter {
|
class ModelFilter: public Fm::ProxyFolderModelFilter {
|
||||||
public:
|
public:
|
||||||
ModelFilter();
|
ModelFilter();
|
||||||
virtual bool filterAcceptsRow(const Fm::ProxyFolderModel* model, FmFileInfo* info) const;
|
virtual bool filterAcceptsRow(const Fm::ProxyFolderModel* model, const std::shared_ptr<const Fm::FileInfo>& info) const override;
|
||||||
virtual ~ModelFilter();
|
virtual ~ModelFilter();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ void PreferencesDialog::initIconThemes(Settings& settings) {
|
|||||||
|
|
||||||
iconThemes.remove("hicolor"); // remove hicolor, which is only a fallback
|
iconThemes.remove("hicolor"); // remove hicolor, which is only a fallback
|
||||||
QHash<QString, QString>::const_iterator it;
|
QHash<QString, QString>::const_iterator it;
|
||||||
for(it = iconThemes.begin(); it != iconThemes.end(); ++it) {
|
for(it = iconThemes.constBegin(); it != iconThemes.constEnd(); ++it) {
|
||||||
ui.iconTheme->addItem(it.value(), it.key());
|
ui.iconTheme->addItem(it.value(), it.key());
|
||||||
}
|
}
|
||||||
ui.iconTheme->model()->sort(0); // sort the list of icon theme names
|
ui.iconTheme->model()->sort(0); // sort the list of icon theme names
|
||||||
|
@ -26,47 +26,58 @@
|
|||||||
|
|
||||||
using namespace LxImage;
|
using namespace LxImage;
|
||||||
|
|
||||||
SaveImageJob::SaveImageJob(MainWindow* window, FmPath* filePath):
|
SaveImageJob::SaveImageJob(const QImage & image, const Fm::FilePath & filePath):
|
||||||
Job(),
|
path_{filePath},
|
||||||
mainWindow_(window),
|
image_{image},
|
||||||
path_(fm_path_ref(filePath)),
|
failed_{true}
|
||||||
image_(window->image()) {
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
SaveImageJob::~SaveImageJob() {
|
SaveImageJob::~SaveImageJob() {
|
||||||
fm_path_unref(path_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is called from the worker thread, not main thread
|
// This is called from the worker thread, not main thread
|
||||||
bool SaveImageJob::run() {
|
void SaveImageJob::exec() {
|
||||||
GFile* gfile = fm_path_to_gfile(path_);
|
const Fm::CStrPtr f = path_.baseName();
|
||||||
GFileOutputStream* fileStream = g_file_replace(gfile, NULL, false, G_FILE_CREATE_NONE, cancellable_, &error_);
|
char const * format = f.get();
|
||||||
g_object_unref(gfile);
|
|
||||||
|
|
||||||
if(fileStream) { // if the file stream is successfually opened
|
|
||||||
const char* format = fm_path_get_basename(path_);
|
|
||||||
format = strrchr(format, '.');
|
format = strrchr(format, '.');
|
||||||
if(format) // use filename extension as the image format
|
if(format) // use filename extension as the image format
|
||||||
++format;
|
++format;
|
||||||
|
|
||||||
QBuffer imageBuffer;
|
QBuffer imageBuffer;
|
||||||
image_.save(&imageBuffer, format); // save the image to buffer
|
image_.save(&imageBuffer, format); // save the image to buffer
|
||||||
|
|
||||||
|
GFileOutputStream* fileStream = nullptr;
|
||||||
|
Fm::GErrorPtr error;
|
||||||
|
ErrorAction act = ErrorAction::RETRY;
|
||||||
|
while (act == ErrorAction::RETRY && !isCancelled())
|
||||||
|
{
|
||||||
|
error.reset();
|
||||||
|
if (nullptr == (fileStream = g_file_replace(path_.gfile().get(), NULL, false, G_FILE_CREATE_NONE, cancellable().get(), &error)))
|
||||||
|
{
|
||||||
|
act = emitError(error);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the file stream is successfually opened
|
||||||
|
if (!isCancelled())
|
||||||
|
{
|
||||||
GOutputStream* outputStream = G_OUTPUT_STREAM(fileStream);
|
GOutputStream* outputStream = G_OUTPUT_STREAM(fileStream);
|
||||||
g_output_stream_write_all(outputStream,
|
g_output_stream_write_all(outputStream,
|
||||||
imageBuffer.data().constData(),
|
imageBuffer.data().constData(),
|
||||||
imageBuffer.size(),
|
imageBuffer.size(),
|
||||||
NULL,
|
NULL,
|
||||||
cancellable_,
|
cancellable().get(),
|
||||||
&error_);
|
&error);
|
||||||
g_output_stream_close(outputStream, NULL, NULL);
|
g_output_stream_close(outputStream, NULL, NULL);
|
||||||
|
if (!error)
|
||||||
|
{
|
||||||
|
// successfully written
|
||||||
|
failed_ = false;
|
||||||
|
break; // successfully written
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this function is called from main thread only
|
act = emitError(error);
|
||||||
void SaveImageJob::finish() {
|
}
|
||||||
// only do processing if the job is not cancelled
|
|
||||||
if(!g_cancellable_is_cancelled(cancellable_)) {
|
|
||||||
mainWindow_->onImageSaved(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,39 +21,39 @@
|
|||||||
#ifndef LXIMAGE_SAVEIMAGEJOB_H
|
#ifndef LXIMAGE_SAVEIMAGEJOB_H
|
||||||
#define LXIMAGE_SAVEIMAGEJOB_H
|
#define LXIMAGE_SAVEIMAGEJOB_H
|
||||||
|
|
||||||
#include <gio/gio.h>
|
#include <libfm-qt/core/job.h>
|
||||||
#include <libfm/fm.h>
|
#include <libfm-qt/core/filepath.h>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include "job.h"
|
|
||||||
|
|
||||||
namespace LxImage {
|
namespace LxImage {
|
||||||
|
|
||||||
class MainWindow;
|
class SaveImageJob : public Fm::Job {
|
||||||
|
|
||||||
class SaveImageJob : public Job {
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SaveImageJob(MainWindow* window, FmPath* filePath);
|
SaveImageJob(const QImage & image, const Fm::FilePath & filePath);
|
||||||
|
|
||||||
QImage image() const {
|
QImage image() const {
|
||||||
return image_;
|
return image_;
|
||||||
}
|
}
|
||||||
|
|
||||||
FmPath* filePath() const {
|
const Fm::FilePath & filePath() const {
|
||||||
return path_;
|
return path_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool failed() const
|
||||||
|
{
|
||||||
|
return failed_;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool run();
|
virtual void exec() override;
|
||||||
virtual void finish();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~SaveImageJob(); // prevent direct deletion
|
~SaveImageJob(); // prevent direct deletion
|
||||||
|
|
||||||
public:
|
const Fm::FilePath path_;
|
||||||
MainWindow* mainWindow_;
|
const QImage image_;
|
||||||
FmPath* path_;
|
bool failed_;
|
||||||
QImage image_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "screenshotdialog.h"
|
#include "screenshotdialog.h"
|
||||||
|
#include "screenshotselectarea.h"
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
@ -173,6 +174,13 @@ void ScreenshotDialog::doScreenshot() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(ui.screenArea->isChecked() && !image.isNull()) {
|
||||||
|
ScreenshotSelectArea selectArea(image);
|
||||||
|
if(QDialog::Accepted == selectArea.exec()) {
|
||||||
|
image = image.copy(selectArea.selectedArea());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Application* app = static_cast<Application*>(qApp);
|
Application* app = static_cast<Application*>(qApp);
|
||||||
MainWindow* window = app->createWindow();
|
MainWindow* window = app->createWindow();
|
||||||
if(!image.isNull())
|
if(!image.isNull())
|
||||||
|
@ -15,8 +15,7 @@
|
|||||||
</property>
|
</property>
|
||||||
<property name="windowIcon">
|
<property name="windowIcon">
|
||||||
<iconset theme="camera-photo">
|
<iconset theme="camera-photo">
|
||||||
<normaloff/>
|
<normaloff>.</normaloff>.</iconset>
|
||||||
</iconset>
|
|
||||||
</property>
|
</property>
|
||||||
<layout class="QFormLayout" name="formLayout">
|
<layout class="QFormLayout" name="formLayout">
|
||||||
<property name="fieldGrowthPolicy">
|
<property name="fieldGrowthPolicy">
|
||||||
@ -52,6 +51,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="screenArea">
|
||||||
|
<property name="text">
|
||||||
|
<string>Capture an area of the screen</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
51
src/screenshotselectarea.cpp
Normal file
51
src/screenshotselectarea.cpp
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
<one line to give the library's name and an idea of what it does.>
|
||||||
|
Copyright (C) 2013 <copyright holder> <email>
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library 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
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "screenshotselectarea.h"
|
||||||
|
#include <QMouseEvent>
|
||||||
|
|
||||||
|
using namespace LxImage;
|
||||||
|
|
||||||
|
ScreenshotSelectArea::ScreenshotSelectArea(const QImage & image, QWidget* parent) : QDialog(parent)
|
||||||
|
{
|
||||||
|
scene_ = new QGraphicsScene(this);
|
||||||
|
scene_->addPixmap(QPixmap::fromImage(image));
|
||||||
|
|
||||||
|
view_ = new ScreenshotSelectAreaGraphicsView(scene_, this);
|
||||||
|
view_->setRenderHints( QPainter::Antialiasing );
|
||||||
|
view_->setHorizontalScrollBarPolicy ( Qt::ScrollBarAlwaysOff );
|
||||||
|
view_->setVerticalScrollBarPolicy ( Qt::ScrollBarAlwaysOff );
|
||||||
|
view_->show();
|
||||||
|
view_->move(0,0);
|
||||||
|
view_->resize(image.width(), image.height());
|
||||||
|
setWindowState(windowState() | Qt::WindowFullScreen);
|
||||||
|
connect(view_, &ScreenshotSelectAreaGraphicsView::selectedArea, this, &ScreenshotSelectArea::areaSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect ScreenshotSelectArea::selectedArea()
|
||||||
|
{
|
||||||
|
return selectedRect_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenshotSelectArea::areaSelected(QRect rect)
|
||||||
|
{
|
||||||
|
this->selectedRect_ = rect;
|
||||||
|
accept();
|
||||||
|
}
|
50
src/screenshotselectarea.h
Normal file
50
src/screenshotselectarea.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
<one line to give the library's name and an idea of what it does.>
|
||||||
|
Copyright (C) 2013 <copyright holder> <email>
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library 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
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef LXIMAGE_SCREENSHOTDIALOG_SELECT_AREA_H
|
||||||
|
#define LXIMAGE_SCREENSHOTDIALOG_SELECT_AREA_H
|
||||||
|
|
||||||
|
#include "screenshotselectareagraphicsview.h"
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QGraphicsScene>
|
||||||
|
#include <QGraphicsView>
|
||||||
|
#include <QGraphicsRectItem>
|
||||||
|
|
||||||
|
|
||||||
|
namespace LxImage {
|
||||||
|
|
||||||
|
class ScreenshotSelectArea : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
ScreenshotSelectArea(const QImage & image, QWidget* parent = 0);
|
||||||
|
QRect selectedArea();
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void areaSelected(QRect rect);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QGraphicsScene *scene_;
|
||||||
|
ScreenshotSelectAreaGraphicsView *view_;
|
||||||
|
QRect selectedRect_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // LXIMAGE_SCREENSHOTDIALOG_SELECT_AREA_H
|
58
src/screenshotselectareagraphicsview.cpp
Normal file
58
src/screenshotselectareagraphicsview.cpp
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
<one line to give the library's name and an idea of what it does.>
|
||||||
|
Copyright (C) 2013 <copyright holder> <email>
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library 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
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "screenshotselectareagraphicsview.h"
|
||||||
|
#include <QMouseEvent>
|
||||||
|
|
||||||
|
using namespace LxImage;
|
||||||
|
|
||||||
|
ScreenshotSelectAreaGraphicsView::ScreenshotSelectAreaGraphicsView(QGraphicsScene* scene, QWidget* parent) : QGraphicsView(scene, parent)
|
||||||
|
{
|
||||||
|
p0_ = QPointF(-1.0, -1.0);
|
||||||
|
selectedAreaRect_ = nullptr;
|
||||||
|
setCursor(Qt::CrossCursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenshotSelectAreaGraphicsView::mousePressEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
if(p0_.x() < 0) {
|
||||||
|
p0_ = QPointF(event->pos());
|
||||||
|
} else {
|
||||||
|
if(selectedAreaRect_ == nullptr) {
|
||||||
|
QColor highlight = palette().color(QPalette::Active,QPalette::Highlight);
|
||||||
|
QPen pen(highlight, 3, Qt::DashDotLine, Qt::RoundCap, Qt::RoundJoin);
|
||||||
|
QColor color(highlight);
|
||||||
|
color.setAlpha(128);
|
||||||
|
QBrush brush(color);
|
||||||
|
selectedAreaRect_ = scene()->addRect(QRectF(), pen, brush);
|
||||||
|
}
|
||||||
|
selectedAreaRect_->setRect(QRectF(p0_,QPointF(event->pos())).normalized());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenshotSelectAreaGraphicsView::mouseMoveEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
mousePressEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenshotSelectAreaGraphicsView::mouseReleaseEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
Q_EMIT selectedArea(QRectF(p0_,QPointF(event->pos())).normalized().toRect());
|
||||||
|
}
|
52
src/screenshotselectareagraphicsview.h
Normal file
52
src/screenshotselectareagraphicsview.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
<one line to give the library's name and an idea of what it does.>
|
||||||
|
Copyright (C) 2013 <copyright holder> <email>
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library 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
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef LXIMAGE_SCREENSHOTDIALOG_SELECT_AREA_GRAPICS_VIEW_H
|
||||||
|
#define LXIMAGE_SCREENSHOTDIALOG_SELECT_AREA_GRAPICS_VIEW_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QGraphicsScene>
|
||||||
|
#include <QGraphicsView>
|
||||||
|
#include <QGraphicsRectItem>
|
||||||
|
|
||||||
|
|
||||||
|
namespace LxImage {
|
||||||
|
|
||||||
|
class ScreenshotSelectAreaGraphicsView : public QGraphicsView {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
ScreenshotSelectAreaGraphicsView(QGraphicsScene* scene, QWidget* parent = 0);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void selectedArea(QRect rect);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void mousePressEvent(QMouseEvent *event);
|
||||||
|
void mouseMoveEvent(QMouseEvent *event);
|
||||||
|
void mouseReleaseEvent(QMouseEvent *event);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPointF p0_;
|
||||||
|
QGraphicsRectItem *selectedAreaRect_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // LXIMAGE_SCREENSHOTDIALOG_SELECT_AREA_H
|
4
src/translations/lximage-qt-screenshot_lt.desktop
Normal file
4
src/translations/lximage-qt-screenshot_lt.desktop
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#Translations
|
||||||
|
Name[lt]=Ekrano kopija
|
||||||
|
GenericName[lt]=Ekrano kopija
|
||||||
|
Comment[lt]=Padaryti ekrano kopiją
|
4
src/translations/lximage-qt_lt.desktop
Normal file
4
src/translations/lximage-qt_lt.desktop
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#Translations
|
||||||
|
Name[lt]=LXImage
|
||||||
|
GenericName[lt]=Paveikslų žiūryklė
|
||||||
|
Comment[lt]=LXQt paveikslų žiūryklė
|
56
src/upload/imageshackprovider.cpp
Normal file
56
src/upload/imageshackprovider.cpp
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
LxImage - image viewer and screenshot tool for lxqt
|
||||||
|
Copyright (C) 2017 Nathan Osman <nathan@quickmediasolutions.com>
|
||||||
|
|
||||||
|
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 2 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, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QHttpMultiPart>
|
||||||
|
#include <QHttpPart>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QUrlQuery>
|
||||||
|
|
||||||
|
#include "imageshackprovider.h"
|
||||||
|
#include "imageshackupload.h"
|
||||||
|
|
||||||
|
using namespace LxImage;
|
||||||
|
|
||||||
|
const QString gUploadURL = "https://api.imageshack.com/v2/images";
|
||||||
|
const QByteArray gAPIKey = "4DINORVXbcbda9ac64b424a0e6b37caed4cf3b8b";
|
||||||
|
|
||||||
|
Upload *ImageShackProvider::upload(QIODevice *device)
|
||||||
|
{
|
||||||
|
// Construct the URL that will be used for the upload
|
||||||
|
QUrlQuery query;
|
||||||
|
query.addQueryItem("api_key", gAPIKey);
|
||||||
|
QUrl url(gUploadURL);
|
||||||
|
url.setQuery(query);
|
||||||
|
|
||||||
|
// The first (and only) part is the file upload
|
||||||
|
QHttpPart filePart;
|
||||||
|
filePart.setBodyDevice(device);
|
||||||
|
filePart.setHeader(
|
||||||
|
QNetworkRequest::ContentDispositionHeader,
|
||||||
|
"form-data; name=\"file\"; filename=\"upload.jpg\""
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create the multipart and append the file part
|
||||||
|
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType, device);
|
||||||
|
multiPart->append(filePart);
|
||||||
|
|
||||||
|
// Start the request and wrap it in an ImageShackUpload
|
||||||
|
return new ImageShackUpload(sManager.post(QNetworkRequest(url), multiPart));
|
||||||
|
}
|
43
src/upload/imageshackprovider.h
Normal file
43
src/upload/imageshackprovider.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
LxImage - image viewer and screenshot tool for lxqt
|
||||||
|
Copyright (C) 2017 Nathan Osman <nathan@quickmediasolutions.com>
|
||||||
|
|
||||||
|
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 2 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, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LXIMAGE_IMAGESHACKPROVIDER_H
|
||||||
|
#define LXIMAGE_IMAGESHACKPROVIDER_H
|
||||||
|
|
||||||
|
#include "provider.h"
|
||||||
|
|
||||||
|
namespace LxImage {
|
||||||
|
|
||||||
|
class Upload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create uploads to ImageShack's API
|
||||||
|
*/
|
||||||
|
class ImageShackProvider : public Provider
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual Upload *upload(QIODevice *device);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // LXIMAGE_IMAGESHACKPROVIDER_H
|
55
src/upload/imageshackupload.cpp
Normal file
55
src/upload/imageshackupload.cpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
LxImage - image viewer and screenshot tool for lxqt
|
||||||
|
Copyright (C) 2017 Nathan Osman <nathan@quickmediasolutions.com>
|
||||||
|
|
||||||
|
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 2 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, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonValue>
|
||||||
|
|
||||||
|
#include "imageshackupload.h"
|
||||||
|
|
||||||
|
using namespace LxImage;
|
||||||
|
|
||||||
|
ImageShackUpload::ImageShackUpload(QNetworkReply *reply)
|
||||||
|
: Upload(reply)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageShackUpload::processReply(const QByteArray &data)
|
||||||
|
{
|
||||||
|
// Obtain the root object from the JSON response
|
||||||
|
QJsonObject object(QJsonDocument::fromJson(data).object());
|
||||||
|
|
||||||
|
// Attempt to retrieve the link
|
||||||
|
bool success = object.value("success").toBool();
|
||||||
|
QString link = object.value("result").toObject().value("images").toArray()
|
||||||
|
.at(0).toObject().value("direct_link").toString();
|
||||||
|
|
||||||
|
// Check for success
|
||||||
|
if (!success || link.isNull()) {
|
||||||
|
QString errorMessage = object.value("error").toObject()
|
||||||
|
.value("error_message").toString();
|
||||||
|
if (errorMessage.isNull()) {
|
||||||
|
errorMessage = tr("unknown error response");
|
||||||
|
}
|
||||||
|
Q_EMIT error(errorMessage);
|
||||||
|
} else {
|
||||||
|
Q_EMIT completed("https://" + link);
|
||||||
|
}
|
||||||
|
}
|
45
src/upload/imageshackupload.h
Normal file
45
src/upload/imageshackupload.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
LxImage - image viewer and screenshot tool for lxqt
|
||||||
|
Copyright (C) 2017 Nathan Osman <nathan@quickmediasolutions.com>
|
||||||
|
|
||||||
|
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 2 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, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LXIMAGE_IMAGESHACKUPLOAD_H
|
||||||
|
#define LXIMAGE_IMAGESHACKUPLOAD_H
|
||||||
|
|
||||||
|
#include "upload.h"
|
||||||
|
|
||||||
|
namespace LxImage {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Upload to ImageShack's API
|
||||||
|
*/
|
||||||
|
class ImageShackUpload : public Upload
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit ImageShackUpload(QNetworkReply *reply);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
virtual void processReply(const QByteArray &data);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // LXIMAGE_IMAGESHACKUPLOAD_H
|
42
src/upload/imgurprovider.cpp
Normal file
42
src/upload/imgurprovider.cpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
LxImage - image viewer and screenshot tool for lxqt
|
||||||
|
Copyright (C) 2017 Nathan Osman <nathan@quickmediasolutions.com>
|
||||||
|
|
||||||
|
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 2 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, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include "imgurprovider.h"
|
||||||
|
#include "imgurupload.h"
|
||||||
|
|
||||||
|
using namespace LxImage;
|
||||||
|
|
||||||
|
const QUrl gUploadURL("https://api.imgur.com/3/upload.json");
|
||||||
|
const QByteArray gAuthHeader = "Client-ID 63ff047cd8bcf9e";
|
||||||
|
const QByteArray gTypeHeader = "application/x-www-form-urlencoded";
|
||||||
|
|
||||||
|
Upload *ImgurProvider::upload(QIODevice *device)
|
||||||
|
{
|
||||||
|
// Create the request with the correct HTTP headers
|
||||||
|
QNetworkRequest request(gUploadURL);
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader, gTypeHeader);
|
||||||
|
request.setRawHeader("Authorization", gAuthHeader);
|
||||||
|
|
||||||
|
// Start the request and wrap it in an ImgurUpload
|
||||||
|
return new ImgurUpload(sManager.post(request, device));
|
||||||
|
}
|
41
src/upload/imgurprovider.h
Normal file
41
src/upload/imgurprovider.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
LxImage - image viewer and screenshot tool for lxqt
|
||||||
|
Copyright (C) 2017 Nathan Osman <nathan@quickmediasolutions.com>
|
||||||
|
|
||||||
|
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 2 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, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LXIMAGE_IMGURPROVIDER_H
|
||||||
|
#define LXIMAGE_IMGURPROVIDER_H
|
||||||
|
|
||||||
|
#include "provider.h"
|
||||||
|
|
||||||
|
namespace LxImage {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create uploads to Imgur's API
|
||||||
|
*/
|
||||||
|
class ImgurProvider : public Provider
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual Upload *upload(QIODevice *device);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // LXIMAGE_IMGURPROVIDER_H
|
52
src/upload/imgurupload.cpp
Normal file
52
src/upload/imgurupload.cpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
LxImage - image viewer and screenshot tool for lxqt
|
||||||
|
Copyright (C) 2017 Nathan Osman <nathan@quickmediasolutions.com>
|
||||||
|
|
||||||
|
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 2 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, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
#include "imgurupload.h"
|
||||||
|
|
||||||
|
using namespace LxImage;
|
||||||
|
|
||||||
|
ImgurUpload::ImgurUpload(QNetworkReply *reply)
|
||||||
|
: Upload(reply)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImgurUpload::processReply(const QByteArray &data)
|
||||||
|
{
|
||||||
|
// Obtain the root object from the JSON response
|
||||||
|
QJsonObject object(QJsonDocument::fromJson(data).object());
|
||||||
|
|
||||||
|
// Attempt to retrieve the value for "success" and "data->link"
|
||||||
|
bool success = object.value("success").toBool();
|
||||||
|
QJsonObject dataObject = object.value("data").toObject();
|
||||||
|
QString dataLink = dataObject.value("link").toString();
|
||||||
|
QString dataError = dataObject.value("error").toString();
|
||||||
|
|
||||||
|
// Ensure that "success" is true & link is valid, otherwise throw an error
|
||||||
|
if (!success || dataLink.isNull()) {
|
||||||
|
if (dataError.isNull()) {
|
||||||
|
dataError = tr("unknown error response");
|
||||||
|
}
|
||||||
|
Q_EMIT error(dataError);
|
||||||
|
} else {
|
||||||
|
Q_EMIT completed(dataLink);
|
||||||
|
}
|
||||||
|
}
|
43
src/upload/imgurupload.h
Normal file
43
src/upload/imgurupload.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
LxImage - image viewer and screenshot tool for lxqt
|
||||||
|
Copyright (C) 2017 Nathan Osman <nathan@quickmediasolutions.com>
|
||||||
|
|
||||||
|
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 2 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, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LXIMAGE_IMGURUPLOAD_H
|
||||||
|
#define LXIMAGE_IMGURUPLOAD_H
|
||||||
|
|
||||||
|
#include "upload.h"
|
||||||
|
|
||||||
|
namespace LxImage {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Process uploads to Imgur's API
|
||||||
|
*/
|
||||||
|
class ImgurUpload : public Upload
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit ImgurUpload(QNetworkReply *reply);
|
||||||
|
|
||||||
|
virtual void processReply(const QByteArray &data);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // LXIMAGE_IMGURUPLOAD_H
|
24
src/upload/provider.cpp
Normal file
24
src/upload/provider.cpp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
LxImage - image viewer and screenshot tool for lxqt
|
||||||
|
Copyright (C) 2017 Nathan Osman <nathan@quickmediasolutions.com>
|
||||||
|
|
||||||
|
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 2 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, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "provider.h"
|
||||||
|
|
||||||
|
using namespace LxImage;
|
||||||
|
|
||||||
|
QNetworkAccessManager Provider::sManager;
|
54
src/upload/provider.h
Normal file
54
src/upload/provider.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
LxImage - image viewer and screenshot tool for lxqt
|
||||||
|
Copyright (C) 2017 Nathan Osman <nathan@quickmediasolutions.com>
|
||||||
|
|
||||||
|
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 2 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, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LXIMAGE_PROVIDER_H
|
||||||
|
#define LXIMAGE_PROVIDER_H
|
||||||
|
|
||||||
|
#include <QIODevice>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
namespace LxImage {
|
||||||
|
|
||||||
|
class Upload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Base class for providers
|
||||||
|
*/
|
||||||
|
class Provider : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Upload the provided data
|
||||||
|
* @param device reader for uploaded data from
|
||||||
|
* @return newly created upload
|
||||||
|
*/
|
||||||
|
virtual Upload *upload(QIODevice *device) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
static QNetworkAccessManager sManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // LXIMAGE_PROVIDER_H
|
57
src/upload/upload.cpp
Normal file
57
src/upload/upload.cpp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
LxImage - image viewer and screenshot tool for lxqt
|
||||||
|
Copyright (C) 2017 Nathan Osman <nathan@quickmediasolutions.com>
|
||||||
|
|
||||||
|
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 2 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, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "upload.h"
|
||||||
|
|
||||||
|
using namespace LxImage;
|
||||||
|
|
||||||
|
Upload::Upload(QNetworkReply *reply)
|
||||||
|
: mReply(reply)
|
||||||
|
{
|
||||||
|
// Reparent the reply to this object
|
||||||
|
mReply->setParent(this);
|
||||||
|
|
||||||
|
// Emit progress() when upload progress changes
|
||||||
|
connect(mReply, &QNetworkReply::uploadProgress, [this](qint64 bytesSent, qint64 bytesTotal) {
|
||||||
|
Q_EMIT progress(static_cast<int>(
|
||||||
|
static_cast<double>(bytesSent) / static_cast<double>(bytesTotal) * 100.0
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Emit error() when a socket error occurs
|
||||||
|
connect(mReply, static_cast<void(QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error), [this](QNetworkReply::NetworkError) {
|
||||||
|
Q_EMIT error(mReply->errorString());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Process the request when it finishes
|
||||||
|
connect(mReply, &QNetworkReply::finished, [this]() {
|
||||||
|
if (mReply->error() == QNetworkReply::NoError) {
|
||||||
|
processReply(mReply->readAll());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Emit finished() when completed() or error() is emitted
|
||||||
|
connect(this, &Upload::completed, this, &Upload::finished);
|
||||||
|
connect(this, &Upload::error, this, &Upload::finished);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Upload::abort()
|
||||||
|
{
|
||||||
|
mReply->abort();
|
||||||
|
}
|
96
src/upload/upload.h
Normal file
96
src/upload/upload.h
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
LxImage - image viewer and screenshot tool for lxqt
|
||||||
|
Copyright (C) 2017 Nathan Osman <nathan@quickmediasolutions.com>
|
||||||
|
|
||||||
|
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 2 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, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LXIMAGE_UPLOAD_H
|
||||||
|
#define LXIMAGE_UPLOAD_H
|
||||||
|
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
namespace LxImage {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Base class for uploads
|
||||||
|
*/
|
||||||
|
class Upload : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create an upload
|
||||||
|
* @param reply network reply
|
||||||
|
*
|
||||||
|
* The upload will assume ownership of the network reply and connect to its
|
||||||
|
* signals, emitting uploadError() when something goes wrong.
|
||||||
|
*/
|
||||||
|
explicit Upload(QNetworkReply *reply);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Abort the upload
|
||||||
|
*/
|
||||||
|
void abort();
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Indicate that upload progress has changed
|
||||||
|
* @param value new progress value
|
||||||
|
*/
|
||||||
|
void progress(int value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Indicate that the upload completed
|
||||||
|
* @param url new URL of the upload
|
||||||
|
*/
|
||||||
|
void completed(const QString &url);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Indicate that an error occurred
|
||||||
|
* @param message description of the error
|
||||||
|
*/
|
||||||
|
void error(const QString &message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Indicate that the upload finished
|
||||||
|
*
|
||||||
|
* This signal is emitted after either completed() or error().
|
||||||
|
*/
|
||||||
|
void finished();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Process the data from the reply
|
||||||
|
* @param data content from the reply
|
||||||
|
*
|
||||||
|
* This method should parse the data and either emit the completed() or
|
||||||
|
* error() signal.
|
||||||
|
*/
|
||||||
|
virtual void processReply(const QByteArray &data) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
QNetworkReply *mReply;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // LXIMAGE_UPLOAD_H
|
141
src/upload/uploaddialog.cpp
Normal file
141
src/upload/uploaddialog.cpp
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
LxImage - image viewer and screenshot tool for lxqt
|
||||||
|
Copyright (C) 2017 Nathan Osman <nathan@quickmediasolutions.com>
|
||||||
|
|
||||||
|
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 2 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, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QProgressBar>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
#include "imageshackprovider.h"
|
||||||
|
#include "imgurprovider.h"
|
||||||
|
#include "provider.h"
|
||||||
|
#include "upload.h"
|
||||||
|
#include "uploaddialog.h"
|
||||||
|
|
||||||
|
using namespace LxImage;
|
||||||
|
|
||||||
|
ImgurProvider gImgurProvider;
|
||||||
|
ImageShackProvider gImageShackProvider;
|
||||||
|
|
||||||
|
UploadDialog::UploadDialog(QWidget *parent, const QString &filename)
|
||||||
|
: QDialog(parent),
|
||||||
|
mState(SelectProvider),
|
||||||
|
mFile(filename),
|
||||||
|
mUpload(nullptr)
|
||||||
|
{
|
||||||
|
ui.setupUi(this);
|
||||||
|
|
||||||
|
// Populate the list of providers
|
||||||
|
ui.providerComboBox->addItem(tr("Imgur"), QVariant::fromValue(&gImgurProvider));
|
||||||
|
ui.providerComboBox->addItem(tr("ImageShack"), QVariant::fromValue(&gImageShackProvider));
|
||||||
|
|
||||||
|
updateUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UploadDialog::on_actionButton_clicked()
|
||||||
|
{
|
||||||
|
switch (mState) {
|
||||||
|
case SelectProvider:
|
||||||
|
start();
|
||||||
|
break;
|
||||||
|
case UploadInProgress:
|
||||||
|
mUpload->abort();
|
||||||
|
break;
|
||||||
|
case Completed:
|
||||||
|
accept();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UploadDialog::start()
|
||||||
|
{
|
||||||
|
// Attempt to open the file
|
||||||
|
if (!mFile.open(QIODevice::ReadOnly)) {
|
||||||
|
showError(mFile.errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the selected provider
|
||||||
|
Provider *provider = ui.providerComboBox->itemData(
|
||||||
|
ui.providerComboBox->currentIndex()
|
||||||
|
).value<Provider*>();
|
||||||
|
|
||||||
|
// Create the upload
|
||||||
|
mUpload = provider->upload(&mFile);
|
||||||
|
|
||||||
|
// Update the progress bar as the upload progresses
|
||||||
|
connect(mUpload, &Upload::progress, ui.progressBar, &QProgressBar::setValue);
|
||||||
|
|
||||||
|
// If the request completes, show the link to the user
|
||||||
|
connect(mUpload, &Upload::completed, [this](const QString &url) {
|
||||||
|
ui.linkLineEdit->setText(url);
|
||||||
|
|
||||||
|
mState = Completed;
|
||||||
|
updateUi();
|
||||||
|
});
|
||||||
|
|
||||||
|
// If the request fails, show an error
|
||||||
|
connect(mUpload, &Upload::error, [this](const QString &message) {
|
||||||
|
showError(message);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Destroy the upload when it completes
|
||||||
|
connect(mUpload, &Upload::finished, [this]() {
|
||||||
|
mFile.close();
|
||||||
|
mUpload->deleteLater();
|
||||||
|
mUpload = nullptr;
|
||||||
|
});
|
||||||
|
|
||||||
|
mState = UploadInProgress;
|
||||||
|
updateUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UploadDialog::updateUi()
|
||||||
|
{
|
||||||
|
// Show the appropriate control given the current state
|
||||||
|
ui.providerComboBox->setVisible(mState == SelectProvider);
|
||||||
|
ui.progressBar->setVisible(mState == UploadInProgress);
|
||||||
|
ui.linkLineEdit->setVisible(mState == Completed);
|
||||||
|
|
||||||
|
// Reset the progress bar to zero
|
||||||
|
ui.progressBar->setValue(0);
|
||||||
|
|
||||||
|
// Set the correct button text
|
||||||
|
switch (mState) {
|
||||||
|
case SelectProvider:
|
||||||
|
ui.actionButton->setText(tr("Start"));
|
||||||
|
break;
|
||||||
|
case UploadInProgress:
|
||||||
|
ui.actionButton->setText(tr("Stop"));
|
||||||
|
break;
|
||||||
|
case Completed:
|
||||||
|
ui.actionButton->setText(tr("Close"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UploadDialog::showError(const QString &message)
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, tr("Error"), message);
|
||||||
|
|
||||||
|
mState = SelectProvider;
|
||||||
|
updateUi();
|
||||||
|
}
|
73
src/upload/uploaddialog.h
Normal file
73
src/upload/uploaddialog.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
LxImage - image viewer and screenshot tool for lxqt
|
||||||
|
Copyright (C) 2017 Nathan Osman <nathan@quickmediasolutions.com>
|
||||||
|
|
||||||
|
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 2 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, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LXIMAGE_UPLOADDIALOG_H
|
||||||
|
#define LXIMAGE_UPLOADDIALOG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
|
#include "ui_uploaddialog.h"
|
||||||
|
|
||||||
|
namespace LxImage {
|
||||||
|
|
||||||
|
class Upload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Dialog for uploading an image
|
||||||
|
*/
|
||||||
|
class UploadDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a dialog for uploading the specified file
|
||||||
|
* @param parent widget parent
|
||||||
|
* @param filename absolute path to file
|
||||||
|
*/
|
||||||
|
UploadDialog(QWidget *parent, const QString &filename);
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
|
||||||
|
void on_actionButton_clicked();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void start();
|
||||||
|
void updateUi();
|
||||||
|
void showError(const QString &message);
|
||||||
|
|
||||||
|
Ui::UploadDialog ui;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SelectProvider,
|
||||||
|
UploadInProgress,
|
||||||
|
Completed,
|
||||||
|
} mState;
|
||||||
|
|
||||||
|
QFile mFile;
|
||||||
|
|
||||||
|
Upload *mUpload;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // LXIMAGE_UPLOADDIALOG_H
|
79
src/upload/uploaddialog.ui
Normal file
79
src/upload/uploaddialog.ui
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>UploadDialog</class>
|
||||||
|
<widget class="QDialog" name="UploadDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>300</width>
|
||||||
|
<height>100</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Upload</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="providerComboBox"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QProgressBar" name="progressBar">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="linkLineEdit">
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="actionButton">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
Loading…
x
Reference in New Issue
Block a user