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 ®Exp) 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 ®Exp) 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 ®Exp) 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 ®Exp) 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