]> git.lyx.org Git - lyx.git/blob - src/frontends/qt/GuiInclude.cpp
Inform user if panel or tab has invalid content (#10827)
[lyx.git] / src / frontends / qt / GuiInclude.cpp
1 /**
2  * \file GuiInclude.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alejandro Aguilar Sierra
7  * \author John Levon
8  * \author Angus Leeming
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "GuiInclude.h"
16
17 #include "Buffer.h"
18 #include "BufferList.h"
19 #include "BufferParams.h"
20 #include "FuncRequest.h"
21 #include "LyXRC.h"
22
23 #include "qt_helpers.h"
24
25 #include "support/gettext.h"
26 #include "support/lstrings.h"
27 #include "support/os.h"
28 #include "support/FileName.h"
29 #include "support/filetools.h"
30
31 #include "frontends/alert.h"
32
33 #include "insets/InsetListingsParams.h"
34 #include "insets/InsetInclude.h"
35
36 #include <QCheckBox>
37 #include <QFile>
38 #include <QLineEdit>
39 #include <QPushButton>
40
41 #include <utility>
42
43 using namespace std;
44 using namespace lyx::support;
45 using namespace lyx::support::os;
46
47 namespace lyx {
48 namespace frontend {
49
50
51 GuiInclude::GuiInclude(GuiView & lv)
52         : GuiDialog(lv, "include", qt_("Child Document")),
53           params_(insetCode("include"))
54 {
55         setupUi(this);
56
57         connect(buttonBox, SIGNAL(clicked(QAbstractButton *)),
58                 this, SLOT(slotButtonBox(QAbstractButton *)));
59
60         connect(visiblespaceCB, SIGNAL(clicked()), this, SLOT(change_adaptor()));
61         connect(filenameED, SIGNAL(textChanged(const QString &)),
62                 this, SLOT(change_adaptor()));
63         connect(editPB, SIGNAL(clicked()), this, SLOT(edit()));
64         connect(browsePB, SIGNAL(clicked()), this, SLOT(browse()));
65         connect(typeCO, SIGNAL(activated(int)), this, SLOT(change_adaptor()));
66         connect(typeCO, SIGNAL(activated(int)), this, SLOT(typeChanged(int)));
67         connect(previewCB, SIGNAL(clicked()), this, SLOT(change_adaptor()));
68         connect(captionLE, SIGNAL(textChanged(const QString&)), this, SLOT(change_adaptor()));
69         connect(labelLE, SIGNAL(textChanged(const QString&)), this, SLOT(change_adaptor()));
70         connect(literalCB, SIGNAL(clicked()), this, SLOT(change_adaptor()));
71         connect(listingsED, SIGNAL(textChanged()), this, SLOT(change_adaptor()));
72         connect(listingsED, SIGNAL(textChanged()), this, SLOT(setListingsMsg()));
73         connect(bypassCB, SIGNAL(clicked()), this, SLOT(change_adaptor()));
74         connect(bypassCB, SIGNAL(clicked()), this, SLOT(setListingsMsg()));
75
76         setFocusProxy(filenameED);
77
78         bc().setPolicy(ButtonPolicy::OkApplyCancelReadOnlyPolicy);
79         bc().setOK(buttonBox->button(QDialogButtonBox::Ok));
80         bc().setCancel(buttonBox->button(QDialogButtonBox::Cancel));
81         bc().addReadOnly(filenameED);
82         bc().addReadOnly(browsePB);
83         bc().addReadOnly(visiblespaceCB);
84         bc().addReadOnly(typeCO);
85         bc().addReadOnly(listingsED);
86
87         // FIXME does not make sense, as we do not have a validator
88         // for this widget
89         //bc().addCheckedLineEdit(filenameED, filenameLA);
90 }
91
92
93 void GuiInclude::change_adaptor()
94 {
95         changed();
96 }
97
98
99 docstring GuiInclude::validate_listings_params()
100 {
101         if (typeCO->currentIndex() != 3 || bypassCB->isChecked())
102                 return docstring();
103         string params = fromqstr(listingsED->toPlainText());
104         InsetListingsParams lstparams(params);
105         lstparams.setMinted(buffer().params().use_minted);
106         return lstparams.validate();
107 }
108
109
110 void GuiInclude::setListingsMsg()
111 {
112         // FIXME THREAD
113         static bool isOK = true;
114         docstring msg = validate_listings_params();
115         if (msg.empty()) {
116                 if (isOK)
117                         return;
118                 isOK = true;
119                 listingsTB->setPlainText(
120                         qt_("Input listing parameters on the right. Enter ? for a list of parameters."));
121         } else {
122                 isOK = false;
123                 listingsTB->setPlainText(toqstr(msg));
124         }
125 }
126
127
128 void GuiInclude::typeChanged(int v)
129 {
130         switch (v) {
131                 //case Include
132                 case 0:
133                         visiblespaceCB->setEnabled(false);
134                         visiblespaceCB->setChecked(false);
135                         previewCB->setEnabled(false);
136                         previewCB->setChecked(false);
137                         listingsGB->setEnabled(false);
138                         break;
139                 //case Input
140                 case 1:
141                         visiblespaceCB->setEnabled(false);
142                         visiblespaceCB->setChecked(false);
143                         previewCB->setEnabled(true);
144                         listingsGB->setEnabled(false);
145                         break;
146                 //case listings
147                 case 3:
148                         visiblespaceCB->setEnabled(false);
149                         visiblespaceCB->setChecked(false);
150                         previewCB->setEnabled(false);
151                         previewCB->setChecked(false);
152                         listingsGB->setEnabled(true);
153                         break;
154                 //case Verbatim
155                 default:
156                         visiblespaceCB->setEnabled(true);
157                         previewCB->setEnabled(false);
158                         previewCB->setChecked(false);
159                         listingsGB->setEnabled(false);
160                         break;
161         }
162 }
163
164
165 void GuiInclude::paramsToDialog(InsetCommandParams const & icp)
166 {
167         filenameED->setText(toqstr(icp["filename"]));
168
169         visiblespaceCB->setChecked(false);
170         visiblespaceCB->setEnabled(false);
171         previewCB->setChecked(false);
172         previewCB->setEnabled(false);
173         listingsGB->setEnabled(false);
174         captionLE->clear();
175         labelLE->clear();
176         listingsED->clear();
177         listingsTB->setPlainText(
178                 qt_("Input listing parameters on the right. Enter ? for a list of parameters."));
179
180         string cmdname = icp.getCmdName();
181         if (cmdname != "include" &&
182             cmdname != "verbatiminput" &&
183             cmdname != "verbatiminput*" &&
184             cmdname != "lstinputlisting" &&
185             cmdname != "inputminted")
186                 cmdname = "input";
187
188         if (cmdname == "include") {
189                 typeCO->setCurrentIndex(0);
190
191         } else if (cmdname == "input") {
192                 typeCO->setCurrentIndex(1);
193                 previewCB->setEnabled(true);
194                 previewCB->setChecked(icp.preview());
195
196         } else if (cmdname == "verbatiminput*") {
197                 typeCO->setCurrentIndex(2);
198                 visiblespaceCB->setEnabled(true);
199                 visiblespaceCB->setChecked(true);
200
201         } else if (cmdname == "verbatiminput") {
202                 typeCO->setCurrentIndex(2);
203                 visiblespaceCB->setEnabled(true);
204
205         } else if (cmdname == "lstinputlisting" || cmdname == "inputminted") {
206                 typeCO->setCurrentIndex(3);
207                 listingsGB->setEnabled(true);
208                 listingsED->setEnabled(true);
209                 InsetListingsParams par(to_utf8(icp["lstparams"]));
210                 // extract caption and label and put them into their respective editboxes
211                 vector<string> pars = getVectorFromString(par.separatedParams(), "\n");
212                 for (vector<string>::iterator it = pars.begin();
213                         it != pars.end(); ++it) {
214                         if (prefixIs(*it, "caption=")) {
215                                 string cap = it->substr(8);
216                                 if (cap[0] == '{' && cap[cap.size() - 1] == '}') {
217                                         captionLE->setText(toqstr(cap.substr(1, cap.size() - 2)));
218                                         *it = "";
219                                 }
220                         } else if (prefixIs(*it, "label=")) {
221                                 string lbl = it->substr(6);
222                                 if (lbl[0] == '{' && lbl[lbl.size()-1] == '}') {
223                                         labelLE->setText(toqstr(lbl.substr(1, lbl.size() - 2)));
224                                         *it = "";
225                                 }
226                         }
227                 }
228                 // the rest is put to the extra edit box.
229                 string extra = getStringFromVector(pars);
230                 listingsED->setPlainText(toqstr(InsetListingsParams(extra).separatedParams()));
231         }
232         literalCB->setChecked(icp["literal"] == "true");
233
234         // Make sure that the bc is in the INITIAL state
235         if (bc().policy().buttonStatus(ButtonPolicy::OKAY))
236                 bc().restore();
237 }
238
239
240 void GuiInclude::applyView()
241 {
242         params_["filename"] = from_utf8(internal_path(fromqstr(filenameED->text())));
243         params_.preview(previewCB->isChecked());
244
245         int const item = typeCO->currentIndex();
246         if (item == 0) {
247                 params_.setCmdName("include");
248         } else if (item == 1) {
249                 params_.setCmdName("input");
250         } else if (item == 3) {
251                 if (buffer().params().use_minted)
252                         params_.setCmdName("inputminted");
253                 else
254                         params_.setCmdName("lstinputlisting");
255                 // the parameter string should have passed validation
256                 InsetListingsParams par(fromqstr(listingsED->toPlainText()));
257                 string caption = fromqstr(captionLE->text());
258                 string label = fromqstr(labelLE->text());
259                 if (!caption.empty())
260                         par.addParam("caption", "{" + caption + "}");
261                 if (!label.empty())
262                         par.addParam("label", "{" + label + "}");
263                 string const listparams = par.params();
264                 params_["lstparams"] = from_utf8(listparams);
265         } else {
266                 if (visiblespaceCB->isChecked())
267                         params_.setCmdName("verbatiminput*");
268                 else
269                         params_.setCmdName("verbatiminput");
270         }
271         params_["literal"] = literalCB->isChecked()
272                         ? from_ascii("true") : from_ascii("false");
273
274         // Do we need to create a LyX file?
275         if (item == 0 || item == 1) {
276                 QString fname = filenameED->text();
277                 string const mypath = buffer().absFileName();
278                 string const bpath = buffer().filePath();
279                 QString absfname = makeAbsPath(fname, toqstr(bpath));
280                 if (!QFile::exists(absfname)) {
281                         dispatch(FuncRequest(LFUN_BUFFER_NEW, fromqstr(absfname)));
282                         dispatch(FuncRequest(LFUN_BUFFER_WRITE));
283                         dispatch(FuncRequest(LFUN_BUFFER_SWITCH, mypath));
284                 }
285         }
286 }
287
288
289 void GuiInclude::edit()
290 {
291         if (!isValid())
292                 return;
293         if (bc().policy().buttonStatus(ButtonPolicy::OKAY)) {
294                 slotOK();
295                 applyView();
296         } else
297                 hideView();
298         QString const fname = filenameED->text();
299         string const bpath = buffer().filePath();
300         string const absfname = support::makeAbsPath(fromqstr(fname), bpath).absFileName();
301         // The button is enabled only if the document is already open.
302         // If something goes wrong and it is not, we'll get an error
303         // message from the dispatch. So no need for one here.
304         dispatch(FuncRequest(LFUN_BUFFER_SWITCH, absfname));
305 }
306
307
308 bool GuiInclude::isValid()
309 {
310         QString fname = filenameED->text();
311         if (fname.isEmpty() || !validate_listings_params().empty()) {
312                 editPB->setEnabled(false);
313                 return false;
314         }
315
316         QPushButton * okbutton = buttonBox->button(QDialogButtonBox::Ok);
317         int const item = typeCO->currentIndex();
318         // Are we inputting or including a LyX file?
319         if (item != 0 && item != 1) {
320                 okbutton->setText(qt_("&OK"));
321                 return true;
322         }
323
324         string const bpath = buffer().filePath();
325         // Path might be relative to current Buffer, so make absolute
326         FileName const absfname = support::makeAbsPath(fromqstr(fname), bpath);
327
328         // Do we have a LyX filename?
329         if (!isLyXFileName(fromqstr(fname))) {
330                 okbutton->setText(qt_("&OK"));
331                 return absfname.exists();
332         }
333
334         // Set OK button text according to whether file already exists
335         okbutton->setText(absfname.exists() ? qt_("&OK") : qt_("&Create"));
336         // enable edit button iff file is open in some Buffer
337         editPB->setEnabled(theBufferList().getBuffer(absfname));
338         return true;
339 }
340
341
342 void GuiInclude::browse()
343 {
344         Type type;
345
346         int const item = typeCO->currentIndex();
347         if (item == 0)
348                 type = INCLUDE;
349         else if (item == 1)
350                 type = INPUT;
351         else if (item == 2)
352                 type = VERBATIM;
353         else
354                 type = LISTINGS;
355
356         QString name = browse(filenameED->text(), type);
357         if (!name.isEmpty())
358                 filenameED->setText(name);
359 }
360
361
362 QString GuiInclude::browse(QString const & in_name, Type in_type) const
363 {
364         QString const title = qt_("Select document to include");
365
366         // input TeX, verbatim, or LyX file ?
367         QStringList filters;
368         switch (in_type) {
369         case INCLUDE:
370         case INPUT:
371                 filters = fileFilters(qt_("LaTeX/LyX Documents (*.tex *.lyx)"));
372                 break;
373         case VERBATIM:
374         case LISTINGS:
375                 filters = fileFilters(QString());
376                 break;
377         }
378
379         QString const docpath = toqstr(support::onlyPath(buffer().absFileName()));
380
381         return browseRelToParent(in_name, docpath, title, filters, false,
382                 qt_("D&ocuments"), toqstr(lyxrc.document_path));
383 }
384
385
386 bool GuiInclude::initialiseParams(std::string const & sdata)
387 {
388         InsetCommand::string2params(sdata, params_);
389         paramsToDialog(params_);
390         return true;
391 }
392
393
394 void GuiInclude::dispatchParams()
395 {
396         std::string const lfun = InsetCommand::params2string(params_);
397         dispatch(FuncRequest(getLfun(), lfun));
398 }
399
400
401 } // namespace frontend
402 } // namespace lyx
403
404 #include "moc_GuiInclude.cpp"