#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "binaryninjaapi.h" #include "clickablelabel.h" #include "filter.h" #include "menus.h" #include "uitypes.h" class BINARYNINJAUIAPI ProjectStatusWidget: public MenuHelper { protected: virtual void showMenu() override; public: ProjectStatusWidget(QWidget* parent, const QString& text); }; class BINARYNINJAUIAPI ProjectItemModel: public QStandardItemModel, public BinaryNinja::ProjectNotification { Q_OBJECT ProjectRef m_project; QMimeDatabase m_mimeDatabase; QFileIconProvider m_iconProvider; QSettings m_qsettings; QLocale m_locale; QFileSystemWatcher* m_fsWatcher; std::unordered_map m_itemsById; bool m_globalShowFullFileSize; QHash m_pathMimeTypeCache; QHash m_pathSizeCache; QHash m_pathIconCache; QHash m_idUploadProgress; bool OnBeforeProjectMetadataWritten(BinaryNinja::Project*, std::string& key, BinaryNinja::Metadata* value) override; void OnAfterProjectFileCreated(BinaryNinja::Project*, BinaryNinja::ProjectFile*) override; void OnAfterProjectFileUpdated(BinaryNinja::Project*, BinaryNinja::ProjectFile*) override; void OnAfterProjectFileDeleted(BinaryNinja::Project*, BinaryNinja::ProjectFile*) override; void OnAfterProjectFolderCreated(BinaryNinja::Project*, BinaryNinja::ProjectFolder*) override; void OnAfterProjectFolderUpdated(BinaryNinja::Project*, BinaryNinja::ProjectFolder*) override; void OnAfterProjectFolderDeleted(BinaryNinja::Project*, BinaryNinja::ProjectFolder*) override; void AddFolder(ProjectFolderRef folder); void UpdateFolder(ProjectFolderRef folder); void RemoveFolder(ProjectFolderRef folder); void AddFile(ProjectFileRef file); void UpdateFile(ProjectFileRef file); void RemoveFile(ProjectFileRef file); void CachePathInformation(const QString& path); void RemoveCachedPathInformation(const QString& path); void WatchEvent(const QString& path); public: ProjectItemModel(ProjectRef project, QObject* parent = nullptr); ~ProjectItemModel(); QStandardItem* ItemForId(const std::string& id); void reloadData(const BinaryNinja::ProgressFunction& progress = [](size_t, size_t){ return true; }); virtual QMimeData* mimeData(const QModelIndexList& indexes) const override; virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override; virtual bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; virtual void toggleGlobalShowFileSize(); virtual bool isGlobalShowFileSize(); enum { TypeRole = Qt::UserRole, IdRole, DiskPathRole, SortRole, }; enum { FileType, FolderType, }; enum { COL_NAME = 0, COL_TYPE, COL_SIZE_ON_DISK, COL_CREATED, COL_LAST_OPENED, COL_DESCRIPTION, COL_STATUS, COLUMN_COUNT, }; Q_SIGNALS: void itemsDropped(Qt::DropAction action, const QList fileIds, const QList folderIds, const QList newUrls, ProjectFolderRef newParentFolder); void projectFileCreated(ProjectFileRef projectFile); void projectFileUpdated(ProjectFileRef projectFile); void projectFileDeleted(ProjectFileRef projectFile); void projectFolderCreated(ProjectFolderRef projectFolder); void projectFolderUpdated(ProjectFolderRef projectFolder); void projectFolderDeleted(ProjectFolderRef projectFolder); private Q_SLOTS: void fileChanged(const QString& path); void directoryChanged(const QString& path); }; class BINARYNINJAUIAPI ProjectTreeFilterModel: public QSortFilterProxyModel { bool m_acceptAllFolders = false; ProjectRef m_project; protected: virtual bool lessThan(const QModelIndex& sourceLeft, const QModelIndex& sourceRight) const override; virtual bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; public: ProjectTreeFilterModel(ProjectRef project, QObject* parent = nullptr): QSortFilterProxyModel(parent), m_project(project) {}; void setAcceptAllFolders(bool accept) { m_acceptAllFolders = accept; } bool acceptAllFolders() const { return m_acceptAllFolders; } }; class BINARYNINJAUIAPI ProjectTableItemModel: public QAbstractProxyModel { Q_OBJECT ProjectRef m_project; QList m_sourceFileIndexes; QHash m_sourceToProxyRow; QList m_pendingRemovals; void rebuildMapping(); void collectFileIndexes(const QModelIndex& parent, QList& results) const; QString folderPathForIndex(int row) const; void sourceRowsInserted(const QModelIndex& parent, int first, int last); void sourceRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last); void sourceRowsRemoved(); void sourceDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QList& roles); public: ProjectTableItemModel(ProjectRef project, QObject* parent = nullptr); enum { COL_FOLDER = ProjectItemModel::COLUMN_COUNT, COLUMN_COUNT, }; void setSourceModel(QAbstractItemModel* sourceModel) override; QModelIndex mapToSource(const QModelIndex& proxyIndex) const override; QModelIndex mapFromSource(const QModelIndex& sourceIndex) const override; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex& child) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& proxyIndex, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; Qt::ItemFlags flags(const QModelIndex& index) const override; }; class BINARYNINJAUIAPI ProjectTableFilterModel: public QSortFilterProxyModel { ProjectRef m_project; protected: bool lessThan(const QModelIndex& sourceLeft, const QModelIndex& sourceRight) const override; bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; public: ProjectTableFilterModel(ProjectRef project, QObject* parent = nullptr): QSortFilterProxyModel(parent), m_project(project) {}; }; class BINARYNINJAUIAPI ProjectTable: public QTableView, public FilterTarget { Q_OBJECT virtual void scrollToFirstItem() override; virtual void scrollToCurrentItem() override; virtual void ensureSelection() override; virtual void activateSelection() override; virtual void setFilter(const std::string& filter, FilterOptions options) override; protected: void mousePressEvent(QMouseEvent* event) override; void keyPressEvent(QKeyEvent* event) override; public: ProjectTable(QWidget* parent = nullptr); QMenu* createHeaderContextMenu(); Q_SIGNALS: void filterChanged(const QString& filter, FilterOptions options); }; class BINARYNINJAUIAPI ProjectTreeStyle: public QProxyStyle { public: ProjectTreeStyle(QStyle* style) : QProxyStyle(style) {}; void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; }; class BINARYNINJAUIAPI ProjectTree: public QTreeView, public FilterTarget { Q_OBJECT QSet m_expandedIds; virtual void scrollToFirstItem() override; virtual void scrollToCurrentItem() override; virtual void ensureSelection() override; virtual void activateSelection() override; virtual void setFilter(const std::string& filter, FilterOptions options) override; protected: void mousePressEvent(QMouseEvent* event) override; void keyPressEvent(QKeyEvent* event) override; void rowsInserted(const QModelIndex& parent, int start, int end) override; public: ProjectTree(QWidget* parent = nullptr); QMenu* createHeaderContextMenu(); Q_SIGNALS: void filterChanged(const QString& filter, FilterOptions options); }; class BINARYNINJAUIAPI RecentsList: public QListWidget, public FilterTarget { Q_OBJECT QString m_filter; FilterOptions m_filterOptions; virtual void scrollToFirstItem() override; virtual void scrollToCurrentItem() override; virtual void ensureSelection() override; virtual void activateSelection() override; void applyFilter(); virtual void setFilter(const std::string& filter, FilterOptions options) override; public: enum { FileIdRole = Qt::UserRole, NameRole, PathRole }; RecentsList(QWidget* parent = nullptr) : QListWidget(parent) {} }; class BINARYNINJAUIAPI RecentFileItem: public QWidget { Q_OBJECT ProjectFileRef m_projectFile; QIcon m_icon; QLabel* m_fileNameLabel; QLabel* m_filePathLabel; public: RecentFileItem(ProjectFileRef file, QWidget* parent = nullptr); Q_SIGNALS: void showInProject(ProjectFileRef file); }; class BINARYNINJAUIAPI InfoWidget: public QWidget { ProjectRef m_project; std::unordered_map m_files; std::unordered_map m_folders; QVBoxLayout* m_layout; QLabel* m_descriptionTitleLabel; QTextEdit* m_descriptionText; QLabel* m_selectedItemNameLabel; QLabel* m_selectedItemDetailsLabel; QHBoxLayout* m_descriptionButtonLayout; QWidget* m_descriptionButtonWidget; QPushButton* m_descriptionSaveButton; QPushButton* m_descriptionCancelButton; bool HasDescriptionChanged() const; void descriptionChanged(); void cancelChangeDescription(); void saveDescription(); public: InfoWidget(ProjectRef project, QWidget* parent = nullptr); bool ContainsFile(const std::string& id); bool ContainsFolder(const std::string& id); void UpdateInfo(); void PromptSave(); void AddFile(ProjectFileRef file); void RemoveFile(ProjectFileRef file); void AddFolder(ProjectFolderRef folder); void RemoveFolder(ProjectFolderRef folder); }; class BINARYNINJAUIAPI ProjectBrowser: public QWidget, public UIContextNotification, public BinaryNinja::ProjectNotification { Q_OBJECT ProjectRef m_project; FilterEdit* m_treeFilterEdit; FilteredView* m_filteredTreeView; FilterEdit* m_recentsFilterEdit; FilteredView* m_filteredRecentsView; DockableTabBar* m_tabBar; ProjectItemModel* m_projectModel; ProjectTreeFilterModel* m_sortFilterTreeModel; ProjectTableItemModel* m_tableModel; ProjectTableFilterModel* m_sortFilterTableModel; QLabel* m_nameLabel; QLabel* m_descriptionLabel; ProjectTree* m_projectTree; ProjectTable* m_projectTable; InfoWidget* m_infoWidget; QWidget* m_projectContainer; QStackedWidget* m_viewStack; FilterEdit* m_tableFilterEdit; FilteredView* m_filteredTableView; bool m_useTableView = false; ClickableIcon* m_viewToggleButton; RecentsList* m_recentFilesList; ClickableIcon* m_refreshButton; ClickableIcon* m_editDetailsButton; UIActionHandler* m_projectTreeActionHandler = nullptr; UIActionHandler* m_projectTableActionHandler = nullptr; ContextMenuManager* m_projectContextMenuManager = nullptr; Menu m_projectMenu; ProjectFolderRef GetFolderContainingIndex(const QModelIndex& index) const; QModelIndex GetCurrentSelectedIndex() const; QItemSelectionModel* activeSelectionModel() const; UIActionHandler* activeUIActionHandler() const; void setUseTableView(bool useTable); virtual void OnAfterOpenProjectFile(UIContext* context, ProjectFileRef projectFile, ViewFrame* frame) override; void updateWindowTitle(); void updateRecentFileList(); void AddRecentProjectFile(ProjectFileRef projectFile); void UpdateDetails(); void LoadProjectData(); virtual void OnAfterProjectMetadataWritten(BinaryNinja::Project* project, std::string& key, BinaryNinja::Metadata* value) override; void storeTreeState(); void restoreTreeState(); void initActions(); UIActionContext actionContext(); std::vector GetSelectedFilesRecursive(); private slots: void treeItemDoubleClicked(const QModelIndex& index); void fileDoubleClicked(ProjectFileRef file); void openProjectFile(ProjectFileRef file, bool openWithOptions = false, bool forceContainerBrowser = false); void treeItemSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); void itemChanged(QStandardItem* item); void handleItemsDropped(Qt::DropAction action, const QList fileIds, const QList folderIds, const QList newUrls, ProjectFolderRef newParentFolder); void onTreeFilterChanged(const QString& filter, FilterOptions options); void tableItemDoubleClicked(const QModelIndex& index); void tableItemSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); void onTableFilterChanged(const QString& filter, FilterOptions options); protected: void SelectItems(std::vector files, std::vector folders); void SelectItemsById(std::vector fileIds, std::vector folderIds); void PromptImportFile(ProjectFolderRef folder = nullptr); void PromptImportFolder(ProjectFolderRef parent = nullptr); void MakeNewFolder(ProjectFolderRef parent = nullptr); void PromptDeleteSelected(); void PromptOpenSelected(bool withOptions = false, bool forceContainerBrowser = false); void PromptExportSelected(); void PromptAnalyzeSelected(); void PromptEditProjectDetails(); void Refresh(); void DownloadSelectedFilesRecursive(); public: ProjectBrowser(QWidget* parent, ProjectRef project); ~ProjectBrowser(); ProjectRef GetProject() const { return m_project; }; static void registerActions(); };