diff options
author | jmpoep <OriginalEntryPoint@qq.com> | 2023-12-07 16:51:07 +0800 |
---|---|---|
committer | jmpoep <OriginalEntryPoint@qq.com> | 2023-12-07 16:51:07 +0800 |
commit | 28008a746a31abb7909dd86cb0cd413ac8943b0b (patch) | |
tree | a30b74b8cad548048c3c1551d652828ab76fa9bd /VMProtect/help_browser.cc | |
download | vmprotect-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 |
Diffstat (limited to 'VMProtect/help_browser.cc')
-rw-r--r-- | VMProtect/help_browser.cc | 689 |
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 |