]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/GuiView.cpp
Merge branch 'master' into biblatex2
[lyx.git] / src / frontends / qt4 / GuiView.cpp
index 1342eb7998a9d3f755a435d3214a58186d2b055d..2c481245680727876bc3d2c5f4171cec0a3608fb 100644 (file)
@@ -15,7 +15,6 @@
 
 #include "GuiView.h"
 
-#include "Dialog.h"
 #include "DispatchResult.h"
 #include "FileDialog.h"
 #include "FontLoader.h"
 #include <QSplitter>
 #include <QStackedWidget>
 #include <QStatusBar>
-#if QT_VERSION >= 0x050000
 #include <QSvgRenderer>
-#endif
 #include <QtConcurrentRun>
 #include <QTime>
 #include <QTimer>
@@ -330,62 +327,6 @@ public:
                delete stack_widget_;
        }
 
-       QMenu * toolBarPopup(GuiView * parent)
-       {
-               // FIXME: translation
-               QMenu * menu = new QMenu(parent);
-               QActionGroup * iconSizeGroup = new QActionGroup(parent);
-
-               QAction * smallIcons = new QAction(iconSizeGroup);
-               smallIcons->setText(qt_("Small-sized icons"));
-               smallIcons->setCheckable(true);
-               QObject::connect(smallIcons, SIGNAL(triggered()),
-                       parent, SLOT(smallSizedIcons()));
-               menu->addAction(smallIcons);
-
-               QAction * normalIcons = new QAction(iconSizeGroup);
-               normalIcons->setText(qt_("Normal-sized icons"));
-               normalIcons->setCheckable(true);
-               QObject::connect(normalIcons, SIGNAL(triggered()),
-                       parent, SLOT(normalSizedIcons()));
-               menu->addAction(normalIcons);
-
-               QAction * bigIcons = new QAction(iconSizeGroup);
-               bigIcons->setText(qt_("Big-sized icons"));
-               bigIcons->setCheckable(true);
-               QObject::connect(bigIcons, SIGNAL(triggered()),
-                       parent, SLOT(bigSizedIcons()));
-               menu->addAction(bigIcons);
-
-               QAction * hugeIcons = new QAction(iconSizeGroup);
-               hugeIcons->setText(qt_("Huge-sized icons"));
-               hugeIcons->setCheckable(true);
-               QObject::connect(hugeIcons, SIGNAL(triggered()),
-                       parent, SLOT(hugeSizedIcons()));
-               menu->addAction(hugeIcons);
-
-               QAction * giantIcons = new QAction(iconSizeGroup);
-               giantIcons->setText(qt_("Giant-sized icons"));
-               giantIcons->setCheckable(true);
-               QObject::connect(giantIcons, SIGNAL(triggered()),
-                       parent, SLOT(giantSizedIcons()));
-               menu->addAction(giantIcons);
-
-               unsigned int cur = parent->iconSize().width();
-               if ( cur == parent->d.smallIconSize)
-                       smallIcons->setChecked(true);
-               else if (cur == parent->d.normalIconSize)
-                       normalIcons->setChecked(true);
-               else if (cur == parent->d.bigIconSize)
-                       bigIcons->setChecked(true);
-               else if (cur == parent->d.hugeIconSize)
-                       hugeIcons->setChecked(true);
-               else if (cur == parent->d.giantIconSize)
-                       giantIcons->setChecked(true);
-
-               return menu;
-       }
-
        void setBackground()
        {
                stack_widget_->setCurrentWidget(bg_widget_);
@@ -442,6 +383,60 @@ public:
                processing_thread_watcher_.setFuture(f);
        }
 
+       QSize iconSize(docstring const & icon_size)
+       {
+               unsigned int size;
+               if (icon_size == "small")
+                       size = smallIconSize;
+               else if (icon_size == "normal")
+                       size = normalIconSize;
+               else if (icon_size == "big")
+                       size = bigIconSize;
+               else if (icon_size == "huge")
+                       size = hugeIconSize;
+               else if (icon_size == "giant")
+                       size = giantIconSize;
+               else
+                       size = icon_size.empty() ? normalIconSize : convert<int>(icon_size);
+
+               if (size < smallIconSize)
+                       size = smallIconSize;
+
+               return QSize(size, size);
+       }
+
+       QSize iconSize(QString const & icon_size)
+       {
+               return iconSize(qstring_to_ucs4(icon_size));
+       }
+
+       string & iconSize(QSize const & qsize)
+       {
+               LATTEST(qsize.width() == qsize.height());
+
+               static string icon_size;
+
+               unsigned int size = qsize.width();
+
+               if (size < smallIconSize)
+                       size = smallIconSize;
+
+               if (size == smallIconSize)
+                       icon_size = "small";
+               else if (size == normalIconSize)
+                       icon_size = "normal";
+               else if (size == bigIconSize)
+                       icon_size = "big";
+               else if (size == hugeIconSize)
+                       icon_size = "huge";
+               else if (size == giantIconSize)
+                       icon_size = "giant";
+               else
+                       icon_size = convert<string>(size);
+
+               return icon_size;
+       }
+
 public:
        GuiView * gv_;
        GuiWorkArea * current_work_area_;
