#include <QHeaderView>
#include <QItemDelegate>
#include <QPainter>
+#include <QRegExp>
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
#include <QTextFrame>
namespace lyx {
namespace frontend {
-
+
class LayoutItemDelegate : public QItemDelegate {
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;
///
GuiLayoutFilterModel(QObject * parent = 0)
: QSortFilterProxyModel(parent)
{}
-
+
///
void triggerLayoutChange()
{
//
/////////////////////////////////////////////////////////////////////
-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
filterModel_(new GuiLayoutFilterModel(p)),
lastSel_(-1),
layoutItemDelegate_(new LayoutItemDelegate(parent)),
- visibleCategories_(0), inShowPopup_(false)
+ visibleCategories_(0)
{
filterModel_->setSourceModel(model_);
}
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_
LayoutItemDelegate * layoutItemDelegate_;
///
unsigned visibleCategories_;
- ///
- bool inShowPopup_;
};
opt.state = state;
// draw category header
- drawCategoryHeader(painter, opt,
+ drawCategoryHeader(painter, opt,
category(*index.model(), index.row()));
// move rect down below header
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;
}
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
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."),
else
owner_.message(_("Enter characters to filter the layout list."));
}
-
+
p->view()->setUpdatesEnabled(enabled);
}
// 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)));
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) {
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);
}
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()) {
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);
// skip the Standard layout
if (i == 0)
++i;
-
+
// the simple unsorted case
if (!sorted) {
if (sortedByCat) {
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;
}
if (!bv) {
d->model_->clear();
setEnabled(false);
+ setMinimumWidth(sizeHint().width());
d->text_class_.reset();
d->inset_ = 0;
return;
// 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();
}