Cherry-picking upstream release 0.6.0.
* Switched to experimental * Bumped libfm-qt-dev to >=0.12.0 * Bumped build-tools to >= 0.4.0 * Bumped Standards to 4.1.1 * Bumped years in copyright
This commit is contained in:
parent
75af31920b
commit
b8d61bddf8
2
AUTHORS
2
AUTHORS
@ -3,7 +3,7 @@ Upstream Authors:
|
||||
Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
||||
|
||||
Copyright:
|
||||
Copyright (c) 2013-2015 LXQt team
|
||||
Copyright (c) 2013-2017 LXQt team
|
||||
|
||||
License: GPL-2+ and LGPL-2.1+
|
||||
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)
|
||||
* Create lximage-qt-screenshot_it.desktop (#83)
|
||||
* Add *da.desktop files
|
||||
|
@ -4,37 +4,35 @@ project(lximage-qt)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set(MAJOR_VERSION 0)
|
||||
set(MINOR_VERSION 5)
|
||||
set(PATCH_VERSION 1)
|
||||
set(MINOR_VERSION 6)
|
||||
set(PATCH_VERSION 0)
|
||||
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_INCLUDE_CURRENT_DIR ON)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
find_package(Qt5Network REQUIRED)
|
||||
find_package(Qt5DBus REQUIRED)
|
||||
find_package(Qt5PrintSupport REQUIRED QUIET)
|
||||
find_package(Qt5X11Extras REQUIRED QUIET)
|
||||
find_package(Qt5LinguistTools REQUIRED QUIET)
|
||||
find_package(Qt5Svg REQUIRED QUIET)
|
||||
find_package(fm-qt REQUIRED QUIET)
|
||||
find_package(Qt5PrintSupport REQUIRED)
|
||||
find_package(Qt5X11Extras REQUIRED)
|
||||
find_package(Qt5LinguistTools REQUIRED)
|
||||
find_package(Qt5Svg REQUIRED)
|
||||
find_package(fm-qt 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)
|
||||
|
||||
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.
|
||||
# for screenshot support
|
||||
find_package(X11 REQUIRED)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
# Xfixes is needed to capture the mouse cursor image
|
||||
pkg_check_modules(XFIXES REQUIRED xfixes)
|
||||
|
||||
|
11
debian/changelog
vendored
11
debian/changelog
vendored
@ -1,3 +1,14 @@
|
||||
lximage-qt (0.6.0-1) experimental; urgency=medium
|
||||
|
||||
* Cherry-picking upstream release 0.6.0.
|
||||
* Switched to experimental
|
||||
* Bumped libfm-qt-dev to >=0.12.0
|
||||
* Bumped build-tools to >= 0.4.0
|
||||
* Bumped Standards to 4.1.1
|
||||
* Bumped years in copyright
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Tue, 24 Oct 2017 23:03:31 +0200
|
||||
|
||||
lximage-qt (0.5.1-1) unstable; urgency=medium
|
||||
|
||||
* Cherry-picking upstream release 0.5.1.
|
||||
|
10
debian/control
vendored
10
debian/control
vendored
@ -9,17 +9,17 @@ Build-Depends: debhelper (>= 10),
|
||||
libexif-dev,
|
||||
libglib2.0-dev,
|
||||
libfm-dev,
|
||||
libfm-qt-dev (>= 0.11.2),
|
||||
libfm-qt-dev (>= 0.12.0),
|
||||
libkf5windowsystem-dev,
|
||||
libmenu-cache-dev,
|
||||
libqt5svg5-dev,
|
||||
libqt5x11extras5-dev,
|
||||
libx11-dev,
|
||||
libxfixes-dev,
|
||||
lxqt-build-tools (>= 0.3.0)
|
||||
Standards-Version: 3.9.8
|
||||
Vcs-Browser: https://anonscm.debian.org/cgit/pkg-lxqt/lximage-qt.git/?h=debian/sid
|
||||
Vcs-Git: https://anonscm.debian.org/git/pkg-lxqt/lximage-qt.git -b debian/sid
|
||||
lxqt-build-tools (>= 0.4.0),
|
||||
Standards-Version: 4.1.1
|
||||
Vcs-Browser: https://anonscm.debian.org/cgit/pkg-lxqt/lximage-qt.git/?h=debian/experimental
|
||||
Vcs-Git: https://anonscm.debian.org/git/pkg-lxqt/lximage-qt.git -b debian/experimental
|
||||
Homepage: https://github.com/lxde/lximage-qt
|
||||
|
||||
Package: lximage-qt
|
||||
|
13
debian/copyright
vendored
13
debian/copyright
vendored
@ -4,7 +4,7 @@ Source: https://github.com/lxde/lximage-qt
|
||||
|
||||
Files: *
|
||||
Copyright: 2013-2016 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
||||
2013-2016 LXQt team
|
||||
2013-2017 LXQt team
|
||||
License: GPL-2+ and LGPL-2.1+
|
||||
|
||||
Files: src/application.*
|
||||
@ -17,17 +17,8 @@ Files: src/application.*
|
||||
Copyright: 2013-2016 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
||||
License: GPL-2+
|
||||
|
||||
Files: src/imageview.*
|
||||
Copyright: Copyright (C) 2013
|
||||
License: GPL-2+
|
||||
|
||||
Files: src/job.*
|
||||
src/screenshotdialog.*
|
||||
Copyright: Copyright (C) 2013 - 2014
|
||||
License: LGPL-2.1+
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2014-2016 Alf Gaida <agaida@siduction.org>
|
||||
Copyright: 2014-2017 Alf Gaida <agaida@siduction.org>
|
||||
2015 Andrew Lee (李健秋) <ajqlee@debian.org>
|
||||
2015 ChangZhuo Chen (陳昌倬) <czchen@debian.org>
|
||||
License: LGPL-2.1+
|
||||
|
@ -16,11 +16,21 @@ set(lximage-qt_SRCS
|
||||
application.cpp
|
||||
imageview.cpp
|
||||
modelfilter.cpp
|
||||
job.cpp
|
||||
loadimagejob.cpp
|
||||
saveimagejob.cpp
|
||||
screenshotdialog.cpp
|
||||
screenshotselectarea.cpp
|
||||
screenshotselectareagraphicsview.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
|
||||
@ -35,6 +45,8 @@ set(lximage-qt_UIS
|
||||
mainwindow.ui
|
||||
preferencesdialog.ui
|
||||
screenshotdialog.ui
|
||||
|
||||
upload/uploaddialog.ui
|
||||
)
|
||||
qt5_wrap_ui(lximage-qt_UI_H ${lximage-qt_UIS})
|
||||
|
||||
@ -64,7 +76,7 @@ include(LXQtTranslateDesktop)
|
||||
|
||||
file(GLOB desktop_files_in ../data/*.desktop.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
|
||||
${lximage-qt_SRCS}
|
||||
@ -74,11 +86,11 @@ add_executable(lximage-qt
|
||||
)
|
||||
|
||||
add_definitions(
|
||||
-DLXIMAGE_DATA_DIR="${CMAKE_INSTALL_PREFIX}/share/lximage-qt"
|
||||
-DLXIMAGE_DATA_DIR="${CMAKE_INSTALL_FULL_DATAROOTDIR}/lximage-qt"
|
||||
-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
|
||||
fm-qt
|
||||
@ -88,4 +100,4 @@ target_link_libraries(lximage-qt
|
||||
${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) {
|
||||
Q_UNUSED(argc)
|
||||
Q_UNUSED(argv)
|
||||
|
||||
setAttribute(Qt::AA_UseHighDpiPixmaps, true);
|
||||
|
||||
// install the translations built-into Qt itself
|
||||
qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
|
||||
installTranslator(&qtTranslator);
|
||||
|
@ -18,45 +18,31 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LXIMAGE_JOB_H
|
||||
#define LXIMAGE_JOB_H
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include "graphicsscene.h"
|
||||
#include <QMimeData>
|
||||
#include <QUrl>
|
||||
|
||||
namespace LxImage {
|
||||
|
||||
class Job {
|
||||
public:
|
||||
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);
|
||||
};
|
||||
|
||||
GraphicsScene::GraphicsScene(QObject *parent):
|
||||
QGraphicsScene (parent) {
|
||||
}
|
||||
|
||||
#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):
|
||||
QGraphicsView(parent),
|
||||
scene_(new QGraphicsScene(this)),
|
||||
scene_(new GraphicsScene(this)),
|
||||
imageItem_(new QGraphicsRectItem()),
|
||||
gifMovie_(nullptr),
|
||||
cacheTimer_(nullptr),
|
||||
@ -49,6 +49,7 @@ ImageView::ImageView(QWidget* parent):
|
||||
setLineWidth(0);
|
||||
|
||||
setScene(scene_);
|
||||
connect(scene_, &GraphicsScene::fileDropped, this, &ImageView::onFileDropped);
|
||||
imageItem_->hide();
|
||||
imageItem_->setPen(QPen(Qt::NoPen)); // remove the border
|
||||
scene_->addItem(imageItem_);
|
||||
@ -68,6 +69,9 @@ ImageView::~ImageView() {
|
||||
}
|
||||
}
|
||||
|
||||
void ImageView::onFileDropped(const QString file) {
|
||||
Q_EMIT fileDropped(file);
|
||||
}
|
||||
|
||||
void ImageView::wheelEvent(QWheelEvent* event) {
|
||||
int delta = event->delta();
|
||||
|
@ -21,6 +21,7 @@
|
||||
#ifndef LXIMAGE_IMAGEVIEW_H
|
||||
#define LXIMAGE_IMAGEVIEW_H
|
||||
|
||||
#include "graphicsscene.h"
|
||||
#include <QGraphicsView>
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsRectItem>
|
||||
@ -71,6 +72,9 @@ public:
|
||||
// if set to true, hides the cursor after 3s of inactivity
|
||||
void hideCursor(bool enable);
|
||||
|
||||
Q_SIGNALS:
|
||||
void fileDropped(const QString file);
|
||||
|
||||
protected:
|
||||
virtual void wheelEvent(QWheelEvent* event);
|
||||
virtual void mouseDoubleClickEvent(QMouseEvent* event);
|
||||
@ -87,11 +91,12 @@ private:
|
||||
QRect sceneToViewport(const QRectF& rect);
|
||||
|
||||
private Q_SLOTS:
|
||||
void onFileDropped(const QString file);
|
||||
void generateCache();
|
||||
void blankCursor();
|
||||
|
||||
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
|
||||
QImage image_; // image to show
|
||||
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,104 +21,106 @@
|
||||
#include "loadimagejob.h"
|
||||
#include "mainwindow.h"
|
||||
#include <QImageReader>
|
||||
#include <QBuffer>
|
||||
#include <QByteArray>
|
||||
#include <qvarlengtharray.h>
|
||||
#include <libexif/exif-loader.h>
|
||||
|
||||
using namespace LxImage;
|
||||
|
||||
LoadImageJob::LoadImageJob(MainWindow* window, FmPath* filePath):
|
||||
Job(),
|
||||
mainWindow_(window),
|
||||
path_(fm_path_ref(filePath)) {
|
||||
LoadImageJob::LoadImageJob(const Fm::FilePath & filePath):
|
||||
path_{filePath} {
|
||||
}
|
||||
|
||||
LoadImageJob::~LoadImageJob() {
|
||||
fm_path_unref(path_);
|
||||
}
|
||||
|
||||
// This is called from the worker thread, not main thread
|
||||
bool LoadImageJob::run() {
|
||||
GFile* gfile = fm_path_to_gfile(path_);
|
||||
GFileInputStream* fileStream = g_file_read(gfile, cancellable_, &error_);
|
||||
g_object_unref(gfile);
|
||||
void LoadImageJob::exec() {
|
||||
GFileInputStream* fileStream = nullptr;
|
||||
Fm::GErrorPtr error;
|
||||
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
|
||||
QBuffer imageBuffer;
|
||||
// the file stream is successfully opened
|
||||
imageBuffer.truncate(0);
|
||||
GInputStream* inputStream = G_INPUT_STREAM(fileStream);
|
||||
while(!g_cancellable_is_cancelled(cancellable_)) {
|
||||
while(!error && !isCancelled()) {
|
||||
char buffer[4096];
|
||||
error.reset();
|
||||
gssize readSize = g_input_stream_read(inputStream,
|
||||
buffer, 4096,
|
||||
cancellable_, &error_);
|
||||
cancellable().get(), &error);
|
||||
if(readSize == -1 || readSize == 0) // error or EOF
|
||||
break;
|
||||
// append the bytes read to the image buffer
|
||||
imageBuffer.buffer().append(buffer, readSize);
|
||||
imageBuffer.append(buffer, readSize);
|
||||
}
|
||||
g_input_stream_close(inputStream, NULL, NULL);
|
||||
|
||||
// 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
|
||||
image_ = QImage::fromData(imageBuffer.buffer());
|
||||
if (!error)
|
||||
break; // everything read or cancel requested
|
||||
|
||||
if(!image_.isNull()) { // if the image is loaded correctly
|
||||
// check if this file is a jpeg file
|
||||
// FIXME: can we use FmFileInfo instead if it's available?
|
||||
const char* basename = fm_path_get_basename(path_);
|
||||
char* mime_type = g_content_type_guess(basename, NULL, 0, NULL);
|
||||
if(mime_type && strcmp(mime_type, "image/jpeg") == 0) { // this is a jpeg file
|
||||
// use libexif to extract additional info embedded in jpeg files
|
||||
ExifLoader *exif_loader = exif_loader_new();
|
||||
// write image data to exif loader
|
||||
exif_loader_write(exif_loader, (unsigned char*)imageBuffer.data().constData(), (unsigned int)imageBuffer.size());
|
||||
ExifData *exif_data = exif_loader_get_data(exif_loader);
|
||||
exif_loader_unref(exif_loader);
|
||||
if(exif_data) {
|
||||
/* reference for EXIF orientation tag:
|
||||
* http://www.impulseadventure.com/photo/exif-orientation.html */
|
||||
ExifEntry* orient_ent = exif_data_get_entry(exif_data, EXIF_TAG_ORIENTATION);
|
||||
if(orient_ent) { /* orientation flag found in EXIF */
|
||||
gushort orient;
|
||||
ExifByteOrder bo = exif_data_get_byte_order(exif_data);
|
||||
/* bo == EXIF_BYTE_ORDER_INTEL ; */
|
||||
orient = exif_get_short (orient_ent->data, bo);
|
||||
qreal rotate_degrees = 0.0;
|
||||
switch(orient) {
|
||||
case 1: /* no rotation */
|
||||
break;
|
||||
case 8:
|
||||
rotate_degrees = 270.0;
|
||||
break;
|
||||
case 3:
|
||||
rotate_degrees = 180.0;
|
||||
break;
|
||||
case 6:
|
||||
rotate_degrees = 90.0;
|
||||
break;
|
||||
}
|
||||
// rotate the image according to EXIF orientation tag
|
||||
if(rotate_degrees != 0.0) {
|
||||
QTransform transform;
|
||||
transform.rotate(rotate_degrees);
|
||||
image_ = image_.transformed(transform, Qt::SmoothTransformation);
|
||||
}
|
||||
// TODO: handle other EXIF tags as well
|
||||
act = emitError(error);
|
||||
}
|
||||
|
||||
// FIXME: maybe it's a better idea to implement a GInputStream based QIODevice.
|
||||
if(!error && !isCancelled()) { // load the image from buffer if there are no errors
|
||||
image_ = QImage::fromData(imageBuffer);
|
||||
|
||||
if(!image_.isNull()) { // if the image is loaded correctly
|
||||
// check if this file is a jpeg file
|
||||
// FIXME: can we use FmFileInfo instead if it's available?
|
||||
const Fm::CStrPtr basename = path_.baseName();
|
||||
const Fm::CStrPtr mime_type{g_content_type_guess(basename.get(), NULL, 0, NULL)};
|
||||
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
|
||||
std::unique_ptr<ExifLoader, decltype (&exif_loader_unref)> exif_loader{exif_loader_new(), &exif_loader_unref};
|
||||
// write image data to exif loader
|
||||
exif_loader_write(exif_loader.get(), reinterpret_cast<unsigned char*>(const_cast<char *>(imageBuffer.constData())), static_cast<unsigned int>(imageBuffer.size()));
|
||||
std::unique_ptr<ExifData, decltype (&exif_data_unref)> exif_data{exif_loader_get_data(exif_loader.get()), &exif_data_unref};
|
||||
exif_loader.reset();
|
||||
if (exif_data) {
|
||||
/* reference for EXIF orientation tag:
|
||||
* http://www.impulseadventure.com/photo/exif-orientation.html */
|
||||
ExifEntry* orient_ent = exif_data_get_entry(exif_data.get(), EXIF_TAG_ORIENTATION);
|
||||
if(orient_ent) { /* orientation flag found in EXIF */
|
||||
gushort orient;
|
||||
ExifByteOrder bo = exif_data_get_byte_order(exif_data.get());
|
||||
/* bo == EXIF_BYTE_ORDER_INTEL ; */
|
||||
orient = exif_get_short (orient_ent->data, bo);
|
||||
qreal rotate_degrees = 0.0;
|
||||
switch(orient) {
|
||||
case 1: /* no rotation */
|
||||
break;
|
||||
case 8:
|
||||
rotate_degrees = 270.0;
|
||||
break;
|
||||
case 3:
|
||||
rotate_degrees = 180.0;
|
||||
break;
|
||||
case 6:
|
||||
rotate_degrees = 90.0;
|
||||
break;
|
||||
}
|
||||
exif_data_unref(exif_data);
|
||||
// rotate the image according to EXIF orientation tag
|
||||
if(rotate_degrees != 0.0) {
|
||||
QTransform transform;
|
||||
transform.rotate(rotate_degrees);
|
||||
image_ = image_.transformed(transform, Qt::SmoothTransformation);
|
||||
}
|
||||
// TODO: handle other EXIF tags as well
|
||||
}
|
||||
}
|
||||
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
|
||||
#define LXIMAGE_LOADIMAGEJOB_H
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <libfm/fm.h>
|
||||
#include <libfm-qt/core/filepath.h>
|
||||
#include <QImage>
|
||||
#include "job.h"
|
||||
#include <libfm-qt/core/job.h>
|
||||
|
||||
namespace LxImage {
|
||||
|
||||
class MainWindow;
|
||||
|
||||
class LoadImageJob : public Job {
|
||||
class LoadImageJob : public Fm::Job {
|
||||
|
||||
public:
|
||||
LoadImageJob(MainWindow* window, FmPath* filePath);
|
||||
LoadImageJob(const Fm::FilePath & filePath);
|
||||
|
||||
QImage image() const {
|
||||
return image_;
|
||||
}
|
||||
|
||||
FmPath* filePath() const {
|
||||
const Fm::FilePath & filePath() const {
|
||||
return path_;
|
||||
}
|
||||
|
||||
private:
|
||||
~LoadImageJob(); // prevent direct deletion
|
||||
|
||||
virtual bool run();
|
||||
virtual void finish();
|
||||
virtual void exec() override;
|
||||
|
||||
public:
|
||||
MainWindow* mainWindow_;
|
||||
FmPath* path_;
|
||||
const Fm::FilePath path_;
|
||||
QImage image_;
|
||||
};
|
||||
|
||||
|
@ -43,6 +43,9 @@
|
||||
#include <libfm-qt/folderview.h>
|
||||
#include <libfm-qt/filepropsdialog.h>
|
||||
#include <libfm-qt/fileoperation.h>
|
||||
#include <libfm-qt/folderitemdelegate.h>
|
||||
|
||||
#include "upload/uploaddialog.h"
|
||||
|
||||
using namespace LxImage;
|
||||
|
||||
@ -51,11 +54,9 @@ MainWindow::MainWindow():
|
||||
contextMenu_(new QMenu(this)),
|
||||
slideShowTimer_(nullptr),
|
||||
image_(),
|
||||
currentFile_(nullptr),
|
||||
// currentFileInfo_(nullptr),
|
||||
imageModified_(false),
|
||||
folder_(nullptr),
|
||||
folderPath_(nullptr),
|
||||
folderModel_(new Fm::FolderModel()),
|
||||
proxyModel_(new Fm::ProxyFolderModel()),
|
||||
modelFilter_(new ModelFilter()),
|
||||
@ -83,6 +84,8 @@ MainWindow::MainWindow():
|
||||
ui.view->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui.view, &QWidget::customContextMenuRequested, this, &MainWindow::onContextMenu);
|
||||
|
||||
connect(ui.view, &ImageView::fileDropped, this, &MainWindow::onFileDropped);
|
||||
|
||||
// install an event filter on the image view
|
||||
ui.view->installEventFilter(this);
|
||||
ui.view->setBackgroundBrush(QBrush(settings.bgColor()));
|
||||
@ -128,16 +131,8 @@ MainWindow::~MainWindow() {
|
||||
loadJob_->cancel();
|
||||
// we don't need to do delete here. It will be done automatically
|
||||
}
|
||||
if(currentFile_)
|
||||
fm_path_unref(currentFile_);
|
||||
//if(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 proxyModel_;
|
||||
delete modelFilter_;
|
||||
@ -175,7 +170,7 @@ void MainWindow::on_actionZoomOut_triggered() {
|
||||
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
|
||||
// since the folder is fully loaded.
|
||||
if(currentFile_ && !currentIndex_.isValid()) {
|
||||
@ -187,22 +182,20 @@ void MainWindow::onFolderLoaded(FmFolder* folder) {
|
||||
}
|
||||
}
|
||||
// this is used to open the first image of a folder
|
||||
else if (currentFile_ == nullptr)
|
||||
else if (!currentFile_)
|
||||
on_actionFirst_triggered();
|
||||
}
|
||||
|
||||
void MainWindow::openImageFile(QString fileName) {
|
||||
FmPath* path = fm_path_new_for_str(qPrintable(fileName));
|
||||
if(currentFile_ && fm_path_equal(currentFile_, path)) {
|
||||
const Fm::FilePath path = Fm::FilePath::fromPathStr(qPrintable(fileName));
|
||||
// the same file! do not load it again
|
||||
fm_path_unref(path);
|
||||
if(currentFile_ && currentFile_ == path)
|
||||
return;
|
||||
}
|
||||
|
||||
if (QFileInfo(fileName).isDir()) {
|
||||
if(fm_path_equal(path, folderPath_)) {
|
||||
fm_path_unref(path);
|
||||
if(path == folderPath_)
|
||||
return;
|
||||
}
|
||||
|
||||
QList<QByteArray> formats = QImageReader::supportedImageFormats();
|
||||
QStringList formatsFilters;
|
||||
for (const QByteArray& format: formats)
|
||||
@ -210,20 +203,16 @@ void MainWindow::openImageFile(QString fileName) {
|
||||
QDir dir(fileName);
|
||||
dir.setNameFilters(formatsFilters);
|
||||
dir.setFilter(QDir::Files | QDir::NoDotAndDotDot);
|
||||
if(dir.entryList().isEmpty()) {
|
||||
fm_path_unref(path);
|
||||
if(dir.entryList().isEmpty())
|
||||
return;
|
||||
}
|
||||
if(currentFile_)
|
||||
fm_path_unref(currentFile_);
|
||||
currentFile_ = nullptr;
|
||||
|
||||
currentFile_ = Fm::FilePath{};
|
||||
loadFolder(path);
|
||||
} else {
|
||||
// load the image file asynchronously
|
||||
loadImage(path);
|
||||
loadFolder(fm_path_get_parent(path));
|
||||
loadFolder(path.parent());
|
||||
}
|
||||
fm_path_unref(path);
|
||||
}
|
||||
|
||||
// paste the specified image into the current view,
|
||||
@ -238,9 +227,7 @@ void MainWindow::pasteImage(QImage newImage) {
|
||||
setModified(true);
|
||||
|
||||
currentIndex_ = QModelIndex(); // invaludate current index since we don't have a folder model now
|
||||
if(currentFile_)
|
||||
fm_path_unref(currentFile_);
|
||||
currentFile_ = nullptr;
|
||||
currentFile_ = Fm::FilePath{};
|
||||
|
||||
image_ = newImage;
|
||||
ui.view->setImage(image_);
|
||||
@ -342,44 +329,36 @@ void MainWindow::on_actionSaveAs_triggered() {
|
||||
if(saveJob_) // if we're currently saving another file
|
||||
return;
|
||||
QString baseName;
|
||||
if(currentFile_) {
|
||||
char* dispName = fm_path_display_name(currentFile_, false);
|
||||
baseName = QString::fromUtf8(dispName);
|
||||
g_free(dispName);
|
||||
}
|
||||
if(currentFile_)
|
||||
baseName = QString::fromUtf8(currentFile_.displayName().get());
|
||||
|
||||
QString fileName = saveFileName(baseName);
|
||||
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
|
||||
saveImage(path);
|
||||
|
||||
if(!currentFile_) { // if we haven't load any file yet
|
||||
currentFile_ = path;
|
||||
loadFolder(fm_path_get_parent(path));
|
||||
loadFolder(path.parent());
|
||||
}
|
||||
else
|
||||
fm_path_unref(path);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_actionDelete_triggered() {
|
||||
// delete the current file
|
||||
if(currentFile_) {
|
||||
FmPathList* paths = fm_path_list_new();
|
||||
fm_path_list_push_tail(paths, currentFile_);
|
||||
Fm::FileOperation::deleteFiles(paths);
|
||||
fm_path_list_unref(paths);
|
||||
}
|
||||
if(currentFile_)
|
||||
Fm::FileOperation::deleteFiles({currentFile_});
|
||||
}
|
||||
|
||||
void MainWindow::on_actionFileProperties_triggered() {
|
||||
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
|
||||
// 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.
|
||||
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);
|
||||
else
|
||||
index = proxyModel_->index(0, 0);
|
||||
FmFileInfo* info = proxyModel_->fileInfoFromIndex(index);
|
||||
if(info) {
|
||||
FmPath* path = fm_file_info_get_path(info);
|
||||
loadImage(path, index);
|
||||
}
|
||||
const auto info = proxyModel_->fileInfoFromIndex(index);
|
||||
if(info)
|
||||
loadImage(info->path(), index);
|
||||
}
|
||||
}
|
||||
|
||||
@ -413,79 +390,72 @@ void MainWindow::on_actionPrevious_triggered() {
|
||||
index = proxyModel_->index(currentIndex_.row() - 1, 0);
|
||||
else
|
||||
index = proxyModel_->index(proxyModel_->rowCount() - 1, 0);
|
||||
FmFileInfo* info = proxyModel_->fileInfoFromIndex(index);
|
||||
if(info) {
|
||||
FmPath* path = fm_file_info_get_path(info);
|
||||
loadImage(path, index);
|
||||
}
|
||||
const auto info = proxyModel_->fileInfoFromIndex(index);
|
||||
if(info)
|
||||
loadImage(info->path(), index);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_actionFirst_triggered() {
|
||||
QModelIndex index = proxyModel_->index(0, 0);
|
||||
if(index.isValid()) {
|
||||
FmFileInfo* info = proxyModel_->fileInfoFromIndex(index);
|
||||
if(info) {
|
||||
FmPath* path = fm_file_info_get_path(info);
|
||||
loadImage(path, index);
|
||||
}
|
||||
const auto info = proxyModel_->fileInfoFromIndex(index);
|
||||
if(info)
|
||||
loadImage(info->path(), index);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_actionLast_triggered() {
|
||||
QModelIndex index = proxyModel_->index(proxyModel_->rowCount() - 1, 0);
|
||||
if(index.isValid()) {
|
||||
FmFileInfo* info = proxyModel_->fileInfoFromIndex(index);
|
||||
if(info) {
|
||||
FmPath* path = fm_file_info_get_path(info);;
|
||||
loadImage(path, index);
|
||||
}
|
||||
const auto info = proxyModel_->fileInfoFromIndex(index);
|
||||
if(info)
|
||||
loadImage(info->path(), index);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::loadFolder(FmPath* newFolderPath) {
|
||||
void MainWindow::loadFolder(const Fm::FilePath & newFolderPath) {
|
||||
if(folder_) { // an folder is already loaded
|
||||
if(fm_path_equal(newFolderPath, folderPath_)) // same folder, ignore
|
||||
if(newFolderPath == folderPath_) // same folder, ignore
|
||||
return;
|
||||
// free current folder
|
||||
g_signal_handlers_disconnect_by_func(folder_, gpointer(_onFolderLoaded), this);
|
||||
g_object_unref(folder_);
|
||||
fm_path_unref(folderPath_);
|
||||
disconnect(folder_.get(), nullptr, this, nullptr); // disconnect from all signals
|
||||
}
|
||||
|
||||
folderPath_ = fm_path_ref(newFolderPath);
|
||||
folder_ = fm_folder_from_path(newFolderPath);
|
||||
g_signal_connect(folder_, "finish-loading", G_CALLBACK(_onFolderLoaded), this);
|
||||
folderPath_ = newFolderPath;
|
||||
folder_ = Fm::Folder::fromPath(folderPath_);
|
||||
connect(folder_.get(), &Fm::Folder::finishLoading, this, &MainWindow::onFolderLoaded);
|
||||
|
||||
folderModel_->setFolder(folder_);
|
||||
currentIndex_ = QModelIndex(); // set current index to invalid
|
||||
}
|
||||
|
||||
// the image is loaded (the method is only called if the loading is not cancelled)
|
||||
void MainWindow::onImageLoaded(LoadImageJob* job) {
|
||||
loadJob_ = nullptr; // the job object will be freed later automatically
|
||||
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();
|
||||
|
||||
image_ = job->image();
|
||||
ui.view->setAutoZoomFit(true);
|
||||
ui.view->setImage(image_);
|
||||
loadJob_ = nullptr; // the job object will be freed later automatically
|
||||
|
||||
if(!currentIndex_.isValid())
|
||||
currentIndex_ = indexFromPath(currentFile_);
|
||||
ui.view->setAutoZoomFit(true);
|
||||
ui.view->setImage(image_);
|
||||
|
||||
updateUI();
|
||||
if(!currentIndex_.isValid())
|
||||
currentIndex_ = indexFromPath(currentFile_);
|
||||
|
||||
if(job->error()) {
|
||||
// if there are errors
|
||||
// TODO: show a info bar?
|
||||
updateUI();
|
||||
|
||||
/* we resized and moved the window without showing
|
||||
it in updateUI(), so we need to show it here */
|
||||
show();
|
||||
}
|
||||
|
||||
/* we resized and moved the window without showing
|
||||
it in updateUI(), so we need to show it here */
|
||||
show();
|
||||
}
|
||||
|
||||
void MainWindow::onImageSaved(SaveImageJob* job) {
|
||||
if(!job->error()) {
|
||||
void MainWindow::onImageSaved() {
|
||||
if(!saveJob_->failed()) {
|
||||
setModified(false);
|
||||
}
|
||||
saveJob_ = nullptr;
|
||||
@ -535,16 +505,16 @@ bool MainWindow::eventFilter(QObject* watched, QEvent* 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
|
||||
// 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;
|
||||
int count = proxyModel_->rowCount();
|
||||
for(int row = 0; row < count; ++row) {
|
||||
index = proxyModel_->index(row, 0);
|
||||
FmFileInfo* info = proxyModel_->fileInfoFromIndex(index);
|
||||
if(info && fm_path_equal(filePath, fm_file_info_get_path(info))) {
|
||||
const auto info = proxyModel_->fileInfoFromIndex(index);
|
||||
if(info && filePath == info->path()) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
@ -564,19 +534,19 @@ void MainWindow::updateUI() {
|
||||
|
||||
QString title;
|
||||
if(currentFile_) {
|
||||
char* dispName = fm_path_display_basename(currentFile_);
|
||||
const Fm::CStrPtr dispName = currentFile_.displayName();
|
||||
if(loadJob_) { // if loading is in progress
|
||||
title = tr("[*]%1 (Loading...) - Image Viewer")
|
||||
.arg(QString::fromUtf8(dispName));
|
||||
.arg(QString::fromUtf8(dispName.get()));
|
||||
}
|
||||
else {
|
||||
if(image_.isNull()) {
|
||||
title = tr("[*]%1 (Failed to Load) - Image Viewer")
|
||||
.arg(QString::fromUtf8(dispName));
|
||||
.arg(QString::fromUtf8(dispName.get()));
|
||||
}
|
||||
else {
|
||||
title = tr("[*]%1 (%2x%3) - Image Viewer")
|
||||
.arg(QString::fromUtf8(dispName))
|
||||
.arg(QString::fromUtf8(dispName.get()))
|
||||
.arg(image_.width())
|
||||
.arg(image_.height());
|
||||
/* 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
|
||||
}
|
||||
else {
|
||||
@ -620,7 +589,7 @@ void MainWindow::updateUI() {
|
||||
|
||||
// Load the specified image file asynchronously in a worker thread.
|
||||
// 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
|
||||
if(loadJob_) {
|
||||
loadJob_->cancel(); // the job object will be freed automatically later
|
||||
@ -633,14 +602,12 @@ void MainWindow::loadImage(FmPath* filePath, QModelIndex index) {
|
||||
}
|
||||
|
||||
currentIndex_ = index;
|
||||
if(currentFile_)
|
||||
fm_path_unref(currentFile_);
|
||||
currentFile_ = fm_path_ref(filePath);
|
||||
currentFile_ = filePath;
|
||||
// clear current image, but do not update the view now to prevent flickers
|
||||
image_ = QImage();
|
||||
|
||||
const char* basename = fm_path_get_basename(currentFile_);
|
||||
char* mime_type = g_content_type_guess(basename, NULL, 0, NULL);
|
||||
const Fm::CStrPtr basename = currentFile_.baseName();
|
||||
char* mime_type = g_content_type_guess(basename.get(), NULL, 0, NULL);
|
||||
QString mimeType;
|
||||
if (mime_type) {
|
||||
mimeType = QString(mime_type);
|
||||
@ -648,33 +615,47 @@ void MainWindow::loadImage(FmPath* filePath, QModelIndex index) {
|
||||
}
|
||||
if(mimeType == "image/gif"
|
||||
|| mimeType == "image/svg+xml" || mimeType == "image/svg+xml-compressed") {
|
||||
char *file_name = fm_path_to_str(currentFile_);
|
||||
QString fileName(file_name);
|
||||
g_free(file_name);
|
||||
const Fm::CStrPtr file_name = currentFile_.toString();
|
||||
ui.view->setAutoZoomFit(true); // like in onImageLoaded()
|
||||
if(mimeType == "image/gif")
|
||||
ui.view->setGifAnimation(fileName);
|
||||
ui.view->setGifAnimation(QString{file_name.get()});
|
||||
else
|
||||
ui.view->setSVG(fileName);
|
||||
ui.view->setSVG(QString{file_name.get()});
|
||||
image_ = ui.view->image();
|
||||
updateUI();
|
||||
show();
|
||||
}
|
||||
else {
|
||||
// start a new gio job to load the specified image
|
||||
loadJob_ = new LoadImageJob(this, filePath);
|
||||
loadJob_->start();
|
||||
loadJob_ = new LoadImageJob(currentFile_);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
return;
|
||||
// start a new gio job to save current image to the specified path
|
||||
saveJob_ = new SaveImageJob(this, filePath);
|
||||
saveJob_->start();
|
||||
saveJob_ = new SaveImageJob(image_, filePath);
|
||||
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?
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
bool hasQGraphicsItem(false);
|
||||
if(QGraphicsItem *graphItem = getGraphicsItem()) {
|
||||
@ -871,8 +859,10 @@ void MainWindow::setShowThumbnails(bool show) {
|
||||
listView->installEventFilter(this);
|
||||
// FIXME: optimize the size of the thumbnail view
|
||||
// FIXME if the thumbnail view is docked elsewhere, update the settings.
|
||||
int scrollHeight = style()->pixelMetric(QStyle::PM_ScrollBarExtent);
|
||||
thumbnailsView_->setFixedHeight(listView->gridSize().height() + scrollHeight);
|
||||
if(Fm::FolderItemDelegate* delegate = static_cast<Fm::FolderItemDelegate*>(listView->itemDelegateForColumn(Fm::FolderModel::ColumnFileName))) {
|
||||
int scrollHeight = style()->pixelMetric(QStyle::PM_ScrollBarExtent);
|
||||
thumbnailsView_->setFixedHeight(delegate->itemSize().height() + scrollHeight);
|
||||
}
|
||||
thumbnailsView_->setModel(proxyModel_);
|
||||
proxyModel_->setShowThumbnails(true);
|
||||
if (currentFile_) { // select the loaded image
|
||||
@ -978,14 +968,18 @@ void MainWindow::onExitFullscreen() {
|
||||
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
|
||||
if(!selected.isEmpty()) {
|
||||
QModelIndex index = selected.first().topLeft();
|
||||
if(index.isValid() && index != currentIndex_) {
|
||||
FmFileInfo* file = proxyModel_->fileInfoFromIndex(index);
|
||||
const auto file = proxyModel_->fileInfoFromIndex(index);
|
||||
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);
|
||||
|
||||
FmPath* currentFile() const {
|
||||
const Fm::FilePath & currentFile() const {
|
||||
return currentFile_;
|
||||
}
|
||||
|
||||
@ -70,9 +70,9 @@ public:
|
||||
void applySettings();
|
||||
|
||||
protected:
|
||||
void loadImage(FmPath* filePath, QModelIndex index = QModelIndex());
|
||||
void saveImage(FmPath* filePath); // save current image to a file
|
||||
void loadFolder(FmPath* newFolderPath);
|
||||
void loadImage(const Fm::FilePath & filePath, QModelIndex index = QModelIndex());
|
||||
void saveImage(const Fm::FilePath & filePath); // save current image to a file
|
||||
void loadFolder(const Fm::FilePath & newFolderPath);
|
||||
QString openFileName();
|
||||
QString openDirectory();
|
||||
QString saveFileName(QString defaultName = QString());
|
||||
@ -80,8 +80,8 @@ protected:
|
||||
virtual void resizeEvent(QResizeEvent *event);
|
||||
virtual void closeEvent(QCloseEvent *event);
|
||||
|
||||
void onImageLoaded(LoadImageJob* job);
|
||||
void onImageSaved(SaveImageJob* job);
|
||||
void onImageLoaded();
|
||||
void onImageSaved();
|
||||
|
||||
virtual bool eventFilter(QObject* watched, QEvent* event);
|
||||
private Q_SLOTS:
|
||||
@ -103,6 +103,7 @@ private Q_SLOTS:
|
||||
void on_actionFlipHorizontal_triggered();
|
||||
void on_actionCopy_triggered();
|
||||
void on_actionPaste_triggered();
|
||||
void on_actionUpload_triggered();
|
||||
|
||||
void on_actionShowThumbnails_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 onFileDropped(const QString path);
|
||||
|
||||
private:
|
||||
void onFolderLoaded(FmFolder* folder);
|
||||
void onFolderLoaded();
|
||||
void updateUI();
|
||||
void setModified(bool modified);
|
||||
QModelIndex indexFromPath(FmPath* filePath);
|
||||
QModelIndex indexFromPath(const Fm::FilePath & filePath);
|
||||
QGraphicsItem* getGraphicsItem();
|
||||
|
||||
// GObject related signal handers and callbacks
|
||||
static void _onFolderLoaded(FmFolder* folder, MainWindow* _this) {
|
||||
_this->onFolderLoaded(folder);
|
||||
}
|
||||
|
||||
private:
|
||||
Ui::MainWindow ui;
|
||||
QMenu* contextMenu_;
|
||||
QTimer* slideShowTimer_;
|
||||
|
||||
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
|
||||
bool imageModified_; // the current image is modified by rotation, flip, or others and needs to be saved
|
||||
|
||||
// folder browsing
|
||||
FmFolder* folder_;
|
||||
FmPath* folderPath_;
|
||||
std::shared_ptr<Fm::Folder> folder_;
|
||||
Fm::FilePath folderPath_;
|
||||
Fm::FolderModel* folderModel_;
|
||||
Fm::ProxyFolderModel* proxyModel_;
|
||||
ModelFilter* modelFilter_;
|
||||
@ -161,6 +159,6 @@ private:
|
||||
SaveImageJob* saveJob_;
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif // LXIMAGE_MAINWINDOW_H
|
||||
|
@ -108,6 +108,8 @@
|
||||
<addaction name="actionCopy"/>
|
||||
<addaction name="actionPaste"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionUpload"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionPreferences"/>
|
||||
</widget>
|
||||
<addaction name="menu_File"/>
|
||||
@ -466,6 +468,17 @@
|
||||
<string>Ctrl+D</string>
|
||||
</property>
|
||||
</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>
|
||||
<customwidgets>
|
||||
<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.
|
||||
if(!fm_file_info_is_image(info))
|
||||
return false;
|
||||
return true;
|
||||
return info && info->isImage();
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ namespace LxImage {
|
||||
class ModelFilter: public Fm::ProxyFolderModelFilter {
|
||||
public:
|
||||
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();
|
||||
};
|
||||
|
||||
|
@ -111,7 +111,7 @@ void PreferencesDialog::initIconThemes(Settings& settings) {
|
||||
|
||||
iconThemes.remove("hicolor"); // remove hicolor, which is only a fallback
|
||||
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->model()->sort(0); // sort the list of icon theme names
|
||||
|
@ -26,47 +26,58 @@
|
||||
|
||||
using namespace LxImage;
|
||||
|
||||
SaveImageJob::SaveImageJob(MainWindow* window, FmPath* filePath):
|
||||
Job(),
|
||||
mainWindow_(window),
|
||||
path_(fm_path_ref(filePath)),
|
||||
image_(window->image()) {
|
||||
SaveImageJob::SaveImageJob(const QImage & image, const Fm::FilePath & filePath):
|
||||
path_{filePath},
|
||||
image_{image},
|
||||
failed_{true}
|
||||
{
|
||||
}
|
||||
|
||||
SaveImageJob::~SaveImageJob() {
|
||||
fm_path_unref(path_);
|
||||
}
|
||||
|
||||
// This is called from the worker thread, not main thread
|
||||
bool SaveImageJob::run() {
|
||||
GFile* gfile = fm_path_to_gfile(path_);
|
||||
GFileOutputStream* fileStream = g_file_replace(gfile, NULL, false, G_FILE_CREATE_NONE, cancellable_, &error_);
|
||||
g_object_unref(gfile);
|
||||
void SaveImageJob::exec() {
|
||||
const Fm::CStrPtr f = path_.baseName();
|
||||
char const * format = f.get();
|
||||
format = strrchr(format, '.');
|
||||
if(format) // use filename extension as the image format
|
||||
++format;
|
||||
|
||||
if(fileStream) { // if the file stream is successfually opened
|
||||
const char* format = fm_path_get_basename(path_);
|
||||
format = strrchr(format, '.');
|
||||
if(format) // use filename extension as the image format
|
||||
++format;
|
||||
QBuffer imageBuffer;
|
||||
image_.save(&imageBuffer, format); // save the image to buffer
|
||||
|
||||
QBuffer imageBuffer;
|
||||
image_.save(&imageBuffer, format); // save the image to buffer
|
||||
GOutputStream* outputStream = G_OUTPUT_STREAM(fileStream);
|
||||
g_output_stream_write_all(outputStream,
|
||||
imageBuffer.data().constData(),
|
||||
imageBuffer.size(),
|
||||
NULL,
|
||||
cancellable_,
|
||||
&error_);
|
||||
g_output_stream_close(outputStream, NULL, NULL);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// this function is called from main thread only
|
||||
void SaveImageJob::finish() {
|
||||
// only do processing if the job is not cancelled
|
||||
if(!g_cancellable_is_cancelled(cancellable_)) {
|
||||
mainWindow_->onImageSaved(this);
|
||||
// the file stream is successfually opened
|
||||
if (!isCancelled())
|
||||
{
|
||||
GOutputStream* outputStream = G_OUTPUT_STREAM(fileStream);
|
||||
g_output_stream_write_all(outputStream,
|
||||
imageBuffer.data().constData(),
|
||||
imageBuffer.size(),
|
||||
NULL,
|
||||
cancellable().get(),
|
||||
&error);
|
||||
g_output_stream_close(outputStream, NULL, NULL);
|
||||
if (!error)
|
||||
{
|
||||
// successfully written
|
||||
failed_ = false;
|
||||
break; // successfully written
|
||||
}
|
||||
|
||||
act = emitError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,39 +21,39 @@
|
||||
#ifndef LXIMAGE_SAVEIMAGEJOB_H
|
||||
#define LXIMAGE_SAVEIMAGEJOB_H
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <libfm/fm.h>
|
||||
#include <libfm-qt/core/job.h>
|
||||
#include <libfm-qt/core/filepath.h>
|
||||
#include <QImage>
|
||||
#include "job.h"
|
||||
|
||||
namespace LxImage {
|
||||
|
||||
class MainWindow;
|
||||
|
||||
class SaveImageJob : public Job {
|
||||
class SaveImageJob : public Fm::Job {
|
||||
|
||||
public:
|
||||
SaveImageJob(MainWindow* window, FmPath* filePath);
|
||||
SaveImageJob(const QImage & image, const Fm::FilePath & filePath);
|
||||
|
||||
QImage image() const {
|
||||
return image_;
|
||||
}
|
||||
|
||||
FmPath* filePath() const {
|
||||
const Fm::FilePath & filePath() const {
|
||||
return path_;
|
||||
}
|
||||
|
||||
bool failed() const
|
||||
{
|
||||
return failed_;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool run();
|
||||
virtual void finish();
|
||||
virtual void exec() override;
|
||||
|
||||
private:
|
||||
~SaveImageJob(); // prevent direct deletion
|
||||
|
||||
public:
|
||||
MainWindow* mainWindow_;
|
||||
FmPath* path_;
|
||||
QImage image_;
|
||||
const Fm::FilePath path_;
|
||||
const QImage image_;
|
||||
bool failed_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
|
||||
#include "screenshotdialog.h"
|
||||
#include "screenshotselectarea.h"
|
||||
#include <QTimer>
|
||||
#include <QPixmap>
|
||||
#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);
|
||||
MainWindow* window = app->createWindow();
|
||||
if(!image.isNull())
|
||||
|
@ -15,8 +15,7 @@
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset theme="camera-photo">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="fieldGrowthPolicy">
|
||||
@ -52,6 +51,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="screenArea">
|
||||
<property name="text">
|
||||
<string>Capture an area of the screen</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</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