]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/LayoutBox.cpp
Use <cstdint> instead of <boost/cstdint.hpp>
[lyx.git] / src / frontends / qt4 / LayoutBox.cpp
index 55888d5ec173cfb11e5190d2a595f8c2e60736b5..7d0cfd666f3f1696cf80ae5c8e03ebd8dff6dcc2 100644 (file)
@@ -24,6 +24,7 @@
 #include "BufferParams.h"
 #include "BufferView.h"
 #include "Cursor.h"
+#include "DocumentClassPtr.h"
 #include "FuncRequest.h"
 #include "FuncStatus.h"
 #include "LyX.h"
@@ -42,6 +43,7 @@
 #include <QHeaderView>
 #include <QItemDelegate>
 #include <QPainter>
+#include <QRegExp>
 #include <QSortFilterProxyModel>
 #include <QStandardItemModel>
 #include <QTextFrame>
@@ -52,7 +54,7 @@ using namespace lyx::support;
 namespace lyx {
 namespace frontend {
 
-       
+
 class LayoutItemDelegate : public QItemDelegate {
 public:
        ///
@@ -68,11 +70,11 @@ public:
        ///
        QSize sizeHint(QStyleOptionViewItem const & opt,
                QModelIndex const & index) const;
-       
+
 private:
        ///
        void drawCategoryHeader(QPainter * painter, QStyleOptionViewItem const & opt,
-               QString const & category) const;        
+               QString const & category) const;
        ///
        QString underlineFilter(QString const & s) const;
        ///
@@ -86,7 +88,7 @@ public:
        GuiLayoutFilterModel(QObject * parent = 0)
                : QSortFilterProxyModel(parent)
        {}
-       
+
        ///
        void triggerLayoutChange()
        {
@@ -102,9 +104,14 @@ public:
 //
 /////////////////////////////////////////////////////////////////////
 
-struct LayoutBox::Private
+class LayoutBox::Private
 {
+       /// noncopyable
+       Private(Private const &);
+       void operator=(Private const &);
+public:
        Private(LayoutBox * parent, GuiView & gv) : p(parent), owner_(gv),
+               inset_(0),
                // set the layout model with two columns
                // 1st: translated layout names
                // 2nd: raw layout names
@@ -112,7 +119,7 @@ struct LayoutBox::Private
                filterModel_(new GuiLayoutFilterModel(p)),
                lastSel_(-1),
                layoutItemDelegate_(new LayoutItemDelegate(parent)),
-               visibleCategories_(0), inShowPopup_(false)
+               visibleCategories_(0)
        {
                filterModel_->setSourceModel(model_);
        }
@@ -127,10 +134,10 @@ struct LayoutBox::Private
        ///
        GuiView & owner_;
        ///
-       DocumentClass const * text_class_;
+       DocumentClassConstPtr text_class_;
        ///
        Inset const * inset_;
-       
+
        /// the layout model: 1st column translated, 2nd column raw layout name
        QStandardItemModel * model_;
        /// the proxy model filtering \c model_
@@ -143,8 +150,6 @@ struct LayoutBox::Private
        LayoutItemDelegate * layoutItemDelegate_;
        ///
        unsigned visibleCategories_;
-       ///
-       bool inShowPopup_;
 };
 
 
@@ -188,7 +193,7 @@ void LayoutItemDelegate::paint(QPainter * painter, QStyleOptionViewItem const &
                        opt.state = state;
 
                        // draw category header
-                       drawCategoryHeader(painter, opt, 
+                       drawCategoryHeader(painter, opt,
                                category(*index.model(), index.row()));
 
                        // move rect down below header
@@ -233,38 +238,22 @@ void LayoutItemDelegate::drawDisplay(QPainter * painter, QStyleOptionViewItem co
 QSize LayoutItemDelegate::sizeHint(QStyleOptionViewItem const & opt,
                                                                   QModelIndex const & index) const
 {
-       QSortFilterProxyModel const * model =
-               static_cast<QSortFilterProxyModel const *>(index.model());      
        QSize size = QItemDelegate::sizeHint(opt, index);
+       if (!lyxrc.group_layouts)
+               return size;
 
-       /// QComboBox uses the first row height to estimate the
-       /// complete popup height during QComboBox::showPopup().
-       /// To avoid scrolling we have to sneak in space for the headers.
-       /// So we tweak this value accordingly. It's not nice, but the
-       /// only possible way it seems.
-       if (lyxrc.group_layouts && index.row() == 0 && layout_->d->inShowPopup_) {
-               int itemHeight = size.height();
-
-               // we have to show \c cats many headers:
-               unsigned cats = layout_->d->visibleCategories_;
-
-               // and we have \c n items to distribute the needed space over
-               unsigned n = layout_->model()->rowCount();
+       // Add space for the category headers.
+       QSortFilterProxyModel const * const model =
+               static_cast<QSortFilterProxyModel const *>(index.model());
+       QString const stdCat = category(*model->sourceModel(), 0);
+       QString const cat = category(*index.model(), index.row());
 
-               // so the needed average height (rounded upwards) is:
-               size.setHeight((headerHeight(opt) * cats + itemHeight * n + n - 1) / n); 
+       // There is no header for the stuff at the top.
+       if (stdCat == cat)
                return size;
-       }
 
-       // Add space for the category headers here?
-       // Not for the standard layout though.
-       QString stdCat = category(*model->sourceModel(), 0);
-       QString cat = category(*index.model(), index.row());
-       if (lyxrc.group_layouts && stdCat != cat
-               && (index.row() == 0 || cat != category(*index.model(), index.row() - 1))) {
+       if (index.row() == 0 || cat != category(*index.model(), index.row() - 1))
                size.setHeight(size.height() + headerHeight(opt));
-       }
-
        return size;
 }
 
@@ -310,45 +299,19 @@ QString LayoutItemDelegate::underlineFilter(QString const & s) const
        QString const & f = layout_->filter();
        if (f.isEmpty())
                return s;
-
-       // step through data item and put "(x)" for every matching character
-       QString r;
-       int lastp = -1;
-       layout_->filter();
-       for (int i = 0; i < f.length(); ++i) {
-               int p = s.indexOf(f[i], lastp + 1, Qt::CaseInsensitive);
-               LASSERT(p != -1, /**/);
-               if (lastp == p - 1 && lastp != -1) {
-                       // remove ")" and append "x)"
-                       r = r.left(r.length() - 4) + s[p] + "</u>";
-               } else {
-                       // append "(x)"
-                       r += s.mid(lastp + 1, p - lastp - 1);
-                       r += QString("<u>") + s[p] + "</u>";
-               }
-               lastp = p;
-       }
-       r += s.mid(lastp + 1);
+       QString r(s);
+       QRegExp pattern(charFilterRegExpC(f));
+       r.replace(pattern, "<u><b>\\1</b></u>");
        return r;
 }
 
 
-static QString charFilterRegExp(QString const & filter)
-{
-       QString re;
-       for (int i = 0; i < filter.length(); ++i) {
-               QChar c = filter[i];
-               if (c.isLower())
-                       re += ".*[" + QRegExp::escape(c) + QRegExp::escape(c.toUpper()) + "]";
-               else
-                       re += ".*" + QRegExp::escape(c);
-       }
-       return re;
-}
-
-
 void LayoutBox::Private::setFilter(QString const & s)
 {
+       // exit early if nothing has to be done
+       if (filter_ == s)
+               return;
+
        bool enabled = p->view()->updatesEnabled();
        p->view()->setUpdatesEnabled(false);
 
@@ -360,31 +323,16 @@ void LayoutBox::Private::setFilter(QString const & s)
        filter_ = s;
        filterModel_->setFilterRegExp(charFilterRegExp(filter_));
        countCategories();
-       
+
        // restore old selection
        if (lastSel_ != -1) {
                QModelIndex i = filterModel_->mapFromSource(model_->index(lastSel_, 0));
                if (i.isValid())
                        p->setCurrentIndex(i.row());
        }
-       
-       // Workaround to resize to content size
-       // FIXME: There must be a better way. The QComboBox::AdjustToContents)
-       //        does not help.
+
        if (p->view()->isVisible()) {
-               // call QComboBox::showPopup. But set the inShowPopup_ flag to switch on
-               // the hack in the item delegate to make space for the headers.
-               // We do not call our implementation of showPopup because that
-               // would reset the filter again. This is only needed if the user clicks
-               // on the QComboBox.
-               LASSERT(!inShowPopup_, /**/);
-               inShowPopup_ = true;
                p->QComboBox::showPopup();
-               inShowPopup_ = false;
-
-               // The item delegate hack is off again. So trigger a relayout of the popup.
-               filterModel_->triggerLayoutChange();
-               
                if (!s.isEmpty())
                        owner_.message(bformat(_("Filtering layouts with \"%1$s\". "
                                                 "Press ESC to remove filter."),
@@ -392,7 +340,7 @@ void LayoutBox::Private::setFilter(QString const & s)
                else
                        owner_.message(_("Enter characters to filter the layout list."));
        }
-       
+
        p->view()->setUpdatesEnabled(enabled);
 }
 
@@ -410,7 +358,7 @@ LayoutBox::LayoutBox(GuiView & owner)
        // for the filtering we have to intercept characters
        view()->installEventFilter(this);
        view()->setItemDelegateForColumn(0, d->layoutItemDelegate_);
-       
+
        QObject::connect(this, SIGNAL(activated(int)),
                this, SLOT(selected(int)));
 
@@ -418,6 +366,11 @@ LayoutBox::LayoutBox(GuiView & owner)
 }
 
 
+LayoutBox::~LayoutBox() {
+       delete d;
+}
+
+
 void LayoutBox::Private::countCategories()
 {
        int n = filterModel_->rowCount();
@@ -426,7 +379,7 @@ void LayoutBox::Private::countCategories()
                return;
 
        // skip the "Standard" category
-       QString prevCat = model_->index(0, 2).data().toString(); 
+       QString prevCat = model_->index(0, 2).data().toString();
 
        // count categories
        for (int i = 0; i < n; ++i) {
@@ -444,19 +397,8 @@ void LayoutBox::showPopup()
 
        bool enabled = view()->updatesEnabled();
        view()->setUpdatesEnabled(false);
-
        d->resetFilter();
-
-       // call QComboBox::showPopup. But set the inShowPopup_ flag to switch on
-       // the hack in the item delegate to make space for the headers.
-       LASSERT(!d->inShowPopup_, /**/);
-       d->inShowPopup_ = true;
        QComboBox::showPopup();
-       d->inShowPopup_ = false;
-       
-       // The item delegate hack is off again. So trigger a relayout of the popup.
-       d->filterModel_->triggerLayoutChange();
-       
        view()->setUpdatesEnabled(enabled);
 }
 
@@ -470,7 +412,7 @@ bool LayoutBox::eventFilter(QObject * o, QEvent * e)
        bool modified = (ke->modifiers() == Qt::ControlModifier)
                || (ke->modifiers() == Qt::AltModifier)
                || (ke->modifiers() == Qt::MetaModifier);
-       
+
        switch (ke->key()) {
        case Qt::Key_Escape:
                if (!modified && !d->filter_.isEmpty()) {
@@ -509,10 +451,10 @@ bool LayoutBox::eventFilter(QObject * o, QEvent * e)
        return QComboBox::eventFilter(o, e);
 }
 
-       
+
 void LayoutBox::setIconSize(QSize size)
 {
-#ifdef Q_WS_MACX
+#ifdef Q_OS_MAC
        bool small = size.height() < 20;
        setAttribute(Qt::WA_MacSmallSize, small);
        setAttribute(Qt::WA_MacNormalSize, !small);
@@ -525,10 +467,13 @@ void LayoutBox::setIconSize(QSize size)
 void LayoutBox::set(docstring const & layout)
 {
        d->resetFilter();
-       
+
        if (!d->text_class_)
                return;
 
+       if (!d->text_class_->hasLayout(layout))
+               return;
+
        Layout const & lay = (*d->text_class_)[layout];
        QString newLayout = toqstr(lay.name());
 
@@ -560,9 +505,9 @@ void LayoutBox::addItemSort(docstring const & item, docstring const & category,
        bool sorted, bool sortedByCat, bool unknown)
 {
        QString qitem = toqstr(item);
-       // FIXME This is wrong for RTL, I'd suppose.
-       QString titem = toqstr(translateIfPossible(item) +
-                              (unknown ? _(" (unknown)") : from_ascii("")));
+       docstring const loc_item = translateIfPossible(item);
+       QString titem = unknown ? toqstr(bformat(_("%1$s (unknown)"), loc_item))
+                               : toqstr(loc_item);
        QString qcat = toqstr(translateIfPossible(category));
 
        QList<QStandardItem *> row;
@@ -587,7 +532,7 @@ void LayoutBox::addItemSort(docstring const & item, docstring const & category,
        // skip the Standard layout
        if (i == 0)
                ++i;
-       
+
        // the simple unsorted case
        if (!sorted) {
                if (sortedByCat) {
@@ -604,7 +549,7 @@ void LayoutBox::addItemSort(docstring const & item, docstring const & category,
        if (i < end) {
                // find alphabetic position
                while (i != end
-                      && d->model_->item(i, 0)->text().localeAwareCompare(titem) < 0 
+                      && d->model_->item(i, 0)->text().localeAwareCompare(titem) < 0
                       && (!sortedByCat || d->model_->item(i, 2)->text() == qcat))
                        ++i;
        }
@@ -620,13 +565,14 @@ void LayoutBox::updateContents(bool reset)
        if (!bv) {
                d->model_->clear();
                setEnabled(false);
-               d->text_class_ = 0;
+               setMinimumWidth(sizeHint().width());
+               d->text_class_.reset();
                d->inset_ = 0;
                return;
        }
        // we'll only update the layout list if the text class has changed
        // or we've moved from one inset to another
-       DocumentClass const * text_class = &(bv->buffer().params().documentClass());
+       DocumentClassConstPtr text_class = bv->buffer().params().documentClassPtr();
        Inset const * inset = &(bv->cursor().innerText()->inset());
        if (!reset && d->text_class_ == text_class && d->inset_ == inset) {
                set(bv->cursor().innerParagraph().layout().name());
@@ -653,19 +599,16 @@ void LayoutBox::updateContents(bool reset)
                // obsoleted layouts are skipped as well
                if (!lit->obsoleted_by().empty())
                        continue;
-               addItemSort(name, lit->category(), lyxrc.sort_layouts, 
+               addItemSort(name, lit->category(), lyxrc.sort_layouts,
                                lyxrc.group_layouts, lit->isUnknown());
        }
 
        set(d->owner_.currentBufferView()->cursor().innerParagraph().layout().name());
        d->countCategories();
-       
-       // needed to recalculate size hint
-       hide();
+
        setMinimumWidth(sizeHint().width());
        setEnabled(!bv->buffer().isReadonly() &&
                lyx::getStatus(FuncRequest(LFUN_LAYOUT)).enabled());
-       show();
 }