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