mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
support search
- Ctrl+E C to toggle the search dock;
This commit is contained in:
parent
d404360fee
commit
a2ee5413a1
@ -75,7 +75,7 @@ void VExportDialog::setupUI()
|
||||
// Notes to export.
|
||||
m_srcCB = VUtils::getComboBox();
|
||||
m_srcCB->setToolTip(tr("Choose notes to export"));
|
||||
m_srcCB->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
|
||||
m_srcCB->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
connect(m_srcCB, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(handleCurrentSrcChanged(int)));
|
||||
|
||||
@ -580,7 +580,7 @@ void VExportDialog::startExport()
|
||||
|
||||
if (opt.m_outputSuffix.isEmpty()
|
||||
|| opt.m_cmd.isEmpty()
|
||||
|| opt.m_allInOne && opt.m_folderSep.isEmpty()) {
|
||||
|| (opt.m_allInOne && opt.m_folderSep.isEmpty())) {
|
||||
appendLogLine(tr("Invalid configurations for custom export."));
|
||||
m_inExport = false;
|
||||
m_exportBtn->setEnabled(true);
|
||||
@ -1274,11 +1274,11 @@ int VExportDialog::doExportCustomAllInOne(const QList<QString> &p_files,
|
||||
QWidget *VExportDialog::setupCustomAdvancedSettings()
|
||||
{
|
||||
// Source format.
|
||||
m_customSrcFormatCB = VUtils::getComboBox();
|
||||
m_customSrcFormatCB = VUtils::getComboBox(this);
|
||||
m_customSrcFormatCB->setToolTip(tr("Choose format of the input"));
|
||||
|
||||
// Output suffix.
|
||||
m_customSuffixEdit = new VLineEdit();
|
||||
m_customSuffixEdit = new VLineEdit(this);
|
||||
m_customSuffixEdit->setPlaceholderText(tr("Without the preceding dot"));
|
||||
m_customSuffixEdit->setToolTip(tr("Suffix of the output file without the preceding dot"));
|
||||
QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp),
|
||||
@ -1288,11 +1288,12 @@ QWidget *VExportDialog::setupCustomAdvancedSettings()
|
||||
QLabel *tipsLabel = new QLabel(tr("<span><span style=\"font-weight:bold;\">%0</span> for the input file; "
|
||||
"<span style=\"font-weight:bold;\">%1</span> for the output file; "
|
||||
"<span style=\"font-weight:bold;\">%2</span> for the rendering CSS style file; "
|
||||
"<span style=\"font-weight:bold;\">%3</span> for the input file directory.</span>"));
|
||||
"<span style=\"font-weight:bold;\">%3</span> for the input file directory.</span>"),
|
||||
this);
|
||||
tipsLabel->setWordWrap(true);
|
||||
|
||||
// Enable All In One.
|
||||
m_customAllInOneCB = new QCheckBox(tr("Enable All In One"));
|
||||
m_customAllInOneCB = new QCheckBox(tr("Enable All In One"), this);
|
||||
m_customAllInOneCB->setToolTip(tr("Pass a list of input files to the custom command"));
|
||||
connect(m_customAllInOneCB, &QCheckBox::stateChanged,
|
||||
this, [this](int p_state) {
|
||||
@ -1302,13 +1303,13 @@ QWidget *VExportDialog::setupCustomAdvancedSettings()
|
||||
});
|
||||
|
||||
// Input directory separator.
|
||||
m_customFolderSepEdit = new VLineEdit();
|
||||
m_customFolderSepEdit = new VLineEdit(this);
|
||||
m_customFolderSepEdit->setPlaceholderText(tr("Separator to concatenate input files directories"));
|
||||
m_customFolderSepEdit->setToolTip(tr("Separator to concatenate input files directories"));
|
||||
m_customFolderSepEdit->setEnabled(false);
|
||||
|
||||
// Target file name for all in one.
|
||||
m_customTargetFileNameEdit = new VLineEdit();
|
||||
m_customTargetFileNameEdit = new VLineEdit(this);
|
||||
m_customTargetFileNameEdit->setPlaceholderText(tr("Empty to use the name of the first source file"));
|
||||
m_customTargetFileNameEdit->setToolTip(tr("Name of the generated All-In-One file"));
|
||||
validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp),
|
||||
@ -1317,7 +1318,7 @@ QWidget *VExportDialog::setupCustomAdvancedSettings()
|
||||
m_customTargetFileNameEdit->setEnabled(false);
|
||||
|
||||
// Cmd edit.
|
||||
m_customCmdEdit = new QPlainTextEdit();
|
||||
m_customCmdEdit = new QPlainTextEdit(this);
|
||||
m_customCmdEdit->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
|
||||
QString cmdExamp("pandoc --resource-path=.:\"%3\" --css=\"%2\" -s -o \"%1\" \"%0\"");
|
||||
m_customCmdEdit->setPlaceholderText(cmdExamp);
|
||||
@ -1348,12 +1349,12 @@ QWidget *VExportDialog::setupCustomAdvancedSettings()
|
||||
QWidget *wid = new QWidget();
|
||||
wid->setLayout(advLayout);
|
||||
|
||||
m_customCmdEdit->setMaximumHeight(100);
|
||||
m_customCmdEdit->setMaximumHeight(m_customSrcFormatCB->height() * 3);
|
||||
|
||||
return wid;
|
||||
}
|
||||
|
||||
int VExportDialog::outputAsHTML(QString &p_outputFolder,
|
||||
int VExportDialog::outputAsHTML(const QString &p_outputFolder,
|
||||
QString *p_errMsg,
|
||||
QList<QString> *p_outputFiles)
|
||||
{
|
||||
|
@ -368,7 +368,7 @@ private:
|
||||
|
||||
ExportFormat currentFormat() const;
|
||||
|
||||
int outputAsHTML(QString &p_outputFolder,
|
||||
int outputAsHTML(const QString &p_outputFolder,
|
||||
QString *p_errMsg = NULL,
|
||||
QList<QString> *p_outputFiles = NULL);
|
||||
|
||||
|
@ -49,7 +49,7 @@ void VFindReplaceDialog::setupUI()
|
||||
m_replaceFindBtn->setProperty("FlatBtn", true);
|
||||
m_replaceAllBtn = new QPushButton(tr("Replace A&ll"));
|
||||
m_replaceAllBtn->setProperty("FlatBtn", true);
|
||||
m_advancedBtn = new QPushButton(tr("&Advanced >>"));
|
||||
m_advancedBtn = new QPushButton(tr("&Advanced >>>"));
|
||||
m_advancedBtn->setProperty("FlatBtn", true);
|
||||
m_advancedBtn->setCheckable(true);
|
||||
|
||||
@ -199,9 +199,9 @@ void VFindReplaceDialog::handleFindTextChanged(const QString &p_text)
|
||||
void VFindReplaceDialog::advancedBtnToggled(bool p_checked)
|
||||
{
|
||||
if (p_checked) {
|
||||
m_advancedBtn->setText("B&asic <<");
|
||||
m_advancedBtn->setText(tr("B&asic <<<"));
|
||||
} else {
|
||||
m_advancedBtn->setText("&Advanced <<");
|
||||
m_advancedBtn->setText(tr("&Advanced >>>"));
|
||||
}
|
||||
|
||||
m_caseSensitiveCheck->setVisible(p_checked);
|
||||
|
@ -48,6 +48,7 @@ private slots:
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
// Bit OR of FindOption
|
||||
uint m_options;
|
||||
bool m_replaceAvailable;
|
||||
|
34
src/isearchengine.h
Normal file
34
src/isearchengine.h
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef ISEARCHENGINE_H
|
||||
#define ISEARCHENGINE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QVector>
|
||||
|
||||
#include "vsearchconfig.h"
|
||||
|
||||
// Abstract class for search engine.
|
||||
class ISearchEngine : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ISearchEngine(QObject *p_parent = nullptr)
|
||||
: QObject(p_parent)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void search(const QSharedPointer<VSearchConfig> &p_config,
|
||||
const QSharedPointer<VSearchResult> &p_result) = 0;
|
||||
|
||||
virtual void stop() = 0;
|
||||
|
||||
virtual void clear() = 0;
|
||||
|
||||
signals:
|
||||
void finished(const QSharedPointer<VSearchResult> &p_result);
|
||||
|
||||
void resultItemAdded(const QSharedPointer<VSearchResultItem> &p_item);
|
||||
|
||||
protected:
|
||||
QSharedPointer<VSearchResult> m_result;
|
||||
};
|
||||
#endif // ISEARCHENGINE_H
|
10
src/resources/icons/clear_search.svg
Normal file
10
src/resources/icons/clear_search.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<path style="fill:#C9302C" d="M443.6,387.1L312.4,255.4l131.5-130c5.4-5.4,5.4-14.2,0-19.6l-37.4-37.6c-2.6-2.6-6.1-4-9.8-4c-3.7,0-7.2,1.5-9.8,4
|
||||
L256,197.8L124.9,68.3c-2.6-2.6-6.1-4-9.8-4c-3.7,0-7.2,1.5-9.8,4L68,105.9c-5.4,5.4-5.4,14.2,0,19.6l131.5,130L68.4,387.1
|
||||
c-2.6,2.6-4.1,6.1-4.1,9.8c0,3.7,1.4,7.2,4.1,9.8l37.4,37.6c2.7,2.7,6.2,4.1,9.8,4.1c3.5,0,7.1-1.3,9.8-4.1L256,313.1l130.7,131.1
|
||||
c2.7,2.7,6.2,4.1,9.8,4.1c3.5,0,7.1-1.3,9.8-4.1l37.4-37.6c2.6-2.6,4.1-6.1,4.1-9.8C447.7,393.2,446.2,389.7,443.6,387.1z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 995 B |
15
src/resources/icons/note_item.svg
Normal file
15
src/resources/icons/note_item.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<g>
|
||||
<path style="fill:#000000" d="M398.6,169.2c-0.9-2.2-2-4.3-3.5-6.1l-83.8-91.7c-1.9-2.1-4.2-3.6-6.7-4.9c-2.9-1.5-6.1-2.1-9.5-2.1H135.2
|
||||
c-12.4,0-22.7,10.6-22.7,23.9v335.2c0,13.4,10.3,24.9,22.7,24.9h243.1c12.4,0,22.2-11.5,22.2-24.9V179.4
|
||||
C400.5,175.8,400,172.3,398.6,169.2z M160.5,178.6c0-1.5,1.8-2.1,3.4-2.1h70.8c1.6,0,2.8,0.6,2.8,2.1v10.8c0,1.4-1.1,3.1-2.8,3.1
|
||||
h-70.8c-1.6,0-3.4-1.7-3.4-3.1V178.6z M160.5,306.6c0-1.5,1.8-2.1,3.4-2.1h122.2c1.6,0,2.4,0.6,2.4,2.1v10.8c0,1.4-0.7,3.1-2.4,3.1
|
||||
H163.9c-1.6,0-3.4-1.7-3.4-3.1V306.6z M320.5,381.4c0,1.4-0.7,3.1-2.4,3.1H163.9c-1.6,0-3.4-1.7-3.4-3.1v-10.8
|
||||
c0-1.5,1.8-2.1,3.4-2.1h154.2c1.6,0,2.4,0.6,2.4,2.1V381.4z M352.5,253.4c0,1.4-0.7,3.1-2.4,3.1H163.9c-1.6,0-3.4-1.7-3.4-3.1
|
||||
v-10.8c0-1.5,1.8-2.1,3.4-2.1h186.2c1.6,0,2.4,0.6,2.4,2.1V253.4z M305.6,177.5c-5.6,0-11.1-5.2-11.1-11.3v-66l71.2,77.3H305.6z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
10
src/resources/icons/search.svg
Normal file
10
src/resources/icons/search.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<path style="fill:#000000" d="M445,386.7l-84.8-85.9c13.8-24.1,21-50.9,21-77.9c0-87.6-71.2-158.9-158.6-158.9C135.2,64,64,135.3,64,222.9
|
||||
c0,87.6,71.2,158.9,158.6,158.9c27.9,0,55.5-7.7,80.1-22.4l84.4,85.6c1.9,1.9,4.6,3.1,7.3,3.1c2.7,0,5.4-1.1,7.3-3.1l43.3-43.8
|
||||
C449,397.1,449,390.7,445,386.7z M222.6,125.9c53.4,0,96.8,43.5,96.8,97c0,53.5-43.4,97-96.8,97c-53.4,0-96.8-43.5-96.8-97
|
||||
C125.8,169.4,169.2,125.9,222.6,125.9z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 905 B |
14
src/resources/icons/search_advanced.svg
Normal file
14
src/resources/icons/search_advanced.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<path style="fill:#000000" d="M32,376h283.35c6.186-14.112,20.281-24,36.65-24s30.465,9.888,36.65,24H480v32h-91.35c-6.186,14.112-20.281,24-36.65,24
|
||||
s-30.465-9.888-36.65-24H32"/>
|
||||
<path style="fill:#000000" d="M32,240h91.35c6.186-14.112,20.281-24,36.65-24s30.465,9.888,36.65,24H480v32H196.65c-6.186,14.112-20.281,24-36.65,24
|
||||
s-30.465-9.888-36.65-24H32"/>
|
||||
<path style="fill:#000000" d="M32,104h283.35c6.186-14.112,20.281-24,36.65-24s30.465,9.888,36.65,24H480v32h-91.35c-6.186,14.112-20.281,24-36.65,24
|
||||
s-30.465-9.888-36.65-24H32"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1022 B |
12
src/resources/icons/search_console.svg
Normal file
12
src/resources/icons/search_console.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<g>
|
||||
<path style="fill:#000000" d="M468.7,64H43.3c-6,0-11.3,5-11.3,11.1v265.7c0,6.2,5.2,11.1,11.3,11.1h425.4c6,0,11.3-5,11.3-11.1V75.1
|
||||
C480,69,474.8,64,468.7,64z M448,320H64V96h384V320z"/>
|
||||
<path style="fill:#000000" d="M302.5,448c28-0.5,41.5-3.9,29-12.5c-12.5-8.7-28.5-15.3-29-22.5c-0.3-3.7-1.7-45-1.7-45H256h-44.8c0,0-1.5,41.3-1.7,45
|
||||
c-0.5,7.1-16.5,13.8-29,22.5c-12.5,8.7,1,12,29,12.5H302.5z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 890 B |
@ -183,64 +183,18 @@ QDockWidget::close-button:hover, QDockWidget::float-button:hover {
|
||||
/* End DockWidget */
|
||||
|
||||
/* QPushButton */
|
||||
QPushButton {
|
||||
color: @pushbutton_fg;
|
||||
background: @pushbutton_bg;
|
||||
border: 1px solid @pushbutton_border;
|
||||
padding: 3px;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
QPushButton:focus {
|
||||
background-color: @pushbutton_focus_bg;
|
||||
}
|
||||
|
||||
QPushButton:pressed {
|
||||
background-color: @pushbutton_pressed_bg;
|
||||
}
|
||||
|
||||
QPushButton:checked {
|
||||
background-color: @pushbutton_checked_bg;
|
||||
}
|
||||
|
||||
QPushButton:checked:hover {
|
||||
background-color: @pushbutton_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton:hover {
|
||||
background-color: @pushbutton_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton:flat {
|
||||
border: none;
|
||||
}
|
||||
|
||||
QPushButton:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
}
|
||||
|
||||
QPushButton:disabled {
|
||||
color: @pushbutton_disabled_fg;
|
||||
background-color: @pushbutton_disabled_bg;
|
||||
}
|
||||
|
||||
QPushButton::menu-indicator {
|
||||
image: url(arrow_dropdown.svg);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
QPushButton[SpecialBtn="true"] {
|
||||
color: @pushbutton_specialbtn_fg;
|
||||
background: @pushbutton_specialbtn_bg;
|
||||
}
|
||||
|
||||
QPushButton[SpecialBtn="true"]:focus {
|
||||
background-color: @pushbutton_specialbtn_focus_bg;
|
||||
QPushButton[SpecialBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: @pushbutton_specialbtn_bg;
|
||||
}
|
||||
|
||||
QPushButton[SpecialBtn="true"]:pressed {
|
||||
background-color: @pushbutton_specialbtn_pressed_bg;
|
||||
QPushButton[SpecialBtn="true"]:focus {
|
||||
background-color: @pushbutton_specialbtn_focus_bg;
|
||||
}
|
||||
|
||||
QPushButton[SpecialBtn="true"]:checked {
|
||||
@ -255,16 +209,15 @@ QPushButton[SpecialBtn="true"]:hover {
|
||||
background-color: @pushbutton_specialbtn_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton[SpecialBtn="true"]:pressed {
|
||||
background-color: @pushbutton_specialbtn_pressed_bg;
|
||||
}
|
||||
|
||||
QPushButton[SpecialBtn="true"]:disabled {
|
||||
color: @pushbutton_disabled_fg;
|
||||
background-color: @pushbutton_disabled_bg;
|
||||
}
|
||||
|
||||
QPushButton[SpecialBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: @pushbutton_specialbtn_bg;
|
||||
}
|
||||
|
||||
QPushButton[CornerBtn="true"] {
|
||||
padding: 4px -2px 4px -2px;
|
||||
margin: 0px;
|
||||
@ -277,14 +230,19 @@ QPushButton[CornerBtn="true"]::menu-indicator {
|
||||
image: none;
|
||||
}
|
||||
|
||||
QPushButton[CornerBtn="true"]:hover {
|
||||
background-color: @pushbutton_cornerbtn_hover_bg;
|
||||
QPushButton[CornerBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QPushButton[CornerBtn="true"]:focus {
|
||||
background-color: @pushbutton_cornerbtn_focus_bg;
|
||||
}
|
||||
|
||||
QPushButton[CornerBtn="true"]:hover {
|
||||
background-color: @pushbutton_cornerbtn_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton[CornerBtn="true"]:pressed {
|
||||
background-color: @pushbutton_cornerbtn_pressed_bg;
|
||||
}
|
||||
@ -294,11 +252,6 @@ QPushButton[CornerBtn="true"]:disabled {
|
||||
background-color: @pushbutton_disabled_bg;
|
||||
}
|
||||
|
||||
QPushButton[CornerBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QPushButton[StatusBtn="true"] {
|
||||
font: bold;
|
||||
padding: 0px 2px 0px 2px;
|
||||
@ -308,14 +261,19 @@ QPushButton[StatusBtn="true"] {
|
||||
min-width: -1;
|
||||
}
|
||||
|
||||
QPushButton[StatusBtn="true"]:hover {
|
||||
background-color: @pushbutton_statusbtn_hover_bg;
|
||||
QPushButton[StatusBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QPushButton[StatusBtn="true"]:focus {
|
||||
background-color: @pushbutton_statusbtn_focus_bg;;
|
||||
}
|
||||
|
||||
QPushButton[StatusBtn="true"]:hover {
|
||||
background-color: @pushbutton_statusbtn_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton[StatusBtn="true"]:pressed {
|
||||
background-color: @pushbutton_statusbtn_pressed_bg;
|
||||
}
|
||||
@ -325,11 +283,6 @@ QPushButton[StatusBtn="true"]:disabled {
|
||||
background-color: @pushbutton_disabled_bg;
|
||||
}
|
||||
|
||||
QPushButton[StatusBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QPushButton[FlatBtn="true"] {
|
||||
padding: 4px;
|
||||
margin: 0px;
|
||||
@ -338,14 +291,19 @@ QPushButton[FlatBtn="true"] {
|
||||
min-width: -1;
|
||||
}
|
||||
|
||||
QPushButton[FlatBtn="true"]:hover {
|
||||
background-color: @pushbutton_flatbtn_hover_bg;
|
||||
QPushButton[FlatBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QPushButton[FlatBtn="true"]:focus {
|
||||
background-color: @pushbutton_flatbtn_focus_bg;
|
||||
}
|
||||
|
||||
QPushButton[FlatBtn="true"]:hover {
|
||||
background-color: @pushbutton_flatbtn_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton[FlatBtn="true"]:pressed {
|
||||
background-color: @pushbutton_flatbtn_pressed_bg;
|
||||
}
|
||||
@ -355,11 +313,6 @@ QPushButton[FlatBtn="true"]:disabled {
|
||||
background-color: @pushbutton_disabled_bg;
|
||||
}
|
||||
|
||||
QPushButton[FlatBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QPushButton[SelectionBtn="true"] {
|
||||
padding: 4px 10px 4px 10px;
|
||||
border: none;
|
||||
@ -369,14 +322,19 @@ QPushButton[SelectionBtn="true"] {
|
||||
min-width: -1;
|
||||
}
|
||||
|
||||
QPushButton[SelectionBtn="true"]:hover {
|
||||
background-color: @pushbutton_selectionbtn_hover_bg;
|
||||
QPushButton[SelectionBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QPushButton[SelectionBtn="true"]:focus {
|
||||
background-color: @pushbutton_selectionbtn_focus_bg;
|
||||
}
|
||||
|
||||
QPushButton[SelectionBtn="true"]:hover {
|
||||
background-color: @pushbutton_selectionbtn_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton[SelectionBtn="true"]:pressed {
|
||||
background-color: @pushbutton_selectionbtn_pressed_bg;
|
||||
}
|
||||
@ -386,11 +344,6 @@ QPushButton[SelectionBtn="true"]:disabled {
|
||||
background-color: @pushbutton_disabled_bg;
|
||||
}
|
||||
|
||||
QPushButton[SelectionBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QPushButton[TitleBtn="true"] {
|
||||
padding: 4px;
|
||||
margin: 0px;
|
||||
@ -399,14 +352,19 @@ QPushButton[TitleBtn="true"] {
|
||||
min-width: -1;
|
||||
}
|
||||
|
||||
QPushButton[TitleBtn="true"]:hover {
|
||||
background-color: @pushbutton_titlebtn_hover_bg;
|
||||
QPushButton[TitleBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: @pushbutton_titlebtn_bg;
|
||||
}
|
||||
|
||||
QPushButton[TitleBtn="true"]:focus {
|
||||
background-color: @pushbutton_titlebtn_focus_bg;
|
||||
}
|
||||
|
||||
QPushButton[TitleBtn="true"]:hover {
|
||||
background-color: @pushbutton_titlebtn_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton[TitleBtn="true"]:pressed {
|
||||
background-color: @pushbutton_titlebtn_pressed_bg;
|
||||
}
|
||||
@ -416,11 +374,6 @@ QPushButton[TitleBtn="true"]:disabled {
|
||||
background-color: @pushbutton_disabled_bg;
|
||||
}
|
||||
|
||||
QPushButton[TitleBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: @pushbutton_titlebtn_bg;
|
||||
}
|
||||
|
||||
QPushButton[DangerBtn="true"] {
|
||||
color: @pushbutton_dangerbtn_fg;
|
||||
border: none;
|
||||
@ -428,14 +381,23 @@ QPushButton[DangerBtn="true"] {
|
||||
min-width: -1;
|
||||
}
|
||||
|
||||
QPushButton[DangerBtn="true"]:hover {
|
||||
background-color: @pushbutton_dangerbtn_hover_bg;
|
||||
QPushButton[DangerBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: @pushbutton_dangerbtn_bg;
|
||||
}
|
||||
|
||||
QPushButton[DangerBtn="true"]:focus {
|
||||
background-color: @pushbutton_dangerbtn_focus_bg;
|
||||
}
|
||||
|
||||
QPushButton[DangerBtn="true"]:checked {
|
||||
background-color: @pushbutton_dangerbtn_pressed_bg;
|
||||
}
|
||||
|
||||
QPushButton[DangerBtn="true"]:hover {
|
||||
background-color: @pushbutton_dangerbtn_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton[DangerBtn="true"]:pressed {
|
||||
background-color: @pushbutton_dangerbtn_pressed_bg;
|
||||
}
|
||||
@ -445,11 +407,6 @@ QPushButton[DangerBtn="true"]:disabled {
|
||||
background-color: @pushbutton_disabled_bg;
|
||||
}
|
||||
|
||||
QPushButton[DangerBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: @pushbutton_dangerbtn_bg;
|
||||
}
|
||||
|
||||
QPushButton[ToolBoxActiveBtn="true"] {
|
||||
padding: 4px 10px 4px 4px;
|
||||
margin: 0px;
|
||||
@ -459,6 +416,11 @@ QPushButton[ToolBoxActiveBtn="true"] {
|
||||
min-width: -1;
|
||||
}
|
||||
|
||||
QPushButton[ToolBoxActiveBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: @pushbutton_toolboxbtn_active_bg;
|
||||
}
|
||||
|
||||
QPushButton[ToolBoxActiveBtn="true"]:focus {
|
||||
background-color: @pushbutton_toolboxbtn_active_focus_bg;
|
||||
}
|
||||
@ -476,11 +438,6 @@ QPushButton[ToolBoxActiveBtn="true"]:disabled {
|
||||
background-color: @pushbutton_disabled_bg;
|
||||
}
|
||||
|
||||
QPushButton[ToolBoxActiveBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: @pushbutton_toolboxbtn_active_bg;
|
||||
}
|
||||
|
||||
QPushButton[AvatarBtn="true"] {
|
||||
padding: 2px 4px 2px 4px;
|
||||
margin: 0px;
|
||||
@ -489,14 +446,19 @@ QPushButton[AvatarBtn="true"] {
|
||||
min-width: -1;
|
||||
}
|
||||
|
||||
QPushButton[AvatarBtn="true"]:hover {
|
||||
background-color: @pushbutton_avatarbtn_hover_bg;
|
||||
QPushButton[AvatarBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QPushButton[AvatarBtn="true"]:focus {
|
||||
background-color: @pushbutton_avatarbtn_focus_bg;;
|
||||
}
|
||||
|
||||
QPushButton[AvatarBtn="true"]:hover {
|
||||
background-color: @pushbutton_avatarbtn_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton[AvatarBtn="true"]:pressed {
|
||||
background-color: @pushbutton_avatarbtn_pressed_bg;
|
||||
}
|
||||
@ -506,9 +468,51 @@ QPushButton[AvatarBtn="true"]:disabled {
|
||||
background-color: @pushbutton_disabled_bg;
|
||||
}
|
||||
|
||||
QPushButton[AvatarBtn="true"]:default {
|
||||
QPushButton {
|
||||
color: @pushbutton_fg;
|
||||
background: @pushbutton_bg;
|
||||
border: 1px solid @pushbutton_border;
|
||||
padding: 3px;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
QPushButton:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QPushButton:focus {
|
||||
background-color: @pushbutton_focus_bg;
|
||||
}
|
||||
|
||||
QPushButton:checked {
|
||||
background-color: @pushbutton_checked_bg;
|
||||
}
|
||||
|
||||
QPushButton:checked:hover {
|
||||
background-color: @pushbutton_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton:flat {
|
||||
border: none;
|
||||
}
|
||||
|
||||
QPushButton:hover {
|
||||
background-color: @pushbutton_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton:pressed {
|
||||
background-color: @pushbutton_pressed_bg;
|
||||
}
|
||||
|
||||
QPushButton:disabled {
|
||||
color: @pushbutton_disabled_fg;
|
||||
background-color: @pushbutton_disabled_bg;
|
||||
}
|
||||
|
||||
QPushButton::menu-indicator {
|
||||
image: url(arrow_dropdown.svg);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
VButtonMenuItem {
|
||||
@ -544,11 +548,11 @@ VButtonMenuItem[Heading6="true"] {
|
||||
font-size: 14pt;
|
||||
}
|
||||
|
||||
VButtonMenuItem:hover {
|
||||
VButtonMenuItem:focus {
|
||||
background-color: @menubar_item_selected_bg;
|
||||
}
|
||||
|
||||
VButtonMenuItem:focus {
|
||||
VButtonMenuItem:hover {
|
||||
background-color: @menubar_item_selected_bg;
|
||||
}
|
||||
|
||||
|
@ -183,64 +183,18 @@ QDockWidget::close-button:hover, QDockWidget::float-button:hover {
|
||||
/* End DockWidget */
|
||||
|
||||
/* QPushButton */
|
||||
QPushButton {
|
||||
color: @pushbutton_fg;
|
||||
background: @pushbutton_bg;
|
||||
border: 1px solid @pushbutton_border;
|
||||
padding: 3px;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
QPushButton:focus {
|
||||
background-color: @pushbutton_focus_bg;
|
||||
}
|
||||
|
||||
QPushButton:pressed {
|
||||
background-color: @pushbutton_pressed_bg;
|
||||
}
|
||||
|
||||
QPushButton:checked {
|
||||
background-color: @pushbutton_checked_bg;
|
||||
}
|
||||
|
||||
QPushButton:checked:hover {
|
||||
background-color: @pushbutton_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton:hover {
|
||||
background-color: @pushbutton_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton:flat {
|
||||
border: none;
|
||||
}
|
||||
|
||||
QPushButton:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
}
|
||||
|
||||
QPushButton:disabled {
|
||||
color: @pushbutton_disabled_fg;
|
||||
background-color: @pushbutton_disabled_bg;
|
||||
}
|
||||
|
||||
QPushButton::menu-indicator {
|
||||
image: url(arrow_dropdown.svg);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
QPushButton[SpecialBtn="true"] {
|
||||
color: @pushbutton_specialbtn_fg;
|
||||
background: @pushbutton_specialbtn_bg;
|
||||
}
|
||||
|
||||
QPushButton[SpecialBtn="true"]:focus {
|
||||
background-color: @pushbutton_specialbtn_focus_bg;
|
||||
QPushButton[SpecialBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: @pushbutton_specialbtn_bg;
|
||||
}
|
||||
|
||||
QPushButton[SpecialBtn="true"]:pressed {
|
||||
background-color: @pushbutton_specialbtn_pressed_bg;
|
||||
QPushButton[SpecialBtn="true"]:focus {
|
||||
background-color: @pushbutton_specialbtn_focus_bg;
|
||||
}
|
||||
|
||||
QPushButton[SpecialBtn="true"]:checked {
|
||||
@ -255,16 +209,15 @@ QPushButton[SpecialBtn="true"]:hover {
|
||||
background-color: @pushbutton_specialbtn_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton[SpecialBtn="true"]:pressed {
|
||||
background-color: @pushbutton_specialbtn_pressed_bg;
|
||||
}
|
||||
|
||||
QPushButton[SpecialBtn="true"]:disabled {
|
||||
color: @pushbutton_disabled_fg;
|
||||
background-color: @pushbutton_disabled_bg;
|
||||
}
|
||||
|
||||
QPushButton[SpecialBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: @pushbutton_specialbtn_bg;
|
||||
}
|
||||
|
||||
QPushButton[CornerBtn="true"] {
|
||||
padding: 4px -2px 4px -2px;
|
||||
margin: 0px;
|
||||
@ -277,14 +230,19 @@ QPushButton[CornerBtn="true"]::menu-indicator {
|
||||
image: none;
|
||||
}
|
||||
|
||||
QPushButton[CornerBtn="true"]:hover {
|
||||
background-color: @pushbutton_cornerbtn_hover_bg;
|
||||
QPushButton[CornerBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QPushButton[CornerBtn="true"]:focus {
|
||||
background-color: @pushbutton_cornerbtn_focus_bg;
|
||||
}
|
||||
|
||||
QPushButton[CornerBtn="true"]:hover {
|
||||
background-color: @pushbutton_cornerbtn_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton[CornerBtn="true"]:pressed {
|
||||
background-color: @pushbutton_cornerbtn_pressed_bg;
|
||||
}
|
||||
@ -294,11 +252,6 @@ QPushButton[CornerBtn="true"]:disabled {
|
||||
background-color: @pushbutton_disabled_bg;
|
||||
}
|
||||
|
||||
QPushButton[CornerBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QPushButton[StatusBtn="true"] {
|
||||
font: bold;
|
||||
padding: 0px 2px 0px 2px;
|
||||
@ -308,14 +261,19 @@ QPushButton[StatusBtn="true"] {
|
||||
min-width: -1;
|
||||
}
|
||||
|
||||
QPushButton[StatusBtn="true"]:hover {
|
||||
background-color: @pushbutton_statusbtn_hover_bg;
|
||||
QPushButton[StatusBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QPushButton[StatusBtn="true"]:focus {
|
||||
background-color: @pushbutton_statusbtn_focus_bg;;
|
||||
}
|
||||
|
||||
QPushButton[StatusBtn="true"]:hover {
|
||||
background-color: @pushbutton_statusbtn_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton[StatusBtn="true"]:pressed {
|
||||
background-color: @pushbutton_statusbtn_pressed_bg;
|
||||
}
|
||||
@ -325,11 +283,6 @@ QPushButton[StatusBtn="true"]:disabled {
|
||||
background-color: @pushbutton_disabled_bg;
|
||||
}
|
||||
|
||||
QPushButton[StatusBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QPushButton[FlatBtn="true"] {
|
||||
padding: 4px;
|
||||
margin: 0px;
|
||||
@ -338,14 +291,19 @@ QPushButton[FlatBtn="true"] {
|
||||
min-width: -1;
|
||||
}
|
||||
|
||||
QPushButton[FlatBtn="true"]:hover {
|
||||
background-color: @pushbutton_flatbtn_hover_bg;
|
||||
QPushButton[FlatBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QPushButton[FlatBtn="true"]:focus {
|
||||
background-color: @pushbutton_flatbtn_focus_bg;
|
||||
}
|
||||
|
||||
QPushButton[FlatBtn="true"]:hover {
|
||||
background-color: @pushbutton_flatbtn_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton[FlatBtn="true"]:pressed {
|
||||
background-color: @pushbutton_flatbtn_pressed_bg;
|
||||
}
|
||||
@ -355,11 +313,6 @@ QPushButton[FlatBtn="true"]:disabled {
|
||||
background-color: @pushbutton_disabled_bg;
|
||||
}
|
||||
|
||||
QPushButton[FlatBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QPushButton[SelectionBtn="true"] {
|
||||
padding: 4px 10px 4px 10px;
|
||||
border: none;
|
||||
@ -369,14 +322,19 @@ QPushButton[SelectionBtn="true"] {
|
||||
min-width: -1;
|
||||
}
|
||||
|
||||
QPushButton[SelectionBtn="true"]:hover {
|
||||
background-color: @pushbutton_selectionbtn_hover_bg;
|
||||
QPushButton[SelectionBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QPushButton[SelectionBtn="true"]:focus {
|
||||
background-color: @pushbutton_selectionbtn_focus_bg;
|
||||
}
|
||||
|
||||
QPushButton[SelectionBtn="true"]:hover {
|
||||
background-color: @pushbutton_selectionbtn_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton[SelectionBtn="true"]:pressed {
|
||||
background-color: @pushbutton_selectionbtn_pressed_bg;
|
||||
}
|
||||
@ -386,11 +344,6 @@ QPushButton[SelectionBtn="true"]:disabled {
|
||||
background-color: @pushbutton_disabled_bg;
|
||||
}
|
||||
|
||||
QPushButton[SelectionBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QPushButton[TitleBtn="true"] {
|
||||
padding: 4px;
|
||||
margin: 0px;
|
||||
@ -399,14 +352,19 @@ QPushButton[TitleBtn="true"] {
|
||||
min-width: -1;
|
||||
}
|
||||
|
||||
QPushButton[TitleBtn="true"]:hover {
|
||||
background-color: @pushbutton_titlebtn_hover_bg;
|
||||
QPushButton[TitleBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: @pushbutton_titlebtn_bg;
|
||||
}
|
||||
|
||||
QPushButton[TitleBtn="true"]:focus {
|
||||
background-color: @pushbutton_titlebtn_focus_bg;
|
||||
}
|
||||
|
||||
QPushButton[TitleBtn="true"]:hover {
|
||||
background-color: @pushbutton_titlebtn_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton[TitleBtn="true"]:pressed {
|
||||
background-color: @pushbutton_titlebtn_pressed_bg;
|
||||
}
|
||||
@ -416,11 +374,6 @@ QPushButton[TitleBtn="true"]:disabled {
|
||||
background-color: @pushbutton_disabled_bg;
|
||||
}
|
||||
|
||||
QPushButton[TitleBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: @pushbutton_titlebtn_bg;
|
||||
}
|
||||
|
||||
QPushButton[DangerBtn="true"] {
|
||||
color: @pushbutton_dangerbtn_fg;
|
||||
border: none;
|
||||
@ -428,21 +381,25 @@ QPushButton[DangerBtn="true"] {
|
||||
min-width: -1;
|
||||
}
|
||||
|
||||
QPushButton[DangerBtn="true"]:hover {
|
||||
background-color: @pushbutton_dangerbtn_hover_bg;
|
||||
QPushButton[DangerBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: @pushbutton_dangerbtn_bg;
|
||||
}
|
||||
|
||||
QPushButton[DangerBtn="true"]:focus {
|
||||
background-color: @pushbutton_dangerbtn_focus_bg;
|
||||
}
|
||||
|
||||
QPushButton[DangerBtn="true"]:pressed {
|
||||
QPushButton[DangerBtn="true"]:checked {
|
||||
background-color: @pushbutton_dangerbtn_pressed_bg;
|
||||
}
|
||||
|
||||
QPushButton[DangerBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: @pushbutton_dangerbtn_bg;
|
||||
QPushButton[DangerBtn="true"]:hover {
|
||||
background-color: @pushbutton_dangerbtn_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton[DangerBtn="true"]:pressed {
|
||||
background-color: @pushbutton_dangerbtn_pressed_bg;
|
||||
}
|
||||
|
||||
QPushButton[DangerBtn="true"]:disabled {
|
||||
@ -459,28 +416,28 @@ QPushButton[ToolBoxActiveBtn="true"] {
|
||||
min-width: -1;
|
||||
}
|
||||
|
||||
QPushButton[ToolBoxActiveBtn="true"]:focus {
|
||||
background-color: @pushbutton_toolboxbtn_active_focus_bg;
|
||||
QPushButton[ToolBoxActiveBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: @pushbutton_toolboxbtn_active_bg;
|
||||
}
|
||||
|
||||
QPushButton[ToolBoxActiveBtn="true"]:hover {
|
||||
background-color: @pushbutton_toolboxbtn_active_hover_bg;
|
||||
QPushButton[ToolBoxActiveBtn="true"]:focus {
|
||||
background-color: @pushbutton_toolboxbtn_active_focus_bg;
|
||||
}
|
||||
|
||||
QPushButton[ToolBoxActiveBtn="true"]:pressed {
|
||||
background-color: @pushbutton_toolboxbtn_active_pressed_bg;
|
||||
}
|
||||
|
||||
QPushButton[ToolBoxActiveBtn="true"]:hover {
|
||||
background-color: @pushbutton_toolboxbtn_active_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton[ToolBoxActiveBtn="true"]:disabled {
|
||||
color: @pushbutton_disabled_fg;
|
||||
background-color: @pushbutton_disabled_bg;
|
||||
}
|
||||
|
||||
QPushButton[ToolBoxActiveBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: @pushbutton_toolboxbtn_active_bg;
|
||||
}
|
||||
|
||||
QPushButton[AvatarBtn="true"] {
|
||||
padding: 2px 4px 2px 4px;
|
||||
margin: 0px;
|
||||
@ -489,14 +446,19 @@ QPushButton[AvatarBtn="true"] {
|
||||
min-width: -1;
|
||||
}
|
||||
|
||||
QPushButton[AvatarBtn="true"]:hover {
|
||||
background-color: @pushbutton_avatarbtn_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton[AvatarBtn="true"]:focus {
|
||||
background-color: @pushbutton_avatarbtn_focus_bg;;
|
||||
}
|
||||
|
||||
QPushButton[AvatarBtn="true"]:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QPushButton[AvatarBtn="true"]:hover {
|
||||
background-color: @pushbutton_avatarbtn_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton[AvatarBtn="true"]:pressed {
|
||||
background-color: @pushbutton_avatarbtn_pressed_bg;
|
||||
}
|
||||
@ -506,9 +468,51 @@ QPushButton[AvatarBtn="true"]:disabled {
|
||||
background-color: @pushbutton_disabled_bg;
|
||||
}
|
||||
|
||||
QPushButton[AvatarBtn="true"]:default {
|
||||
QPushButton {
|
||||
color: @pushbutton_fg;
|
||||
background: @pushbutton_bg;
|
||||
border: 1px solid @pushbutton_border;
|
||||
padding: 3px;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
QPushButton:focus {
|
||||
background-color: @pushbutton_focus_bg;
|
||||
}
|
||||
|
||||
QPushButton:checked {
|
||||
background-color: @pushbutton_checked_bg;
|
||||
}
|
||||
|
||||
QPushButton:checked:hover {
|
||||
background-color: @pushbutton_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton:flat {
|
||||
border: none;
|
||||
}
|
||||
|
||||
QPushButton:default {
|
||||
border: 1px solid @pushbutton_default_border;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QPushButton:hover {
|
||||
background-color: @pushbutton_hover_bg;
|
||||
}
|
||||
|
||||
QPushButton:pressed {
|
||||
background-color: @pushbutton_pressed_bg;
|
||||
}
|
||||
|
||||
QPushButton:disabled {
|
||||
color: @pushbutton_disabled_fg;
|
||||
background-color: @pushbutton_disabled_bg;
|
||||
}
|
||||
|
||||
QPushButton::menu-indicator {
|
||||
image: url(arrow_dropdown.svg);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
VButtonMenuItem {
|
||||
@ -544,11 +548,11 @@ VButtonMenuItem[Heading6="true"] {
|
||||
font-size: 14pt;
|
||||
}
|
||||
|
||||
VButtonMenuItem:hover {
|
||||
VButtonMenuItem:focus {
|
||||
background-color: @menubar_item_selected_bg;
|
||||
}
|
||||
|
||||
VButtonMenuItem:focus {
|
||||
VButtonMenuItem:hover {
|
||||
background-color: @menubar_item_selected_bg;
|
||||
}
|
||||
|
||||
|
@ -149,6 +149,9 @@ enable_compact_mode=true
|
||||
; Whether enable tools dock widget
|
||||
tools_dock_checked=true
|
||||
|
||||
; Whether enable search dock widget
|
||||
search_dock_checked=false
|
||||
|
||||
; Whether show menu bar
|
||||
menu_bar_checked=true
|
||||
|
||||
@ -201,6 +204,10 @@ single_click_close_previous_tab=true
|
||||
; Whether enable auto wildcard match in simple search like list and tree widgets
|
||||
enable_wildcard_in_simple_search=true
|
||||
|
||||
; Search options
|
||||
; scope,object,target,engine,option,pattern
|
||||
search_options=4,2,7,0,0,""
|
||||
|
||||
[export]
|
||||
; Path of the wkhtmltopdf tool
|
||||
wkhtmltopdf=wkhtmltopdf
|
||||
@ -279,6 +286,8 @@ Find=Ctrl+F
|
||||
FindNext=F3
|
||||
; Find previous occurence
|
||||
FindPrevious=Shift+F3
|
||||
; Advanced find
|
||||
AdvancedFind=Ctrl+Alt+F
|
||||
; Recover last closed file
|
||||
LastClosedFile=Ctrl+Shift+T
|
||||
; Activate next tab
|
||||
@ -311,6 +320,8 @@ OnePanelView=P
|
||||
DiscardAndRead=Q
|
||||
; Toggle Tools dock widget
|
||||
ToolsDock=T
|
||||
; Toggle Search dock widget
|
||||
SearchDock=C
|
||||
; Close current note
|
||||
CloseNote=X
|
||||
; Show shortcuts help document
|
||||
|
14
src/src.pro
14
src/src.pro
@ -113,7 +113,11 @@ SOURCES += main.cpp\
|
||||
vstyleditemdelegate.cpp \
|
||||
vtreewidget.cpp \
|
||||
dialog/vexportdialog.cpp \
|
||||
vexporter.cpp
|
||||
vexporter.cpp \
|
||||
vsearcher.cpp \
|
||||
vsearch.cpp \
|
||||
vsearchresulttree.cpp \
|
||||
vsearchengine.cpp
|
||||
|
||||
HEADERS += vmainwindow.h \
|
||||
vdirectorytree.h \
|
||||
@ -214,7 +218,13 @@ HEADERS += vmainwindow.h \
|
||||
vtreewidget.h \
|
||||
dialog/vexportdialog.h \
|
||||
vexporter.h \
|
||||
vwordcountinfo.h
|
||||
vwordcountinfo.h \
|
||||
vsearcher.h \
|
||||
vsearch.h \
|
||||
vsearchresulttree.h \
|
||||
isearchengine.h \
|
||||
vsearchconfig.h \
|
||||
vsearchengine.h
|
||||
|
||||
RESOURCES += \
|
||||
vnote.qrc \
|
||||
|
@ -522,13 +522,18 @@ bool VUtils::isImageURLText(const QString &p_url)
|
||||
|
||||
qreal VUtils::calculateScaleFactor()
|
||||
{
|
||||
// const qreal refHeight = 1152;
|
||||
// const qreal refWidth = 2048;
|
||||
const qreal refDpi = 96;
|
||||
static qreal factor = -1;
|
||||
|
||||
qreal dpi = QGuiApplication::primaryScreen()->logicalDotsPerInch();
|
||||
qreal factor = dpi / refDpi;
|
||||
return factor < 1 ? 1 : factor;
|
||||
if (factor < 0) {
|
||||
const qreal refDpi = 96;
|
||||
qreal dpi = QGuiApplication::primaryScreen()->logicalDotsPerInch();
|
||||
factor = dpi / refDpi;
|
||||
if (factor < 1) {
|
||||
factor = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return factor;
|
||||
}
|
||||
|
||||
bool VUtils::realEqual(qreal p_a, qreal p_b)
|
||||
|
@ -104,8 +104,6 @@ void VConfigManager::initialize()
|
||||
curRenderBackgroundColor = getConfigFromSettings("global",
|
||||
"current_render_background_color").toString();
|
||||
|
||||
m_toolsDockChecked = getConfigFromSettings("global", "tools_dock_checked").toBool();
|
||||
|
||||
m_findCaseSensitive = getConfigFromSettings("global",
|
||||
"find_case_sensitive").toBool();
|
||||
m_findWholeWordOnly = getConfigFromSettings("global",
|
||||
@ -498,11 +496,9 @@ QString VConfigManager::fetchDirConfigFilePath(const QString &p_path)
|
||||
if (!dir.rename(c_obsoleteDirConfigFile, c_dirConfigFile)) {
|
||||
fileName = c_obsoleteDirConfigFile;
|
||||
}
|
||||
qDebug() << "rename old directory config file:" << fileName;
|
||||
}
|
||||
|
||||
QString filePath = QDir::cleanPath(dir.filePath(fileName));
|
||||
qDebug() << "use directory config file:" << filePath;
|
||||
return filePath;
|
||||
}
|
||||
|
||||
@ -1473,6 +1469,7 @@ void VConfigManager::resetConfigurations()
|
||||
void VConfigManager::resetLayoutConfigurations()
|
||||
{
|
||||
resetDefaultConfig("global", "tools_dock_checked");
|
||||
resetDefaultConfig("global", "search_dock_checked");
|
||||
resetDefaultConfig("global", "menu_bar_checked");
|
||||
resetDefaultConfig("global", "enable_compact_mode");
|
||||
|
||||
|
@ -184,6 +184,9 @@ public:
|
||||
bool getToolsDockChecked() const;
|
||||
void setToolsDockChecked(bool p_checked);
|
||||
|
||||
bool getSearchDockChecked() const;
|
||||
void setSearchDockChecked(bool p_checked);
|
||||
|
||||
const QByteArray &getMainWindowGeometry() const;
|
||||
void setMainWindowGeometry(const QByteArray &p_geometry);
|
||||
|
||||
@ -466,6 +469,9 @@ public:
|
||||
QStringList getCustomExport() const;
|
||||
void setCustomExport(const QStringList &p_exp);
|
||||
|
||||
QStringList getSearchOptions() const;
|
||||
void setSearchOptions(const QStringList &p_opts);
|
||||
|
||||
private:
|
||||
// Look up a config from user and default settings.
|
||||
QVariant getConfigFromSettings(const QString §ion, const QString &key) const;
|
||||
@ -605,8 +611,6 @@ private:
|
||||
|
||||
QString curRenderBackgroundColor;
|
||||
|
||||
bool m_toolsDockChecked;
|
||||
|
||||
QByteArray m_mainWindowGeometry;
|
||||
QByteArray m_mainWindowState;
|
||||
QByteArray m_mainSplitterState;
|
||||
@ -1153,14 +1157,26 @@ inline void VConfigManager::setCurRenderBackgroundColor(const QString &colorName
|
||||
|
||||
inline bool VConfigManager::getToolsDockChecked() const
|
||||
{
|
||||
return m_toolsDockChecked;
|
||||
return getConfigFromSettings("global", "tools_dock_checked").toBool();
|
||||
}
|
||||
|
||||
inline void VConfigManager::setToolsDockChecked(bool p_checked)
|
||||
{
|
||||
m_toolsDockChecked = p_checked;
|
||||
setConfigToSettings("global", "tools_dock_checked",
|
||||
m_toolsDockChecked);
|
||||
setConfigToSettings("global",
|
||||
"tools_dock_checked",
|
||||
p_checked);
|
||||
}
|
||||
|
||||
inline bool VConfigManager::getSearchDockChecked() const
|
||||
{
|
||||
return getConfigFromSettings("global", "search_dock_checked").toBool();
|
||||
}
|
||||
|
||||
inline void VConfigManager::setSearchDockChecked(bool p_checked)
|
||||
{
|
||||
setConfigToSettings("global",
|
||||
"search_dock_checked",
|
||||
p_checked);
|
||||
}
|
||||
|
||||
inline const QByteArray& VConfigManager::getMainWindowGeometry() const
|
||||
@ -2146,4 +2162,15 @@ inline void VConfigManager::setCustomExport(const QStringList &p_exp)
|
||||
{
|
||||
setConfigToSettings("export", "custom_export", p_exp);
|
||||
}
|
||||
|
||||
inline QStringList VConfigManager::getSearchOptions() const
|
||||
{
|
||||
return getConfigFromSettings("global",
|
||||
"search_options").toStringList();
|
||||
}
|
||||
|
||||
inline void VConfigManager::setSearchOptions(const QStringList &p_opts)
|
||||
{
|
||||
setConfigToSettings("global", "search_options", p_opts);
|
||||
}
|
||||
#endif // VCONFIGMANAGER_H
|
||||
|
@ -191,12 +191,14 @@ void VDirectoryTree::setNotebook(VNotebook *p_notebook)
|
||||
|
||||
void VDirectoryTree::fillTreeItem(QTreeWidgetItem *p_item, VDirectory *p_directory)
|
||||
{
|
||||
static QIcon itemIcon = VIconUtils::treeViewIcon(":/resources/icons/dir_item.svg");
|
||||
|
||||
int col = 0;
|
||||
QString name = p_directory->getName();
|
||||
p_item->setText(col, name);
|
||||
p_item->setToolTip(col, name);
|
||||
p_item->setData(col, Qt::UserRole, QVariant::fromValue(p_directory));
|
||||
p_item->setIcon(col, VIconUtils::treeViewIcon(":/resources/icons/dir_item.svg"));
|
||||
p_item->setIcon(col, itemIcon);
|
||||
}
|
||||
|
||||
void VDirectoryTree::updateDirectoryTree()
|
||||
|
@ -188,6 +188,7 @@ void VEdit::insertLink()
|
||||
"",
|
||||
linkText,
|
||||
linkUrl,
|
||||
false,
|
||||
this);
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
linkText = dialog.getLinkText();
|
||||
|
@ -570,6 +570,20 @@ VEditTab *VEditArea::getTab(int p_winIdx, int p_tabIdx) const
|
||||
return win->getTab(p_tabIdx);
|
||||
}
|
||||
|
||||
VEditTab *VEditArea::getTab(const VFile *p_file) const
|
||||
{
|
||||
int nrWin = splitter->count();
|
||||
for (int winIdx = 0; winIdx < nrWin; ++winIdx) {
|
||||
VEditWindow *win = getWindow(winIdx);
|
||||
int tabIdx = win->findTabByFile(p_file);
|
||||
if (tabIdx != -1) {
|
||||
return win->getTab(tabIdx);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QVector<VEditTabInfo> VEditArea::getAllTabsInfo() const
|
||||
{
|
||||
QVector<VEditTabInfo> tabs;
|
||||
|
@ -44,6 +44,9 @@ public:
|
||||
// Return the @p_tabIdx tab in the @p_winIdx window.
|
||||
VEditTab *getTab(int p_winIdx, int p_tabIdx) const;
|
||||
|
||||
// Return the tab for @p_file file.
|
||||
VEditTab *getTab(const VFile *p_file) const;
|
||||
|
||||
// Return VEditTabInfo of all edit tabs.
|
||||
QVector<VEditTabInfo> getAllTabsInfo() const;
|
||||
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "dialog/vtipsdialog.h"
|
||||
#include "vcart.h"
|
||||
#include "dialog/vexportdialog.h"
|
||||
#include "vsearcher.h"
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
@ -148,6 +149,7 @@ void VMainWindow::registerCaptainAndNavigationTargets()
|
||||
m_captain->registerNavigationTarget(m_toolBox);
|
||||
m_captain->registerNavigationTarget(outline);
|
||||
m_captain->registerNavigationTarget(m_snippetList);
|
||||
m_captain->registerNavigationTarget(m_searcher);
|
||||
|
||||
// Register Captain mode targets.
|
||||
m_captain->registerCaptainTarget(tr("AttachmentList"),
|
||||
@ -174,6 +176,10 @@ void VMainWindow::registerCaptainAndNavigationTargets()
|
||||
g_config->getCaptainShortcutKeySequence("ToolsDock"),
|
||||
this,
|
||||
toggleToolsDockByCaptain);
|
||||
m_captain->registerCaptainTarget(tr("SearchDock"),
|
||||
g_config->getCaptainShortcutKeySequence("SearchDock"),
|
||||
this,
|
||||
toggleSearchDockByCaptain);
|
||||
m_captain->registerCaptainTarget(tr("CloseNote"),
|
||||
g_config->getCaptainShortcutKeySequence("CloseNote"),
|
||||
this,
|
||||
@ -1073,6 +1079,17 @@ void VMainWindow::initEditMenu()
|
||||
connect(m_findReplaceAct, &QAction::triggered,
|
||||
this, &VMainWindow::openFindDialog);
|
||||
|
||||
QAction *advFindAct = new QAction(tr("Advanced Find"), this);
|
||||
advFindAct->setToolTip(tr("Advanced find within VNote"));
|
||||
keySeq = g_config->getShortcutKeySequence("AdvancedFind");
|
||||
qDebug() << "set AdvancedFind shortcut to" << keySeq;
|
||||
advFindAct->setShortcut(QKeySequence(keySeq));
|
||||
connect(advFindAct, &QAction::triggered,
|
||||
this, [this]() {
|
||||
m_searchDock->setVisible(true);
|
||||
m_searcher->focusToSearch();
|
||||
});
|
||||
|
||||
m_findNextAct = new QAction(tr("Find Next"), this);
|
||||
m_findNextAct->setToolTip(tr("Find next occurence"));
|
||||
keySeq = g_config->getShortcutKeySequence("FindNext");
|
||||
@ -1188,6 +1205,8 @@ void VMainWindow::initEditMenu()
|
||||
QMenu *findReplaceMenu = editMenu->addMenu(tr("Find/Replace"));
|
||||
findReplaceMenu->setToolTipsVisible(true);
|
||||
findReplaceMenu->addAction(m_findReplaceAct);
|
||||
findReplaceMenu->addAction(advFindAct);
|
||||
findReplaceMenu->addSeparator();
|
||||
findReplaceMenu->addAction(m_findNextAct);
|
||||
findReplaceMenu->addAction(m_findPreviousAct);
|
||||
findReplaceMenu->addAction(m_replaceAct);
|
||||
@ -1268,9 +1287,22 @@ void VMainWindow::initEditMenu()
|
||||
|
||||
void VMainWindow::initDockWindows()
|
||||
{
|
||||
toolDock = new QDockWidget(tr("Tools"), this);
|
||||
toolDock->setObjectName("ToolsDock");
|
||||
toolDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
|
||||
setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::West);
|
||||
setTabPosition(Qt::RightDockWidgetArea, QTabWidget::East);
|
||||
setTabPosition(Qt::TopDockWidgetArea, QTabWidget::North);
|
||||
setTabPosition(Qt::BottomDockWidgetArea, QTabWidget::South);
|
||||
|
||||
setDockNestingEnabled(true);
|
||||
|
||||
initToolsDock();
|
||||
initSearchDock();
|
||||
}
|
||||
|
||||
void VMainWindow::initToolsDock()
|
||||
{
|
||||
m_toolDock = new QDockWidget(tr("Tools"), this);
|
||||
m_toolDock->setObjectName("ToolsDock");
|
||||
m_toolDock->setAllowedAreas(Qt::AllDockWidgetAreas);
|
||||
|
||||
// Outline tree.
|
||||
outline = new VOutline(this);
|
||||
@ -1298,16 +1330,35 @@ void VMainWindow::initDockWindows()
|
||||
":/resources/icons/cart.svg",
|
||||
tr("Cart"));
|
||||
|
||||
toolDock->setWidget(m_toolBox);
|
||||
addDockWidget(Qt::RightDockWidgetArea, toolDock);
|
||||
m_toolDock->setWidget(m_toolBox);
|
||||
addDockWidget(Qt::RightDockWidgetArea, m_toolDock);
|
||||
|
||||
QAction *toggleAct = toolDock->toggleViewAction();
|
||||
QAction *toggleAct = m_toolDock->toggleViewAction();
|
||||
toggleAct->setToolTip(tr("Toggle the tools dock widget"));
|
||||
VUtils::fixTextWithCaptainShortcut(toggleAct, "ToolsDock");
|
||||
|
||||
m_viewMenu->addAction(toggleAct);
|
||||
}
|
||||
|
||||
void VMainWindow::initSearchDock()
|
||||
{
|
||||
m_searchDock = new QDockWidget(tr("Search"), this);
|
||||
m_searchDock->setObjectName("SearchDock");
|
||||
m_searchDock->setAllowedAreas(Qt::AllDockWidgetAreas);
|
||||
|
||||
m_searcher = new VSearcher(this);
|
||||
|
||||
m_searchDock->setWidget(m_searcher);
|
||||
|
||||
addDockWidget(Qt::RightDockWidgetArea, m_searchDock);
|
||||
|
||||
QAction *toggleAct = m_searchDock->toggleViewAction();
|
||||
toggleAct->setToolTip(tr("Toggle the search dock widget"));
|
||||
VUtils::fixTextWithCaptainShortcut(toggleAct, "SearchDock");
|
||||
|
||||
m_viewMenu->addAction(toggleAct);
|
||||
}
|
||||
|
||||
void VMainWindow::importNoteFromFile()
|
||||
{
|
||||
static QString lastPath = QDir::homePath();
|
||||
@ -2162,7 +2213,8 @@ void VMainWindow::saveStateAndGeometry()
|
||||
{
|
||||
g_config->setMainWindowGeometry(saveGeometry());
|
||||
g_config->setMainWindowState(saveState());
|
||||
g_config->setToolsDockChecked(toolDock->isVisible());
|
||||
g_config->setToolsDockChecked(m_toolDock->isVisible());
|
||||
g_config->setSearchDockChecked(m_searchDock->isVisible());
|
||||
|
||||
if (m_panelViewState == PanelViewState::CompactMode) {
|
||||
g_config->setNaviSplitterState(m_naviSplitter->saveState());
|
||||
@ -2181,11 +2233,14 @@ void VMainWindow::restoreStateAndGeometry()
|
||||
if (!geometry.isEmpty()) {
|
||||
restoreGeometry(geometry);
|
||||
}
|
||||
|
||||
const QByteArray &state = g_config->getMainWindowState();
|
||||
if (!state.isEmpty()) {
|
||||
restoreState(state);
|
||||
}
|
||||
toolDock->setVisible(g_config->getToolsDockChecked());
|
||||
|
||||
m_toolDock->setVisible(g_config->getToolsDockChecked());
|
||||
m_searchDock->setVisible(g_config->getSearchDockChecked());
|
||||
|
||||
const QByteArray &splitterState = g_config->getMainSplitterState();
|
||||
if (!splitterState.isEmpty()) {
|
||||
@ -2260,6 +2315,32 @@ bool VMainWindow::locateFile(VFile *p_file)
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool VMainWindow::locateDirectory(VDirectory *p_directory)
|
||||
{
|
||||
bool ret = false;
|
||||
if (!p_directory) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
VNotebook *notebook = p_directory->getNotebook();
|
||||
if (notebookSelector->locateNotebook(notebook)) {
|
||||
while (directoryTree->currentNotebook() != notebook) {
|
||||
QCoreApplication::sendPostedEvents();
|
||||
}
|
||||
|
||||
ret = directoryTree->locateDirectory(p_directory);
|
||||
}
|
||||
|
||||
// Open the directory and file panels after location.
|
||||
if (m_panelViewState == PanelViewState::CompactMode) {
|
||||
compactModeView();
|
||||
} else {
|
||||
twoPanelView();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void VMainWindow::handleFindDialogTextChanged(const QString &p_text, uint /* p_options */)
|
||||
{
|
||||
bool enabled = true;
|
||||
@ -2659,7 +2740,21 @@ bool VMainWindow::toggleToolsDockByCaptain(void *p_target, void *p_data)
|
||||
{
|
||||
Q_UNUSED(p_data);
|
||||
VMainWindow *obj = static_cast<VMainWindow *>(p_target);
|
||||
obj->toolDock->setVisible(!obj->toolDock->isVisible());
|
||||
obj->m_toolDock->setVisible(!obj->m_toolDock->isVisible());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VMainWindow::toggleSearchDockByCaptain(void *p_target, void *p_data)
|
||||
{
|
||||
Q_UNUSED(p_data);
|
||||
VMainWindow *obj = static_cast<VMainWindow *>(p_target);
|
||||
bool visible = obj->m_searchDock->isVisible();
|
||||
obj->m_searchDock->setVisible(!visible);
|
||||
if (!visible) {
|
||||
obj->m_searcher->focusToSearch();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,7 @@ class VButtonWithWidget;
|
||||
class VAttachmentList;
|
||||
class VSnippetList;
|
||||
class VCart;
|
||||
class VSearcher;
|
||||
class QPrinter;
|
||||
|
||||
enum class PanelViewState
|
||||
@ -60,6 +61,9 @@ public:
|
||||
// Returns true if the location succeeds.
|
||||
bool locateFile(VFile *p_file);
|
||||
|
||||
// Returns true if the location succeeds.
|
||||
bool locateDirectory(VDirectory *p_directory);
|
||||
|
||||
VFileList *getFileList() const;
|
||||
|
||||
VEditArea *getEditArea() const;
|
||||
@ -68,6 +72,10 @@ public:
|
||||
|
||||
VCart *getCart() const;
|
||||
|
||||
VDirectoryTree *getDirectoryTree() const;
|
||||
|
||||
VNotebookSelector *getNotebookSelector() const;
|
||||
|
||||
// View and edit the information of @p_file, which is an orphan file.
|
||||
void editOrphanFileInfo(VFile *p_file);
|
||||
|
||||
@ -205,6 +213,10 @@ private:
|
||||
|
||||
void initDockWindows();
|
||||
|
||||
void initToolsDock();
|
||||
|
||||
void initSearchDock();
|
||||
|
||||
void initRenderBackgroundMenu(QMenu *menu);
|
||||
|
||||
void initRenderStyleMenu(QMenu *p_menu);
|
||||
@ -280,6 +292,8 @@ private:
|
||||
|
||||
static bool toggleToolsDockByCaptain(void *p_target, void *p_data);
|
||||
|
||||
static bool toggleSearchDockByCaptain(void *p_target, void *p_data);
|
||||
|
||||
static bool closeFileByCaptain(void *p_target, void *p_data);
|
||||
|
||||
static bool shortcutsHelpByCaptain(void *p_target, void *p_data);
|
||||
@ -311,7 +325,9 @@ private:
|
||||
|
||||
VEditArea *editArea;
|
||||
|
||||
QDockWidget *toolDock;
|
||||
QDockWidget *m_toolDock;
|
||||
|
||||
QDockWidget *m_searchDock;
|
||||
|
||||
// Tool box in the dock widget.
|
||||
VToolBox *m_toolBox;
|
||||
@ -324,6 +340,9 @@ private:
|
||||
// View and manage cart.
|
||||
VCart *m_cart;
|
||||
|
||||
// Advanced search.
|
||||
VSearcher *m_searcher;
|
||||
|
||||
VFindReplaceDialog *m_findReplaceDialog;
|
||||
|
||||
VVimCmdLineEdit *m_vimCmd;
|
||||
@ -453,4 +472,13 @@ inline VCart *VMainWindow::getCart() const
|
||||
return m_cart;
|
||||
}
|
||||
|
||||
inline VDirectoryTree *VMainWindow::getDirectoryTree() const
|
||||
{
|
||||
return directoryTree;
|
||||
}
|
||||
|
||||
inline VNotebookSelector *VMainWindow::getNotebookSelector() const
|
||||
{
|
||||
return notebookSelector;
|
||||
}
|
||||
#endif // VMAINWINDOW_H
|
||||
|
@ -95,6 +95,8 @@ VMdEditor::VMdEditor(VFile *p_file,
|
||||
connect(this, &VTextEdit::cursorPositionChanged,
|
||||
this, &VMdEditor::updateCurrentHeader);
|
||||
|
||||
setDisplayScaleFactor(VUtils::calculateScaleFactor());
|
||||
|
||||
updateFontAndPalette();
|
||||
|
||||
updateConfig();
|
||||
|
@ -314,6 +314,17 @@ VDirectory *VNote::getInternalDirectory(const QString &p_path)
|
||||
|
||||
}
|
||||
|
||||
VNotebook *VNote::getNotebook(const QString &p_path)
|
||||
{
|
||||
for (auto & nb : m_notebooks) {
|
||||
if (VUtils::equalPath(nb->getPath(), p_path)) {
|
||||
return nb;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void VNote::freeOrphanFiles()
|
||||
{
|
||||
for (int i = 0; i < m_externalFiles.size();) {
|
||||
|
@ -95,6 +95,10 @@ public:
|
||||
// Otherwise, returns NULL.
|
||||
VDirectory *getInternalDirectory(const QString &p_path);
|
||||
|
||||
// Given the path of a file, try to find it in all notebooks.
|
||||
// Returns a VNotebook struct if it is the root folder of a notebook.
|
||||
VNotebook *getNotebook(const QString &p_path);
|
||||
|
||||
void freeOrphanFiles();
|
||||
|
||||
// @p_renderBg: background color, empty to not specify given color.
|
||||
|
@ -243,5 +243,10 @@
|
||||
<file>resources/themes/v_moonlight/arrow_dropdown_disabled.svg</file>
|
||||
<file>resources/themes/v_pure/arrow_dropdown_disabled.svg</file>
|
||||
<file>resources/themes/v_white/arrow_dropdown_disabled.svg</file>
|
||||
<file>resources/icons/clear_search.svg</file>
|
||||
<file>resources/icons/search.svg</file>
|
||||
<file>resources/icons/search_advanced.svg</file>
|
||||
<file>resources/icons/search_console.svg</file>
|
||||
<file>resources/icons/note_item.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include <QString>
|
||||
#include <QStyleFactory>
|
||||
#include <QWidgetAction>
|
||||
#include <QLabel>
|
||||
|
||||
#include "veditwindow.h"
|
||||
#include "vnotefile.h"
|
||||
|
418
src/vsearch.cpp
Normal file
418
src/vsearch.cpp
Normal file
@ -0,0 +1,418 @@
|
||||
#include "vsearch.h"
|
||||
|
||||
#include "utils/vutils.h"
|
||||
#include "vfile.h"
|
||||
#include "vdirectory.h"
|
||||
#include "vnotebook.h"
|
||||
#include "veditarea.h"
|
||||
#include "vmainwindow.h"
|
||||
#include "vtableofcontent.h"
|
||||
#include "vsearchengine.h"
|
||||
|
||||
extern VMainWindow *g_mainWin;
|
||||
|
||||
VSearch::VSearch(QObject *p_parent)
|
||||
: QObject(p_parent),
|
||||
m_askedToStop(false),
|
||||
m_engine(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
QSharedPointer<VSearchResult> VSearch::search(const QVector<VFile *> &p_files)
|
||||
{
|
||||
Q_ASSERT(!askedToStop());
|
||||
|
||||
QSharedPointer<VSearchResult> result(new VSearchResult(this));
|
||||
|
||||
if (p_files.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!testTarget(VSearchConfig::Note)) {
|
||||
qDebug() << "search is not applicable for note";
|
||||
result->m_state = VSearchState::Success;
|
||||
return result;
|
||||
}
|
||||
|
||||
result->m_state = VSearchState::Busy;
|
||||
|
||||
for (auto const & it : p_files) {
|
||||
if (!it) {
|
||||
continue;
|
||||
}
|
||||
|
||||
searchFirstPhase(it, result, true);
|
||||
|
||||
if (askedToStop()) {
|
||||
qDebug() << "asked to cancel the search";
|
||||
result->m_state = VSearchState::Cancelled;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result->m_state == VSearchState::Busy) {
|
||||
result->m_state = VSearchState::Success;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QSharedPointer<VSearchResult> VSearch::search(VDirectory *p_directory)
|
||||
{
|
||||
Q_ASSERT(!askedToStop());
|
||||
|
||||
QSharedPointer<VSearchResult> result(new VSearchResult(this));
|
||||
|
||||
if (!p_directory) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((!testTarget(VSearchConfig::Note)
|
||||
&& !testTarget(VSearchConfig::Folder))
|
||||
|| testObject(VSearchConfig::Outline)) {
|
||||
qDebug() << "search is not applicable for folder";
|
||||
result->m_state = VSearchState::Success;
|
||||
return result;
|
||||
}
|
||||
|
||||
result->m_state = VSearchState::Busy;
|
||||
|
||||
searchFirstPhase(p_directory, result);
|
||||
|
||||
if (result->hasSecondPhaseItems()) {
|
||||
searchSecondPhase(result);
|
||||
} else if (result->m_state == VSearchState::Busy) {
|
||||
result->m_state = VSearchState::Success;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QSharedPointer<VSearchResult> VSearch::search(const QVector<VNotebook *> &p_notebooks)
|
||||
{
|
||||
Q_ASSERT(!askedToStop());
|
||||
|
||||
QSharedPointer<VSearchResult> result(new VSearchResult(this));
|
||||
|
||||
if (p_notebooks.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (testObject(VSearchConfig::Outline)) {
|
||||
qDebug() << "search is not applicable for notebook";
|
||||
result->m_state = VSearchState::Success;
|
||||
return result;
|
||||
}
|
||||
|
||||
result->m_state = VSearchState::Busy;
|
||||
|
||||
for (auto const & nb : p_notebooks) {
|
||||
if (!nb) {
|
||||
continue;
|
||||
}
|
||||
|
||||
searchFirstPhase(nb, result);
|
||||
|
||||
if (askedToStop()) {
|
||||
qDebug() << "asked to cancel the search";
|
||||
result->m_state = VSearchState::Cancelled;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result->hasSecondPhaseItems()) {
|
||||
searchSecondPhase(result);
|
||||
} else if (result->m_state == VSearchState::Busy) {
|
||||
result->m_state = VSearchState::Success;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void VSearch::searchFirstPhase(VFile *p_file,
|
||||
const QSharedPointer<VSearchResult> &p_result,
|
||||
bool p_searchContent)
|
||||
{
|
||||
Q_ASSERT(testTarget(VSearchConfig::Note));
|
||||
|
||||
QString name = p_file->getName();
|
||||
if (!m_patternReg.isEmpty()) {
|
||||
if (!matchOneLine(name, m_patternReg)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QString filePath = p_file->fetchPath();
|
||||
if (testObject(VSearchConfig::Name)) {
|
||||
if (matchOneLine(name, m_searchReg)) {
|
||||
VSearchResultItem *item = new VSearchResultItem(VSearchResultItem::Note,
|
||||
VSearchResultItem::LineNumber,
|
||||
name,
|
||||
filePath);
|
||||
QSharedPointer<VSearchResultItem> pitem(item);
|
||||
emit resultItemAdded(pitem);
|
||||
}
|
||||
}
|
||||
|
||||
if (testObject(VSearchConfig::Outline)) {
|
||||
VSearchResultItem *item = searchForOutline(p_file);
|
||||
if (item) {
|
||||
QSharedPointer<VSearchResultItem> pitem(item);
|
||||
emit resultItemAdded(pitem);
|
||||
}
|
||||
}
|
||||
|
||||
if (testObject(VSearchConfig::Content)) {
|
||||
// Search content in first phase.
|
||||
if (p_searchContent) {
|
||||
VSearchResultItem *item = searchForContent(p_file);
|
||||
if (item) {
|
||||
QSharedPointer<VSearchResultItem> pitem(item);
|
||||
emit resultItemAdded(pitem);
|
||||
}
|
||||
} else {
|
||||
// Add an item for second phase process.
|
||||
p_result->addSecondPhaseItem(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VSearch::searchFirstPhase(VDirectory *p_directory,
|
||||
const QSharedPointer<VSearchResult> &p_result)
|
||||
{
|
||||
if (!testTarget(VSearchConfig::Note)
|
||||
&& !testTarget(VSearchConfig::Folder)) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool opened = p_directory->isOpened();
|
||||
if (!opened && !p_directory->open()) {
|
||||
p_result->logError(QString("Fail to open folder %1.").arg(p_directory->fetchRelativePath()));
|
||||
p_result->m_state = VSearchState::Fail;
|
||||
return;
|
||||
}
|
||||
|
||||
if (testTarget(VSearchConfig::Folder)
|
||||
&& testObject(VSearchConfig::Name)) {
|
||||
QString text = p_directory->getName();
|
||||
if (matchOneLine(text, m_searchReg)) {
|
||||
VSearchResultItem *item = new VSearchResultItem(VSearchResultItem::Folder,
|
||||
VSearchResultItem::LineNumber,
|
||||
text,
|
||||
p_directory->fetchPath());
|
||||
QSharedPointer<VSearchResultItem> pitem(item);
|
||||
emit resultItemAdded(pitem);
|
||||
}
|
||||
}
|
||||
|
||||
// Search files.
|
||||
if (testTarget(VSearchConfig::Note)) {
|
||||
for (auto const & file : p_directory->getFiles()) {
|
||||
if (askedToStop()) {
|
||||
qDebug() << "asked to cancel the search";
|
||||
p_result->m_state = VSearchState::Cancelled;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
searchFirstPhase(file, p_result);
|
||||
}
|
||||
}
|
||||
|
||||
// Search subfolders.
|
||||
for (auto const & dir : p_directory->getSubDirs()) {
|
||||
if (askedToStop()) {
|
||||
qDebug() << "asked to cancel the search";
|
||||
p_result->m_state = VSearchState::Cancelled;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
searchFirstPhase(dir, p_result);
|
||||
}
|
||||
|
||||
exit:
|
||||
if (!opened) {
|
||||
p_directory->close();
|
||||
}
|
||||
}
|
||||
|
||||
void VSearch::searchFirstPhase(VNotebook *p_notebook,
|
||||
const QSharedPointer<VSearchResult> &p_result)
|
||||
{
|
||||
bool opened = p_notebook->isOpened();
|
||||
if (!opened && !p_notebook->open()) {
|
||||
p_result->logError(QString("Fail to open notebook %1.").arg(p_notebook->getName()));
|
||||
p_result->m_state = VSearchState::Fail;
|
||||
return;
|
||||
}
|
||||
|
||||
if (testTarget(VSearchConfig::Notebook)
|
||||
&& testObject(VSearchConfig::Name)) {
|
||||
QString text = p_notebook->getName();
|
||||
if (matchOneLine(text, m_searchReg)) {
|
||||
VSearchResultItem *item = new VSearchResultItem(VSearchResultItem::Notebook,
|
||||
VSearchResultItem::LineNumber,
|
||||
text,
|
||||
p_notebook->getPath());
|
||||
QSharedPointer<VSearchResultItem> pitem(item);
|
||||
emit resultItemAdded(pitem);
|
||||
}
|
||||
}
|
||||
|
||||
if (!testTarget(VSearchConfig::Note)
|
||||
&& !testTarget(VSearchConfig::Folder)) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Search for subfolders.
|
||||
for (auto const & dir : p_notebook->getRootDir()->getSubDirs()) {
|
||||
if (askedToStop()) {
|
||||
qDebug() << "asked to cancel the search";
|
||||
p_result->m_state = VSearchState::Cancelled;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
searchFirstPhase(dir, p_result);
|
||||
}
|
||||
|
||||
exit:
|
||||
if (!opened) {
|
||||
p_notebook->close();
|
||||
}
|
||||
}
|
||||
|
||||
VSearchResultItem *VSearch::searchForOutline(const VFile *p_file) const
|
||||
{
|
||||
VEditTab *tab = g_mainWin->getEditArea()->getTab(p_file);
|
||||
if (!tab) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const VTableOfContent &toc = tab->getOutline();
|
||||
const QVector<VTableOfContentItem> &table = toc.getTable();
|
||||
VSearchResultItem *item = NULL;
|
||||
for (auto const & it: table) {
|
||||
if (it.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!matchOneLine(it.m_name, m_searchReg)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!item) {
|
||||
item = new VSearchResultItem(VSearchResultItem::Note,
|
||||
VSearchResultItem::OutlineIndex,
|
||||
p_file->getName(),
|
||||
p_file->fetchPath());
|
||||
}
|
||||
|
||||
VSearchResultSubItem sitem(it.m_index, it.m_name);
|
||||
item->m_matches.append(sitem);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
VSearchResultItem *VSearch::searchForContent(const VFile *p_file) const
|
||||
{
|
||||
Q_ASSERT(p_file->isOpened());
|
||||
const QString &content = p_file->getContent();
|
||||
if (content.isEmpty()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
VSearchResultItem *item = NULL;
|
||||
int lineNum = 1;
|
||||
int pos = 0;
|
||||
int size = content.size();
|
||||
QRegExp newLineReg = QRegExp("\\n|\\r\\n|\\r");
|
||||
Qt::CaseSensitivity cs = testOption(VSearchConfig::CaseSensitive)
|
||||
? Qt::CaseSensitive : Qt::CaseInsensitive;
|
||||
while (pos < size) {
|
||||
int idx = content.indexOf(newLineReg, pos);
|
||||
if (idx == -1) {
|
||||
idx = size;
|
||||
}
|
||||
|
||||
if (idx > pos) {
|
||||
QString lineText = content.mid(pos, idx - pos);
|
||||
bool matched = false;
|
||||
if (m_contentSearchReg.isEmpty()) {
|
||||
matched = lineText.contains(m_config->m_keyword, cs);
|
||||
} else {
|
||||
matched = (m_contentSearchReg.indexIn(lineText) != -1);
|
||||
}
|
||||
|
||||
if (matched) {
|
||||
if (!item) {
|
||||
item = new VSearchResultItem(VSearchResultItem::Note,
|
||||
VSearchResultItem::LineNumber,
|
||||
p_file->getName(),
|
||||
p_file->fetchPath());
|
||||
}
|
||||
|
||||
VSearchResultSubItem sitem(lineNum, lineText);
|
||||
item->m_matches.append(sitem);
|
||||
}
|
||||
}
|
||||
|
||||
if (idx == size) {
|
||||
break;
|
||||
}
|
||||
|
||||
pos = idx + newLineReg.matchedLength();
|
||||
++lineNum;
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void VSearch::searchSecondPhase(const QSharedPointer<VSearchResult> &p_result)
|
||||
{
|
||||
delete m_engine;
|
||||
m_engine = NULL;
|
||||
|
||||
switch (m_config->m_engine) {
|
||||
case VSearchConfig::Internal:
|
||||
{
|
||||
m_engine = new VSearchEngine(this);
|
||||
m_engine->search(m_config, p_result);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
p_result->m_state = VSearchState::Success;
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_engine) {
|
||||
connect(m_engine, &ISearchEngine::finished,
|
||||
this, &VSearch::finished);
|
||||
connect(m_engine, &ISearchEngine::resultItemAdded,
|
||||
this, &VSearch::resultItemAdded);
|
||||
}
|
||||
}
|
||||
|
||||
void VSearch::clear()
|
||||
{
|
||||
m_config.clear();
|
||||
|
||||
if (m_engine) {
|
||||
m_engine->clear();
|
||||
|
||||
delete m_engine;
|
||||
m_engine = NULL;
|
||||
}
|
||||
|
||||
m_askedToStop = false;
|
||||
}
|
||||
|
||||
void VSearch::stop()
|
||||
{
|
||||
qDebug() << "VSearch asked to stop";
|
||||
m_askedToStop = true;
|
||||
|
||||
if (m_engine) {
|
||||
m_engine->stop();
|
||||
}
|
||||
}
|
162
src/vsearch.h
Normal file
162
src/vsearch.h
Normal file
@ -0,0 +1,162 @@
|
||||
#ifndef VSEARCH_H
|
||||
#define VSEARCH_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QSharedPointer>
|
||||
#include <QRegExp>
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include "vsearchconfig.h"
|
||||
|
||||
class VFile;
|
||||
class VDirectory;
|
||||
class VNotebook;
|
||||
class ISearchEngine;
|
||||
|
||||
|
||||
class VSearch : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VSearch(QObject *p_parent = nullptr);
|
||||
|
||||
void setConfig(QSharedPointer<VSearchConfig> p_config);
|
||||
|
||||
// Search list of files for CurrentNote and OpenedNotes.
|
||||
QSharedPointer<VSearchResult> search(const QVector<VFile *> &p_files);
|
||||
|
||||
// Search folder for CurrentFolder.
|
||||
QSharedPointer<VSearchResult> search(VDirectory *p_directory);
|
||||
|
||||
// Search folder for CurrentNotebook and AllNotebooks.
|
||||
QSharedPointer<VSearchResult> search(const QVector<VNotebook *> &p_notebooks);
|
||||
|
||||
// Clear resources after a search completed.
|
||||
void clear();
|
||||
|
||||
void stop();
|
||||
|
||||
signals:
|
||||
// Emitted when a new item added as result.
|
||||
void resultItemAdded(const QSharedPointer<VSearchResultItem> &p_item);
|
||||
|
||||
// Emitted when async task finished.
|
||||
void finished(const QSharedPointer<VSearchResult> &p_result);
|
||||
|
||||
private:
|
||||
bool askedToStop() const;
|
||||
|
||||
// @p_searchContent: whether search content in first phase.
|
||||
void searchFirstPhase(VFile *p_file,
|
||||
const QSharedPointer<VSearchResult> &p_result,
|
||||
bool p_searchContent = false);
|
||||
|
||||
void searchFirstPhase(VDirectory *p_directory,
|
||||
const QSharedPointer<VSearchResult> &p_result);
|
||||
|
||||
void searchFirstPhase(VNotebook *p_notebook,
|
||||
const QSharedPointer<VSearchResult> &p_result);
|
||||
|
||||
bool testTarget(VSearchConfig::Target p_target) const;
|
||||
|
||||
bool testObject(VSearchConfig::Object p_object) const;
|
||||
|
||||
bool testOption(VSearchConfig::Option p_option) const;
|
||||
|
||||
bool matchOneLine(const QString &p_text, const QRegExp &p_reg) const;
|
||||
|
||||
VSearchResultItem *searchForOutline(const VFile *p_file) const;
|
||||
|
||||
VSearchResultItem *searchForContent(const VFile *p_file) const;
|
||||
|
||||
void searchSecondPhase(const QSharedPointer<VSearchResult> &p_result);
|
||||
|
||||
bool m_askedToStop;
|
||||
|
||||
QSharedPointer<VSearchConfig> m_config;
|
||||
|
||||
ISearchEngine *m_engine;
|
||||
|
||||
// Search reg used for name, outline, tag.
|
||||
QRegExp m_searchReg;
|
||||
|
||||
// Search reg used for content.
|
||||
// We use raw string to speed up if it is empty.
|
||||
QRegExp m_contentSearchReg;
|
||||
|
||||
// Wildcard reg to for file name pattern.
|
||||
QRegExp m_patternReg;
|
||||
};
|
||||
|
||||
inline bool VSearch::askedToStop() const
|
||||
{
|
||||
QCoreApplication::processEvents();
|
||||
return m_askedToStop;
|
||||
}
|
||||
|
||||
inline void VSearch::setConfig(QSharedPointer<VSearchConfig> p_config)
|
||||
{
|
||||
m_config = p_config;
|
||||
|
||||
// Compile reg.
|
||||
const QString &keyword = m_config->m_keyword;
|
||||
m_contentSearchReg = QRegExp();
|
||||
if (keyword.isEmpty()) {
|
||||
m_searchReg = QRegExp();
|
||||
return;
|
||||
}
|
||||
|
||||
Qt::CaseSensitivity cs = testOption(VSearchConfig::CaseSensitive)
|
||||
? Qt::CaseSensitive : Qt::CaseInsensitive;
|
||||
if (testOption(VSearchConfig::RegularExpression)) {
|
||||
m_searchReg = QRegExp(keyword, cs);
|
||||
m_contentSearchReg = QRegExp(keyword, cs);
|
||||
} else {
|
||||
if (testOption(VSearchConfig::Fuzzy)) {
|
||||
QString wildcardText(keyword.size() * 2 + 1, '*');
|
||||
for (int i = 0, j = 1; i < keyword.size(); ++i, j += 2) {
|
||||
wildcardText[j] = keyword[i];
|
||||
}
|
||||
|
||||
m_searchReg = QRegExp(wildcardText, cs, QRegExp::Wildcard);
|
||||
} else {
|
||||
QString pattern = QRegExp::escape(keyword);
|
||||
if (testOption(VSearchConfig::WholeWordOnly)) {
|
||||
pattern = "\\b" + pattern + "\\b";
|
||||
|
||||
// We only use m_contentSearchReg when WholeWordOnly is checked.
|
||||
m_contentSearchReg = QRegExp(pattern, cs);
|
||||
}
|
||||
|
||||
m_searchReg = QRegExp(pattern, cs);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_config->m_pattern.isEmpty()) {
|
||||
m_patternReg = QRegExp();
|
||||
} else {
|
||||
m_patternReg = QRegExp(m_config->m_pattern, Qt::CaseInsensitive, QRegExp::Wildcard);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool VSearch::testTarget(VSearchConfig::Target p_target) const
|
||||
{
|
||||
return p_target & m_config->m_target;
|
||||
}
|
||||
|
||||
inline bool VSearch::testObject(VSearchConfig::Object p_object) const
|
||||
{
|
||||
return p_object & m_config->m_object;
|
||||
}
|
||||
|
||||
inline bool VSearch::testOption(VSearchConfig::Option p_option) const
|
||||
{
|
||||
return p_option & m_config->m_option;
|
||||
}
|
||||
|
||||
inline bool VSearch::matchOneLine(const QString &p_text, const QRegExp &p_reg) const
|
||||
{
|
||||
return p_reg.indexIn(p_text) != -1;
|
||||
}
|
||||
#endif // VSEARCH_H
|
273
src/vsearchconfig.h
Normal file
273
src/vsearchconfig.h
Normal file
@ -0,0 +1,273 @@
|
||||
#ifndef VSEARCHCONFIG_H
|
||||
#define VSEARCHCONFIG_H
|
||||
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QSharedPointer>
|
||||
|
||||
|
||||
struct VSearchConfig
|
||||
{
|
||||
enum Scope
|
||||
{
|
||||
NoneScope = 0,
|
||||
CurrentNote,
|
||||
OpenedNotes,
|
||||
CurrentFolder,
|
||||
CurrentNotebook,
|
||||
AllNotebooks
|
||||
};
|
||||
|
||||
enum Object
|
||||
{
|
||||
NoneObject = 0,
|
||||
Name = 0x1UL,
|
||||
Content = 0x2UL,
|
||||
Outline = 0x4UL,
|
||||
Tag = 0x8UL
|
||||
};
|
||||
|
||||
enum Target
|
||||
{
|
||||
NoneTarget = 0,
|
||||
Note = 0x1UL,
|
||||
Folder = 0x2UL,
|
||||
Notebook = 0x4UL
|
||||
};
|
||||
|
||||
enum Engine
|
||||
{
|
||||
Internal = 0
|
||||
};
|
||||
|
||||
enum Option
|
||||
{
|
||||
NoneOption = 0,
|
||||
CaseSensitive = 0x1UL,
|
||||
WholeWordOnly = 0x2UL,
|
||||
Fuzzy = 0x4UL,
|
||||
RegularExpression = 0x8UL
|
||||
};
|
||||
|
||||
VSearchConfig()
|
||||
: VSearchConfig(Scope::NoneScope,
|
||||
Object::NoneObject,
|
||||
Target::NoneTarget,
|
||||
Engine::Internal,
|
||||
Option::NoneOption,
|
||||
"",
|
||||
"")
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
VSearchConfig(int p_scope,
|
||||
int p_object,
|
||||
int p_target,
|
||||
int p_engine,
|
||||
int p_option,
|
||||
const QString &p_keyword,
|
||||
const QString &p_pattern)
|
||||
: m_scope(p_scope),
|
||||
m_object(p_object),
|
||||
m_target(p_target),
|
||||
m_engine(p_engine),
|
||||
m_option(p_option),
|
||||
m_keyword(p_keyword),
|
||||
m_pattern(p_pattern)
|
||||
{
|
||||
}
|
||||
|
||||
QStringList toConfig() const
|
||||
{
|
||||
QStringList str;
|
||||
str << QString::number(m_scope);
|
||||
str << QString::number(m_object);
|
||||
str << QString::number(m_target);
|
||||
str << QString::number(m_engine);
|
||||
str << QString::number(m_option);
|
||||
str << m_pattern;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static VSearchConfig fromConfig(const QStringList &p_str)
|
||||
{
|
||||
VSearchConfig config;
|
||||
if (p_str.size() != 6) {
|
||||
return config;
|
||||
}
|
||||
|
||||
config.m_scope = p_str[0].toInt();
|
||||
config.m_object = p_str[1].toInt();
|
||||
config.m_target = p_str[2].toInt();
|
||||
config.m_engine = p_str[3].toInt();
|
||||
config.m_option = p_str[4].toInt();
|
||||
config.m_pattern = p_str[5];
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
int m_scope;
|
||||
int m_object;
|
||||
int m_target;
|
||||
int m_engine;
|
||||
int m_option;
|
||||
|
||||
QString m_keyword;
|
||||
|
||||
// Wildcard pattern to filter file.
|
||||
QString m_pattern;
|
||||
};
|
||||
|
||||
|
||||
struct VSearchResultSubItem
|
||||
{
|
||||
VSearchResultSubItem()
|
||||
: m_lineNumber(-1)
|
||||
{
|
||||
}
|
||||
|
||||
VSearchResultSubItem(int p_lineNumber,
|
||||
const QString &p_text)
|
||||
: m_lineNumber(p_lineNumber),
|
||||
m_text(p_text)
|
||||
{
|
||||
}
|
||||
|
||||
int m_lineNumber;
|
||||
|
||||
QString m_text;
|
||||
};
|
||||
|
||||
|
||||
struct VSearchResultItem
|
||||
{
|
||||
enum ItemType
|
||||
{
|
||||
None = 0,
|
||||
Note,
|
||||
Folder,
|
||||
Notebook
|
||||
};
|
||||
|
||||
|
||||
enum MatchType
|
||||
{
|
||||
LineNumber = 0,
|
||||
OutlineIndex
|
||||
};
|
||||
|
||||
|
||||
VSearchResultItem()
|
||||
: m_type(ItemType::None),
|
||||
m_matchType(MatchType::LineNumber)
|
||||
{
|
||||
}
|
||||
|
||||
VSearchResultItem(VSearchResultItem::ItemType p_type,
|
||||
VSearchResultItem::MatchType p_matchType,
|
||||
const QString &p_text,
|
||||
const QString &p_path)
|
||||
: m_type(p_type),
|
||||
m_matchType(p_matchType),
|
||||
m_text(p_text),
|
||||
m_path(p_path)
|
||||
{
|
||||
}
|
||||
|
||||
bool isEmpty() const
|
||||
{
|
||||
return m_type == ItemType::None;
|
||||
}
|
||||
|
||||
QString toString() const
|
||||
{
|
||||
return QString("item text: [%1] path: [%2] subitems: %3")
|
||||
.arg(m_text)
|
||||
.arg(m_path)
|
||||
.arg(m_matches.size());
|
||||
}
|
||||
|
||||
|
||||
ItemType m_type;
|
||||
|
||||
MatchType m_matchType;
|
||||
|
||||
// Text to displayed. If empty, use @m_path instead.
|
||||
QString m_text;
|
||||
|
||||
// Path of the target.
|
||||
QString m_path;
|
||||
|
||||
// Matched places within this item.
|
||||
QList<VSearchResultSubItem> m_matches;
|
||||
};
|
||||
|
||||
|
||||
class VSearch;
|
||||
|
||||
|
||||
enum class VSearchState
|
||||
{
|
||||
Idle = 0,
|
||||
Busy,
|
||||
Success,
|
||||
Fail,
|
||||
Cancelled
|
||||
};
|
||||
|
||||
|
||||
struct VSearchResult
|
||||
{
|
||||
friend class VSearch;
|
||||
|
||||
VSearchResult(VSearch *p_search)
|
||||
: m_state(VSearchState::Idle),
|
||||
m_search(p_search)
|
||||
{
|
||||
}
|
||||
|
||||
bool hasError() const
|
||||
{
|
||||
return !m_errMsg.isEmpty();
|
||||
}
|
||||
|
||||
void logError(const QString &p_err)
|
||||
{
|
||||
if (m_errMsg.isEmpty()) {
|
||||
m_errMsg = p_err;
|
||||
} else {
|
||||
m_errMsg = "\n" + p_err;
|
||||
}
|
||||
}
|
||||
|
||||
void addSecondPhaseItem(const QString &p_item)
|
||||
{
|
||||
m_secondPhaseItems.append(p_item);
|
||||
}
|
||||
|
||||
QString toString() const
|
||||
{
|
||||
QString str = QString("search result: state %1 err %2")
|
||||
.arg((int)m_state)
|
||||
.arg(!m_errMsg.isEmpty());
|
||||
return str;
|
||||
}
|
||||
|
||||
bool hasSecondPhaseItems() const
|
||||
{
|
||||
return !m_secondPhaseItems.isEmpty();
|
||||
}
|
||||
|
||||
VSearchState m_state;
|
||||
|
||||
QString m_errMsg;
|
||||
|
||||
QStringList m_secondPhaseItems;
|
||||
|
||||
private:
|
||||
VSearch *m_search;
|
||||
};
|
||||
|
||||
#endif // VSEARCHCONFIG_H
|
255
src/vsearchengine.cpp
Normal file
255
src/vsearchengine.cpp
Normal file
@ -0,0 +1,255 @@
|
||||
#include "vsearchengine.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QMimeDatabase>
|
||||
|
||||
#include "utils/vutils.h"
|
||||
|
||||
VSearchEngineWorker::VSearchEngineWorker(QObject *p_parent)
|
||||
: QThread(p_parent),
|
||||
m_stop(0),
|
||||
m_state(VSearchState::Idle)
|
||||
{
|
||||
}
|
||||
|
||||
void VSearchEngineWorker::setData(const QStringList &p_files,
|
||||
const QRegExp &p_reg,
|
||||
const QString &p_keyword,
|
||||
Qt::CaseSensitivity p_cs)
|
||||
{
|
||||
m_files = p_files;
|
||||
m_reg = p_reg;
|
||||
m_keyword = p_keyword;
|
||||
m_caseSensitivity = p_cs;
|
||||
}
|
||||
|
||||
void VSearchEngineWorker::stop()
|
||||
{
|
||||
m_stop.store(1);
|
||||
}
|
||||
|
||||
void VSearchEngineWorker::run()
|
||||
{
|
||||
qDebug() << "worker" << QThread::currentThreadId() << m_files.size();
|
||||
|
||||
QMimeDatabase mimeDatabase;
|
||||
m_state = VSearchState::Busy;
|
||||
|
||||
for (auto const & fileName : m_files) {
|
||||
if (m_stop.load() == 1) {
|
||||
m_state = VSearchState::Cancelled;
|
||||
qDebug() << "worker" << QThread::currentThreadId() << "is asked to stop";
|
||||
break;
|
||||
}
|
||||
|
||||
const QMimeType mimeType = mimeDatabase.mimeTypeForFile(fileName);
|
||||
if (mimeType.isValid() && !mimeType.inherits(QStringLiteral("text/plain"))) {
|
||||
appendError(tr("Skip binary file %1.").arg(fileName));
|
||||
continue;
|
||||
}
|
||||
|
||||
VSearchResultItem *item = searchFile(fileName);
|
||||
if (item) {
|
||||
emit resultItemReady(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_state == VSearchState::Busy) {
|
||||
m_state = VSearchState::Success;
|
||||
}
|
||||
}
|
||||
|
||||
VSearchResultItem *VSearchEngineWorker::searchFile(const QString &p_fileName)
|
||||
{
|
||||
QFile file(p_fileName);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int lineNum = 1;
|
||||
VSearchResultItem *item = NULL;
|
||||
QString line;
|
||||
QTextStream in(&file);
|
||||
while (!in.atEnd()) {
|
||||
if (m_stop.load() == 1) {
|
||||
m_state = VSearchState::Cancelled;
|
||||
qDebug() << "worker" << QThread::currentThreadId() << "is asked to stop";
|
||||
break;
|
||||
}
|
||||
|
||||
bool matched = false;
|
||||
line = in.readLine();
|
||||
if (m_reg.isEmpty()) {
|
||||
if (line.contains(m_keyword, m_caseSensitivity)) {
|
||||
matched = true;
|
||||
}
|
||||
} else if (m_reg.indexIn(line) != -1) {
|
||||
matched = true;
|
||||
}
|
||||
|
||||
if (matched) {
|
||||
if (!item) {
|
||||
item = new VSearchResultItem(VSearchResultItem::Note,
|
||||
VSearchResultItem::LineNumber,
|
||||
VUtils::fileNameFromPath(p_fileName),
|
||||
p_fileName);
|
||||
}
|
||||
|
||||
VSearchResultSubItem sitem(lineNum, line);
|
||||
item->m_matches.append(sitem);
|
||||
}
|
||||
|
||||
++lineNum;
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
VSearchEngine::VSearchEngine(QObject *p_parent)
|
||||
: ISearchEngine(p_parent),
|
||||
m_finishedWorkers(0)
|
||||
{
|
||||
}
|
||||
|
||||
void VSearchEngine::search(const QSharedPointer<VSearchConfig> &p_config,
|
||||
const QSharedPointer<VSearchResult> &p_result)
|
||||
{
|
||||
int numThread = QThread::idealThreadCount();
|
||||
if (numThread < 1) {
|
||||
numThread = 1;
|
||||
}
|
||||
|
||||
const QStringList items = p_result->m_secondPhaseItems;
|
||||
Q_ASSERT(!items.isEmpty());
|
||||
if (items.size() < numThread) {
|
||||
numThread = items.size();
|
||||
}
|
||||
|
||||
m_result = p_result;
|
||||
|
||||
QRegExp reg = compileRegExpFromConfig(p_config);
|
||||
Qt::CaseSensitivity cs = (p_config->m_option & VSearchConfig::CaseSensitive)
|
||||
? Qt::CaseSensitive : Qt::CaseInsensitive;
|
||||
|
||||
clearAllWorkers();
|
||||
m_workers.reserve(numThread);
|
||||
m_finishedWorkers = 0;
|
||||
int totalSize = m_result->m_secondPhaseItems.size();
|
||||
int step = totalSize / numThread;
|
||||
int remain = totalSize % numThread;
|
||||
|
||||
for (int i = 0; i < numThread; ++i) {
|
||||
int start = i * step;
|
||||
if (start >= totalSize) {
|
||||
break;
|
||||
}
|
||||
|
||||
int len = step;
|
||||
if (remain) {
|
||||
++len;
|
||||
--remain;
|
||||
}
|
||||
|
||||
if (start + len > totalSize) {
|
||||
len = totalSize - start;
|
||||
}
|
||||
|
||||
VSearchEngineWorker *th = new VSearchEngineWorker(this);
|
||||
th->setData(m_result->m_secondPhaseItems.mid(start, len),
|
||||
reg,
|
||||
p_config->m_keyword,
|
||||
cs);
|
||||
connect(th, &VSearchEngineWorker::finished,
|
||||
this, &VSearchEngine::handleWorkerFinished);
|
||||
connect(th, &VSearchEngineWorker::resultItemReady,
|
||||
this, [this](VSearchResultItem *p_item) {
|
||||
emit resultItemAdded(QSharedPointer<VSearchResultItem>(p_item));
|
||||
});
|
||||
|
||||
m_workers.append(th);
|
||||
th->start();
|
||||
}
|
||||
|
||||
qDebug() << "schedule tasks to threads" << m_workers.size() << totalSize << step;
|
||||
}
|
||||
|
||||
QRegExp VSearchEngine::compileRegExpFromConfig(const QSharedPointer<VSearchConfig> &p_config) const
|
||||
{
|
||||
const QString &keyword = p_config->m_keyword;
|
||||
Qt::CaseSensitivity cs = (p_config->m_option & VSearchConfig::CaseSensitive)
|
||||
? Qt::CaseSensitive : Qt::CaseInsensitive;
|
||||
if (p_config->m_option & VSearchConfig::RegularExpression) {
|
||||
return QRegExp(keyword, cs);
|
||||
} else if (p_config->m_option & VSearchConfig::WholeWordOnly) {
|
||||
QString pattern = QRegExp::escape(keyword);
|
||||
pattern = "\\b" + pattern + "\\b";
|
||||
return QRegExp(pattern, cs);
|
||||
} else {
|
||||
return QRegExp();
|
||||
}
|
||||
}
|
||||
|
||||
void VSearchEngine::stop()
|
||||
{
|
||||
qDebug() << "VSearchEngine asked to stop";
|
||||
for (auto const & th : m_workers) {
|
||||
th->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void VSearchEngine::handleWorkerFinished()
|
||||
{
|
||||
++m_finishedWorkers;
|
||||
|
||||
qDebug() << m_finishedWorkers << "workers finished";
|
||||
if (m_finishedWorkers == m_workers.size()) {
|
||||
VSearchState state = VSearchState::Success;
|
||||
|
||||
for (auto const & th : m_workers) {
|
||||
if (th->m_state == VSearchState::Fail) {
|
||||
if (state != VSearchState::Cancelled) {
|
||||
state = VSearchState::Fail;
|
||||
}
|
||||
} else if (th->m_state == VSearchState::Cancelled) {
|
||||
state = VSearchState::Cancelled;
|
||||
}
|
||||
|
||||
if (!th->m_error.isEmpty()) {
|
||||
m_result->logError(th->m_error);
|
||||
}
|
||||
|
||||
Q_ASSERT(th->isFinished());
|
||||
th->deleteLater();
|
||||
}
|
||||
|
||||
m_workers.clear();
|
||||
m_finishedWorkers = 0;
|
||||
|
||||
m_result->m_state = state;
|
||||
qDebug() << "SearchEngine finished" << (int)state;
|
||||
emit finished(m_result);
|
||||
}
|
||||
}
|
||||
|
||||
void VSearchEngine::clear()
|
||||
{
|
||||
clearAllWorkers();
|
||||
|
||||
m_finishedWorkers = 0;
|
||||
|
||||
m_result.clear();
|
||||
}
|
||||
|
||||
void VSearchEngine::clearAllWorkers()
|
||||
{
|
||||
for (auto const & th : m_workers) {
|
||||
th->quit();
|
||||
th->wait();
|
||||
|
||||
delete th;
|
||||
}
|
||||
|
||||
m_workers.clear();
|
||||
}
|
89
src/vsearchengine.h
Normal file
89
src/vsearchengine.h
Normal file
@ -0,0 +1,89 @@
|
||||
#ifndef VSEARCHENGINE_H
|
||||
#define VSEARCHENGINE_H
|
||||
|
||||
#include <QThread>
|
||||
#include <QRegExp>
|
||||
#include <QAtomicInt>
|
||||
#include "isearchengine.h"
|
||||
|
||||
class VSearchEngineWorker : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend class VSearchEngine;
|
||||
|
||||
public:
|
||||
explicit VSearchEngineWorker(QObject *p_parent = nullptr);
|
||||
|
||||
void setData(const QStringList &p_files,
|
||||
const QRegExp &p_reg,
|
||||
const QString &p_keyword,
|
||||
Qt::CaseSensitivity p_cs);
|
||||
|
||||
public slots:
|
||||
void stop();
|
||||
|
||||
signals:
|
||||
void resultItemReady(VSearchResultItem *p_item);
|
||||
|
||||
protected:
|
||||
void run() Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
void appendError(const QString &p_err);
|
||||
|
||||
VSearchResultItem *searchFile(const QString &p_fileName);
|
||||
|
||||
QAtomicInt m_stop;
|
||||
|
||||
QStringList m_files;
|
||||
|
||||
QRegExp m_reg;
|
||||
|
||||
QString m_keyword;
|
||||
|
||||
Qt::CaseSensitivity m_caseSensitivity;
|
||||
|
||||
VSearchState m_state;
|
||||
|
||||
QString m_error;
|
||||
};
|
||||
|
||||
inline void VSearchEngineWorker::appendError(const QString &p_err)
|
||||
{
|
||||
if (m_error.isEmpty()) {
|
||||
m_error = p_err;
|
||||
} else {
|
||||
m_error = "\n" + p_err;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class VSearchEngine : public ISearchEngine
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VSearchEngine(QObject *p_parent = nullptr);
|
||||
|
||||
void search(const QSharedPointer<VSearchConfig> &p_config,
|
||||
const QSharedPointer<VSearchResult> &p_result) Q_DECL_OVERRIDE;
|
||||
|
||||
void stop() Q_DECL_OVERRIDE;
|
||||
|
||||
void clear() Q_DECL_OVERRIDE;
|
||||
|
||||
private slots:
|
||||
void handleWorkerFinished();
|
||||
|
||||
private:
|
||||
// Returns an empty object if raw string is preferred.
|
||||
QRegExp compileRegExpFromConfig(const QSharedPointer<VSearchConfig> &p_config) const;
|
||||
|
||||
void clearAllWorkers();
|
||||
|
||||
int m_finishedWorkers;
|
||||
|
||||
QVector<VSearchEngineWorker *> m_workers;
|
||||
};
|
||||
|
||||
#endif // VSEARCHENGINE_H
|
548
src/vsearcher.cpp
Normal file
548
src/vsearcher.cpp
Normal file
@ -0,0 +1,548 @@
|
||||
#include "vsearcher.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include "vlineedit.h"
|
||||
#include "utils/vutils.h"
|
||||
#include "utils/viconutils.h"
|
||||
#include "vsearch.h"
|
||||
#include "vsearchresulttree.h"
|
||||
#include "vmainwindow.h"
|
||||
#include "veditarea.h"
|
||||
#include "vdirectorytree.h"
|
||||
#include "vdirectory.h"
|
||||
#include "vnotebookselector.h"
|
||||
#include "vnotebook.h"
|
||||
#include "vnote.h"
|
||||
#include "vconfigmanager.h"
|
||||
|
||||
extern VMainWindow *g_mainWin;
|
||||
|
||||
extern VNote *g_vnote;
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
VSearcher::VSearcher(QWidget *p_parent)
|
||||
: QWidget(p_parent),
|
||||
m_inSearch(false),
|
||||
m_askedToStop(false),
|
||||
m_search(this)
|
||||
{
|
||||
setupUI();
|
||||
|
||||
initUIFields();
|
||||
|
||||
handleInputChanged();
|
||||
|
||||
connect(&m_search, &VSearch::resultItemAdded,
|
||||
m_results, &VSearchResultTree::addResultItem);
|
||||
connect(&m_search, &VSearch::finished,
|
||||
this, &VSearcher::handleSearchFinished);
|
||||
}
|
||||
|
||||
void VSearcher::setupUI()
|
||||
{
|
||||
// Search button.
|
||||
m_searchBtn = new QPushButton(VIconUtils::buttonIcon(":/resources/icons/search.svg"), "", this);
|
||||
m_searchBtn->setToolTip(tr("Search"));
|
||||
m_searchBtn->setProperty("FlatBtn", true);
|
||||
connect(m_searchBtn, &QPushButton::clicked,
|
||||
this, &VSearcher::startSearch);
|
||||
|
||||
// Clear button.
|
||||
m_clearBtn = new QPushButton(VIconUtils::buttonDangerIcon(":/resources/icons/clear_search.svg"), "", this);
|
||||
m_clearBtn->setToolTip(tr("Clear Results"));
|
||||
m_clearBtn->setProperty("FlatBtn", true);
|
||||
connect(m_clearBtn, &QPushButton::clicked,
|
||||
this, [this]() {
|
||||
m_results->clearResults();
|
||||
});
|
||||
|
||||
// Advanced button.
|
||||
m_advBtn = new QPushButton(VIconUtils::buttonIcon(":/resources/icons/search_advanced.svg"),
|
||||
"",
|
||||
this);
|
||||
m_advBtn->setToolTip(tr("Advanced Settings"));
|
||||
m_advBtn->setProperty("FlatBtn", true);
|
||||
m_advBtn->setCheckable(true);
|
||||
connect(m_advBtn, &QPushButton::toggled,
|
||||
this, [this](bool p_checked) {
|
||||
m_advWidget->setVisible(p_checked);
|
||||
});
|
||||
|
||||
// Console button.
|
||||
m_consoleBtn = new QPushButton(VIconUtils::buttonIcon(":/resources/icons/search_console.svg"),
|
||||
"",
|
||||
this);
|
||||
m_consoleBtn->setToolTip(tr("Console"));
|
||||
m_consoleBtn->setProperty("FlatBtn", true);
|
||||
m_consoleBtn->setCheckable(true);
|
||||
connect(m_consoleBtn, &QPushButton::toggled,
|
||||
this, [this](bool p_checked) {
|
||||
m_consoleEdit->setVisible(p_checked);
|
||||
});
|
||||
|
||||
m_numLabel = new QLabel(this);
|
||||
|
||||
QHBoxLayout *btnLayout = new QHBoxLayout();
|
||||
btnLayout->addWidget(m_searchBtn);
|
||||
btnLayout->addWidget(m_clearBtn);
|
||||
btnLayout->addWidget(m_advBtn);
|
||||
btnLayout->addWidget(m_consoleBtn);
|
||||
btnLayout->addStretch();
|
||||
btnLayout->addWidget(m_numLabel);
|
||||
btnLayout->setContentsMargins(0, 0, 3, 0);
|
||||
|
||||
// Keyword.
|
||||
m_keywordCB = VUtils::getComboBox(this);
|
||||
m_keywordCB->setEditable(true);
|
||||
m_keywordCB->setLineEdit(new VLineEdit(this));
|
||||
m_keywordCB->setToolTip(tr("Keywords to search for"));
|
||||
connect(m_keywordCB, &QComboBox::currentTextChanged,
|
||||
this, &VSearcher::handleInputChanged);
|
||||
connect(m_keywordCB->lineEdit(), &QLineEdit::returnPressed,
|
||||
this, &VSearcher::animateSearchClick);
|
||||
m_keywordCB->completer()->setCaseSensitivity(Qt::CaseSensitive);
|
||||
|
||||
// Scope.
|
||||
m_searchScopeCB = VUtils::getComboBox(this);
|
||||
m_searchScopeCB->setToolTip(tr("Scope to search"));
|
||||
connect(m_searchScopeCB, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, &VSearcher::handleInputChanged);
|
||||
|
||||
// Object.
|
||||
m_searchObjectCB = VUtils::getComboBox(this);
|
||||
m_searchObjectCB->setToolTip(tr("Object to search"));
|
||||
connect(m_searchObjectCB, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, &VSearcher::handleInputChanged);
|
||||
|
||||
// Target.
|
||||
m_searchTargetCB = VUtils::getComboBox(this);
|
||||
m_searchTargetCB->setToolTip(tr("Target to search"));
|
||||
connect(m_searchTargetCB, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, &VSearcher::handleInputChanged);
|
||||
|
||||
// Pattern.
|
||||
m_filePatternCB = VUtils::getComboBox(this);
|
||||
m_filePatternCB->setEditable(true);
|
||||
m_filePatternCB->setLineEdit(new VLineEdit(this));
|
||||
m_filePatternCB->setToolTip(tr("Wildcard pattern to filter the files to be searched"));
|
||||
m_filePatternCB->completer()->setCaseSensitivity(Qt::CaseSensitive);
|
||||
|
||||
// Engine.
|
||||
m_searchEngineCB = VUtils::getComboBox(this);
|
||||
m_searchEngineCB->setToolTip(tr("Engine to execute the search"));
|
||||
|
||||
// Case sensitive.
|
||||
m_caseSensitiveCB = new QCheckBox(tr("&Case sensitive"), this);
|
||||
|
||||
// Whole word only.
|
||||
m_wholeWordOnlyCB = new QCheckBox(tr("&Whole word only"), this);
|
||||
|
||||
// Fuzzy search.
|
||||
m_fuzzyCB = new QCheckBox(tr("&Fuzzy search"), this);
|
||||
m_fuzzyCB->setToolTip(tr("Not available for content search"));
|
||||
connect(m_fuzzyCB, &QCheckBox::stateChanged,
|
||||
this, [this](int p_state) {
|
||||
bool checked = p_state == Qt::Checked;
|
||||
m_wholeWordOnlyCB->setEnabled(!checked);
|
||||
});
|
||||
|
||||
// Regular expression.
|
||||
m_regularExpressionCB = new QCheckBox(tr("Re&gular expression"), this);
|
||||
connect(m_regularExpressionCB, &QCheckBox::stateChanged,
|
||||
this, [this](int p_state) {
|
||||
bool checked = p_state == Qt::Checked;
|
||||
m_wholeWordOnlyCB->setEnabled(!checked);
|
||||
m_fuzzyCB->setEnabled(!checked);
|
||||
});
|
||||
|
||||
QFormLayout *advLayout = new QFormLayout();
|
||||
advLayout->addRow(tr("File pattern:"), m_filePatternCB);
|
||||
advLayout->addRow(tr("Engine:"), m_searchEngineCB);
|
||||
advLayout->addRow(m_caseSensitiveCB);
|
||||
advLayout->addRow(m_wholeWordOnlyCB);
|
||||
advLayout->addRow(m_fuzzyCB);
|
||||
advLayout->addRow(m_regularExpressionCB);
|
||||
advLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
m_advWidget = new QWidget(this);
|
||||
m_advWidget->setLayout(advLayout);
|
||||
m_advWidget->hide();
|
||||
|
||||
// Progress bar.
|
||||
m_proBar = new QProgressBar(this);
|
||||
m_proBar->setRange(0, 0);
|
||||
|
||||
// Cancel button.
|
||||
m_cancelBtn = new QPushButton(VIconUtils::buttonIcon(":/resources/icons/close.svg"),
|
||||
"",
|
||||
this);
|
||||
m_cancelBtn->setToolTip(tr("Cancel"));
|
||||
m_cancelBtn->setProperty("FlatBtn", true);
|
||||
connect(m_cancelBtn, &QPushButton::clicked,
|
||||
this, [this]() {
|
||||
if (m_inSearch) {
|
||||
appendLogLine(tr("Cancelling the export..."));
|
||||
m_askedToStop = true;
|
||||
m_search.stop();
|
||||
}
|
||||
});
|
||||
|
||||
QHBoxLayout *proLayout = new QHBoxLayout();
|
||||
proLayout->addWidget(m_proBar);
|
||||
proLayout->addWidget(m_cancelBtn);
|
||||
proLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
// Console.
|
||||
m_consoleEdit = new QPlainTextEdit(this);
|
||||
m_consoleEdit->setReadOnly(true);
|
||||
m_consoleEdit->setLineWrapMode(QPlainTextEdit::WidgetWidth);
|
||||
m_consoleEdit->setProperty("LineEdit", true);
|
||||
m_consoleEdit->setPlaceholderText(tr("Output logs will be shown here"));
|
||||
m_consoleEdit->setMaximumHeight(m_searchScopeCB->height() * 2);
|
||||
m_consoleEdit->hide();
|
||||
|
||||
// List.
|
||||
m_results = new VSearchResultTree(this);
|
||||
m_results->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
|
||||
connect(m_results, &VSearchResultTree::countChanged,
|
||||
this, [this](int p_count) {
|
||||
m_clearBtn->setEnabled(p_count > 0);
|
||||
updateNumLabel(p_count);
|
||||
});
|
||||
|
||||
QFormLayout *formLayout = new QFormLayout();
|
||||
formLayout->addRow(tr("Keywords:"), m_keywordCB);
|
||||
formLayout->addRow(tr("Scope:"), m_searchScopeCB);
|
||||
formLayout->addRow(tr("Object:"), m_searchObjectCB);
|
||||
formLayout->addRow(tr("Target:"), m_searchTargetCB);
|
||||
formLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
mainLayout->addLayout(btnLayout);
|
||||
mainLayout->addLayout(formLayout);
|
||||
mainLayout->addWidget(m_advWidget);
|
||||
mainLayout->addLayout(proLayout);
|
||||
mainLayout->addWidget(m_consoleEdit);
|
||||
mainLayout->addWidget(m_results);
|
||||
|
||||
setLayout(mainLayout);
|
||||
}
|
||||
|
||||
void VSearcher::initUIFields()
|
||||
{
|
||||
VSearchConfig config = VSearchConfig::fromConfig(g_config->getSearchOptions());
|
||||
|
||||
// Scope.
|
||||
m_searchScopeCB->addItem(tr("Current Note"), VSearchConfig::CurrentNote);
|
||||
m_searchScopeCB->addItem(tr("Opened Notes"), VSearchConfig::OpenedNotes);
|
||||
m_searchScopeCB->addItem(tr("Current Folder"), VSearchConfig::CurrentFolder);
|
||||
m_searchScopeCB->addItem(tr("Current Notebook"), VSearchConfig::CurrentNotebook);
|
||||
m_searchScopeCB->addItem(tr("All Notebooks"), VSearchConfig::AllNotebooks);
|
||||
m_searchScopeCB->setCurrentIndex(m_searchScopeCB->findData(config.m_scope));
|
||||
|
||||
// Object.
|
||||
m_searchObjectCB->addItem(tr("Name"), VSearchConfig::Name);
|
||||
m_searchObjectCB->addItem(tr("Content"), VSearchConfig::Content);
|
||||
m_searchObjectCB->addItem(tr("Outline"), VSearchConfig::Outline);
|
||||
m_searchObjectCB->setCurrentIndex(m_searchObjectCB->findData(config.m_object));
|
||||
|
||||
// Target.
|
||||
m_searchTargetCB->addItem(tr("Note"), VSearchConfig::Note);
|
||||
m_searchTargetCB->addItem(tr("Folder"), VSearchConfig::Folder);
|
||||
m_searchTargetCB->addItem(tr("Notebook"), VSearchConfig::Notebook);
|
||||
m_searchTargetCB->addItem(tr("All"),
|
||||
VSearchConfig::Note
|
||||
| VSearchConfig:: Folder
|
||||
| VSearchConfig::Notebook);
|
||||
m_searchTargetCB->setCurrentIndex(m_searchTargetCB->findData(config.m_target));
|
||||
|
||||
// Engine.
|
||||
m_searchEngineCB->addItem(tr("Internal"), VSearchConfig::Internal);
|
||||
m_searchEngineCB->setCurrentIndex(m_searchEngineCB->findData(config.m_engine));
|
||||
|
||||
// Pattern.
|
||||
m_filePatternCB->setCurrentText(config.m_pattern);
|
||||
|
||||
m_caseSensitiveCB->setChecked(config.m_option & VSearchConfig::CaseSensitive);
|
||||
m_wholeWordOnlyCB->setChecked(config.m_option & VSearchConfig::WholeWordOnly);
|
||||
m_fuzzyCB->setChecked(config.m_option & VSearchConfig::Fuzzy);
|
||||
m_regularExpressionCB->setChecked(config.m_option & VSearchConfig::RegularExpression);
|
||||
|
||||
setProgressVisible(false);
|
||||
|
||||
m_clearBtn->setEnabled(false);
|
||||
}
|
||||
|
||||
void VSearcher::updateItemToComboBox(QComboBox *p_comboBox)
|
||||
{
|
||||
QString text = p_comboBox->currentText();
|
||||
if (!text.isEmpty() && p_comboBox->findText(text) == -1) {
|
||||
p_comboBox->addItem(text);
|
||||
}
|
||||
}
|
||||
|
||||
void VSearcher::setProgressVisible(bool p_visible)
|
||||
{
|
||||
m_proBar->setVisible(p_visible);
|
||||
m_cancelBtn->setVisible(p_visible);
|
||||
}
|
||||
|
||||
void VSearcher::appendLogLine(const QString &p_text)
|
||||
{
|
||||
m_consoleEdit->appendPlainText(">>> " + p_text);
|
||||
m_consoleEdit->ensureCursorVisible();
|
||||
QCoreApplication::sendPostedEvents();
|
||||
}
|
||||
|
||||
void VSearcher::showMessage(const QString &p_text) const
|
||||
{
|
||||
g_mainWin->showStatusMessage(p_text);
|
||||
QCoreApplication::sendPostedEvents();
|
||||
}
|
||||
|
||||
void VSearcher::handleInputChanged()
|
||||
{
|
||||
bool readyToSearch = true;
|
||||
|
||||
// Keyword.
|
||||
QString keyword = m_keywordCB->currentText();
|
||||
readyToSearch = !keyword.isEmpty();
|
||||
|
||||
if (readyToSearch) {
|
||||
// Other targets are only available for Name.
|
||||
int obj = m_searchObjectCB->currentData().toInt();
|
||||
if (obj != VSearchConfig::Name) {
|
||||
int target = m_searchTargetCB->currentData().toInt();
|
||||
if (!(target & VSearchConfig::Note)) {
|
||||
readyToSearch = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (readyToSearch && obj == VSearchConfig::Outline) {
|
||||
// Outline is available only for CurrentNote and OpenedNotes.
|
||||
int scope = m_searchScopeCB->currentData().toInt();
|
||||
if (scope != VSearchConfig::CurrentNote
|
||||
&& scope != VSearchConfig::OpenedNotes) {
|
||||
readyToSearch = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_searchBtn->setEnabled(readyToSearch);
|
||||
}
|
||||
|
||||
void VSearcher::startSearch()
|
||||
{
|
||||
if (m_inSearch) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_searchBtn->setEnabled(false);
|
||||
setProgressVisible(true);
|
||||
m_results->clearResults();
|
||||
m_askedToStop = false;
|
||||
m_inSearch = true;
|
||||
m_consoleEdit->clear();
|
||||
appendLogLine(tr("Search started."));
|
||||
|
||||
updateItemToComboBox(m_keywordCB);
|
||||
updateItemToComboBox(m_filePatternCB);
|
||||
|
||||
QSharedPointer<VSearchConfig> config(new VSearchConfig(m_searchScopeCB->currentData().toInt(),
|
||||
m_searchObjectCB->currentData().toInt(),
|
||||
m_searchTargetCB->currentData().toInt(),
|
||||
m_searchEngineCB->currentData().toInt(),
|
||||
getSearchOption(),
|
||||
m_keywordCB->currentText(),
|
||||
m_filePatternCB->currentText()));
|
||||
m_search.setConfig(config);
|
||||
|
||||
g_config->setSearchOptions(config->toConfig());
|
||||
|
||||
QSharedPointer<VSearchResult> result;
|
||||
switch (config->m_scope) {
|
||||
case VSearchConfig::CurrentNote:
|
||||
{
|
||||
QVector<VFile *> files;
|
||||
files.append(g_mainWin->getCurrentFile());
|
||||
if (files[0]) {
|
||||
QString msg(tr("Search current note %1.").arg(files[0]->getName()));
|
||||
appendLogLine(msg);
|
||||
showMessage(msg);
|
||||
}
|
||||
|
||||
result = m_search.search(files);
|
||||
break;
|
||||
}
|
||||
|
||||
case VSearchConfig::OpenedNotes:
|
||||
{
|
||||
QVector<VEditTabInfo> tabs = g_mainWin->getEditArea()->getAllTabsInfo();
|
||||
QVector<VFile *> files;
|
||||
files.reserve(tabs.size());
|
||||
for (auto const & ta : tabs) {
|
||||
files.append(ta.m_editTab->getFile());
|
||||
}
|
||||
|
||||
result = m_search.search(files);
|
||||
break;
|
||||
}
|
||||
|
||||
case VSearchConfig::CurrentFolder:
|
||||
{
|
||||
VDirectory *dir = g_mainWin->getDirectoryTree()->currentDirectory();
|
||||
if (dir) {
|
||||
QString msg(tr("Search current folder %1.").arg(dir->getName()));
|
||||
appendLogLine(msg);
|
||||
showMessage(msg);
|
||||
}
|
||||
|
||||
result = m_search.search(dir);
|
||||
break;
|
||||
}
|
||||
|
||||
case VSearchConfig::CurrentNotebook:
|
||||
{
|
||||
QVector<VNotebook *> notebooks;
|
||||
notebooks.append(g_mainWin->getNotebookSelector()->currentNotebook());
|
||||
if (notebooks[0]) {
|
||||
QString msg(tr("Search current notebook %1.").arg(notebooks[0]->getName()));
|
||||
appendLogLine(msg);
|
||||
showMessage(msg);
|
||||
}
|
||||
|
||||
result = m_search.search(notebooks);
|
||||
break;
|
||||
}
|
||||
|
||||
case VSearchConfig::AllNotebooks:
|
||||
{
|
||||
const QVector<VNotebook *> ¬ebooks = g_vnote->getNotebooks();
|
||||
result = m_search.search(notebooks);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
handleSearchFinished(result);
|
||||
}
|
||||
|
||||
void VSearcher::handleSearchFinished(const QSharedPointer<VSearchResult> &p_result)
|
||||
{
|
||||
Q_ASSERT(m_inSearch);
|
||||
Q_ASSERT(p_result->m_state != VSearchState::Idle);
|
||||
|
||||
qDebug() << "handleSearchFinished" << (int)p_result->m_state;
|
||||
|
||||
QString msg;
|
||||
switch (p_result->m_state) {
|
||||
case VSearchState::Busy:
|
||||
msg = tr("Search is on going.");
|
||||
appendLogLine(msg);
|
||||
return;
|
||||
|
||||
case VSearchState::Success:
|
||||
msg = tr("Search succeeded.");
|
||||
appendLogLine(msg);
|
||||
showMessage(msg);
|
||||
break;
|
||||
|
||||
case VSearchState::Fail:
|
||||
msg = tr("Search failed.");
|
||||
appendLogLine(msg);
|
||||
showMessage(msg);
|
||||
VUtils::showMessage(QMessageBox::Warning,
|
||||
tr("Warning"),
|
||||
tr("Search failed."),
|
||||
p_result->m_errMsg,
|
||||
QMessageBox::Ok,
|
||||
QMessageBox::Ok,
|
||||
this);
|
||||
break;
|
||||
|
||||
case VSearchState::Cancelled:
|
||||
Q_ASSERT(m_askedToStop);
|
||||
msg = tr("User cancelled the search. Aborted!");
|
||||
appendLogLine(msg);
|
||||
showMessage(msg);
|
||||
m_askedToStop = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (p_result->m_state != VSearchState::Fail
|
||||
&& p_result->hasError()) {
|
||||
VUtils::showMessage(QMessageBox::Warning,
|
||||
tr("Warning"),
|
||||
tr("Errors found during search."),
|
||||
p_result->m_errMsg,
|
||||
QMessageBox::Ok,
|
||||
QMessageBox::Ok,
|
||||
this);
|
||||
}
|
||||
|
||||
m_search.clear();
|
||||
|
||||
m_inSearch = false;
|
||||
m_searchBtn->setEnabled(true);
|
||||
setProgressVisible(false);
|
||||
}
|
||||
|
||||
void VSearcher::animateSearchClick()
|
||||
{
|
||||
m_searchBtn->animateClick();
|
||||
}
|
||||
|
||||
int VSearcher::getSearchOption() const
|
||||
{
|
||||
int ret = VSearchConfig::NoneOption;
|
||||
|
||||
if (m_caseSensitiveCB->isChecked()) {
|
||||
ret |= VSearchConfig::CaseSensitive;
|
||||
}
|
||||
|
||||
if (m_wholeWordOnlyCB->isChecked()) {
|
||||
ret |= VSearchConfig::WholeWordOnly;
|
||||
}
|
||||
|
||||
if (m_fuzzyCB->isChecked()) {
|
||||
ret |= VSearchConfig::Fuzzy;
|
||||
}
|
||||
|
||||
if (m_regularExpressionCB->isChecked()) {
|
||||
ret |= VSearchConfig::RegularExpression;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void VSearcher::updateNumLabel(int p_count)
|
||||
{
|
||||
m_numLabel->setText(tr("%1 Items").arg(p_count));
|
||||
}
|
||||
|
||||
void VSearcher::focusToSearch()
|
||||
{
|
||||
m_keywordCB->setFocus(Qt::OtherFocusReason);
|
||||
}
|
||||
|
||||
void VSearcher::showNavigation()
|
||||
{
|
||||
VNavigationMode::showNavigation(m_results);
|
||||
}
|
||||
|
||||
bool VSearcher::handleKeyNavigation(int p_key, bool &p_succeed)
|
||||
{
|
||||
static bool secondKey = false;
|
||||
return VNavigationMode::handleKeyNavigation(m_results,
|
||||
secondKey,
|
||||
p_key,
|
||||
p_succeed);
|
||||
}
|
108
src/vsearcher.h
Normal file
108
src/vsearcher.h
Normal file
@ -0,0 +1,108 @@
|
||||
#ifndef VSEARCHER_H
|
||||
#define VSEARCHER_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "vsearch.h"
|
||||
|
||||
#include "vnavigationmode.h"
|
||||
|
||||
class QComboBox;
|
||||
class QCheckBox;
|
||||
class QPushButton;
|
||||
class QLabel;
|
||||
class VSearchResultTree;
|
||||
class QProgressBar;
|
||||
class QPlainTextEdit;
|
||||
|
||||
class VSearcher : public QWidget, public VNavigationMode
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VSearcher(QWidget *p_parent = nullptr);
|
||||
|
||||
void focusToSearch();
|
||||
|
||||
// Implementations for VNavigationMode.
|
||||
void showNavigation() Q_DECL_OVERRIDE;
|
||||
bool handleKeyNavigation(int p_key, bool &p_succeed) Q_DECL_OVERRIDE;
|
||||
|
||||
private slots:
|
||||
void handleSearchFinished(const QSharedPointer<VSearchResult> &p_result);
|
||||
|
||||
private:
|
||||
void startSearch();
|
||||
|
||||
void setupUI();
|
||||
|
||||
void initUIFields();
|
||||
|
||||
void setProgressVisible(bool p_visible);
|
||||
|
||||
void appendLogLine(const QString &p_text);
|
||||
|
||||
void handleInputChanged();
|
||||
|
||||
void animateSearchClick();
|
||||
|
||||
void updateItemToComboBox(QComboBox *p_comboBox);
|
||||
|
||||
// Get the OR of the search options.
|
||||
int getSearchOption() const;
|
||||
|
||||
void updateNumLabel(int p_count);
|
||||
|
||||
void showMessage(const QString &p_text) const;
|
||||
|
||||
QComboBox *m_keywordCB;
|
||||
|
||||
// All notebooks, current notebook, and so on.
|
||||
QComboBox *m_searchScopeCB;
|
||||
|
||||
// Name, content, tag.
|
||||
QComboBox *m_searchObjectCB;
|
||||
|
||||
// Notebook, folder, note.
|
||||
QComboBox *m_searchTargetCB;
|
||||
|
||||
QComboBox *m_filePatternCB;
|
||||
|
||||
QComboBox *m_searchEngineCB;
|
||||
|
||||
QCheckBox *m_caseSensitiveCB;
|
||||
|
||||
QCheckBox *m_wholeWordOnlyCB;
|
||||
|
||||
QCheckBox *m_fuzzyCB;
|
||||
|
||||
QCheckBox *m_regularExpressionCB;
|
||||
|
||||
QPushButton *m_searchBtn;
|
||||
|
||||
QPushButton *m_clearBtn;
|
||||
|
||||
QPushButton *m_advBtn;
|
||||
|
||||
QPushButton *m_consoleBtn;
|
||||
|
||||
QLabel *m_numLabel;
|
||||
|
||||
QWidget *m_advWidget;
|
||||
|
||||
VSearchResultTree *m_results;
|
||||
|
||||
QProgressBar *m_proBar;
|
||||
|
||||
QPushButton *m_cancelBtn;
|
||||
|
||||
QPlainTextEdit *m_consoleEdit;
|
||||
|
||||
bool m_inSearch;
|
||||
|
||||
bool m_askedToStop;
|
||||
|
||||
VSearch m_search;
|
||||
};
|
||||
|
||||
#endif // VSEARCHER_H
|
143
src/vsearchresulttree.cpp
Normal file
143
src/vsearchresulttree.cpp
Normal file
@ -0,0 +1,143 @@
|
||||
#include "vsearchresulttree.h"
|
||||
|
||||
#include "utils/viconutils.h"
|
||||
#include "vnote.h"
|
||||
#include "vmainwindow.h"
|
||||
#include "vnotebookselector.h"
|
||||
|
||||
extern VNote *g_vnote;
|
||||
|
||||
extern VMainWindow *g_mainWin;
|
||||
|
||||
VSearchResultTree::VSearchResultTree(QWidget *p_parent)
|
||||
: VTreeWidget(p_parent)
|
||||
{
|
||||
setColumnCount(1);
|
||||
setHeaderHidden(true);
|
||||
setExpandsOnDoubleClick(false);
|
||||
|
||||
setSimpleSearchMatchFlags(getSimpleSearchMatchFlags() & ~Qt::MatchRecursive);
|
||||
|
||||
m_noteIcon = VIconUtils::treeViewIcon(":/resources/icons/note_item.svg");
|
||||
m_folderIcon = VIconUtils::treeViewIcon(":/resources/icons/dir_item.svg");
|
||||
m_notebookIcon = VIconUtils::treeViewIcon(":/resources/icons/notebook_item.svg");
|
||||
|
||||
connect(this, &VTreeWidget::itemActivated,
|
||||
this, &VSearchResultTree::handleItemActivated);
|
||||
}
|
||||
|
||||
void VSearchResultTree::updateResults(const QList<QSharedPointer<VSearchResultItem> > &p_items)
|
||||
{
|
||||
clearResults();
|
||||
|
||||
for (auto const & it : p_items) {
|
||||
appendItem(it);
|
||||
}
|
||||
|
||||
emit countChanged(topLevelItemCount());
|
||||
}
|
||||
|
||||
void VSearchResultTree::addResultItem(const QSharedPointer<VSearchResultItem> &p_item)
|
||||
{
|
||||
appendItem(p_item);
|
||||
|
||||
emit countChanged(topLevelItemCount());
|
||||
}
|
||||
|
||||
void VSearchResultTree::clearResults()
|
||||
{
|
||||
clearAll();
|
||||
|
||||
m_data.clear();
|
||||
|
||||
emit countChanged(topLevelItemCount());
|
||||
}
|
||||
|
||||
void VSearchResultTree::appendItem(const QSharedPointer<VSearchResultItem> &p_item)
|
||||
{
|
||||
m_data.append(p_item);
|
||||
|
||||
QTreeWidgetItem *item = new QTreeWidgetItem(this);
|
||||
item->setData(0, Qt::UserRole, m_data.size() - 1);
|
||||
item->setText(0, p_item->m_text.isEmpty() ? p_item->m_path : p_item->m_text);
|
||||
item->setToolTip(0, p_item->m_path);
|
||||
|
||||
switch (p_item->m_type) {
|
||||
case VSearchResultItem::Note:
|
||||
item->setIcon(0, m_noteIcon);
|
||||
break;
|
||||
|
||||
case VSearchResultItem::Folder:
|
||||
item->setIcon(0, m_folderIcon);
|
||||
break;
|
||||
|
||||
case VSearchResultItem::Notebook:
|
||||
item->setIcon(0, m_notebookIcon);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto const & it: p_item->m_matches) {
|
||||
QTreeWidgetItem *subItem = new QTreeWidgetItem(item);
|
||||
QString text;
|
||||
if (it.m_lineNumber > -1) {
|
||||
text = QString("[%1] %2").arg(it.m_lineNumber).arg(it.m_text);
|
||||
} else {
|
||||
text = it.m_text;
|
||||
}
|
||||
|
||||
subItem->setText(0, text);
|
||||
subItem->setToolTip(0, it.m_text);
|
||||
}
|
||||
}
|
||||
|
||||
void VSearchResultTree::handleItemActivated(QTreeWidgetItem *p_item, int p_column)
|
||||
{
|
||||
Q_UNUSED(p_column);
|
||||
if (!p_item) {
|
||||
return;
|
||||
}
|
||||
|
||||
QTreeWidgetItem *topItem = p_item;
|
||||
if (p_item->parent()) {
|
||||
topItem = p_item->parent();
|
||||
}
|
||||
|
||||
int idx = topItem->data(0, Qt::UserRole).toInt();
|
||||
Q_ASSERT(idx >= 0 && idx < m_data.size());
|
||||
|
||||
const QSharedPointer<VSearchResultItem> &resItem = m_data[idx];
|
||||
switch (resItem->m_type) {
|
||||
case VSearchResultItem::Note:
|
||||
{
|
||||
QStringList files(resItem->m_path);
|
||||
g_mainWin->openFiles(files);
|
||||
break;
|
||||
}
|
||||
|
||||
case VSearchResultItem::Folder:
|
||||
{
|
||||
VDirectory *dir = g_vnote->getInternalDirectory(resItem->m_path);
|
||||
if (dir) {
|
||||
g_mainWin->locateDirectory(dir);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case VSearchResultItem::Notebook:
|
||||
{
|
||||
VNotebook *nb = g_vnote->getNotebook(resItem->m_path);
|
||||
if (nb) {
|
||||
g_mainWin->getNotebookSelector()->locateNotebook(nb);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
39
src/vsearchresulttree.h
Normal file
39
src/vsearchresulttree.h
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef VSEARCHRESULTTREE_H
|
||||
#define VSEARCHRESULTTREE_H
|
||||
|
||||
#include <QIcon>
|
||||
|
||||
#include "vtreewidget.h"
|
||||
#include "vsearch.h"
|
||||
|
||||
|
||||
class VSearchResultTree : public VTreeWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VSearchResultTree(QWidget *p_parent = nullptr);
|
||||
|
||||
void updateResults(const QList<QSharedPointer<VSearchResultItem> > &p_items);
|
||||
|
||||
void clearResults();
|
||||
|
||||
public slots:
|
||||
void addResultItem(const QSharedPointer<VSearchResultItem> &p_item);
|
||||
|
||||
signals:
|
||||
void countChanged(int p_count);
|
||||
|
||||
private slots:
|
||||
void handleItemActivated(QTreeWidgetItem *p_item, int p_column);
|
||||
|
||||
private:
|
||||
void appendItem(const QSharedPointer<VSearchResultItem> &p_item);
|
||||
|
||||
QVector<QSharedPointer<VSearchResultItem> > m_data;
|
||||
|
||||
QIcon m_noteIcon;
|
||||
QIcon m_folderIcon;
|
||||
QIcon m_notebookIcon;
|
||||
};
|
||||
|
||||
#endif // VSEARCHRESULTTREE_H
|
@ -47,6 +47,10 @@ public:
|
||||
|
||||
void setNavigationKeyEnabled(bool p_enabled);
|
||||
|
||||
void setMatchFlags(Qt::MatchFlags p_flags);
|
||||
|
||||
Qt::MatchFlags getMatchFlags() const;
|
||||
|
||||
signals:
|
||||
// Search mode is triggered.
|
||||
void triggered(bool p_inSearchMode);
|
||||
@ -101,4 +105,14 @@ inline void VSimpleSearchInput::setNavigationKeyEnabled(bool p_enabled)
|
||||
{
|
||||
m_navigationKeyEnabled = p_enabled;
|
||||
}
|
||||
|
||||
inline void VSimpleSearchInput::setMatchFlags(Qt::MatchFlags p_flags)
|
||||
{
|
||||
m_matchFlags = p_flags;
|
||||
}
|
||||
|
||||
inline Qt::MatchFlags VSimpleSearchInput::getMatchFlags() const
|
||||
{
|
||||
return m_matchFlags;
|
||||
}
|
||||
#endif // VSIMPLESEARCHINPUT_H
|
||||
|
@ -43,6 +43,8 @@ void VTextEdit::init()
|
||||
{
|
||||
setAcceptRichText(false);
|
||||
|
||||
m_defaultCursorWidth = 1;
|
||||
|
||||
m_lineNumberType = LineNumberType::None;
|
||||
|
||||
m_blockImageEnabled = false;
|
||||
@ -60,6 +62,8 @@ void VTextEdit::init()
|
||||
|
||||
docLayout->setVirtualCursorBlockWidth(VIRTUAL_CURSOR_BLOCK_WIDTH);
|
||||
|
||||
docLayout->setCursorWidth(m_defaultCursorWidth);
|
||||
|
||||
connect(docLayout, &VTextDocumentLayout::cursorBlockWidthUpdated,
|
||||
this, [this](int p_width) {
|
||||
if (p_width != cursorWidth()
|
||||
@ -361,7 +365,7 @@ void VTextEdit::setCursorBlockMode(CursorBlock p_mode)
|
||||
layout->setCursorBlockMode(m_cursorBlockMode);
|
||||
layout->clearLastCursorBlockWidth();
|
||||
setCursorWidth(m_cursorBlockMode != CursorBlock::None ? VIRTUAL_CURSOR_BLOCK_WIDTH
|
||||
: 1);
|
||||
: m_defaultCursorWidth);
|
||||
layout->updateBlockByNumber(textCursor().blockNumber());
|
||||
}
|
||||
}
|
||||
@ -388,3 +392,12 @@ void VTextEdit::relayout()
|
||||
{
|
||||
getLayout()->relayout();
|
||||
}
|
||||
|
||||
void VTextEdit::setDisplayScaleFactor(qreal p_factor)
|
||||
{
|
||||
m_defaultCursorWidth = p_factor + 0.5;
|
||||
|
||||
setCursorWidth(m_cursorBlockMode != CursorBlock::None ? VIRTUAL_CURSOR_BLOCK_WIDTH
|
||||
: m_defaultCursorWidth);
|
||||
getLayout()->setCursorWidth(m_defaultCursorWidth);
|
||||
}
|
||||
|
@ -65,6 +65,8 @@ public:
|
||||
|
||||
void relayout();
|
||||
|
||||
void setDisplayScaleFactor(qreal p_factor);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
@ -91,6 +93,8 @@ private:
|
||||
CursorBlock m_cursorBlockMode;
|
||||
|
||||
bool m_highlightCursorLineBlock;
|
||||
|
||||
int m_defaultCursorWidth;
|
||||
};
|
||||
|
||||
inline void VTextEdit::setLineNumberType(LineNumberType p_type)
|
||||
|
@ -21,6 +21,10 @@ public:
|
||||
// Clear tree widget as well as other data.
|
||||
void clearAll();
|
||||
|
||||
void setSimpleSearchMatchFlags(Qt::MatchFlags p_flags);
|
||||
|
||||
Qt::MatchFlags getSimpleSearchMatchFlags() const;
|
||||
|
||||
// Implement ISimpleSearch.
|
||||
virtual QList<void *> searchItems(const QString &p_text,
|
||||
Qt::MatchFlags p_flags) const Q_DECL_OVERRIDE;
|
||||
@ -65,4 +69,14 @@ private:
|
||||
|
||||
QTimer *m_searchColdTimer;
|
||||
};
|
||||
|
||||
inline void VTreeWidget::setSimpleSearchMatchFlags(Qt::MatchFlags p_flags)
|
||||
{
|
||||
m_searchInput->setMatchFlags(p_flags);
|
||||
}
|
||||
|
||||
inline Qt::MatchFlags VTreeWidget::getSimpleSearchMatchFlags() const
|
||||
{
|
||||
return m_searchInput->getMatchFlags();
|
||||
}
|
||||
#endif // VTREEWIDGET_H
|
||||
|
Loading…
x
Reference in New Issue
Block a user