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
ubuntu/disco debian/0.6.0-1
Alf Gaida 7 years ago
parent 75af31920b
commit b8d61bddf8

@ -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.

@ -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

@ -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

@ -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

@ -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;
GraphicsScene::GraphicsScene(QObject *parent):
QGraphicsScene (parent) {
}
protected:
GCancellable* cancellable_;
GError* error_;
void GraphicsScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event) {
if(event->mimeData()->hasUrls())
event->acceptProposedAction();
}
private:
static gboolean _jobThread(GIOSchedulerJob* job, GCancellable* cancellable, Job* pThis);
static gboolean _finish(Job* pThis);
static void _freeMe(Job* pThis);
};
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();
}
#endif // LXIMAGE_JOB_H
}

@ -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)

@ -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
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 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
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;
}
// 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
}
// 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);
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>

@ -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();
}

@ -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

@ -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());
}

@ -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

@ -0,0 +1,4 @@
#Translations
Name[lt]=Ekrano kopija
GenericName[lt]=Ekrano kopija
Comment[lt]=Padaryti ekrano kopiją

@ -0,0 +1,4 @@
#Translations
Name[lt]=LXImage
GenericName[lt]=Paveikslų žiūryklė
Comment[lt]=LXQt paveikslų žiūryklė

@ -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));
}

@ -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

@ -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);
}
}

@ -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

@ -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));
}

@ -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

@ -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);
}
}

@ -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

@ -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;

@ -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

@ -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();
}

@ -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

@ -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();
}

@ -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

@ -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…
Cancel
Save