]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/GuiView.cpp
Use <cstdint> instead of <boost/cstdint.hpp>
[lyx.git] / src / frontends / qt4 / GuiView.cpp
index 104214ee43538d5b9fa37ccf4d6f4290567a165f..6e87f007f93151b27ab99bf45d5fce837529f9a4 100644 (file)
@@ -19,6 +19,7 @@
 #include "FileDialog.h"
 #include "FontLoader.h"
 #include "GuiApplication.h"
+#include "GuiClickableLabel.h"
 #include "GuiCommandBuffer.h"
 #include "GuiCompleter.h"
 #include "GuiKeySymbol.h"
@@ -51,7 +52,9 @@
 #include "FuncStatus.h"
 #include "FuncRequest.h"
 #include "Intl.h"
+#include "Language.h"
 #include "Layout.h"
+#include "LayoutFile.h"
 #include "Lexer.h"
 #include "LyXAction.h"
 #include "LyX.h"
 #include <QMovie>
 #include <QPainter>
 #include <QPixmap>
-#include <QPixmapCache>
 #include <QPoint>
 #include <QPushButton>
 #include <QScrollBar>
@@ -220,7 +222,7 @@ public:
                }
                // The longest line in the reference font (for English)
                // is 180. Calculate scale factor from that.
-               double const wscale = (180.0 / wline);
+               double const wscale = wline > 0 ? (180.0 / wline) : 1;
                // Now do the same for the height (necessary for condensed fonts)
                double const hscale = (34.0 / hline);
                // take the lower of the two scale factors.
@@ -274,7 +276,7 @@ private:
        }
 
        qreal fontSize() const {
-               return toqstr(lyxrc.font_sizes[FONT_SIZE_NORMAL]).toDouble();
+               return toqstr(lyxrc.font_sizes[NORMAL_SIZE]).toDouble();
        }
 
        QPointF textPosition(bool const heading) const {
@@ -609,7 +611,7 @@ GuiView::GuiView(int id)
        setAcceptDrops(true);
 
        // add busy indicator to statusbar
-       QLabel * busylabel = new QLabel(statusBar());
+       GuiClickableLabel * busylabel = new GuiClickableLabel(statusBar());
        statusBar()->addPermanentWidget(busylabel);
        search_mode mode = theGuiApp()->imageSearchMode();
        QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
@@ -622,6 +624,7 @@ GuiView::GuiView(int id)
                busylabel, SLOT(show()));
        connect(&d.processing_thread_watcher_, SIGNAL(finished()),
                busylabel, SLOT(hide()));
+       connect(busylabel, SIGNAL(clicked()), this, SLOT(checkCancelBackground()));
 
        QFontMetrics const fm(statusBar()->fontMetrics());
        int const iconheight = max(int(d.normalIconSize), fm.height());
@@ -712,6 +715,18 @@ void GuiView::disableShellEscape()
 }
 
 
+void GuiView::checkCancelBackground()
+{
+       docstring const ttl = _("Cancel Export?");
+       docstring const msg = _("Do you want to cancel the background export process?");
+       int const ret =
+               Alert::prompt(ttl, msg, 1, 1,
+                       _("&Cancel export"), _("Co&ntinue"));
+       if (ret == 0)
+               Systemcall::killscript();
+}
+
+
 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
 {
        QVector<GuiWorkArea*> areas;
@@ -779,7 +794,7 @@ void GuiView::processingThreadFinished()
        bool const error = (status != Buffer::ExportSuccess &&
                            status != Buffer::PreviewSuccess &&
                            status != Buffer::ExportCancel);
-       if (error) {
+       if (error && bv) {
                ErrorList & el = bv->buffer().errorList(d.last_export_format);
                // at this point, we do not know if buffer-view or
                // master-buffer-view was called. If there was an export error,
@@ -1991,6 +2006,7 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                }
                // fall through
        case LFUN_BUFFER_WRITE_AS:
+       case LFUN_BUFFER_WRITE_AS_TEMPLATE:
                enable = doc_buffer != 0;
                break;
 
@@ -2099,6 +2115,7 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                if (!doc_buffer)
                        enable = name == "aboutlyx"
                                || name == "file" //FIXME: should be removed.
+                               || name == "lyxfiles"
                                || name == "prefs"
                                || name == "texinfo"
                                || name == "progress"
@@ -2567,7 +2584,8 @@ void GuiView::importDocument(string const & argument)
 }
 
 
-void GuiView::newDocument(string const & filename, bool from_template)
+void GuiView::newDocument(string const & filename, string templatefile,
+                         bool from_template)
 {
        FileName initpath(lyxrc.document_path);
        if (documentBufferView()) {
@@ -2577,9 +2595,9 @@ void GuiView::newDocument(string const & filename, bool from_template)
                        initpath = trypath;
        }
 
-       string templatefile;
        if (from_template) {
-               templatefile = selectTemplateFile().absFileName();
+               if (templatefile.empty())
+                       templatefile =  selectTemplateFile().absFileName();
                if (templatefile.empty())
                        return;
        }
@@ -2601,7 +2619,7 @@ void GuiView::newDocument(string const & filename, bool from_template)
 }
 
 
-void GuiView::insertLyXFile(docstring const & fname)
+void GuiView::insertLyXFile(docstring const & fname, bool ignorelang)
 {
        BufferView * bv = documentBufferView();
        if (!bv)
@@ -2640,34 +2658,116 @@ void GuiView::insertLyXFile(docstring const & fname)
                }
        }
 
