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