|
a/src/qtgui/ssearch_w.cpp |
|
b/src/qtgui/ssearch_w.cpp |
|
... |
|
... |
63 |
queryText->setInsertPolicy(QComboBox::NoInsert);
|
63 |
queryText->setInsertPolicy(QComboBox::NoInsert);
|
64 |
|
64 |
|
65 |
queryText->addItems(prefs.ssearchHistory);
|
65 |
queryText->addItems(prefs.ssearchHistory);
|
66 |
queryText->setEditText("");
|
66 |
queryText->setEditText("");
|
67 |
connect(queryText->lineEdit(), SIGNAL(returnPressed()),
|
67 |
connect(queryText->lineEdit(), SIGNAL(returnPressed()),
|
68 |
this, SLOT(startSimpleSearch()));
|
68 |
this, SLOT(startSimpleSearch()));
|
69 |
connect(queryText->lineEdit(), SIGNAL(textChanged(const QString&)),
|
69 |
connect(queryText->lineEdit(), SIGNAL(textChanged(const QString&)),
|
70 |
this, SLOT(searchTextChanged(const QString&)));
|
70 |
this, SLOT(searchTextChanged(const QString&)));
|
71 |
connect(clearqPB, SIGNAL(clicked()),
|
71 |
connect(clearqPB, SIGNAL(clicked()),
|
72 |
queryText->lineEdit(), SLOT(clear()));
|
72 |
queryText->lineEdit(), SLOT(clear()));
|
73 |
connect(searchPB, SIGNAL(clicked()), this, SLOT(startSimpleSearch()));
|
73 |
connect(searchPB, SIGNAL(clicked()), this, SLOT(startSimpleSearch()));
|
74 |
connect(searchTypCMB, SIGNAL(activated(int)), this, SLOT(searchTypeChanged(int)));
|
74 |
connect(searchTypCMB, SIGNAL(activated(int)), this,
|
|
|
75 |
SLOT(searchTypeChanged(int)));
|
75 |
|
76 |
|
76 |
queryText->installEventFilter(this);
|
77 |
queryText->installEventFilter(this);
|
77 |
queryText->view()->installEventFilter(this);
|
78 |
queryText->view()->installEventFilter(this);
|
78 |
queryText->setInsertPolicy(QComboBox::NoInsert);
|
79 |
queryText->setInsertPolicy(QComboBox::NoInsert);
|
79 |
// Note: we can't do the obvious and save the completer instead because
|
80 |
// Note: we can't do the obvious and save the completer instead because
|
80 |
// the combobox lineedit will delete the completer on setCompleter(0).
|
81 |
// the combobox lineedit will delete the completer on setCompleter(0).
|
81 |
// But the model does not belong to the completer so it's not deleted...
|
82 |
// But the model does not belong to the completer so it's not deleted...
|
82 |
m_savedModel = queryText->completer()->model();
|
83 |
m_savedModel = queryText->completer()->model();
|
83 |
if (prefs.ssearchNoComplete)
|
84 |
if (prefs.ssearchNoComplete)
|
84 |
queryText->completer()->setModel(0);
|
85 |
queryText->completer()->setModel(0);
|
85 |
// Recoll searches are always case-sensitive because of the use of
|
86 |
// Recoll searches are always case-sensitive because of the use of
|
86 |
// capitalization to suppress stemming
|
87 |
// capitalization to suppress stemming
|
87 |
queryText->completer()->setCaseSensitivity(Qt::CaseSensitive);
|
88 |
queryText->completer()->setCaseSensitivity(Qt::CaseSensitive);
|
88 |
m_displayingCompletions = false;
|
89 |
m_displayingCompletions = false;
|
89 |
m_escape = false;
|
90 |
m_escape = false;
|
|
... |
|
... |
94 |
m_keystroke = false;
|
95 |
m_keystroke = false;
|
95 |
}
|
96 |
}
|
96 |
|
97 |
|
97 |
void SSearch::takeFocus()
|
98 |
void SSearch::takeFocus()
|
98 |
{
|
99 |
{
|
99 |
LOGDEB2("SSearch: take focus\n" );
|
100 |
LOGDEB2("SSearch: take focus\n");
|
100 |
queryText->setFocus(Qt::ShortcutFocusReason);
|
101 |
queryText->setFocus(Qt::ShortcutFocusReason);
|
101 |
// If the focus was already in the search entry, the text is not selected.
|
102 |
// If the focus was already in the search entry, the text is not selected.
|
102 |
// Do it for consistency
|
103 |
// Do it for consistency
|
103 |
queryText->lineEdit()->selectAll();
|
104 |
queryText->lineEdit()->selectAll();
|
104 |
}
|
105 |
}
|
105 |
|
106 |
|
106 |
void SSearch::timerDone()
|
107 |
void SSearch::timerDone()
|
107 |
{
|
108 |
{
|
108 |
QString qs = queryText->currentText();
|
109 |
QString qs = queryText->currentText();
|
109 |
LOGDEB1("SSearch::timerDone: qs [" << (qs2utf8s(qs)) << "]\n" );
|
110 |
LOGDEB1("SSearch::timerDone: qs [" << qs2utf8s(qs) << "]\n");
|
110 |
searchTextChanged(qs);
|
111 |
searchTextChanged(qs);
|
111 |
}
|
112 |
}
|
112 |
|
113 |
|
113 |
void SSearch::searchTextChanged(const QString& text)
|
114 |
void SSearch::searchTextChanged(const QString& text)
|
114 |
{
|
115 |
{
|
115 |
QString qs = queryText->currentText();
|
116 |
QString qs = queryText->currentText();
|
116 |
LOGDEB1("SSearch::searchTextChanged. ks " << (m_keystroke) << " qs [" << (qs2utf8s(text)) << "]\n" );
|
117 |
LOGDEB1("SSearch::searchTextChanged. ks " << m_keystroke << " qs [" <<
|
|
|
118 |
qs2utf8s(text) << "]\n");
|
117 |
if (text.isEmpty()) {
|
119 |
if (text.isEmpty()) {
|
118 |
searchPB->setEnabled(false);
|
120 |
searchPB->setEnabled(false);
|
119 |
clearqPB->setEnabled(false);
|
121 |
clearqPB->setEnabled(false);
|
120 |
queryText->setFocus();
|
122 |
queryText->setFocus();
|
121 |
emit clearSearch();
|
123 |
emit clearSearch();
|
122 |
} else {
|
124 |
} else {
|
123 |
searchPB->setEnabled(true);
|
125 |
searchPB->setEnabled(true);
|
124 |
clearqPB->setEnabled(true);
|
126 |
clearqPB->setEnabled(true);
|
125 |
if (m_keystroke) {
|
127 |
if (m_keystroke) {
|
126 |
m_tstartqs = qs;
|
128 |
m_tstartqs = qs;
|
127 |
}
|
129 |
}
|
128 |
if (prefs.ssearchAsYouType && !m_disableAutosearch &&
|
130 |
if (prefs.ssearchAsYouType && !m_disableAutosearch &&
|
129 |
!m_keystroke && m_tstartqs == qs) {
|
131 |
!m_keystroke && m_tstartqs == qs) {
|
130 |
m_disableAutosearch = true;
|
132 |
m_disableAutosearch = true;
|
131 |
string s;
|
133 |
string s;
|
132 |
int cs = partialWord(s);
|
134 |
int cs = partialWord(s);
|
133 |
LOGDEB1("SSearch::searchTextChanged: autosearch. cs " << (cs) << " s [" << (s) << "]\n" );
|
135 |
LOGDEB1("SSearch::searchTextChanged: autosearch. cs " << cs <<
|
|
|
136 |
" s [" << s << "]\n");
|
134 |
if (cs < 0) {
|
137 |
if (cs < 0) {
|
135 |
startSimpleSearch();
|
138 |
startSimpleSearch();
|
136 |
} else if (!m_stroketimeout->isActive() && s.size() >= 2) {
|
139 |
} else if (!m_stroketimeout->isActive() && s.size() >= 2) {
|
137 |
s = qs2utf8s(queryText->currentText());
|
140 |
s = qs2utf8s(queryText->currentText());
|
138 |
s += "*";
|
141 |
s += "*";
|
139 |
startSimpleSearch(s, 20);
|
142 |
startSimpleSearch(s, 20);
|
140 |
}
|
143 |
}
|
141 |
}
|
144 |
}
|
142 |
}
|
145 |
}
|
143 |
m_keystroke = false;
|
146 |
m_keystroke = false;
|
144 |
}
|
147 |
}
|
145 |
|
148 |
|
146 |
void SSearch::searchTypeChanged(int typ)
|
149 |
void SSearch::searchTypeChanged(int typ)
|
147 |
{
|
150 |
{
|
148 |
LOGDEB("Search type now " << (typ) << "\n" );
|
151 |
LOGDEB("Search type now " << typ << "\n");
|
149 |
// Adjust context help
|
152 |
// Adjust context help
|
150 |
if (typ == SST_LANG)
|
153 |
if (typ == SST_LANG) {
|
151 |
HelpClient::installMap((const char *)this->objectName().toUtf8(),
|
154 |
HelpClient::installMap((const char *)this->objectName().toUtf8(),
|
152 |
"RCL.SEARCH.LANG");
|
155 |
"RCL.SEARCH.LANG");
|
153 |
else
|
156 |
} else {
|
154 |
HelpClient::installMap((const char *)this->objectName().toUtf8(),
|
157 |
HelpClient::installMap((const char *)this->objectName().toUtf8(),
|
155 |
"RCL.SEARCH.GUI.SIMPLE");
|
158 |
"RCL.SEARCH.GUI.SIMPLE");
|
156 |
|
159 |
}
|
157 |
// Also fix tooltips
|
160 |
// Also fix tooltips
|
158 |
switch (typ) {
|
161 |
switch (typ) {
|
159 |
case SST_LANG:
|
162 |
case SST_LANG:
|
160 |
queryText->setToolTip(tr(
|
163 |
queryText->setToolTip(
|
|
|
164 |
tr(
|
161 |
"Enter query language expression. Cheat sheet:<br>\n"
|
165 |
"Enter query language expression. Cheat sheet:<br>\n"
|
162 |
"<i>term1 term2</i> : 'term1' and 'term2' in any field.<br>\n"
|
166 |
"<i>term1 term2</i> : 'term1' and 'term2' in any field.<br>\n"
|
163 |
"<i>field:term1</i> : 'term1' in field 'field'.<br>\n"
|
167 |
"<i>field:term1</i> : 'term1' in field 'field'.<br>\n"
|
164 |
" Standard field names/synonyms:<br>\n"
|
168 |
" Standard field names/synonyms:<br>\n"
|
165 |
" title/subject/caption, author/from, recipient/to, filename, ext.<br>\n"
|
169 |
" title/subject/caption, author/from, recipient/to, filename, ext.<br>\n"
|
|
... |
|
... |
168 |
"<i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br>\n"
|
172 |
"<i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br>\n"
|
169 |
" You can use parentheses to make things clearer.<br>\n"
|
173 |
" You can use parentheses to make things clearer.<br>\n"
|
170 |
"<i>\"term1 term2\"</i> : phrase (must occur exactly). Possible modifiers:<br>\n"
|
174 |
"<i>\"term1 term2\"</i> : phrase (must occur exactly). Possible modifiers:<br>\n"
|
171 |
"<i>\"term1 term2\"p</i> : unordered proximity search with default distance.<br>\n"
|
175 |
"<i>\"term1 term2\"p</i> : unordered proximity search with default distance.<br>\n"
|
172 |
"Use <b>Show Query</b> link when in doubt about result and see manual (<F1>) for more detail.\n"
|
176 |
"Use <b>Show Query</b> link when in doubt about result and see manual (<F1>) for more detail.\n"
|
173 |
));
|
177 |
));
|
174 |
break;
|
178 |
break;
|
175 |
case SST_FNM:
|
179 |
case SST_FNM:
|
176 |
queryText->setToolTip(tr("Enter file name wildcard expression."));
|
180 |
queryText->setToolTip(tr("Enter file name wildcard expression."));
|
177 |
break;
|
181 |
break;
|
178 |
case SST_ANY:
|
182 |
case SST_ANY:
|
179 |
case SST_ALL:
|
183 |
case SST_ALL:
|
180 |
default:
|
184 |
default:
|
181 |
queryText->setToolTip(tr(
|
185 |
queryText->setToolTip(
|
|
|
186 |
tr(
|
182 |
"Enter search terms here. Type ESC SPC for completions of current term."
|
187 |
"Enter search terms here. Type ESC SPC for completions of current term."
|
183 |
));
|
188 |
));
|
184 |
}
|
189 |
}
|
185 |
}
|
190 |
}
|
186 |
|
191 |
|
187 |
void SSearch::startSimpleSearch()
|
192 |
void SSearch::startSimpleSearch()
|
188 |
{
|
193 |
{
|
189 |
QString qs = queryText->currentText();
|
194 |
QString qs = queryText->currentText();
|
190 |
LOGDEB("SSearch::startSimpleSearch(): qs [" << (qs2utf8s(qs)) << "]\n" );
|
195 |
LOGDEB("SSearch::startSimpleSearch(): qs [" << qs2utf8s(qs) << "]\n");
|
191 |
if (qs.length() == 0)
|
196 |
if (qs.length() == 0)
|
192 |
return;
|
197 |
return;
|
193 |
|
198 |
|
194 |
string u8 = (const char *)queryText->currentText().toUtf8();
|
199 |
string u8 = (const char *)queryText->currentText().toUtf8();
|
195 |
|
200 |
|
196 |
trimstring(u8);
|
201 |
trimstring(u8);
|
197 |
if (u8.length() == 0)
|
202 |
if (u8.length() == 0)
|
198 |
return;
|
203 |
return;
|
199 |
|
204 |
|
200 |
if (!startSimpleSearch(u8))
|
205 |
if (!startSimpleSearch(u8))
|
201 |
return;
|
206 |
return;
|
202 |
|
207 |
|
203 |
LOGDEB("startSimpleSearch: updating history\n" );
|
208 |
LOGDEB("startSimpleSearch: updating history\n");
|
204 |
// Search terms history:
|
209 |
// Search terms history:
|
205 |
// We want to have the new text at the top and any older identical
|
210 |
// We want to have the new text at the top and any older identical
|
206 |
// entry to be erased. There is no standard qt policy to do this ?
|
211 |
// entry to be erased. There is no standard qt policy to do this ?
|
207 |
// So do it by hand.
|
212 |
// So do it by hand.
|
208 |
QString txt = queryText->currentText();
|
213 |
QString txt = queryText->currentText();
|
209 |
QString txtt = txt.trimmed();
|
214 |
QString txtt = txt.trimmed();
|
210 |
int index = queryText->findText(txtt);
|
215 |
int index = queryText->findText(txtt);
|
211 |
if (index > 0) {
|
216 |
if (index > 0) {
|
212 |
queryText->removeItem(index);
|
217 |
queryText->removeItem(index);
|
213 |
}
|
218 |
}
|
214 |
if (index != 0) {
|
219 |
if (index != 0) {
|
215 |
queryText->insertItem(0, txtt);
|
220 |
queryText->insertItem(0, txtt);
|
216 |
queryText->setEditText(txt);
|
221 |
queryText->setEditText(txt);
|
217 |
}
|
222 |
}
|
218 |
m_disableAutosearch = true;
|
223 |
m_disableAutosearch = true;
|
219 |
m_stroketimeout->stop();
|
224 |
m_stroketimeout->stop();
|
220 |
|
225 |
|
221 |
// Save the current state of the listbox list to the prefs (will
|
226 |
// Save the current state of the listbox list to the prefs (will
|
222 |
// go to disk)
|
227 |
// go to disk)
|
223 |
prefs.ssearchHistory.clear();
|
228 |
prefs.ssearchHistory.clear();
|
224 |
for (int index = 0; index < queryText->count(); index++) {
|
229 |
for (int index = 0; index < queryText->count(); index++) {
|
225 |
prefs.ssearchHistory.push_back(queryText->itemText(index));
|
230 |
prefs.ssearchHistory.push_back(queryText->itemText(index));
|
226 |
}
|
231 |
}
|
227 |
}
|
232 |
}
|
228 |
void SSearch::setPrefs()
|
233 |
void SSearch::setPrefs()
|
229 |
{
|
234 |
{
|
230 |
if (prefs.ssearchNoComplete) {
|
235 |
if (prefs.ssearchNoComplete) {
|
231 |
queryText->completer()->setModel(0);
|
236 |
queryText->completer()->setModel(0);
|
232 |
} else {
|
237 |
} else {
|
233 |
queryText->completer()->setModel(m_savedModel);
|
238 |
queryText->completer()->setModel(m_savedModel);
|
234 |
}
|
239 |
}
|
235 |
}
|
240 |
}
|
236 |
|
241 |
|
237 |
string SSearch::asXML()
|
242 |
string SSearch::asXML()
|
238 |
{
|
243 |
{
|
239 |
return m_xml;
|
244 |
return m_xml;
|
240 |
}
|
245 |
}
|
241 |
|
246 |
|
242 |
bool SSearch::startSimpleSearch(const string& u8, int maxexp)
|
247 |
bool SSearch::startSimpleSearch(const string& u8, int maxexp)
|
243 |
{
|
248 |
{
|
244 |
LOGDEB("SSearch::startSimpleSearch(" << (u8) << ")\n" );
|
249 |
LOGDEB("SSearch::startSimpleSearch(" << u8 << ")\n");
|
245 |
string stemlang = prefs.stemlang();
|
250 |
string stemlang = prefs.stemlang();
|
246 |
|
251 |
|
247 |
ostringstream xml;
|
252 |
ostringstream xml;
|
248 |
xml << "<SD type='ssearch'>\n";
|
253 |
xml << "<SD type='ssearch'>\n";
|
249 |
xml << " <SL>" << stemlang << "</SL>\n";
|
254 |
xml << " <SL>" << stemlang << "</SL>\n";
|
|
... |
|
... |
252 |
SSearchType tp = (SSearchType)searchTypCMB->currentIndex();
|
257 |
SSearchType tp = (SSearchType)searchTypCMB->currentIndex();
|
253 |
Rcl::SearchData *sdata = 0;
|
258 |
Rcl::SearchData *sdata = 0;
|
254 |
|
259 |
|
255 |
if (tp == SST_LANG) {
|
260 |
if (tp == SST_LANG) {
|
256 |
xml << " <SM>QL</SM>\n";
|
261 |
xml << " <SM>QL</SM>\n";
|
257 |
string reason;
|
262 |
string reason;
|
258 |
if (prefs.autoSuffsEnable) {
|
263 |
if (prefs.autoSuffsEnable) {
|
259 |
sdata = wasaStringToRcl(theconfig, stemlang, u8, reason,
|
264 |
sdata = wasaStringToRcl(theconfig, stemlang, u8, reason,
|
260 |
(const char *)prefs.autoSuffs.toUtf8());
|
265 |
(const char *)prefs.autoSuffs.toUtf8());
|
261 |
if (!prefs.autoSuffs.isEmpty()) {
|
266 |
if (!prefs.autoSuffs.isEmpty()) {
|
262 |
xml << " <AS>" << qs2utf8s(prefs.autoSuffs) << "</AS>\n";
|
267 |
xml << " <AS>" << qs2utf8s(prefs.autoSuffs) << "</AS>\n";
|
263 |
}
|
268 |
}
|
264 |
} else {
|
269 |
} else {
|
265 |
sdata = wasaStringToRcl(theconfig, stemlang, u8, reason);
|
270 |
sdata = wasaStringToRcl(theconfig, stemlang, u8, reason);
|
266 |
}
|
271 |
}
|
267 |
if (sdata == 0) {
|
272 |
if (sdata == 0) {
|
268 |
QMessageBox::warning(0, "Recoll", tr("Bad query string") + ": " +
|
273 |
QMessageBox::warning(0, "Recoll", tr("Bad query string") + ": " +
|
269 |
QString::fromUtf8(reason.c_str()));
|
274 |
QString::fromUtf8(reason.c_str()));
|
270 |
return false;
|
275 |
return false;
|
271 |
}
|
276 |
}
|
272 |
} else {
|
277 |
} else {
|
273 |
sdata = new Rcl::SearchData(Rcl::SCLT_OR, stemlang);
|
278 |
sdata = new Rcl::SearchData(Rcl::SCLT_OR, stemlang);
|
274 |
if (sdata == 0) {
|
279 |
if (sdata == 0) {
|
275 |
QMessageBox::warning(0, "Recoll", tr("Out of memory"));
|
280 |
QMessageBox::warning(0, "Recoll", tr("Out of memory"));
|
276 |
return false;
|
281 |
return false;
|
277 |
}
|
282 |
}
|
278 |
Rcl::SearchDataClause *clp = 0;
|
283 |
Rcl::SearchDataClause *clp = 0;
|
279 |
if (tp == SST_FNM) {
|
284 |
if (tp == SST_FNM) {
|
280 |
xml << " <SM>FN</SM>\n";
|
285 |
xml << " <SM>FN</SM>\n";
|
281 |
clp = new Rcl::SearchDataClauseFilename(u8);
|
286 |
clp = new Rcl::SearchDataClauseFilename(u8);
|
282 |
} else {
|
287 |
} else {
|
283 |
// ANY or ALL, several words.
|
288 |
// ANY or ALL, several words.
|
284 |
if (tp == SST_ANY) {
|
289 |
if (tp == SST_ANY) {
|
285 |
xml << " <SM>OR</SM>\n";
|
290 |
xml << " <SM>OR</SM>\n";
|
286 |
clp = new Rcl::SearchDataClauseSimple(Rcl::SCLT_OR, u8);
|
291 |
clp = new Rcl::SearchDataClauseSimple(Rcl::SCLT_OR, u8);
|
287 |
} else {
|
292 |
} else {
|
288 |
xml << " <SM>AND</SM>\n";
|
293 |
xml << " <SM>AND</SM>\n";
|
289 |
clp = new Rcl::SearchDataClauseSimple(Rcl::SCLT_AND, u8);
|
294 |
clp = new Rcl::SearchDataClauseSimple(Rcl::SCLT_AND, u8);
|
290 |
}
|
295 |
}
|
291 |
}
|
296 |
}
|
292 |
sdata->addClause(clp);
|
297 |
sdata->addClause(clp);
|
293 |
}
|
298 |
}
|
294 |
|
299 |
|
295 |
if (prefs.ssearchAutoPhrase && rcldb) {
|
300 |
if (prefs.ssearchAutoPhrase && rcldb) {
|
296 |
xml << " <AP/>\n";
|
301 |
xml << " <AP/>\n";
|
297 |
sdata->maybeAddAutoPhrase(*rcldb,
|
302 |
sdata->maybeAddAutoPhrase(*rcldb,
|
298 |
prefs.ssearchAutoPhraseThreshPC / 100.0);
|
303 |
prefs.ssearchAutoPhraseThreshPC / 100.0);
|
299 |
}
|
304 |
}
|
300 |
if (maxexp != -1) {
|
305 |
if (maxexp != -1) {
|
301 |
sdata->setMaxExpand(maxexp);
|
306 |
sdata->setMaxExpand(maxexp);
|
302 |
}
|
307 |
}
|
303 |
|
308 |
|
304 |
for (const auto& dbdir : prefs.activeExtraDbs) {
|
309 |
for (const auto& dbdir : prefs.activeExtraDbs) {
|
305 |
xml << " <EX>" << base64_encode(dbdir) << "</EX>";
|
310 |
xml << " <EX>" << base64_encode(dbdir) << "</EX>";
|
306 |
}
|
311 |
}
|
307 |
|
312 |
|
308 |
xml << "</SD>\n";
|
313 |
xml << "</SD>\n";
|
309 |
m_xml = xml.str();
|
314 |
m_xml = xml.str();
|
310 |
LOGDEB("SSearch::startSimpleSearch:xml:[" << (m_xml) << "]\n" );
|
315 |
LOGDEB("SSearch::startSimpleSearch:xml:[" << m_xml << "]\n");
|
311 |
|
316 |
|
312 |
std::shared_ptr<Rcl::SearchData> rsdata(sdata);
|
317 |
std::shared_ptr<Rcl::SearchData> rsdata(sdata);
|
313 |
emit startSearch(rsdata, true);
|
318 |
emit startSearch(rsdata, true);
|
314 |
return true;
|
319 |
return true;
|
315 |
}
|
320 |
}
|
|
... |
|
... |
391 |
// instead of auto as for preview, needed because it's built by
|
396 |
// instead of auto as for preview, needed because it's built by
|
392 |
// fragments?).
|
397 |
// fragments?).
|
393 |
static const char* punct = " \t()<>\"'[]{}!^*.,:;\n\r";
|
398 |
static const char* punct = " \t()<>\"'[]{}!^*.,:;\n\r";
|
394 |
void SSearch::addTerm(QString term)
|
399 |
void SSearch::addTerm(QString term)
|
395 |
{
|
400 |
{
|
396 |
LOGDEB("SSearch::AddTerm: [" << ((const char *)term.toUtf8()) << "]\n" );
|
401 |
LOGDEB("SSearch::AddTerm: [" << qs2utf8s(term) << "]\n");
|
397 |
string t = (const char *)term.toUtf8();
|
402 |
string t = (const char *)term.toUtf8();
|
398 |
string::size_type pos = t.find_last_not_of(punct);
|
403 |
string::size_type pos = t.find_last_not_of(punct);
|
399 |
if (pos == string::npos)
|
404 |
if (pos == string::npos)
|
400 |
return;
|
405 |
return;
|
401 |
t = t.substr(0, pos+1);
|
406 |
t = t.substr(0, pos+1);
|
402 |
pos = t.find_first_not_of(punct);
|
407 |
pos = t.find_first_not_of(punct);
|
403 |
if (pos != string::npos)
|
408 |
if (pos != string::npos)
|
404 |
t = t.substr(pos);
|
409 |
t = t.substr(pos);
|
405 |
if (t.empty())
|
410 |
if (t.empty())
|
406 |
return;
|
411 |
return;
|
407 |
term = QString::fromUtf8(t.c_str());
|
412 |
term = QString::fromUtf8(t.c_str());
|
408 |
|
413 |
|
409 |
QString text = queryText->currentText();
|
414 |
QString text = queryText->currentText();
|
410 |
text += QString::fromLatin1(" ") + term;
|
415 |
text += QString::fromLatin1(" ") + term;
|
411 |
queryText->setEditText(text);
|
416 |
queryText->setEditText(text);
|
|
... |
|
... |
413 |
m_stroketimeout->stop();
|
418 |
m_stroketimeout->stop();
|
414 |
}
|
419 |
}
|
415 |
|
420 |
|
416 |
void SSearch::onWordReplace(const QString& o, const QString& n)
|
421 |
void SSearch::onWordReplace(const QString& o, const QString& n)
|
417 |
{
|
422 |
{
|
418 |
LOGDEB("SSearch::onWordReplace: o [" << (qs2utf8s(o)) << "] n [" << (qs2utf8s(n)) << "]\n" );
|
423 |
LOGDEB("SSearch::onWordReplace: o [" << qs2utf8s(o) << "] n [" <<
|
|
|
424 |
qs2utf8s(n) << "]\n");
|
419 |
QString txt = queryText->currentText();
|
425 |
QString txt = queryText->currentText();
|
420 |
QRegExp exp = QRegExp(QString("\\b") + o + QString("\\b"));
|
426 |
QRegExp exp = QRegExp(QString("\\b") + o + QString("\\b"));
|
421 |
exp.setCaseSensitivity(Qt::CaseInsensitive);
|
427 |
exp.setCaseSensitivity(Qt::CaseInsensitive);
|
422 |
txt.replace(exp, n);
|
428 |
txt.replace(exp, n);
|
423 |
queryText->setEditText(txt);
|
429 |
queryText->setEditText(txt);
|
424 |
m_disableAutosearch = true;
|
430 |
m_disableAutosearch = true;
|
425 |
m_stroketimeout->stop();
|
431 |
m_stroketimeout->stop();
|
426 |
Qt::KeyboardModifiers mods = QApplication::keyboardModifiers ();
|
432 |
Qt::KeyboardModifiers mods = QApplication::keyboardModifiers ();
|
427 |
if (mods == Qt::NoModifier)
|
433 |
if (mods == Qt::NoModifier)
|
428 |
startSimpleSearch();
|
434 |
startSimpleSearch();
|
429 |
}
|
435 |
}
|
430 |
|
436 |
|
431 |
void SSearch::setAnyTermMode()
|
437 |
void SSearch::setAnyTermMode()
|
432 |
{
|
438 |
{
|
433 |
searchTypCMB->setCurrentIndex(SST_ANY);
|
439 |
searchTypCMB->setCurrentIndex(SST_ANY);
|
|
... |
|
... |
439 |
{
|
445 |
{
|
440 |
// Extract last word in text
|
446 |
// Extract last word in text
|
441 |
QString txt = queryText->currentText();
|
447 |
QString txt = queryText->currentText();
|
442 |
int cs = txt.lastIndexOf(" ");
|
448 |
int cs = txt.lastIndexOf(" ");
|
443 |
if (cs == -1)
|
449 |
if (cs == -1)
|
444 |
cs = 0;
|
450 |
cs = 0;
|
445 |
else
|
451 |
else
|
446 |
cs++;
|
452 |
cs++;
|
447 |
if (txt.size() == 0 || cs == txt.size()) {
|
453 |
if (txt.size() == 0 || cs == txt.size()) {
|
448 |
return -1;
|
454 |
return -1;
|
449 |
}
|
455 |
}
|
450 |
s = qs2utf8s(txt.right(txt.size() - cs));
|
456 |
s = qs2utf8s(txt.right(txt.size() - cs));
|
451 |
return cs;
|
457 |
return cs;
|
452 |
}
|
458 |
}
|
453 |
|
459 |
|
454 |
// Create completion list for term by adding a joker at the end and calling
|
460 |
// Create completion list for term by adding a joker at the end and calling
|
455 |
// rcldb->termMatch().
|
461 |
// rcldb->termMatch().
|
456 |
int SSearch::completionList(string s, QStringList& lst, int max)
|
462 |
int SSearch::completionList(string s, QStringList& lst, int max)
|
457 |
{
|
463 |
{
|
458 |
if (!rcldb)
|
464 |
if (!rcldb)
|
459 |
return -1;
|
465 |
return -1;
|
460 |
if (s.empty())
|
466 |
if (s.empty())
|
461 |
return 0;
|
467 |
return 0;
|
462 |
// Query database for completions
|
468 |
// Query database for completions
|
463 |
s += "*";
|
469 |
s += "*";
|
464 |
Rcl::TermMatchResult tmres;
|
470 |
Rcl::TermMatchResult tmres;
|
465 |
if (!rcldb->termMatch(Rcl::Db::ET_WILD, "", s, tmres, max) ||
|
471 |
if (!rcldb->termMatch(Rcl::Db::ET_WILD, "", s, tmres, max) ||
|
466 |
tmres.entries.size() == 0) {
|
472 |
tmres.entries.size() == 0) {
|
467 |
return 0;
|
473 |
return 0;
|
468 |
}
|
474 |
}
|
469 |
for (vector<Rcl::TermMatchEntry>::iterator it = tmres.entries.begin();
|
475 |
for (const auto& entry: tmres.entries) {
|
470 |
it != tmres.entries.end(); it++) {
|
476 |
lst.push_back(u8s2qs(entry.term));
|
471 |
lst.push_back(QString::fromUtf8(it->term.c_str()));
|
|
|
472 |
}
|
477 |
}
|
473 |
return lst.size();
|
478 |
return lst.size();
|
474 |
}
|
479 |
}
|
475 |
|
480 |
|
476 |
// Complete last word in input by querying db for all possible terms.
|
481 |
// Complete last word in input by querying db for all possible terms.
|
477 |
void SSearch::completion()
|
482 |
void SSearch::completion()
|
478 |
{
|
483 |
{
|
479 |
LOGDEB("SSearch::completion\n" );
|
484 |
LOGDEB("SSearch::completion\n");
|
480 |
|
485 |
|
481 |
m_disableAutosearch = true;
|
486 |
m_disableAutosearch = true;
|
482 |
m_stroketimeout->stop();
|
487 |
m_stroketimeout->stop();
|
483 |
|
488 |
|
484 |
if (!rcldb)
|
489 |
if (!rcldb)
|
485 |
return;
|
490 |
return;
|
486 |
if (searchTypCMB->currentIndex() == SST_FNM) {
|
491 |
if (searchTypCMB->currentIndex() == SST_FNM) {
|
487 |
// Filename: no completion
|
492 |
// Filename: no completion
|
488 |
QApplication::beep();
|
493 |
QApplication::beep();
|
489 |
return;
|
494 |
return;
|
490 |
}
|
495 |
}
|
491 |
|
496 |
|
492 |
// Extract last word in text
|
497 |
// Extract last word in text
|
493 |
string s;
|
498 |
string s;
|
494 |
int cs = partialWord(s);
|
499 |
int cs = partialWord(s);
|
495 |
if (cs < 0) {
|
500 |
if (cs < 0) {
|
496 |
QApplication::beep();
|
501 |
QApplication::beep();
|
497 |
return;
|
502 |
return;
|
498 |
}
|
503 |
}
|
499 |
|
504 |
|
500 |
// Query database for completions
|
505 |
// Query database for completions
|
501 |
QStringList lst;
|
506 |
QStringList lst;
|
502 |
const int maxdpy = 80;
|
507 |
const int maxdpy = 80;
|
503 |
const int maxwalked = 10000;
|
508 |
const int maxwalked = 10000;
|
504 |
if (completionList(s, lst, maxwalked) <= 0) {
|
509 |
if (completionList(s, lst, maxwalked) <= 0) {
|
505 |
QApplication::beep();
|
510 |
QApplication::beep();
|
506 |
return;
|
511 |
return;
|
507 |
}
|
512 |
}
|
508 |
if (lst.size() >= maxdpy) {
|
513 |
if (lst.size() >= maxdpy) {
|
509 |
LOGDEB0("SSearch::completion(): truncating list\n" );
|
514 |
LOGDEB0("SSearch::completion(): truncating list\n");
|
510 |
lst = lst.mid(0, maxdpy);
|
515 |
lst = lst.mid(0, maxdpy);
|
511 |
lst.append("[...]");
|
516 |
lst.append("[...]");
|
512 |
}
|
517 |
}
|
513 |
|
518 |
|
514 |
// If list from db is single word, insert it, else popup the listview
|
519 |
// If list from db is single word, insert it, else popup the listview
|
515 |
if (lst.size() == 1) {
|
520 |
if (lst.size() == 1) {
|
516 |
QString txt = queryText->currentText();
|
521 |
QString txt = queryText->currentText();
|
517 |
txt.truncate(cs);
|
522 |
txt.truncate(cs);
|
518 |
txt.append(lst[0]);
|
523 |
txt.append(lst[0]);
|
519 |
queryText->setEditText(txt);
|
524 |
queryText->setEditText(txt);
|
520 |
} else {
|
525 |
} else {
|
521 |
m_savedEditText = queryText->currentText();
|
526 |
m_savedEditText = queryText->currentText();
|
522 |
m_displayingCompletions = true;
|
527 |
m_displayingCompletions = true;
|
523 |
m_chosenCompletion.clear();
|
528 |
m_chosenCompletion.clear();
|
524 |
m_completedWordStart = cs;
|
529 |
m_completedWordStart = cs;
|
525 |
|
530 |
|
526 |
queryText->clear();
|
531 |
queryText->clear();
|
527 |
queryText->addItems(lst);
|
532 |
queryText->addItems(lst);
|
528 |
queryText->showPopup();
|
533 |
queryText->showPopup();
|
529 |
|
534 |
|
530 |
connect(queryText, SIGNAL(activated(const QString&)), this,
|
535 |
connect(queryText, SIGNAL(activated(const QString&)), this,
|
531 |
SLOT(completionTermChosen(const QString&)));
|
536 |
SLOT(completionTermChosen(const QString&)));
|
532 |
}
|
537 |
}
|
533 |
}
|
538 |
}
|
534 |
|
539 |
|
535 |
void SSearch::completionTermChosen(const QString& text)
|
540 |
void SSearch::completionTermChosen(const QString& text)
|
536 |
{
|
541 |
{
|
537 |
if (text != "[...]")
|
542 |
if (text != "[...]")
|
538 |
m_chosenCompletion = text;
|
543 |
m_chosenCompletion = text;
|
539 |
else
|
544 |
else
|
540 |
m_chosenCompletion.clear();
|
545 |
m_chosenCompletion.clear();
|
541 |
}
|
546 |
}
|
542 |
|
547 |
|
543 |
void SSearch::wrapupCompletion()
|
548 |
void SSearch::wrapupCompletion()
|
544 |
{
|
549 |
{
|
545 |
LOGDEB("SSearch::wrapupCompletion\n" );
|
550 |
LOGDEB("SSearch::wrapupCompletion\n");
|
546 |
|
551 |
|
547 |
queryText->clear();
|
552 |
queryText->clear();
|
548 |
queryText->addItems(prefs.ssearchHistory);
|
553 |
queryText->addItems(prefs.ssearchHistory);
|
549 |
if (!m_chosenCompletion.isEmpty()) {
|
554 |
if (!m_chosenCompletion.isEmpty()) {
|
550 |
m_savedEditText.truncate(m_completedWordStart);
|
555 |
m_savedEditText.truncate(m_completedWordStart);
|
551 |
m_savedEditText.append(m_chosenCompletion);
|
556 |
m_savedEditText.append(m_chosenCompletion);
|
552 |
}
|
557 |
}
|
553 |
queryText->setEditText(m_savedEditText);
|
558 |
queryText->setEditText(m_savedEditText);
|
554 |
m_disableAutosearch = true;
|
559 |
m_disableAutosearch = true;
|
555 |
m_savedEditText.clear();
|
560 |
m_savedEditText.clear();
|
556 |
m_chosenCompletion.clear();
|
561 |
m_chosenCompletion.clear();
|
557 |
m_displayingCompletions = false;
|
562 |
m_displayingCompletions = false;
|
558 |
disconnect(queryText, SIGNAL(activated(const QString&)), this,
|
563 |
disconnect(queryText, SIGNAL(activated(const QString&)), this,
|
559 |
SLOT(completionTermChosen(const QString&)));
|
564 |
SLOT(completionTermChosen(const QString&)));
|
560 |
}
|
565 |
}
|
561 |
|
566 |
|
562 |
#undef SHOWEVENTS
|
567 |
#undef SHOWEVENTS
|
563 |
#if defined(SHOWEVENTS)
|
568 |
#if defined(SHOWEVENTS)
|
564 |
const char *eventTypeToStr(int tp)
|
569 |
const char *eventTypeToStr(int tp)
|
|
... |
|
... |
600 |
case 37: return "ApplicationLayoutDirectionChange";
|
605 |
case 37: return "ApplicationLayoutDirectionChange";
|
601 |
case 38: return "ApplicationPaletteChange";
|
606 |
case 38: return "ApplicationPaletteChange";
|
602 |
case 39: return "PaletteChange";
|
607 |
case 39: return "PaletteChange";
|
603 |
case 40: return "Clipboard";
|
608 |
case 40: return "Clipboard";
|
604 |
case 42: return "Speech";
|
609 |
case 42: return "Speech";
|
605 |
case 43: return "MetaCall";
|
610 |
case 43: return "MetaCall";
|
606 |
case 50: return "SockAct";
|
611 |
case 50: return "SockAct";
|
607 |
case 132: return "WinEventAct";
|
612 |
case 132: return "WinEventAct";
|
608 |
case 52: return "DeferredDelete";
|
613 |
case 52: return "DeferredDelete";
|
609 |
case 60: return "DragEnter";
|
614 |
case 60: return "DragEnter";
|
610 |
case 61: return "DragMove";
|
615 |
case 61: return "DragMove";
|
|
... |
|
... |
702 |
|
707 |
|
703 |
bool SSearch::eventFilter(QObject *target, QEvent *event)
|
708 |
bool SSearch::eventFilter(QObject *target, QEvent *event)
|
704 |
{
|
709 |
{
|
705 |
#if defined(SHOWEVENTS)
|
710 |
#if defined(SHOWEVENTS)
|
706 |
if (event->type() == QEvent::Timer ||
|
711 |
if (event->type() == QEvent::Timer ||
|
707 |
event->type() == QEvent::UpdateRequest ||
|
712 |
event->type() == QEvent::UpdateRequest ||
|
708 |
event->type() == QEvent::Paint)
|
713 |
event->type() == QEvent::Paint)
|
709 |
return false;
|
714 |
return false;
|
710 |
LOGDEB2("SSearch::eventFilter: target " << (target) << " (" << (queryText->lineEdit()) << ") type " << (eventTypeToStr(event->type())) << "\n" );
|
715 |
LOGDEB2("SSearch::eventFilter: target " << target << " (" <<
|
|
|
716 |
queryText->lineEdit() << ") type " <<
|
|
|
717 |
eventTypeToStr(event->type()) << "\n");
|
711 |
#endif
|
718 |
#endif
|
712 |
|
719 |
|
713 |
if (target == (QObject*)(queryText->view())) {
|
720 |
if (target == (QObject*)(queryText->view())) {
|
714 |
if (event->type() == QEvent::Hide) {
|
721 |
if (event->type() == QEvent::Hide) {
|
715 |
// List was closed. If we were displaying completions, need
|
722 |
// List was closed. If we were displaying completions, need
|
716 |
// to reset state.
|
723 |
// to reset state.
|
717 |
if (m_displayingCompletions) {
|
724 |
if (m_displayingCompletions) {
|
718 |
QTimer::singleShot(0, this, SLOT(wrapupCompletion()));
|
725 |
QTimer::singleShot(0, this, SLOT(wrapupCompletion()));
|
719 |
}
|
726 |
}
|
720 |
}
|
727 |
}
|
721 |
return false;
|
728 |
return false;
|
722 |
}
|
729 |
}
|
723 |
|
730 |
|
724 |
if (event->type() == QEvent::KeyPress) {
|
731 |
if (event->type() == QEvent::KeyPress) {
|
725 |
QKeyEvent *ke = (QKeyEvent *)event;
|
732 |
QKeyEvent *ke = (QKeyEvent *)event;
|
726 |
LOGDEB1("SSearch::eventFilter: keyPress (m_escape " << (m_escape) << ") key " << (ke->key()) << "\n" );
|
733 |
LOGDEB1("SSearch::eventFilter: keyPress (m_escape " << m_escape <<
|
|
|
734 |
") key " << ke->key() << "\n");
|
727 |
if (ke->key() == Qt::Key_Escape) {
|
735 |
if (ke->key() == Qt::Key_Escape) {
|
728 |
LOGDEB("Escape\n" );
|
736 |
LOGDEB("Escape\n");
|
729 |
m_escape = true;
|
737 |
m_escape = true;
|
730 |
m_disableAutosearch = true;
|
738 |
m_disableAutosearch = true;
|
731 |
m_stroketimeout->stop();
|
739 |
m_stroketimeout->stop();
|
732 |
return true;
|
740 |
return true;
|
733 |
} else if (m_escape && ke->key() == Qt::Key_Space) {
|
741 |
} else if (m_escape && ke->key() == Qt::Key_Space) {
|
734 |
LOGDEB("Escape space\n" );
|
742 |
LOGDEB("Escape space\n");
|
735 |
ke->accept();
|
743 |
ke->accept();
|
736 |
completion();
|
744 |
completion();
|
737 |
m_escape = false;
|
745 |
m_escape = false;
|
738 |
m_disableAutosearch = true;
|
746 |
m_disableAutosearch = true;
|
739 |
m_stroketimeout->stop();
|
747 |
m_stroketimeout->stop();
|
740 |
return true;
|
748 |
return true;
|
741 |
} else if (ke->key() == Qt::Key_Space) {
|
749 |
} else if (ke->key() == Qt::Key_Space) {
|
742 |
if (prefs.ssearchOnWS)
|
750 |
if (prefs.ssearchOnWS)
|
743 |
startSimpleSearch();
|
751 |
startSimpleSearch();
|
744 |
} else {
|
752 |
} else {
|
745 |
m_escape = false;
|
753 |
m_escape = false;
|
746 |
m_keystroke = true;
|
754 |
m_keystroke = true;
|
747 |
if (prefs.ssearchAsYouType) {
|
755 |
if (prefs.ssearchAsYouType) {
|
748 |
m_disableAutosearch = false;
|
756 |
m_disableAutosearch = false;
|
749 |
QString qs = queryText->currentText();
|
757 |
QString qs = queryText->currentText();
|
750 |
LOGDEB0("SSearch::eventFilter: start timer, qs [" << (qs2utf8s(qs)) << "]\n" );
|
758 |
LOGDEB0("SSearch::eventFilter: start timer, qs [" <<
|
|
|
759 |
qs2utf8s(qs) << "]\n");
|
751 |
m_stroketimeout->start(strokeTimeoutMS);
|
760 |
m_stroketimeout->start(strokeTimeoutMS);
|
752 |
}
|
761 |
}
|
753 |
}
|
762 |
}
|
754 |
}
|
763 |
}
|
755 |
return false;
|
764 |
return false;
|
756 |
}
|
765 |
}
|
757 |
|
|
|