1 /* Copyright (C) 2005-2019 J.F.Dockes
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the
14 * Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 */
17 #include "autoconfig.h"
18
19 #include <QMessageBox>
20 #include <QShortcut>
21
22 #include "log.h"
23 #include "internfile.h"
24 #include "rclzg.h"
25 #include "rclmain_w.h"
26
27 static const QKeySequence quitKeySeq("Ctrl+q");
28
29 // If a preview (toplevel) window gets closed by the user, we need to
30 // clean up because there is no way to reopen it. And check the case
31 // where the current one is closed
previewClosed(Preview * w)32 void RclMain::previewClosed(Preview *w)
33 {
34 LOGDEB("RclMain::previewClosed(" << w << ")\n");
35 if (w == curPreview) {
36 LOGDEB("Active preview closed\n");
37 curPreview = 0;
38 } else {
39 LOGDEB("Old preview closed\n");
40 }
41 }
42
43 // Document up to date check. The main problem we try to solve is
44 // displaying the wrong message from a compacted mail folder.
45 //
46 // Also we should re-run the query after updating the index because
47 // the ipaths may be wrong in the current result list. For now, the
48 // user does this by clicking search again once the indexing is done
49 //
50 // We only do this for the main index, else jump and prey (cant update
51 // anyway, even the makesig() call might not make sense for our base
52 // config)
containerUpToDate(Rcl::Doc & doc)53 bool RclMain::containerUpToDate(Rcl::Doc& doc)
54 {
55 static bool ignore_out_of_date_preview = false;
56
57 // If ipath is empty, we decide we don't care. Also, we need an index,
58 if (ignore_out_of_date_preview || doc.ipath.empty() || rcldb == 0)
59 return true;
60
61 string udi;
62 doc.getmeta(Rcl::Doc::keyudi, &udi);
63 if (udi.empty()) {
64 // Whatever...
65 return true;
66 }
67
68 string sig;
69 if (!FileInterner::makesig(theconfig, doc, sig)) {
70 QMessageBox::warning(0, "Recoll", tr("Can't access file: ") +
71 path2qs(doc.url));
72 // Let's try the preview anyway...
73 return true;
74 }
75
76 if (!rcldb->needUpdate(udi, sig)) {
77 // Alles ist in ordnung
78 return true;
79 }
80
81 // Top level (container) document, for checking for indexing error
82 string ctsig = "+";
83 Rcl::Doc ctdoc;
84 if (rcldb->getContainerDoc(doc, ctdoc)) {
85 ctdoc.getmeta(Rcl::Doc::keysig, &ctsig);
86 }
87
88 // We can only run indexing on the main index (dbidx 0)
89 bool ismainidx = rcldb->fromMainIndex(doc);
90 // Indexer already running?
91 bool ixnotact = (m_indexerState == IXST_NOTRUNNING);
92
93 QString msg = tr("Index not up to date for this file.<br>");
94 if (ctsig.back() == '+') {
95 msg += tr("<em>Also, it seems that the last index update for the file "
96 "failed.</em><br/>");
97 }
98 if (ixnotact && ismainidx) {
99 msg += tr("Click Ok to try to update the "
100 "index for this file. You will need to "
101 "run the query again when indexing is done.<br>");
102 } else if (ismainidx) {
103 msg += tr("The indexer is running so things should "
104 "improve when it's done. ");
105 } else if (ixnotact) {
106 // Not main index
107 msg += tr("The document belongs to an external index "
108 "which I can't update. ");
109 }
110 msg += tr("Click Cancel to return to the list.<br>"
111 "Click Ignore to show the preview anyway (and remember for "
112 "this session). There is a risk of showing the wrong entry.<br/>");
113
114 QMessageBox::StandardButtons bts =
115 QMessageBox::Ignore | QMessageBox::Cancel;
116
117 if (ixnotact &&ismainidx)
118 bts |= QMessageBox::Ok;
119
120 int rep =
121 QMessageBox::warning(0, tr("Warning"), msg, bts,
122 (ixnotact && ismainidx) ?
123 QMessageBox::Cancel : QMessageBox::NoButton);
124
125 if (m_indexerState == IXST_NOTRUNNING && rep == QMessageBox::Ok) {
126 LOGDEB("Requesting index update for " << doc.url << "\n");
127 vector<Rcl::Doc> docs(1, doc);
128 updateIdxForDocs(docs);
129 }
130 if (rep == QMessageBox::Ignore) {
131 ignore_out_of_date_preview = true;
132 return true;
133 } else {
134 return false;
135 }
136 }
137
138 /**
139 * Open a preview window for a given document, or load it into new tab of
140 * existing window.
141 *
142 * @param docnum db query index
143 * @param mod keyboards modifiers like ControlButton, ShiftButton
144 */
startPreview(int docnum,Rcl::Doc doc,int mod)145 void RclMain::startPreview(int docnum, Rcl::Doc doc, int mod)
146 {
147 LOGDEB("startPreview(" << docnum << ", doc, " << mod << ")\n");
148
149 if (!containerUpToDate(doc))
150 return;
151
152 // Do the zeitgeist thing
153 zg_send_event(ZGSEND_PREVIEW, doc);
154
155 if (mod & Qt::ShiftModifier) {
156 // User wants new preview window
157 curPreview = 0;
158 }
159 if (curPreview == 0) {
160 HighlightData hdata;
161 m_source->getTerms(hdata);
162 curPreview = new Preview(this, reslist->listId(), hdata);
163
164 if (curPreview == 0) {
165 QMessageBox::warning(0, tr("Warning"),
166 tr("Can't create preview window"),
167 QMessageBox::Ok,
168 QMessageBox::NoButton);
169 return;
170 }
171 connect(new QShortcut(quitKeySeq, curPreview), SIGNAL (activated()),
172 this, SLOT (fileExit()));
173 connect(curPreview, SIGNAL(previewClosed(Preview *)),
174 this, SLOT(previewClosed(Preview *)));
175 connect(curPreview, SIGNAL(wordSelect(QString)),
176 sSearch, SLOT(addTerm(QString)));
177 connect(curPreview, SIGNAL(showNext(Preview *, int, int)),
178 this, SLOT(previewNextInTab(Preview *, int, int)));
179 connect(curPreview, SIGNAL(showPrev(Preview *, int, int)),
180 this, SLOT(previewPrevInTab(Preview *, int, int)));
181 connect(curPreview, SIGNAL(previewExposed(Preview *, int, int)),
182 this, SLOT(previewExposed(Preview *, int, int)));
183 connect(curPreview, SIGNAL(saveDocToFile(Rcl::Doc)),
184 this, SLOT(saveDocToFile(Rcl::Doc)));
185 connect(curPreview, SIGNAL(editRequested(Rcl::Doc)),
186 this, SLOT(startNativeViewer(Rcl::Doc)));
187 curPreview->setWindowTitle(getQueryDescription());
188 curPreview->show();
189 }
190 curPreview->makeDocCurrent(doc, docnum);
191 }
192
193 /**
194 * Open a preview window for a given document, no linking to result list
195 *
196 * This is used to show ie parent documents, which have no corresponding
197 * entry in the result list.
198 *
199 */
startPreview(Rcl::Doc doc)200 void RclMain::startPreview(Rcl::Doc doc)
201 {
202 Preview *preview = new Preview(this, 0, HighlightData());
203 if (preview == 0) {
204 QMessageBox::warning(0, tr("Warning"),
205 tr("Can't create preview window"),
206 QMessageBox::Ok,
207 QMessageBox::NoButton);
208 return;
209 }
210 connect(new QShortcut(quitKeySeq, preview), SIGNAL (activated()),
211 this, SLOT (fileExit()));
212 connect(preview, SIGNAL(wordSelect(QString)),
213 sSearch, SLOT(addTerm(QString)));
214 // Do the zeitgeist thing
215 zg_send_event(ZGSEND_PREVIEW, doc);
216 preview->show();
217 preview->makeDocCurrent(doc, 0);
218 }
219
220 // Show next document from result list in current preview tab
previewNextInTab(Preview * w,int sid,int docnum)221 void RclMain::previewNextInTab(Preview * w, int sid, int docnum)
222 {
223 previewPrevOrNextInTab(w, sid, docnum, true);
224 }
225
226 // Show previous document from result list in current preview tab
previewPrevInTab(Preview * w,int sid,int docnum)227 void RclMain::previewPrevInTab(Preview * w, int sid, int docnum)
228 {
229 previewPrevOrNextInTab(w, sid, docnum, false);
230 }
231
232 // Combined next/prev from result list in current preview tab
previewPrevOrNextInTab(Preview * w,int sid,int docnum,bool nxt)233 void RclMain::previewPrevOrNextInTab(Preview * w, int sid, int docnum, bool nxt)
234 {
235 LOGDEB("RclMain::previewNextInTab sid " << sid << " docnum " << docnum <<
236 ", listId " << reslist->listId() << "\n");
237
238 if (w == 0) // ??
239 return;
240
241 if (sid != reslist->listId()) {
242 QMessageBox::warning(0, "Recoll",
243 tr("This search is not active any more"));
244 return;
245 }
246
247 if (nxt)
248 docnum++;
249 else
250 docnum--;
251 if (docnum < 0 || !m_source || docnum >= m_source->getResCnt()) {
252 if (!prefs.noBeeps) {
253 LOGDEB("Beeping\n");
254 QApplication::beep();
255 } else {
256 LOGDEB("Not beeping because nobeep is set\n");
257 }
258 return;
259 }
260
261 Rcl::Doc doc;
262 if (!reslist->getDoc(docnum, doc)) {
263 QMessageBox::warning(0, "Recoll",
264 tr("Cannot retrieve document info from database"));
265 return;
266 }
267
268 w->makeDocCurrent(doc, docnum, true);
269 }
270
271 // Preview tab exposed: if the preview comes from the currently
272 // displayed result list, tell reslist (to color the paragraph)
previewExposed(Preview *,int sid,int docnum)273 void RclMain::previewExposed(Preview *, int sid, int docnum)
274 {
275 LOGDEB2("RclMain::previewExposed: sid " << sid << " docnum " << docnum <<
276 ", m_sid " << reslist->listId() << "\n");
277 if (sid != reslist->listId()) {
278 return;
279 }
280 reslist->previewExposed(docnum);
281 }
282