]> git.lyx.org Git - lyx.git/blobdiff - src/BufferView.cpp
Fix scons and a file inclusion problem in ControlCommand.h
[lyx.git] / src / BufferView.cpp
index 39874ee0174840ef72373aef86cf361318ff524b..180c2b1aa768476a5a483e836d483eff9eb4bfc3 100644 (file)
@@ -20,7 +20,6 @@
 #include "buffer_funcs.h"
 #include "BufferList.h"
 #include "BufferParams.h"
-#include "callback.h" // added for Dispatch functions
 #include "CoordCache.h"
 #include "CutAndPaste.h"
 #include "debug.h"
@@ -49,7 +48,6 @@
 #include "TexRow.h"
 #include "Text.h"
 #include "TextClass.h"
-#include "toc.h"
 #include "Undo.h"
 #include "VSpace.h"
 #include "WordLangTuple.h"
@@ -60,6 +58,7 @@
 #include "insets/InsetText.h"
 
 #include "frontends/alert.h"
+#include "frontends/Delegates.h"
 #include "frontends/FileDialog.h"
 #include "frontends/FontMetrics.h"
 #include "frontends/Painter.h"
 #include "support/filetools.h"
 #include "support/Package.h"
 #include "support/types.h"
+#include "support/fs_extras.h"
 
 #include <boost/bind.hpp>
 #include <boost/current_function.hpp>
+#include <boost/next_prior.hpp>
+#include <boost/filesystem/operations.hpp>
 
+#include <cerrno>
+#include <fstream>
 #include <functional>
 #include <vector>
 
 using std::distance;
 using std::endl;
+using std::ifstream;
 using std::istringstream;
+using std::istream_iterator;
 using std::make_pair;
 using std::min;
 using std::max;
@@ -89,6 +95,7 @@ using std::mem_fun_ref;
 using std::string;
 using std::vector;
 
+namespace fs = boost::filesystem;
 
 namespace lyx {
 
@@ -100,6 +107,7 @@ using support::fileSearch;
 using support::isDirWriteable;
 using support::isFileReadable;
 using support::makeDisplayPath;
+using support::makeAbsPath;
 using support::package;
 
 namespace Alert = frontend::Alert;
@@ -203,14 +211,145 @@ void gotoInset(BufferView * bv, Inset_code code, bool same_content)
 }
 
 
