]> git.lyx.org Git - lyx.git/blob - src/frontends/qt/GuiParagraph.cpp
Move Lexer to support/ directory (and lyx::support namespace)
[lyx.git] / src / frontends / qt / GuiParagraph.cpp
1 /**
2  * \file GuiParagraph.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Edwin Leuven
7  * \author Richard Kimberly Heck
8  * \author Abdelrazak Younes
9  * \author Angus Leeming
10  *
11  * Full author contact details are available in file CREDITS.
12  */
13
14 #include <config.h>
15
16 #include "GuiParagraph.h"
17
18 #include "qt_helpers.h"
19
20 #include "Buffer.h"
21 #include "BufferParams.h"
22 #include "BufferView.h"
23 #include "Cursor.h"
24 #include "FuncRequest.h"
25 #include "GuiView.h"
26 #include "Layout.h"
27 #include "Paragraph.h"
28 #include "ParagraphParameters.h"
29 #include "Spacing.h"
30
31 #include "support/debug.h"
32 #include "support/Lexer.h"
33
34 #include <QCheckBox>
35 #include <QDialogButtonBox>
36 #include <QLineEdit>
37 #include <QPushButton>
38 #include <QSettings>
39 #include <QVariant>
40
41 #include <sstream>
42
43 using namespace std;
44
45 namespace lyx {
46 namespace frontend {
47
48 GuiParagraph::GuiParagraph(GuiView & lv)
49         : DialogView(lv, "paragraph", qt_("Paragraph Settings"))
50 {
51         setupUi(this);
52
53         // fix height to minimum
54         setFixedHeight(sizeHint().height());
55
56         connect(alignDefaultRB, SIGNAL(clicked()), this, SLOT(changed()));
57         connect(alignJustRB, SIGNAL(clicked()), this, SLOT(changed()));
58         connect(alignLeftRB, SIGNAL(clicked()), this, SLOT(changed()));
59         connect(alignRightRB, SIGNAL(clicked()), this, SLOT(changed()));
60         connect(alignCenterRB, SIGNAL(clicked()), this, SLOT(changed()));
61         connect(linespacing, SIGNAL(activated(int)), this, SLOT(changed()));
62         connect(linespacingValue, SIGNAL(textChanged(QString)),
63                 this, SLOT(changed()));
64         connect(noindentCB, SIGNAL(clicked()), this, SLOT(changed()));
65         connect(labelWidth, SIGNAL(textChanged(QString)),
66                 this, SLOT(changed()));
67
68 #ifdef Q_OS_MAC
69         // On Mac it's common to have tool windows which are always in the
70         // foreground and are hidden when the main window is not focused.
71         setWindowFlags(Qt::Tool);
72         synchronizedViewCB->setChecked(true);
73 #else
74         synchronizedViewCB->setChecked(false);
75 #endif
76
77         setButtons(synchronizedViewCB->isChecked());
78         QDoubleValidator * val = new QDoubleValidator(linespacingValue);
79         val->setNotation(QDoubleValidator::StandardNotation);
80         linespacingValue->setValidator(val);
81
82         labelWidth->setWhatsThis(qt_(
83                 "As described in the User Guide, the width of"
84                 " this text determines the width of the label part"
85                 " of each item in environments like List and"
86                 " Description.\n"
87                 "\n"
88                 " Normally, you won't need to set this,"
89                 " since the largest label width of all the"
90                 " items is used."
91         ));
92
93         radioMap_[LYX_ALIGN_LAYOUT] = alignDefaultRB;
94         radioMap_[LYX_ALIGN_BLOCK]  = alignJustRB;
95         radioMap_[LYX_ALIGN_LEFT]   = alignLeftRB;
96         radioMap_[LYX_ALIGN_RIGHT]  = alignRightRB;
97         radioMap_[LYX_ALIGN_CENTER] = alignCenterRB;
98
99         alignDefaultLabel_ = alignDefaultRB->text();
100 }
101
102
103 void GuiParagraph::on_linespacing_activated(int index)
104 {
105         linespacingValue->setEnabled(index == 4);
106 }
107
108
109 void GuiParagraph::checkAlignmentRadioButtons()
110 {
111         static std::map<LyXAlignment, QString> labelMap_;
112         if (labelMap_.empty()) {
113                 labelMap_[LYX_ALIGN_BLOCK]  = qt_("Justified");
114                 labelMap_[LYX_ALIGN_LEFT]   = qt_("Left");
115                 labelMap_[LYX_ALIGN_RIGHT]  = qt_("Right");
116                 labelMap_[LYX_ALIGN_CENTER] = qt_("Center");
117         }
118
119         RadioMap::iterator it = radioMap_.begin();
120         for (; it != radioMap_.end(); ++it) {
121                 LyXAlignment const align = it->first;
122                 it->second->setEnabled(align & alignPossible());
123         }
124         if (haveMultiParSelection())
125                 alignDefaultRB->setText(alignDefaultLabel_);
126         else
127                 alignDefaultRB->setText(alignDefaultLabel_ + " ("
128                         + labelMap_[bufferview()->cursor().innerParagraph().getDefaultAlign(buffer().params())] + ")");
129 }
130
131
132 void GuiParagraph::alignmentToRadioButtons(LyXAlignment align)
133 {
134         RadioMap::const_iterator it = radioMap_.begin();
135         for (;it != radioMap_.end(); ++it) {
136                 it->second->blockSignals(true);
137                 it->second->setChecked(align == it->first);
138                 it->second->blockSignals(false);
139         }
140 }
141
142
143 LyXAlignment GuiParagraph::getAlignmentFromDialog() const
144 {
145         LyXAlignment alignment = LYX_ALIGN_NONE;
146         RadioMap::const_iterator it = radioMap_.begin();
147         for (; it != radioMap_.end(); ++it) {
148                 if (it->second->isChecked()) {
149                         alignment = it->first;
150                         break;
151                 }
152         }
153         return alignment;
154 }
155
156
157 void GuiParagraph::on_synchronizedViewCB_stateChanged(int state)
158 {
159         bool in_sync = state == Qt::Checked;
160         setButtons(in_sync);
161
162         // Apply pending changes
163         if (in_sync) 
164                 changed();
165 }
166
167
168 void GuiParagraph::setButtons(bool const in_sync)
169 {
170         buttonBox->button(QDialogButtonBox::Reset)->setEnabled(!in_sync);
171         buttonBox->button(QDialogButtonBox::Apply)->setEnabled(!in_sync);
172         buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!in_sync);
173         if (in_sync) 
174                 buttonBox->button(QDialogButtonBox::Cancel)->setText(qt_("&Close"));
175         else
176                 buttonBox->button(QDialogButtonBox::Cancel)->setText(qt_("&Cancel"));
177 }
178
179
180 void GuiParagraph::changed()
181 {
182         QLocale loc;
183         // We apply immediately, except if we have custom line spacing
184         // with an intermediate result (trailing decimal separator) or
185         // an invalid value (which might as well be intermediate)
186         if (synchronizedViewCB->isChecked()
187             && (linespacing->currentIndex() != 4
188                 || (!linespacingValue->text().endsWith(loc.decimalPoint())
189                     && linespacingValue->hasAcceptableInput())))
190                 applyView();
191 }
192
193
194 void GuiParagraph::on_buttonBox_clicked(QAbstractButton * button)
195 {
196         switch (buttonBox->standardButton(button)) {
197         case QDialogButtonBox::Ok:
198                 applyView();
199                 hide();
200                 break;
201         case QDialogButtonBox::Apply:
202                 applyView();
203                 break;
204         case QDialogButtonBox::Cancel:
205                 hide();
206                 break;
207         case QDialogButtonBox::Reset:
208                 updateView();
209                 break;
210         default:
211                 break;
212         }
213 }
214
215
216 void GuiParagraph::applyView()
217 {
218         params_ = params();
219
220         params_.align(getAlignmentFromDialog());
221
222         // get spacing
223         Spacing::Space ls = Spacing::Default;
224         string other;
225         switch (linespacing->currentIndex()) {
226         case 0:
227                 ls = Spacing::Default;
228                 break;
229         case 1:
230                 ls = Spacing::Single;
231                 break;
232         case 2:
233                 ls = Spacing::Onehalf;
234                 break;
235         case 3:
236                 ls = Spacing::Double;
237                 break;
238         case 4:
239                 ls = Spacing::Other;
240                 other = widgetToDoubleStr(linespacingValue);
241                 break;
242         }
243
244         Spacing const spacing(ls, other);
245         params_.spacing(spacing);
246
247         // label width
248         params_.labelWidthString(qstring_to_ucs4(labelWidth->text()));
249         // indentation
250         params_.noindent(noindentCB->isChecked());
251
252         dispatchParams();
253 }
254
255
256 void GuiParagraph::updateView()
257 {
258         setButtons(synchronizedViewCB->isChecked());
259
260         ParagraphParameters const & pp = params();
261
262         // label width
263         docstring const & labelwidth = pp.labelWidthString();
264         if (hasLabelwidth()) {
265                 labelwidthGB->setEnabled(true);
266                 labelWidth->setText(toqstr(labelwidth));
267         } else {
268                 labelwidthGB->setEnabled(false);
269                 labelWidth->setText(QString());
270         }
271
272         // alignment
273         checkAlignmentRadioButtons();
274         alignmentToRadioButtons(pp.align());
275
276         //indentation
277         bool const canindent = canIndent();
278         noindentCB->setEnabled(canindent);
279         noindentCB->setChecked(canindent && pp.noindent());
280
281         // linespacing
282         int ls;
283         bool pending_input = false;
284         Spacing const & space = pp.spacing();
285         if (synchronizedViewCB->isChecked() && linespacingValue->hasFocus()) {
286                 // The user is about to enter custom spacing.
287                 // We thus stay in Custom mode.
288                 // This prevents the combo from e.g. immediately
289                 // switching to single if a user enters "1" in the
290                 // linespacingValue widget while aiming at e.g. "1.3"
291                 ls = 4;
292                 pending_input = true;
293         } else {
294                 switch (space.getSpace()) {
295                 case Spacing::Single:
296                         ls = 1;
297                         break;
298                 case Spacing::Onehalf:
299                         ls = 2;
300                         break;
301                 case Spacing::Double:
302                         ls = 3;
303                         break;
304                 case Spacing::Other:
305                         ls = 4;
306                         break;
307                 default:
308                         ls = 0;
309                         break;
310                 }
311         }
312         linespacing->setCurrentIndex(ls);
313         if (space.getSpace() == Spacing::Other || pending_input) {
314                 doubleToWidget(linespacingValue, space.getValue());
315                 linespacingValue->setEnabled(true);
316         } else {
317                 linespacingValue->setText(QString());
318                 linespacingValue->setEnabled(false);
319         }
320         // Somewhere in the chain this can lose default status (#11417)
321         buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
322 }
323
324
325 void GuiParagraph::enableView(bool enable)
326 {
327         noindentCB->setEnabled(canIndent() && enable);
328         linespacing->setEnabled(enable);
329         labelWidth->setEnabled(enable);
330         synchronizedViewCB->setEnabled(enable);
331         buttonBox->button(QDialogButtonBox::Apply)->setEnabled(enable);
332         buttonBox->button(QDialogButtonBox::Reset)->setEnabled(enable);
333         if (!enable)
334                 synchronizedViewCB->setChecked(true);
335         RadioMap::const_iterator it = radioMap_.begin();
336         for (; it != radioMap_.end(); ++it)
337                 it->second->setEnabled(enable);
338 }
339
340
341 ParagraphParameters const & GuiParagraph::params() const
342 {
343         if (haveMultiParSelection()) {
344                 // FIXME: in case of multi-paragraph selection, it would be nice to
345                 // initialise the parameters that are common to all paragraphs.
346                 static ParagraphParameters const empty;
347                 return empty;
348         }
349         return bufferview()->cursor().innerParagraph().params();
350 }
351
352
353 void GuiParagraph::dispatchParams()
354 {
355         ostringstream os;
356         params_.write(os);
357         FuncRequest const fr(getLfun(), os.str());
358         dispatch(fr);
359 }
360
361
362 bool GuiParagraph::haveMultiParSelection() const
363 {
364         Cursor const & cur = bufferview()->cursor();
365         return cur.selection() && cur.selBegin().pit() != cur.selEnd().pit();
366 }
367
368
369 bool GuiParagraph::canIndent() const
370 {
371         Layout const lay = bufferview()->cursor().innerParagraph().layout();
372         if (buffer().params().paragraph_separation
373                 == BufferParams::ParagraphIndentSeparation)
374                 return (lay.toggle_indent != ITOGGLE_NEVER);
375         return (lay.toggle_indent == ITOGGLE_ALWAYS);
376 }
377
378
379 LyXAlignment GuiParagraph::alignPossible() const
380 {
381         return bufferview()->cursor().innerParagraph().layout().alignpossible;
382 }
383
384
385 bool GuiParagraph::hasLabelwidth() const
386 {
387         Layout layout = bufferview()->cursor().innerParagraph().layout();
388         return (layout.margintype == MARGIN_MANUAL
389                 || layout.latextype == LATEX_BIB_ENVIRONMENT);
390 }
391
392
393 void GuiParagraph::saveSession(QSettings & settings) const
394 {
395         Dialog::saveSession(settings);
396         settings.setValue(sessionKey() + "/autoapply", synchronizedViewCB->isChecked());
397 }
398
399
400 void GuiParagraph::restoreSession()
401 {
402         Dialog::restoreSession();
403         QSettings settings;
404         synchronizedViewCB->setChecked(
405                 settings.value(sessionKey() + "/autoapply").toBool());
406 }
407
408
409 } // namespace frontend
410 } // namespace lyx
411
412 #include "moc_GuiParagraph.cpp"