]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiViewSource.cpp
Substack support for XHTML.
[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 "BufferView.h"
21 #include "Buffer.h"
22 #include "Cursor.h"
23 #include "Format.h"
24 #include "Paragraph.h"
25 #include "TexRow.h"
26
27 #include "support/debug.h"
28 #include "support/lassert.h"
29 #include "support/docstream.h"
30 #include "support/gettext.h"
31
32 #include <boost/crc.hpp>
33
34 #include <QSettings>
35 #include <QTextCursor>
36 #include <QTextDocument>
37 #include <QVariant>
38
39 using namespace std;
40
41 namespace lyx {
42 namespace frontend {
43
44 ViewSourceWidget::ViewSourceWidget()
45         :       bv_(0), document_(new QTextDocument(this)),
46                 highlighter_(new LaTeXHighlighter(document_)),
47                 force_getcontent_(true)
48 {
49         setupUi(this);
50
51         connect(viewFullSourceCB, SIGNAL(clicked()),
52                 this, SLOT(fullSourceChanged()));
53         connect(autoUpdateCB, SIGNAL(toggled(bool)),
54                 updatePB, SLOT(setDisabled(bool)));
55         connect(autoUpdateCB, SIGNAL(toggled(bool)),
56                 this, SLOT(updateView()));
57         connect(updatePB, SIGNAL(clicked()),
58                 this, SLOT(updateView()));
59         connect(outputFormatCO, SIGNAL(activated(int)),
60                 this, SLOT(updateView()));
61
62         // setting a document at this point trigger an assertion in Qt
63         // so we disable the signals here:
64         document_->blockSignals(true);
65         viewSourceTV->setDocument(document_);
66         document_->blockSignals(false);
67         viewSourceTV->setReadOnly(true);
68         ///dialog_->viewSourceTV->setAcceptRichText(false);
69         // this is personal. I think source code should be in fixed-size font
70         QFont font(guiApp->typewriterFontName());
71         font.setKerning(false);
72         font.setFixedPitch(true);
73         font.setStyleHint(QFont::TypeWriter);
74         viewSourceTV->setFont(font);
75         // again, personal taste
76         viewSourceTV->setWordWrapMode(QTextOption::NoWrap);
77 }
78
79
80 static size_t crcCheck(docstring const & s)
81 {
82         boost::crc_32_type crc;
83         crc.process_bytes(&s[0], sizeof(char_type) * s.size());
84         return crc.checksum();
85 }
86
87
88 /** get the source code of selected paragraphs, or the whole document
89         \param fullSource get full source code
90         \return true if the content has changed since last call.
91  */
92 static bool getContent(BufferView const * view, bool fullSource,
93                        QString & qstr, string const format, bool force_getcontent)
94 {
95         // get the *top* level paragraphs that contain the cursor,
96         // or the selected text
97         pit_type par_begin;
98         pit_type par_end;
99
100         if (!view->cursor().selection()) {
101                 par_begin = view->cursor().bottom().pit();
102                 par_end = par_begin;
103         } else {
104                 par_begin = view->cursor().selectionBegin().bottom().pit();
105                 par_end = view->cursor().selectionEnd().bottom().pit();
106         }
107         if (par_begin > par_end)
108                 swap(par_begin, par_end);
109         odocstringstream ostr;
110         view->buffer().getSourceCode(ostr, format, par_begin, par_end + 1, fullSource);
111         docstring s = ostr.str();
112         static size_t crc = 0;
113         size_t newcrc = crcCheck(s);
114         if (newcrc == crc && !force_getcontent)
115                 return false;
116         crc = newcrc;
117         qstr = toqstr(s);
118         return true;
119 }
120
121
122 void ViewSourceWidget::setBufferView(BufferView const * bv)
123 {
124         if (bv_ != bv)
125                 force_getcontent_ = true;
126         bv_ = bv;
127         setEnabled(bv ?  true : false);
128 }
129
130
131 void ViewSourceWidget::fullSourceChanged()
132 {
133         if (autoUpdateCB->isChecked())
134                 updateView();
135 }
136
137
138 void ViewSourceWidget::updateView()
139 {
140         if (!bv_) {
141                 document_->setPlainText(QString());
142                 setEnabled(false);
143                 return;
144         }
145
146         setEnabled(true);
147
148         string const format = fromqstr(outputFormatCO->itemData(
149                 outputFormatCO->currentIndex()).toString());
150
151         QString content;
152         if (getContent(bv_, viewFullSourceCB->isChecked(), content,
153                   format, force_getcontent_))
154                 document_->setPlainText(content);
155
156         CursorSlice beg = bv_->cursor().selectionBegin().bottom();
157         CursorSlice end = bv_->cursor().selectionEnd().bottom();
158         int const begrow = bv_->buffer().texrow().
159                 getRowFromIdPos(beg.paragraph().id(), beg.pos());
160         int endrow = bv_->buffer().texrow().
161                 getRowFromIdPos(end.paragraph().id(), end.pos());
162         int const nextendrow = bv_->buffer().texrow().
163                 getRowFromIdPos(end.paragraph().id(), end.pos() + 1);
164         if (endrow != nextendrow)
165                 endrow = nextendrow - 1;
166
167         QTextCursor c = QTextCursor(viewSourceTV->document());
168         c.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, begrow);
169         c.select(QTextCursor::BlockUnderCursor);
170         c.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor,
171                 endrow - begrow + 1);
172         viewSourceTV->setTextCursor(c);
173 }
174
175
176 void ViewSourceWidget::updateDefaultFormat()
177 {
178         if (!bv_)
179                 return;
180
181         outputFormatCO->blockSignals(true);
182         outputFormatCO->clear();
183         outputFormatCO->addItem(qt_("Default"),
184                                 QVariant(QString("default")));
185         typedef vector<Format const *> Formats;
186         Formats formats = bv_->buffer().exportableFormats(true);
187         Formats::const_iterator cit = formats.begin();
188         Formats::const_iterator end = formats.end();
189         for (; cit != end; ++cit)
190                 outputFormatCO->addItem(qt_((*cit)->prettyname()),
191                                 QVariant(toqstr((*cit)->name())));
192         outputFormatCO->blockSignals(false);
193 }
194
195
196 GuiViewSource::GuiViewSource(GuiView & parent,
197                 Qt::DockWidgetArea area, Qt::WindowFlags flags)
198         : DockView(parent, "view-source", qt_("LaTeX Source"), area, flags)
199 {
200         widget_ = new ViewSourceWidget();
201         setWidget(widget_);
202 }
203
204
205 GuiViewSource::~GuiViewSource()
206 {
207         delete widget_;
208 }
209
210
211 void GuiViewSource::updateView()
212 {
213         if (widget_->autoUpdateCB->isChecked()) {
214                 widget_->setBufferView(bufferview());
215                 widget_->updateView();
216         }
217 }
218
219
220 void GuiViewSource::enableView(bool enable)
221 {
222         widget_->setBufferView(bufferview());
223         widget_->updateDefaultFormat();
224         if (!enable)
225                 // In the opposite case, updateView() will be called anyway.
226                 widget_->updateView();
227 }
228
229
230 bool GuiViewSource::initialiseParams(string const & /*source*/)
231 {
232         setWindowTitle(title());
233         return true;
234 }
235
236
237 QString GuiViewSource::title() const
238 {
239         switch (docType()) {
240                 case LATEX:
241                         return qt_("LaTeX Source");
242                 case DOCBOOK:
243                         return qt_("DocBook Source");
244                 case LITERATE:
245                         return qt_("Literate Source");
246         }
247         LASSERT(false, /**/);
248         return QString();
249 }
250
251
252 void GuiViewSource::saveSession() const
253 {
254         Dialog::saveSession();
255         QSettings settings;
256         settings.setValue(
257                 sessionKey() + "/fullsource", widget_->viewFullSourceCB->isChecked());
258         settings.setValue(
259                 sessionKey() + "/autoupdate", widget_->autoUpdateCB->isChecked());
260 }
261
262
263 void GuiViewSource::restoreSession()
264 {
265         DockView::restoreSession();
266         // FIXME: Full source updating is too slow to be done at startup.
267         //widget_->viewFullSourceCB->setChecked(
268         //      settings.value(sessionKey() + "/fullsource", false).toBool());
269         widget_->viewFullSourceCB->setChecked(false);
270         QSettings settings;
271         widget_->autoUpdateCB->setChecked(
272                 settings.value(sessionKey() + "/autoupdate", true).toBool());
273         widget_->updateView();
274 }
275
276
277 Dialog * createGuiViewSource(GuiView & lv)
278 {
279         return new GuiViewSource(lv);
280 }
281
282
283 } // namespace frontend
284 } // namespace lyx
285
286 #include "moc_GuiViewSource.cpp"