1 /* BEGIN_COMMON_COPYRIGHT_HEADER
2  * (c)LGPL2+
3  *
4  * LXQt - a lightweight, Qt based, desktop toolset
5  * https://lxqt.org
6  *
7  * Copyright: 2010-2013 Razor team
8  * Authors:
9  *   Alexander Sokoloff <sokoloff.a@gmail.com>
10  *   Aaron Lewis <the.warl0ck.1989@gmail.com>
11  *   Petr Vanek <petr@scribus.info>
12  *   Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
13  *
14  * This program or library is free software; you can redistribute it
15  * and/or modify it under the terms of the GNU Lesser General Public
16  * License as published by the Free Software Foundation; either
17  * version 2.1 of the License, or (at your option) any later version.
18  *
19  * This library is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22  * Lesser General Public License for more details.
23 
24  * You should have received a copy of the GNU Lesser General
25  * Public License along with this library; if not, write to the
26  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
27  * Boston, MA 02110-1301 USA
28  *
29  * END_COMMON_COPYRIGHT_HEADER */
30 
31 
32 #include "providers.h"
33 #include "yamlparser.h"
34 #include <XdgDesktopFile>
35 #include <XdgMenu>
36 #include <XmlHelper>
37 #include <XdgDirs>
38 
39 #include <QProcess>
40 #include <QtAlgorithms>
41 #include <QFileInfo>
42 #include <QSettings>
43 #include <QDir>
44 #include <QApplication>
45 #include <QAction>
46 #include <LXQt/Globals>
47 #include <LXQt/PowerManager>
48 #include <LXQt/ScreenSaver>
49 #include "providers.h"
50 #include <LXQtGlobalKeys/Action>
51 #include <wordexp.h>
52 #include <QStandardPaths>
53 
54 #define MAX_HISTORY 100
55 
56 
57 /************************************************
58 
59  ************************************************/
expandCommand(const QString & command,QStringList * arguments=0)60 static QString expandCommand(const QString &command, QStringList *arguments=0)
61 {
62     QString program;
63     wordexp_t words;
64 
65     if (wordexp(command.toLocal8Bit().data(), &words, 0) != 0)
66         return QString();
67 
68     char **w;
69     w = words.we_wordv;
70     program = QString::fromLocal8Bit(w[0]);
71 
72     if (arguments)
73     {
74         for (size_t i = 1; i < words.we_wordc; i++)
75             *arguments << QString::fromLocal8Bit(w[i]);
76     }
77 
78     wordfree(&words);
79     return program;
80 }
81 
82 
83 /************************************************
84 
85  ************************************************/
which(const QString & progName)86 static QString which(const QString &progName)
87 {
88     if (progName.isEmpty())
89         return QString();
90 
91     if (progName.startsWith(QDir::separator()))
92     {
93         QFileInfo fileInfo(progName);
94         if (fileInfo.isExecutable() && fileInfo.isFile())
95             return fileInfo.absoluteFilePath();
96     }
97 
98     const QStringList dirs = QFile::decodeName(qgetenv("PATH")).split(QL1C(':'));
99 
100     for (const QString &dir : dirs)
101     {
102         QFileInfo fileInfo(QDir(dir), progName);
103         if (fileInfo.isExecutable() && fileInfo.isFile())
104             return fileInfo.absoluteFilePath();
105     }
106 
107     return QString();
108 }
109 
110 
111 /************************************************
112 
113  ************************************************/
startProcess(QString command)114 static bool startProcess(QString command)
115 {
116     QStringList args;
117     QString program  = expandCommand(command, &args);
118     if (program.isEmpty())
119         return false;
120     if (QProcess::startDetached(program, args))
121     {
122         return true;
123     } else
124     {
125         //fallback for executable script with no #!
126         //trying as in system(2)
127         args.prepend(program);
128         args.prepend(QSL("-c"));
129         return QProcess::startDetached(QSL("/bin/sh"), args);
130     }
131 
132 }
133 
134 
135 /************************************************
136 
137  ************************************************/
stringRank(const QString str,const QString pattern) const138 unsigned int CommandProviderItem::stringRank(const QString str, const QString pattern) const
139 {
140     int n = str.indexOf(pattern, 0, Qt::CaseInsensitive);
141     if (n<0)
142         return 0;
143 
144     return MAX_RANK - ((str.length() - pattern.length()) + n * 5);
145 }
146 
147 
148 /************************************************
149 
150  ************************************************/
CommandProvider()151 CommandProvider::CommandProvider():
152     QObject(),
153     QList<CommandProviderItem*>()
154 {
155 }
156 
157 
158 /************************************************
159 
160  ************************************************/
~CommandProvider()161 CommandProvider::~CommandProvider()
162 {
163 //    qDebug() << "*****************************************";
164 //    qDebug() << hex << this;
165 //    qDebug() << "DESTROY";
166     qDeleteAll(*this);
167 //    qDebug() << "*****************************************";
168 }
169 
170 
171 /************************************************
172 
173  ************************************************/
AppLinkItem(const QDomElement & element)174 AppLinkItem::AppLinkItem(const QDomElement &element):
175         CommandProviderItem()
176 {
177     mIconName = element.attribute(QLatin1String("icon"));
178     mTitle = element.attribute(QLatin1String("title"));
179     mComment = element.attribute(QSL("genericName"));
180     mToolTip = element.attribute(QLatin1String("comment"));
181     mCommand = element.attribute(QL1S("exec"));
182     mProgram = QFileInfo(element.attribute(QL1S("exec"))).baseName().section(QL1C(' '), 0, 0);
183     mDesktopFile = element.attribute(QSL("desktopFile"));
184     initExec();
185     QMetaObject::invokeMethod(this, "updateIcon", Qt::QueuedConnection);
186 }
187 
188 #ifdef HAVE_MENU_CACHE
AppLinkItem(MenuCacheApp * app)189 AppLinkItem::AppLinkItem(MenuCacheApp* app):
190         CommandProviderItem()
191 {
192     MenuCacheItem* item = MENU_CACHE_ITEM(app);
193     mIconName = QString::fromUtf8(menu_cache_item_get_icon(item));
194     mTitle = QString::fromUtf8(menu_cache_item_get_name(item));
195     mComment = QString::fromUtf8(menu_cache_app_get_generic_name(app));
196     mToolTip = QString::fromUtf8(menu_cache_item_get_comment(item));
197     mCommand = QString::fromUtf8(menu_cache_app_get_exec(app));
198     mProgram = QFileInfo(mCommand).baseName().section(QL1C(' '), 0, 0);
199     char* path = menu_cache_item_get_file_path(MENU_CACHE_ITEM(app));
200     mDesktopFile = QString::fromLocal8Bit(path);
201     g_free(path);
202     initExec();
203     QMetaObject::invokeMethod(this, "updateIcon", Qt::QueuedConnection);
204     // qDebug() << "FOUND: " << mIconName << ", " << mCommand;
205 }
206 #endif
207 
208 /************************************************
209 
210  ************************************************/
updateIcon()211 void AppLinkItem::updateIcon()
212 {
213 //    qDebug() << "*****************************************";
214 //    qDebug() << hex << this;
215 //    qDebug() << Q_FUNC_INFO;
216     if (mIcon.isNull())
217         mIcon = QIcon::fromTheme(mIconName);
218 //    qDebug() << "*****************************************";
219 }
220 
221 
222 /************************************************
223 
224  ************************************************/
operator =(const AppLinkItem & other)225 void AppLinkItem::operator=(const AppLinkItem &other)
226 {
227     mTitle = other.title();
228     mComment = other.comment();
229     mToolTip = other.toolTip();
230 
231     mCommand = other.mCommand;
232     mProgram = other.mProgram;
233     mDesktopFile = other.mDesktopFile;
234     mExec = other.mExec;
235 
236     mIconName = other.mIconName;
237     mIcon = other.icon();
238 }
239 
240 
241 /************************************************
242 
243  ************************************************/
rank(const QString & pattern) const244 unsigned int AppLinkItem::rank(const QString &pattern) const
245 {
246     unsigned int result =  qMax(stringRank(mProgram, pattern),
247                                 stringRank(mTitle, pattern)
248                                 );
249     return result;
250 }
251 
252 
253 /************************************************
254 
255  ************************************************/
run() const256 bool AppLinkItem::run() const
257 {
258     XdgDesktopFile desktop;
259     if (desktop.load(mDesktopFile))
260         return desktop.startDetached();
261     else
262         return false;
263 }
264 
265 
266 /************************************************
267 
268  ************************************************/
compare(const QRegExp & regExp) const269 bool AppLinkItem::compare(const QRegExp &regExp) const
270 {
271     if (regExp.isEmpty())
272         return false;
273 
274     return mProgram.contains(regExp)
275         || mTitle.contains(regExp)
276         || mComment.contains(regExp)
277         || mToolTip.contains(regExp);
278 }
279 
280 
281 /************************************************
282 
283  ************************************************/
initExec()284 void AppLinkItem::initExec()
285 {
286     static const QRegExp split_re{QSL("\\s")};
287     XdgDesktopFile desktop;
288     if (desktop.load(mDesktopFile))
289     {
290         QStringList cmd = desktop.value(QSL("Exec")).toString().split(split_re);
291         if (0 < cmd.size())
292             mExec = which(expandCommand(cmd[0]));
293     }
294 }
295 
296 
297 /************************************************
298 
299  ************************************************/
AppLinkProvider()300 AppLinkProvider::AppLinkProvider():
301         CommandProvider()
302 {
303 #ifdef HAVE_MENU_CACHE
304     menu_cache_init(0);
305     mMenuCache = menu_cache_lookup(XdgMenu::getMenuFileName().toLocal8Bit().constData());
306     if(mMenuCache)
307         mMenuCacheNotify = menu_cache_add_reload_notify(mMenuCache, (MenuCacheReloadNotify)menuCacheReloadNotify, this);
308     else
309         mMenuCacheNotify = 0;
310 #else
311     mXdgMenu = new XdgMenu();
312     mXdgMenu->setEnvironments(QStringList() << QSL("X-LXQT") << QSL("LXQt"));
313     connect(mXdgMenu, &XdgMenu::changed, this, &AppLinkProvider::update);
314     mXdgMenu->read(XdgMenu::getMenuFileName());
315     update();
316 #endif
317 }
318 
319 
320 /************************************************
321 
322  ************************************************/
~AppLinkProvider()323 AppLinkProvider::~AppLinkProvider()
324 {
325 #ifdef HAVE_MENU_CACHE
326     if(mMenuCache)
327     {
328         menu_cache_remove_reload_notify(mMenuCache, mMenuCacheNotify);
329         menu_cache_unref(mMenuCache);
330     }
331 #else
332     delete mXdgMenu;
333 #endif
334 }
335 
336 
337 /************************************************
338 
339  ************************************************/
340  #ifdef HAVE_MENU_CACHE
341 
menuCacheReloadNotify(MenuCache * cache,gpointer user_data)342 void AppLinkProvider::menuCacheReloadNotify(MenuCache* cache, gpointer user_data)
343 {
344     // qDebug() << Q_FUNC_INFO;
345     reinterpret_cast<AppLinkProvider*>(user_data)->update();
346 }
347 
348  #else // without menu-cache, use libqtxdg
349 
doUpdate(const QDomElement & xml,QHash<QString,AppLinkItem * > & items)350  void doUpdate(const QDomElement &xml, QHash<QString, AppLinkItem*> &items)
351 {
352     DomElementIterator it(xml, QString());
353     while (it.hasNext())
354     {
355         QDomElement e = it.next();
356 
357         // Build submenu ........................
358         if (e.tagName() == QL1S("Menu"))
359             doUpdate(e, items);
360 
361         //Build application link ................
362         else if (e.tagName() == QL1S("AppLink"))
363         {
364             AppLinkItem *item = new AppLinkItem(e);
365             delete items[item->command()]; // delete previous item;
366             items.insert(item->command(), item);
367         }
368     }
369 }
370 #endif
371 
372 /************************************************
373 
374  ************************************************/
update()375 void AppLinkProvider::update()
376 {
377     emit aboutToBeChanged();
378     QHash<QString, AppLinkItem*> newItems;
379 
380 #ifdef HAVE_MENU_CACHE
381     // libmenu-cache is available, use it to get cached app list
382     GSList* apps = menu_cache_list_all_apps(mMenuCache);
383     for(GSList* l = apps; l; l = l->next)
384     {
385         MenuCacheApp* app = MENU_CACHE_APP(l->data);
386         AppLinkItem *item = new AppLinkItem(app);
387         AppLinkItem *prevItem = newItems[item->command()];
388         if(prevItem)
389             delete prevItem; // delete previous item;
390         newItems.insert(item->command(), item);
391         menu_cache_item_unref(MENU_CACHE_ITEM(app));
392     }
393     g_slist_free(apps);
394 #else
395     // use libqtxdg XdgMenu to get installed apps
396     doUpdate(mXdgMenu->xml().documentElement(), newItems);
397 #endif
398     {
399         QMutableListIterator<CommandProviderItem*> i(*this);
400         while (i.hasNext()) {
401             AppLinkItem *item = static_cast<AppLinkItem*>(i.next());
402             AppLinkItem *newItem = newItems.take(item->command());
403             if (newItem)
404             {
405                 *(item) = *newItem;  // Copy by value, not pointer!
406                 // After the item is copied, the original "updateIcon" call queued
407                 // on the newItem object is never called since the object iss going to
408                 // be deleted. Hence we need to call it on the copied item manually.
409                 // Otherwise the copied item will have no icon.
410                 // FIXME: this is a dirty hack and it should be made cleaner later.
411                 if(item->icon().isNull())
412                     QMetaObject::invokeMethod(item, "updateIcon", Qt::QueuedConnection);
413                 delete newItem;
414             }
415             else
416             {
417                 i.remove();
418                 delete item;
419             }
420         }
421     }
422 
423     {
424         QHashIterator<QString, AppLinkItem*> i(newItems);
425         while (i.hasNext())
426         {
427             append(i.next().value());
428         }
429     }
430 
431     emit changed();
432 }
433 
434 
435 
436 
437 /************************************************
438 
439  ************************************************/
HistoryItem(const QString & command)440 HistoryItem::HistoryItem(const QString &command):
441         CommandProviderItem()
442 {
443     mIcon = QIcon::fromTheme(QLatin1String("application-x-executable"));
444     mTitle = command;
445     mComment = QObject::tr("History");
446     mCommand = command;
447 }
448 
449 
450 /************************************************
451 
452  ************************************************/
run() const453 bool HistoryItem::run() const
454 {
455     return startProcess(mCommand);
456 }
457 
458 
459 /************************************************
460 
461  ************************************************/
compare(const QRegExp & regExp) const462 bool HistoryItem::compare(const QRegExp &regExp) const
463 {
464     return mCommand.contains(regExp);
465 }
466 
467 
468 /************************************************
469 
470  ************************************************/
rank(const QString & pattern) const471 unsigned int HistoryItem::rank(const QString &pattern) const
472 {
473     return stringRank(mCommand, pattern);
474 }
475 
476 
477 /************************************************
478 
479  ************************************************/
HistoryProvider()480 HistoryProvider::HistoryProvider():
481         CommandProvider()
482 {
483     QString fileName = (XdgDirs::cacheHome() + QSL("/lxqt-runner.history"));
484     mHistoryFile = new QSettings(fileName, QSettings::IniFormat);
485     mHistoryFile->beginGroup(QSL("commands"));
486     for (uint i=0; i<MAX_HISTORY; ++i)
487     {
488         QString key = QString::fromLatin1("%1").arg(i, 3, 10, QL1C('0'));
489         if (mHistoryFile->contains(key))
490         {
491             HistoryItem *item = new HistoryItem(mHistoryFile->value(key).toString());
492             append(item);
493         }
494     }
495 }
496 
497 
498 /************************************************
499 
500  ************************************************/
~HistoryProvider()501 HistoryProvider::~HistoryProvider()
502 {
503     delete mHistoryFile;
504 }
505 
506 
507 /************************************************
508 
509  ************************************************/
AddCommand(const QString & command)510 void HistoryProvider::AddCommand(const QString &command)
511 {
512     bool commandExists = false;
513     for (int i=0; !commandExists && i<length(); ++i)
514     {
515         if (command == static_cast<HistoryItem*>(at(i))->command())
516         {
517             move(i, 0);
518             commandExists = true;
519         }
520     }
521 
522     if (!commandExists)
523     {
524         HistoryItem *item = new HistoryItem(command);
525         insert(0, item);
526     }
527 
528     mHistoryFile->clear();
529     for (int i=0; i<qMin(length(), MAX_HISTORY); ++i)
530     {
531         QString key = QString::fromLatin1("%1").arg(i, 3, 10, QL1C('0'));
532         mHistoryFile->setValue(key, static_cast<HistoryItem*>(at(i))->command());
533     }
534 }
535 
clearHistory()536 void HistoryProvider::clearHistory()
537 {
538     clear();
539     mHistoryFile->clear();
540 }
541 
542 /************************************************
543 
544  ************************************************/
CustomCommandItem(CustomCommandProvider * provider)545 CustomCommandItem::CustomCommandItem(CustomCommandProvider *provider):
546     CommandProviderItem(),
547     mProvider(provider)
548 {
549     mIcon = QIcon::fromTheme(QSL("utilities-terminal"));
550 }
551 
552 
553 /************************************************
554 
555  ************************************************/
setCommand(const QString & command)556 void CustomCommandItem::setCommand(const QString &command)
557 {
558     mCommand = command;
559     mTitle = mCommand;
560 
561     mExec = which(expandCommand(command));
562 
563     if (!mExec.isEmpty())
564         mComment = QString::fromLatin1("%1 %2").arg(mExec, command.section(QL1C(' '), 1));
565     else
566         mComment = QString();
567 
568 }
569 
570 
571 /************************************************
572 
573  ************************************************/
run() const574 bool CustomCommandItem::run() const
575 {
576     bool ret = startProcess(mCommand);
577     if (ret && mProvider->historyProvider())
578         mProvider->historyProvider()->AddCommand(mCommand);
579 
580     return ret;
581 }
582 
583 
584 /************************************************
585 
586  ************************************************/
compare(const QRegExp &) const587 bool CustomCommandItem::compare(const QRegExp & /*regExp*/) const
588 {
589     return !mComment.isEmpty();
590 }
591 
592 
593 /************************************************
594 
595  ************************************************/
rank(const QString &) const596 unsigned int CustomCommandItem::rank(const QString & /*pattern*/) const
597 {
598     return 0;
599 }
600 
601 
602 /************************************************
603 
604  ************************************************/
CustomCommandProvider()605 CustomCommandProvider::CustomCommandProvider():
606     CommandProvider(),
607     mHistoryProvider(0)
608 {
609     mItem = new CustomCommandItem(this);
610     append(mItem);
611 }
612 
613 #ifdef VBOX_ENABLED
VirtualBoxItem(const QString & MachineName,const QIcon & Icon)614 VirtualBoxItem::VirtualBoxItem(const QString & MachineName , const QIcon & Icon):
615         CommandProviderItem()
616 {
617     mTitle = MachineName;
618     mIcon = Icon;
619 }
620 
setRDEPort(const QString & portNum)621 void VirtualBoxItem::setRDEPort(const QString & portNum)
622 {
623     m_rdePortNum = portNum;
624 }
625 
run() const626 bool VirtualBoxItem::run() const
627 {
628     QStringList arguments;
629 #ifdef VBOX_HEADLESS_ENABLED
630     arguments << QSL("-startvm") << title();
631     return QProcess::startDetached (QSL("VBoxHeadless") , arguments);
632 #else
633     arguments << QSL("startvm") << title();
634     return QProcess::startDetached (QSL("VBoxManage") , arguments);
635 #endif
636 
637 }
638 
compare(const QRegExp & regExp) const639 bool VirtualBoxItem::compare(const QRegExp &regExp) const
640 {
641     return (! regExp.isEmpty() && -1 != regExp.indexIn (title ()));
642 }
643 
rank(const QString & pattern) const644 unsigned int VirtualBoxItem::rank(const QString &pattern) const
645 {
646     return stringRank(mTitle, pattern);
647 }
648 
homeDir()649 inline QString homeDir() {
650     return QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
651 }
652 
653 ///////
VirtualBoxProvider()654 VirtualBoxProvider::VirtualBoxProvider():
655         virtualBoxConfig ( homeDir() + QSL("/.VirtualBox/VirtualBox.xml"))
656 {
657     fp.setFileName (virtualBoxConfig);
658 
659     static const char *kOSTypeIcons [][2] =
660     {
661         {"Other",           ":/vbox-icons/os_other.png"},
662         {"DOS",             ":/vbox-icons/os_dos.png"},
663         {"Netware",         ":/vbox-icons/os_netware.png"},
664         {"L4",              ":/vbox-icons/os_l4.png"},
665         {"Windows31",       ":/vbox-icons/os_win31.png"},
666         {"Windows95",       ":/vbox-icons/os_win95.png"},
667         {"Windows98",       ":/vbox-icons/os_win98.png"},
668         {"WindowsMe",       ":/vbox-icons/os_winme.png"},
669         {"WindowsNT4",      ":/vbox-icons/os_winnt4.png"},
670         {"Windows2000",     ":/vbox-icons/os_win2k.png"},
671         {"WindowsXP",       ":/vbox-icons/os_winxp.png"},
672         {"WindowsXP_64",    ":/vbox-icons/os_winxp_64.png"},
673         {"Windows2003",     ":/vbox-icons/os_win2k3.png"},
674         {"Windows2003_64",  ":/vbox-icons/os_win2k3_64.png"},
675         {"WindowsVista",    ":/vbox-icons/os_winvista.png"},
676         {"WindowsVista_64", ":/vbox-icons/os_winvista_64.png"},
677         {"Windows2008",     ":/vbox-icons/os_win2k8.png"},
678         {"Windows2008_64",  ":/vbox-icons/os_win2k8_64.png"},
679         {"Windows7",        ":/vbox-icons/os_win7.png"},
680         {"Windows7_64",     ":/vbox-icons/os_win7_64.png"},
681         {"WindowsNT",       ":/vbox-icons/os_win_other.png"},
682         {"OS2Warp3",        ":/vbox-icons/os_os2warp3.png"},
683         {"OS2Warp4",        ":/vbox-icons/os_os2warp4.png"},
684         {"OS2Warp45",       ":/vbox-icons/os_os2warp45.png"},
685         {"OS2eCS",          ":/vbox-icons/os_os2ecs.png"},
686         {"OS2",             ":/vbox-icons/os_os2_other.png"},
687         {"Linux22",         ":/vbox-icons/os_linux22.png"},
688         {"Linux24",         ":/vbox-icons/os_linux24.png"},
689         {"Linux24_64",      ":/vbox-icons/os_linux24_64.png"},
690         {"Linux26",         ":/vbox-icons/os_linux26.png"},
691         {"Linux26_64",      ":/vbox-icons/os_linux26_64.png"},
692         {"ArchLinux",       ":/vbox-icons/os_archlinux.png"},
693         {"ArchLinux_64",    ":/vbox-icons/os_archlinux_64.png"},
694         {"Debian",          ":/vbox-icons/os_debian.png"},
695         {"Debian_64",       ":/vbox-icons/os_debian_64.png"},
696         {"OpenSUSE",        ":/vbox-icons/os_opensuse.png"},
697         {"OpenSUSE_64",     ":/vbox-icons/os_opensuse_64.png"},
698         {"Fedora",          ":/vbox-icons/os_fedora.png"},
699         {"Fedora_64",       ":/vbox-icons/os_fedora_64.png"},
700         {"Gentoo",          ":/vbox-icons/os_gentoo.png"},
701         {"Gentoo_64",       ":/vbox-icons/os_gentoo_64.png"},
702         {"Mandriva",        ":/vbox-icons/os_mandriva.png"},
703         {"Mandriva_64",     ":/vbox-icons/os_mandriva_64.png"},
704         {"RedHat",          ":/vbox-icons/os_redhat.png"},
705         {"RedHat_64",       ":/vbox-icons/os_redhat_64.png"},
706         {"Ubuntu",          ":/vbox-icons/os_ubuntu.png"},
707         {"Ubuntu_64",       ":/vbox-icons/os_ubuntu_64.png"},
708         {"Xandros",         ":/vbox-icons/os_xandros.png"},
709         {"Xandros_64",      ":/vbox-icons/os_xandros_64.png"},
710         {"Linux",           ":/vbox-icons/os_linux_other.png"},
711         {"FreeBSD",         ":/vbox-icons/os_freebsd.png"},
712         {"FreeBSD_64",      ":/vbox-icons/os_freebsd_64.png"},
713         {"OpenBSD",         ":/vbox-icons/os_openbsd.png"},
714         {"OpenBSD_64",      ":/vbox-icons/os_openbsd_64.png"},
715         {"NetBSD",          ":/vbox-icons/os_netbsd.png"},
716         {"NetBSD_64",       ":/vbox-icons/os_netbsd_64.png"},
717         {"Solaris",         ":/vbox-icons/os_solaris.png"},
718         {"Solaris_64",      ":/vbox-icons/os_solaris_64.png"},
719         {"OpenSolaris",     ":/vbox-icons/os_opensolaris.png"},
720         {"OpenSolaris_64",  ":/vbox-icons/os_opensolaris_64.png"},
721         {"QNX",             ":/vbox-icons/os_qnx.png"},
722     };
723 
724     for (size_t n = 0; n < sizeof (kOSTypeIcons) / sizeof(kOSTypeIcons[0]); ++ n)
725     {
726         osIcons.insert (QL1S(kOSTypeIcons [n][0]), (QL1S(kOSTypeIcons [n][1])));
727     }
728 }
729 
rebuild()730 void VirtualBoxProvider::rebuild()
731 {
732     QDomDocument d;
733     if ( !d.setContent( &fp ) )
734     {
735         qDebug() << "Unable to parse: " << fp.fileName();
736         return;
737     }
738 
739     QDomNodeList _dnlist = d.elementsByTagName( QSL("MachineEntry") );
740     for ( int i = 0; i < _dnlist.count(); i++ )
741     {
742         const QDomNode & node = _dnlist.at( i );
743         const QString & ref = node.toElement().attribute( QSL("src") );
744         if ( ref.isEmpty() )
745         {
746             qDebug() << "MachineEntry with no src attribute";
747             continue;
748         }
749 
750         QFile m( ref );
751 
752         QDomDocument mspec;
753         if ( !mspec.setContent( &m ) )
754         {
755             qDebug() << "Could not parse machine file " << m.fileName();
756             continue;
757         }
758 
759         QDomNodeList _mlist = mspec.elementsByTagName( QSL("Machine") );
760         for ( int j = 0; j < _mlist.count(); j++ )
761         {
762          QDomNode mnode = _mlist.at( j );
763 
764          QString type = mnode.toElement().attribute( QSL("OSType") );
765          VirtualBoxItem *virtualBoxItem = new VirtualBoxItem
766             (
767              mnode.toElement().attribute( QSL("name") ) ,
768              QIcon ( osIcons.value (type , QSL(":/vbox-icons/os_other.png")) )
769             );
770 
771          const QDomNodeList & rdeportConfig = mnode.toElement().elementsByTagName(QSL("VRDEProperties"));
772          if ( ! rdeportConfig.isEmpty() )
773          {
774             QDomNode portNode = rdeportConfig.at(0).firstChild();
775             virtualBoxItem->setRDEPort( portNode.toElement().attribute(QSL("value")) );
776          }
777 
778          append ( virtualBoxItem );
779         }
780    }
781 
782     timeStamp = QDateTime::currentDateTime();
783 
784 }
785 
isOutDated() const786 bool VirtualBoxProvider::isOutDated() const
787 {
788     return fp.exists() && ( timeStamp < QFileInfo ( virtualBoxConfig ).lastModified () );
789 }
790 #endif
791 
792 
793 #ifdef MATH_ENABLED
794 #include <muParser.h>
795 #include <QClipboard>
796 
797 class MathItem::Parser : private mu::Parser
798 {
799 public:
Parser()800     Parser()
801         : mLocale(QLocale::system())
802     {
803         try
804         {
805             // use the system's locale instead of the "C"
806             const std::locale loc{""};
807             auto && numpunct = std::use_facet<std::numpunct<mu::char_type>>(loc);
808             SetDecSep(numpunct.decimal_point());
809             SetThousandsSep(0); // means no grouping for muparser API
810 
811             const char arg_sep = GetArgSep();
812             if (numpunct.decimal_point() == arg_sep)
813             {
814                 if (arg_sep == ',')
815                     SetArgSep(';');
816                 else
817                     SetArgSep(',');
818             }
819         } catch (const std::runtime_error & e)
820         {
821             qWarning().noquote() << "Unable to set locale for Math, " << e.what();
822         }
823 
824         // do not group in output
825         mLocale.setNumberOptions(mLocale.numberOptions() | QLocale::OmitGroupSeparator);
826     }
827 
828 
evalString(const QString & s)829     QString evalString(const QString & s)
830     {
831         SetExpr(s.toStdString());
832         return mLocale.toString(Eval()); // can throw
833     }
834 
835 private:
836     QLocale mLocale;
837 };
838 
839 /************************************************
840 
841  ************************************************/
MathItem()842 MathItem::MathItem():
843         CommandProviderItem(),
844         mParser{new Parser}
845 {
846     mToolTip = QObject::tr("Copy calculation result to clipboard");
847     mIcon = QIcon::fromTheme(QSL("accessories-calculator"));
848 }
849 
850 
851 /************************************************
852 
853  ************************************************/
~MathItem()854 MathItem::~MathItem()
855 {
856 }
857 
858 
859 /************************************************
860 
861  ************************************************/
run() const862 bool MathItem::run() const
863 {
864     int posResult = mTitle.indexOf(QL1C('='));
865     if (posResult > -1 && posResult < mTitle.size() - 1)
866     {
867         QApplication::clipboard()->setText(mTitle.mid(posResult + 1));
868         return true;
869     }
870     return false;
871 }
872 
873 
874 /************************************************
875 
876  ************************************************/
compare(const QRegExp & regExp) const877 bool MathItem::compare(const QRegExp &regExp) const
878 {
879     QString s = regExp.pattern().trimmed();
880 
881     bool is_math = false;
882     if (s.startsWith(QLatin1Char('=')))
883     {
884         is_math = true;
885         s.remove(0, 1);
886     }
887     if (s.endsWith(QLatin1Char('=')))
888     {
889         is_math = true;
890         s.chop(1);
891     }
892 
893     if (is_math)
894     {
895         if (s != mCachedInput)
896         {
897             MathItem * self = const_cast<MathItem*>(this);
898             mCachedInput = s;
899             self->mTitle.clear();
900 
901             //try to compute anything suitable
902             for (int attempts = 20; 0 < attempts && 0 < s.size(); s.chop(1), --attempts)
903             {
904                 try
905                 {
906                     self->mTitle = s + QL1C('=') + mParser->evalString(s);
907                     break;
908                 } catch (const mu::Parser::exception_type & e)
909                 {
910                     //don't do anything, return false -> no result will be showed
911                 }
912             }
913         }
914 
915         return !mTitle.isEmpty();
916     }
917 
918     return false;
919 }
920 
921 /************************************************
922 
923  ************************************************/
rank(const QString & pattern) const924 unsigned int MathItem::rank(const QString &pattern) const
925 {
926     return stringRank(mTitle, pattern);
927 }
928 
929 
930 /************************************************
931 
932  ************************************************/
MathProvider()933 MathProvider::MathProvider()
934 {
935     append(new MathItem());
936 }
937 
938 #endif
939 
ExternalProviderItem()940 ExternalProviderItem::ExternalProviderItem()
941 {
942 }
943 
setData(QMap<QString,QString> & data)944 bool ExternalProviderItem::setData(QMap<QString,QString> & data)
945 {
946     if (! (data.contains(QL1S("title")) && data.contains(QL1S("dialog/monitor"))))
947     {
948         return false;
949     }
950 
951     mTitle = data[QLatin1String("title")];
952     mComment = data[QLatin1String("comment")];
953     mToolTip = data[QSL("tooltip")];
954     mCommand = data[QL1S("dialog/monitor")];
955     if (data.contains(QL1S("icon")))
956         mIcon = QIcon::fromTheme(data[QL1S("icon")]);
957 
958     return true;
959 }
960 
961 
962 
run() const963 bool ExternalProviderItem::run() const
964 {
965     return startProcess(mCommand);
966 }
967 
rank(const QString & pattern) const968 unsigned int ExternalProviderItem::rank(const QString& pattern) const
969 {
970     return stringRank(title(), pattern);
971 }
972 
ExternalProvider(const QString name,const QString externalProgram)973 ExternalProvider::ExternalProvider(const QString name, const QString externalProgram) :
974 CommandProvider(), mName(name)
975 {
976     mExternalProcess = new QProcess(this);
977     mYamlParser = new YamlParser();
978     connect(mYamlParser, &YamlParser::newListOfMaps, this, &ExternalProvider::newListOfMaps);
979 
980     connect(mExternalProcess, &QProcess::readyRead, this, &ExternalProvider::readFromProcess);
981     mExternalProcess->start(externalProgram, QStringList());
982 }
983 
setSearchTerm(const QString searchTerm)984 void ExternalProvider::setSearchTerm(const QString searchTerm)
985 {
986     mExternalProcess->write(searchTerm.toUtf8());
987     mExternalProcess->write(QBAL("\n"));
988 //    mExternalProcess->write(QString("\n").toUtf8());
989 }
990 
newListOfMaps(QList<QMap<QString,QString>> maps)991 void ExternalProvider::newListOfMaps(QList<QMap<QString,QString> > maps)
992 {
993     emit aboutToBeChanged();
994 
995     qDeleteAll(*this);
996     clear();
997 
998     for(auto map : qAsConst(maps))
999     {
1000         ExternalProviderItem *item  = new ExternalProviderItem();
1001         if (item->setData(map))
1002             this->append(item);
1003         else
1004             delete item;
1005     }
1006 
1007     emit changed();
1008 }
1009 
readFromProcess()1010 void ExternalProvider::readFromProcess()
1011 {
1012     char ch;
1013     while (mExternalProcess->getChar(&ch))
1014     {
1015         if (ch == '\n')
1016         {
1017             QString textLine = QString::fromLocal8Bit(mBuffer);
1018             mYamlParser->consumeLine(textLine);
1019             mBuffer.clear();
1020         }
1021         else
1022         {
1023             mBuffer.append(ch);
1024         }
1025     }
1026 }
1027 
1028