").arg(escapeHtml(name)));
+ m_nameLabel->setToolTip(info.name);
+ m_pluginLabel->setText(plugin_name);
+ m_nameFrame->show();
+ m_line_1->show();
+ if (info.description.length() > 0) {
+ //NEW_TODO
+ clearLayout(m_descFrameLyt);
+ Q_FOREACH (SearchPluginIface::DescriptionInfo desc, info.description) {
+ QLabel * descLabel = new QLabel(m_descFrame);
+ QString show_desc = desc.key + ": " + desc.value;
+ descLabel->setText(show_desc);
+ m_descFrameLyt->addWidget(descLabel);
+ }
+ m_line_2->show();
+ }
+ clearLayout(m_actionFrameLyt);
+ Q_FOREACH (auto action, info.actionMap) {
+ //NEW_TODO
+ QLabel * actionLabel = new QLabel(m_actionFrame);
+ actionLabel->setText(action);
+ m_actionFrameLyt->addWidget(actionLabel);
+ }
+ m_actionFrame->show();
+}
+
+void DetailWidget::clear()
+{
+ m_iconLabel->hide();
+ m_nameFrame->hide();
+ m_line_1->hide();
+ m_descFrame->hide();
+ m_line_2->hide();
+ m_actionFrame->hide();
+}
+
+void DetailWidget::initUi()
+{
+ m_mainLyt = new QVBoxLayout(this);
+ this->setLayout(m_mainLyt);
+ m_mainLyt->setContentsMargins(DETAIL_WIDGET_MARGINS);
+ m_mainLyt->setAlignment(Qt::AlignHCenter);
+
+ m_iconLabel = new QLabel(this);
+ m_iconLabel->setFixedHeight(DETAIL_ICON_HEIGHT);
+ m_iconLabel->setAlignment(Qt::AlignCenter);
+
+ m_nameFrame = new QFrame(this);
+ m_nameFrameLyt = new QHBoxLayout(m_nameFrame);
+ m_nameFrame->setLayout(m_nameFrameLyt);
+ m_nameFrameLyt->setContentsMargins(DETAIL_FRAME_MARGINS);
+ m_nameLabel = new QLabel(m_nameFrame);
+ m_nameLabel->setMaximumWidth(NAME_LABEL_WIDTH);
+ m_pluginLabel = new QLabel(m_nameFrame);
+ m_nameFrameLyt->addWidget(m_nameLabel);
+ m_nameFrameLyt->addStretch();
+ m_nameFrameLyt->addWidget(m_pluginLabel);
+
+ m_line_1 = new QFrame(this);
+ m_line_1->setFixedHeight(1);
+ m_line_1->setLineWidth(0);
+ m_line_1->setStyleSheet(LINE_STYLE);
+ m_line_2 = new QFrame(this);
+ m_line_2->setFixedHeight(1);
+ m_line_2->setLineWidth(0);
+ m_line_2->setStyleSheet(LINE_STYLE);
+
+ m_descFrame = new QFrame(this);
+ m_descFrameLyt = new QVBoxLayout(m_descFrame);
+ m_descFrame->setLayout(m_descFrameLyt);
+ m_descFrameLyt->setContentsMargins(DETAIL_FRAME_MARGINS);
+
+ m_actionFrame = new QFrame(this);
+ m_actionFrameLyt = new QVBoxLayout(m_actionFrame);
+ m_actionFrame->setLayout(m_actionFrameLyt);
+ m_actionFrameLyt->setContentsMargins(DETAIL_FRAME_MARGINS);
+
+ m_mainLyt->addWidget(m_iconLabel);
+ m_mainLyt->addWidget(m_nameFrame);
+ m_mainLyt->addWidget(m_line_1);
+ m_mainLyt->addWidget(m_descFrame);
+ m_mainLyt->addWidget(m_line_2);
+ m_mainLyt->addWidget(m_actionFrame);
+ m_mainLyt->addStretch();
+}
+
+void DetailWidget::paintEvent(QPaintEvent * event)
+{
+ QStyleOption opt;
+ opt.init(this);
+ QPainter p(this);
+ style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
+
+ QRect rect = this->rect();
+ p.setRenderHint(QPainter::Antialiasing); // 反锯齿;
+ p.setBrush(opt.palette.color(QPalette::Text));
+ p.setOpacity(DETAIL_WIDGET_TRANSPARENT);
+ p.setPen(Qt::NoPen);
+ p.drawRoundedRect(rect, DETAIL_WIDGET_BORDER_RADIUS, DETAIL_WIDGET_BORDER_RADIUS);
+ return QWidget::paintEvent(event);
+}
+
+void DetailWidget::clearLayout(QLayout *layout)
+{
+ if(! layout) return;
+ QLayoutItem * child;
+ while((child = layout->takeAt(0)) != 0) {
+ if(child->widget()) {
+ child->widget()->setParent(NULL);
+ }
+ delete child;
+ }
+ child = NULL;
+}
diff --git a/frontend/control/stack-pages/search-page-section.h b/frontend/control/stack-pages/search-page-section.h
new file mode 100644
index 0000000..342ab1b
--- /dev/null
+++ b/frontend/control/stack-pages/search-page-section.h
@@ -0,0 +1,96 @@
+/*
+ *
+ * Copyright (C) 2020, KylinSoft Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Authors: zhangjiaping
+ *
+ */
+#ifndef SEARCHPAGESECTION_H
+#define SEARCHPAGESECTION_H
+#include
+#include
+#include
+#include
+#include "result-view.h"
+#include "plugininterface/search-plugin-iface.h"
+
+namespace Zeeker {
+class ResultArea : public QScrollArea
+{
+ Q_OBJECT
+public:
+ ResultArea(QWidget *parent = nullptr);
+ ~ResultArea() = default;
+ void appendWidet(ResultWidget *);
+ void setVisibleList(const QStringList &);
+private:
+ void initUi();
+ void setupConnectionsForWidget(ResultWidget *);
+ QWidget * m_widget = nullptr;
+ QVBoxLayout * m_mainLyt = nullptr;
+ QList m_widget_list;
+
+Q_SIGNALS:
+ void startSearch(const QString &);
+ void stopSearch();
+
+};
+
+class DetailWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ DetailWidget(QWidget *parent = nullptr);
+ ~DetailWidget() = default;
+ void clear();
+
+public Q_SLOTS:
+ void setWidgetInfo(const QString&, const SearchPluginIface::ResultInfo&);
+
+private:
+ void initUi();
+ void paintEvent(QPaintEvent *);
+ void clearLayout(QLayout *);
+ QVBoxLayout * m_mainLyt = nullptr;
+ QLabel * m_iconLabel = nullptr;
+ QFrame * m_nameFrame = nullptr;
+ QHBoxLayout * m_nameFrameLyt = nullptr;
+ QLabel * m_nameLabel = nullptr;
+ QLabel * m_pluginLabel = nullptr;
+ QFrame * m_line_1 = nullptr;
+ QFrame * m_descFrame = nullptr;
+ QVBoxLayout * m_descFrameLyt = nullptr;
+ QFrame * m_line_2 = nullptr;
+ QFrame * m_actionFrame = nullptr;
+ QVBoxLayout * m_actionFrameLyt = nullptr;
+};
+
+class DetailArea : public QScrollArea
+{
+ Q_OBJECT
+public:
+ DetailArea(QWidget *parent = nullptr);
+ ~DetailArea() = default;
+private:
+ void initUi();
+ DetailWidget * m_detailWidget = nullptr;
+
+Q_SIGNALS:
+ void setWidgetInfo(const QString&, const SearchPluginIface::ResultInfo&);
+};
+}
+
+#endif // SEARCHPAGESECTION_H
diff --git a/frontend/control/stack-pages/search-page.cpp b/frontend/control/stack-pages/search-page.cpp
new file mode 100644
index 0000000..3c3938e
--- /dev/null
+++ b/frontend/control/stack-pages/search-page.cpp
@@ -0,0 +1,79 @@
+/*
+ *
+ * Copyright (C) 2020, KylinSoft Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Authors: zhangjiaping
+ *
+ */
+#include "search-page.h"
+using namespace Zeeker;
+
+#define RESULT_WIDTH 240
+#define DETAIL_WIDTH 400
+
+SearchPage::SearchPage(QWidget *parent) : QWidget(parent)
+{
+ initUi();
+ initConnections();
+}
+
+void SearchPage::setSize(const int&width, const int&height)
+{
+ m_splitter->setFixedSize(width, height);
+}
+
+void SearchPage::setPlugins(const QStringList &plugins_id)
+{
+ Q_FOREACH (QString plugin_id, plugins_id) {
+ ResultWidget * widget = new ResultWidget(plugin_id, m_resultArea);
+ m_resultArea->appendWidet(widget);
+ setupConnectionsForWidget(widget);
+ }
+}
+
+void SearchPage::appendPlugin(const QString &plugin_id)
+{
+ ResultWidget * widget = new ResultWidget(plugin_id, m_resultArea);
+ m_resultArea->appendWidet(widget);
+ setupConnectionsForWidget(widget);
+}
+
+void SearchPage::initUi()
+{
+ m_splitter = new QSplitter(this);
+ m_resultArea = new ResultArea(m_splitter);
+ m_detailArea = new DetailArea(m_splitter);
+ m_splitter->addWidget(m_resultArea);
+ m_splitter->addWidget(m_detailArea);
+ m_splitter->setOpaqueResize(false);
+ QList size_list;
+ size_list<setSizes(size_list);
+ m_splitter->handle(1)->setEnabled(false); //暂时禁止拖动分隔条
+}
+
+void SearchPage::initConnections()
+{
+ connect(this, &SearchPage::startSearch, m_resultArea, &ResultArea::startSearch);
+ connect(this, &SearchPage::stopSearch, m_resultArea, &ResultArea::stopSearch);
+}
+
+void SearchPage::setupConnectionsForWidget(ResultWidget *widget)
+{
+ connect(widget, &ResultWidget::currentRowChanged, m_detailArea, &DetailArea::setWidgetInfo);
+ connect(widget, &ResultWidget::currentRowChanged, this, &SearchPage::currentRowChanged);
+ connect(this, &SearchPage::currentRowChanged, widget, &ResultWidget::clearSelectedRow);
+}
diff --git a/frontend/control/stack-pages/search-page.h b/frontend/control/stack-pages/search-page.h
new file mode 100644
index 0000000..2740da5
--- /dev/null
+++ b/frontend/control/stack-pages/search-page.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * Copyright (C) 2020, KylinSoft Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Authors: zhangjiaping
+ *
+ */
+#ifndef SEARCHPAGE_H
+#define SEARCHPAGE_H
+
+#include
+#include "search-page-section.h"
+
+namespace Zeeker {
+class SearchPage : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit SearchPage(QWidget *parent = nullptr);
+ ~SearchPage() = default;
+ void setSize(const int&, const int&);
+ void setPlugins(const QStringList &plugins_id);
+ void appendPlugin(const QString &plugin_id);
+
+private:
+ void initUi();
+ void initConnections();
+ void setupConnectionsForWidget(ResultWidget *);
+ QSplitter * m_splitter = nullptr;
+ ResultArea * m_resultArea = nullptr;
+ DetailArea * m_detailArea = nullptr;
+
+Q_SIGNALS:
+ void startSearch(const QString &);
+ void stopSearch();
+ void currentRowChanged(const QString &, const SearchPluginIface::ResultInfo&);
+};
+}
+
+#endif // SEARCHPAGE_H
diff --git a/frontend/control/stack-pages/stack-pages.pri b/frontend/control/stack-pages/stack-pages.pri
new file mode 100644
index 0000000..924e1a9
--- /dev/null
+++ b/frontend/control/stack-pages/stack-pages.pri
@@ -0,0 +1,13 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/home-page-section.h \
+ $$PWD/home-page.h \
+ $$PWD/search-page-section.h \
+ $$PWD/search-page.h \
+
+SOURCES += \
+ $$PWD/home-page-section.cpp \
+ $$PWD/home-page.cpp \
+ $$PWD/search-page-section.cpp \
+ $$PWD/search-page.cpp \
diff --git a/frontend/control/stacked-widget.cpp b/frontend/control/stacked-widget.cpp
new file mode 100644
index 0000000..2bc4040
--- /dev/null
+++ b/frontend/control/stacked-widget.cpp
@@ -0,0 +1,91 @@
+/*
+ *
+ * Copyright (C) 2020, KylinSoft Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Authors: zhangjiaping
+ *
+ */
+#include "stacked-widget.h"
+
+using namespace Zeeker;
+Zeeker::StackedWidget::StackedWidget(QWidget *)
+{
+ this->initWidgets();
+ this->initConnections();
+}
+
+Zeeker::StackedWidget::~StackedWidget()
+{
+ if (m_homePage) {
+ delete m_homePage;
+ m_homePage = NULL;
+ }
+ if (m_searchPage) {
+ delete m_searchPage;
+ m_searchPage = NULL;
+ }
+}
+
+/**
+ * @brief StackedWidget::setPage 设置StackedWidget所显示页面
+ * @param type 页面类型
+ */
+void StackedWidget::setPage(const int & type)
+{
+ this->setCurrentIndex(type);
+}
+
+int StackedWidget::currentPage()
+{
+ return this->currentIndex();
+}
+
+/**
+ * @brief StackedWidget::setPlugins 设置所有插件
+ */
+void StackedWidget::setPlugins(const QStringList &plugins)
+{
+ m_searchPage->setPlugins(plugins);
+}
+
+/**
+ * @brief StackedWidget::addPlugin 添加一个插件
+ */
+void StackedWidget::appendPlugin(const QString &plugin)
+{
+ m_searchPage->appendPlugin(plugin);
+}
+
+/**
+ * @brief StackedWidget::initWidgets 初始化向stackedwidget添加窗口
+ */
+void StackedWidget::initWidgets()
+{
+ //NEW_TODO
+ m_homePage = new HomePage;
+ this->insertWidget(int(StackedPage::HomePage), m_homePage);
+ this->setPage(int(StackedPage::HomePage));
+
+ m_searchPage = new SearchPage;
+ m_searchPage->setSize(this->width(), this->height());
+ this->insertWidget(int(StackedPage::SearchPage), m_searchPage);
+}
+
+void StackedWidget::initConnections()
+{
+ connect(this, &StackedWidget::startSearch, m_searchPage, &SearchPage::startSearch);
+ connect(this, &StackedWidget::stopSearch, m_searchPage, &SearchPage::stopSearch);
+}
diff --git a/frontend/control/stacked-widget.h b/frontend/control/stacked-widget.h
new file mode 100644
index 0000000..4caf68d
--- /dev/null
+++ b/frontend/control/stacked-widget.h
@@ -0,0 +1,62 @@
+/*
+ *
+ * Copyright (C) 2020, KylinSoft Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Authors: zhangjiaping
+ *
+ */
+#ifndef STACKEDWIDGET_H
+#define STACKEDWIDGET_H
+
+#include
+#include
+#include "input-box.h"
+#include "home-page.h"
+#include "search-page.h"
+
+namespace Zeeker {
+
+enum class StackedPage {
+ HomePage = 0,
+ SearchPage
+};
+
+class StackedWidget : public QStackedWidget {
+ Q_OBJECT
+public:
+ StackedWidget(QWidget *);
+ ~StackedWidget();
+
+ void setPage(const int &);
+ int currentPage();
+ void setPlugins(const QStringList&);
+ void appendPlugin(const QString&);
+
+Q_SIGNALS:
+ void startSearch(const QString &);
+ void stopSearch();
+
+private:
+ void initWidgets();
+ void initConnections();
+ QString m_keyword;
+ HomePage * m_homePage = nullptr;
+ SearchPage * m_searchPage = nullptr;
+// StackedPage m_current_page;
+};
+}
+
+#endif // STACKEDWIDGET_H
diff --git a/frontend/frontend.pro b/frontend/frontend.pro
new file mode 100644
index 0000000..7922326
--- /dev/null
+++ b/frontend/frontend.pro
@@ -0,0 +1,83 @@
+QT += core gui dbus KWindowSystem xml x11extras
+
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+VERSION = 0.0.1
+TARGET = ukui-search
+TEMPLATE = app
+
+PKGCONFIG += gio-2.0 glib-2.0 gio-unix-2.0
+CONFIG += c++11 link_pkgconfig no_keywords lrelease
+LIBS += -lxapian -lgsettings-qt -lquazip5 -lX11
+LIBS += -lukui-log4qt
+# The following define makes your compiler emit warnings if you use
+# any Qt feature that has been marked deprecated (the exact warnings
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
+
+include(control/control.pri)
+include(model/model.pri)
+include(xatom/xatom.pri)
+include(singleapplication/qt-single-application.pri)
+include(view/view.pri)
+
+SOURCES += \
+ main.cpp \
+ mainwindow.cpp
+
+
+HEADERS += \
+ mainwindow.h \
+
+# Default rules for deployment.
+
+target.path = /usr/bin
+!isEmpty(target.path): INSTALLS += target
+
+data-menu.path = /usr/share/applications
+data-menu.files += ../data/ukui-search-menu.desktop
+data.path = /etc/xdg/autostart
+data.files += ../data/ukui-search.desktop
+
+INSTALLS += data data-menu
+
+RESOURCES += \
+ resource.qrc
+
+TRANSLATIONS += \
+ ../translations/ukui-search/zh_CN.ts \
+ ../translations/ukui-search/tr.ts \
+ ../translations/ukui-search/bo.ts
+
+qm_files.path = /usr/share/ukui-search/translations/
+qm_files.files = $$OUT_PWD/.qm/*.qm
+
+schemes.path = /usr/share/glib-2.0/schemas/
+schemes.files += ../data/org.ukui.search.data.gschema.xml ../data/org.ukui.log4qt.ukui-search.gschema.xml
+
+INSTALLS += qm_files schemes
+
+LIBS += -L$$OUT_PWD/../libchinese-segmentation -lchinese-segmentation \
+ -L$$OUT_PWD/../libsearch -lukui-search
+
+INCLUDEPATH += $$PWD/../libchinese-segmentation
+DEPENDPATH += $$PWD/../libchinese-segmentation
+
+INCLUDEPATH += $$PWD/../libsearch
+DEPENDPATH += $$PWD/../libsearch
+
+#DISTFILES += \
+# ../data/ukui-search-menu.desktop \
+# $$OUT_PWD/.qm/bo.qm \
+# $$OUT_PWD/.qm/tr.qm \
+# $$OUT_PWD/.qm/zh_CN.qm
+
+DISTFILES += \
+ ../data/org.ukui.log4qt.ukui-search.gschema.xml \
+ ../data/org.ukui.search.data.gschema.xml
diff --git a/frontend/main.cpp b/frontend/main.cpp
new file mode 100644
index 0000000..c703f26
--- /dev/null
+++ b/frontend/main.cpp
@@ -0,0 +1,295 @@
+/*
+ *
+ * Copyright (C) 2020, KylinSoft Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Authors: zhangjiaping
+ * Modified by: zhangpengfei
+ * Modified by: zhangzihao
+ *
+ */
+
+#include "mainwindow.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
+#include
+#endif
+#include
+#include
+#include "qt-single-application.h"
+#include "qt-local-peer.h"
+#include "libsearch.h"
+#include "global-settings.h"
+
+using namespace Zeeker;
+
+void messageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
+{
+ QByteArray localMsg = msg.toLocal8Bit();
+ QByteArray currentTime = QTime::currentTime().toString().toLocal8Bit();
+
+ bool showDebug = true;
+// QString logFilePath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/ukui-search.log";
+// QString logFilePath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/.config/org.ukui/ukui-search/ukui-search.log";
+ QString logFilePath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/.config/org.ukui/ukui-search.log";
+ if (!QFile::exists(logFilePath)) {
+ showDebug = false;
+ }
+ FILE *log_file = nullptr;
+
+ if (showDebug) {
+ log_file = fopen(logFilePath.toLocal8Bit().constData(), "a+");
+ }
+
+ const char *file = context.file ? context.file : "";
+ const char *function = context.function ? context.function : "";
+ switch (type) {
+ case QtDebugMsg:
+ if (!log_file) {
+ break;
+ }
+ fprintf(log_file, "Debug: %s: %s (%s:%u, %s)\n", currentTime.constData(), localMsg.constData(), file, context.line, function);
+ break;
+ case QtInfoMsg:
+ fprintf(log_file? log_file: stdout, "Info: %s: %s (%s:%u, %s)\n", currentTime.constData(), localMsg.constData(), file, context.line, function);
+ break;
+ case QtWarningMsg:
+ fprintf(log_file? log_file: stderr, "Warning: %s: %s (%s:%u, %s)\n", currentTime.constData(), localMsg.constData(), file, context.line, function);
+ break;
+ case QtCriticalMsg:
+ fprintf(log_file? log_file: stderr, "Critical: %s: %s (%s:%u, %s)\n", currentTime.constData(), localMsg.constData(), file, context.line, function);
+ break;
+ case QtFatalMsg:
+ fprintf(log_file? log_file: stderr, "Fatal: %s: %s (%s:%u, %s)\n", currentTime.constData(), localMsg.constData(), file, context.line, function);
+ break;
+ }
+
+ if (log_file)
+ fclose(log_file);
+}
+
+void centerToScreen(QWidget* widget) {
+ if(!widget)
+ return;
+ QDesktopWidget* m = QApplication::desktop();
+ QRect desk_rect = m->screenGeometry(m->screenNumber(QCursor::pos()));
+ int desk_x = desk_rect.width();
+ int desk_y = desk_rect.height();
+ int x = widget->width();
+ int y = widget->height();
+ widget->move(desk_x / 2 - x / 2 + desk_rect.left(), desk_y / 2 - y / 2 + desk_rect.top());
+}
+/*
+void searchMethod(FileUtils::SearchMethod sm){
+ qWarning() << "searchMethod start: " << static_cast(sm);
+ if (FileUtils::SearchMethod::INDEXSEARCH == sm || FileUtils::SearchMethod::DIRECTSEARCH == sm) {
+ FileUtils::searchMethod = sm;
+ } else {
+ printf("enum class error!!!\n");
+ qWarning("enum class error!!!\n");
+ }
+ if (FileUtils::SearchMethod::INDEXSEARCH == sm && 0 == FileUtils::_index_status) {
+ qWarning() << "start first index";
+ FirstIndex fi("/home/zhangzihao/Desktop");
+ fi.start();
+ qWarning() << "start inotify index";
+// InotifyIndex ii("/home");
+// ii.start();
+ InotifyIndex* ii = InotifyIndex::getInstance("/home");
+ if (!ii->isRunning()) {
+ ii->start();
+ }
+ qDebug()<<"Search method has been set to INDEXSEARCH";
+ }
+ qWarning() << "searchMethod end: " << static_cast(FileUtils::searchMethod);
+}
+*/
+int main(int argc, char *argv[]) {
+//v101日志模块
+//#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
+// //Init log module
+// initUkuiLog4qt("ukui-search");
+//#endif
+
+ // Determine whether the home directory has been created, and if not, keep waiting.
+ char *p_home = NULL;
+
+ unsigned int i = 0;
+ while(p_home == NULL) {
+ ::sleep(1);
+ ++i;
+ p_home = getenv("HOME");
+ if(i % 5 == 0) {
+ qWarning() << "I can't find home! I'm done here!!";
+ printf("I can't find home! I'm done here!!");
+ syslog(LOG_ERR, "I can't find home! I'm done here!!\n");
+ }
+ }
+ p_home = NULL;
+ while(!QDir(QDir::homePath()).exists()) {
+ qWarning() << "Home is not exits!!";
+ printf("Home is not exits!!");
+ syslog(LOG_ERR, "Home is not exits!!\n");
+ ::sleep(1);
+ }
+
+ // Output log to file
+ qInstallMessageHandler(messageOutput);
+//若使用v101日志模块,可以解放如下判断条件
+//#if (QT_VERSION < QT_VERSION_CHECK(5, 12, 0))
+// // Output log to file
+// qInstallMessageHandler(messageOutput);
+//#endif
+
+ // Register meta type
+ qDebug() << "ukui-search main start";
+ qRegisterMetaType>("QPair");
+ qRegisterMetaType("Document");
+
+ // If qt version bigger than 5.12, enable high dpi scaling and use high dpi pixmaps?
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
+ QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+ QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
+#endif
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
+ QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
+#endif
+
+ // Make sure only one ukui-search is running.
+ QtSingleApplication app("ukui-search", argc, argv);
+ app.setQuitOnLastWindowClosed(false);
+
+ if(app.isRunning()) {
+ app.sendMessage(QApplication::arguments().length() > 1 ? QApplication::arguments().at(1) : app.applicationFilePath());
+ qDebug() << QObject::tr("ukui-search is already running!");
+ return EXIT_SUCCESS;
+ }/*else {
+ QCommandLineParser parser;
+ QCommandLineOption debugOption({"d", "debug"}, QObject::tr("Display debug information"));
+ QCommandLineOption showsearch({"s", "show"}, QObject::tr("show search widget"));
+ parser.addOptions({debugOption, showsearch});
+ parser.process(app);
+ }*/
+ /*
+ // Create a fifo at ~/.config/org.ukui/ukui-search, the fifo is used to control the order of child processes' running.
+ QDir fifoDir = QDir(QDir::homePath()+"/.config/org.ukui/ukui-search");
+ if(!fifoDir.exists())
+ qDebug()<<"create fifo path"<start();
+// QTime t2 = QTime::currentTime();
+// qDebug() << t1;
+// qDebug() << t2;
+ /*-------------InotyifyRefact Test End-----------------*/
+
+ /*-------------content index Test Start---------------*/
+// QTime t3 = QTime::currentTime();
+// FileTypeFilter* ftf = new FileTypeFilter("/home");
+// ftf->Test();
+// QTime t4 = QTime::currentTime();
+// delete ftf;
+// ftf = nullptr;
+// qDebug() << t3;
+// qDebug() << t4;
+ /*-------------content index Test End-----------------*/
+ /*-------------文本搜索 Test start-----------------*/
+// FileSearcher *search = new FileSearcher();
+// search->onKeywordSearchContent("重要器官移植⑤白血病");
+// search->onKeywordSearchContent("g,e,x");
+ /*-------------文本搜索 Test End-----------------*/
+
+ // Load translations
+ QTranslator translator;
+ try {
+ if(! translator.load("/usr/share/ukui-search/translations/" + QLocale::system().name())) throw - 1;
+ app.installTranslator(&translator);
+ } catch(...) {
+ qDebug() << "Load translations file" << QLocale() << "failed!";
+ }
+
+ QTranslator qt_translator;
+ try {
+ if(! qt_translator.load(":/res/qt-translations/qt_zh_CN.qm")) throw - 1;
+ app.installTranslator(&qt_translator);
+ } catch(...) {
+ qDebug() << "Load translations file" << QLocale() << "failed!";
+ }
+
+ //set main window to the center of screen
+ MainWindow *w = new MainWindow;
+ qApp->setWindowIcon(QIcon::fromTheme("kylin-search"));
+ centerToScreen(w);
+
+ //请务必在connect之后初始化mainwindow的Gsettings,为了保证gsettings第一次读取到的配置值能成功应用
+ w->initGsettings();
+
+ app.setActivationWindow(w);
+
+ // Processing startup parameters
+ if(QString::compare(QString("-s"), QString(QLatin1String(argv[1]))) == 0) {
+ centerToScreen(w);
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
+ XAtomHelper::getInstance()->setWindowMotifHint(w->winId(), w->m_hints);
+#endif
+ w->show();
+ }
+
+ QObject::connect(&app, &QtSingleApplication::messageReceived, w, &MainWindow::bootOptionsFilter);
+
+ // Start app search thread
+ AppMatch::getAppMatch()->start();
+
+ // NEW_TODO
+ // Set threads which in global thread pool expiry time in 5ms, some prolems here
+ QThreadPool::globalInstance()->setExpiryTimeout(5);
+
+ // NEW_TODO
+ // First insdex start, the parameter us useless, should remove the parameter
+// FirstIndex fi("/home/zhangzihao/Desktop");
+// fi.start();
+
+ // NEW_TODO
+ // Inotify index start, the parameter us useless, should remove the parameter
+// InotifyIndex* ii = InotifyIndex::getInstance("/home");
+// ii->start();
+
+ return app.exec();
+}
diff --git a/frontend/mainwindow.cpp b/frontend/mainwindow.cpp
new file mode 100644
index 0000000..25c60a4
--- /dev/null
+++ b/frontend/mainwindow.cpp
@@ -0,0 +1,577 @@
+/*
+ *
+ * Copyright (C) 2020, KylinSoft Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Authors: zhangjiaping
+ * Modified by: zhangpengfei
+ *
+ */
+
+#include "mainwindow.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
+#include
+#include "kwindowsystem.h"
+#endif
+#include "qt-single-application.h"
+#include "global-settings.h"
+
+#define MAIN_MARGINS 16,8,16,16
+#define TITLE_MARGINS 0,0,0,0
+#define UKUI_SEARCH_SCHEMAS "org.ukui.search.settings"
+#define SEARCH_METHOD_KEY "indexSearch"
+#define WEB_ENGINE_KEY "webEngine"
+#define WINDOW_WIDTH 640
+#define WINDOW_HEIGHT 590
+#define TITLE_HEIGHT 40
+#define WINDOW_ICON_SIZE 24
+#define SETTING_BTN_SIZE 30
+#define SEARCH_BAR_SIZE 44
+#define ASK_INDEX_TIME 5*1000
+#define RESEARCH_TIME 10*1000
+
+using namespace Zeeker;
+extern void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed);
+/**
+ * @brief MainWindow 主界面
+ * @param parent
+ *
+ * 慎用KWindowSystem::setShowingDesktop(!KWindowSystem::showingDesktop());
+ * 可能造成窗口属性的混乱
+ */
+MainWindow::MainWindow(QWidget *parent) :
+ QMainWindow(parent) {
+ this->setAttribute(Qt::WA_TranslucentBackground, true);
+ this->setAutoFillBackground(false);
+ this->setFocusPolicy(Qt::StrongFocus);
+ this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ this->setWindowTitle(tr("ukui-search"));
+ initUi();
+ initTimer();
+
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
+// setProperty("useStyleWindowManager", false); //禁止拖动
+ m_hints.flags = MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS;
+ m_hints.functions = MWM_FUNC_ALL;
+ m_hints.decorations = MWM_DECOR_BORDER;
+ XAtomHelper::getInstance()->setWindowMotifHint(winId(), m_hints);
+
+ QPainterPath path;
+ auto rect = this->rect();
+ rect.adjust(1, 1, -1, -1);
+ path.addRect(rect);
+ setProperty("blurRegion", QRegion(path.toFillPolygon().toPolygon()));
+ KWindowEffects::enableBlurBehind(this->winId(), true, QRegion(path.toFillPolygon().toPolygon()));
+#endif
+
+ m_sys_tray_icon = new QSystemTrayIcon(this);
+ m_sys_tray_icon->setIcon(QIcon::fromTheme("system-search-symbolic"));
+ m_sys_tray_icon->setToolTip(tr("Global Search"));
+ m_sys_tray_icon->show();
+ installEventFilter(this);
+ initConnections();
+
+ //NEW_TODO, register plugins
+// SearchPluginManager::getInstance()->registerPlugin(\\);
+// m_stackedWidget->setPlugins(SearchPluginManager::getInstance()->getPluginIds());
+ m_stackedWidget->setPlugins(QStringList()<<"File"<<"Folder");
+}
+
+MainWindow::~MainWindow() {
+#if (QT_VERSION < QT_VERSION_CHECK(5, 12, 0))
+ if(m_settingsWidget) {
+ delete m_settingsWidget;
+ m_settingsWidget = NULL;
+ }
+#endif
+ if(m_askDialog) {
+ delete m_askDialog;
+ m_askDialog = NULL;
+ }
+ if(m_askTimer) {
+ delete m_askTimer;
+ m_askTimer = NULL;
+ }
+ if(m_search_gsettings) {
+ delete m_search_gsettings;
+ m_search_gsettings = NULL;
+ }
+ if(m_searchWidget) {
+ delete m_searchWidget;
+ m_searchWidget = NULL;
+ }
+ if(m_searchLayout) {
+ delete m_searchLayout;
+ m_searchLayout = NULL;
+ }
+}
+
+/**
+ * @brief initUi 初始化主界面主要ui控件
+ */
+void MainWindow::initUi() {
+ this->setFixedSize(WINDOW_WIDTH, WINDOW_HEIGHT);
+
+ m_frame = new QFrame(this);
+
+ this->setCentralWidget(m_frame);
+ QVBoxLayout * mainlayout = new QVBoxLayout(m_frame);
+ mainlayout->setContentsMargins(MAIN_MARGINS);
+ m_frame->setLayout(mainlayout);
+
+ m_titleFrame = new QFrame(m_frame);//标题栏
+ m_titleFrame->setFixedHeight(TITLE_HEIGHT);
+ m_titleLyt = new QHBoxLayout(m_titleFrame);
+ m_titleLyt->setContentsMargins(TITLE_MARGINS);
+ m_iconLabel = new QLabel(m_titleFrame);
+ m_iconLabel->setFixedSize(WINDOW_ICON_SIZE, WINDOW_ICON_SIZE);
+ m_iconLabel->setPixmap(QIcon::fromTheme("kylin-search").pixmap(QSize(WINDOW_ICON_SIZE, WINDOW_ICON_SIZE)));
+ m_titleLabel = new QLabel(m_titleFrame);
+ m_titleLabel->setText(tr("Search"));
+ m_settingsBtn = new QPushButton(m_titleFrame);
+ m_settingsBtn->setFixedSize(SETTING_BTN_SIZE, SETTING_BTN_SIZE);
+ m_settingsBtn->setIcon(QIcon::fromTheme("document-properties-symbolic"));
+ m_settingsBtn->setProperty("useIconHighlightEffect", 0x2);
+ m_settingsBtn->setProperty("isWindowButton", 0x01);
+ m_settingsBtn->setFlat(true);
+ m_titleLyt->addWidget(m_iconLabel);
+ m_titleLyt->addWidget(m_titleLabel);
+ m_titleLyt->addStretch();
+ m_titleLyt->addWidget(m_settingsBtn);
+ m_stackedWidget = new StackedWidget(m_frame);//内容栏
+
+ m_searchWidget = new SeachBarWidget(this);
+ m_searchLayout = new SearchBarHLayout(this);
+ m_searchWidget->setLayout(m_searchLayout);
+ m_searchWidget->setFixedHeight(SEARCH_BAR_SIZE);
+
+ mainlayout->addWidget(m_titleFrame);
+ mainlayout->addWidget(m_stackedWidget);
+ mainlayout->addWidget(m_searchWidget);
+
+ //创建索引询问弹窗
+ m_askDialog = new CreateIndexAskDialog(this);
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
+ MotifWmHints ask_dialog_hints;
+ ask_dialog_hints.flags = MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS;
+ ask_dialog_hints.functions = MWM_FUNC_ALL;
+ ask_dialog_hints.decorations = MWM_DECOR_BORDER;
+ XAtomHelper::getInstance()->setWindowMotifHint(m_askDialog->winId(), ask_dialog_hints);
+#endif
+}
+
+void MainWindow::initConnections()
+{
+ connect(m_sys_tray_icon, &QSystemTrayIcon::activated, this, &MainWindow::trayIconActivatedSlot);
+ QObject::connect(this, &MainWindow::searchMethodChanged, this, [ = ](FileUtils::SearchMethod sm) {
+ this->m_searchMethodManager.searchMethod(sm);
+ });
+ connect(QApplication::primaryScreen(), &QScreen::geometryChanged, this, &MainWindow::monitorResolutionChange);
+ connect(qApp, &QApplication::primaryScreenChanged, this, &MainWindow::primaryScreenChangedSlot);
+ connect(m_askDialog, &CreateIndexAskDialog::closed, this, [ = ]() {
+ m_isAskDialogVisible = false;
+ });
+ connect(m_askDialog, &CreateIndexAskDialog::btnClicked, this, [ = ](const bool &is_create_index, const bool &is_ask_again) {
+ setSearchMethodConfig(is_create_index, is_ask_again);
+ });
+ connect(m_settingsBtn, &QPushButton::clicked, this, &MainWindow::settingsBtnClickedSlot);
+ //主题改变时,更新自定义标题栏的图标
+ connect(qApp, &QApplication::paletteChanged, this, [ = ]() {
+ m_iconLabel->setPixmap(QIcon::fromTheme("kylin-search").pixmap(QSize(WINDOW_ICON_SIZE, WINDOW_ICON_SIZE)));
+ });
+ connect(m_searchLayout, &SearchBarHLayout::requestSearchKeyword, this, &MainWindow::searchKeywordSlot);
+}
+
+/**
+ * @brief bootOptionsFilter 过滤终端命令
+ * @param opt
+ */
+void MainWindow::bootOptionsFilter(QString opt) {
+ if(opt == "-s" || opt == "--show") {
+ clearSearchResult();
+ centerToScreen(this);
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
+ XAtomHelper::getInstance()->setWindowMotifHint(winId(), m_hints);
+#endif
+ this->show();
+ this->m_searchLayout->focusIn();
+ this->raise();
+ this->activateWindow();
+ }
+}
+
+/**
+ * @brief clearSearchResult 清空搜索结果
+ */
+void MainWindow::clearSearchResult() {
+ m_searchLayout->clearText();
+ m_searchLayout->focusOut();
+}
+
+/**
+ * @brief MainWindow::trayIconActivatedSlot 点击任务栏图标的槽函数
+ * @param reason
+ */
+void MainWindow::trayIconActivatedSlot(QSystemTrayIcon::ActivationReason reason)
+{
+ if(reason == QSystemTrayIcon::Trigger) {
+ if(!this->isVisible()) {
+ clearSearchResult();
+ centerToScreen(this);
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
+ XAtomHelper::getInstance()->setWindowMotifHint(winId(), m_hints);
+#endif
+ this->show();
+ this->m_searchLayout->focusIn(); //打开主界面时输入框夺焦,可直接输入
+ this->raise();
+ this->activateWindow();
+ } else {
+ tryHideMainwindow();
+ }
+ }
+}
+
+/**
+ * @brief setSearchMethodConfig 在询问弹窗点击按钮后执行
+ * @param create_index 是否同意创建索引
+ * @param no_longer_ask 是否勾选了不再提示
+ */
+void MainWindow::setSearchMethodConfig(const bool &create_index, const bool &no_longer_ask)
+{
+ if(no_longer_ask) {
+ GlobalSettings::getInstance()->setValue(ENABLE_CREATE_INDEX_ASK_DIALOG, "false");
+ } else {
+ GlobalSettings::getInstance()->setValue(ENABLE_CREATE_INDEX_ASK_DIALOG, "true");
+ }
+ if(create_index) {
+ if(m_search_gsettings && m_search_gsettings->keys().contains(SEARCH_METHOD_KEY)) {
+ m_search_gsettings->set(SEARCH_METHOD_KEY, true);
+ } else {
+ //调用创建索引接口
+ Q_EMIT this->searchMethodChanged(FileUtils::SearchMethod::INDEXSEARCH);
+ //创建索引十秒后重新搜索一次(如果用户十秒内没有退出搜索界面且没有重新搜索)
+ m_researchTimer->start();
+ }
+ }
+}
+
+/**
+ * @brief MainWindow::settingsBtnClickedSlot 点击设置按钮的槽函数
+ */
+void MainWindow::settingsBtnClickedSlot()
+{
+#if (QT_VERSION < QT_VERSION_CHECK(5, 12, 0))
+ if(m_settingsWidget) { //当此窗口已存在时,仅需置顶
+ if(!m_settingsWidget->isVisible()) {
+ centerToScreen(m_settingsWidget);
+ }
+ m_settingsWidget->showWidget();
+ return;
+ }
+ m_settingsWidget = new SettingsWidget();
+ connect(this, &MainWindow::webEngineChanged, m_settingsWidget, [ = ]() {
+ m_settingsWidget->resetWebEngine();
+ });
+ connect(m_settingsWidget, &SettingsWidget::webEngineChanged, this, [ = ](const QString & engine) {
+ if(m_search_gsettings && m_search_gsettings->keys().contains(WEB_ENGINE_KEY)) {
+ m_search_gsettings->set(WEB_ENGINE_KEY, engine);
+ } else {
+ GlobalSettings::getInstance()->setValue(WEB_ENGINE, engine);
+ }
+ });
+ centerToScreen(m_settingsWidget);
+ m_settingsWidget->show();
+ connect(m_settingsWidget, &SettingsWidget::settingWidgetClosed, this, [ = ]() {
+ QTimer::singleShot(100, this, [ = ] {
+// clearSearchResult(); //现暂定从设置页返回主页面不清空搜索结果
+ this->setWindowState(this->windowState() & ~Qt::WindowMinimized);
+ this->raise();
+ this->showNormal();
+ this->activateWindow();
+ });
+ });
+#endif
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
+ //打开控制面板的设置页
+ QProcess process;
+ process.startDetached("ukui-control-center --search");
+#endif
+}
+
+/**
+ * @brief MainWindow::searchKeywordSlot 执行搜索的槽函数
+ * @param keyword 关键词
+ */
+void MainWindow::searchKeywordSlot(const QString &keyword)
+{
+ //NEW_TODO
+ if(keyword == "") {
+ m_stackedWidget->setPage(int(StackedPage::HomePage));
+ m_askTimer->stop();
+ Q_EMIT m_stackedWidget->stopSearch();
+ } else {
+ m_stackedWidget->setPage(int(StackedPage::SearchPage));
+ QTimer::singleShot(10, this, [ = ]() {
+ //允许弹窗且当前次搜索(为关闭主界面,算一次搜索过程)未询问且当前为暴力搜索
+ if(GlobalSettings::getInstance()->getValue(ENABLE_CREATE_INDEX_ASK_DIALOG).toString() != "false" && !m_currentSearchAsked && FileUtils::searchMethod == FileUtils::SearchMethod::DIRECTSEARCH)
+ m_askTimer->start();
+ Q_EMIT m_stackedWidget->startSearch(keyword);
+ });
+ }
+ m_researchTimer->stop(); //如果搜索内容发生改变,则停止建索引后重新搜索的倒计时
+}
+
+/**
+ * @brief monitorResolutionChange 监听屏幕改变
+ * @param rect
+ */
+void MainWindow::monitorResolutionChange(QRect rect) {
+ Q_UNUSED(rect);
+}
+
+/**
+ * @brief primaryScreenChangedSlot 监听分辨率改变
+ * @param screen
+ */
+void MainWindow::primaryScreenChangedSlot(QScreen *screen) {
+ Q_UNUSED(screen);
+
+}
+
+/**
+ * @brief MainWindow::moveToPanel 将主界面移动到任务栏旁边(跟随任务栏位置)
+ */
+void MainWindow::moveToPanel() {
+ QRect availableGeometry = qApp->primaryScreen()->availableGeometry();
+ QRect screenGeometry = qApp->primaryScreen()->geometry();
+
+ QDBusInterface primaryScreenInterface("org.ukui.SettingsDaemon",
+ "/org/ukui/SettingsDaemon/wayland",
+ "org.ukui.SettingsDaemon.wayland",
+ QDBusConnection::sessionBus());
+ if(QDBusReply(primaryScreenInterface.call("x")).isValid()) {
+ QDBusReply x = primaryScreenInterface.call("x");
+ QDBusReply y = primaryScreenInterface.call("y");
+ QDBusReply width = primaryScreenInterface.call("width");
+ QDBusReply height = primaryScreenInterface.call("height");
+ screenGeometry.setX(x);
+ screenGeometry.setY(y);
+ screenGeometry.setWidth(width);
+ screenGeometry.setHeight(height);
+ availableGeometry.setX(x);
+ availableGeometry.setY(y);
+ availableGeometry.setWidth(width);
+ availableGeometry.setHeight(height);
+ }
+
+ QDesktopWidget * desktopWidget = QApplication::desktop();
+ QRect screenMainRect = desktopWidget->screenGeometry(0);//获取设备屏幕大小
+
+ QDBusInterface interface("com.ukui.panel.desktop",
+ "/",
+ "com.ukui.panel.desktop",
+ QDBusConnection::sessionBus());
+
+ int position = QDBusReply(interface.call("GetPanelPosition", "position"));
+ int height = QDBusReply(interface.call("GetPanelSize", "height"));
+ int d = 8; //窗口边沿到任务栏距离
+
+ if(position == 0) {
+ //任务栏在下侧
+ this->move(availableGeometry.x() + availableGeometry.width() - this->width() - d, screenGeometry.y() + screenGeometry.height() - this->height() - height - d);
+ } else if(position == 1) {
+ //任务栏在上侧
+ this->move(availableGeometry.x() + availableGeometry.width() - this->width() - d, screenGeometry.y() + height + d);
+ } else if(position == 2) {
+ //任务栏在左侧
+ this->move(screenGeometry.x() + height + d, screenGeometry.y() + screenGeometry.height() - this->height() - d);
+ } else if(position == 3) {
+ //任务栏在右侧
+ this->move(screenGeometry.x() + screenGeometry.width() - this->width() - height - d, screenGeometry.y() + screenGeometry.height() - this->height() - d);
+ }
+}
+
+/**
+ * @brief MainWindow::centerToScreen 使窗口显示在屏幕中间
+ * @param widget
+ */
+void MainWindow::centerToScreen(QWidget* widget) {
+ if(!widget)
+ return;
+ QDesktopWidget* m = QApplication::desktop();
+ QRect desk_rect = m->screenGeometry(m->screenNumber(QCursor::pos()));
+ int desk_x = desk_rect.width();
+ int desk_y = desk_rect.height();
+ int x = widget->width();
+ int y = widget->height();
+ QDBusInterface primaryScreenInterface("org.ukui.SettingsDaemon",
+ "/org/ukui/SettingsDaemon/wayland",
+ "org.ukui.SettingsDaemon.wayland",
+ QDBusConnection::sessionBus());
+ if(QDBusReply(primaryScreenInterface.call("x")).isValid()) {
+ QDBusReply width = primaryScreenInterface.call("width");
+ QDBusReply height = primaryScreenInterface.call("height");
+ desk_x = width;
+ desk_y = height;
+ }
+ widget->move(desk_x / 2 - x / 2 + desk_rect.left(), desk_y / 2 - y / 2 + desk_rect.top());
+}
+
+void MainWindow::initGsettings() {
+ const QByteArray id(UKUI_SEARCH_SCHEMAS);
+ if(QGSettings::isSchemaInstalled(id)) {
+ m_search_gsettings = new QGSettings(id);
+ connect(m_search_gsettings, &QGSettings::changed, this, [ = ](const QString & key) {
+ if(key == SEARCH_METHOD_KEY) {
+ bool is_index_search = m_search_gsettings->get(SEARCH_METHOD_KEY).toBool();
+ this->setSearchMethod(is_index_search);
+ } else if(key == WEB_ENGINE_KEY) {
+ QString web_engine = m_search_gsettings->get(WEB_ENGINE_KEY).toString();
+ GlobalSettings::getInstance()->setValue(WEB_ENGINE, web_engine);
+ Q_EMIT this->webEngineChanged();
+ }
+ });
+ if(m_search_gsettings->keys().contains(SEARCH_METHOD_KEY)) {
+ bool is_index_search = m_search_gsettings->get(SEARCH_METHOD_KEY).toBool();
+ this->setSearchMethod(is_index_search);
+ }
+ if(m_search_gsettings->keys().contains(WEB_ENGINE_KEY)) {
+ QString web_engine = m_search_gsettings->get(WEB_ENGINE_KEY).toString();
+ GlobalSettings::getInstance()->setValue(WEB_ENGINE, web_engine);
+ }
+ }
+}
+
+//使用GSetting获取当前窗口应该使用的透明度
+double MainWindow::getTransparentData() {
+ return GlobalSettings::getInstance()->getValue(TRANSPARENCY_KEY).toDouble();
+}
+
+void MainWindow::initTimer() {
+ m_askTimer = new QTimer;
+ m_askTimer->setInterval(ASK_INDEX_TIME);
+ connect(m_askTimer, &QTimer::timeout, this, [ = ]() {
+ if(this->isVisible()) {
+ m_isAskDialogVisible = true;
+ m_askDialog->show();
+ m_currentSearchAsked = true;
+ }
+ m_askTimer->stop();
+ });
+ m_researchTimer = new QTimer;
+ m_researchTimer->setInterval(RESEARCH_TIME);
+ connect(m_researchTimer, &QTimer::timeout, this, [ = ]() {
+ if(this->isVisible()) {
+ m_searchLayout->reSearch();
+ }
+ m_researchTimer->stop();
+ });
+ connect(m_searchLayout, &SearchBarHLayout::requestSearchKeyword, this, [ = ](QString text) {
+ if(text == "" || text.isEmpty()) {
+ m_askTimer->stop();
+ } else {
+ //允许弹窗且当前次搜索(为关闭主界面,算一次搜索过程)未询问且当前为暴力搜索
+ if(GlobalSettings::getInstance()->getValue(ENABLE_CREATE_INDEX_ASK_DIALOG).toString() != "false" && !m_currentSearchAsked && FileUtils::searchMethod == FileUtils::SearchMethod::DIRECTSEARCH)
+ m_askTimer->start();
+ }
+ });
+}
+
+/**
+ * @brief MainWindow::tryHideMainwindow 尝试隐藏主界面并停止部分未完成的动作,重置部分状态值
+ */
+bool MainWindow::tryHideMainwindow()
+{
+ if (!m_isAskDialogVisible) {
+ qDebug()<<"Mainwindow will be hidden";
+ m_currentSearchAsked = false;
+ this->hide();
+ m_askTimer->stop();
+ m_researchTimer->stop();
+ Q_EMIT m_stackedWidget->stopSearch();
+ return true;
+ } else {
+ //有上层弹窗未关闭,不允许隐藏主界面
+ qWarning()<<"There is a dialog onside, so that mainwindow can not be hidden.";
+ return false;
+ }
+}
+
+/**
+ * @brief MainWindow::setSearchMethod 设置搜索模式
+ * @param is_index_search true为索引搜索,false为暴力搜索
+ */
+void MainWindow::setSearchMethod(const bool &is_index_search) {
+ if(is_index_search) {
+ //调用创建索引接口
+ Q_EMIT this->searchMethodChanged(FileUtils::SearchMethod::INDEXSEARCH);
+ //创建索引十秒后重新搜索一次(如果用户十秒内没有退出搜索界面且没有重新搜索)
+ m_researchTimer->start();
+ } else {
+ Q_EMIT this->searchMethodChanged(FileUtils::SearchMethod::DIRECTSEARCH);
+ m_researchTimer->stop();
+ }
+}
+
+/**
+ * @brief MainWindow::keyPressEvent 按esc键关闭主界面
+ * @param event
+ */
+void MainWindow::keyPressEvent(QKeyEvent *event)
+{
+ if (event->key() == Qt::Key_Escape) {
+ tryHideMainwindow();
+ }
+ return QWidget::keyPressEvent(event);
+}
+
+bool MainWindow::eventFilter(QObject *watched, QEvent *event)
+{
+ if (event->type() == QEvent::ActivationChange) {
+ if(QApplication::activeWindow() != this) {
+ tryHideMainwindow();
+ }
+ }
+ return QMainWindow::eventFilter(watched,event);
+}
+
+void MainWindow::paintEvent(QPaintEvent *event) {
+ Q_UNUSED(event)
+
+ double trans = getTransparentData();
+ QStyleOption opt;
+ opt.init(this);
+ QPainter p(this);
+ style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
+
+ QRect rect = this->rect();
+ p.setRenderHint(QPainter::Antialiasing); // 反锯齿;
+ p.setBrush(opt.palette.color(QPalette::Base));
+ p.setOpacity(trans);
+ p.setPen(Qt::NoPen);
+// p.drawRoundedRect(rect, 6, 6);
+ p.drawRect(rect);
+ return QWidget::paintEvent(event);
+
+}
diff --git a/frontend/mainwindow.h b/frontend/mainwindow.h
new file mode 100644
index 0000000..1951fa2
--- /dev/null
+++ b/frontend/mainwindow.h
@@ -0,0 +1,153 @@
+/*
+ *
+ * Copyright (C) 2020, KylinSoft Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Authors: zhangjiaping
+ * Modified by: zhangpengfei
+ *
+ */
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "index/index-generator.h"
+#include "libsearch.h"
+#include "create-index-ask-dialog.h"
+#include "stacked-widget.h"
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
+#include "xatom-helper.h"
+#endif
+#if (QT_VERSION < QT_VERSION_CHECK(5, 12, 0))
+#include "settings-widget.h"
+#endif
+
+namespace Zeeker {
+class SearchResult;
+class MainWindow : public QMainWindow {
+ friend class SearchResult;
+ Q_OBJECT
+
+public:
+ explicit MainWindow(QWidget *parent = nullptr);
+ ~MainWindow();
+ /**
+ * @brief Load the main window
+ */
+
+ // The position which mainwindow shows follow the ukui-panel.
+ void moveToPanel();
+
+ // The position which mainwindow shows in the center of screen where the cursor in.
+ void centerToScreen(QWidget* widget);
+ void initGsettings();
+
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
+ MotifWmHints m_hints;
+#endif
+
+private:
+
+ QFrame * m_frame = nullptr; // Main frame
+ QFrame * m_titleFrame = nullptr; // Title bar frame
+ QHBoxLayout * m_titleLyt = nullptr; // Title layout
+ QLabel * m_iconLabel = nullptr; // Icon lable
+ QLabel * m_titleLabel = nullptr; // Title lable
+ QPushButton * m_settingsBtn = nullptr; // Menu button
+ StackedWidget * m_stackedWidget = nullptr; // Stacked widget
+ SearchBarHLayout * m_searchLayout = nullptr; // Search bar layout
+ SeachBarWidget * m_searchWidget = nullptr; // Search bar
+#if (QT_VERSION < QT_VERSION_CHECK(5, 12, 0))
+ SettingsWidget * m_settingsWidget = nullptr; // Settings Widget
+#endif
+
+ QStringList m_dirList;
+
+ QQueue *m_search_result_file = nullptr;
+ QQueue *m_search_result_dir = nullptr;
+ QQueue> *m_search_result_content = nullptr;
+
+ QSystemTrayIcon *m_sys_tray_icon = nullptr;
+ CreateIndexAskDialog * m_askDialog = nullptr;
+ bool m_isAskDialogVisible = false;
+
+ QTimer * m_askTimer = nullptr; //询问是否创建索引弹窗弹出的计时器
+ QTimer * m_researchTimer = nullptr; //创建索引后重新执行一次搜索的计时器
+ bool m_currentSearchAsked = false; //本次搜索是否已经询问过是否创建索引了
+ QGSettings * m_search_gsettings = nullptr;
+
+ SearchMethodManager m_searchMethodManager;
+
+ void setSearchMethod(const bool&);
+ double getTransparentData();
+ void initTimer();
+ bool tryHideMainwindow();
+ void setSearchMethodConfig(const bool&, const bool&);
+
+protected:
+ void paintEvent(QPaintEvent *);
+ void keyPressEvent(QKeyEvent *event);
+ bool eventFilter(QObject *watched, QEvent *event) override;
+ void initUi();
+ void initConnections();
+
+Q_SIGNALS:
+ void searchMethodChanged(FileUtils::SearchMethod);
+ void webEngineChanged();
+
+public Q_SLOTS:
+ /**
+ * @brief Monitor screen resolution
+ * @param rect: Screen resolution
+ */
+ void monitorResolutionChange(QRect rect);
+ /**
+ * @brief Monitor primary screen changes
+ * @param screen: Primary screen
+ */
+ void primaryScreenChangedSlot(QScreen *screen);
+
+ void bootOptionsFilter(QString opt); // 过滤终端命令
+ void clearSearchResult(); //清空搜索结果
+ void trayIconActivatedSlot(QSystemTrayIcon::ActivationReason reason);
+ void settingsBtnClickedSlot();
+ void searchKeywordSlot(const QString&);
+};
+}
+
+#endif // MAINWINDOW_H
diff --git a/frontend/model/README.md b/frontend/model/README.md
new file mode 100644
index 0000000..e69de29
diff --git a/frontend/model/model.pri b/frontend/model/model.pri
new file mode 100644
index 0000000..05b21b8
--- /dev/null
+++ b/frontend/model/model.pri
@@ -0,0 +1,9 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/search-result-manager.h \
+ $$PWD/search-result-model.h \
+
+SOURCES += \
+ $$PWD/search-result-manager.cpp \
+ $$PWD/search-result-model.cpp \
diff --git a/frontend/model/search-result-manager.cpp b/frontend/model/search-result-manager.cpp
new file mode 100644
index 0000000..fbcbc9a
--- /dev/null
+++ b/frontend/model/search-result-manager.cpp
@@ -0,0 +1,126 @@
+/*
+ *
+ * Copyright (C) 2020, KylinSoft Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Authors: zhangjiaping
+ *
+ */
+#include "search-result-manager.h"
+
+using namespace Zeeker;
+SearchResultManager::SearchResultManager(const QString& plugin_id, QObject *parent) : QObject(parent)
+{
+ m_plugin_id = plugin_id;
+ m_result_queue = new QQueue;
+ m_get_result_thread = new ReceiveResultThread(m_result_queue);
+ initConnections();
+}
+
+void SearchResultManager::startSearch(const QString &keyword)
+{
+ //NEW_TODO 加锁?停止线程?重新搜索?
+ stopSearch();
+ m_result_queue->clear();
+ SearchPluginIface *plugin = SearchPluginManager::getInstance()->getPlugin(m_plugin_id);
+// plugin->KeywordSearch(keyword, m_result_queue);
+ /*********************测试用数据*********************/
+ SearchPluginIface::ResultInfo test_info;
+ if (m_plugin_id == "File") {
+ test_info.icon = QIcon::fromTheme("ukui-control-center");
+ test_info.name = "搜索";
+ QVector desc;
+ SearchPluginIface::DescriptionInfo desc_1;
+ desc_1.key = "描述";
+ desc_1.value = "控制面板搜索插件";
+ desc.append(desc_1);
+ QMap actions;
+ actions.insert("ukcc-search","打开");
+ test_info.description = desc;
+ test_info.actionMap = actions;
+ m_result_queue->append(test_info);
+ } else {
+ test_info.icon = QIcon::fromTheme("ukui-control-center");
+ test_info.name = "文件";
+ QVector desc;
+ SearchPluginIface::DescriptionInfo desc_1;
+ SearchPluginIface::DescriptionInfo desc_2;
+ desc_1.key = "描述";
+ desc_1.value = "一个文件";
+ desc_2.key = "路径";
+ desc_2.value = "一个路径";
+ desc.append(desc_1);
+ desc.append(desc_2);
+ QMap actions;
+ actions.insert("file1","打开");
+ actions.insert("file2","复制路径");
+ test_info.description = desc;
+ test_info.actionMap = actions;
+ m_result_queue->append(test_info);
+ }
+ /********************测试用数据********************/
+
+ qWarning()<start by others";
+ m_get_result_thread->start();
+}
+
+/**
+ * @brief SearchResultManager::stopSearch 停止搜索,开始一次新搜索前或主界面退出时执行
+ */
+void SearchResultManager::stopSearch()
+{
+ if(m_get_result_thread->isRunning()) {
+ qWarning()<stopped by others";
+ m_get_result_thread->requestInterruption();
+ m_get_result_thread->quit();
+ }
+}
+
+void SearchResultManager::initConnections()
+{
+ connect(m_get_result_thread, &ReceiveResultThread::gotResultInfo, this, &SearchResultManager::gotResultInfo);
+}
+
+ReceiveResultThread::ReceiveResultThread(QQueue * result_queue, QObject *parent)
+{
+ m_result_queue = result_queue;
+}
+
+//NEW_TODO 还未对队列加锁
+void ReceiveResultThread::run()
+{
+ QTimer * m_timer = new QTimer;
+ m_timer->setInterval(3000);
+ bool is_empty;
+ while(!isInterruptionRequested()) {
+ is_empty = false;
+ if(!m_result_queue->isEmpty()) {
+ Q_EMIT this->gotResultInfo(m_result_queue->dequeue());
+ } else {
+ is_empty = true;
+ }
+ if(m_timer->isActive() && m_timer->remainingTime() < 0.01) {
+ this->requestInterruption();
+ qWarning()<<"-------------->stopped by self";
+ }
+ if(is_empty && !m_timer->isActive()) {
+ m_timer->start();
+ } else if(!is_empty) {
+ m_timer->stop();
+ } else {
+ msleep(100);
+ }
+ }
+}
diff --git a/frontend/model/search-result-manager.h b/frontend/model/search-result-manager.h
new file mode 100644
index 0000000..1941173
--- /dev/null
+++ b/frontend/model/search-result-manager.h
@@ -0,0 +1,71 @@
+/*
+ *
+ * Copyright (C) 2020, KylinSoft Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Authors: zhangjiaping
+ *
+ */
+#ifndef SEARCHRESULTMANAGER_H
+#define SEARCHRESULTMANAGER_H
+
+#include
+#include
+#include
+#include
+#include "pluginmanage/search-plugin-manager.h"
+
+namespace Zeeker {
+
+class ReceiveResultThread : public QThread {
+ Q_OBJECT
+public:
+ ReceiveResultThread(QQueue * result_queue, QObject * parent = nullptr);
+ ~ReceiveResultThread() = default;
+protected:
+ void run() override;
+
+private:
+ QQueue * m_result_queue;
+
+
+Q_SIGNALS:
+ void gotResultInfo(const SearchPluginIface::ResultInfo&);
+
+};
+
+class SearchResultManager : public QObject
+{
+ Q_OBJECT
+public:
+ explicit SearchResultManager(const QString &plugin_id, QObject *parent = nullptr);
+ ~SearchResultManager() = default;
+
+public Q_SLOTS:
+ void startSearch(const QString &);
+ void stopSearch();
+
+private:
+ void initConnections();
+ QString m_plugin_id;
+ QQueue * m_result_queue;
+ ReceiveResultThread * m_get_result_thread = nullptr;
+
+Q_SIGNALS:
+ void gotResultInfo(const SearchPluginIface::ResultInfo&);
+};
+}
+
+#endif // SEARCHRESULTMANAGER_H
diff --git a/frontend/model/search-result-model.cpp b/frontend/model/search-result-model.cpp
new file mode 100644
index 0000000..922515f
--- /dev/null
+++ b/frontend/model/search-result-model.cpp
@@ -0,0 +1,102 @@
+/*
+ *
+ * Copyright (C) 2020, KylinSoft Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Authors: zhangjiaping
+ *
+ */
+#include "search-result-model.h"
+using namespace Zeeker;
+
+SearchResultModel::SearchResultModel(const QString &plugin_id)
+{
+ m_item = new SearchResultItem;
+ m_plugin_id = plugin_id;
+ m_search_manager = new SearchResultManager(plugin_id);
+ initConnections();
+}
+
+QModelIndex SearchResultModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if(row < 0 || row > m_item->m_result_info_list.length() - 1)
+ return QModelIndex();
+// QVector * m_info = &m_result_info_list;
+ return createIndex(row, column, m_item);
+}
+
+QModelIndex SearchResultModel::parent(const QModelIndex &child) const
+{
+ return QModelIndex();
+}
+
+int SearchResultModel::rowCount(const QModelIndex &index) const
+{
+ return index.isValid() ? 0 : m_item->m_result_info_list.length();
+}
+
+int SearchResultModel::columnCount(const QModelIndex &index) const
+{
+ return index.isValid() ? 0 : 1;
+}
+
+QVariant SearchResultModel::data(const QModelIndex &index, int role) const
+{
+ if(!index.isValid())
+ return QVariant();
+ switch(role) {
+ case Qt::DecorationRole: {
+ return m_item->m_result_info_list.at(index.row()).icon;
+ }
+ case Qt::DisplayRole: {
+ return m_item->m_result_info_list.at(index.row()).name;
+ }
+ default:
+ return QVariant();
+ }
+ return QVariant();
+}
+
+//bool SearchResultModel::insertRows(int row, int count, const QModelIndex &parent)
+//{
+// this->beginInsertRows(parent, row, count);
+// this->endInsertRows();
+// return true;
+//}
+
+void SearchResultModel::appendInfo(const SearchPluginIface::ResultInfo &info)
+{
+ this->beginResetModel();
+ qWarning()<<"Got a result. name ="<m_result_info_list.append(info);
+ this->endResetModel();
+}
+
+void SearchResultModel::initConnections()
+{
+ connect(this, &SearchResultModel::startSearch, m_search_manager, &SearchResultManager::startSearch);
+ connect(this, &SearchResultModel::stopSearch, m_search_manager, &SearchResultManager::stopSearch);
+ connect(m_search_manager, &SearchResultManager::gotResultInfo, this, &SearchResultModel::appendInfo);
+}
+
+const SearchPluginIface::ResultInfo &SearchResultModel::getInfo(const QModelIndex &index)
+{
+ return m_item->m_result_info_list.at(index.row());
+}
+
+SearchResultItem::SearchResultItem(QObject *parent) : QObject(parent)
+{
+
+}
diff --git a/frontend/model/search-result-model.h b/frontend/model/search-result-model.h
new file mode 100644
index 0000000..f6d9bca
--- /dev/null
+++ b/frontend/model/search-result-model.h
@@ -0,0 +1,67 @@
+/*
+ *
+ * Copyright (C) 2020, KylinSoft Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Authors: zhangjiaping
+ *
+ */
+#ifndef SEARCHRESULTMODEL_H
+#define SEARCHRESULTMODEL_H
+#include
+#include "search-result-manager.h"
+
+namespace Zeeker {
+
+class SearchResultItem : public QObject {
+ friend class SearchResultModel;
+ Q_OBJECT
+public:
+ explicit SearchResultItem(QObject *parent = nullptr);
+ ~SearchResultItem() = default;
+private:
+ //此插件所有搜索结果<具体信息>
+ QVector m_result_info_list;
+};
+class SearchResultModel : public QAbstractItemModel
+{
+ Q_OBJECT
+public:
+ SearchResultModel(const QString &plugin_id);
+ ~SearchResultModel() = default;
+ QModelIndex index(int row, int column, const QModelIndex &parent) const override;
+ QModelIndex parent(const QModelIndex &child) const override;
+ int rowCount(const QModelIndex &parent) const override;
+ int columnCount(const QModelIndex &parent) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+// bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex())override;
+ const SearchPluginIface::ResultInfo & getInfo(const QModelIndex&);
+
+public Q_SLOTS:
+ void appendInfo(const SearchPluginIface::ResultInfo &);
+
+Q_SIGNALS:
+ void startSearch(const QString &);
+ void stopSearch();
+
+private:
+ void initConnections();
+ SearchResultItem * m_item = nullptr;
+ QString m_plugin_id;
+ SearchResultManager * m_search_manager = nullptr;
+};
+}
+
+#endif // SEARCHRESULTMODEL_H
diff --git a/frontend/res/icons/close.svg b/frontend/res/icons/close.svg
new file mode 100644
index 0000000..06a5a23
--- /dev/null
+++ b/frontend/res/icons/close.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/res/icons/desktop.png b/frontend/res/icons/desktop.png
new file mode 100644
index 0000000..92c38ee
Binary files /dev/null and b/frontend/res/icons/desktop.png differ
diff --git a/frontend/res/icons/edit-find-symbolic.svg b/frontend/res/icons/edit-find-symbolic.svg
new file mode 100644
index 0000000..82e1356
--- /dev/null
+++ b/frontend/res/icons/edit-find-symbolic.svg
@@ -0,0 +1,14 @@
+
+
\ No newline at end of file
diff --git a/frontend/res/icons/net-disconnected.svg b/frontend/res/icons/net-disconnected.svg
new file mode 100644
index 0000000..4d5f99f
--- /dev/null
+++ b/frontend/res/icons/net-disconnected.svg
@@ -0,0 +1,30 @@
+
+
\ No newline at end of file
diff --git a/frontend/resource.qrc b/frontend/resource.qrc
new file mode 100644
index 0000000..018ebdd
--- /dev/null
+++ b/frontend/resource.qrc
@@ -0,0 +1,9 @@
+
+
+ res/icons/edit-find-symbolic.svg
+ res/icons/desktop.png
+ res/icons/close.svg
+ res/qt-translations/qt_zh_CN.qm
+ res/icons/net-disconnected.svg
+
+
diff --git a/frontend/singleapplication/qt-local-peer.cpp b/frontend/singleapplication/qt-local-peer.cpp
new file mode 100644
index 0000000..4a62537
--- /dev/null
+++ b/frontend/singleapplication/qt-local-peer.cpp
@@ -0,0 +1,205 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies)
+** 2020 KylinSoft Co., Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+**
+** This file is part of the Qt Solutions component.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+**
+****************************************************************************/
+
+
+#include "qt-local-peer.h"
+#include
+#include
+#include
+
+#if defined(Q_OS_UNIX)
+#include
+#include
+#include
+#endif
+
+namespace QtLP_Private {
+#include "qt-locked-file.cpp"
+#include "qt-locked-file-unix.cpp"
+}
+
+const char* QtLocalPeer::ack = "ack";
+
+QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId)
+ : QObject(parent), id(appId) {
+ QString prefix = id;
+ if(id.isEmpty()) {
+ id = QCoreApplication::applicationFilePath();
+#if defined(Q_OS_WIN)
+ id = id.toLower();
+#endif
+ prefix = id.section(QLatin1Char('/'), -1); //完整路径按‘/’分隔后取最后一个字段
+ }
+ prefix.remove(QRegExp("[^a-zA-Z]")); //去掉名称中的非字母
+ prefix.truncate(6); //取前六位
+
+ QByteArray idc = id.toUtf8();
+ quint16 idNum = qChecksum(idc.constData(), idc.size());
+ socketName = QLatin1String("qtsingleapp-") + prefix
+ + QLatin1Char('-') + QString::number(idNum, 16);
+
+#if defined(Q_OS_WIN)
+ if(!pProcessIdToSessionId) {
+ QLibrary lib("kernel32");
+ pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId");
+ }
+ if(pProcessIdToSessionId) {
+ DWORD sessionId = 0;
+ pProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
+ socketName += QLatin1Char('-') + QString::number(sessionId, 16);
+ }
+#else
+ socketName += QLatin1Char('-') + QString::number(::getuid(), 16);
+#endif
+
+ server = new QLocalServer(this);
+ QString lockName = QDir(QDir::tempPath()).absolutePath()
+ + QLatin1Char('/') + socketName
+ + QLatin1String("-lockfile"); //tmp目录下的锁文件
+ lockFile.setFileName(lockName);
+ lockFile.open(QIODevice::ReadWrite);
+}
+
+
+
+bool QtLocalPeer::isClient() {
+ if(lockFile.isLocked())
+ return false;
+
+ if(!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false))
+ return true;
+
+ //由于文件锁的存在,仅当本进程第一次启动时能执行到此并使server进行监听和关联槽函数
+ bool res = server->listen(socketName);
+#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0))
+ // ### Workaround
+ if(!res && server->serverError() == QAbstractSocket::AddressInUseError) {
+ QFile::remove(QDir::cleanPath(QDir::tempPath()) + QLatin1Char('/') + socketName);
+ res = server->listen(socketName);
+ }
+#endif
+ if(!res)
+ qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString()));
+ QObject::connect(server, &QLocalServer::newConnection, this, &QtLocalPeer::receiveConnection);
+ return false;
+}
+
+
+bool QtLocalPeer::sendMessage(const QString &message, int timeout) {
+ if(!isClient())
+ return false;
+
+ QLocalSocket socket;
+ bool connOk = false;
+ for(int i = 0; i < 2; i++) {
+ // Try twice, in case the other instance is just starting up
+ socket.connectToServer(socketName);
+ connOk = socket.waitForConnected(timeout / 2);
+ if(connOk || i)
+ break;
+ int ms = 250;
+#if defined(Q_OS_WIN)
+ Sleep(DWORD(ms));
+#else
+ struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
+ nanosleep(&ts, NULL);
+#endif
+ }
+ if(!connOk)
+ return false;
+
+ QByteArray uMsg(message.toUtf8());
+ QDataStream ds(&socket);
+ ds.writeBytes(uMsg.constData(), uMsg.size());
+ bool res = socket.waitForBytesWritten(timeout);
+ if(res) {
+ res &= socket.waitForReadyRead(timeout); // wait for ack
+ if(res)
+ res &= (socket.read(qstrlen(ack)) == ack);
+ }
+ return res;
+}
+
+/**
+ * @brief QtLocalPeer::receiveConnection 当新进程启动时,会尝试连接此进程server,server接收到newConnection信号并触发此槽函数
+ */
+void QtLocalPeer::receiveConnection() {
+ QLocalSocket* socket = server->nextPendingConnection(); //获取新进程的socket
+ if(!socket)
+ return;
+
+ while(true) {
+ if(socket->state() == QLocalSocket::UnconnectedState) {
+ qWarning("QtLocalPeer: Peer disconnected");
+ delete socket;
+ return;
+ }
+ if(socket->bytesAvailable() >= qint64(sizeof(quint32)))
+ break;
+ socket->waitForReadyRead();
+ }
+
+ QDataStream ds(socket);
+ QByteArray uMsg;
+ quint32 remaining;
+ ds >> remaining;
+ uMsg.resize(remaining);
+ int got = 0;
+ char* uMsgBuf = uMsg.data();
+ do {
+ got = ds.readRawData(uMsgBuf, remaining);
+ remaining -= got;
+ uMsgBuf += got;
+ } while(remaining && got >= 0 && socket->waitForReadyRead(2000));
+ if(got < 0) {
+ qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData());
+ delete socket;
+ return;
+ }
+ QString message(QString::fromUtf8(uMsg));
+ socket->write(ack, qstrlen(ack));
+ socket->waitForBytesWritten(1000);
+ socket->waitForDisconnected(1000); // make sure client reads ack
+ delete socket;
+ Q_EMIT messageReceived(message); //获取新进程的启动信息并作为信号发送给前端
+}
diff --git a/frontend/singleapplication/qt-local-peer.h b/frontend/singleapplication/qt-local-peer.h
new file mode 100644
index 0000000..883aec2
--- /dev/null
+++ b/frontend/singleapplication/qt-local-peer.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies)
+** 2020 KylinSoft Co., Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+**
+** This file is part of the Qt Solutions component.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+**
+****************************************************************************/
+
+#ifndef QTLOCALPEER_H
+#define QTLOCALPEER_H
+
+#include
+#include
+#include
+
+#include "qt-locked-file.h"
+
+class QtLocalPeer : public QObject {
+ Q_OBJECT
+
+public:
+ QtLocalPeer(QObject *parent = 0, const QString &appId = QString());
+ bool isClient();
+ bool sendMessage(const QString &message, int timeout);
+ QString applicationId() const {
+ return id;
+ }
+
+Q_SIGNALS:
+ void messageReceived(const QString &message);
+
+protected Q_SLOTS:
+ void receiveConnection();
+
+protected:
+ QString id;
+ QString socketName;
+ QLocalServer* server;
+ QtLP_Private::QtLockedFile lockFile;
+
+private:
+ static const char* ack;
+};
+
+#endif // QTLOCALPEER_H
diff --git a/frontend/singleapplication/qt-locked-file-unix.cpp b/frontend/singleapplication/qt-locked-file-unix.cpp
new file mode 100644
index 0000000..5147252
--- /dev/null
+++ b/frontend/singleapplication/qt-locked-file-unix.cpp
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies)
+** 2020 KylinSoft Co., Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Solutions component.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include
+#include
+#include
+#include
+
+#include "qt-locked-file.h"
+
+bool QtLockedFile::lock(LockMode mode, bool block) {
+ if(!isOpen()) {
+ qWarning("QtLockedFile::lock(): file is not opened");
+ return false;
+ }
+
+ if(mode == NoLock)
+ return unlock();
+
+ if(mode == m_lock_mode)
+ return true;
+
+ if(m_lock_mode != NoLock)
+ unlock();
+
+ struct flock fl;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK;
+ int cmd = block ? F_SETLKW : F_SETLK;
+ int ret = fcntl(handle(), cmd, &fl);
+
+ if(ret == -1) {
+ if(errno != EINTR && errno != EAGAIN)
+ qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno));
+ return false;
+ }
+
+
+ m_lock_mode = mode;
+ return true;
+}
+
+
+bool QtLockedFile::unlock() {
+ if(!isOpen()) {
+ qWarning("QtLockedFile::unlock(): file is not opened");
+ return false;
+ }
+
+ if(!isLocked())
+ return true;
+
+ struct flock fl;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_type = F_UNLCK;
+ int ret = fcntl(handle(), F_SETLKW, &fl);
+
+ if(ret == -1) {
+ qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno));
+ return false;
+ }
+
+ m_lock_mode = NoLock;
+ return true;
+}
+
+QtLockedFile::~QtLockedFile() {
+ if(isOpen())
+ unlock();
+}
+
diff --git a/frontend/singleapplication/qt-locked-file.cpp b/frontend/singleapplication/qt-locked-file.cpp
new file mode 100644
index 0000000..707b4cf
--- /dev/null
+++ b/frontend/singleapplication/qt-locked-file.cpp
@@ -0,0 +1,189 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies)
+** 2020 KylinSoft Co., Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Solutions component.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qt-locked-file.h"
+
+/*!
+ \class QtLockedFile
+
+ \brief The QtLockedFile class extends QFile with advisory locking
+ functions.
+
+ A file may be locked in read or write mode. Multiple instances of
+ \e QtLockedFile, created in multiple processes running on the same
+ machine, may have a file locked in read mode. Exactly one instance
+ may have it locked in write mode. A read and a write lock cannot
+ exist simultaneously on the same file.
+
+ The file locks are advisory. This means that nothing prevents
+ another process from manipulating a locked file using QFile or
+ file system functions offered by the OS. Serialization is only
+ guaranteed if all processes that access the file use
+ QLockedFile. Also, while holding a lock on a file, a process
+ must not open the same file again (through any API), or locks
+ can be unexpectedly lost.
+
+ The lock provided by an instance of \e QtLockedFile is released
+ whenever the program terminates. This is true even when the
+ program crashes and no destructors are called.
+*/
+
+/*! \enum QtLockedFile::LockMode
+
+ This enum describes the available lock modes.
+
+ \value ReadLock A read lock.
+ \value WriteLock A write lock.
+ \value NoLock Neither a read lock nor a write lock.
+*/
+
+/*!
+ Constructs an unlocked \e QtLockedFile object. This constructor
+ behaves in the same way as \e QFile::QFile().
+
+ \sa QFile::QFile()
+*/
+QtLockedFile::QtLockedFile()
+ : QFile() {
+#ifdef Q_OS_WIN
+ wmutex = 0;
+ rmutex = 0;
+#endif
+ m_lock_mode = NoLock;
+}
+
+/*!
+ Constructs an unlocked QtLockedFile object with file \a name. This
+ constructor behaves in the same way as \e QFile::QFile(const
+ QString&).
+
+ \sa QFile::QFile()
+*/
+QtLockedFile::QtLockedFile(const QString &name)
+ : QFile(name) {
+#ifdef Q_OS_WIN
+ wmutex = 0;
+ rmutex = 0;
+#endif
+ m_lock_mode = NoLock;
+}
+
+/*!
+ Opens the file in OpenMode \a mode.
+
+ This is identical to QFile::open(), with the one exception that the
+ Truncate mode flag is disallowed. Truncation would conflict with the
+ advisory file locking, since the file would be modified before the
+ write lock is obtained. If truncation is required, use resize(0)
+ after obtaining the write lock.
+
+ Returns true if successful; otherwise false.
+
+ \sa QFile::open(), QFile::resize()
+*/
+bool QtLockedFile::open(OpenMode mode) {
+ if(mode & QIODevice::Truncate) {
+ qWarning("QtLockedFile::open(): Truncate mode not allowed.");
+ return false;
+ }
+ return QFile::open(mode);
+}
+
+/*!
+ Returns \e true if this object has a in read or write lock;
+ otherwise returns \e false.
+
+ \sa lockMode()
+*/
+bool QtLockedFile::isLocked() const {
+ return m_lock_mode != NoLock;
+}
+
+/*!
+ Returns the type of lock currently held by this object, or \e
+ QtLockedFile::NoLock.
+
+ \sa isLocked()
+*/
+QtLockedFile::LockMode QtLockedFile::lockMode() const {
+ return m_lock_mode;
+}
+
+/*!
+ \fn bool QtLockedFile::lock(LockMode mode, bool block = true)
+
+ Obtains a lock of type \a mode. The file must be opened before it
+ can be locked.
+
+ If \a block is true, this function will block until the lock is
+ aquired. If \a block is false, this function returns \e false
+ immediately if the lock cannot be aquired.
+
+ If this object already has a lock of type \a mode, this function
+ returns \e true immediately. If this object has a lock of a
+ different type than \a mode, the lock is first released and then a
+ new lock is obtained.
+
+ This function returns \e true if, after it executes, the file is
+ locked by this object, and \e false otherwise.
+
+ \sa unlock(), isLocked(), lockMode()
+*/
+
+/*!
+ \fn bool QtLockedFile::unlock()
+
+ Releases a lock.
+
+ If the object has no lock, this function returns immediately.
+
+ This function returns \e true if, after it executes, the file is
+ not locked by this object, and \e false otherwise.
+
+ \sa lock(), isLocked(), lockMode()
+*/
+
+/*!
+ \fn QtLockedFile::~QtLockedFile()
+
+ Destroys the \e QtLockedFile object. If any locks were held, they
+ are released.
+*/
diff --git a/frontend/singleapplication/qt-locked-file.h b/frontend/singleapplication/qt-locked-file.h
new file mode 100644
index 0000000..332d648
--- /dev/null
+++ b/frontend/singleapplication/qt-locked-file.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies)
+** 2020 KylinSoft Co., Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Solutions component.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTLOCKEDFILE_H
+#define QTLOCKEDFILE_H
+
+#include
+#ifdef Q_OS_WIN
+#include
+#endif
+
+#if defined(Q_OS_WIN)
+# if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT)
+# define QT_QTLOCKEDFILE_EXPORT
+# elif defined(QT_QTLOCKEDFILE_IMPORT)
+# if defined(QT_QTLOCKEDFILE_EXPORT)
+# undef QT_QTLOCKEDFILE_EXPORT
+# endif
+# define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport)
+# elif defined(QT_QTLOCKEDFILE_EXPORT)
+# undef QT_QTLOCKEDFILE_EXPORT
+# define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport)
+# endif
+#else
+# define QT_QTLOCKEDFILE_EXPORT
+#endif
+
+namespace QtLP_Private {
+
+class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile {
+public:
+ enum LockMode { NoLock = 0, ReadLock, WriteLock };
+
+ QtLockedFile();
+ QtLockedFile(const QString &name);
+ ~QtLockedFile();
+
+ bool open(OpenMode mode);
+
+ bool lock(LockMode mode, bool block = true);
+ bool unlock();
+ bool isLocked() const;
+ LockMode lockMode() const;
+
+private:
+#ifdef Q_OS_WIN
+ Qt::HANDLE wmutex;
+ Qt::HANDLE rmutex;
+ QVector rmutexes;
+ QString mutexname;
+
+ Qt::HANDLE getMutexHandle(int idx, bool doCreate);
+ bool waitMutex(Qt::HANDLE mutex, bool doBlock);
+
+#endif
+ LockMode m_lock_mode;
+};
+}
+#endif
diff --git a/frontend/singleapplication/qt-single-application.cpp b/frontend/singleapplication/qt-single-application.cpp
new file mode 100644
index 0000000..95b1900
--- /dev/null
+++ b/frontend/singleapplication/qt-single-application.cpp
@@ -0,0 +1,357 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies)
+** 2020 KylinSoft Co., Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+**
+** This file is part of the Qt Solutions component.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+**
+****************************************************************************/
+
+
+#include "qt-single-application.h"
+#include "qt-local-peer.h"
+#include
+#include
+#include
+#include
+#include
+#include "mainwindow.h"
+
+
+/*!
+ \class QtSingleApplication qtsingleapplication.h
+ \brief The QtSingleApplication class provides an API to detect and
+ communicate with running instances of an application.
+
+ This class allows you to create applications where only one
+ instance should be running at a time. I.e., if the user tries to
+ launch another instance, the already running instance will be
+ activated instead. Another usecase is a client-server system,
+ where the first started instance will assume the role of server,
+ and the later instances will act as clients of that server.
+
+ By default, the full path of the executable file is used to
+ determine whether two processes are instances of the same
+ application. You can also provide an explicit identifier string
+ that will be compared instead.
+
+ The application should create the QtSingleApplication object early
+ in the startup phase, and call isRunning() to find out if another
+ instance of this application is already running. If isRunning()
+ returns false, it means that no other instance is running, and
+ this instance has assumed the role as the running instance. In
+ this case, the application should continue with the initialization
+ of the application user interface before entering the event loop
+ with exec(), as normal.
+
+ The messageReceived() signal will be emitted when the running
+ application receives messages from another instance of the same
+ application. When a message is received it might be helpful to the
+ user to raise the application so that it becomes visible. To
+ facilitate this, QtSingleApplication provides the
+ setActivationWindow() function and the activateWindow() slot.
+
+ If isRunning() returns true, another instance is already
+ running. It may be alerted to the fact that another instance has
+ started by using the sendMessage() function. Also data such as
+ startup parameters (e.g. the name of the file the user wanted this
+ new instance to open) can be passed to the running instance with
+ this function. Then, the application should terminate (or enter
+ client mode).
+
+ If isRunning() returns true, but sendMessage() fails, that is an
+ indication that the running instance is frozen.
+
+ Here's an example that shows how to convert an existing
+ application to use QtSingleApplication. It is very simple and does
+ not make use of all QtSingleApplication's functionality (see the
+ examples for that).
+
+ \code
+ // Original
+ int main(int argc, char **argv)
+ {
+ QApplication app(argc, argv);
+
+ MyMainWidget mmw;
+ mmw.show();
+ return app.exec();
+ }
+
+ // Single instance
+ int main(int argc, char **argv)
+ {
+ QtSingleApplication app(argc, argv);
+
+ if (app.isRunning())
+ return !app.sendMessage(someDataString);
+
+ MyMainWidget mmw;
+ app.setActivationWindow(&mmw);
+ mmw.show();
+ return app.exec();
+ }
+ \endcode
+
+ Once this QtSingleApplication instance is destroyed (normally when
+ the process exits or crashes), when the user next attempts to run the
+ application this instance will not, of course, be encountered. The
+ next instance to call isRunning() or sendMessage() will assume the
+ role as the new running instance.
+
+ For console (non-GUI) applications, QtSingleCoreApplication may be
+ used instead of this class, to avoid the dependency on the QtGui
+ library.
+
+ \sa QtSingleCoreApplication
+*/
+
+
+void QtSingleApplication::sysInit(const QString &appId) {
+ actWin = 0;
+ peer = new QtLocalPeer(this, appId);
+// connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&)));
+ connect(peer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::messageReceived);
+}
+
+
+/*!
+ Creates a QtSingleApplication object. The application identifier
+ will be QCoreApplication::applicationFilePath(). \a argc, \a
+ argv, and \a GUIenabled are passed on to the QAppliation constructor.
+
+ If you are creating a console application (i.e. setting \a
+ GUIenabled to false), you may consider using
+ QtSingleCoreApplication instead.
+*/
+
+QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled)
+ : QApplication(argc, argv, GUIenabled) {
+ sysInit();
+}
+
+
+/*!
+ Creates a QtSingleApplication object with the application
+ identifier \a appId. \a argc and \a argv are passed on to the
+ QAppliation constructor.
+*/
+
+QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv)
+ : QApplication(argc, argv) {
+ sysInit(appId);
+}
+
+#if QT_VERSION < 0x050000
+
+/*!
+ Creates a QtSingleApplication object. The application identifier
+ will be QCoreApplication::applicationFilePath(). \a argc, \a
+ argv, and \a type are passed on to the QAppliation constructor.
+*/
+QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type)
+ : QApplication(argc, argv, type) {
+ sysInit();
+}
+
+
+# if defined(Q_WS_X11)
+/*!
+ Special constructor for X11, ref. the documentation of
+ QApplication's corresponding constructor. The application identifier
+ will be QCoreApplication::applicationFilePath(). \a dpy, \a visual,
+ and \a cmap are passed on to the QApplication constructor.
+*/
+QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE cmap)
+ : QApplication(dpy, visual, cmap) {
+ sysInit();
+}
+
+/*!
+ Special constructor for X11, ref. the documentation of
+ QApplication's corresponding constructor. The application identifier
+ will be QCoreApplication::applicationFilePath(). \a dpy, \a argc, \a
+ argv, \a visual, and \a cmap are passed on to the QApplication
+ constructor.
+*/
+QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap)
+ : QApplication(dpy, argc, argv, visual, cmap) {
+ sysInit();
+}
+
+/*!
+ Special constructor for X11, ref. the documentation of
+ QApplication's corresponding constructor. The application identifier
+ will be \a appId. \a dpy, \a argc, \a
+ argv, \a visual, and \a cmap are passed on to the QApplication
+ constructor.
+*/
+QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap)
+ : QApplication(dpy, argc, argv, visual, cmap) {
+ sysInit(appId);
+}
+# endif // Q_WS_X11
+#endif // QT_VERSION < 0x050000
+
+
+/*!
+ Returns true if another instance of this application is running;
+ otherwise false.
+
+ This function does not find instances of this application that are
+ being run by a different user (on Windows: that are running in
+ another session).
+
+ \sa sendMessage()
+*/
+
+bool QtSingleApplication::isRunning() {
+ return peer->isClient();
+}
+
+
+/*!
+ Tries to send the text \a message to the currently running
+ instance. The QtSingleApplication object in the running instance
+ will emit the messageReceived() signal when it receives the
+ message.
+
+ This function returns true if the message has been sent to, and
+ processed by, the current instance. If there is no instance
+ currently running, or if the running instance fails to process the
+ message within \a timeout milliseconds, this function return false.
+
+ \sa isRunning(), messageReceived()
+*/
+bool QtSingleApplication::sendMessage(const QString &message, int timeout) {
+ return peer->sendMessage(message, timeout);
+}
+
+
+/*!
+ Returns the application identifier. Two processes with the same
+ identifier will be regarded as instances of the same application.
+*/
+QString QtSingleApplication::id() const {
+ return peer->applicationId();
+}
+
+
+/*!
+ Sets the activation window of this application to \a aw. The
+ activation window is the widget that will be activated by
+ activateWindow(). This is typically the application's main window.
+
+ If \a activateOnMessage is true (the default), the window will be
+ activated automatically every time a message is received, just prior
+ to the messageReceived() signal being emitted.
+
+ \sa activateWindow(), messageReceived()
+*/
+
+void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage) {
+ actWin = aw;
+ //目前不需要用到此处的置顶方法,故此信号槽暂时注释掉,若后续需要根据新起进程传递的信号执行部分操作时可以把这里放开
+// if (activateOnMessage)
+// connect(peer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::activateWindow);
+// else
+// disconnect(peer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::activateWindow);
+}
+
+
+/*!
+ Returns the applications activation window if one has been set by
+ calling setActivationWindow(), otherwise returns 0.
+
+ \sa setActivationWindow()
+*/
+QWidget* QtSingleApplication::activationWindow() const {
+ return actWin;
+}
+
+
+/*!
+ De-minimizes, raises, and activates this application's activation window.
+ This function does nothing if no activation window has been set.
+
+ This is a convenience function to show the user that this
+ application instance has been activated when he has tried to start
+ another instance.
+
+ This function should typically be called in response to the
+ messageReceived() signal. By default, that will happen
+ automatically, if an activation window has been set.
+
+ \sa setActivationWindow(), messageReceived(), initialize()
+*/
+void QtSingleApplication::activateWindow() {
+ //单例置顶策略,由于bootOptionsFilter in mainwindow自带置顶策略,故注掉此处
+// if (actWin) {
+// if(this->applicationState() & Qt::ApplicationInactive)
+// {
+// MainWindow* w=qobject_cast(actWin);
+//// w->loadMainWindow();
+// w->clearSearchResult();
+// actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized);
+// actWin->raise();
+// actWin->showNormal();
+// actWin->activateWindow();
+// }
+// else {
+// actWin->setWindowState(actWin->windowState() & Qt::WindowMinimized);
+// actWin->hide();
+// }
+
+// }
+}
+
+
+/*!
+ \fn void QtSingleApplication::messageReceived(const QString& message)
+
+ This signal is emitted when the current instance receives a \a
+ message from another instance of this application.
+
+ \sa sendMessage(), setActivationWindow(), activateWindow()
+*/
+
+
+/*!
+ \fn void QtSingleApplication::initialize(bool dummy = true)
+
+ \obsolete
+*/
diff --git a/frontend/singleapplication/qt-single-application.h b/frontend/singleapplication/qt-single-application.h
new file mode 100644
index 0000000..15df5a8
--- /dev/null
+++ b/frontend/singleapplication/qt-single-application.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies)
+** 2020 KylinSoft Co., Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+**
+** This file is part of the Qt Solutions component.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+**
+****************************************************************************/
+
+#ifndef QTSINGLEAPPLICATION_H
+#define QTSINGLEAPPLICATION_H
+
+#include
+
+class QtLocalPeer;
+
+#if defined(Q_OS_WIN)
+# if !defined(QT_QTSINGLEAPPLICATION_EXPORT) && !defined(QT_QTSINGLEAPPLICATION_IMPORT)
+# define QT_QTSINGLEAPPLICATION_EXPORT
+# elif defined(QT_QTSINGLEAPPLICATION_IMPORT)
+# if defined(QT_QTSINGLEAPPLICATION_EXPORT)
+# undef QT_QTSINGLEAPPLICATION_EXPORT
+# endif
+# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllimport)
+# elif defined(QT_QTSINGLEAPPLICATION_EXPORT)
+# undef QT_QTSINGLEAPPLICATION_EXPORT
+# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllexport)
+# endif
+#else
+# define QT_QTSINGLEAPPLICATION_EXPORT
+#endif
+
+class QT_QTSINGLEAPPLICATION_EXPORT QtSingleApplication : public QApplication {
+ Q_OBJECT
+
+public:
+ QtSingleApplication(int &argc, char **argv, bool GUIenabled = true);
+ QtSingleApplication(const QString &id, int &argc, char **argv);
+#if QT_VERSION < 0x050000
+ QtSingleApplication(int &argc, char **argv, Type type);
+# if defined(Q_WS_X11)
+ QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0);
+ QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap = 0);
+ QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0);
+# endif // Q_WS_X11
+#endif // QT_VERSION < 0x050000
+
+ bool isRunning();
+ QString id() const;
+
+ void setActivationWindow(QWidget* aw, bool activateOnMessage = true);
+ QWidget* activationWindow() const;
+
+ // Obsolete:
+ void initialize(bool dummy = true) {
+ isRunning();
+ Q_UNUSED(dummy)
+ }
+
+public Q_SLOTS:
+ bool sendMessage(const QString &message, int timeout = 5000);
+ void activateWindow();
+
+
+Q_SIGNALS:
+ void messageReceived(const QString &message);
+
+
+private:
+ void sysInit(const QString &appId = QString());
+ QtLocalPeer *peer;
+ QWidget *actWin;
+};
+
+#endif // QTSINGLEAPPLICATION_H
diff --git a/frontend/singleapplication/qt-single-application.pri b/frontend/singleapplication/qt-single-application.pri
new file mode 100644
index 0000000..0a4ca9a
--- /dev/null
+++ b/frontend/singleapplication/qt-single-application.pri
@@ -0,0 +1,27 @@
+INCLUDEPATH += $$PWD
+DEPENDPATH += $$PWD
+QT *= network
+greaterThan(QT_MAJOR_VERSION, 4): QT *= widgets
+
+qtsingleapplication-uselib:!qtsingleapplication-buildlib {
+ LIBS += -L$$QTSINGLEAPPLICATION_LIBDIR -l$$QTSINGLEAPPLICATION_LIBNAME
+} else {
+ SOURCES +=
+ HEADERS +=
+}
+
+win32 {
+ contains(TEMPLATE, lib):contains(CONFIG, shared):DEFINES += QT_QTSINGLEAPPLICATION_EXPORT
+ else:qtsingleapplication-uselib:DEFINES += QT_QTSINGLEAPPLICATION_IMPORT
+}
+
+HEADERS += \
+ $$PWD/qt-local-peer.h \
+ $$PWD/qt-locked-file.h \
+ $$PWD/qt-single-application.h
+
+SOURCES += \
+ $$PWD/qt-local-peer.cpp \
+ $$PWD/qt-locked-file-unix.cpp \
+ $$PWD/qt-single-application.cpp \
+ $$PWD/qt-locked-file.cpp
diff --git a/frontend/view/result-view.cpp b/frontend/view/result-view.cpp
new file mode 100644
index 0000000..5fe8022
--- /dev/null
+++ b/frontend/view/result-view.cpp
@@ -0,0 +1,104 @@
+#include "result-view.h"
+#define MAIN_MARGINS 0,0,0,0
+#define MAIN_SPACING 0
+#define TITLE_HEIGHT 30
+#define UNFOLD_LABEL_HEIGHT 24
+
+using namespace Zeeker;
+ResultWidget::ResultWidget(const QString &plugin_id, QWidget *parent) : QWidget(parent)
+{
+ m_plugin_id = plugin_id;
+ this->initUi();
+ initConnections();
+}
+
+QString ResultWidget::pluginId()
+{
+ return m_plugin_id;
+}
+
+void ResultWidget::setEnabled(const bool &enabled)
+{
+ m_enabled = enabled;
+}
+
+void ResultWidget::initUi()
+{
+ m_mainLyt = new QVBoxLayout(this);
+ this->setLayout(m_mainLyt);
+ m_mainLyt->setContentsMargins(MAIN_MARGINS);
+ m_mainLyt->setSpacing(MAIN_SPACING);
+
+ m_titleLabel = new TitleLabel(this);
+ m_titleLabel->setText(m_plugin_id);
+ m_titleLabel->setFixedHeight(TITLE_HEIGHT);
+
+ m_resultView = new ResultView(m_plugin_id, this);
+
+ //NEW_TODO
+ m_showMoreLabel = new ShowMoreLabel(this);
+ m_showMoreLabel->setFixedHeight(UNFOLD_LABEL_HEIGHT);
+
+ m_mainLyt->addWidget(m_titleLabel);
+ m_mainLyt->addWidget(m_resultView);
+ m_mainLyt->addWidget(m_showMoreLabel);
+ this->setFixedHeight(m_resultView->height() + TITLE_HEIGHT + UNFOLD_LABEL_HEIGHT);
+}
+
+void ResultWidget::initConnections()
+{
+ connect(this, &ResultWidget::startSearch, m_resultView, &ResultView::startSearch);
+ connect(this, &ResultWidget::stopSearch, m_resultView, &ResultView::stopSearch);
+ connect(m_resultView, &ResultView::currentRowChanged, this, &ResultWidget::currentRowChanged);
+ connect(this, &ResultWidget::clearSelectedRow, m_resultView, &ResultView::clearSelectedRow);
+}
+
+ResultView::ResultView(const QString &plugin_id, QWidget *parent) : QTreeView(parent)
+{
+ this->setFrameShape(QFrame::NoFrame);
+ this->viewport()->setAutoFillBackground(false);
+ this->setRootIsDecorated(false);
+ this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ this->setSelectionBehavior(QAbstractItemView::SelectRows);
+ this->setSelectionMode(QAbstractItemView::SingleSelection);
+ this->setHeaderHidden(true);
+ m_model = new SearchResultModel(plugin_id);
+ this->setModel(m_model);
+ initConnections();
+ m_plugin_id = plugin_id;
+}
+
+bool ResultView::isSelected()
+{
+ return m_is_selected;
+}
+
+void ResultView::clearSelectedRow()
+{
+ if (!m_is_selected) {
+ this->blockSignals(true);
+ this->clearSelection();
+ this->blockSignals(false);
+ }
+}
+
+void ResultView::initConnections()
+{
+ connect(this, &ResultView::startSearch, m_model, &SearchResultModel::startSearch);
+ connect(this, &ResultView::stopSearch, m_model, &SearchResultModel::stopSearch);
+ connect(this->selectionModel(), &QItemSelectionModel::selectionChanged, this, [ = ](const QItemSelection & selected, const QItemSelection & deselected) {
+ //NEW_TODO 处理选中事件
+ m_is_selected = true;
+ Q_EMIT this->currentRowChanged(m_plugin_id, m_model->getInfo(this->currentIndex()));
+ m_is_selected = false;
+ if(!selected.isEmpty()) {
+ QRegion region = visualRegionForSelection(selected);
+ QRect rect = region.boundingRect();
+// Q_EMIT this->currentSelectPos(mapToParent(rect.topLeft()));
+ }
+ });
+
+ connect(this, &ResultView::activated, this, [ = ](const QModelIndex & index) {
+ //NEW_TODO 处理双击打开事件
+ });
+}
diff --git a/frontend/view/result-view.h b/frontend/view/result-view.h
new file mode 100644
index 0000000..6fac408
--- /dev/null
+++ b/frontend/view/result-view.h
@@ -0,0 +1,63 @@
+#ifndef RESULTVIEW_H
+#define RESULTVIEW_H
+#include
+#include
+#include
+#include "search-result-model.h"
+#include "show-more-label.h"
+#include "title-label.h"
+
+namespace Zeeker {
+
+class ResultView : public QTreeView
+{
+ Q_OBJECT
+public:
+ ResultView(const QString &plugin_id, QWidget *parent = nullptr);
+ ~ResultView() = default;
+ bool isSelected();
+
+public Q_SLOTS:
+ void clearSelectedRow();
+
+private:
+ void initConnections();
+ SearchResultModel * m_model = nullptr;
+ QString m_plugin_id;
+ bool m_is_selected = false;
+
+Q_SIGNALS:
+ void startSearch(const QString &);
+ void stopSearch();
+ void currentRowChanged(const QString &, const SearchPluginIface::ResultInfo&);
+
+};
+
+class ResultWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ ResultWidget(const QString &plugin_id, QWidget *parent = nullptr);
+ ~ResultWidget() = default;
+ QString pluginId();
+ void setEnabled(const bool&);
+private:
+ QString m_plugin_id;
+ bool m_enabled = true;
+
+ void initUi();
+ void initConnections();
+ QVBoxLayout * m_mainLyt = nullptr;
+ TitleLabel * m_titleLabel = nullptr;
+ ResultView * m_resultView = nullptr;
+ ShowMoreLabel * m_showMoreLabel = nullptr;
+
+Q_SIGNALS:
+ void startSearch(const QString &);
+ void stopSearch();
+ void currentRowChanged(const QString &, const SearchPluginIface::ResultInfo&);
+ void clearSelectedRow();
+};
+}
+
+#endif // RESULTVIEW_H
diff --git a/frontend/view/view.pri b/frontend/view/view.pri
new file mode 100644
index 0000000..5ee6282
--- /dev/null
+++ b/frontend/view/view.pri
@@ -0,0 +1,7 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/result-view.h \
+
+SOURCES += \
+ $$PWD/result-view.cpp \
diff --git a/frontend/xatom/xatom-helper.cpp b/frontend/xatom/xatom-helper.cpp
new file mode 100644
index 0000000..9c17419
--- /dev/null
+++ b/frontend/xatom/xatom-helper.cpp
@@ -0,0 +1,200 @@
+/*
+ * KWin Style UKUI
+ *
+ * Copyright (C) 2020, KylinSoft Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Authors: Yue Lan
+ *
+ */
+
+#include "xatom-helper.h"
+
+#include
+
+#include
+
+#include
+#include
+#include
+
+static XAtomHelper *global_instance = nullptr;
+
+XAtomHelper *XAtomHelper::getInstance() {
+ if(!global_instance)
+ global_instance = new XAtomHelper;
+ return global_instance;
+}
+
+bool XAtomHelper::isFrameLessWindow(int winId) {
+ auto hints = getInstance()->getWindowMotifHint(winId);
+ if(hints.flags == MWM_HINTS_DECORATIONS && hints.functions == 1) {
+ return true;
+ }
+ return false;
+}
+
+bool XAtomHelper::isWindowDecorateBorderOnly(int winId) {
+ return isWindowMotifHintDecorateBorderOnly(getInstance()->getWindowMotifHint(winId));
+}
+
+bool XAtomHelper::isWindowMotifHintDecorateBorderOnly(const MotifWmHints &hint) {
+ bool isDeco = false;
+ if(hint.flags & MWM_HINTS_DECORATIONS && hint.flags != MWM_HINTS_DECORATIONS) {
+ if(hint.decorations == MWM_DECOR_BORDER)
+ isDeco = true;
+ }
+ return isDeco;
+}
+
+bool XAtomHelper::isUKUICsdSupported() {
+ // fixme:
+ return false;
+}
+
+bool XAtomHelper::isUKUIDecorationWindow(int winId) {
+ if(m_ukuiDecorationAtion == None)
+ return false;
+
+ Atom type;
+ int format;
+ ulong nitems;
+ ulong bytes_after;
+ uchar *data;
+
+ bool isUKUIDecoration = false;
+
+ XGetWindowProperty(QX11Info::display(), winId, m_ukuiDecorationAtion,
+ 0, LONG_MAX, false,
+ m_ukuiDecorationAtion, &type,
+ &format, &nitems,
+ &bytes_after, &data);
+
+ if(type == m_ukuiDecorationAtion) {
+ if(nitems == 1) {
+ isUKUIDecoration = data[0];
+ }
+ }
+
+ return isUKUIDecoration;
+}
+
+UnityCorners XAtomHelper::getWindowBorderRadius(int winId) {
+ UnityCorners corners;
+
+ Atom type;
+ int format;
+ ulong nitems;
+ ulong bytes_after;
+ uchar *data;
+
+ if(m_unityBorderRadiusAtom != None) {
+ XGetWindowProperty(QX11Info::display(), winId, m_unityBorderRadiusAtom,
+ 0, LONG_MAX, false,
+ XA_CARDINAL, &type,
+ &format, &nitems,
+ &bytes_after, &data);
+
+ if(type == XA_CARDINAL) {
+ if(nitems == 4) {
+ corners.topLeft = static_cast(data[0]);
+ corners.topRight = static_cast(data[1 * sizeof(ulong)]);
+ corners.bottomLeft = static_cast(data[2 * sizeof(ulong)]);
+ corners.bottomRight = static_cast(data[3 * sizeof(ulong)]);
+ }
+ XFree(data);
+ }
+ }
+
+ return corners;
+}
+
+void XAtomHelper::setWindowBorderRadius(int winId, const UnityCorners &data) {
+ if(m_unityBorderRadiusAtom == None)
+ return;
+
+ ulong corners[4] = {data.topLeft, data.topRight, data.bottomLeft, data.bottomRight};
+
+ XChangeProperty(QX11Info::display(), winId, m_unityBorderRadiusAtom, XA_CARDINAL,
+ 32, XCB_PROP_MODE_REPLACE, (const unsigned char *) &corners, sizeof(corners) / sizeof(corners[0]));
+}
+
+void XAtomHelper::setWindowBorderRadius(int winId, int topLeft, int topRight, int bottomLeft, int bottomRight) {
+ if(m_unityBorderRadiusAtom == None)
+ return;
+
+ ulong corners[4] = {(ulong)topLeft, (ulong)topRight, (ulong)bottomLeft, (ulong)bottomRight};
+
+ XChangeProperty(QX11Info::display(), winId, m_unityBorderRadiusAtom, XA_CARDINAL,
+ 32, XCB_PROP_MODE_REPLACE, (const unsigned char *) &corners, sizeof(corners) / sizeof(corners[0]));
+}
+
+void XAtomHelper::setUKUIDecoraiontHint(int winId, bool set) {
+ if(m_ukuiDecorationAtion == None)
+ return;
+
+ XChangeProperty(QX11Info::display(), winId, m_ukuiDecorationAtion, m_ukuiDecorationAtion, 32, XCB_PROP_MODE_REPLACE, (const unsigned char *) &set, 1);
+}
+
+void XAtomHelper::setWindowMotifHint(int winId, const MotifWmHints &hints) {
+ if(m_unityBorderRadiusAtom == None)
+ return;
+
+ XChangeProperty(QX11Info::display(), winId, m_motifWMHintsAtom, m_motifWMHintsAtom,
+ 32, XCB_PROP_MODE_REPLACE, (const unsigned char *)&hints, sizeof(MotifWmHints) / sizeof(ulong));
+}
+
+MotifWmHints XAtomHelper::getWindowMotifHint(int winId) {
+ MotifWmHints hints;
+
+ if(m_unityBorderRadiusAtom == None)
+ return hints;
+
+ uchar *data;
+ Atom type;
+ int format;
+ ulong nitems;
+ ulong bytes_after;
+
+ XGetWindowProperty(QX11Info::display(), winId, m_motifWMHintsAtom,
+ 0, sizeof(MotifWmHints) / sizeof(long), false, AnyPropertyType, &type,
+ &format, &nitems, &bytes_after, &data);
+
+ if(type == None) {
+ return hints;
+ } else {
+ hints = *(MotifWmHints *)data;
+ XFree(data);
+ }
+ return hints;
+}
+
+XAtomHelper::XAtomHelper(QObject *parent) : QObject(parent) {
+ if(!QX11Info::isPlatformX11())
+ return;
+
+ m_motifWMHintsAtom = XInternAtom(QX11Info::display(), "_MOTIF_WM_HINTS", true);
+ m_unityBorderRadiusAtom = XInternAtom(QX11Info::display(), "_UNITY_GTK_BORDER_RADIUS", false);
+ m_ukuiDecorationAtion = XInternAtom(QX11Info::display(), "_KWIN_UKUI_DECORAION", false);
+}
+
+Atom XAtomHelper::registerUKUICsdNetWmSupportAtom() {
+ // fixme:
+ return None;
+}
+
+void XAtomHelper::unregisterUKUICsdNetWmSupportAtom() {
+ // fixme:
+}
diff --git a/frontend/xatom/xatom-helper.h b/frontend/xatom/xatom-helper.h
new file mode 100644
index 0000000..615ce6d
--- /dev/null
+++ b/frontend/xatom/xatom-helper.h
@@ -0,0 +1,110 @@
+/*
+ * KWin Style UKUI
+ *
+ * Copyright (C) 2020, KylinSoft Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Authors: Yue Lan
+ *
+ */
+
+#ifndef XATOMHELPER_H
+#define XATOMHELPER_H
+
+#include
+
+#include
+#include
+
+struct UnityCorners {
+ ulong topLeft = 0;
+ ulong topRight = 0;
+ ulong bottomLeft = 0;
+ ulong bottomRight = 0;
+};
+
+typedef struct {
+ ulong flags = 0;
+ ulong functions = 0;
+ ulong decorations = 0;
+ long input_mode = 0;
+ ulong status = 0;
+} MotifWmHints, MwmHints;
+
+#define MWM_HINTS_FUNCTIONS (1L << 0)
+#define MWM_HINTS_DECORATIONS (1L << 1)
+#define MWM_HINTS_INPUT_MODE (1L << 2)
+#define MWM_HINTS_STATUS (1L << 3)
+
+#define MWM_FUNC_ALL (1L << 0)
+#define MWM_FUNC_RESIZE (1L << 1)
+#define MWM_FUNC_MOVE (1L << 2)
+#define MWM_FUNC_MINIMIZE (1L << 3)
+#define MWM_FUNC_MAXIMIZE (1L << 4)
+#define MWM_FUNC_CLOSE (1L << 5)
+
+#define MWM_DECOR_ALL (1L << 0)
+#define MWM_DECOR_BORDER (1L << 1)
+#define MWM_DECOR_RESIZEH (1L << 2)
+#define MWM_DECOR_TITLE (1L << 3)
+#define MWM_DECOR_MENU (1L << 4)
+#define MWM_DECOR_MINIMIZE (1L << 5)
+#define MWM_DECOR_MAXIMIZE (1L << 6)
+
+#define MWM_INPUT_MODELESS 0
+#define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1
+#define MWM_INPUT_SYSTEM_MODAL 2
+#define MWM_INPUT_FULL_APPLICATION_MODAL 3
+#define MWM_INPUT_APPLICATION_MODAL MWM_INPUT_PRIMARY_APPLICATION_MODAL
+
+#define MWM_TEAROFF_WINDOW (1L<<0)
+
+namespace UKUI {
+class Decoration;
+}
+
+class XAtomHelper : public QObject {
+ // friend class UKUI::Decoration;
+ Q_OBJECT
+public:
+ static XAtomHelper *getInstance();
+
+ static bool isFrameLessWindow(int winId);
+
+ static bool isWindowDecorateBorderOnly(int winId);
+ static bool isWindowMotifHintDecorateBorderOnly(const MotifWmHints &hint);
+ bool isUKUICsdSupported();
+ bool isUKUIDecorationWindow(int winId);
+
+ UnityCorners getWindowBorderRadius(int winId);
+ void setWindowBorderRadius(int winId, const UnityCorners &data);
+ void setWindowBorderRadius(int winId, int topLeft, int topRight, int bottomLeft, int bottomRight);
+ void setUKUIDecoraiontHint(int winId, bool set = true);
+
+ void setWindowMotifHint(int winId, const MotifWmHints &hints);
+ MotifWmHints getWindowMotifHint(int winId);
+
+private:
+ explicit XAtomHelper(QObject *parent = nullptr);
+
+ Atom registerUKUICsdNetWmSupportAtom();
+ void unregisterUKUICsdNetWmSupportAtom();
+
+ Atom m_motifWMHintsAtom = None;
+ Atom m_unityBorderRadiusAtom = None;
+ Atom m_ukuiDecorationAtion = None;
+};
+
+#endif // XATOMHELPER_H
diff --git a/frontend/xatom/xatom.pri b/frontend/xatom/xatom.pri
new file mode 100644
index 0000000..2d5b140
--- /dev/null
+++ b/frontend/xatom/xatom.pri
@@ -0,0 +1,7 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/xatom-helper.h \
+
+SOURCES += \
+ $$PWD/xatom-helper.cpp \
diff --git a/src/control/title-label.cpp b/src/control/title-label.cpp
index 3a1af24..0d8aaef 100644
--- a/src/control/title-label.cpp
+++ b/src/control/title-label.cpp
@@ -43,7 +43,7 @@ void TitleLabel::paintEvent(QPaintEvent * event) {
QRect rect = this->rect();
p.setRenderHint(QPainter::Antialiasing); // 反锯齿;
p.setBrush(opt.palette.color(QPalette::Text));
- p.setOpacity(0.06);
+ p.setOpacity(0.04);
p.setPen(Qt::NoPen);
p.drawRoundedRect(rect, 0, 0);
return QLabel::paintEvent(event);
diff --git a/ukui-search.pro b/ukui-search.pro
index 47447cd..f23af01 100644
--- a/ukui-search.pro
+++ b/ukui-search.pro
@@ -2,6 +2,7 @@ TEMPLATE = subdirs
SUBDIRS += $$PWD/libchinese-segmentation \
$$PWD/libsearch \
$$PWD/src \
+ $$PWD/frontend \
$$PWD/ukuisearch-systemdbus
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
@@ -17,5 +18,8 @@ DEFINES += QT_DEPRECATED_WARNINGS
libsearch.depends += libchinese-segmentation
src.depends = libsearch
-CONFIG += ordered
+CONFIG += ordered \
+ qt
+
+QT += widgets