@@ -513,10 +508,13 @@ QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
 
 GuiView::GuiView(int id)
        : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0),
-         command_execute_(false)
+         command_execute_(false), minibuffer_focus_(false)
 {
+       connect(this, SIGNAL(bufferViewChanged()),
+               this, SLOT(onBufferViewChanged()));
+
        // GuiToolbars *must* be initialised before the menu bar.
-       normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
+       setIconSize(QSize(d.normalIconSize, d.normalIconSize)); // at least on Mac the default is 32 otherwise, which is huge
        constructToolbars();
 
        // set ourself as the current view. This is needed for the menu bar
@@ -553,7 +551,7 @@ GuiView::GuiView(int id)
 #endif
 
 #endif
-       resetWindowTitleAndIconText();
+       resetWindowTitle();
 
        // use tabbed dock area for multiple docks
        // (such as "source" and "messages")
@@ -577,6 +575,23 @@ GuiView::GuiView(int id)
        connect(&d.processing_thread_watcher_, SIGNAL(finished()), 
                busylabel, SLOT(hide()));
 
+       QFontMetrics const fm(statusBar()->fontMetrics());
+       int const roheight = max(int(d.normalIconSize), fm.height());
+       QSize const rosize(roheight, roheight);
+       QPixmap readonly = QIcon(getPixmap("images/", "emblem-readonly", "svgz,png")).pixmap(rosize);
+       read_only_ = new QLabel(statusBar());
+       read_only_->setPixmap(readonly);
+       read_only_->setScaledContents(true);
+       read_only_->setAlignment(Qt::AlignCenter);
+       read_only_->hide();
+       statusBar()->addPermanentWidget(read_only_);
+
+       version_control_ = new QLabel(statusBar());
+       version_control_->setAlignment(Qt::AlignCenter);
+       version_control_->setFrameStyle(QFrame::StyledPanel);
+       version_control_->hide();
+       statusBar()->addPermanentWidget(version_control_);
+
        statusBar()->setSizeGripEnabled(true);
        updateStatusBar();
 
@@ -591,6 +606,11 @@ GuiView::GuiView(int id)
        connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
                SLOT(doShowDialog(QString const &, QString const &, Inset *)));
 
+       // set custom application bars context menu, e.g. tool bar and menu bar
+       setContextMenuPolicy(Qt::CustomContextMenu);
+       connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
+               SLOT(toolBarPopup(const QPoint &)));
+
        // Forbid too small unresizable window because it can happen
        // with some window manager under X11.
        setMinimumSize(300, 200);
@@ -702,7 +722,7 @@ void GuiView::saveLayout() const
        settings.setValue("geometry", saveGeometry());
 #endif
        settings.setValue("layout", saveState(0));
-       settings.setValue("icon_size", iconSize());
+       settings.setValue("icon_size", toqstr(d.iconSize(iconSize())));
 }
 
 
@@ -729,17 +749,7 @@ bool GuiView::restoreLayout()
                return false;
 
        //code below is skipped when when ~/.config/LyX is (re)created
-       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.width() != int(d.hugeIconSize) &&
-           icon_size.width() != int(d.giantIconSize)) {
-               icon_size.setWidth(d.normalIconSize);
-               icon_size.setHeight(d.normalIconSize);
-       }
-       setIconSize(icon_size);
+       setIconSize(d.iconSize(settings.value(icon_key).toString()));
 
 #if defined(Q_WS_X11) || defined(QPA_XCB)
        QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
@@ -904,12 +914,6 @@ void GuiView::focusInEvent(QFocusEvent * e)
 }
 
 