-       bv->insertLyXFile(filename);
+       bv->insertLyXFile(filename, ignorelang);
        bv->buffer().errors("Parse");
 }
 
 
+string const GuiView::getTemplatesPath(Buffer & b)
+{
+       // We start off with the user's templates path
+       string result = addPath(package().user_support().absFileName(), "templates");
+       // Check for the document language
+       string const langcode = b.params().language->code();
+       string const shortcode = langcode.substr(0, 2);
+       if (!langcode.empty() && shortcode != "en") {
+               string subpath = addPath(result, shortcode);
+               string subpath_long = addPath(result, langcode);
+               // If we have a subdirectory for the language already,
+               // navigate there
+               FileName sp = FileName(subpath);
+               if (sp.isDirectory())
+                       result = subpath;
+               else if (FileName(subpath_long).isDirectory())
+                       result = subpath_long;
+               else {
+                       // Ask whether we should create such a subdirectory
+                       docstring const text =
+                               bformat(_("It is suggested to save the template in a subdirectory\n"
+                                         "appropriate to the document language (%1$s).\n"
+                                         "This subdirectory does not exists yet.\n"
+                                         "Do you want to create it?"),
+                                       _(b.params().language->display()));
+                       if (Alert::prompt(_("Create Language Directory?"),
+                                         text, 0, 1, _("&Yes, Create"), _("&No, Save Template in Parent Directory")) == 0) {
+                               // If the user agreed, we try to create it and report if this failed.
+                               if (!sp.createDirectory(0777))
+                                       Alert::error(_("Subdirectory creation failed!"),
+                                                    _("Could not create subdirectory.\n"
+                                                      "The template will be saved in the parent directory."));
+                               else
+                                       result = subpath;
+                       }
+               }
+       }
+       // Do we have a layout category?
+       string const cat = b.params().baseClass() ?
+                               b.params().baseClass()->category()
+                             : string();
+       if (!cat.empty()) {
+               string subpath = addPath(result, cat);
+               // If we have a subdirectory for the category already,
+               // navigate there
+               FileName sp = FileName(subpath);
+               if (sp.isDirectory())
+                       result = subpath;
+               else {
+                       // Ask whether we should create such a subdirectory
+                       docstring const text =
+                               bformat(_("It is suggested to save the template in a subdirectory\n"
+                                         "appropriate to the layout category (%1$s).\n"
+                                         "This subdirectory does not exists yet.\n"
+                                         "Do you want to create it?"),
+                                       _(cat));
+                       if (Alert::prompt(_("Create Category Directory?"),
+                                         text, 0, 1, _("&Yes, Create"), _("&No, Save Template in Parent Directory")) == 0) {
+                               // If the user agreed, we try to create it and report if this failed.
+                               if (!sp.createDirectory(0777))
+                                       Alert::error(_("Subdirectory creation failed!"),
+                                                    _("Could not create subdirectory.\n"
+                                                      "The template will be saved in the parent directory."));
+                               else
+                                       result = subpath;
+                       }
+               }
+       }
+       return result;
+}
+
+
 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
 {
        FileName fname = b.fileName();
        FileName const oldname = fname;
+       bool const as_template = (kind == LV_WRITE_AS_TEMPLATE);
 
        if (!newname.empty()) {
                // FIXME UNICODE
-               fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
+               if (as_template)
+                       fname = support::makeAbsPath(to_utf8(newname), getTemplatesPath(b));
+               else
+                       fname = support::makeAbsPath(to_utf8(newname),
+                                                    oldname.onlyPath().absFileName());
        } else {
                // Switch to this Buffer.
                setBuffer(&b);
 
                // No argument? Ask user through dialog.
                // FIXME UNICODE
-               FileDialog dlg(qt_("Choose a filename to save document as"));
+               QString const title = as_template ? qt_("Choose a filename to save template as")
+                                                 : qt_("Choose a filename to save document as");
+               FileDialog dlg(title);
                dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
                dlg.setButton2(qt_("&Templates"), toqstr(lyxrc.template_path));
 
                if (!isLyXFileName(fname.absFileName()))
                        fname.changeExtension(".lyx");
 
+               string const path = as_template ?
+                                       getTemplatesPath(b)
+                                     : fname.onlyPath().absFileName();
                FileDialog::Result result =
-                       dlg.save(toqstr(fname.onlyPath().absFileName()),
+                       dlg.save(toqstr(path),
                                   QStringList(qt_("LyX Documents (*.lyx)")),
                                         toqstr(fname.onlyFileName()));
 
@@ -2759,6 +2859,7 @@ bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kin
                break;
        }
        case LV_WRITE_AS:
+       case LV_WRITE_AS_TEMPLATE:
                break;
        }
        // LyXVC created the file already in case of LV_VC_RENAME or
@@ -3044,35 +3145,51 @@ bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
 
 bool GuiView::closeBuffer(Buffer & buf)
 {
-       // If we are in a close_event all children will be closed in some time,
-       // so no need to do it here. This will ensure that the children end up
-       // in the session file in the correct order. If we close the master
-       // buffer, we can close or release the child buffers here too.
        bool success = true;
-       if (!closing_) {
-               ListOfBuffers clist = buf.getChildren();
-               ListOfBuffers::const_iterator it = clist.begin();
-               ListOfBuffers::const_iterator const bend = clist.end();
-               for (; it != bend; ++it) {
-                       Buffer * child_buf = *it;
-                       if (theBufferList().isOthersChild(&buf, child_buf)) {
-                               child_buf->setParent(0);
-                               continue;
-                       }
+       ListOfBuffers clist = buf.getChildren();
+       ListOfBuffers::const_iterator it = clist.begin();
+       ListOfBuffers::const_iterator const bend = clist.end();
+       for (; it != bend; ++it) {
+               Buffer * child_buf = *it;
+               if (theBufferList().isOthersChild(&buf, child_buf)) {
+                       child_buf->setParent(0);
+                       continue;
+               }
 
-                       // FIXME: should we look in other tabworkareas?
-                       // ANSWER: I don't think so. I've tested, and if the child is
-                       // open in some other window, it closes without a problem.
-                       GuiWorkArea * child_wa = workArea(*child_buf);
-                       if (child_wa) {
-                               success = closeWorkArea(child_wa, true);
-                               if (!success)
-                                       break;
-                       } else {
-                               // In this case the child buffer is open but hidden.
-                               // It therefore should not (MUST NOT) be dirty!
-                               LATTEST(child_buf->isClean());
+               // FIXME: should we look in other tabworkareas?
+               // ANSWER: I don't think so. I've tested, and if the child is
+               // open in some other window, it closes without a problem.
+               GuiWorkArea * child_wa = workArea(*child_buf);
+               if (child_wa) {
+                       if (closing_)
+                               // If we are in a close_event all children will be closed in some time,
+                               // so no need to do it here. This will ensure that the children end up
+                               // in the session file in the correct order. If we close the master
+                               // buffer, we can close or release the child buffers here too.
+                               continue;
+                       success = closeWorkArea(child_wa, true);
+                       if (!success)
+                               break;
+               } else {
+                       // In this case the child buffer is open but hidden.
+                       // Even in this case, children can be dirty (e.g.,
+                       // after a label change in the master, see #11405).
+                       // Therefore, check this
+                       if (closing_ && (child_buf->isClean() || child_buf->paragraphs().empty()))
+                               // If we are in a close_event all children will be closed in some time,
+                               // so no need to do it here. This will ensure that the children end up
+                               // in the session file in the correct order. If we close the master
+                               // buffer, we can close or release the child buffers here too.
+                               continue;
+                       // Save dirty buffers also if closing_!
+                       if (saveBufferIfNeeded(*child_buf, false)) {
+                               child_buf->removeAutosaveFile();
                                theBufferList().release(child_buf);
+                       } else {
+                               // Saving of dirty children has been cancelled.
+                               // Cancel the whole process.
+                               success = false;
+                               break;
                        }
                }
        }
@@ -3656,10 +3773,10 @@ bool GuiView::GuiViewPrivate::asyncBufferProcessing(
 #if EXPORT_in_THREAD
        if (allow_async) {
                GuiViewPrivate::busyBuffers.insert(used_buffer);
-               Buffer * cloned_buffer = used_buffer->cloneFromMaster();
+               Buffer * cloned_buffer = used_buffer->cloneWithChildren();
                if (!cloned_buffer) {
                        Alert::error(_("Export Error"),
-                                                                        _("Error cloning the Buffer."));
+                                    _("Error cloning the Buffer."));
                        return false;
                }
                QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
@@ -3709,15 +3826,21 @@ void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
 
        // Let the current BufferView dispatch its own actions.
        bv->dispatch(cmd, dr);
-       if (dr.dispatched())
+       if (dr.dispatched()) {
+               if (cmd.action() == LFUN_REDO || cmd.action() == LFUN_UNDO)
+                       updateDialog("document", "");
                return;
+       }
 
        // Try with the document BufferView dispatch if any.
        BufferView * doc_bv = documentBufferView();
        if (doc_bv && doc_bv != bv) {
                doc_bv->dispatch(cmd, dr);
-               if (dr.dispatched())
+               if (dr.dispatched()) {
+                       if (cmd.action() == LFUN_REDO || cmd.action() == LFUN_UNDO)
+                               updateDialog("document", "");
                        return;
+               }
        }
 
        // Then let the current Cursor dispatch its own actions.
@@ -3940,9 +4063,13 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                                menu->exec(QCursor::pos());
                        break;
 
-               case LFUN_FILE_INSERT:
-                       insertLyXFile(cmd.argument());
+               case LFUN_FILE_INSERT: {
+                       if (cmd.getArg(1) == "ignorelang")
+                               insertLyXFile(from_utf8(cmd.getArg(0)), true);
+                       else
+                               insertLyXFile(cmd.argument());
                        break;
+               }
 
                case LFUN_FILE_INSERT_PLAINTEXT:
                case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
@@ -4019,6 +4146,12 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        renameBuffer(*doc_buffer, cmd.argument());
                        break;
 
+               case LFUN_BUFFER_WRITE_AS_TEMPLATE:
+                       LASSERT(doc_buffer, break);
+                       renameBuffer(*doc_buffer, cmd.argument(),
+                                    LV_WRITE_AS_TEMPLATE);
+                       break;
+
                case LFUN_BUFFER_WRITE_ALL: {
                        Buffer * first = theBufferList().first();
                        if (!first)
@@ -4287,9 +4420,6 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        dr.setMessage(bformat(_("Zoom level is now %1$d% (default value: %2$d%)"),
                                              lyxrc.currentZoom, lyxrc.defaultZoom));
 
-                       // The global QPixmapCache is used in GuiPainter to cache text
-                       // painting so we must reset it.
-                       QPixmapCache::clear();
                        guiApp->fontLoader().update();
                        dr.screenUpdate(Update::Force | Update::FitCursor);
                        break;
@@ -4527,7 +4657,7 @@ char const * const dialognames[] = {
 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
 "href", "include", "index", "index_print", "info", "listings", "label", "line",
-"log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
+"log", "lyxfiles", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
@@ -4617,7 +4747,7 @@ void GuiView::doShowDialog(QString const & qname, QString const & qdata,
                if (dialog) {
                        bool const visible = dialog->isVisibleView();
                        dialog->showData(sdata);
-                       if (inset && currentBufferView())
+                       if (currentBufferView())
                                currentBufferView()->editInset(name, inset);
                        // We only set the focus to the new dialog if it was not yet
                        // visible in order not to change the existing previous behaviour
@@ -4723,6 +4853,7 @@ Dialog * createGuiInclude(GuiView & lv);
 Dialog * createGuiIndex(GuiView & lv);
 Dialog * createGuiListings(GuiView & lv);
 Dialog * createGuiLog(GuiView & lv);
+Dialog * createGuiLyXFiles(GuiView & lv);
 Dialog * createGuiMathMatrix(GuiView & lv);
 Dialog * createGuiNote(GuiView & lv);
 Dialog * createGuiParagraph(GuiView & lv);
@@ -4793,6 +4924,8 @@ Dialog * GuiView::build(string const & name)
                return createGuiListings(*this);
        if (name == "log")
                return createGuiLog(*this);
+       if (name == "lyxfiles")
+               return createGuiLyXFiles(*this);
        if (name == "mathdelimiter")
                return createGuiDelimiter(*this);
        if (name == "mathmatrix")