+
+/// the type of outline operation
+enum OutlineOp {
+       OutlineUp, // Move this header with text down
+       OutlineDown,   // Move this header with text up
+       OutlineIn, // Make this header deeper
+       OutlineOut // Make this header shallower
+};
+
+
+void outline(OutlineOp mode, Cursor & cur)
+{
+       Buffer & buf = cur.buffer();
+       pit_type & pit = cur.pit();
+       ParagraphList & pars = buf.text().paragraphs();
+       ParagraphList::iterator bgn = pars.begin();
+       // The first paragraph of the area to be copied:
+       ParagraphList::iterator start = boost::next(bgn, pit);
+       // The final paragraph of area to be copied:
+       ParagraphList::iterator finish = start;
+       ParagraphList::iterator end = pars.end();
+
+       TextClass::const_iterator lit =
+               buf.params().getTextClass().begin();
+       TextClass::const_iterator const lend =
+               buf.params().getTextClass().end();
+
+       int const thistoclevel = start->layout()->toclevel;
+       int toclevel;
+       switch (mode) {
+               case OutlineUp: {
+                       // Move out (down) from this section header
+                       if (finish != end)
+                               ++finish;
+                       // Seek the one (on same level) below
+                       for (; finish != end; ++finish) {
+                               toclevel = finish->layout()->toclevel;
+                               if (toclevel != Layout::NOT_IN_TOC
+                                   && toclevel <= thistoclevel) {
+                                       break;
+                               }
+                       }
+                       ParagraphList::iterator dest = start;
+                       // Move out (up) from this header
+                       if (dest == bgn)
+                               break;
+                       // Search previous same-level header above
+                       do {
+                               --dest;
+                               toclevel = dest->layout()->toclevel;
+                       } while(dest != bgn
+                               && (toclevel == Layout::NOT_IN_TOC
+                                   || toclevel > thistoclevel));
+                       // Not found; do nothing
+                       if (toclevel == Layout::NOT_IN_TOC || toclevel > thistoclevel)
+                               break;
+                       pit_type const newpit = std::distance(bgn, dest);
+                       pit_type const len = std::distance(start, finish);
+                       pit_type const deletepit = pit + len;
+                       recordUndo(cur, Undo::ATOMIC, newpit, deletepit - 1);
+                       pars.insert(dest, start, finish);
+                       start = boost::next(pars.begin(), deletepit);
+                       pit = newpit;
+                       pars.erase(start, finish);
+                       break;
+               }
+               case OutlineDown: {
+                       // Go down out of current header:
+                       if (finish != end)
+                               ++finish;
+                       // Find next same-level header:
+                       for (; finish != end; ++finish) {
+                               toclevel = finish->layout()->toclevel;
+                               if (toclevel != Layout::NOT_IN_TOC && toclevel <= thistoclevel)
+                                       break;
+                       }
+                       ParagraphList::iterator dest = finish;
+                       // Go one down from *this* header:
+                       if (dest != end)
+                               ++dest;
+                       else
+                               break;
+                       // Go further down to find header to insert in front of:
+                       for (; dest != end; ++dest) {
+                               toclevel = dest->layout()->toclevel;
+                               if (toclevel != Layout::NOT_IN_TOC && toclevel <= thistoclevel)
+                                       break;
+                       }
+                       // One such was found:
+                       pit_type newpit = std::distance(bgn, dest);
+                       pit_type const len = std::distance(start, finish);
+                       recordUndo(cur, Undo::ATOMIC, pit, newpit - 1);
+                       pars.insert(dest, start, finish);
+                       start = boost::next(bgn, pit);
+                       pit = newpit - len;
+                       pars.erase(start, finish);
+                       break;
+               }
+               case OutlineIn:
+                       recordUndo(cur);
+                       for (; lit != lend; ++lit) {
+                               if ((*lit)->toclevel == thistoclevel + 1 &&
+                                   start->layout()->labeltype == (*lit)->labeltype) {
+                                       start->layout((*lit));
+                                       break;
+                               }
+                       }
+                       break;
+               case OutlineOut:
+                       recordUndo(cur);
+                       for (; lit != lend; ++lit) {
+                               if ((*lit)->toclevel == thistoclevel - 1 &&
+                                   start->layout()->labeltype == (*lit)->labeltype) {
+                                       start->layout((*lit));
+                                       break;
+                               }
+                       }
+                       break;
+               default:
+                       break;
+       }
+}
+
 } // anon namespace
 
 
+/////////////////////////////////////////////////////////////////////
+//
+// BufferView
+//
+/////////////////////////////////////////////////////////////////////
+
+
 BufferView::BufferView(Buffer & buf)
        : width_(0), height_(0), buffer_(buf), wh_(0),
          cursor_(*this),
          multiparsel_cache_(false), anchor_ref_(0), offset_ref_(0),
-         need_centering_(false), intl_(new Intl), last_inset_(0)
+         need_centering_(false), intl_(new Intl), last_inset_(0),
+         gui_(0)
 {
        xsel_cache_.set = false;
        intl_->initKeyMapper(lyxrc.use_kbmap);
@@ -762,12 +901,12 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
 
        case LFUN_FILE_INSERT_PLAINTEXT_PARA:
                // FIXME UNICODE
-               insertPlaintextFile(this, to_utf8(cmd.argument()), true);
+               insertPlaintextFile(to_utf8(cmd.argument()), true);
                break;
 
        case LFUN_FILE_INSERT_PLAINTEXT:
                // FIXME UNICODE
-               insertPlaintextFile(this, to_utf8(cmd.argument()), false);
+               insertPlaintextFile(to_utf8(cmd.argument()), false);
                break;
 
        case LFUN_FONT_STATE:
@@ -831,21 +970,21 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
        }
 
        case LFUN_OUTLINE_UP:
