2 * \file GuiDelimiter.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
8 * Full author contact details are available in file CREDITS.
13 #include "GuiDelimiter.h"
15 #include "GuiApplication.h"
16 #include "GuiFontLoader.h"
18 #include "qt_helpers.h"
20 #include "FontEnums.h"
22 #include "FuncRequest.h"
24 #include "support/debug.h"
25 #include "support/docstring.h"
26 #include "support/gettext.h"
27 #include "support/lstrings.h"
33 #include <QListWidgetItem>
46 static char const * latex_delimiters[] = {
47 "(", ")", "{", "}", "[", "]",
48 "lceil", "rceil", "lfloor", "rfloor", "langle", "rangle",
49 "llbracket", "rrbracket",
50 "uparrow", "updownarrow", "Uparrow", "Updownarrow", "downarrow", "Downarrow",
51 "|", "Vert", "/", "backslash", ""
55 static int const nr_latex_delimiters =
56 sizeof(latex_delimiters) / sizeof(char const *);
58 static string const bigleft[] = {"", "bigl", "Bigl", "biggl", "Biggl"};
60 static string const bigright[] = {"", "bigr", "Bigr", "biggr", "Biggr"};
62 static char const * const biggui[] = {
63 N_("big[[delimiter size]]"),
64 N_("Big[[delimiter size]]"),
65 N_("bigg[[delimiter size]]"),
66 N_("Bigg[[delimiter size]]"),
71 // FIXME: It might be better to fix the big delim LFUN to not require
72 // additional '\' prefix.
73 static docstring fix_name(string const & str, bool big)
76 return from_ascii(".");
77 if (!big || str == "(" || str == ")" || str == "[" || str == "]"
78 || str == "|" || str == "/")
79 return from_ascii(str);
81 return "\\" + from_ascii(str);
85 MathSymbol(char_type uc = '?', string icon = string())
86 : unicode(uc), icon(icon)
93 QPixmap getSelectedPixmap(QPixmap pixmap, QSize const icon_size)
95 QPalette palette = QPalette();
96 QColor text_color = (guiApp->isInDarkMode())
97 ? palette.color(QPalette::Active, QPalette::WindowText)
99 QColor highlight_color = palette.color(QPalette::Active, QPalette::HighlightedText);
100 QColor highlight_bg = palette.color(QPalette::Active, QPalette::Highlight);
102 // create a layer with black text turned to QPalette::HighlightedText
103 QPixmap hl_overlay(pixmap.size());
104 hl_overlay.fill(highlight_color);
105 hl_overlay.setMask(pixmap.createMaskFromColor(text_color, Qt::MaskOutColor));
107 // Create highlighted background
108 QPixmap hl_background(icon_size);
109 hl_background.fill(highlight_bg);
111 // put layers on top of existing pixmap
112 QPainter painter(&pixmap);
113 painter.drawPixmap(pixmap.rect(), hl_background);
114 painter.drawPixmap(pixmap.rect(), hl_overlay);
119 /// TeX-name / Math-symbol map.
120 static map<std::string, MathSymbol> math_symbols_;
121 /// Math-symbol / TeX-name map.
122 /// This one is for fast search, it contains the same data as
123 /// \c math_symbols_.
124 static map<char_type, string> tex_names_;
126 typedef map<char_type, QListWidgetItem *> ListItems;
127 ListItems left_list_items_;
128 ListItems right_list_items_;
130 void initMathSymbols()
132 // FIXME: Ideally, those unicode codepoints would be defined
133 // in "lib/symbols". Unfortunately, some of those are already
134 // defined with non-unicode ids for use within mathed.
135 // FIXME 2: We should fill-in this map with the parsed "symbols"
136 // file done in MathFactory.cpp.
137 math_symbols_["("] = MathSymbol('(', "lparen");
138 math_symbols_[")"] = MathSymbol(')', "rparen");
139 math_symbols_["{"] = MathSymbol('{', "lbrace");
140 math_symbols_["}"] = MathSymbol('}', "rbrace");
141 math_symbols_["["] = MathSymbol('[', "lbracket");
142 math_symbols_["]"] = MathSymbol(']', "rbracket");
143 math_symbols_["|"] = MathSymbol('|', "mid");
144 math_symbols_["/"] = MathSymbol('/', "slash");
145 math_symbols_["backslash"] = MathSymbol('\\', "backslash");
146 math_symbols_["lceil"] = MathSymbol(0x2308, "lceil");
147 math_symbols_["rceil"] = MathSymbol(0x2309, "rceil");
148 math_symbols_["lfloor"] = MathSymbol(0x230A, "lfloor");
149 math_symbols_["rfloor"] = MathSymbol(0x230B, "rfloor");
150 math_symbols_["langle"] = MathSymbol(0x2329, "langle");
151 math_symbols_["rangle"] = MathSymbol(0x232A, "rangle");
152 math_symbols_["llbracket"] = MathSymbol(0x27e6, "llbracket");
153 math_symbols_["rrbracket"] = MathSymbol(0x27e7, "rrbracket");
154 math_symbols_["uparrow"] = MathSymbol(0x2191, "uparrow");
155 math_symbols_["Uparrow"] = MathSymbol(0x21D1, "uparrow2");
156 math_symbols_["updownarrow"] = MathSymbol(0x2195, "updownarrow");
157 math_symbols_["Updownarrow"] = MathSymbol(0x21D5, "updownarrow2");
158 math_symbols_["downarrow"] = MathSymbol(0x2193, "downarrow");
159 math_symbols_["Downarrow"] = MathSymbol(0x21D3, "downarrow2");
160 math_symbols_["downdownarrows"] = MathSymbol(0x21CA, "downdownarrows");
161 math_symbols_["downharpoonleft"] = MathSymbol(0x21C3, "downharpoonleft");
162 math_symbols_["downharpoonright"] = MathSymbol(0x21C2, "downharpoonright");
163 math_symbols_["vert"] = MathSymbol(0x007C, "vert");
164 math_symbols_["Vert"] = MathSymbol(0x2016, "vert2");
166 map<string, MathSymbol>::const_iterator it = math_symbols_.begin();
167 map<string, MathSymbol>::const_iterator end = math_symbols_.end();
168 for (; it != end; ++it)
169 tex_names_[it->second.unicode] = it->first;
172 /// \return the math unicode symbol associated to a TeX name.
173 MathSymbol const & mathSymbol(string const & tex_name)
175 map<string, MathSymbol>::const_iterator it =
176 math_symbols_.find(tex_name);
178 static MathSymbol const unknown_symbol;
179 if (it == math_symbols_.end())
180 return unknown_symbol;
185 /// \return the TeX name associated to a math unicode symbol.
186 string const & texName(char_type math_symbol)
188 map<char_type, string>::const_iterator it =
189 tex_names_.find(math_symbol);
191 static string const empty_string;
192 if (it == tex_names_.end())
199 void setDelimiterName(QListWidgetItem * lwi, string const & name)
201 lwi->setData(Qt::UserRole, toqstr(name));
205 string getDelimiterName(QListWidgetItem const * lwi)
207 return fromqstr(lwi->data(Qt::UserRole).toString());
214 GuiDelimiter::GuiDelimiter(GuiView & lv)
215 : GuiDialog(lv, "mathdelimiter", qt_("Math Delimiter"))
219 connect(buttonBox, SIGNAL(rejected()), this, SLOT(accept()));
221 setFocusProxy(leftLW);
223 leftLW->setViewMode(QListView::IconMode);
224 rightLW->setViewMode(QListView::IconMode);
226 leftLW->setDragDropMode(QAbstractItemView::NoDragDrop);
227 rightLW->setDragDropMode(QAbstractItemView::NoDragDrop);
229 left_list_items_.clear();
230 right_list_items_.clear();
234 QSize icon_size(26, 26);
236 // we calculate the appropriate width to fit 4 icons in a row
237 leftLW->setMinimumWidth((4 * (icon_size.width() + (2 * leftLW->spacing())))
238 + (leftLW->frameWidth() * 2)
239 + leftLW->verticalScrollBar()->height());
240 rightLW->setMinimumWidth((4 * (icon_size.width() + (2 * rightLW->spacing())))
241 + (rightLW->frameWidth() * 2)
242 + rightLW->verticalScrollBar()->height());
243 leftLW->setIconSize(icon_size);
244 rightLW->setIconSize(icon_size);
246 // The last element is the empty one.
247 int const end = nr_latex_delimiters - 1;
248 for (int i = 0; i < end; ++i) {
249 string const delim = latex_delimiters[i];
250 MathSymbol const & ms = mathSymbol(delim);
251 // get pixmap with bullets
252 QPixmap pixmap = getPixmap("images/math/", toqstr(ms.icon), "svgz,png");
254 icon.addPixmap(getSelectedPixmap(pixmap, icon_size), QIcon::Selected);
255 QListWidgetItem * lwi = new QListWidgetItem(icon, QString());
256 setDelimiterName(lwi, delim);
257 left_list_items_[ms.unicode] = lwi;
258 lwi->setToolTip(toqstr(delim));
259 lwi->setSizeHint(icon_size);
260 leftLW->addItem(lwi);
263 for (int i = 0; i != leftLW->count(); ++i) {
264 MathSymbol const & ms = mathSymbol(getDelimiterName(leftLW->item(i)));
265 char_type const rdelim = doMatch(ms.unicode);
266 QListWidgetItem * rwi = left_list_items_[rdelim]->clone();
267 right_list_items_[mathSymbol(texName(rdelim)).unicode] = rwi;
268 rightLW->addItem(rwi);
271 // The last element is the empty one.
272 QListWidgetItem * lwi = new QListWidgetItem(qt_("(None)"));
273 lwi->setToolTip(qt_("No Delimiter"));
274 left_list_items_['?'] = lwi;
275 QListWidgetItem * rwi = new QListWidgetItem(qt_("(None)"));
276 rwi->setToolTip(qt_("No Delimiter"));
277 right_list_items_['?'] = rwi;
278 leftLW->addItem(lwi);
279 rightLW->addItem(rwi);
281 sizeCO->addItem(qt_("Variable"));
283 for (int i = 0; *biggui[i]; ++i)
284 sizeCO->addItem(qt_(biggui[i]));
286 on_leftLW_currentRowChanged(0);
287 // synchronise the scroll bars
288 on_matchCB_stateChanged(matchCB->checkState());
289 bc().setPolicy(ButtonPolicy::IgnorantPolicy);
293 char_type GuiDelimiter::doMatch(char_type const symbol)
295 string const & str = texName(symbol);
311 else if (str == "rceil")
313 else if (str == "lceil")
315 else if (str == "rfloor")
317 else if (str == "lfloor")
319 else if (str == "rangle")
321 else if (str == "langle")
323 else if (str == "llbracket")
325 else if (str == "rrbracket")
327 else if (str == "backslash")
334 return mathSymbol(match).unicode;
338 void GuiDelimiter::updateTeXCode(int size)
340 bool const bigsize = size != 0;
342 docstring left_str = fix_name(getDelimiterName(leftLW->currentItem()),
344 docstring right_str = fix_name(getDelimiterName(rightLW->currentItem()),
348 tex_code_ = left_str + ' ' + right_str;
350 tex_code_ = from_ascii(bigleft[size]) + ' '
352 + from_ascii(bigright[size]) + ' '
356 // Generate TeX-code for GUI display.
357 // FIXME: Instead of reconstructing the TeX code it would be nice to
358 // FIXME: retrieve the LateX code directly from mathed.
359 // In all cases, we want the '\' prefix if needed, so we pass 'true'
361 left_str = fix_name(getDelimiterName(leftLW->currentItem()),
363 right_str = fix_name(getDelimiterName(rightLW->currentItem()),
367 code_str = "\\left" + left_str + " \\right" + right_str;
369 // There should be nothing in the TeX-code when the delimiter is "None".
371 code_str = "\\" + from_ascii(bigleft[size]) + left_str + ' ';
372 if (right_str != ".")
373 code_str += "\\" + from_ascii(bigright[size]) + right_str;
376 texCodeL->setText(qt_("TeX Code: ") + toqstr(code_str));
378 // Enable the Swap button with non-matched pairs
379 bool const allow_swap =
380 (doMatch(mathSymbol(getDelimiterName(leftLW->currentItem())).unicode)
381 != mathSymbol(getDelimiterName(rightLW->currentItem())).unicode);
382 swapPB->setEnabled(allow_swap);
386 void GuiDelimiter::on_buttonBox_clicked(QAbstractButton * button)
388 switch (buttonBox->standardButton(button)) {
389 case QDialogButtonBox::Apply:
392 case QDialogButtonBox::Ok:
395 case QDialogButtonBox::Cancel:
404 void GuiDelimiter::insert()
406 if (sizeCO->currentIndex() == 0)
407 dispatch(FuncRequest(LFUN_MATH_DELIM, tex_code_));
409 docstring command = '"' + tex_code_ + '"';
410 command = support::subst(command, from_ascii(" "), from_ascii("\" \""));
411 dispatch(FuncRequest(LFUN_MATH_BIGDELIM, command));
413 buttonBox->button(QDialogButtonBox::Cancel)->setText(qt_("Close"));
417 void GuiDelimiter::on_sizeCO_activated(int index)
419 updateTeXCode(index);
423 void GuiDelimiter::on_leftLW_itemActivated(QListWidgetItem *)
425 // do not auto-apply if !matchCB->isChecked()
426 if (!matchCB->isChecked())
433 void GuiDelimiter::on_rightLW_itemActivated(QListWidgetItem *)
435 // do not auto-apply if !matchCB->isChecked()
436 if (!matchCB->isChecked())
443 void GuiDelimiter::on_leftLW_currentRowChanged(int item)
445 if (matchCB->isChecked())
446 rightLW->setCurrentRow(item);
448 updateTeXCode(sizeCO->currentIndex());
452 void GuiDelimiter::on_rightLW_currentRowChanged(int item)
454 if (matchCB->isChecked())
455 leftLW->setCurrentRow(item);
457 updateTeXCode(sizeCO->currentIndex());
461 void GuiDelimiter::on_matchCB_stateChanged(int state)
463 // Synchronise the vertical scroll bars when checked
464 QScrollBar * ls = leftLW->verticalScrollBar();
465 QScrollBar * rs = rightLW->verticalScrollBar();
467 if (state == Qt::Checked) {
468 on_leftLW_currentRowChanged(leftLW->currentRow());
470 connect(ls, SIGNAL(valueChanged(int)), rs, SLOT(setValue(int)),
471 Qt::UniqueConnection);
472 connect(rs, SIGNAL(valueChanged(int)), ls, SLOT(setValue(int)),
473 Qt::UniqueConnection);
474 rs->setValue(ls->value());
480 updateTeXCode(sizeCO->currentIndex());
483 void GuiDelimiter::on_swapPB_clicked()
485 // Get current math symbol for each side.
486 MathSymbol const & lms =
487 mathSymbol(getDelimiterName(leftLW->currentItem()));
488 MathSymbol const & rms =
489 mathSymbol(getDelimiterName(rightLW->currentItem()));
492 char_type const lc = doMatch(rms.unicode);
493 char_type const rc = doMatch(lms.unicode);
495 // Convert back to QString to locate them in the widget.
496 MathSymbol const & nlms = mathSymbol(texName(lc));
497 MathSymbol const & nrms = mathSymbol(texName(rc));
499 // Locate matching QListWidgetItem.
500 QListWidgetItem * lwi = left_list_items_[nlms.unicode];
501 QListWidgetItem * rwi = right_list_items_[nrms.unicode];
504 leftLW->setCurrentItem(lwi);
505 rightLW->setCurrentItem(rwi);
507 updateTeXCode(sizeCO->currentIndex());
511 } // namespace frontend
514 #include "moc_GuiDelimiter.cpp"