aboutsummaryrefslogtreecommitdiff
path: root/VMProtect/help_browser.cc
diff options
context:
space:
mode:
authorjmpoep <OriginalEntryPoint@qq.com>2023-12-07 16:51:07 +0800
committerjmpoep <OriginalEntryPoint@qq.com>2023-12-07 16:51:07 +0800
commit28008a746a31abb7909dd86cb0cd413ac8943b0b (patch)
treea30b74b8cad548048c3c1551d652828ab76fa9bd /VMProtect/help_browser.cc
downloadvmprotect-3.5.1-master.tar
vmprotect-3.5.1-master.tar.gz
vmprotect-3.5.1-master.tar.bz2
vmprotect-3.5.1-master.zip
first commitHEADmaster
Diffstat (limited to 'VMProtect/help_browser.cc')
-rw-r--r--VMProtect/help_browser.cc689
1 files changed, 689 insertions, 0 deletions
diff --git a/VMProtect/help_browser.cc b/VMProtect/help_browser.cc
new file mode 100644
index 0000000..cb028e2
--- /dev/null
+++ b/VMProtect/help_browser.cc
@@ -0,0 +1,689 @@
+#include "help_browser.h"
+#include "message_dialog.h"
+#include "widgets.h"
+#include "moc/moc_help_browser.cc"
+#include "../core/objects.h"
+#include "../core/lang.h"
+#include "../core/inifile.h"
+#include "remotecontrol.h"
+
+/**
+ * HelpContentModel
+ */
+
+HelpContentModel::HelpContentModel(QHelpEngine *engine, QObject *parent)
+ : QIdentityProxyModel(parent)
+{
+ setSourceModel(engine->contentModel());
+}
+
+QVariant HelpContentModel::data(const QModelIndex &index, int role) const
+{
+ if (role == Qt::FontRole) {
+ if (!index.parent().isValid()) {
+ // top level
+ QFont font;
+ font.setBold(true);
+ return font;
+ }
+ }
+ return QIdentityProxyModel::data(index, role);
+}
+
+QVariant HelpContentModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (role == Qt::DisplayRole) {
+ if (section == 0)
+ return QString::fromUtf8(language[lsContents].c_str());
+ }
+ return QIdentityProxyModel::headerData(section, orientation, role);
+}
+
+QHelpContentItem *HelpContentModel::contentItemAt(const QModelIndex &index) const
+{
+ QHelpContentModel *model = qobject_cast<QHelpContentModel*>(sourceModel());
+ if (model)
+ return model->contentItemAt(index);
+ return NULL;
+}
+
+QModelIndex HelpContentModel::indexOf(const QUrl &link)
+{
+ if (link.scheme() != QLatin1String("qthelp"))
+ return QModelIndex();
+
+ QString fragment = link.fragment();
+ for (int i = 0; i < rowCount(); ++i) {
+ QHelpContentItem *item = contentItemAt(index(i, 0));
+ if (item && item->url().host() == link.host()) {
+ QString path = link.path();
+ //if (path.startsWith(QLatin1Char('/')))
+ // path = path.mid(1);
+ QModelIndex res = searchContentItem(index(i, 0), path);
+ if (res.isValid()) {
+ if (!fragment.isEmpty()) {
+ item = contentItemAt(res);
+ assert(item);
+ for (int j = 0; item && (j < item->childCount()); ++j) {
+ auto ch = item->child(j);
+ assert(ch);
+ if (ch && (ch->url().fragment() == fragment)) {
+ res = index(j, 0, res);
+ break;
+ }
+ }
+ }
+ return res;
+ }
+ }
+ }
+ return QModelIndex();
+}
+
+QModelIndex HelpContentModel::searchContentItem(const QModelIndex &parent, const QString &path)
+{
+ QHelpContentItem *parentItem = contentItemAt(parent);
+ if (!parentItem)
+ return QModelIndex();
+
+ QString cpath = parentItem->url().path();
+ if (cpath == path)
+ return parent;
+
+ for (int i = 0; i < parentItem->childCount(); ++i) {
+ QModelIndex res = searchContentItem(index(i, 0, parent), path);
+ if (res.isValid())
+ return res;
+ }
+ return QModelIndex();
+}
+
+/**
+ * HelpResultItem
+ */
+
+HelpResultItem::HelpResultItem(const QString &title, const QString &url, const QString &toolTip)
+ : title_(title), url_(url), toolTip_(toolTip), parent_(NULL)
+{
+
+}
+
+HelpResultItem::~HelpResultItem()
+{
+ clear();
+ if (parent_)
+ parent_->removeChild(this);
+}
+
+void HelpResultItem::clear()
+{
+ for (int i = 0; i < children_.size(); i++) {
+ HelpResultItem *item = children_[i];
+ item->parent_ = NULL;
+ delete item;
+ }
+ children_.clear();
+}
+
+void HelpResultItem::addChild(HelpResultItem *child)
+{
+ insertChild(childCount(), child);
+}
+
+void HelpResultItem::insertChild(int index, HelpResultItem *child)
+{
+ if (child->parent())
+ return;
+
+ child->parent_ = this;
+ children_.insert(index, child);
+}
+
+void HelpResultItem::removeChild(HelpResultItem *child)
+{
+ if (children_.removeOne(child))
+ child->parent_ = NULL;
+}
+
+/**
+ * HelpResultModel
+ */
+
+HelpResultModel::HelpResultModel(QHelpEngine *engine, QObject *parent)
+ : QAbstractItemModel(parent), engine_(engine)
+{
+ root_ = new HelpResultItem(QString(), QString(), QString());
+
+ connect(engine_->searchEngine(), SIGNAL(searchingFinished(int)), this, SLOT(searchingFinished()));
+}
+
+HelpResultModel::~HelpResultModel()
+{
+ delete root_;
+}
+
+QModelIndex HelpResultModel::parent(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return QModelIndex();
+
+ HelpResultItem *childItem = itemAt(index);
+ HelpResultItem *parentItem = childItem->parent();
+ if (parentItem == root_)
+ return QModelIndex();
+
+ return createIndex(parentItem->parent()->children().indexOf(parentItem), 0, parentItem);
+}
+
+int HelpResultModel::columnCount(const QModelIndex & /* parent */) const
+{
+ return 1;
+}
+
+QVariant HelpResultModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (role == Qt::DisplayRole) {
+ if (section == 0)
+ return QString::fromUtf8(language[lsSearchResult].c_str());
+ }
+ return QVariant();
+}
+
+int HelpResultModel::rowCount(const QModelIndex &parent) const
+{
+ HelpResultItem *parentItem = itemAt(parent);
+ return parentItem->childCount();
+}
+
+QVariant HelpResultModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ HelpResultItem *item = itemAt(index);
+ switch (role) {
+ case Qt::DisplayRole:
+ return item->title();
+ case Qt::ToolTipRole:
+ return item->toolTip();
+ case Qt::FontRole:
+ if (!index.parent().isValid()) {
+ // top level
+ QFont font;
+ font.setBold(true);
+ return font;
+ }
+ break;
+ }
+ return QVariant();
+}
+
+QModelIndex HelpResultModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (parent.isValid() && parent.column() != 0)
+ return QModelIndex();
+
+ HelpResultItem *parentItem = itemAt(parent);
+ HelpResultItem *childItem = parentItem->child(row);
+ if (!childItem)
+ return QModelIndex();
+
+ return createIndex(row, column, childItem);
+}
+
+HelpResultItem *HelpResultModel::itemAt(const QModelIndex &index) const
+{
+ if (index.isValid())
+ return static_cast<HelpResultItem *>(index.internalPointer());
+ return root_;
+}
+
+void HelpResultModel::search(const QString &text)
+{
+ QHelpSearchQuery q(QHelpSearchQuery::DEFAULT, QStringList(text));
+ QList<QHelpSearchQuery> ql;
+ ql.append(q);
+ engine_->searchEngine()->search(ql);
+}
+
+void HelpResultModel::searchingFinished()
+{
+ beginResetModel();
+
+ root_->clear();
+ QList<QHelpSearchEngine::SearchHit> hits = engine_->searchEngine()->hits(0, engine_->searchEngine()->hitsCount());
+ foreach(const QHelpSearchEngine::SearchHit &hit, hits) {
+ QString toolTip;
+ QModelIndex index = engine_->contentWidget()->indexOf(hit.first);
+ while (index.isValid()) {
+ QHelpContentItem *item = engine_->contentModel()->contentItemAt(index);
+ if (item) {
+ if (!toolTip.isEmpty())
+ toolTip = " > " + toolTip;
+ toolTip = item->title() + toolTip;
+ }
+ index = index.parent();
+ }
+ root_->addChild(new HelpResultItem(hit.second, hit.first, toolTip));
+ }
+
+ endResetModel();
+
+ emit finished(root_->childCount());
+}
+
+class TextBrowser : public QTextBrowser
+{
+public:
+ TextBrowser(QHelpEngine *helpEngine, QWidget *parent_ = 0): QTextBrowser(parent_), helpEngine_(helpEngine) {}
+ QVariant loadResource(int type, const QUrl &url)
+ {
+ const QString &scheme = url.scheme();
+ if(scheme == "qthelp")
+ return QVariant(helpEngine_->fileData(url));
+ else
+ return QTextBrowser::loadResource(type, url);
+ }
+ bool navigateToKeyword(const QString &keywordId)
+ {
+ auto links = helpEngine_->linksForIdentifier(keywordId);
+ if(links.count() > 0)
+ {
+ setSource(links.begin().value());
+ return true;
+ }
+ return false;
+ }
+ void setSource(const QUrl &src)
+ {
+ const QUrl &prevSrc = source();
+ if(src == prevSrc) return;
+
+ const QString &scheme = src.scheme();
+ if(scheme != "qthelp")
+ {
+ QDesktopServices::openUrl(src);
+ return;
+ }
+
+ QTextBrowser::setSource(src);
+ }
+
+ bool findText(const QString &text, QTextDocument::FindFlags flags, bool incremental, bool fromSearch)
+ {
+ QTextDocument *doc = document();
+ QTextCursor cursor = textCursor();
+ if (!doc || cursor.isNull())
+ return false;
+
+ const int position = cursor.selectionStart();
+ if (incremental)
+ cursor.setPosition(position);
+
+ QTextCursor found = doc->find(text, cursor, flags);
+ if (found.isNull()) {
+ if ((flags & QTextDocument::FindBackward) == 0)
+ cursor.movePosition(QTextCursor::Start);
+ else
+ cursor.movePosition(QTextCursor::End);
+ found = doc->find(text, cursor, flags);
+ }
+
+ if (fromSearch) {
+ cursor.beginEditBlock();
+ viewport()->setUpdatesEnabled(false);
+
+ QTextCharFormat marker;
+ marker.setForeground(Qt::red);
+ marker.setBackground(Qt::yellow);
+ cursor.movePosition(QTextCursor::Start);
+ setTextCursor(cursor);
+
+ while (find(text)) {
+ QTextCursor hit = textCursor();
+ hit.mergeCharFormat(marker);
+ }
+
+ viewport()->setUpdatesEnabled(true);
+ cursor.endEditBlock();
+ }
+
+ bool cursorIsNull = found.isNull();
+ if (cursorIsNull) {
+ found = textCursor();
+ found.setPosition(position);
+ }
+ setTextCursor(found);
+ return !cursorIsNull;
+ }
+
+private:
+ QHelpEngine *helpEngine_;
+};
+
+HelpBrowser::HelpBrowser(const QString &fileName)
+ : QMainWindow(NULL), fileName_(fileName), contentsCreated_(false)
+{
+ settings_file();
+
+ QVBoxLayout *mainLayout = new QVBoxLayout;
+ mainLayout->setContentsMargins(0, 0, 0, 0);
+ mainLayout->setSpacing(0);
+
+ QToolBar *toolBar = new QToolBar(this);
+ QSplitter *splitter = new QSplitter(Qt::Horizontal, this);
+
+ QIcon icon = QIcon(":/images/back.png");
+ icon.addPixmap(QPixmap(":/images/back_hover.png"), QIcon::Active, QIcon::Off);
+ icon.addPixmap(QPixmap(":/images/back_disabled.png"), QIcon::Disabled, QIcon::Off);
+ backButton_ = new QAction(icon, QString::fromUtf8(language[lsBack].c_str()), this);
+ backButton_->setEnabled(false);
+
+ icon = QIcon(":/images/forward.png");
+ icon.addPixmap(QPixmap(":/images/forward_hover.png"), QIcon::Active, QIcon::Off);
+ icon.addPixmap(QPixmap(":/images/forward_disabled.png"), QIcon::Disabled, QIcon::Off);
+ fwdButton_ = new QAction(icon, QString::fromUtf8(language[lsForward].c_str()), this);
+ fwdButton_->setEnabled(false);
+
+ searchBox_ = new SearchLineEdit(this);
+ searchBox_->setFrame(false);
+ searchBox_->setFixedWidth(200);
+ searchBox_->setPlaceholderText(QString::fromUtf8(language[lsSearch].c_str()));
+ connect(searchBox_, SIGNAL(textChanged(const QString &)), this, SLOT(searchBoxChanged()));
+ connect(searchBox_, SIGNAL(returnPressed()), this, SLOT(searchBoxChanged()));
+
+ toolBar->setMovable(false);
+ toolBar->setIconSize(QSize(22, 22));
+ toolBar->addSeparator();
+ toolBar->addAction(backButton_);
+ toolBar->addAction(fwdButton_);
+ toolBar->addSeparator();
+ QWidget *spacer = new QWidget(this);
+ spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+ toolBar->addWidget(spacer);
+ toolBar->addWidget(searchBox_);
+
+ QVBoxLayout *vLayout = new QVBoxLayout(this);
+ vLayout->setContentsMargins(0, 0, 0, 0);
+ vLayout->setSpacing(0);
+
+ helpEngine_ = new QHelpEngine(fileName_, splitter);
+ connect(helpEngine_->contentModel(), SIGNAL(contentsCreated()), this, SLOT(contentsCreated()));
+ if (!helpEngine_->setupData()) {
+ contentsCreated_ = true;
+ MessageDialog::critical(this, QString("Can not open file \"%1\"").arg(fileName_), QMessageBox::Ok);
+ }
+
+ contentModel_ = new HelpContentModel(helpEngine_, this);
+ resultModel_ = new HelpResultModel(helpEngine_, this);
+
+ contentWidget_ = new TreeView(this);
+ contentWidget_->setObjectName("project");
+ contentWidget_->setFrameShape(QFrame::NoFrame);
+ contentWidget_->header()->setObjectName("project");
+ contentWidget_->setUniformRowHeights(true);
+ contentWidget_->setModel(contentModel_);
+
+ resultWidget_ = new TreeView(this);
+ resultWidget_->setObjectName("project");
+ resultWidget_->setFrameShape(QFrame::NoFrame);
+ resultWidget_->header()->setObjectName("project");
+ resultWidget_->setUniformRowHeights(true);
+ resultWidget_->setModel(resultModel_);
+
+ QToolButton *closeButton = new QToolButton(this);
+ closeButton->setObjectName("cancel");
+ closeButton->setToolTip(QString::fromUtf8(language[lsClose].c_str()));
+
+ QHBoxLayout *layout = new QHBoxLayout;
+ layout->setContentsMargins(5, 0, 5, 0);
+ layout->setSpacing(0);
+ layout->addWidget(closeButton, 0, Qt::AlignRight);
+ resultWidget_->header()->setLayout(layout);
+
+ tabWidget_ = new QStackedWidget;
+ tabWidget_->addWidget(contentWidget_);
+ tabWidget_->addWidget(resultWidget_);
+
+ splitter->addWidget(tabWidget_);
+ helpBrowser_ = new TextBrowser(helpEngine_);
+ helpBrowser_->setFrameShape(QFrame::NoFrame);
+
+ QWidget *right = new QWidget();
+ vLayout->addWidget(helpBrowser_);
+ findWidget_ = new FindWidget(this);
+ vLayout->addWidget(findWidget_);
+ findWidget_->hide();
+ right->setLayout(vLayout);
+ splitter->addWidget(right);
+ splitter->setStretchFactor(1, 1);
+
+ mainLayout->addWidget(toolBar);
+ mainLayout->addWidget(splitter);
+
+ QWidget *window = new QWidget();
+ window->setLayout(mainLayout);
+ setCentralWidget(window);
+
+ RemoteControl *rc = new RemoteControl(this);
+ connect(helpBrowser_, SIGNAL(sourceChanged(const QUrl &)), this, SLOT(syncTree(const QUrl &)));
+ connect(rc, SIGNAL(handleNavigateToKeywordCommand(const QString &)), this, SLOT(handleNavigateToKeywordCommand(const QString &)));
+
+ connect(contentWidget_, SIGNAL(clicked(const QModelIndex &)), this, SLOT(contentWidgetClicked(const QModelIndex &)));
+ connect(resultWidget_, SIGNAL(clicked(const QModelIndex &)), this, SLOT(resultWidgetClicked(const QModelIndex &)));
+ connect(backButton_, SIGNAL(triggered()), helpBrowser_, SLOT(backward()));
+ connect(fwdButton_, SIGNAL(triggered()), helpBrowser_, SLOT(forward()));
+ connect(helpBrowser_, SIGNAL(backwardAvailable(bool)), backButton_, SLOT(setEnabled(bool)));
+ connect(helpBrowser_, SIGNAL(forwardAvailable(bool)), fwdButton_, SLOT(setEnabled(bool)));
+ connect(helpBrowser_, SIGNAL(sourceChanged(const QUrl &)), this, SLOT(highlightSearchTerms()));
+ connect(resultModel_, SIGNAL(finished(int)), this, SLOT(searchingFinished()));
+ connect(closeButton, SIGNAL(clicked(bool)), this, SLOT(resultCloseClicked()));
+
+ connect(findWidget_, SIGNAL(findNext()), this, SLOT(findNext()));
+ connect(findWidget_, SIGNAL(findPrevious()), this, SLOT(findPrevious()));
+ connect(findWidget_, SIGNAL(find(QString, bool, bool)), this,
+ SLOT(find(QString, bool, bool)));
+ connect(findWidget_, SIGNAL(escapePressed()), helpBrowser_, SLOT(setFocus()));
+
+ resize(890, 440);
+}
+
+void HelpBrowser::handleNavigateToKeywordCommand(const QString &arg)
+{
+ if(arg.length()) navigateToKeyword(arg);
+ raise();
+ activateWindow();
+}
+
+void HelpBrowser::findNext()
+{
+ find(findWidget_->text(), true, false);
+}
+
+void HelpBrowser::findPrevious()
+{
+ find(findWidget_->text(), false, false);
+}
+
+void HelpBrowser::find(const QString &ttf, bool forward, bool incremental)
+{
+ bool found = false;
+ QTextDocument::FindFlags flags = (QTextDocument::FindFlags)0;
+ if (!forward)
+ flags |= QTextDocument::FindBackward;
+ if (findWidget_->caseSensitive())
+ flags |= QTextDocument::FindCaseSensitively;
+ found = helpBrowser_->findText(ttf, flags, incremental, false);
+
+ if (!found && ttf.isEmpty())
+ found = true; // the line edit is empty, no need to mark it red...
+
+ if (!findWidget_->isVisible())
+ findWidget_->show();
+ findWidget_->setPalette(found);
+}
+
+void HelpBrowser::highlightSearchTerms()
+{
+ if (tabWidget_->currentIndex() != 1)
+ return;
+
+ QHelpSearchEngine *searchEngine = helpEngine_->searchEngine();
+ QList<QHelpSearchQuery> queryList = searchEngine->query();
+
+ QStringList terms;
+ foreach (const QHelpSearchQuery &query, queryList) {
+ switch (query.fieldName)
+ {
+ default: break;
+ case QHelpSearchQuery::ALL:
+ case QHelpSearchQuery::PHRASE:
+ case QHelpSearchQuery::DEFAULT:
+ case QHelpSearchQuery::ATLEAST:
+ foreach (QString term, query.wordList)
+ terms.append(term.remove(QLatin1Char('"')));
+ }
+ }
+
+ foreach (const QString& term, terms)
+ helpBrowser_->findText(term, 0, false, true);
+}
+
+void HelpBrowser::keyPressEvent(QKeyEvent *e)
+{
+ if ((e->modifiers() & Qt::ControlModifier) && e->key()==Qt::Key_F) {
+ if (!findWidget_->isVisible()) {
+ findWidget_->showAndClear(searchBox_->text());
+ } else {
+ findWidget_->show();
+ }
+ } else {
+ QWidget::keyPressEvent(e);
+ }
+}
+
+void HelpBrowser::searchBoxChanged()
+{
+ QString term = searchBox_->text();
+ if(term.length() < 3) {
+ tabWidget_->setCurrentIndex(0);
+ return;
+ }
+
+ resultModel_->search(term);
+}
+
+void HelpBrowser::searchingFinished()
+{
+ tabWidget_->setCurrentIndex(1);
+}
+
+void HelpBrowser::syncTree(const QUrl &src)
+{
+ setWindowTitle(QString("%1: %2").arg(QString::fromUtf8(language[lsHelp].c_str())).arg(helpBrowser_->documentTitle()));
+
+ // Synchronize with help content widget
+ QModelIndex indexOfSource = contentModel_->indexOf(src);
+ if (contentWidget_->currentIndex() != indexOfSource)
+ contentWidget_->setCurrentIndex(indexOfSource);
+
+ if(indexOfSource.isValid())
+ contentWidget_->expand(indexOfSource);
+ else
+ contentWidget_->expandAll();
+}
+
+static QString helpFileName(const QString &lang)
+{
+ return QString("%1/Help/%2.qhc").arg(QCoreApplication::applicationDirPath()).arg(lang);
+}
+
+void HelpBrowser::showTopic(const QString &keywordId)
+{
+ QString fileName = helpFileName(QString::fromUtf8(settings_file().language().c_str()));
+ if (!QFileInfo(fileName).exists())
+ fileName = helpFileName("en");
+
+ bool bOldProcess = true;
+ if (help_browser_process_ == NULL || help_browser_process_->state() == QProcess::NotRunning || help_browser_process_->fileName_ != fileName) {
+ delete help_browser_process_;
+ help_browser_process_ = new QHelpBrowserProcess(qApp, fileName);
+ if (!help_browser_process_->waitForStarted())
+ {
+ MessageDialog::critical(NULL, QString("Can not launch help system"), QMessageBox::Ok);
+ delete help_browser_process_;
+ help_browser_process_ = NULL;
+ return;
+ }
+ bOldProcess = false;
+ }
+ QByteArray ba;
+ ba += "navigateToKeyword#";
+ ba += (keywordId.length() || bOldProcess) ? keywordId : QString("default");
+ ba += ";\n";
+ help_browser_process_->write(ba);
+}
+
+void HelpBrowser::navigateToKeyword(const QString &keywordId)
+{
+ for (int i = 0; i < 50 && !contentsCreated_; i++)
+ QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
+ if(!helpBrowser_->navigateToKeyword(keywordId))
+ MessageDialog::critical(this, QString("Can not find keyword \"%1\" for \"%2\"").arg(keywordId, fileName_), QMessageBox::Ok);
+}
+
+void HelpBrowser::contentWidgetClicked(const QModelIndex &index)
+{
+ QHelpContentItem *item = contentModel_->contentItemAt(index);
+ if (!item)
+ return;
+
+ QUrl url = item->url();
+ if (url.isValid())
+ helpBrowser_->setSource(url);
+}
+
+void HelpBrowser::resultWidgetClicked(const QModelIndex &index)
+{
+ HelpResultItem *item = resultModel_->itemAt(index);
+ if (!item)
+ return;
+
+ QUrl url = item->url();
+ if (url.isValid())
+ helpBrowser_->setSource(url);
+}
+
+void HelpBrowser::pathClicked(const QString &link)
+{
+ helpBrowser_->setSource(link);
+}
+
+void HelpBrowser::resultCloseClicked()
+{
+ searchBox_->clear();
+}
+
+void HelpBrowser::contentsCreated()
+{
+ contentsCreated_ = true;
+}
+
+/**
+ * QHelpBrowserProcess
+ */
+
+QHelpBrowserProcess *HelpBrowser::help_browser_process_;
+
+QHelpBrowserProcess::QHelpBrowserProcess(QObject *parent, const QString &fileName) : QProcess(parent), fileName_(fileName)
+{
+ QStringList args;
+ args << "--help" << fileName;
+ start(QCoreApplication::applicationFilePath(), args);
+}
+
+QHelpBrowserProcess::~QHelpBrowserProcess()
+{
+ if(state() != NotRunning)
+ kill();
+} \ No newline at end of file