X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Ffrontends%2Fqt4%2FGuiView.cpp;h=3eda5409a27cd6e0f4382abdb946e9e009ce7a4e;hb=4985015e8939df78a8d29c31aa04bc28caa3a47f;hp=28e6fcf00072f7b10671bcf389e7d387dead0f0c;hpb=d0933f4bd9a39292f97175170b8f90fcbfb31415;p=lyx.git diff --git a/src/frontends/qt4/GuiView.cpp b/src/frontends/qt4/GuiView.cpp index 28e6fcf000..3eda5409a2 100644 --- a/src/frontends/qt4/GuiView.cpp +++ b/src/frontends/qt4/GuiView.cpp @@ -88,9 +88,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -134,6 +136,11 @@ using namespace std; using namespace lyx::support; namespace lyx { + +using support::addExtension; +using support::changeExtension; +using support::removeExtension; + namespace frontend { namespace { @@ -158,9 +165,8 @@ public: font.setStyleHint(QFont::SansSerif); font.setWeight(QFont::Bold); font.setPointSize(int(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble())); - int width = QFontMetrics(font).width(text); pain.setFont(font); - pain.drawText(397 - width, 15, text); + pain.drawText(190, 225, text); setFocusPolicy(Qt::StrongFocus); } @@ -205,10 +211,23 @@ struct GuiView::GuiViewPrivate in_show_(false) { // hardcode here the platform specific icon size - smallIconSize = 14; // scaling problems - normalIconSize = 20; // ok, default + smallIconSize = 16; // scaling problems + normalIconSize = 20; // ok, default if iconsize.png is missing bigIconSize = 26; // better for some math icons + // if it exists, use width of iconsize.png as normal size + QString const dir = toqstr(addPath("images", lyxrc.icon_set)); + FileName const fn = lyx::libFileSearch(dir, "iconsize.png"); + if (!fn.empty()) { + QImage image(toqstr(fn.absFileName())); + if (image.width() < int(smallIconSize)) + normalIconSize = smallIconSize; + else if (image.width() > int(bigIconSize)) + normalIconSize = bigIconSize; + else + normalIconSize = image.width(); + } + splitter_ = new QSplitter; bg_widget_ = new BackgroundWidget; stack_widget_ = new QStackedWidget; @@ -311,8 +330,20 @@ struct GuiView::GuiViewPrivate return tabWorkArea(0); } + int countWorkAreasOf(Buffer & buf) + { + int areas = tabWorkAreaCount(); + int count = 0; + for (int i = 0; i != areas; ++i) { + TabWorkArea * twa = tabWorkArea(i); + if (twa->workArea(buf)) + ++count; + } + return count; + } + #if (QT_VERSION >= 0x040400) - void setPreviewFuture(QFuture const & f) + void setPreviewFuture(QFuture const & f) { if (processing_thread_watcher_.isRunning()) { // we prefer to cancel this preview in order to keep a snappy @@ -363,34 +394,32 @@ public: #if (QT_VERSION >= 0x040400) /// QFutureWatcher autosave_watcher_; - QFutureWatcher processing_thread_watcher_; + QFutureWatcher processing_thread_watcher_; /// string last_export_format; + string processing_format; #else struct DummyWatcher { bool isRunning(){return false;} }; DummyWatcher processing_thread_watcher_; #endif static QSet busyBuffers; - static docstring previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format); - static docstring exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format); - static docstring compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format); + static Buffer::ExportStatus previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format); + static Buffer::ExportStatus exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format); + static Buffer::ExportStatus compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format); static docstring autosaveAndDestroy(Buffer const * orig, Buffer * buffer); template - static docstring runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format, string const & msg); + static Buffer::ExportStatus runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format); // TODO syncFunc/previewFunc: use bind bool asyncBufferProcessing(string const & argument, Buffer const * used_buffer, docstring const & msg, - docstring (*asyncFunc)(Buffer const *, Buffer *, string const &), - bool (Buffer::*syncFunc)(string const &, bool, bool) const, - bool (Buffer::*previewFunc)(string const &, bool) const); + Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &), + Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const, + Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const); - QTimer processing_cursor_timer_; - bool indicates_processing_; - QMap orig_cursors_; QVector guiWorkAreas(); }; @@ -427,11 +456,18 @@ GuiView::GuiView(int id) setAttribute(Qt::WA_DeleteOnClose, true); #if (!defined(Q_WS_WIN) && !defined(Q_WS_MACX)) + // QIcon::fromTheme was introduced in Qt 4.6 +#if (QT_VERSION >= 0x040600) // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac, - // since the icon is provided in the application bundle. + // since the icon is provided in the application bundle. We use a themed + // version when available and use the bundled one as fallback. + setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "png"))); +#else setWindowIcon(getPixmap("images/", "lyx", "png")); #endif +#endif + #if (QT_VERSION >= 0x040300) // use tabbed dock area for multiple docks // (such as "source" and "messages") @@ -441,18 +477,33 @@ GuiView::GuiView(int id) // For Drag&Drop. setAcceptDrops(true); +#if (QT_VERSION >= 0x040400) + + // add busy indicator to statusbar + QLabel * busylabel = new QLabel(statusBar()); + statusBar()->addPermanentWidget(busylabel); + QString fn = toqstr(lyx::libFileSearch("images", "busy.gif").absFileName()); + QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel); + busylabel->setMovie(busyanim); + busyanim->start(); + busylabel->hide(); + + connect(&d.processing_thread_watcher_, SIGNAL(started()), + busylabel, SLOT(show())); + connect(&d.processing_thread_watcher_, SIGNAL(finished()), + busylabel, SLOT(hide())); + statusBar()->setSizeGripEnabled(true); updateStatusBar(); -#if (QT_VERSION >= 0x040400) connect(&d.autosave_watcher_, SIGNAL(finished()), this, SLOT(autoSaveThreadFinished())); + + connect(&d.processing_thread_watcher_, SIGNAL(started()), this, + SLOT(processingThreadStarted())); connect(&d.processing_thread_watcher_, SIGNAL(finished()), this, SLOT(processingThreadFinished())); - d.processing_cursor_timer_.setInterval(1000 * 3); - connect(&d.processing_cursor_timer_, SIGNAL(timeout()), this, - SLOT(indicateProcessing())); #endif connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)), @@ -496,114 +547,75 @@ QVector GuiView::GuiViewPrivate::guiWorkAreas() return areas; } - -#if QT_VERSION >= 0x040400 -void GuiView::setCursorShapes(Qt::CursorShape shape) +static void handleExportStatus(GuiView * view, Buffer::ExportStatus status, + string const & format) { - QVector areas = d.guiWorkAreas(); - Q_FOREACH(GuiWorkArea* wa, areas) { - wa->setCursorShape(shape); + docstring const fmt = formats.prettyName(format); + docstring msg; + switch (status) { + case Buffer::ExportSuccess: + msg = bformat(_("Successful export to format: %1$s"), fmt); + break; + case Buffer::ExportCancel: + msg = _("Document export cancelled."); + break; + case Buffer::ExportError: + case Buffer::ExportNoPathToFormat: + case Buffer::ExportTexPathHasSpaces: + case Buffer::ExportConverterError: + msg = bformat(_("Error while exporting format: %1$s"), fmt); + break; + case Buffer::PreviewSuccess: + msg = bformat(_("Successful preview of format: %1$s"), fmt); + break; + case Buffer::PreviewError: + msg = bformat(_("Error while previewing format: %1$s"), fmt); + break; } + view->message(msg); } -void GuiView::restoreCursorShapes() -{ - QVector areas = d.guiWorkAreas(); - Q_FOREACH(GuiWorkArea* wa, areas) { - if (d.orig_cursors_.contains(wa)) { - wa->setCursorShape(d.orig_cursors_[wa]); - } - } -} - +#if QT_VERSION >= 0x040400 -void GuiView::saveCursorShapes() +void GuiView::processingThreadStarted() { - d.orig_cursors_.clear(); - QVector areas = d.guiWorkAreas(); - Q_FOREACH(GuiWorkArea* wa, areas) { - d.orig_cursors_[wa] = wa->cursorShape(); - } } -void GuiView::indicateProcessing() +void GuiView::processingThreadFinished() { - if (d.indicates_processing_) { - restoreCursorShapes(); - } else { - setCursorShapes(Qt::BusyCursor); - } - d.indicates_processing_ = !d.indicates_processing_; -} + QFutureWatcher const * watcher = + static_cast const *>(sender()); - -void GuiView::processingThreadStarted() -{ - saveCursorShapes(); - d.indicates_processing_ = false; - indicateProcessing(); - d.processing_cursor_timer_.start(); + Buffer::ExportStatus const status = watcher->result(); + handleExportStatus(this, status, d.processing_format); + + updateToolbars(); + BufferView const * const bv = currentBufferView(); + if (bv && !bv->buffer().errorList("Export").empty()) { + errors("Export"); + return; + } + errors(d.last_export_format); } -void GuiView::processingThreadFinished(bool show_errors) +void GuiView::autoSaveThreadFinished() { QFutureWatcher const * watcher = static_cast const *>(sender()); message(watcher->result()); updateToolbars(); - if (show_errors) { - errors(d.last_export_format); - } - d.processing_cursor_timer_.stop(); - restoreCursorShapes(); - d.indicates_processing_ = false; -} - -void GuiView::processingThreadFinished() -{ - processingThreadFinished(true); -} - -void GuiView::autoSaveThreadFinished() -{ - processingThreadFinished(false); } #else -void GuiView::setCursorShapes(Qt::CursorShape) -{ -} - - -void GuiView::restoreCursorShapes() -{ -} - - -void GuiView::saveCursorShapes() -{ -} - - -void GuiView::indicateProcessing() -{ -} - - void GuiView::processingThreadStarted() { } -void GuiView::processingThreadFinished(bool) -{ -} - - void GuiView::processingThreadFinished() { } @@ -631,6 +643,19 @@ void GuiView::saveLayout() const } +void GuiView::saveUISettings() const +{ + // Save the toolbar private states + ToolbarMap::iterator end = d.toolbars_.end(); + for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it) + it->second->saveSession(); + // Now take care of all other dialogs + map::const_iterator it = d.dialogs_.begin(); + for (; it!= d.dialogs_.end(); ++it) + it->second->saveSession(); +} + + bool GuiView::restoreLayout() { QSettings settings; @@ -641,7 +666,16 @@ bool GuiView::restoreLayout() return false; //code below is skipped when when ~/.config/LyX is (re)created - setIconSize(settings.value(icon_key).toSize()); + QSize icon_size = settings.value(icon_key).toSize(); + // Check whether session size changed. + if (icon_size.width() != int(d.smallIconSize) && + icon_size.width() != int(d.normalIconSize) && + icon_size.width() != int(d.bigIconSize)) { + icon_size.setWidth(d.normalIconSize); + icon_size.setHeight(d.normalIconSize); + } + setIconSize(icon_size); + #ifdef Q_WS_X11 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint(); QSize size = settings.value("size", QSize(690, 510)).toSize(); @@ -668,6 +702,8 @@ bool GuiView::restoreLayout() dialog->prepareView(); if ((dialog = findOrBuild("progress", true))) dialog->prepareView(); + if ((dialog = findOrBuild("findreplaceadv", true))) + dialog->prepareView(); if (!restoreState(settings.value("layout").toByteArray(), 0)) initToolbars(); @@ -780,10 +816,10 @@ void GuiView::focusInEvent(QFocusEvent * e) QMainWindow::focusInEvent(e); // Make sure guiApp points to the correct view. guiApp->setCurrentView(this); - if (currentMainWorkArea()) - currentMainWorkArea()->setFocus(); - else if (currentWorkArea()) + if (currentWorkArea()) currentWorkArea()->setFocus(); + else if (currentMainWorkArea()) + currentMainWorkArea()->setFocus(); else d.bg_widget_->setFocus(); } @@ -805,6 +841,7 @@ void GuiView::showEvent(QShowEvent * e) // No work area, switch to the background widget. d.setBackground(); + updateToolbars(); QMainWindow::showEvent(e); } @@ -862,16 +899,8 @@ void GuiView::closeEvent(QCloseEvent * close_event) // Saving fullscreen requires additional tweaks in the toolbar code. // It wouldn't also work under linux natively. if (lyxrc.allow_geometry_session) { - // Save this window geometry and layout. saveLayout(); - // Then the toolbar private states. - ToolbarMap::iterator end = d.toolbars_.end(); - for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it) - it->second->saveSession(); - // Now take care of all other dialogs: - map::const_iterator it = d.dialogs_.begin(); - for (; it!= d.dialogs_.end(); ++it) - it->second->saveSession(); + saveUISettings(); } close_event->accept(); @@ -909,7 +938,7 @@ void GuiView::dropEvent(QDropEvent * event) = theConverters().importableFormats(); vector::const_iterator it = import_formats.begin(); for (; it != import_formats.end(); ++it) - if ((*it)->extension() == ext) + if ((*it)->hasExtension(ext)) found_formats.push_back(*it); FuncRequest cmd; @@ -1005,6 +1034,9 @@ void GuiView::updateWindowTitle(GuiWorkArea * wa) void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa) { + if (d.current_work_area_) + QObject::disconnect(d.current_work_area_, SIGNAL(busy(bool)), + this, SLOT(setBusy(bool))); disconnectBuffer(); disconnectBufferView(); connectBufferView(wa->bufferView()); @@ -1012,6 +1044,7 @@ void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa) d.current_work_area_ = wa; QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)), this, SLOT(updateWindowTitle(GuiWorkArea *))); + QObject::connect(wa, SIGNAL(busy(bool)), this, SLOT(setBusy(bool))); updateWindowTitle(wa); structureChanged(); @@ -1184,18 +1217,12 @@ void GuiView::setBusy(bool busy) // busy state didn't change return; - if (d.current_work_area_) { - d.current_work_area_->setUpdatesEnabled(!busy); - if (busy) - d.current_work_area_->stopBlinkingCursor(); - else - d.current_work_area_->startBlinkingCursor(); - } - - if (busy) + if (busy) { QApplication::setOverrideCursor(Qt::WaitCursor); - else - QApplication::restoreOverrideCursor(); + return; + } + QApplication::restoreOverrideCursor(); + updateLayoutList(); } @@ -1390,12 +1417,14 @@ void GuiView::updateToolbars() lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true); bool const mathmacrotemplate = lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled(); + bool const ipa = + lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled(); for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it) - it->second->update(math, table, review, mathmacrotemplate); + it->second->update(math, table, review, mathmacrotemplate, ipa); } else for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it) - it->second->update(false, false, false, false); + it->second->update(false, false, false, false, false); } @@ -1403,11 +1432,12 @@ void GuiView::setBuffer(Buffer * newBuffer) { LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl); LASSERT(newBuffer, return); - setBusy(true); - + GuiWorkArea * wa = workArea(*newBuffer); if (wa == 0) { + setBusy(true); newBuffer->masterBuffer()->updateBuffer(); + setBusy(false); wa = addWorkArea(*newBuffer); // scroll to the position when the BufferView was last closed if (lyxrc.use_lastfilepos) { @@ -1422,8 +1452,6 @@ void GuiView::setBuffer(Buffer * newBuffer) connectBuffer(*newBuffer); connectBufferView(wa->bufferView()); setCurrentWorkArea(wa); - - setBusy(false); } @@ -1459,9 +1487,20 @@ void GuiView::errors(string const & error_type, bool from_master) if (!bv) return; - ErrorList & el = from_master ? +#if EXPORT_in_THREAD && (QT_VERSION >= 0x040400) + // We are called with from_master == false by default, so we + // have to figure out whether that is the case or not. + ErrorList & el = bv->buffer().errorList(error_type); + if (el.empty()) { + el = bv->buffer().masterBuffer()->errorList(error_type); + from_master = true; + } +#else + ErrorList const & el = from_master ? bv->buffer().masterBuffer()->errorList(error_type) : bv->buffer().errorList(error_type); +#endif + if (el.empty()) return; @@ -1532,10 +1571,10 @@ BufferView const * GuiView::currentBufferView() const #if (QT_VERSION >= 0x040400) docstring GuiView::GuiViewPrivate::autosaveAndDestroy( - Buffer const * orig, Buffer * buffer) + Buffer const * orig, Buffer * clone) { - bool const success = buffer->autoSave(); - delete buffer; + bool const success = clone->autoSave(); + delete clone; busyBuffers.remove(orig); return success ? _("Automatic save done.") @@ -1550,13 +1589,15 @@ void GuiView::autoSave() Buffer * buffer = documentBufferView() ? &documentBufferView()->buffer() : 0; - if (!buffer) + if (!buffer) { + resetAutosaveTimers(); return; + } #if (QT_VERSION >= 0x040400) GuiViewPrivate::busyBuffers.insert(buffer); QFuture f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy, - buffer, buffer->clone()); + buffer, buffer->cloneBufferOnly()); d.autosave_watcher_.setFuture(f); #else buffer->autoSave(); @@ -1614,8 +1655,8 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag) } string format = to_utf8(cmd.argument()); if (cmd.argument().empty()) - format = doc_buffer->getDefaultOutputFormat(); - enable = doc_buffer->isExportableFormat(format); + format = doc_buffer->params().getDefaultOutputFormat(); + enable = doc_buffer->params().isExportableFormat(format); break; } @@ -1654,10 +1695,12 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag) } case LFUN_BUFFER_WRITE_AS: + case LFUN_BUFFER_EXPORT_AS: enable = doc_buffer; break; case LFUN_BUFFER_CLOSE: + case LFUN_VIEW_CLOSE: enable = doc_buffer; break; @@ -1675,7 +1718,7 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag) break; case LFUN_CLOSE_TAB_GROUP: - enable = d.currentTabWorkArea(); + enable = d.tabWorkAreaCount() > 1; break; case LFUN_TOOLBAR_TOGGLE: { @@ -1719,22 +1762,22 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag) || name == "progress" || name == "compare"; else if (name == "print") - enable = doc_buffer->isExportable("dvi") + enable = doc_buffer->params().isExportable("dvi") && lyxrc.print_command != "none"; else if (name == "character" || name == "symbols") { if (!buf || buf->isReadonly()) enable = false; else { - // FIXME we should consider passthru - // paragraphs too. - Inset const & in = currentBufferView()->cursor().inset(); - enable = !in.getLayout().isPassThru(); + Cursor const & cur = currentBufferView()->cursor(); + enable = !(cur.inTexted() && cur.paragraph().isPassThru()); } } else if (name == "latexlog") enable = FileName(doc_buffer->logName()).isReadableFile(); else if (name == "spellchecker") - enable = theSpellChecker() && !doc_buffer->isReadonly(); + enable = theSpellChecker() + && !doc_buffer->isReadonly() + && !doc_buffer->text().empty(); else if (name == "vclog") enable = doc_buffer->lyxvc().inUse(); break; @@ -1849,6 +1892,11 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag) enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty()); break; + case LFUN_FILE_INSERT_PLAINTEXT: + case LFUN_FILE_INSERT_PLAINTEXT_PARA: + enable = documentBufferView() && documentBufferView()->cursor().inTexted(); + break; + default: return false; } @@ -1888,20 +1936,19 @@ Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles) setBusy(false); throw(e); } + setBusy(false); if (!newBuffer) { message(_("Document not loaded.")); - setBusy(false); return 0; } - newBuffer->errors("Parse"); setBuffer(newBuffer); + newBuffer->errors("Parse"); if (tolastfiles) theSession().lastFiles().add(filename); - setBusy(false); return newBuffer; } @@ -1995,8 +2042,9 @@ static bool import(GuiView * lv, FileName const & filename, string loader_format; vector loaders = theConverters().loaders(); if (find(loaders.begin(), loaders.end(), format) == loaders.end()) { - for (vector::const_iterator it = loaders.begin(); - it != loaders.end(); ++it) { + vector::const_iterator it = loaders.begin(); + vector::const_iterator en = loaders.end(); + for (; it != en; ++it) { if (!theConverters().isReachable(format, *it)) continue; @@ -2067,10 +2115,10 @@ void GuiView::importDocument(string const & argument) toqstr(addPath(package().system_support().absFileName(), "examples"))); docstring filter = formats.prettyName(format); - filter += " (*."; + filter += " (*.{"; // FIXME UNICODE - filter += from_utf8(formats.extension(format)); - filter += ')'; + filter += from_utf8(formats.extensions(format)); + filter += "})"; FileDialog::Result result = dlg.open(toqstr(initpath), fileFilters(toqstr(filter))); @@ -2211,49 +2259,6 @@ void GuiView::insertLyXFile(docstring const & fname) } -void GuiView::insertPlaintextFile(docstring const & fname, - bool asParagraph) -{ - BufferView * bv = documentBufferView(); - if (!bv) - return; - - if (!fname.empty() && !FileName::isAbsolute(to_utf8(fname))) { - message(_("Absolute filename expected.")); - return; - } - - // FIXME UNICODE - FileName filename(to_utf8(fname)); - - if (!filename.empty()) { - bv->insertPlaintextFile(filename, asParagraph); - return; - } - - FileDialog dlg(qt_("Select file to insert"), (asParagraph ? - LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT)); - - FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()), - QStringList(qt_("All Files (*)"))); - - if (result.first == FileDialog::Later) - return; - - // FIXME UNICODE - filename.set(fromqstr(result.second)); - - // check selected filename - if (filename.empty()) { - // emit message signal. - message(_("Canceled.")); - return; - } - - bv->insertPlaintextFile(filename, asParagraph); -} - - bool GuiView::renameBuffer(Buffer & b, docstring const & newname) { FileName fname = b.fileName(); @@ -2294,9 +2299,29 @@ bool GuiView::renameBuffer(Buffer & b, docstring const & newname) } // fname is now the new Buffer location. + + // if there is already a Buffer open with this name, we do not want + // to have another one. (the second test makes sure we're not just + // trying to overwrite ourselves, which is fine.) + if (theBufferList().exists(fname) && fname != oldname + && theBufferList().getBuffer(fname) != &b) { + docstring const text = + bformat(_("The file\n%1$s\nis already open in your current session.\n" + "Please close it before attempting to overwrite it.\n" + "Do you want to choose a new filename?"), + from_utf8(fname.absFileName())); + int const ret = Alert::prompt(_("Chosen File Already Open"), + text, 0, 1, _("&Rename"), _("&Cancel")); + switch (ret) { + case 0: return renameBuffer(b, docstring()); + case 1: return false; + } + //return false; + } + if (FileName(fname).exists()) { docstring const file = makeDisplayPath(fname.absFileName(), 30); - docstring text = bformat(_("The document %1$s already " + docstring const text = bformat(_("The document %1$s already " "exists.\n\nDo you want to " "overwrite that document?"), file); @@ -2309,7 +2334,85 @@ bool GuiView::renameBuffer(Buffer & b, docstring const & newname) } } - return saveBuffer(b, fname); + bool const saved = saveBuffer(b, fname); + if (saved) + b.reload(false); + return saved; +} + + +struct PrettyNameComparator +{ + bool operator()(Format const *first, Format const *second) const { + return compare_ascii_no_case(first->prettyname(), second->prettyname()) <= 0; + } +}; + + +bool GuiView::exportBufferAs(Buffer & b) +{ + FileName fname = b.fileName(); + + FileDialog dlg(qt_("Choose a filename to export the document as")); + dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path)); + + QStringList types; + types << "Any supported format (*.*)"; + Formats::const_iterator it = formats.begin(); + vector export_formats; + for (; it != formats.end(); ++it) + if (it->documentFormat() && it->inExportMenu()) + export_formats.push_back(&(*it)); + PrettyNameComparator cmp; + sort(export_formats.begin(), export_formats.end(), cmp); + vector::const_iterator fit = export_formats.begin(); + for (; fit != export_formats.end(); ++fit) + types << toqstr((*fit)->prettyname() + " (*." + (*fit)->extension() + ")"); + QString filter; + FileDialog::Result result = + dlg.save(toqstr(fname.onlyPath().absFileName()), + types, + toqstr(fname.onlyFileName()), + &filter); + if (result.first != FileDialog::Chosen) + return false; + + string s = fromqstr(filter); + size_t pos = s.find(" (*."); + LASSERT(pos != string::npos, /**/); + string fmt_prettyname = s.substr(0, pos); + string fmt_name; + fname.set(fromqstr(result.second)); + if (fmt_prettyname == "Any supported format") + fmt_name = formats.getFormatFromExtension(fname.extension()); + else + fmt_name = formats.getFormatFromPrettyName(fmt_prettyname); + LYXERR(Debug::FILES, "fmt_prettyname=" << fmt_prettyname + << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName()); + + if (fmt_name.empty() || fname.empty()) + return false; + + // fname is now the new Buffer location. + if (FileName(fname).exists()) { + docstring const file = makeDisplayPath(fname.absFileName(), 30); + docstring text = bformat(_("The document %1$s already " + "exists.\n\nDo you want to " + "overwrite that document?"), + file); + int const ret = Alert::prompt(_("Overwrite document?"), + text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel")); + switch (ret) { + case 0: break; + case 1: return exportBufferAs(b); + case 2: return false; + } + } + + FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName()); + DispatchResult dr; + dispatch(cmd, dr); + return dr.dispatched(); } @@ -2368,10 +2471,45 @@ bool GuiView::hideWorkArea(GuiWorkArea * wa) } +// We only want to close the buffer if it is not visible in other workareas +// of the same view, nor in other views, and if this is not a child bool GuiView::closeWorkArea(GuiWorkArea * wa) { Buffer & buf = wa->bufferView().buffer(); - return closeWorkArea(wa, !buf.parent()); + + bool last_wa = d.countWorkAreasOf(buf) == 1 + && !inOtherView(buf) && !buf.parent(); + + bool close_buffer = last_wa; + + if (last_wa) { + if (lyxrc.close_buffer_with_last_view == "yes") + ; // Nothing to do + else if (lyxrc.close_buffer_with_last_view == "no") + close_buffer = false; + else { + docstring file; + if (buf.isUnnamed()) + file = from_utf8(buf.fileName().onlyFileName()); + else + file = buf.fileName().displayName(30); + docstring const text = bformat( + _("Last view on document %1$s is being closed.\n" + "Would you like to close or hide the document?\n" + "\n" + "Hidden documents can be displayed back through\n" + "the menu: View->Hidden->...\n" + "\n" + "To remove this question, set your preference in:\n" + " Tools->Preferences->Look&Feel->UserInterface\n" + ), file); + int ret = Alert::prompt(_("Close or hide document?"), + text, 0, 1, _("&Close"), _("&Hide")); + close_buffer = (ret == 0); + } + } + + return closeWorkArea(wa, close_buffer); } @@ -2508,11 +2646,9 @@ bool GuiView::closeBuffer(Buffer & buf) return true; } } - // open all children again to avoid a crash (bug 6603) - // FIXME updateMacros() does more than needed - buf.updateMacros(); - // get rid of dangling inset pointers in TOC (bug 6603) - buf.tocBackend().update(); + // open all children again to avoid a crash because of dangling + // pointers (bug 6603) + buf.updateBuffer(); return false; } @@ -2529,7 +2665,7 @@ bool GuiView::closeTabWorkArea(TabWorkArea * twa) // in another view, and if this is not a child and if we are closing // a view (not a tabgroup). bool const close_buffer = - !inMultiViews(wa) && !b.parent() && closing_; + !inOtherView(b) && !b.parent() && closing_; if (!closeWorkArea(wa, close_buffer)) return false; @@ -2605,17 +2741,15 @@ bool GuiView::inMultiTabs(GuiWorkArea * wa) if (wa_ && wa_ != wa) return true; } - return inMultiViews(wa); + return inOtherView(buf); } -bool GuiView::inMultiViews(GuiWorkArea * wa) +bool GuiView::inOtherView(Buffer & buf) { QList const ids = guiApp->viewIds(); - Buffer & buf = wa->bufferView().buffer(); - int found_twa = 0; - for (int i = 0; i != ids.size() && found_twa <= 1; ++i) { + for (int i = 0; i != ids.size(); ++i) { if (id_ == ids[i]) continue; @@ -2935,6 +3069,13 @@ bool GuiView::goToFileRow(string const & argument) return false; } } + if (!buf) { + message(bformat( + _("No buffer for file: %1$s."), + makeDisplayPath(file_name)) + ); + return false; + } setBuffer(buf); documentBufferView()->setCursorFromRow(row); return true; @@ -2943,67 +3084,61 @@ bool GuiView::goToFileRow(string const & argument) #if (QT_VERSION >= 0x040400) template -docstring GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format, string const & msg) +Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format) { - bool const update_unincluded = - buffer->params().maintain_unincluded_children - && !buffer->params().getIncludedChildren().empty(); - bool const success = func(format, update_unincluded); - delete buffer; + Buffer::ExportStatus const status = func(format); + + // the cloning operation will have produced a clone of the entire set of + // documents, starting from the master. so we must delete those. + Buffer * mbuf = const_cast(clone->masterBuffer()); + delete mbuf; busyBuffers.remove(orig); - if (msg == "preview") { - return success - ? bformat(_("Successful preview of format: %1$s"), from_utf8(format)) - : bformat(_("Error while previewing format: %1$s"), from_utf8(format)); - } - return success - ? bformat(_("Successful export to format: %1$s"), from_utf8(format)) - : bformat(_("Error while exporting format: %1$s"), from_utf8(format)); + return status; } -docstring GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format) +Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format) { - bool (Buffer::* mem_func)(std::string const &, bool, bool) const = &Buffer::doExport; - return runAndDestroy(bind(mem_func, buffer, _1, true, _2), orig, buffer, format, "export"); + Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport; + return runAndDestroy(bind(mem_func, clone, _1, true), orig, clone, format); } -docstring GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format) +Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format) { - bool (Buffer::* mem_func)(std::string const &, bool, bool) const = &Buffer::doExport; - return runAndDestroy(bind(mem_func, buffer, _1, false, _2), orig, buffer, format, "export"); + Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport; + return runAndDestroy(bind(mem_func, clone, _1, false), orig, clone, format); } -docstring GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format) +Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format) { - bool(Buffer::* mem_func)(std::string const &, bool) const = &Buffer::preview; - return runAndDestroy(bind(mem_func, buffer, _1, _2), orig, buffer, format, "preview"); + Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview; + return runAndDestroy(bind(mem_func, clone, _1), orig, clone, format); } #else // not used, but the linker needs them -docstring GuiView::GuiViewPrivate::compileAndDestroy( +Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy( Buffer const *, Buffer *, string const &) { - return docstring(); + return Buffer::ExportSuccess; } -docstring GuiView::GuiViewPrivate::exportAndDestroy( +Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy( Buffer const *, Buffer *, string const &) { - return docstring(); + return Buffer::ExportSuccess; } -docstring GuiView::GuiViewPrivate::previewAndDestroy( +Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy( Buffer const *, Buffer *, string const &) { - return docstring(); + return Buffer::ExportSuccess; } #endif @@ -3013,47 +3148,53 @@ bool GuiView::GuiViewPrivate::asyncBufferProcessing( string const & argument, Buffer const * used_buffer, docstring const & msg, - docstring (*asyncFunc)(Buffer const *, Buffer *, string const &), - bool (Buffer::*syncFunc)(string const &, bool, bool) const, - bool (Buffer::*previewFunc)(string const &, bool) const) + Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &), + Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const, + Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const) { if (!used_buffer) return false; string format = argument; if (format.empty()) - format = used_buffer->getDefaultOutputFormat(); - + format = used_buffer->params().getDefaultOutputFormat(); + processing_format = format; #if EXPORT_in_THREAD && (QT_VERSION >= 0x040400) - gv_->processingThreadStarted(); if (!msg.empty()) { progress_->clearMessages(); gv_->message(msg); } GuiViewPrivate::busyBuffers.insert(used_buffer); - QFuture f = QtConcurrent::run( + Buffer * cloned_buffer = used_buffer->cloneFromMaster(); + if (!cloned_buffer) { + Alert::error(_("Export Error"), + _("Error cloning the Buffer.")); + return false; + } + QFuture f = QtConcurrent::run( asyncFunc, used_buffer, - used_buffer->clone(), + cloned_buffer, format); setPreviewFuture(f); - last_export_format = used_buffer->bufferFormat(); + last_export_format = used_buffer->params().bufferFormat(); (void) syncFunc; (void) previewFunc; // We are asynchronous, so we don't know here anything about the success return true; #else + Buffer::ExportStatus status; if (syncFunc) { // TODO check here if it breaks exporting with Qt < 4.4 - bool const update_unincluded = - used_buffer->params().maintain_unincluded_children && - !used_buffer->params().getIncludedChildren().empty(); - return (used_buffer->*syncFunc)(format, true, update_unincluded); + status = (used_buffer->*syncFunc)(format, true); } else if (previewFunc) { - return (used_buffer->*previewFunc)(format, false); - } + status = (used_buffer->*previewFunc)(format); + } else + return false; + handleExportStatus(gv_, status, format); (void) asyncFunc; - return false; + return (status == Buffer::ExportSuccess + || status == Buffer::PreviewSuccess); #endif } @@ -3069,7 +3210,7 @@ void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr) // Try with the document BufferView dispatch if any. BufferView * doc_bv = documentBufferView(); - if (doc_bv) { + if (doc_bv && doc_bv != bv) { doc_bv->dispatch(cmd, dr); if (dr.dispatched()) return; @@ -3135,7 +3276,7 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr) #if QT_VERSION < 0x040400 if (!doc_buffer->doExport(argument, false)) { dr.setError(true); - dr.setMessage(bformat(_("Error exporting to format: %1$s."), + dr.setMessage(bformat(_("Error exporting to format: %1$s"), cmd.argument())); } #else @@ -3152,6 +3293,11 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr) break; } + case LFUN_BUFFER_EXPORT_AS: + LASSERT(doc_buffer, break); + exportBufferAs(*doc_buffer); + break; + case LFUN_BUFFER_UPDATE: { d.asyncBufferProcessing(argument, doc_buffer, @@ -3261,13 +3407,37 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr) insertLyXFile(cmd.argument()); break; - case LFUN_FILE_INSERT_PLAINTEXT_PARA: - insertPlaintextFile(cmd.argument(), true); - break; - case LFUN_FILE_INSERT_PLAINTEXT: - insertPlaintextFile(cmd.argument(), false); + case LFUN_FILE_INSERT_PLAINTEXT_PARA: { + bool const as_paragraph = (cmd.action() == LFUN_FILE_INSERT_PLAINTEXT_PARA); + string const fname = to_utf8(cmd.argument()); + if (!fname.empty() && !FileName::isAbsolute(fname)) { + dr.setMessage(_("Absolute filename expected.")); + break; + } + + FileName filename(fname); + if (fname.empty()) { + FileDialog dlg(qt_("Select file to insert"), (as_paragraph ? + LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT)); + + FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()), + QStringList(qt_("All Files (*)"))); + + if (result.first == FileDialog::Later || result.second.isEmpty()) { + dr.setMessage(_("Canceled.")); + break; + } + + filename.set(fromqstr(result.second)); + } + + if (bv) { + FuncRequest const new_cmd(cmd, filename.absoluteFilePath()); + bv->dispatch(new_cmd, dr); + } break; + } case LFUN_BUFFER_RELOAD: { LASSERT(doc_buffer, break); @@ -3355,10 +3525,9 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr) } case LFUN_DIALOG_TOGGLE: { - if (isDialogVisible(cmd.getArg(0))) - dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()), dr); - else - dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()), dr); + FuncCode const func_code = isDialogVisible(cmd.getArg(0)) + ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW; + dispatch(FuncRequest(func_code, cmd.argument()), dr); break; } @@ -3449,6 +3618,21 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr) } break; + case LFUN_VIEW_CLOSE: + if (TabWorkArea * twa = d.currentTabWorkArea()) { + closeWorkArea(twa->currentWorkArea()); + d.current_work_area_ = 0; + twa = d.currentTabWorkArea(); + // Switch to the next GuiWorkArea in the found TabWorkArea. + if (twa) { + // Make sure the work area is up to date. + setCurrentWorkArea(twa->currentWorkArea()); + } else { + setCurrentWorkArea(0); + } + } + break; + case LFUN_COMPLETION_INLINE: if (d.current_work_area_) d.current_work_area_->completer().showInline(); @@ -3516,27 +3700,43 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr) break; case LFUN_FORWARD_SEARCH: { - FileName const path(doc_buffer->temppath()); - string const texname = doc_buffer->latexName(); + Buffer const * doc_master = doc_buffer->masterBuffer(); + FileName const path(doc_master->temppath()); + string const texname = doc_master->isChild(doc_buffer) + ? DocFileName(changeExtension( + doc_buffer->absFileName(), + "tex")).mangledFileName() + : doc_buffer->latexName(); + string const fulltexname = + support::makeAbsPath(texname, doc_master->temppath()).absFileName(); + string const mastername = + removeExtension(doc_master->latexName()); FileName const dviname(addName(path.absFileName(), - support::changeExtension(texname, "dvi"))); + addExtension(mastername, "dvi"))); FileName const pdfname(addName(path.absFileName(), - support::changeExtension(texname, "pdf"))); - if (!dviname.exists() && !pdfname.exists()) { + addExtension(mastername, "pdf"))); + bool const have_dvi = dviname.exists(); + bool const have_pdf = pdfname.exists(); + if (!have_dvi && !have_pdf) { dr.setMessage(_("Please, preview the document first.")); break; } string outname = dviname.onlyFileName(); string command = lyxrc.forward_search_dvi; - if (!dviname.exists() || - pdfname.lastModified() > dviname.lastModified()) { + if (!have_dvi || (have_pdf && + pdfname.lastModified() > dviname.lastModified())) { outname = pdfname.onlyFileName(); command = lyxrc.forward_search_pdf; } - int row = doc_buffer->texrow().getRowFromIdPos(bv->cursor().paragraph().id(), bv->cursor().pos()); + DocIterator tmpcur = bv->cursor(); + // Leave math first + while (tmpcur.inMathed()) + tmpcur.pop_back(); + int row = tmpcur.inMathed() ? 0 : doc_buffer->texrow().getRowFromIdPos( + tmpcur.paragraph().id(), tmpcur.pos()); LYXERR(Debug::ACTION, "Forward search: row:" << row - << " id:" << bv->cursor().paragraph().id()); + << " id:" << tmpcur.paragraph().id()); if (!row || command.empty()) { dr.setMessage(_("Couldn't proceed.")); break; @@ -3544,6 +3744,7 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr) string texrow = convert(row); command = subst(command, "$$n", texrow); + command = subst(command, "$$f", fulltexname); command = subst(command, "$$t", texname); command = subst(command, "$$o", outname); @@ -3730,6 +3931,7 @@ void GuiView::resetDialogs() // Make sure that no LFUN uses any GuiView. guiApp->setCurrentView(0); saveLayout(); + saveUISettings(); menuBar()->clear(); constructToolbars(); guiApp->menus().fillMenuBar(menuBar(), this, false); @@ -3821,9 +4023,12 @@ void GuiView::hideDialog(string const & name, Inset * inset) if (it == d.dialogs_.end()) return; - if (inset && currentBufferView() - && inset != currentBufferView()->editedInset(name)) - return; + if (inset) { + if (!currentBufferView()) + return; + if (inset != currentBufferView()->editedInset(name)) + return; + } Dialog * const dialog = it->second.get(); if (dialog->isVisibleView())