]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/GuiViewSource.cpp
Amend f441590c
[lyx.git] / src / frontends / qt4 / GuiViewSource.cpp
index 50cfa31257793d35b448a8ee8056fca2afb3d91f..e528222dad5b6bcaff2e5997141549bca3756d1e 100644 (file)
 #include "LaTeXHighlighter.h"
 #include "qt_helpers.h"
 
-#include "Buffer.h"
 #include "BufferParams.h"
 #include "BufferView.h"
 #include "Cursor.h"
 #include "Format.h"
 #include "Paragraph.h"
-#include "TexRow.h"
 
 #include "support/debug.h"
 #include "support/lassert.h"
 #include "support/docstream.h"
+#include "support/docstring_list.h"
 #include "support/gettext.h"
 
 #include <boost/crc.hpp>
 
 #include <QBoxLayout>
+#include <QComboBox>
+#include <QScrollBar>
 #include <QSettings>
 #include <QTextCursor>
 #include <QTextDocument>
@@ -46,7 +47,6 @@ namespace frontend {
 ViewSourceWidget::ViewSourceWidget()
        :       bv_(0), document_(new QTextDocument(this)),
                highlighter_(new LaTeXHighlighter(document_)),
-               force_getcontent_(true),
                update_timer_(new QTimer(this))
 {
        setupUi(this);
@@ -56,13 +56,20 @@ ViewSourceWidget::ViewSourceWidget()
        connect(autoUpdateCB, SIGNAL(toggled(bool)),
                updatePB, SLOT(setDisabled(bool)));
        connect(autoUpdateCB, SIGNAL(toggled(bool)),
-               this, SLOT(updateViewNow()));
+               this, SLOT(contentsChanged()));
        connect(masterPerspectiveCB, SIGNAL(toggled(bool)),
-               this, SLOT(updateViewNow()));
+               this, SLOT(contentsChanged()));
        connect(updatePB, SIGNAL(clicked()),
                this, SLOT(updateViewNow()));
        connect(outputFormatCO, SIGNAL(activated(int)),
-               this, SLOT(setViewFormat()));
+               this, SLOT(setViewFormat(int)));
+       connect(outputFormatCO, SIGNAL(activated(int)),
+               this, SLOT(contentsChanged()));
+#ifdef DEVEL_VERSION
+       if (lyx::lyxerr.debugging(Debug::LATEX))
+               connect(viewSourceTV, SIGNAL(cursorPositionChanged()),
+                               this, SLOT(gotoCursor()));
+#endif
 
        // setting the update timer
        update_timer_->setSingleShot(true);
@@ -73,6 +80,8 @@ ViewSourceWidget::ViewSourceWidget()
        // so we disable the signals here:
        document_->blockSignals(true);
        viewSourceTV->setDocument(document_);
+       // reset selections
+       setText();
        document_->blockSignals(false);
        viewSourceTV->setReadOnly(true);
        ///dialog_->viewSourceTV->setAcceptRichText(false);
@@ -86,21 +95,9 @@ ViewSourceWidget::ViewSourceWidget()
 }
 
 
