#include "support/lstrings.h"
#include "support/lyxalgo.h" // sorted
+#include <QAbstractItemDelegate>
+#include <QAbstractTextDocumentLayout>
+#include <QApplication>
#include <QComboBox>
#include <QHeaderView>
#include <QKeyEvent>
#include <QList>
+#include <QPainter>
#include <QPixmap>
#include <QSortFilterProxyModel>
#include <QStandardItem>
#include <QStandardItemModel>
+#include <QTextDocument>
#include <QToolBar>
#include <QToolButton>
#include <QVariant>
//
/////////////////////////////////////////////////////////////////////
-class GuiFilterProxyModel : public QSortFilterProxyModel
-{
+class FilterItemDelegate : public QAbstractItemDelegate {
public:
///
- GuiFilterProxyModel(QObject * parent)
- : QSortFilterProxyModel(parent) {}
+ explicit FilterItemDelegate(QObject * parent = 0)
+ : QAbstractItemDelegate(parent) {}
+
+ ///
+ void paint(QPainter * painter, QStyleOptionViewItem const & option,
+ QModelIndex const & index) const {
+ QComboBox * combo = static_cast<QComboBox const *>(parent());
+
+ // Draw using the menu item style (this is how QComboBox does it).
+ // But for the rich text drawing below we will call it with an
+ // empty string, and later then draw over it the real string.
+ painter->save();
+ QStyleOptionMenuItem opt = getStyleOption(option, index);
+ QString text = underlineFilter(opt.text);
+ opt.text = QString();
+ painter->eraseRect(option.rect);
+ combo->style()->drawControl(QStyle::CE_MenuItem, &opt, painter, combo->view());
+ painter->restore();
+
+ // don't draw string for separator
+ if (opt.menuItemType == QStyleOptionMenuItem::Separator)
+ return;
+
+ // Draw the rich text.
+ painter->save();
+ QColor col = opt.palette.text().color();
+ if (opt.state & QStyle::State_Selected)
+ col = opt.palette.highlightedText().color();
+ QAbstractTextDocumentLayout::PaintContext context;
+ context.palette.setColor(QPalette::Text, col);
+
+ QTextDocument doc;
+ doc.setDefaultFont(opt.font);
+ doc.setHtml(text);
+ painter->translate(opt.rect.x() + 20, opt.rect.y());
+ doc.documentLayout()->draw(painter, context);
+ painter->restore();
+ }
///
- QVariant data(const QModelIndex & index, int role) const
+ QSize sizeHint(QStyleOptionViewItem const & option,
+ QModelIndex const & index) const {
+ QComboBox * combo = static_cast<QComboBox const *>(parent());
+
+ QStyleOptionMenuItem opt = getStyleOption(option, index);
+ return combo->style()->sizeFromContents(
+ QStyle::CT_MenuItem, &opt, option.rect.size(), combo);
+ }
+
+private:
+ ///
+ QString underlineFilter(QString const & s) const
{
+ // get filter
GuiLayoutBox * p = static_cast<GuiLayoutBox *>(parent());
QString const & f = p->filter();
-
- if (!f.isEmpty() && index.isValid() && role == Qt::DisplayRole) {
- // step through data item and put "(x)" for every matching character
- QString s = QSortFilterProxyModel::data(index, role).toString();
- QString r;
- int lastp = -1;
- p->filter();
- for (int i = 0; i < f.length(); ++i) {
- int p = s.indexOf(f[i], lastp + 1, Qt::CaseInsensitive);
- BOOST_ASSERT(p != -1);
- if (lastp == p - 1 && lastp != -1) {
- // remove ")" and append "x)"
- r = r.left(r.length() - 1) + s[p] + ")";
- } else {
- // append "(x)"
- r += s.mid(lastp + 1, p - lastp - 1);
- r += "(" + s[p] + ")";
- }
- lastp = p;
+ if (f.isEmpty())
+ return s;
+
+ // step through data item and put "(x)" for every matching character
+ QString r;
+ int lastp = -1;
+ p->filter();
+ for (int i = 0; i < f.length(); ++i) {
+ int p = s.indexOf(f[i], lastp + 1, Qt::CaseInsensitive);
+ BOOST_ASSERT(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>";
}
- r += s.mid(lastp + 1);
- return r;
+ lastp = p;
}
+ r += s.mid(lastp + 1);
+ return r;
+ }
+
+ ///
+ QStyleOptionMenuItem getStyleOption(QStyleOptionViewItem const & option,
+ QModelIndex const & index) const
+ {
+ QComboBox * combo = static_cast<QComboBox const *>(parent());
+
+ // create the options for a menu item
+ QStyleOptionMenuItem menuOption;
+ menuOption.palette = QApplication::palette("QMenu");
+ menuOption.state = QStyle::State_Active | QStyle::State_Enabled;
+ if (option.state & QStyle::State_Selected)
+ menuOption.state |= QStyle::State_Selected;
+ menuOption.checkType = QStyleOptionMenuItem::NonExclusive;
+ menuOption.checked = combo->currentIndex() == index.row();
+ menuOption.text = index.model()->data(index, Qt::DisplayRole).toString()
+ .replace(QLatin1Char('&'), QLatin1String("&&"));
+ if (menuOption.text.left(2) == "--")
+ menuOption.menuItemType = QStyleOptionMenuItem::Separator;
+ else
+ menuOption.menuItemType = QStyleOptionMenuItem::Normal;
+ menuOption.tabWidth = 0;
+ menuOption.menuRect = option.rect;
+ menuOption.rect = option.rect;
+ menuOption.font = combo->font();
+ menuOption.fontMetrics = QFontMetrics(menuOption.font);
- return QSortFilterProxyModel::data(index, role);
+ return menuOption;
}
-
+};
+
+
+class GuiFilterProxyModel : public QSortFilterProxyModel
+{
+public:
+ ///
+ GuiFilterProxyModel(QObject * parent)
+ : QSortFilterProxyModel(parent) {}
+
///
void setCharFilter(QString const & f)
{
setFilterRegExp(charFilterRegExp(f));
- reset();
+ dataChanged(index(0, 0), index(rowCount() - 1, 1));
}
private:
GuiLayoutBox::GuiLayoutBox(GuiView & owner)
- : owner_(owner)
+ : owner_(owner), filterItemDelegate_(new FilterItemDelegate(this))
{
setSizeAdjustPolicy(QComboBox::AdjustToContents);
setFocusPolicy(Qt::ClickFocus);
// for the filtering we have to intercept characters
view()->installEventFilter(this);
+ view()->setItemDelegateForColumn(0, filterItemDelegate_);
QObject::connect(this, SIGNAL(activated(int)),
this, SLOT(selected(int)));
if (i.isValid())
setCurrentIndex(i.row());
}
+
+ // Workaround to resize to content size
+ // FIXME: There must be a better way. The QComboBox::AdjustToContents)
+ // does not help.
+ if (view()->isVisible())
+ QComboBox::showPopup();
}
}
+void GuiLayoutBox::showPopup()
+{
+ resetFilter();
+ owner_.message(_("Enter characters to filter the layout list."));
+ QComboBox::showPopup();
+}
+
+
bool GuiLayoutBox::eventFilter(QObject * o, QEvent * e)
{
if (e->type() != QEvent::KeyPress)
return;
}
- // find row to insert the item
+ // find row to insert the item, after the separator if it exists
int i = 1; // skip the Standard layout
- QString is = model_->item(i, 1)->text();
- while (is.compare(titem) < 0) {
- // e.g. --Separator--
- if (is[0].category() != QChar::Letter_Uppercase)
- break;
- ++i;
- if (i == end)
- break;
- is = model_->item(i, 1)->text();
+
+ QList<QStandardItem *> sep = model_->findItems("--", Qt::MatchStartsWith);
+ if (!sep.isEmpty())
+ i = sep.first()->index().row() + 1;
+ if (i < model_->rowCount()) {
+ // find alphabetic position
+ QString is = model_->item(i, 0)->text();
+ while (is.compare(titem) < 0) {
+ // e.g. --Separator--
+ if (is.at(0).category() != QChar::Letter_Uppercase)
+ break;
+ ++i;
+ if (i == end)
+ break;
+ is = model_->item(i, 0)->text();
+ }
}
model_->insertRow(i, row);