1 /*  This file is part of the Kate project.
2  *
3  *  SPDX-FileCopyrightText: 2012 Christoph Cullmann <cullmann@kde.org>
4  *
5  *  SPDX-License-Identifier: LGPL-2.0-or-later
6  */
7 
8 #include "kateprojectview.h"
9 #include "branchcheckoutdialog.h"
10 #include "filehistorywidget.h"
11 #include "git/gitutils.h"
12 #include "gitwidget.h"
13 #include "kateprojectfiltermodel.h"
14 #include "kateprojectpluginview.h"
15 
16 #include <KTextEditor/Document>
17 #include <KTextEditor/Editor>
18 #include <KTextEditor/MainWindow>
19 #include <KTextEditor/View>
20 
21 #include <KActionCollection>
22 #include <KLineEdit>
23 #include <KLocalizedString>
24 
25 #include <QPushButton>
26 #include <QSortFilterProxyModel>
27 #include <QTimer>
28 #include <QVBoxLayout>
29 
KateProjectView(KateProjectPluginView * pluginView,KateProject * project,KTextEditor::MainWindow * mainWindow)30 KateProjectView::KateProjectView(KateProjectPluginView *pluginView, KateProject *project, KTextEditor::MainWindow *mainWindow)
31     : m_pluginView(pluginView)
32     , m_project(project)
33     , m_treeView(new KateProjectViewTree(pluginView, project))
34     , m_stackWidget(new QStackedWidget(this))
35     , m_filter(new KLineEdit())
36     , m_branchBtn(new QToolButton)
37 {
38     /**
39      * layout tree view and co.
40      */
41     QVBoxLayout *layout = new QVBoxLayout();
42     layout->setSpacing(0);
43     layout->setContentsMargins(0, 0, 0, 0);
44     layout->addWidget(m_branchBtn);
45     layout->addWidget(m_stackWidget);
46     layout->addWidget(m_filter);
47     setLayout(layout);
48 
49     m_stackWidget->addWidget(m_treeView);
50 
51     m_branchBtn->setAutoRaise(true);
52     m_branchBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
53     m_branchBtn->setSizePolicy(QSizePolicy::Minimum, m_branchBtn->sizePolicy().verticalPolicy());
54     m_branchBtn->setIcon(QIcon(QStringLiteral(":/icons/icons/sc-apps-git.svg")));
55 
56     // let tree get focus for keyboard selection of file to open
57     setFocusProxy(m_treeView);
58 
59     // add to actionCollection so that this is available in Kate Command bar
60     auto chckbr = pluginView->actionCollection()->addAction(QStringLiteral("checkout_branch"), this, [this] {
61         m_branchBtn->click();
62     });
63     chckbr->setText(i18n("Checkout Git Branch"));
64 
65     /**
66      * setup filter line edit
67      */
68     m_filter->setPlaceholderText(i18n("Filter..."));
69     m_filter->setClearButtonEnabled(true);
70     connect(m_filter, &KLineEdit::textChanged, this, &KateProjectView::filterTextChanged);
71 
72     /**
73      * Setup git checkout stuff
74      */
75     connect(m_branchBtn, &QPushButton::clicked, this, [this, mainWindow] {
76         BranchCheckoutDialog bd(mainWindow->window(), m_pluginView, m_project->baseDir());
77         bd.openDialog();
78     });
79 
80     checkAndRefreshGit();
81 
82     connect(m_project, &KateProject::modelChanged, this, &KateProjectView::checkAndRefreshGit);
83     connect(&m_branchChangedWatcher, &QFileSystemWatcher::fileChanged, this, [this] {
84         m_project->reload(true);
85     });
86 
87     // file history
88     connect(m_treeView, &KateProjectViewTree::showFileHistory, this, &KateProjectView::showFileGitHistory);
89 }
90 
~KateProjectView()91 KateProjectView::~KateProjectView()
92 {
93 }
94 
selectFile(const QString & file)95 void KateProjectView::selectFile(const QString &file)
96 {
97     m_treeView->selectFile(file);
98 }
99 
openSelectedDocument()100 void KateProjectView::openSelectedDocument()
101 {
102     m_treeView->openSelectedDocument();
103 }
104 
filterTextChanged(const QString & filterText)105 void KateProjectView::filterTextChanged(const QString &filterText)
106 {
107     /**
108      * filter
109      */
110     static_cast<KateProjectFilterProxyModel *>(m_treeView->model())->setFilterString(filterText);
111 
112     /**
113      * expand
114      */
115     if (!filterText.isEmpty()) {
116         QTimer::singleShot(100, m_treeView, &QTreeView::expandAll);
117     }
118 }
119 
setTreeViewAsCurrent()120 void KateProjectView::setTreeViewAsCurrent()
121 {
122     Q_ASSERT(m_treeView != m_stackWidget->currentWidget());
123 
124     auto currentFileHistory = m_stackWidget->currentWidget();
125     m_stackWidget->removeWidget(currentFileHistory);
126     delete currentFileHistory;
127 
128     m_stackWidget->setCurrentWidget(m_treeView);
129 }
130 
showFileGitHistory(const QString & file)131 void KateProjectView::showFileGitHistory(const QString &file)
132 {
133     // create on demand and on switch back delete
134     auto fhs = new FileHistoryWidget(file);
135     connect(fhs, &FileHistoryWidget::backClicked, this, &KateProjectView::setTreeViewAsCurrent);
136     connect(fhs, &FileHistoryWidget::commitClicked, this, [this](const QByteArray &diff) {
137         m_pluginView->showDiffInFixedView(diff);
138     });
139     connect(fhs, &FileHistoryWidget::errorMessage, m_pluginView, [this](const QString &s, bool warn) {
140         QVariantMap genericMessage;
141         genericMessage.insert(QStringLiteral("type"), warn ? QStringLiteral("Error") : QStringLiteral("Info"));
142         genericMessage.insert(QStringLiteral("category"), i18n("Git"));
143         genericMessage.insert(QStringLiteral("categoryIcon"), QIcon(QStringLiteral(":/icons/icons/sc-apps-git.svg")));
144         genericMessage.insert(QStringLiteral("text"), s);
145         Q_EMIT m_pluginView->message(genericMessage);
146     });
147     m_stackWidget->addWidget(fhs);
148     m_stackWidget->setCurrentWidget(fhs);
149 }
150 
checkAndRefreshGit()151 void KateProjectView::checkAndRefreshGit()
152 {
153     const auto dotGitPath = GitUtils::getDotGitPath(m_project->baseDir());
154     /**
155      * Not in a git repo or git was removed
156      */
157     if (!dotGitPath.has_value()) {
158         if (!m_branchChangedWatcher.files().isEmpty()) {
159             m_branchChangedWatcher.removePaths(m_branchChangedWatcher.files());
160         }
161         m_branchBtn->setHidden(true);
162     } else {
163         m_branchBtn->setHidden(false);
164         m_branchBtn->setText(GitUtils::getCurrentBranchName(dotGitPath.value()));
165         if (m_branchChangedWatcher.files().isEmpty()) {
166             m_branchChangedWatcher.addPath(dotGitPath.value() + QStringLiteral(".git/HEAD"));
167         }
168     }
169 }
170