]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiViewSource.cpp
On Linux show in crash message box the backtrace
[lyx.git] / src / frontends / qt4 / GuiViewSource.cpp
1 /**
2  * \file GuiViewSource.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author John Levon
7  * \author Bo Peng
8  * \author Abdelrazak Younes
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "GuiApplication.h"
16 #include "GuiViewSource.h"
17 #include "LaTeXHighlighter.h"
18 #include "qt_helpers.h"
19
20 #include "Buffer.h"
21 #include "BufferParams.h"
22 #include "BufferView.h"
23 #include "Cursor.h"
24 #include "Format.h"
25 #include "Paragraph.h"
26 #include "TexRow.h"
27
28 #include "support/debug.h"
29 #include "support/lassert.h"
30 #include "support/docstream.h"
31 #include "support/gettext.h"
32
33 #include <boost/crc.hpp>
34
35 #include <QBoxLayout>
36 #include <QSettings>
37 #include <QTextCursor>
38 #include <QTextDocument>
39 #include <QVariant>
40
41 using namespace std;
42
43 namespace lyx {
44 namespace frontend {
45
46 ViewSourceWidget::ViewSourceWidget()
47         :       bv_(0), document_(new QTextDocument(this)),
48                 highlighter_(new LaTeXHighlighter(document_)),
49                 force_getcontent_(true)
50 {
51         setupUi(this);
52
53         connect(contentsCO, SIGNAL(activated(int)),
54                 this, SLOT(contentsChanged()));
55         connect(autoUpdateCB, SIGNAL(toggled(bool)),
56                 updatePB, SLOT(setDisabled(bool)));
57         connect(autoUpdateCB, SIGNAL(toggled(bool)),
58                 this, SLOT(updateView()));
59         connect(masterPerspectiveCB, SIGNAL(toggled(bool)),
60                 this, SLOT(updateView()));
61         connect(updatePB, SIGNAL(clicked()),
62                 this, SLOT(updateView()));
63         connect(outputFormatCO, SIGNAL(activated(int)),
64                 this, SLOT(setViewFormat()));
65
66         // setting a document at this point trigger an assertion in Qt
67         // so we disable the signals here:
68         document_->blockSignals(true);
69         viewSourceTV->setDocument(document_);
70         document_->blockSignals(false);
71         viewSourceTV->setReadOnly(true);
72         ///dialog_->viewSourceTV->setAcceptRichText(false);
73         // this is personal. I think source code should be in fixed-size font
74         QFont font(guiApp->typewriterFontName());
75         font.setKerning(false);
76         font.setFixedPitch(true);
77         font.setStyleHint(QFont::TypeWriter);
78         viewSourceTV->setFont(font);
79         // again, personal taste
80         viewSourceTV->setWordWrapMode(QTextOption::NoWrap);
81 }
82
83
84 static size_t crcCheck(docstring const & s)
85 {
86         boost::crc_32_type crc;
87         crc.process_bytes(&s[0], sizeof(char_type) * s.size());
88         return crc.checksum();
89 }
90
91
92 /** get the source code of selected paragraphs, or the whole document
93         \param fullSource get full source code
94         \return true if the content has changed since last call.
95  */
96 static bool getContent(BufferView const * view, Buffer::OutputWhat output,
97                        QString & qstr, string const format, bool force_getcontent,
98                        bool master)
99 {
100         // get the *top* level paragraphs that contain the cursor,
101         // or the selected text
102         pit_type par_begin;
103         pit_type par_end;
104
105         if (!view->cursor().selection()) {
106                 par_begin = view->cursor().bottom().pit();
107                 par_end = par_begin;
108         } else {
109                 par_begin = view->cursor().selectionBegin().bottom().pit();
110                 par_end = view->cursor().selectionEnd().bottom().pit();
111         }
112         if (par_begin > par_end)
113                 swap(par_begin, par_end);
114         odocstringstream ostr;
115         view->buffer().getSourceCode(ostr, format, par_begin, par_end + 1,
116                                      output, master);
117         docstring s = ostr.str();
118         // FIXME THREAD
119         // Could this be private to this particular dialog? We could have
120         // more than one of these, in different windows.
121         static size_t crc = 0;
122         size_t newcrc = crcCheck(s);
123         if (newcrc == crc && !force_getcontent)
124                 return false;
125         crc = newcrc;
126         qstr = toqstr(s);
127         return true;
128 }
129
130
131 void ViewSourceWidget::setBufferView(BufferView const * bv)
132 {
133         if (bv_ != bv)
134                 force_getcontent_ = true;
135         bv_ = bv;
136         setEnabled(bv ?  true : false);
137 }
138
139
140 void ViewSourceWidget::contentsChanged()
141 {
142         if (autoUpdateCB->isChecked())
143                 updateView();
144 }
145
146
147 void ViewSourceWidget::setViewFormat()
148 {
149         view_format_ = outputFormatCO->itemData(
150               outputFormatCO->currentIndex()).toString();
151         updateView();
152 }
153
154
155 void ViewSourceWidget::updateView()
156 {
157         if (!bv_) {
158                 document_->setPlainText(QString());
159                 setEnabled(false);
160                 return;
161         }
162
163         setEnabled(true);
164
165         string const format = fromqstr(view_format_);
166
167         QString content;
168         Buffer::OutputWhat output = Buffer::CurrentParagraph;
169         if (contentsCO->currentIndex() == 1)
170                 output = Buffer::FullSource;
171         else if (contentsCO->currentIndex() == 2)
172                 output = Buffer::OnlyPreamble;
173         else if (contentsCO->currentIndex() == 3)
174                 output = Buffer::OnlyBody;
175
176         if (getContent(bv_, output, content, format,
177                       force_getcontent_, masterPerspectiveCB->isChecked()))
178                 document_->setPlainText(content);
179
180         CursorSlice beg = bv_->cursor().selectionBegin().bottom();
181         CursorSlice end = bv_->cursor().selectionEnd().bottom();
182         int const begrow = bv_->buffer().texrow().
183                 getRowFromIdPos(beg.paragraph().id(), beg.pos());
184         int endrow = bv_->buffer().texrow().
185                 getRowFromIdPos(end.paragraph().id(), end.pos());
186         int const nextendrow = bv_->buffer().texrow().
187                 getRowFromIdPos(end.paragraph().id(), end.pos() + 1);
188         if (endrow != nextendrow)
189                 endrow = nextendrow - 1;
190
191         QTextCursor c = QTextCursor(viewSourceTV->document());
192         c.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, begrow);
193         c.select(QTextCursor::BlockUnderCursor);
194         c.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor,
195                 endrow - begrow + 1);
196         viewSourceTV->setTextCursor(c);
197 }
198
199
200 void ViewSourceWidget::updateDefaultFormat()
201 {
202         if (!bv_)
203                 return;
204
205         outputFormatCO->blockSignals(true);
206         outputFormatCO->clear();
207         outputFormatCO->addItem(qt_("Default"),
208                                 QVariant(QString("default")));
209
210         int index = 0;
211         vector<string> tmp = bv_->buffer().params().backends();
212         vector<string>::const_iterator it = tmp.begin();
213         vector<string>::const_iterator en = tmp.end();
214         for (; it != en; ++it) {
215                 string const format = *it;
216                 Format const * fmt = formats.getFormat(format);
217                 if (!fmt) {
218                         LYXERR0("Can't find format for backend " << format << "!");
219                         continue;
220                 } 
221
222                 QString const pretty = qt_(fmt->prettyname());
223                 QString const qformat = toqstr(format);
224                 outputFormatCO->addItem(pretty, QVariant(qformat));
225                 if (qformat == view_format_)
226                    index = outputFormatCO->count() -1;
227         }
228         outputFormatCO->setCurrentIndex(index);
229
230         outputFormatCO->blockSignals(false);
231 }
232
233
234 void ViewSourceWidget::resizeEvent (QResizeEvent * event)
235 {
236         QSize const & formSize = formLayout->sizeHint();
237         // minimize the size of the part that contains the buttons
238         if (width() * formSize.height() < height() * formSize.width()) {
239                 layout_->setDirection(QBoxLayout::TopToBottom);
240         } else {
241                 layout_->setDirection(QBoxLayout::LeftToRight);
242         }
243         QWidget::resizeEvent(event);
244 }
245
246
247 GuiViewSource::GuiViewSource(GuiView & parent,
248                 Qt::DockWidgetArea area, Qt::WindowFlags flags)
249         : DockView(parent, "view-source", qt_("LaTeX Source"), area, flags)
250 {
251         widget_ = new ViewSourceWidget;
252         setWidget(widget_);
253 }
254
255
256 GuiViewSource::~GuiViewSource()
257 {
258         delete widget_;
259 }
260
261
262 void GuiViewSource::updateView()
263 {
264         if (widget_->autoUpdateCB->isChecked()) {
265                 widget_->setBufferView(bufferview());
266                 widget_->updateView();
267         }
268         widget_->masterPerspectiveCB->setEnabled(buffer().parent());
269 }
270
271
272 void GuiViewSource::enableView(bool enable)
273 {
274         widget_->setBufferView(bufferview());
275         widget_->updateDefaultFormat();
276         if (!enable)
277                 // In the opposite case, updateView() will be called anyway.
278                 widget_->updateView();
279 }
280
281
282 bool GuiViewSource::initialiseParams(string const & /*source*/)
283 {
284         setWindowTitle(title());
285         return true;
286 }
287
288
289 QString GuiViewSource::title() const
290 {
291         switch (docType()) {
292                 case LATEX:
293                         return qt_("LaTeX Source");
294                 case DOCBOOK:
295                         return qt_("DocBook Source");
296                 case LITERATE:
297                         return qt_("Literate Source");
298         }
299         LATTEST(false);
300         return QString();
301 }
302
303
304 void GuiViewSource::saveSession() const
305 {
306         Dialog::saveSession();
307         QSettings settings;
308         // see below
309         // settings.setValue(
310         //      sessionKey() + "/output", widget_->contentsCO->currentIndex());
311         settings.setValue(
312                 sessionKey() + "/autoupdate", widget_->autoUpdateCB->isChecked());
313 }
314
315
316 void GuiViewSource::restoreSession()
317 {
318         DockView::restoreSession();
319         // FIXME: Full source updating is too slow to be done at startup.
320         //widget_->outputCO-setCurrentIndex(
321         //      settings.value(sessionKey() + "/output", false).toInt());
322         widget_->contentsCO->setCurrentIndex(0);
323         QSettings settings;
324         widget_->autoUpdateCB->setChecked(
325                 settings.value(sessionKey() + "/autoupdate", true).toBool());
326         widget_->updateView();
327 }
328
329
330 Dialog * createGuiViewSource(GuiView & lv)
331 {
332         return new GuiViewSource(lv);
333 }
334
335
336 } // namespace frontend
337 } // namespace lyx
338
339 #include "moc_GuiViewSource.cpp"