Skip to content
Browse files

Improvements in the razor-runner

Added a customommand provider.
  • Loading branch information...
1 parent 4f3659b commit 0f0168946dd31e73cd954d10ca8989ae4fbd37ac @SokoloffA SokoloffA committed Mar 29, 2012
View
29 razorqt-runner/commanditemmodel.cpp
@@ -32,6 +32,7 @@
#include <QtCore/QFileInfo>
#include <QtCore/QProcess>
#include <QtCore/QDebug>
+#include <limits.h>
/************************************************
@@ -110,6 +111,12 @@ bool CommandItemModel::filterAcceptsRow(int sourceRow, const QModelIndex &/*sour
************************************************/
bool CommandItemModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
+ if (left == mSourceModel->customCommandIndex())
+ return true;
+
+ if (right == mSourceModel->customCommandIndex())
+ return false;
+
if (mOnlyHistory)
return left.row() < right.row();
else
@@ -123,25 +130,29 @@ bool CommandItemModel::lessThan(const QModelIndex &left, const QModelIndex &righ
QModelIndex CommandItemModel::appropriateItem(const QString &pattern) const
{
QModelIndex res;
- int delta = 0xFFFF;
+ unsigned int rank = 0;
int cnt = rowCount();
+
for (int i=0; i<cnt; ++i)
{
QModelIndex ind = index(i,0);
QModelIndex srcIndex = mapToSource(ind);
+ if (srcIndex == mSourceModel->customCommandIndex())
+ continue;
+
@amoskvin
Razor-qt member
amoskvin added a note Sep 20, 2012

Is the "continue" intentional? I would "return ind" here, so that it selects the matching command.

@amoskvin
Razor-qt member
amoskvin added a note Sep 23, 2012

It definitely isn't the expected behavior, so I'll change it.

pic
pic

@SokoloffA
Razor-qt member
SokoloffA added a note Oct 10, 2012

No, this not mistake. I wrote it consciously. I thought that founded .desktop entries have priority over commands in the $PATH. But now I'm not sure. On the one hand, if you write "libreoffice", you expected to start libreoffice. not libreoffice calc. On the other hand, many commands have a short names (rm, mv, cp), and your algorithm increases the rate of false positives. When you write "ed" I think, you want to start some GUI editor, but not ed.
Maybe some smarter? If text is shorter then 3 symbols (length is discussed), priority on .desktop, else priority on command.

@amoskvin
Razor-qt member
amoskvin added a note Oct 11, 2012
@SokoloffA
Razor-qt member
SokoloffA added a note Oct 11, 2012

I 've been thinking - the applink provider does not check the command, right?

Why? ApplinkItem check command. See AppLinkItem::rank.

It could be made to check if the command starts with what's typed in, and if so, give it a larger weight.

Algorithm pay attention on position.

unsigned int CommandProviderItem::stringRank(const QString str, const QString pattern) const
{
    int n = str.indexOf(pattern, 0, Qt::CaseInsensitive);
    if (n<0)
        return 0;

    return MAX_RANK - ((str.length() - pattern.length()) + n * 5);
}

If we type "term":

  • for "xterm" rank will be: 65535 - ((5 - 4) + 1 * 5) = 65529
  • for "gnome-terminal" - 65535 - ((14 - 4) + 6 * 5) = 65495

This would solve both problems I showed because if I type in "libr" it would match the LibreOffice entry (the only command that starts with that), and if I type "term" it would match "terminal" rather than "gnome-terminal". Additionally, it would be possible to search by command name, for instance "lowr" would produce "LibreOffice Writer" (lowriter).

Do you advise not show commands that contains text in the middle? I don't like it, I often search the programs only by parts. Like "creat" for qtcreator, or "server" for "rdp server" from history.

It still seems a bit "wrong" to me though, since traditionally launchers (in KDE3, Gnome2, Xfce) were expected to just launch the command that's typed in...

Razor-runner is more like a krunner.

@amoskvin
Razor-qt member
amoskvin added a note Oct 11, 2012

I've been thinking - the applink provider does not check the
command, right?

Why? ApplinkItem check command. See AppLinkItem::rank.

OK, my bad. The menu entry actually starts "libreoffice --writer", not "lowriter" as I expected.

[...]

Do you advise not show commands that contains text in the middle? I
don't like it, I often search the programs only by parts. Like "creat"
for qtcreator, or "server" for "rdp server" from history.

No, what I was thinking is that those items whose command (not the title) matches at the beginning would have a higher rank than all others.

So "creat" should still match Qt Creator since you probably don't have any application (in menu) whose command starts with that. Same with "server".

With the current ranking algorithm, the difference between "terminal" and "xterm" when you type in "term" is only 3 - I think it should be higher. And when you compare gnome-terminal with xfce terminal, doesn't seem like it even takes the command into account since both of them are titled "Terminal" and it does qMax(stringRank(mCommand, pattern), stringRank(mTitle, pattern)).

I haven't played with the code yet, so I can't say any specifics.

It still seems a bit "wrong" to me though, since traditionally
launchers (in KDE3, Gnome2, Xfce) were expected to just launch the
command that's typed in...

Razor-runner is more like a krunner.

KRunner still launches the command when you type it in, though:

pic
pic

It actually does not start searching until there are at least 3 characters, so ed is the only result.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
const CommandProviderItem *item = mSourceModel->command(srcIndex);
if (!item)
continue;
- int d = item->tile().indexOf(pattern, 0, Qt::CaseInsensitive);
- if (d<delta)
+ unsigned int r = item->rank(pattern);
+ if (r > rank)
{
res = ind;
- delta = d;
+ rank = r;
}
- if (delta==0)
+ if (rank >= MAX_RANK)
break;
}
@@ -169,8 +180,14 @@ void CommandItemModel::rebuild()
CommandSourceItemModel::CommandSourceItemModel(QObject *parent) :
QAbstractListModel(parent)
{
+ mCustomCommandProvider = new CustomCommandProvider;
+ mProviders.append(mCustomCommandProvider);
+ rebuild();
+ mCustomCommandIndex = index(0, 0);
+
mHistoryProvider = new HistoryProvider();
mProviders.append(mHistoryProvider);
+
mProviders.append(new AppLinkProvider());
#ifdef MATH_ENABLED
mProviders.append(new MathProvider());
@@ -230,7 +247,7 @@ QVariant CommandSourceItemModel::data(const QModelIndex &index, int role) const
switch (role)
{
case Qt::DisplayRole:
- return QString("<b>%1</b><br>\n%2\n").arg(item->tile(), item->comment());
+ return QString("<b>%1</b><br>\n%2\n").arg(item->title(), item->comment());
case Qt::DecorationRole:
return item->icon();
View
9 razorqt-runner/commanditemmodel.h
@@ -50,12 +50,18 @@ class CommandSourceItemModel: public QAbstractListModel
void addHistoryCommand(const QString &command);
+ QString command() const { return mCustomCommandProvider->command(); }
+ void setCommand(const QString &command) { mCustomCommandProvider->setCommand(command); }
+
+ QPersistentModelIndex customCommandIndex() const { return mCustomCommandIndex; }
public slots:
void rebuild();
private:
QList<CommandProvider*> mProviders;
HistoryProvider *mHistoryProvider;
+ CustomCommandProvider *mCustomCommandProvider;
+ QPersistentModelIndex mCustomCommandIndex;
};
@@ -76,6 +82,9 @@ class CommandItemModel: public QSortFilterProxyModel
bool isShowOnlyHistory() const { return mOnlyHistory; }
void showOnlyHistory(bool onlyHistory) { mOnlyHistory = onlyHistory; }
+ QString command() const { return mSourceModel->command(); }
+ void setCommand(const QString &command) { mSourceModel->setCommand(command); }
+
public slots:
void rebuild();
View
14 razorqt-runner/dialog.cpp
@@ -68,13 +68,10 @@ Dialog::Dialog(QWidget *parent) :
connect(mSettings, SIGNAL(settingsChanged()), this, SLOT(applySettings()));
-
ui->commandEd->installEventFilter(this);
- ui->commandEd->setInsertPolicy(QComboBox::NoInsert);
- ui->commandEd->setCompleter(0);
connect(ui->commandEd, SIGNAL(textChanged(QString)), this, SLOT(setFilter(QString)));
- connect(ui->commandEd->lineEdit(), SIGNAL(returnPressed()), this, SLOT(runCommand()));
+ connect(ui->commandEd, SIGNAL(returnPressed()), this, SLOT(runCommand()));
mCommandItemModel = new CommandItemModel(this);
@@ -168,7 +165,7 @@ bool Dialog::editKeyPressEvent(QKeyEvent *event)
{
case Qt::Key_Up:
case Qt::Key_PageUp:
- if (ui->commandEd->currentText().isEmpty() &&
+ if (ui->commandEd->text().isEmpty() &&
ui->commandList->isVisible() &&
ui->commandList->currentIndex().row() == 0
)
@@ -181,7 +178,7 @@ bool Dialog::editKeyPressEvent(QKeyEvent *event)
case Qt::Key_Down:
case Qt::Key_PageDown:
- if (ui->commandEd->currentText().isEmpty() &&
+ if (ui->commandEd->text().isEmpty() &&
ui->commandList->isHidden()
)
{
@@ -313,6 +310,7 @@ void Dialog::setFilter(const QString &text, bool onlyHistory)
if (mCommandItemModel->isOutDated())
mCommandItemModel->rebuild();
+ mCommandItemModel->setCommand(text);
mCommandItemModel->showOnlyHistory(onlyHistory);
mCommandItemModel->setFilterWildcard(text);
@@ -346,7 +344,7 @@ void Dialog::runCommand()
}
else
{
- QString command = ui->commandEd->currentText();
+ QString command = ui->commandEd->text();
res = QProcess::startDetached(command);
if (res)
{
@@ -357,7 +355,7 @@ void Dialog::runCommand()
if (res)
{
hide();
- ui->commandEd->clearEditText();
+ ui->commandEd->clear();
}
}
View
11 razorqt-runner/dialog.ui
@@ -79,11 +79,7 @@
</widget>
</item>
<item>
- <widget class="CommandComboBox" name="commandEd">
- <property name="editable">
- <bool>true</bool>
- </property>
- </widget>
+ <widget class="QLineEdit" name="commandEd"/>
</item>
<item>
<widget class="QToolButton" name="closeButton">
@@ -146,11 +142,6 @@
<header>widgets.h</header>
<container>1</container>
</customwidget>
- <customwidget>
- <class>CommandComboBox</class>
- <extends>QComboBox</extends>
- <header>widgets.h</header>
- </customwidget>
</customwidgets>
<tabstops>
<tabstop>closeButton</tabstop>
View
234 razorqt-runner/providers.cpp
@@ -37,47 +37,27 @@
#include <QtCore/QtAlgorithms>
#include <QtCore/QFileInfo>
#include <QtCore/QSettings>
+#include <QtCore/QDir>
+#include <QtGui/QApplication>
#include <razorqt-runner/providers.h>
+#include <wordexp.h>
#define MAX_HISORTY 100
/************************************************
************************************************/
-CommandProviderItem::CommandProviderItem()
+unsigned int CommandProviderItem::stringRank(const QString str, const QString pattern) const
{
-}
-
-
-/************************************************
-
- ************************************************/
-CommandProviderItem::~CommandProviderItem()
-{
-}
-
-
-/************************************************
-
- ************************************************/
-void CommandProviderItem::setTile(const QString &title)
-{
- mTitle = title;
-}
+ int n = str.indexOf(pattern, 0, Qt::CaseInsensitive);
+ if (n<0)
+ return 0;
-
-/************************************************
-
- ************************************************/
-void CommandProviderItem::setComment(const QString &comment)
-{
- mComment = comment;
+ return MAX_RANK - ((str.length() - pattern.length()) + n * 5);
}
-
-
/************************************************
************************************************/
@@ -104,11 +84,10 @@ AppLinkItem::AppLinkItem(const QDomElement &element):
CommandProviderItem()
{
mIconName = element.attribute("icon");
- setTile(element.attribute("title"));
- setComment(element.attribute("genericName"));
- setToolTip(element.attribute("comment"));
+ mTitle = element.attribute("title");
+ mComment = element.attribute("genericName");
+ mToolTip = element.attribute("comment");
mCommand = QFileInfo(element.attribute("exec")).baseName().section(" ", 0, 0);
- mSearchText = element.attribute("title") + " " + mCommand;
mDesktopFile = element.attribute("desktopFile");
}
@@ -118,8 +97,8 @@ AppLinkItem::AppLinkItem(const QDomElement &element):
************************************************/
void AppLinkItem::updateIcon()
{
- if (icon().isNull())
- setIcon(XdgIcon::fromTheme(mIconName));
+ if (mIcon.isNull())
+ mIcon = XdgIcon::fromTheme(mIconName);
}
@@ -128,16 +107,26 @@ void AppLinkItem::updateIcon()
************************************************/
void AppLinkItem::operator=(const AppLinkItem &other)
{
- setTile(other.tile());
- setComment(other.comment());
- setToolTip(other.toolTip());
+ mTitle = other.title();
+ mComment = other.comment();
+ mToolTip = other.toolTip();
mCommand = other.mCommand;
- mSearchText = other.mSearchText;
mDesktopFile = other.mDesktopFile;
mIconName = other.mIconName;
- setIcon(other.icon());
+ mIcon = other.icon();
+}
+
+
+/************************************************
+
+ ************************************************/
+unsigned int AppLinkItem::rank(const QString &pattern) const
+{
+ return qMax(stringRank(mCommand, pattern),
+ stringRank(mTitle, pattern)
+ );
}
@@ -162,7 +151,8 @@ bool AppLinkItem::compare(const QRegExp &regExp) const
QRegExp re(regExp);
re.setCaseSensitivity(Qt::CaseInsensitive);
- return mSearchText.contains(re);
+ return mCommand.contains(re) ||
+ mTitle.contains(re) ;
}
@@ -264,9 +254,9 @@ void AppLinkProvider::update()
HistoryItem::HistoryItem(const QString &command):
CommandProviderItem()
{
- setIcon(XdgIcon::defaultApplicationIcon());
- setTile(command);
- setComment(QObject::tr("History"));
+ mIcon = XdgIcon::defaultApplicationIcon();
+ mTitle = command;
+ mComment = QObject::tr("History");
mCommand = command;
}
@@ -292,7 +282,13 @@ bool HistoryItem::compare(const QRegExp &regExp) const
}
+/************************************************
+ ************************************************/
+unsigned int HistoryItem::rank(const QString &pattern) const
+{
+ return stringRank(mCommand, pattern);
+}
/************************************************
@@ -341,27 +337,156 @@ void HistoryProvider::AddCommand(const QString &command)
}
}
+
+
+/************************************************
+
+ ************************************************/
+CustomCommandItem::CustomCommandItem():
+ CommandProviderItem()
+{
+ mIcon = XdgIcon::fromTheme("utilities-terminal");
+}
+
+
+/************************************************
+
+ ************************************************/
+QString which(const QString &progName)
+{
+ if (progName.isEmpty())
+ return "";
+
+ if (progName.startsWith(QDir::separator()))
+ {
+ if (QFileInfo(progName).isExecutable())
+ QFileInfo(progName).absoluteFilePath();
+ }
+
+ QStringList dirs = QString(getenv("PATH")).split(":");
+
+ foreach (QString dir, dirs)
+ {
+ if (QFileInfo(QDir(dir), progName).isExecutable())
+ return QFileInfo(QDir(dir), progName).absoluteFilePath();
+ }
+
+ return "";
+}
+
+
+/************************************************
+
+ ************************************************/
+QString expandCommand(const QString &command, QStringList *arguments=0)
+{
+ QString program;
+ wordexp_t words;
+
+ if (wordexp(command.toLocal8Bit().data(), &words, 0) != 0)
+ return "";
+
+ char **w;
+ w = words.we_wordv;
+ program = QString::fromLocal8Bit(w[0]);
+
+ if (arguments)
+ {
+ for (size_t i = 1; i < words.we_wordc; i++)
+ *arguments << w[i];
+ }
+
+ wordfree(&words);
+ return program;
+}
+
+
+/************************************************
+
+ ************************************************/
+void CustomCommandItem::setCommand(const QString &command)
+{
+ mCommand = command;
+ mTitle = mCommand;
+
+ QString program = which(expandCommand(command));
+
+ if (!program.isEmpty())
+ mComment = QString("%1 %2").arg(program, command.section(' ', 1));
+ else
+ mComment = "";
+
+}
+
+
+/************************************************
+
+ ************************************************/
+bool CustomCommandItem::run() const
+{
+ QStringList args;
+ QString program = expandCommand(mCommand, &args);
+ if (program.isEmpty())
+ return false;
+
+ return QProcess::startDetached(program, args);
+}
+
+
+/************************************************
+
+ ************************************************/
+bool CustomCommandItem::compare(const QRegExp &regExp) const
+{
+ return !mComment.isEmpty();
+}
+
+
+/************************************************
+
+ ************************************************/
+unsigned int CustomCommandItem::rank(const QString &pattern) const
+{
+ return 0;
+}
+
+
+/************************************************
+
+ ************************************************/
+CustomCommandProvider::CustomCommandProvider():
+ CommandProvider()
+{
+ mItem = new CustomCommandItem();
+ append(mItem);
+}
+
#ifdef VBOX_ENABLED
VirtualBoxItem::VirtualBoxItem(const QString & MachineName , const QIcon & Icon):
CommandProviderItem()
{
- setTile (MachineName);
- setIcon (Icon);
+ mTitle = MachineName;
+ mIcon = Icon;
}
bool VirtualBoxItem::run() const
{
QStringList arguments;
- arguments << "startvm" << tile();
+ arguments << "startvm" << title();
return QProcess::startDetached ("VBoxManage" , arguments);
}
bool VirtualBoxItem::compare(const QRegExp &regExp) const
{
QRegExp re(regExp);
re.setCaseSensitivity(Qt::CaseInsensitive);
- //qDebug() << "Title: " << re.indexIn (tile ());
- return (-1 != re.indexIn (tile ()));
+ //qDebug() << "Title: " << re.indexIn (title ());
+ return (-1 != re.indexIn (title ()));
+}
+
+unsigned int VirtualBoxItem::rank(const QString &pattern) const
+{
+ return stringRank(mTitle, pattern);
}
///////
@@ -508,8 +633,8 @@ bool VirtualBoxProvider::isOutDated() const
MathItem::MathItem():
CommandProviderItem()
{
- setToolTip(QObject::tr("Mathematics"));
- setIcon(XdgIcon::fromTheme("accessories-calculator"));
+ mToolTip =QObject::tr("Mathematics");
+ mIcon = XdgIcon::fromTheme("accessories-calculator");
}
@@ -537,14 +662,21 @@ bool MathItem::compare(const QRegExp &regExp) const
if (res.isNumber())
{
MathItem *self=const_cast<MathItem*>(this);
- self->setTile(s + " = " + res.toString());
+ self->mTitle = s + " = " + res.toString();
return true;
}
}
return false;
}
+/************************************************
+
+ ************************************************/
+unsigned int MathItem::rank(const QString &pattern) const
+{
+ return stringRank(mTitle, pattern);
+}
/************************************************
View
83 razorqt-runner/providers.h
@@ -35,36 +35,53 @@
#include <QtCore/QString>
#include <QtGui/QIcon>
+#define MAX_RANK 0xFFFF
+
+/*! The CommandProviderItem class provides an item for use with CommandProvider.
+ Items usually contain title, comment, toolTip and icon.
+ */
class CommandProviderItem
{
public:
- CommandProviderItem();
- virtual ~CommandProviderItem();
+ CommandProviderItem() {}
+ virtual ~CommandProviderItem() {}
virtual bool run() const = 0;
virtual bool compare(const QRegExp &regExp) const = 0;
+ /// Returns the item's icon.
QIcon icon() const { return mIcon; }
- QString tile() const { return mTitle; }
+
+ /// Returns the item's title.
+ QString title() const { return mTitle; }
+
+ /// Returns the item's comment.
QString comment() const { return mComment; }
- QString toolTip() const { return mToolTip; }
-protected:
- void setIcon(const QIcon &icon) { mIcon = icon; }
- void setTile(const QString &title);
- void setComment(const QString &comment);
- void setToolTip(const QString &toolTip) { mToolTip = toolTip; }
+ /// Returns the item's tooltip.
+ QString toolTip() const { return mToolTip; }
+ /*! The result of this function is used when searching for a apropriate item.
+ The item with the highest rank will be highlighted.
+ 0 - not suitable.
+ MAX_RANK - an exact match.
+ In the most cases you can yse something like:
+ return stringRank(mTitle, pattern);
+ */
+ virtual unsigned int rank(const QString &pattern) const = 0;
-private:
+protected:
+ /// Helper function for the CommandProviderItem::rank
+ unsigned int stringRank(const QString str, const QString pattern) const;
QIcon mIcon;
QString mTitle;
QString mComment;
QString mToolTip;
};
-
+/*! The CommandProvider class provides task for the razor-runner.
+ */
class CommandProvider: public QObject, public QList<CommandProviderItem*>
{
Q_OBJECT
@@ -96,9 +113,9 @@ class AppLinkItem: public CommandProviderItem
QString command() const { return mCommand; }
void operator=(const AppLinkItem &other);
+
+ virtual unsigned int rank(const QString &pattern) const;
private:
-public:
- QString mSearchText;
QString mDesktopFile;
QString mIconName;
QString mCommand;
@@ -134,6 +151,7 @@ class HistoryItem: public CommandProviderItem
bool compare(const QRegExp &regExp) const;
QString command() const { return mCommand; }
+ virtual unsigned int rank(const QString &pattern) const;
private:
QString mCommand;
@@ -157,6 +175,43 @@ class HistoryProvider: public CommandProvider
/************************************************
+ * Custom command
+ ************************************************/
+
+class CustomCommandItem: public CommandProviderItem
+{
+public:
+ CustomCommandItem();
+
+ bool run() const;
+ bool compare(const QRegExp &regExp) const;
+
+ QString command() const { return mCommand; }
+ void setCommand(const QString &command);
+
+ virtual unsigned int rank(const QString &pattern) const;
+private:
+ QString mCommand;
+};
+
+
+
+class QSettings;
+class CustomCommandProvider: public CommandProvider
+{
+public:
+ CustomCommandProvider();
+
+ QString command() const { return mItem->command(); }
+ void setCommand(const QString &command) { mItem->setCommand(command); }
+
+private:
+ CustomCommandItem *mItem;
+};
+
+
+
+/************************************************
* Mathematics
************************************************/
class MathItem: public CommandProviderItem
@@ -166,6 +221,7 @@ class MathItem: public CommandProviderItem
bool run() const;
bool compare(const QRegExp &regExp) const;
+ virtual unsigned int rank(const QString &pattern) const;
};
@@ -189,6 +245,7 @@ class VirtualBoxItem: public CommandProviderItem
bool run() const;
bool compare(const QRegExp &regExp) const;
+ virtual unsigned int rank(const QString &pattern) const;
};
class VirtualBoxProvider: public CommandProvider

0 comments on commit 0f01689

Please sign in to comment.
Something went wrong with that request. Please try again.