-static size_t crcCheck(docstring const & s)
-{
-       boost::crc_32_type crc;
-       crc.process_bytes(&s[0], sizeof(char_type) * s.size());
-       return crc.checksum();
-}
-
-
-/** get the source code of selected paragraphs, or the whole document
-       \param fullSource get full source code
-       \return true if the content has changed since last call.
- */
-static bool getContent(BufferView const * view, Buffer::OutputWhat output,
-                      QString & qstr, string const & format, bool force_getcontent,
-                      bool master)
+void ViewSourceWidget::getContent(BufferView const * view,
+                       Buffer::OutputWhat output, docstring & str, string const & format,
+                       bool master)
 {
        // get the *top* level paragraphs that contain the cursor,
        // or the selected text
@@ -117,31 +114,33 @@ static bool getContent(BufferView const * view, Buffer::OutputWhat output,
        if (par_begin > par_end)
                swap(par_begin, par_end);
        odocstringstream ostr;
-       view->buffer().getSourceCode(ostr, format, par_begin, par_end + 1,
-                                    output, master);
-       docstring s = ostr.str();
-       // FIXME THREAD
-       // Could this be private to this particular dialog? We could have
-       // more than one of these, in different windows.
-       static size_t crc = 0;
-       size_t newcrc = crcCheck(s);
-       if (newcrc == crc && !force_getcontent)
-               return false;
-       crc = newcrc;
-       qstr = toqstr(s);
-       return true;
+       texrow_ = view->buffer().getSourceCode(ostr, format,
+                                                                               par_begin, par_end + 1, output, master);
+       //ensure that the last line can always be selected in its full width
+       str = ostr.str() + "\n";
 }
 
 
 void ViewSourceWidget::setBufferView(BufferView const * bv)
 {
-       if (bv_ != bv)
-               force_getcontent_ = true;
-       bv_ = bv;
+       if (bv_ != bv) {
+               setText();
+               bv_ = bv;
+       }
        setEnabled(bv ?  true : false);
 }
 
 
+bool ViewSourceWidget::setText(QString const & qstr)
+{
+       bool const changed = document_->toPlainText() != qstr;
+       viewSourceTV->setExtraSelections(QList<QTextEdit::ExtraSelection>());
+       if (changed)
+               document_->setPlainText(qstr);
+       return changed;
+}
+
+
 void ViewSourceWidget::contentsChanged()
 {
        if (autoUpdateCB->isChecked())
@@ -149,11 +148,10 @@ void ViewSourceWidget::contentsChanged()
 }
 
 
-void ViewSourceWidget::setViewFormat()
+void ViewSourceWidget::setViewFormat(int const index)
 {
-       view_format_ = outputFormatCO->itemData(
-             outputFormatCO->currentIndex()).toString();
-       updateViewNow();
+       outputFormatCO->setCurrentIndex(index);
+       view_format_ = outputFormatCO->itemData(index).toString();
 }
 
 
@@ -166,24 +164,31 @@ void ViewSourceWidget::updateView()
                                                short_delay : long_delay);
 }
 
+
 void ViewSourceWidget::updateViewNow()
 {
        update_timer_->start(0);
 }
 
