diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..f0b4a0529a49cf97eaf13f655b5aaa47e4aabeff --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,78 @@ +# Use latest Ubuntu LTS docker image. +image: ubuntu:xenial + +build: + stage: build + + before_script: + - apt update + # Build dependencies + - apt -y install g++ cmake make git + - apt -y install qtbase5-private-dev qtscript5-dev qttools5-dev qttools5-dev-tools libqt5svg5-dev + # Optional: Better support for X11 + - apt -y install libxfixes-dev libxtst-dev + # Optional: CMake can get version from git + - apt -y install git + + script: + - mkdir -p build + - cd build + - cmake -DWITH_TESTS=TRUE -DWITH_QT5=TRUE -DCMAKE_INSTALL_PREFIX=../copyq -DCOPYQ_ITEMSYNC_UPDATE_INTERVAL_MS=1000 .. + - make install + + # Upload installed application. + artifacts: + paths: + - copyq + + cache: + paths: + - build + +# Run simple tests (doesn't require GUI) +test: + stage: test + + before_script: + - apt update + # Runtime libraries + - apt -y install libqt5core5a libqt5gui5 libqt5network5 libqt5script5 libqt5widgets5 libx11-6 libxtst6 libqt5svg5 libqt5xml5 libqt5test5 + + script: + - copyq/bin/copyq help + - copyq/bin/copyq version + + dependencies: + - build + +# GUI tests (requires X11) +test_gui: + stage: test + + before_script: + - apt update + # Runtime libraries + - apt -y install libqt5core5a libqt5gui5 libqt5network5 libqt5script5 libqt5widgets5 libx11-6 libxtst6 libqt5svg5 libqt5xml5 libqt5test5 + # X11 and window manager + - apt -y install xvfb openbox + # Screenshot utility + - apt -y install scrot + + script: + - export DISPLAY=':99.0' + - Xvfb :99 -screen 0 640x480x24 & + - sleep 5 + - openbox & + - sleep 5 + # Take screenshots in intervals. + - (mkdir -p screenshots && while true; do i=$((i+1)); f="screenshots/$i.png"; sleep 1 && echo " --- $f ---" && scrot "$f" || break; done) & + - copyq/bin/copyq tests + + # Upload screenshots on failure. + artifacts: + when: on_failure + paths: + - screenshots + + dependencies: + - build diff --git a/plugins/itemimage/itemimage.cpp b/plugins/itemimage/itemimage.cpp index 6f5735791a78deb49876ad7d01f4bf4f8a618ca3..1766c2bebaa07a6429d6b2987793de5d9325dd6b 100644 --- a/plugins/itemimage/itemimage.cpp +++ b/plugins/itemimage/itemimage.cpp @@ -209,8 +209,10 @@ ItemWidget *ItemImageLoader::create(const QModelIndex &index, QWidget *parent, b QStringList ItemImageLoader::formatsToSave() const { - return QStringList("image/svg+xml") << QString("image/bmp") << QString("image/png") - << QString("image/jpeg") << QString("image/gif"); + return QStringList() + << QString("image/svg+xml") + << QString("image/png") + << QString("image/gif"); } QVariantMap ItemImageLoader::applySettings() diff --git a/plugins/itemsync/CMakeLists.txt b/plugins/itemsync/CMakeLists.txt index 5addef5c95292aa1d5f94b18e680a876e9157800..3f810b3020843b81bcbd3fbbf046c72c00abda89 100644 --- a/plugins/itemsync/CMakeLists.txt +++ b/plugins/itemsync/CMakeLists.txt @@ -1,3 +1,8 @@ +OPTION(COPYQ_ITEMSYNC_UPDATE_INTERVAL_MS "Interval in ms for updating items from files" -1) +if (COPYQ_ITEMSYNC_UPDATE_INTERVAL_MS GREATER 0) + add_definitions( -DCOPYQ_ITEMSYNC_UPDATE_INTERVAL_MS=${COPYQ_ITEMSYNC_UPDATE_INTERVAL_MS} ) +endif() + set(copyq_plugin_itemsync_SOURCES ../../src/common/common.cpp ../../src/common/config.cpp diff --git a/plugins/itemsync/itemsync.cpp b/plugins/itemsync/itemsync.cpp index 4d5cc73b1f6a13985a70908ffe8484bba005a105..4ea0e0f054aabbafd17f6a13b1c4ed256df86ed4 100644 --- a/plugins/itemsync/itemsync.cpp +++ b/plugins/itemsync/itemsync.cpp @@ -767,6 +767,16 @@ public: { m_watcher.addPath(path); +#ifdef COPYQ_ITEMSYNC_UPDATE_INTERVAL_MS + { + auto t = new QTimer(this); + t->setInterval(COPYQ_ITEMSYNC_UPDATE_INTERVAL_MS); + connect( t, SIGNAL(timeout()), + SLOT(updateItems()) ); + t->start(); + } +#endif + m_updateTimer.setInterval(updateItemsIntervalMs); m_updateTimer.setSingleShot(true); connect( &m_updateTimer, SIGNAL(timeout()), @@ -846,6 +856,8 @@ public slots: */ void updateItems() { + m_updateTimer.stop(); + if ( m_model.isNull() ) return; diff --git a/src/common/common.cpp b/src/common/common.cpp index bce5c3f0cce627d1318a3c8643389ed01c36e1f1..d2708015ee076b56f5785b72505dac5be37f499d 100644 --- a/src/common/common.cpp +++ b/src/common/common.cpp @@ -46,6 +46,11 @@ # include <QTextDocument> // Qt::escape() #endif +// This is needed on X11 when retrieving lots of data from clipboard. +#if QT_VERSION >= 0x050000 && defined(COPYQ_WS_X11) +# define PROCESS_EVENTS_BEFORE_CLIPBOARD_DATA +#endif + namespace { QString getImageFormatFromMime(const QString &mime) @@ -149,7 +154,6 @@ int indexOfKeyHint(const QString &name) return -1; } - QString escapeHtmlSpaces(const QString &str) { QString str2 = str; @@ -158,6 +162,24 @@ QString escapeHtmlSpaces(const QString &str) .replace('\n', "<br />"); } +QByteArray getUtf8Data(const QMimeData &data, const QString &format) +{ + if (format == mimeText || format == mimeHtml) + return dataToText( data.data(format), format ).toUtf8(); + + if (format == mimeUriList) { + QByteArray bytes; + for ( const auto &url : data.urls() ) { + if ( !bytes.isEmpty() ) + bytes += '\n'; + bytes += url.toString().toUtf8(); + } + return bytes; + } + + return data.data(format); +} + } // namespace QString quoteString(const QString &str) @@ -207,24 +229,6 @@ uint hash(const QVariantMap &data) return hash; } -QByteArray getUtf8Data(const QMimeData &data, const QString &format) -{ - if (format == mimeText || format == mimeHtml) - return dataToText( data.data(format), format ).toUtf8(); - - if (format == mimeUriList) { - QByteArray bytes; - for ( const auto &url : data.urls() ) { - if ( !bytes.isEmpty() ) - bytes += '\n'; - bytes += url.toString().toUtf8(); - } - return bytes; - } - - return data.data(format); -} - QString getTextData(const QByteArray &bytes) { // QString::fromUtf8(bytes) ends string at first '\0'. @@ -261,7 +265,19 @@ QVariantMap cloneData(const QMimeData &data, const QStringList &formats) QImage image; bool imageLoaded = false; +#ifdef PROCESS_EVENTS_BEFORE_CLIPBOARD_DATA + const QPointer<const QMimeData> dataGuard(&data); +#endif + for (const auto &mime : formats) { +#ifdef PROCESS_EVENTS_BEFORE_CLIPBOARD_DATA + QCoreApplication::processEvents(); + if (dataGuard.isNull()) { + log("Clipboard data lost", LogWarning); + return newdata; + } +#endif + const QByteArray bytes = getUtf8Data(data, mime); if ( !bytes.isEmpty() ) { newdata.insert(mime, bytes); diff --git a/src/common/common.h b/src/common/common.h index 914d3113229a824ab7c0093df5f8a4bc3abe8a17..387f7d32e33d40787f2cf080006fc3028c6b34b8 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -66,8 +66,6 @@ const QMimeData *clipboardData(QClipboard::Mode mode = QClipboard::Clipboard); uint hash(const QVariantMap &data); -QByteArray getUtf8Data(const QMimeData &data, const QString &format); - QString getTextData(const QByteArray &data); /** diff --git a/src/gui/configurationmanager.cpp b/src/gui/configurationmanager.cpp index bfb5ebe94b2b2c361a1baf55fdd7dea94a8fcf68..c52465a1ba6f02d86b3b1ec53264dcf7e8d32aa6 100644 --- a/src/gui/configurationmanager.cpp +++ b/src/gui/configurationmanager.cpp @@ -482,7 +482,9 @@ void ConfigurationManager::apply() // Language changes after restart. const int newLocaleIndex = ui->comboBoxLanguage->currentIndex(); const QString newLocaleName = ui->comboBoxLanguage->itemData(newLocaleIndex).toString(); - const QString oldLocaleName = settings.value("Options/language").toString(); + QString oldLocaleName = settings.value("Options/language").toString(); + if (oldLocaleName.isEmpty()) + oldLocaleName = "en"; const QLocale oldLocale; settings.setValue("Options/language", newLocaleName); diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index f95daf2d4689af6948829b49014d01158ba379a5..6efee1a8d601de7f0148d9521a1ada587b097385 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -383,6 +383,8 @@ MainWindow::MainWindow(ItemFactory *itemFactory, QWidget *parent) ui->tabWidget->addToolBars(this); addToolBar(Qt::RightToolBarArea, ui->toolBar); + ui->dockWidgetItemPreview->hide(); + WindowGeometryGuard::create(this); restoreState( mainWindowState(objectName()) ); // NOTE: QWidget::isVisible() returns false if parent is not visible. diff --git a/src/tests/test_utils.h b/src/tests/test_utils.h index b28a733bbcb833a11eb3e19caf9e2901087c151d..c16b7d76104c2c6b3e7d7b6459569d1b3de4425b 100644 --- a/src/tests/test_utils.h +++ b/src/tests/test_utils.h @@ -21,7 +21,7 @@ #define TEST_UTILS_H #include <QByteArray> -#include <QDebug> +#include <QFile> #include <QString> #include <QStringList> #include <QTest> @@ -35,7 +35,10 @@ do { \ QByteArray errors_ = (ERRORS_OR_EMPTY); \ if (!errors_.isEmpty()) { \ - qWarning() << errors_; \ + QFile ferr; \ + ferr.open(stderr, QIODevice::WriteOnly); \ + ferr.write(errors_ + "\n"); \ + ferr.close(); \ QVERIFY2(false, "Failed with errors above."); \ } \ } while (false) diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp index 3762cf81b9f71680d82ec4a496f073d9b2cffa17..51e09190a4ed574de0d3b18dd03a6966cd6e5f4a 100644 --- a/src/tests/tests.cpp +++ b/src/tests/tests.cpp @@ -33,6 +33,7 @@ #include <QApplication> #include <QClipboard> +#include <QDebug> #include <QDir> #include <QFileInfo> #include <QMap>