-QMenu * GuiView::createPopupMenu()
-{
-       return d.toolBarPopup(this);
-}
-
-
 void GuiView::showEvent(QShowEvent * e)
 {
        LYXERR(Debug::GUI, "Passed Geometry "
@@ -1097,36 +1101,6 @@ void GuiView::updateStatusBarMessage(QString const & str)
 }
 
 
-void GuiView::smallSizedIcons()
-{
-       setIconSize(QSize(d.smallIconSize, d.smallIconSize));
-}
-
-
-void GuiView::normalSizedIcons()
-{
-       setIconSize(QSize(d.normalIconSize, d.normalIconSize));
-}
-
-
-void GuiView::bigSizedIcons()
-{
-       setIconSize(QSize(d.bigIconSize, d.bigIconSize));
-}
-
-
-void GuiView::hugeSizedIcons()
-{
-       setIconSize(QSize(d.hugeIconSize, d.hugeIconSize));
-}
-
-
-void GuiView::giantSizedIcons()
-{
-       setIconSize(QSize(d.giantIconSize, d.giantIconSize));
-}
-
-
 void GuiView::clearMessage()
 {
        // FIXME: This code was introduced in r19643 to fix bug #4123. However,
@@ -1144,37 +1118,58 @@ void GuiView::updateWindowTitle(GuiWorkArea * wa)
        if (wa != d.current_work_area_
                || wa->bufferView().buffer().isInternal())
                return;
-       setWindowTitle(qt_("LyX: ") + wa->windowTitle());
-       setWindowIconText(wa->windowIconText());
-#if (QT_VERSION >= 0x040400)
-       // Sets the path for the window: this is used by OSX to 
+       Buffer const & buf = wa->bufferView().buffer();
+       // Set the windows title
+       docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
+#ifndef Q_WS_MAC
+       title += from_ascii(" - LyX");
+#endif
+       setWindowTitle(toqstr(title));
+       // Sets the path for the window: this is used by OSX to
        // allow a context click on the title bar showing a menu
        // with the path up to the file
-       setWindowFilePath(toqstr(wa->bufferView().buffer().absFileName()));
-#endif
+       setWindowFilePath(toqstr(buf.absFileName()));
+       // Tell Qt whether the current document is changed
+       setWindowModified(!buf.isClean());
+
+       if (buf.isReadonly())
+               read_only_->show();
+       else
+               read_only_->hide();
+
+       if (buf.lyxvc().inUse()) {
+               version_control_->show();
+               version_control_->setText(toqstr(buf.lyxvc().vcstatus()));
+       } else
+               version_control_->hide();
 }
 
 
 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
 {
        if (d.current_work_area_)
-               QObject::disconnect(d.current_work_area_, SIGNAL(busy(bool)),
-                       this, SLOT(setBusy(bool)));
+               // disconnect the current work area from all slots
+               QObject::disconnect(d.current_work_area_, 0, this, 0);
        disconnectBuffer();
        disconnectBufferView();
        connectBufferView(wa->bufferView());
        connectBuffer(wa->bufferView().buffer());
        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();
+                        this, SLOT(updateWindowTitle(GuiWorkArea *)));
+       QObject::connect(wa, SIGNAL(busy(bool)),
+                        this, SLOT(setBusy(bool)));
+       // connection of a signal to a signal
+       QObject::connect(wa, SIGNAL(bufferViewChanged()),
+                        this, SIGNAL(bufferViewChanged()));
+       Q_EMIT updateWindowTitle(wa);
+       Q_EMIT bufferViewChanged();
+}
 
-       // The document settings needs to be reinitialised.
-       updateDialog("document", "");
 
+void GuiView::onBufferViewChanged()
+{
+       structureChanged();
        // Buffer-dependent dialogs must be updated. This is done here because
        // some dialogs require buffer()->text.
        updateDialogs();
@@ -1192,11 +1187,9 @@ void GuiView::on_lastWorkAreaRemoved()
                return;
 
        // Reset and updates the dialogs.
-       d.toc_models_.reset(0);
-       updateDialog("document", "");
-       updateDialogs();
+       Q_EMIT bufferViewChanged();
 
-       resetWindowTitleAndIconText();
+       resetWindowTitle();
        updateStatusBar();
 
        if (lyxrc.open_buffers_in_tabs)
@@ -1274,19 +1267,10 @@ bool GuiView::event(QEvent * e)
                        cap::saveSelection(old_view->currentBufferView()->cursor());
                }
                guiApp->setCurrentView(this);