-               toc::outline(toc::Up, cursor_);
+               outline(OutlineUp, cursor_);
                cursor_.text()->setCursor(cursor_, cursor_.pit(), 0);
                updateLabels(buffer_);
                break;
        case LFUN_OUTLINE_DOWN:
-               toc::outline(toc::Down, cursor_);
+               outline(OutlineDown, cursor_);
                cursor_.text()->setCursor(cursor_, cursor_.pit(), 0);
                updateLabels(buffer_);
                break;
        case LFUN_OUTLINE_IN:
-               toc::outline(toc::In, cursor_);
+               outline(OutlineIn, cursor_);
                updateLabels(buffer_);
                break;
        case LFUN_OUTLINE_OUT:
-               toc::outline(toc::Out, cursor_);
+               outline(OutlineOut, cursor_);
                updateLabels(buffer_);
                break;
 
@@ -1176,7 +1315,7 @@ Inset const * BufferView::getCoveringInset(Text const & text, int x, int y)
 }
 
 
-bool BufferView::workAreaDispatch(FuncRequest const & cmd0)
+void BufferView::mouseEventDispatch(FuncRequest const & cmd0)
 {
        //lyxerr << BOOST_CURRENT_FUNCTION << "[ cmd0 " << cmd0 << "]" << endl;
 
@@ -1201,7 +1340,7 @@ bool BufferView::workAreaDispatch(FuncRequest const & cmd0)
                        getCoveringInset(buffer_.text(), cmd.x, cmd.y);
                if (covering_inset == last_inset_)
                        // Same inset, no need to do anything...
-                       return false;
+                       return;
 
                bool need_redraw = false;
                // const_cast because of setMouseHover().
@@ -1214,7 +1353,7 @@ bool BufferView::workAreaDispatch(FuncRequest const & cmd0)
                        need_redraw |= inset->setMouseHover(true);
                last_inset_ = inset;
                if (!need_redraw)
-                       return false;
+                       return;
 
                // if last metrics update was in singlepar mode, WorkArea::redraw() will
                // not expose the button for redraw. We adjust here the metrics dimension
@@ -1236,7 +1375,8 @@ bool BufferView::workAreaDispatch(FuncRequest const & cmd0)
 
                // This event (moving without mouse click) is not passed further.
                // This should be changed if it is further utilized.
-               return true;
+               buffer_.changed();
+               return;
        }
 
        // Build temporary cursor.
@@ -1249,9 +1389,8 @@ bool BufferView::workAreaDispatch(FuncRequest const & cmd0)
        // via the temp cursor. If the inset wishes to change the real
        // cursor it has to do so explicitly by using
        //  cur.bv().cursor() = cur;  (or similar)
-       if (inset) {
+       if (inset)
                inset->dispatch(cur, cmd);
-       }
 
        // Now dispatch to the temporary cursor. If the real cursor should
        // be modified, the inset's dispatch has to do so explicitly.
@@ -1261,11 +1400,14 @@ bool BufferView::workAreaDispatch(FuncRequest const & cmd0)
        //Do we have a selection?
        theSelection().haveSelection(cursor().selection());
 
-       // Redraw if requested and necessary.
-       if (cur.result().dispatched() && cur.result().update())
-               return update(cur.result().update());
-
-       return false;
+       // If the command has been dispatched,
+       if (cur.result().dispatched()
+               // an update is asked,
+               && cur.result().update()
+               // and redraw is needed,
+               && update(cur.result().update()))
+               // then trigger a redraw.
+               buffer_.changed();
 }
 
 
@@ -1843,4 +1985,144 @@ void BufferView::draw(frontend::Painter & pain)
                        height_ - metrics_info_.y2, Color::bottomarea);
 }
 
