File indexing completed on 2024-04-28 04:38:49

0001 /*
0002     SPDX-FileCopyrightText: 2020 Jonathan L. Verner <jonathan.verner@matfyz.cz>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #ifndef KDEVPLATFORM_PLUGIN_COMMIT_TOOLVIEW_H
0008 #define KDEVPLATFORM_PLUGIN_COMMIT_TOOLVIEW_H
0009 
0010 #include "repostatusmodel.h"
0011 
0012 #include <interfaces/iuicontroller.h>
0013 
0014 #include <KTextEditor/Attribute>
0015 
0016 #include <QWidget>
0017 
0018 class ActiveStyledDelegate;
0019 class DiffViewsCtrl;
0020 class FilterEmptyItemsProxyModel;
0021 class RepoStatusModel;
0022 class SimpleCommitForm;
0023 
0024 class QAction;
0025 class QMenu;
0026 class QModelIndex;
0027 class QLineEdit;
0028 class QTreeView;
0029 class QAbstractProxyModel;
0030 class QUrl;
0031 
0032 namespace KDevelop {
0033     class IBasicVersionControl;
0034     class IDocument;
0035     class IProject;
0036     class VcsJob;
0037 }
0038 
0039 namespace KTextEditor {
0040     class View;
0041     class Document;
0042 }
0043 
0044 /**
0045  * This implements the git-cola like toolview for preparing commits.
0046  *
0047  * The view contains a list of projects. Each project contains four
0048  * lists:
0049  *
0050  *   - the staged changes list lists all files in the project which
0051  *     have changes staged for commit;
0052  *   - the unstaged changes list lists all files in the project which
0053  *     have changes which are not currently staged for commit;
0054  *   - the conflicts list lists all files which have unresolved (merge)
0055  *     conflicts; and
0056  *   - the untracked list which lists all files not tracked in the VCS
0057  *
0058  * Clicking on a file in one of the staged/unstaged lists opens a document
0059  * tab with the diff showing the changes. The user can then select lines/hunks
0060  * from the diff and remove/add them from the staged changes using the context menu.
0061  *
0062  * Double clicking on a file will, instead, stage/unstage all changes in the file
0063  * or mark the conflicts as resolved or add the file to be tracked in VCS.
0064  *
0065  * Above these lists a lineedit and a textedit may be used to prepare a
0066  * commit message. The commit button will commit the staged changes to the
0067  * repo. If several projects are listed, the one which is expanded will be
0068  * used (only one project is allowed to be expaned to show the lists at a time,
0069  * an expaned project is automatically collapsed when a different one is expanded).
0070  *
0071  * @author Jonathan L. Verner <jonathan.verner@matfyz.cz>
0072  */
0073 
0074 class CommitToolView : public QWidget
0075 {
0076     Q_OBJECT
0077 
0078 public:
0079     enum ShowDiffParams { Activate, NoActivate };
0080 
0081     /**
0082      * @note: m_statusmodel remains the property of the caller whose
0083      * responsibility is to delete it (and care must be taken not
0084      * to delete it before the CommitToolView is deleted)
0085      */
0086     CommitToolView(QWidget* parent, RepoStatusModel* m_statusmodel);
0087     ~CommitToolView() override;
0088 
0089     /**
0090      * @returns the currently active project (i.e. the one that
0091      *          is expanded in the treeview)
0092      */
0093     KDevelop::IProject* activeProject() const;
0094 
0095     /**
0096      * @returns the index of the currently active project (i.e. the one that
0097      *          is expanded in the treeview)
0098      */
0099     QStandardItem* activeProjectItem() const;
0100 
0101     /**
0102      * @returns true if the item pointed to by the repostatusmodel index
0103      *          idx is the root item of the currently active project.
0104      */
0105     bool isActiveProject(const QModelIndex& idx) const;
0106 
0107 Q_SIGNALS:
0108 
0109     /**
0110      * This signal is emitted when the view wants to show a diff
0111      *
0112      * @param url the url to display the changes for
0113      * @param area the type of changes to display
0114      */
0115     void showDiff(const QUrl& url, const RepoStatusModel::Areas area);
0116 
0117     /**
0118      * This signal is emitted when the view wants to show a file
0119      *
0120      * @param url the url of the file to show
0121      */
0122     void showSource(const QUrl& url);
0123 
0124     /**
0125      * This signal is emitted when the diff showing changes of type @param area
0126      * to the file @param url needs to be updated.
0127      */
0128     void updateDiff(const QUrl& url, const RepoStatusModel::Areas area);
0129 
0130     /**
0131      * This signal is emitted when all diffs showing changes to files in
0132      * project @param project need to be updated.
0133      */
0134     void updateProjectDiffs(KDevelop::IProject* project);
0135 
0136     /**
0137      * This signal is emitted when all diffs showing changes to the file
0138      * @param url need to be updated.
0139      *
0140      * @note: In contrast to the updateDiff signal, this also includes diffs
0141      * showing all changes to the owning project (staged/unstaged)
0142      */
0143     void updateUrlDiffs(const QUrl& url);
0144 
0145 public Q_SLOTS:
0146     /**
0147      * Shows the toolview context menu
0148      */
0149     void popupContextMenu(const QPoint& pos);
0150 
0151     /**
0152      * A handler called when the user double clicks
0153      * an item in the treeview.
0154      */
0155     void dblClicked(const QModelIndex& idx);
0156 
0157     /**
0158      * A handler called when the user clicks an item
0159      * in the treeview.
0160      */
0161     void clicked(const QModelIndex& idx);
0162 
0163     /**
0164      * A handler called when a user expands an item
0165      * in the treeview.
0166      */
0167     void activateProject(const QModelIndex& idx);
0168 
0169     /**
0170      * Stages the staged changes in the given files.
0171      *
0172      * @param urls the list of files whose changes to stage
0173      */
0174     void stageSelectedFiles(const QList<QUrl>& urls);
0175 
0176     /**
0177      * Unstages the staged changes in the given files.
0178      *
0179      * @param urls the list of files whose changes to unstage
0180      */
0181     void unstageSelectedFiles(const QList<QUrl>& urls);
0182 
0183     /**
0184      * Reverts the uncommited changes in the given files.
0185      *
0186      * @param urls the list of files whose changes to revert
0187      *
0188      * @note: This is an irreversible and dangerous action,
0189      * a confirmation dialog is shown before it is applied
0190      */
0191     void revertSelectedFiles(const QList<QUrl>& urls);
0192 
0193     /**
0194      * Runs git commit on the staged changes. The commit message
0195      * is constructed from the data in the commit form.
0196      *
0197      * @note This function assumes that there are some staged changes.
0198      * @note The extended description of the commit is wrapped at 70 columns
0199      */
0200     void commitActiveProject();
0201 
0202 private:
0203 
0204     /* Describes an action on selected lines/hunk in a diff */
0205     enum ApplyAction {
0206         Stage,
0207         Unstage,
0208         Revert,
0209     };
0210 
0211     /**
0212      * Updates the toolview layout based on the dock area position:
0213      *
0214      * When the toolview is placed on the left/right, all the widgets
0215      * sit on top of each other; when it is placed on the top/bottom,
0216      * the commit area (commit header, button, description textedit)
0217      * will sit to the left of the changes view with the search filter.
0218      */
0219 
0220     void doLayOut(const Qt::DockWidgetArea area);
0221 
0222     /**
0223      * A helper function which return the VCS plugin which
0224      * handles `url`.
0225      *
0226      * @param url the url for which the plugin is returned
0227      *
0228      * @note: Returns nullptr if no project/VCS plugin for the
0229      * given url exists.
0230      */
0231     KDevelop::IBasicVersionControl* vcsPluginForUrl(const QUrl& url) const;
0232 
0233     /**
0234      * The model which lists the projects and staged/modified/... files
0235      * which are shown in the treeview.
0236      */
0237     RepoStatusModel* m_statusmodel;
0238 
0239     /**
0240      * The filtered repostatus model
0241      */
0242     FilterEmptyItemsProxyModel* m_proxymodel;
0243 
0244     /** The form for composing the commit message and doing the commit. */
0245     SimpleCommitForm* m_commitForm = nullptr;
0246 
0247     /** The treeview listing the projects & their staged/modified/... files */
0248     QTreeView* m_view = nullptr;
0249 
0250     /** The lineedit for filtering the treeview */
0251     QLineEdit* m_filter = nullptr;
0252 
0253     /****************************************
0254      * Various contextmenus & their actions *
0255      ****************************************/
0256 
0257     QMenu
0258         /** Menu with a single "Refresh" action (shown for projects in the toolview) */
0259           *m_refreshMenu
0260         /** Menu with stage/unstage/revert actions (shown for files in the toolview) */
0261         , *m_toolviewMenu
0262         ;
0263     QAction *m_refreshModelAct
0264           , *m_stageFilesAct
0265           , *m_unstageFilesAct
0266           , *m_revertFilesAct
0267           ;
0268 
0269     /** A style delegate for showing the currently selected project in bold */
0270     ActiveStyledDelegate* m_styleDelegate;
0271 };
0272 
0273 /**
0274  * A factory for creating CommitToolViews.
0275  */
0276 class CommitToolViewFactory : public KDevelop::IToolViewFactory
0277 {
0278 public:
0279     explicit CommitToolViewFactory(RepoStatusModel* statusModel);
0280     ~CommitToolViewFactory();
0281     QWidget* create(QWidget* parent = nullptr) override;
0282     Qt::DockWidgetArea defaultPosition() const override;
0283     QString id() const override;
0284 
0285 private:
0286     RepoStatusModel* m_statusmodel;
0287     DiffViewsCtrl* m_diffViewsCtrl;
0288 };
0289 
0290 #endif