-               if (d.current_work_area_) {
-                       BufferView & bv = d.current_work_area_->bufferView();
-                       connectBufferView(bv);
-                       connectBuffer(bv.buffer());
-                       // The document structure, name and dialogs might have
-                       // changed in another view.
-                       structureChanged();
-                       // The document settings needs to be reinitialised.
-                       updateDialog("document", "");
-                       updateDialogs();
-               } else {
-                       resetWindowTitleAndIconText();
-               }
+               if (d.current_work_area_)
+                       on_currentWorkAreaChanged(d.current_work_area_);
+               else
+                       resetWindowTitle();
                setFocus();
                return QMainWindow::event(e);
        }
@@ -1312,10 +1296,9 @@ bool GuiView::event(QEvent * e)
        }
 }
 
-void GuiView::resetWindowTitleAndIconText()
+void GuiView::resetWindowTitle()
 {
        setWindowTitle(qt_("LyX"));
-       setWindowIconText(qt_("LyX"));
 }
 
 bool GuiView::focusNextPrevChild(bool /*next*/)
@@ -1442,6 +1425,7 @@ void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
        if (!wa) {
                d.current_work_area_ = 0;
                d.setBackground();
+               Q_EMIT bufferViewChanged();
                return;
        }
 
@@ -1564,6 +1548,10 @@ void GuiView::updateToolbars()
                        context |= Toolbars::IPA;
                if (command_execute_)
                        context |= Toolbars::MINIBUFFER;
+               if (minibuffer_focus_) {
+                       context |= Toolbars::MINIBUFFER_FOCUS;
+                       minibuffer_focus_ = false;
+               }
 
                for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
                        it->second->update(context);
@@ -1664,6 +1652,10 @@ void GuiView::updateTocItem(string const & type, DocIterator const & dit)
 
 void GuiView::structureChanged()
 {
+       // This is called from the Buffer, which has no way to ensure that cursors
+       // in BufferView remain valid.
+       if (documentBufferView())
+               documentBufferView()->cursor().sanitize();
        // FIXME: This is slightly expensive, though less than the tocBackend update
        // (#9880). This also resets the view in the Toc Widget (#6675).
        d.toc_models_.reset(documentBufferView());
@@ -1814,7 +1806,7 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                string format = to_utf8(cmd.argument());
                if (cmd.argument().empty())
                        format = doc_buffer->params().getDefaultOutputFormat();
-               enable = doc_buffer->params().isExportableFormat(format);
+               enable = doc_buffer->params().isExportable(format, true);
                break;
        }
 
@@ -1892,6 +1884,10 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                break;
        }
 
+       case LFUN_ICON_SIZE:
+               flag.setOnOff(d.iconSize(cmd.argument()) == iconSize());
+               break;
+
        case LFUN_DROP_LAYOUTS_CHOICE:
                enable = buf != 0;
                break;
@@ -1989,15 +1985,21 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                break;
 
        case LFUN_BUFFER_ZOOM_OUT:
-               enable = doc_buffer && lyxrc.zoom > 10;
-               if (lyxrc.zoom <= 10)
-                       flag.message(_("Zoom level cannot be less than 10%."));
-               break;
-
-       case LFUN_BUFFER_ZOOM_IN:
-               enable = doc_buffer != 0;
+       case LFUN_BUFFER_ZOOM_IN: {
+               // only diff between these two is that the default for ZOOM_OUT
+               // is a neg. number
+               bool const neg_zoom =
+                       convert<int>(cmd.argument()) < 0 ||
+                       (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
+               if (lyxrc.zoom <= zoom_min_ && neg_zoom) {
+                       docstring const msg =
+                               bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
+                       flag.message(msg);
+                       enable = false;
+               } else
+                       enable = doc_buffer;
                break;
-
+       }
        case LFUN_BUFFER_MOVE_NEXT:
        case LFUN_BUFFER_MOVE_PREVIOUS:
                // we do not cycle when moving
@@ -2055,6 +2057,7 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                break;
 
        case LFUN_SERVER_GOTO_FILE_ROW:
+       case LFUN_LYX_ACTIVATE:
                break;
        case LFUN_FORWARD_SEARCH:
                enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
@@ -2568,15 +2571,6 @@ bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kin
 }
 
 