+
 void ViewSourceWidget::realUpdateView()
 {
        if (!bv_) {
-               document_->setPlainText(QString());
+               setText();
                setEnabled(false);
                return;
        }
 
        setEnabled(true);
 
+       // we will try to get that much space around the cursor
+       int const v_margin = 3;
+       int const h_margin = 10;
+       // we will try to preserve this
+       int const h_scroll = viewSourceTV->horizontalScrollBar()->value();
+
        string const format = fromqstr(view_format_);
 
-       QString content;
        Buffer::OutputWhat output = Buffer::CurrentParagraph;
        if (contentsCO->currentIndex() == 1)
                output = Buffer::FullSource;
@@ -192,30 +197,126 @@ void ViewSourceWidget::realUpdateView()
        else if (contentsCO->currentIndex() == 3)
                output = Buffer::OnlyBody;
 
-       if (getContent(bv_, output, content, format,
-                     force_getcontent_, masterPerspectiveCB->isChecked()))
-               document_->setPlainText(content);
-
-       CursorSlice beg = bv_->cursor().selectionBegin().bottom();
-       CursorSlice end = bv_->cursor().selectionEnd().bottom();
-       int const begrow = bv_->buffer().texrow().
-               getRowFromIdPos(beg.paragraph().id(), beg.pos());
-       int endrow = bv_->buffer().texrow().
-               getRowFromIdPos(end.paragraph().id(), end.pos());
-       int const nextendrow = bv_->buffer().texrow().
-               getRowFromIdPos(end.paragraph().id(), end.pos() + 1);
-       if (endrow != nextendrow)
-               endrow = nextendrow - 1;
-
-       QTextCursor c = QTextCursor(viewSourceTV->document());
-       c.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, begrow);
-       c.select(QTextCursor::BlockUnderCursor);
-       c.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor,
-               endrow - begrow + 1);
-       viewSourceTV->setTextCursor(c);
+       docstring content;
+       getContent(bv_, output, content, format, masterPerspectiveCB->isChecked());
+       QString old = document_->toPlainText();
+       QString qcontent = toqstr(content);
+#ifdef DEVEL_VERSION
+       // output tex<->row correspondences in the source panel if the "-dbg latex"
+       // option is given.
+       if (texrow_.get() && lyx::lyxerr.debugging(Debug::LATEX)) {
+               QStringList list = qcontent.split(QChar('\n'));
+               docstring_list dlist;
+               for (QStringList::const_iterator it = list.begin(); it != list.end(); ++it)
+                       dlist.push_back(from_utf8(fromqstr(*it)));
+               texrow_->prepend(dlist);
+               qcontent.clear();
+               for (docstring_list::iterator it = dlist.begin(); it != dlist.end(); ++it)
+                       qcontent += toqstr(*it) + '\n';
+       }
+#endif
+       // prevent gotoCursor()
+       viewSourceTV->blockSignals(true);
+       bool const changed = setText(qcontent);
+
+       if (changed && !texrow_.get()) {
+               // position-to-row is unavailable
+               // we jump to the first modification
+               const QChar * oc = old.constData();
+               const QChar * nc = qcontent.constData();
+               int pos = 0;
+               while (*oc != '\0' && *nc != '\0' && *oc == *nc) {
+                       ++oc;
+                       ++nc;
+                       ++pos;
+               }
+               QTextCursor c = QTextCursor(viewSourceTV->document());
+               //get some space below the cursor
+               c.setPosition(pos);
+               c.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor,v_margin);
+               viewSourceTV->setTextCursor(c);
+               //get some space on the right of the cursor
+               viewSourceTV->horizontalScrollBar()->setValue(h_scroll);
+               c.setPosition(pos);
+               const int block = c.blockNumber();
+               for (int i = h_margin; i && block == c.blockNumber(); --i) {
+                       c.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor);
+               }
+               c.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor);
+               viewSourceTV->setTextCursor(c);
+               //back to the position
+               c.setPosition(pos);
+               //c.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor,1);
+               viewSourceTV->setTextCursor(c);
+
+       } else if (texrow_.get()) {
+               // Use the available position-to-row conversion to highlight
+               // the current selection in the source
+               std::pair<int,int> rows = texrow_->rowFromCursor(bv_->cursor());
+               int const beg_row = rows.first;
+               int const end_row = rows.second;
+
+               QTextCursor c = QTextCursor(viewSourceTV->document());
+
+               c.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor,
+                                          beg_row - 1);
+               const int beg_sel = c.position();
+               //get some space above the cursor
+               c.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor,
+                                          v_margin);
+               viewSourceTV->setTextCursor(c);
+               c.setPosition(beg_sel, QTextCursor::MoveAnchor);
+
+               c.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor,
+                                          end_row - beg_row +1);
+               const int end_sel = c.position();
+               //get some space below the cursor
+               c.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor,
+                                          v_margin - 1);
+               viewSourceTV->setTextCursor(c);
+               c.setPosition(end_sel, QTextCursor::KeepAnchor);
+
+               viewSourceTV->setTextCursor(c);
+
+               //the real highlighting is done with an ExtraSelection
+               QTextCharFormat format;
+               QPalette palette = viewSourceTV->palette();
+               //Alternative:
+               //  QColor bg = palette.color(QPalette::Active,QPalette::Highlight);
+               //  bg.setAlpha(64);
+               //  format.setBackground(QBrush(bg));
+               //Other alternatives:
+               //format.setBackground(palette.light());
+               //format.setBackground(palette.alternateBase());
+               format.setBackground(palette.toolTipBase());
+               format.setProperty(QTextFormat::FullWidthSelection, true);
+               QTextEdit::ExtraSelection sel;
+               sel.format = format;
+               sel.cursor = c;
+               viewSourceTV->setExtraSelections(
+                       QList<QTextEdit::ExtraSelection>() << sel);
+
+               //clean up
+               c.clearSelection();
+               viewSourceTV->setTextCursor(c);
+               viewSourceTV->horizontalScrollBar()->setValue(h_scroll);
+       } // else if (texrow)
+       viewSourceTV->blockSignals(false);
+}
+
+
+// only used in DEVEL_MODE for debugging
+// need a proper LFUN if we want to implement it in release mode
+void ViewSourceWidget::gotoCursor()
+{
+       if (!bv_ || !texrow_.get())
+               return;
+       int row = viewSourceTV->textCursor().blockNumber() + 1;
+       const_cast<BufferView *>(bv_)->setCursorFromRow(row, *texrow_);
 }
 
 