+
+void BufferView::message(docstring const & msg)
+{
+       if (gui_)
+               gui_->message(msg);
+}
+
+
+void BufferView::showDialog(std::string const & name)
+{
+       if (gui_)
+               gui_->showDialog(name);
+}
+
+
+void BufferView::showDialogWithData(std::string const & name,
+       std::string const & data)
+{
+       if (gui_)
+               gui_->showDialogWithData(name, data);
+}
+
+
+void BufferView::showInsetDialog(std::string const & name,
+       std::string const & data, Inset * inset)
+{
+       if (gui_)
+               gui_->showInsetDialog(name, data, inset);
+}
+
+
+void BufferView::updateDialog(std::string const & name, std::string const & data)
+{
+       if (gui_)
+               gui_->updateDialog(name, data);
+}
+
+
+void BufferView::setGuiDelegate(frontend::GuiBufferViewDelegate * gui)
+{
+       gui_ = gui;
+}
+
+
+// FIXME: Move this out of BufferView again
+docstring BufferView::contentsOfPlaintextFile(string const & f,
+       bool asParagraph)
+{
+       FileName fname(f);
+
+       if (fname.empty()) {
+               FileDialog fileDlg(_("Select file to insert"),
+                                  ( asParagraph
+                                    ? LFUN_FILE_INSERT_PLAINTEXT_PARA 
+                                    : LFUN_FILE_INSERT_PLAINTEXT) );
+
+               FileDialog::Result result =
+                       fileDlg.open(from_utf8(buffer().filePath()),
+                                    FileFilterList(), docstring());
+
+               if (result.first == FileDialog::Later)
+                       return docstring();
+
+               fname = makeAbsPath(to_utf8(result.second));
+
+               if (fname.empty())
+                       return docstring();
+       }
+
+       if (!fs::is_readable(fname.toFilesystemEncoding())) {
+               docstring const error = from_ascii(strerror(errno));
+               docstring const file = makeDisplayPath(fname.absFilename(), 50);
+               docstring const text =
+                 bformat(_("Could not read the specified document\n"
+                           "%1$s\ndue to the error: %2$s"), file, error);
+               Alert::error(_("Could not read file"), text);
+               return docstring();
+       }
+
+       ifstream ifs(fname.toFilesystemEncoding().c_str());
+       if (!ifs) {
+               docstring const error = from_ascii(strerror(errno));
+               docstring const file = makeDisplayPath(fname.absFilename(), 50);
+               docstring const text =
+                 bformat(_("Could not open the specified document\n"
+                           "%1$s\ndue to the error: %2$s"), file, error);
+               Alert::error(_("Could not open file"), text);
+               return docstring();
+       }
+
+       ifs.unsetf(std::ios::skipws);
+       istream_iterator<char> ii(ifs);
+       istream_iterator<char> end;
+#if !defined(USE_INCLUDED_STRING) && !defined(STD_STRING_IS_GOOD)
+       // We use this until the compilers get better...
+       std::vector<char> tmp;
+       copy(ii, end, back_inserter(tmp));
+       string const tmpstr(tmp.begin(), tmp.end());
+#else
+       // This is what we want to use and what we will use once the
+       // compilers get good enough.
+       //string tmpstr(ii, end); // yet a reason for using std::string
+       // alternate approach to get the file into a string:
+       string tmpstr;
+       copy(ii, end, back_inserter(tmpstr));
+#endif
+
+       // FIXME UNICODE: We don't know the encoding of the file
+       docstring file_content = from_utf8(tmpstr);
+       if (file_content.empty()) {
+               Alert::error(_("Reading not UTF-8 encoded file"),
+                            _("The file is not UTF-8 encoded.\n"
+                              "It will be read as local 8Bit-encoded.\n"
+                              "If this does not give the correct result\n"
+                              "then please change the encoding of the file\n"
+                              "to UTF-8 with a program other than LyX.\n"));
+               file_content = from_local8bit(tmpstr);
+       }
+
+       return normalize_c(file_content);
+}
+
+
+void BufferView::insertPlaintextFile(string const & f, bool asParagraph)
+{
+       docstring const tmpstr = contentsOfPlaintextFile(f, asParagraph);
+
+       if (tmpstr.empty())
+               return;
+
+       Cursor & cur = cursor();
+       cap::replaceSelection(cur);
+       recordUndo(cur);
+       if (asParagraph)
+               cur.innerText()->insertStringAsParagraphs(cur, tmpstr);
+       else
+               cur.innerText()->insertStringAsLines(cur, tmpstr);
+}
+
+
 } // namespace lyx