-struct PrettyNameComparator
-{
-       bool operator()(Format const *first, Format const *second) const {
-               return compare_no_case(translateIfPossible(from_ascii(first->prettyname())),
-                                      translateIfPossible(from_ascii(second->prettyname()))) <= 0;
-       }
-};
-
-
 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
 {
        FileName fname = b.fileName();
@@ -2587,28 +2581,25 @@ bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
        QStringList types;
        QString const anyformat = qt_("Guess from extension (*.*)");
        types << anyformat;
-       Formats::const_iterator it = formats.begin();
+
        vector<Format const *> export_formats;
-       for (; it != formats.end(); ++it)
-               if (it->documentFormat())
-                       export_formats.push_back(&(*it));
-       PrettyNameComparator cmp;
-       sort(export_formats.begin(), export_formats.end(), cmp);
-       vector<Format const *>::const_iterator fit = export_formats.begin();
+       for (Format const & f : formats)
+               if (f.documentFormat())
+                       export_formats.push_back(&f);
+       sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
        map<QString, string> fmap;
        QString filter;
        string ext;
-       for (; fit != export_formats.end(); ++fit) {
-               docstring const loc_prettyname =
-                       translateIfPossible(from_utf8((*fit)->prettyname()));
+       for (Format const * f : export_formats) {
+               docstring const loc_prettyname = translateIfPossible(f->prettyname());
                QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
                                                     loc_prettyname,
-                                                    from_ascii((*fit)->extension())));
+                                                    from_ascii(f->extension())));
                types << loc_filter;
-               fmap[loc_filter] = (*fit)->name();
-               if (from_ascii((*fit)->name()) == iformat) {
+               fmap[loc_filter] = f->name();
+               if (from_ascii(f->name()) == iformat) {
                        filter = loc_filter;
-                       ext = (*fit)->extension();
+                       ext = f->extension();
                }
        }
        string ofname = fname.onlyFileName();
@@ -3366,8 +3357,23 @@ bool GuiView::goToFileRow(string const & argument)
                return false;
        }
        setBuffer(buf);
-       documentBufferView()->setCursorFromRow(row);
-       return true;
+       bool success = documentBufferView()->setCursorFromRow(row);
+       if (!success) {
+               LYXERR(Debug::LATEX,
+                      "setCursorFromRow: invalid position for row " << row);
+               frontend::Alert::error(_("Inverse Search Failed"),
+                                      _("Invalid position requested by inverse search.\n"
+                                        "You may need to update the viewed document."));
+       }
+       return success;
+}
+
+
+void GuiView::toolBarPopup(const QPoint & /*pos*/)
+{
+       QMenu * menu = new QMenu;
+       menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
+       menu->exec(QCursor::pos());
 }
 
 
@@ -3665,6 +3671,7 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
 
                case LFUN_COMMAND_EXECUTE: {
                        command_execute_ = true;
+                       minibuffer_focus_ = true;
                        break;
                }
                case LFUN_DROP_LAYOUTS_CHOICE:
@@ -3775,6 +3782,14 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        break;
                }
 
+               case LFUN_ICON_SIZE: {
+                       QSize size = d.iconSize(cmd.argument());
+                       setIconSize(size);
+                       dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
+                                               size.width(), size.height()));
+                       break;
+               }
+
                case LFUN_DIALOG_UPDATE: {
                        string const name = to_utf8(cmd.argument());
                        if (name == "prefs" || name == "document")
@@ -3946,8 +3961,8 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        } else
                                zoom += convert<int>(cmd.argument());
 
-                       if (zoom < 10)
-                               zoom = 10;
+                       if (zoom < static_cast<int>(zoom_min_))
+                               zoom = zoom_min_;
                        lyxrc.zoom = zoom;
 
                        dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
@@ -3975,7 +3990,12 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        break;
 
                case LFUN_SERVER_GOTO_FILE_ROW:
-                       goToFileRow(to_utf8(cmd.argument()));
+                       if(goToFileRow(to_utf8(cmd.argument())))
+                               dr.screenUpdate(Update::Force | Update::FitCursor);
+                       break;
+
+               case LFUN_LYX_ACTIVATE:
+                       activateWindow();
                        break;
 
                case LFUN_FORWARD_SEARCH: {
@@ -4034,7 +4054,7 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
 
                case LFUN_SPELLING_CONTINUOUSLY:
                        lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
-                       dr.screenUpdate(Update::Force | Update::FitCursor);
+                       dr.screenUpdate(Update::Force);
                        break;
 
                default:
@@ -4054,7 +4074,7 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
        bv = currentBufferView();
 
        // Clear non-empty selections
-        // (e.g. from a "char-forward-select" followed by "char-backward-select")
+       // (e.g. from a "char-forward-select" followed by "char-backward-select")
        if (bv) {
                Cursor & cur = bv->cursor();
                if ((cur.selection() && cur.selBegin() == cur.selEnd())) {