+
 void ViewSourceWidget::updateDefaultFormat()
 {
        if (!bv_)
@@ -244,7 +345,7 @@ void ViewSourceWidget::updateDefaultFormat()
                if (qformat == view_format_)
                   index = outputFormatCO->count() -1;
        }
-       outputFormatCO->setCurrentIndex(index);
+       setViewFormat(index);
 
        outputFormatCO->blockSignals(false);
 }
@@ -262,6 +363,35 @@ void ViewSourceWidget::resizeEvent (QResizeEvent * event)
        QWidget::resizeEvent(event);
 }
 
+void ViewSourceWidget::saveSession(QString const & session_key) const
+{
+       QSettings settings;
+       settings.setValue(session_key + "/output", view_format_);
+       settings.setValue(session_key + "/contents", contentsCO->currentIndex());
+       settings.setValue(session_key + "/autoupdate", autoUpdateCB->isChecked());
+       settings.setValue(session_key + "/masterview",
+                                         masterPerspectiveCB->isChecked());
+}
+
+
+void ViewSourceWidget::restoreSession(QString const & session_key)
+{
+       QSettings settings;
+    view_format_ = settings.value(session_key + "/output", 0).toString();
+       contentsCO->setCurrentIndex(settings
+                                                               .value(session_key + "/contents", 0)
+                                                               .toInt());
+       masterPerspectiveCB->setChecked(settings
+                                                                       .value(session_key + "/masterview", false)
+                                                                       .toBool());
+       bool const checked = settings
+               .value(session_key + "/autoupdate", true)
+               .toBool();
+       autoUpdateCB->setChecked(checked);
+       if (checked)
+               updateView();
+}
+
 
 GuiViewSource::GuiViewSource(GuiView & parent,
                Qt::DockWidgetArea area, Qt::WindowFlags flags)
@@ -294,7 +424,7 @@ void GuiViewSource::enableView(bool enable)
        widget_->updateDefaultFormat();
        if (!enable)
                // In the opposite case, updateView() will be called anyway.
-               widget_->updateView();
+               widget_->contentsChanged();
 }
 
 
@@ -309,6 +439,7 @@ QString GuiViewSource::title() const
 {
        switch (docType()) {
                case LATEX:
+                       //FIXME: this is shown for LyXHTML source, LyX source, etc.
                        return qt_("LaTeX Source");
                case DOCBOOK:
                        return qt_("DocBook Source");
@@ -323,26 +454,14 @@ QString GuiViewSource::title() const
 void GuiViewSource::saveSession() const
 {
        Dialog::saveSession();
-       QSettings settings;
-       // see below
-       // settings.setValue(
-       //      sessionKey() + "/output", widget_->contentsCO->currentIndex());
-       settings.setValue(
-               sessionKey() + "/autoupdate", widget_->autoUpdateCB->isChecked());
+       widget_->saveSession(sessionKey());
 }
 
 
 void GuiViewSource::restoreSession()
 {
        DockView::restoreSession();
-       // FIXME: Full source updating is too slow to be done at startup.
-       //widget_->outputCO-setCurrentIndex(
-       //      settings.value(sessionKey() + "/output", false).toInt());
-       widget_->contentsCO->setCurrentIndex(0);
-       QSettings settings;
-       widget_->autoUpdateCB->setChecked(
-               settings.value(sessionKey() + "/autoupdate", true).toBool());
-       widget_->updateView();
+       widget_->restoreSession(sessionKey());
 }