2 * \file GuiListings.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
7 * \author Jürgen Spitzmüller
9 * Full author contact details are available in file CREDITS.
14 #include "GuiListings.h"
16 #include "qt_helpers.h"
19 #include "BufferParams.h"
20 #include "FuncRequest.h"
22 #include "insets/InsetListings.h"
23 #include "insets/InsetListingsParams.h"
25 #include "support/convert.h"
26 #include "support/debug.h"
27 #include "support/gettext.h"
28 #include "support/lstrings.h"
31 #include <QPushButton>
33 #if QT_VERSION < 0x060000
34 #include <QRegExpValidator>
36 #include <QRegularExpressionValidator>
40 using namespace lyx::support;
46 /////////////////////////////////////////////////////////////////////
50 /////////////////////////////////////////////////////////////////////
53 char const * languages_supported[] =
54 { "no language", "ABAP", "ACM", "ACMscript", "ACSL", "Ada", "ALGOL", "Ant", "Assembler", "Awk", "bash", "Basic", "C",
55 "C++", "Caml", "CIL", "Clean", "Cobol", "Comal 80", "command.com", "Comsol", "csh", "Delphi",
56 "Eiffel", "Elan", "elisp", "erlang", "Euphoria", "Fortran", "GAP", "GCL", "Gnuplot", "Go",
57 "hansl", "Haskell", "HTML", "IDL", "inform",
58 "Java", "JVMIS", "ksh", "Lingo", "Lisp", "LLVM", "Logo", "Lua", "make", "Mathematica", "Matlab", "Mercury",
59 "MetaPost", "Miranda", "Mizar", "ML", "Modula-2", "MuPAD", "NASTRAN", "Oberon-2", "OCL", "Octave", "OORexx",
60 "Oz", "Pascal", "Perl", "PHP", "PL/I", "Plasm", "PostScript", "POV", "Prolog", "Promela",
61 "PSTricks", "Python", "R", "Reduce", "Rexx", "RSL", "Ruby", "S", "SAS", "Scala", "Scilab", "sh",
62 "SHELXL", "Simula", "SPARQL", "SQL", "Swift", "tcl", "TeX", "VBScript", "Verilog", "VHDL",
63 "VRML", "XML", "XSLT", "" };
66 char const * languages_gui[] =
67 { N_("No language"), "ABAP", "ACM", "ACMscript", "ACSL", "Ada", "ALGOL", "Ant", "Assembler", "Awk", "bash", "Basic", "C",
68 "C++", "Caml", "CIL", "Clean", "Cobol", "Comal 80", "command.com", "Comsol", "csh", "Delphi",
69 "Eiffel", "Elan", "elisp", "erlang", "Euphoria", "Fortran", "GAP", "GCL", "Gnuplot", "Go",
70 "hansl", "Haskell", "HTML", "IDL", "inform",
71 "Java", "JVMIS", "ksh", "Lingo", "Lisp", "LLVM", "Logo", "Lua", "make", "Mathematica", "Matlab", "Mercury",
72 "MetaPost", "Miranda", "Mizar", "ML", "Modula-2", "MuPAD", "NASTRAN", "Oberon-2", "OCL", "Octave", "OORexx",
73 "Oz", "Pascal", "Perl", "PHP", "PL/I", "Plasm", "PostScript", "POV", "Prolog", "Promela",
74 "PSTricks", "Python", "R", "Reduce", "Rexx", "RSL", "Ruby", "S", "SAS", "Scala", "Scilab", "sh",
75 "SHELXL", "Simula", "SPARQL", "SQL", "Swift", "tcl", "TeX", "VBScript", "Verilog", "VHDL",
76 "VRML", "XML", "XSLT", "" };
82 /// the associated language
83 char const * language;
84 /// representation of the dialect in the gui
86 /// is this the default dialect?
91 dialect_info const dialects[] = {
92 { "R/2 4.3", "ABAP", "R/2 4.3", false },
93 { "R/2 5.0", "ABAP", "R/2 5.0", false },
94 { "R/3 3.1", "ABAP", "R/3 3.1", false },
95 { "R/3 4.6C", "ABAP", "R/3 4.6C", false },
96 { "R/3 6.10", "ABAP", "R/3 6.10", true },
97 { "2005", "Ada", "2005", true },
98 { "83", "Ada", "83", false },
99 { "95", "Ada", "95", false },
100 { "60", "Algol", "60", false },
101 { "68", "Algol", "68", true },
102 { "Motorola68k", "Assembler", "Motorola 68xxx", false },
103 { "x86masm", "Assembler", "x86 (MASM)", false },
104 { "gnu", "Awk", "gnu", true },
105 { "POSIX", "Awk", "POSIX", false },
106 { "Visual", "Basic", "Visual", false },
107 { "ANSI", "C", "ANSI", true },
108 { "Handel", "C", "Handel", false },
109 { "Objective", "C", "Objective", false },
110 { "Sharp", "C", "Sharp", false },
111 { "ANSI", "C++", "ANSI", false },
112 { "GNU", "C++", "GNU", false },
113 { "ISO", "C++", "ISO", true },
114 { "Visual", "C++", "Visual", false },
115 { "light", "Caml", "light", true },
116 { "Objective", "Caml", "Objective", false },
117 { "1974", "Cobol", "1974", false },
118 { "1985", "Cobol", "1985", true },
119 { "ibm", "Cobol", "IBM", false },
120 { "WinXP", "command.com", "Windows XP", true },
121 { "77", "Fortran", "77", false },
122 { "90", "Fortran", "90", false },
123 { "95", "Fortran", "95", true },
124 { "CORBA", "IDL", "CORBA", false },
125 { "AspectJ", "Java", "Aspect J", false },
126 { "Auto", "Lisp", "Auto", false },
127 { "gnu", "make", "gnu", false },
128 { "1.0", "Mathematica", "1.0", false },
129 { "3.0", "Mathematica", "3.0", false },
130 { "5.2", "Mathematica", "5.2", true },
131 { "decorative", "OCL", "decorative", false },
132 { "OMG", "OCL", "OMG", true },
133 { "Borland6", "Pascal", "Borland 6", false },
134 { "Standard", "Pascal", "Standard", true },
135 { "XSC", "Pascal", "XSC", false },
136 { "PLUS", "S", "PLUS", false },
137 { "67", "Simula", "67", true },
138 { "CII", "Simula", "CII", false },
139 { "DEC", "Simula", "DEC", false },
140 { "IBM", "Simula", "IBM", false },
141 { "tk", "tcl", "tk", false },
142 { "AlLaTeX", "TeX", "AlLaTeX", false },
143 { "common", "TeX", "common", false },
144 { "LaTeX", "TeX", "LaTeX", false },
145 { "plain", "TeX", "plain", true },
146 { "primitive", "TeX", "primitive", false },
147 { "AMS", "VHDL", "AMS", false },
148 { "97", "VRML", "97", true }
152 size_t const nr_dialects = sizeof(dialects) / sizeof(dialect_info);
155 char const * font_sizes[] =
156 { "default", "tiny", "scriptsize", "footnotesize", "small", "normalsize", "large",
159 char const * font_sizes_gui[] =
160 { N_("Default"), N_("Tiny"), N_("Smallest"), N_("Smaller"), N_("Small"), N_("Normal"),
161 N_("Large"), N_("Larger"), "" };
163 char const * font_styles[] =
164 { "default", "rmfamily", "ttfamily", "sffamily", "" };
166 char const * font_styles_gui[] =
167 { N_("Default"), N_("Roman"), N_("Typewriter"), N_("Sans Serif"), "" };
171 GuiListings::GuiListings(GuiView & lv)
172 : GuiDialog(lv, "listings", qt_("Program Listing Settings"))
176 connect(buttonBox, SIGNAL(clicked(QAbstractButton *)),
177 this, SLOT(slotButtonBox(QAbstractButton *)));
179 connect(languageCO, SIGNAL(currentIndexChanged(int)),
180 this, SLOT(change_adaptor()));
181 connect(dialectCO, SIGNAL(currentIndexChanged(int)),
182 this, SLOT(change_adaptor()));
183 connect(inlineCB, SIGNAL(clicked()),
184 this, SLOT(change_adaptor()));
185 connect(floatCB, SIGNAL(clicked()),
186 this, SLOT(change_adaptor()));
187 connect(placementLE, SIGNAL(textChanged(QString)),
188 this, SLOT(change_adaptor()));
189 connect(numberSideCO, SIGNAL(currentIndexChanged(int)),
190 this, SLOT(change_adaptor()));
191 connect(numberStepLE, SIGNAL(textChanged(QString)),
192 this, SLOT(change_adaptor()));
193 connect(numberFontSizeCO, SIGNAL(currentIndexChanged(int)),
194 this, SLOT(change_adaptor()));
195 connect(firstlineLE, SIGNAL(textChanged(QString)),
196 this, SLOT(change_adaptor()));
197 connect(lastlineLE, SIGNAL(textChanged(QString)),
198 this, SLOT(change_adaptor()));
199 connect(fontsizeCO, SIGNAL(currentIndexChanged(int)),
200 this, SLOT(change_adaptor()));
201 connect(fontstyleCO, SIGNAL(currentIndexChanged(int)),
202 this, SLOT(change_adaptor()));
203 connect(breaklinesCB, SIGNAL(clicked()),
204 this, SLOT(change_adaptor()));
205 connect(spaceCB, SIGNAL(clicked()),
206 this, SLOT(change_adaptor()));
207 connect(spaceInStringCB, SIGNAL(clicked()),
208 this, SLOT(change_adaptor()));
209 connect(tabsizeSB, SIGNAL(valueChanged(int)),
210 this, SLOT(change_adaptor()));
211 connect(extendedcharsCB, SIGNAL(clicked()),
212 this, SLOT(change_adaptor()));
214 connect(listingsED, SIGNAL(textChanged()),
215 this, SLOT(change_adaptor()));
216 connect(listingsED, SIGNAL(textChanged()),
217 this, SLOT(setListingsMsg()));
218 connect(bypassCB, SIGNAL(clicked()),
219 this, SLOT(change_adaptor()));
220 connect(bypassCB, SIGNAL(clicked()),
221 this, SLOT(setListingsMsg()));
223 for (int n = 0; languages_supported[n][0]; ++n)
224 languageCO->addItem(qt_(languages_gui[n]));
226 for (int n = 0; font_styles[n][0]; ++n)
227 fontstyleCO->addItem(qt_(font_styles_gui[n]));
229 for (int n = 0; font_sizes[n][0]; ++n) {
230 QString font = qt_(font_sizes_gui[n]);
231 fontsizeCO->addItem(font);
232 numberFontSizeCO->addItem(font);
236 numberStepLE->setValidator(new QIntValidator(0, 1000000, this));
237 firstlineLE->setValidator(new QIntValidator(0, 1000000, this));
238 lastlineLE->setValidator(new QIntValidator(0, 1000000, this));
239 #if QT_VERSION < 0x060000
240 placementLE->setValidator(new QRegExpValidator(QRegExp("[\\*tbph]*"), this));
242 placementLE->setValidator(new QRegularExpressionValidator(QRegularExpression("[\\*tbph]*"), this));
245 bc().setPolicy(ButtonPolicy::NoRepeatedApplyReadOnlyPolicy);
246 bc().setOK(buttonBox->button(QDialogButtonBox::Ok));
247 bc().setApply(buttonBox->button(QDialogButtonBox::Apply));
248 bc().setCancel(buttonBox->button(QDialogButtonBox::Cancel));
249 listingsTB->setPlainText(
250 qt_("Input listing parameters on the right. Enter ? for a list of parameters."));
257 void GuiListings::change_adaptor()
263 string GuiListings::construct_params()
265 string language = languages_supported[qMax(0, languageCO->currentIndex())];
267 string const dialect_gui = fromqstr(dialectCO->currentText());
268 if (dialectCO->currentIndex() > 0) {
269 for (size_t i = 0; i != nr_dialects; ++i) {
270 if (dialect_gui == dialects[i].gui
271 && dialects[i].language == language
272 && !dialects[i].is_default) {
273 dialect = dialects[i].dialect;
279 bool float_ = floatCB->isChecked();
281 if (placementLE->isEnabled())
282 placement = fromqstr(placementLE->text());
285 switch (qMax(0, numberSideCO->currentIndex())) {
293 numberSide = "right";
299 string stepnumber = fromqstr(numberStepLE->text());
300 string numberfontsize = font_sizes[qMax(0, numberFontSizeCO->currentIndex())];
301 string firstline = fromqstr(firstlineLE->text());
302 string lastline = fromqstr(lastlineLE->text());
304 string fontsize = font_sizes[qMax(0, fontsizeCO->currentIndex())];
305 string fontstyle = font_styles[qMax(0, fontstyleCO->currentIndex())];
308 // FIXME: We should not compose listings- or minted-dependant string here
309 // This breaks if a users switches the backend without opening and
310 // re-applying all listings insets. Use a backend-abstract syntax!
311 bool const use_minted = buffer().params().use_minted;
312 if (fontsize != "default") {
314 mintedsize = "\\" + fontsize;
316 basicstyle = "\\" + fontsize;
318 if (fontstyle != "default") {
320 basicstyle = fontstyle.substr(0, 2);
322 basicstyle += "\\" + fontstyle;
324 bool breakline = breaklinesCB->isChecked();
325 bool space = spaceCB->isChecked();
326 int tabsize = tabsizeSB->value();
327 bool spaceInString = spaceInStringCB->isChecked();
328 bool extendedchars = extendedcharsCB->isChecked();
329 string extra = fromqstr(listingsED->toPlainText());
332 InsetListingsParams par;
333 par.setMinted(use_minted);
335 if (language == "no language" && !contains(extra, "language=")) {
336 string const & blp = buffer().params().listings_params;
337 size_t start = blp.find("language=");
338 if (start != string::npos) {
339 start += strlen("language=");
340 size_t len = blp.find(",", start);
341 if (len != string::npos)
343 par.addParam("language", blp.substr(start, len));
345 par.addParam("language", "TeX");
347 par.addParam("language", language);
348 } else if (language != "no language" && !contains(extra, "language=")) {
350 par.addParam("language", language);
352 par.addParam("language", "{[" + dialect + "]" + language + "}");
354 // this dialog uses float=placement instead of float,floatplacement=placement
355 // because float accepts *tbph and floatplacement accepts bph.
356 // our placement textedit is actually for the float parameter
358 par.addParam("float", placement);
359 if (numberSide != "none")
360 par.addParam("numbers", numberSide);
361 if (numberfontsize != "default" && numberSide != "none" && !use_minted)
362 par.addParam("numberstyle", "\\" + numberfontsize);
363 if (!stepnumber.empty() && numberSide != "none")
364 par.addParam("stepnumber", stepnumber);
365 if (!firstline.empty())
366 par.addParam("firstline", firstline);
367 if (!lastline.empty())
368 par.addParam("lastline", lastline);
369 if (!basicstyle.empty())
370 par.addParam(use_minted ? "fontfamily" : "basicstyle", basicstyle);
371 if (!mintedsize.empty())
372 par.addParam("fontsize", mintedsize);
374 par.addParam("breaklines", "true");
376 par.addParam("showspaces", "true");
377 if (!spaceInString && !use_minted)
378 par.addParam("showstringspaces", "false");
380 par.addParam("tabsize", convert<string>(tabsize));
381 if (extendedchars && !use_minted)
382 par.addParam("extendedchars", "true");
383 par.addParams(extra);
388 docstring GuiListings::validate_listings_params()
390 if (bypassCB->isChecked())
392 return InsetListingsParams(construct_params()).validate();
396 void GuiListings::setListingsMsg()
399 static bool isOK = true;
400 docstring msg = validate_listings_params();
405 listingsTB->setPlainText(
406 qt_("Input listing parameters on the right. Enter ? for a list of parameters."));
409 listingsTB->setPlainText(toqstr(msg));
414 void GuiListings::on_floatCB_stateChanged(int state)
416 if (state == Qt::Checked) {
417 inlineCB->setChecked(false);
418 placementLE->setEnabled(true);
420 placementLE->setEnabled(false);
424 void GuiListings::on_inlineCB_stateChanged(int state)
426 if (state == Qt::Checked) {
427 floatCB->setChecked(false);
428 placementLE->setEnabled(false);
433 void GuiListings::on_numberSideCO_currentIndexChanged(int index)
435 numberStepLE->setEnabled(index > 0);
436 numberFontSizeCO->setEnabled(index > 0);
440 void GuiListings::on_languageCO_currentIndexChanged(int index)
444 int default_dialect = 0;
445 dialectCO->addItem(qt_("No dialect"));
446 string const language = languages_supported[index];
448 for (size_t i = 0; i != nr_dialects; ++i) {
449 if (language == dialects[i].language) {
450 dialectCO->addItem(qt_(dialects[i].gui));
451 if (dialects[i].is_default)
453 dialectCO->findText(qt_(dialects[i].gui));
456 dialectCO->setCurrentIndex(default_dialect);
457 dialectCO->setEnabled(dialectCO->count() > 1
458 && !buffer().params().use_minted);
462 void GuiListings::applyView()
464 params_.setMinted(buffer().params().use_minted);
465 params_.setInline(inlineCB->isChecked());
466 params_.setParams(construct_params());
470 static string plainParam(string const & par)
472 // remove enclosing braces
473 if (prefixIs(par, "{") && suffixIs(par, "}"))
474 return par.substr(1, par.size() - 2);
479 void GuiListings::updateContents()
481 bool const use_minted = buffer().params().use_minted;
482 // set default values
483 listingsTB->setPlainText(
484 qt_("Input listing parameters on the right. Enter ? for a list of parameters."));
485 languageCO->setCurrentIndex(findToken(languages_supported, "no language"));
486 dialectCO->setCurrentIndex(0);
487 floatCB->setChecked(false);
488 placementLE->clear();
489 numberSideCO->setCurrentIndex(0);
490 numberStepLE->clear();
491 numberFontSizeCO->setCurrentIndex(findToken(font_sizes, "default"));
492 firstlineLE->clear();
494 fontstyleCO->setCurrentIndex(findToken(font_styles, "default"));
495 fontsizeCO->setCurrentIndex(findToken(font_sizes, "default"));
496 breaklinesCB->setChecked(false);
497 spaceCB->setChecked(false);
498 spaceInStringCB->setChecked(true);
499 tabsizeSB->setValue(8);
500 extendedcharsCB->setChecked(false);
502 // set values from param string
503 inlineCB->setChecked(params_.isInline());
504 if (params_.isInline()) {
505 floatCB->setChecked(false);
506 placementLE->setEnabled(false);
508 // break other parameters and set values
509 vector<string> pars = getVectorFromString(params_.separatedParams(), "\n");
510 // process each of them
511 for (vector<string>::iterator it = pars.begin();
512 it != pars.end(); ++it) {
513 if (prefixIs(*it, "language=")) {
514 string arg = plainParam(it->substr(9));
519 if (prefixIs(arg, "[") && contains(arg, "]")) {
520 size_t end_dialect = arg.find("]");
521 dialect = arg.substr(1, end_dialect - 1);
522 language = arg.substr(end_dialect + 1);
526 int n = findToken(languages_supported, language);
528 languageCO->setCurrentIndex(n);
531 // on_languageCO_currentIndexChanged should have set dialects
532 if (!dialect.empty()) {
534 for (size_t i = 0; i != nr_dialects; ++i) {
535 if (dialect == dialects[i].dialect
536 && dialects[i].language == language) {
537 dialect_gui = dialects[i].gui;
541 n = dialectCO->findText(qt_(dialect_gui));
543 dialectCO->setCurrentIndex(n);
549 languageCO->setEnabled(in_gui);
550 dialectCO->setEnabled(!use_minted &&
551 in_gui && dialectCO->count() > 1);
552 } else if (prefixIs(*it, "float")) {
553 floatCB->setChecked(true);
554 inlineCB->setChecked(false);
555 placementLE->setEnabled(true);
556 if (prefixIs(*it, "float="))
557 placementLE->setText(
558 toqstr(plainParam(it->substr(6))));
560 } else if (prefixIs(*it, "numbers=")) {
561 string s = plainParam(it->substr(8));
565 else if (s == "right")
567 numberSideCO->setCurrentIndex(n);
569 } else if (prefixIs(*it, "stepnumber=")) {
570 numberStepLE->setText(
571 toqstr(plainParam(it->substr(11))));
573 } else if (prefixIs(*it, "numberstyle=")) {
574 string par = plainParam(it->substr(12));
575 int n = findToken(font_sizes, par.substr(1));
577 numberFontSizeCO->setCurrentIndex(n);
579 } else if (prefixIs(*it, "firstline=")) {
580 firstlineLE->setText(
581 toqstr(plainParam(it->substr(10))));
583 } else if (prefixIs(*it, "lastline=")) {
585 toqstr(plainParam(it->substr(9))));
587 } else if (prefixIs(*it, "basicstyle=")) {
590 for (int n = 0; font_styles[n][0]; ++n) {
591 string const s = font_styles[n];
592 if (contains(*it, "\\" + s)) {
597 for (int n = 0; font_sizes[n][0]; ++n) {
598 string const s = font_sizes[n];
599 if (contains(*it, "\\" + s)) {
604 if (plainParam(it->substr(11)) == style + size
605 || plainParam(it->substr(11)) == size + style) {
606 if (!style.empty()) {
607 int n = findToken(font_styles, style.substr(1));
609 fontstyleCO->setCurrentIndex(n);
612 int n = findToken(font_sizes, size.substr(1));
614 fontsizeCO->setCurrentIndex(n);
618 } else if (prefixIs(*it, "fontsize=")) {
620 for (int n = 0; font_sizes[n][0]; ++n) {
621 string const s = font_sizes[n];
622 if (contains(*it, "\\" + s)) {
628 int n = findToken(font_sizes, size.substr(1));
630 fontsizeCO->setCurrentIndex(n);
633 } else if (prefixIs(*it, "fontfamily=")) {
635 for (int n = 0; font_styles[n][0]; ++n) {
636 string const s = font_styles[n];
637 if (contains(*it, "=" + s.substr(0,2))) {
642 if (!style.empty()) {
643 int n = findToken(font_styles, style.substr(1));
645 fontstyleCO->setCurrentIndex(n);
648 } else if (prefixIs(*it, "breaklines=")) {
649 breaklinesCB->setChecked(contains(*it, "true"));
651 } else if (prefixIs(*it, "showspaces=")) {
652 spaceCB->setChecked(contains(*it, "true"));
654 } else if (prefixIs(*it, "showstringspaces=")) {
655 spaceInStringCB->setChecked(contains(*it, "true"));
657 } else if (prefixIs(*it, "tabsize=")) {
658 tabsizeSB->setValue(convert<int>(plainParam(it->substr(8))));
660 } else if (prefixIs(*it, "extendedchars=")) {
661 extendedcharsCB->setChecked(contains(*it, "true"));
666 numberStepLE->setEnabled(numberSideCO->currentIndex() > 0);
667 numberFontSizeCO->setEnabled(numberSideCO->currentIndex() > 0
669 spaceInStringCB->setEnabled(!use_minted);
670 extendedcharsCB->setEnabled(!use_minted);
671 // parameters that can be handled by widgets are cleared
672 // the rest is put to the extra edit box.
673 string extra = getStringFromVector(pars);
674 listingsED->setPlainText(toqstr(InsetListingsParams(extra).separatedParams()));
675 params_.setMinted(use_minted);
679 bool GuiListings::isValid()
681 return validate_listings_params().empty();
685 bool GuiListings::initialiseParams(string const & sdata)
687 InsetListings::string2params(sdata, params_);
692 void GuiListings::clearParams()
695 params_.setMinted(buffer().params().use_minted);
699 void GuiListings::dispatchParams()
701 string const lfun = InsetListings::params2string(params_);
702 dispatch(FuncRequest(getLfun(), lfun));
706 void GuiListings::setParams(InsetListingsParams const & params)
709 params_.setMinted(buffer().params().use_minted);
713 } // namespace frontend
717 #include "moc_GuiListings.cpp"