1 /*
2 SPDX-FileCopyrightText: 2011 Vishesh Yadav <vishesh3y@gmail.com>
3 SPDX-FileCopyrightText: 2015 Tomasz Bojczuk <seelook@gmail.com>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "commitdialog.h"
9 #include "hgwrapper.h"
10 #include "fileviewhgpluginsettings.h"
11
12 #include <QGroupBox>
13 #include <QGridLayout>
14 #include <QLabel>
15 #include <QActionGroup>
16 #include <QPlainTextEdit>
17 #include <QMenu>
18 #include <QAction>
19 #include <QLineEdit>
20 #include <QSplitter>
21 #include <KTextEditor/Document>
22 #include <KTextEditor/View>
23 #include <KTextEditor/Editor>
24 #include <KMessageBox>
25 #include <KLocalizedString>
26
HgCommitDialog(QWidget * parent)27 HgCommitDialog::HgCommitDialog(QWidget *parent):
28 DialogBase(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, parent)
29 {
30 // dialog properties
31 this->setWindowTitle(xi18nc("@title:window",
32 "<application>Hg</application> Commit"));
33
34 okButton()->setText(xi18nc("@action:button", "Commit"));
35 okButton()->setDisabled(true);
36
37 // To show diff between commit
38 KTextEditor::Editor *editor = KTextEditor::Editor::instance();
39 if (!editor) {
40 KMessageBox::error(this,
41 i18n("The KTextEditor component could not be found;"
42 "\nplease check your KDE Frameworks installation."));
43 return;
44 }
45 m_fileDiffDoc = editor->createDocument(nullptr);
46 m_fileDiffView = qobject_cast<KTextEditor::View*>(m_fileDiffDoc->createView(this));
47 m_fileDiffView->setStatusBarEnabled(false);
48 m_fileDiffDoc->setReadWrite(false);
49
50 // Setup actions
51 m_useCurrentBranch = new QAction(this);
52 m_useCurrentBranch->setCheckable(true);
53 m_useCurrentBranch->setText(xi18nc("@action:inmenu",
54 "Commit to current branch"));
55
56 m_newBranch = new QAction(this);
57 m_newBranch->setCheckable(true);
58 m_newBranch->setText(xi18nc("@action:inmenu",
59 "Create new branch"));
60
61 m_closeBranch = new QAction(this);
62 m_closeBranch->setCheckable(true);
63 m_closeBranch->setText(xi18nc("@action:inmenu",
64 "Close current branch"));
65
66 m_branchMenu = new QMenu(this);
67 m_branchMenu->addAction(m_useCurrentBranch);
68 m_branchMenu->addAction(m_newBranch);
69 m_branchMenu->addAction(m_closeBranch);
70
71 QActionGroup *branchActionGroup = new QActionGroup(this);
72 branchActionGroup->addAction(m_useCurrentBranch);
73 branchActionGroup->addAction(m_newBranch);
74 branchActionGroup->addAction(m_closeBranch);
75 m_useCurrentBranch->setChecked(true);
76 connect(branchActionGroup, &QActionGroup::triggered,
77 this, &HgCommitDialog::slotBranchActions);
78
79
80 //////////////
81 // Setup UI //
82 //////////////
83
84 // Top bar of buttons
85 QHBoxLayout *topBarLayout = new QHBoxLayout;
86 m_copyMessageButton = new QPushButton(i18n("Copy Message"));
87 m_branchButton = new QPushButton(i18n("Branch"));
88
89 m_copyMessageMenu = new QMenu(this);
90 createCopyMessageMenu();
91
92 topBarLayout->addWidget(new QLabel(getParentForLabel()));
93 topBarLayout->addStretch();
94 topBarLayout->addWidget(m_branchButton);
95 topBarLayout->addWidget(m_copyMessageButton);
96 m_branchButton->setMenu(m_branchMenu);
97 m_copyMessageButton->setMenu(m_copyMessageMenu);
98
99 // the commit box itself
100 QGroupBox *messageGroupBox = new QGroupBox;
101 QVBoxLayout *commitLayout = new QVBoxLayout;
102 m_commitMessage = editor->createDocument(nullptr);
103 KTextEditor::View *messageView =
104 qobject_cast<KTextEditor::View*>(m_commitMessage->createView(this));
105 messageView->setStatusBarEnabled(false);
106 messageView->setMinimumHeight(fontMetrics().height() * 4);
107 commitLayout->addWidget(messageView);
108 messageGroupBox->setTitle(xi18nc("@title:group", "Commit Message"));
109 messageGroupBox->setLayout(commitLayout);
110
111 // Show diff here
112 QGroupBox *diffGroupBox = new QGroupBox;
113 QVBoxLayout *diffLayout = new QVBoxLayout(diffGroupBox);
114 diffLayout->addWidget(m_fileDiffView);
115 diffGroupBox->setTitle(xi18nc("@title:group", "Diff/Content"));
116 diffGroupBox->setLayout(diffLayout);
117
118 // Set up layout for Status, Commit and Diff boxes
119 m_verticalSplitter = new QSplitter(Qt::Horizontal);
120 m_horizontalSplitter = new QSplitter(Qt::Vertical);
121 m_horizontalSplitter->addWidget(messageGroupBox);
122 m_horizontalSplitter->addWidget(diffGroupBox);
123 m_statusList = new HgStatusList;
124 m_verticalSplitter->addWidget(m_statusList);
125 m_verticalSplitter->addWidget(m_horizontalSplitter);
126
127 // Set up layout and container for main dialog
128 QVBoxLayout *mainLayout = new QVBoxLayout;
129 mainLayout->addLayout(topBarLayout);
130 mainLayout->addWidget(m_verticalSplitter);
131 layout()->insertLayout(0, mainLayout);
132
133 slotBranchActions(m_useCurrentBranch);
134 slotInitDiffOutput(); // initialize with whole repo diff
135
136 // Load saved settings
137 FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self();
138 this->resize(QSize(settings->commitDialogWidth(),
139 settings->commitDialogHeight()));
140
141 m_verticalSplitter->setSizes(settings->verticalSplitterSizes());
142 m_horizontalSplitter->setSizes(settings->horizontalSplitterSizes());
143
144 messageView->setFocus(); // message editor ready to get a text
145
146 connect(m_statusList, &HgStatusList::itemSelectionChanged,
147 this, &HgCommitDialog::slotItemSelectionChanged);
148 connect(m_commitMessage, &KTextEditor::Document::textChanged,
149 this, &HgCommitDialog::slotMessageChanged);
150 connect(this, SIGNAL(finished(int)), this, SLOT(saveGeometry()));
151 }
152
getParentForLabel()153 QString HgCommitDialog::getParentForLabel()
154 {
155 HgWrapper *hgWrapper = HgWrapper::instance();
156 QString line("<b>parents:</b> ");
157 line += hgWrapper->getParentsOfHead();
158 return line;
159 }
160
slotInitDiffOutput()161 void HgCommitDialog::slotInitDiffOutput()
162 {
163 m_fileDiffDoc->setReadWrite(true);
164 m_fileDiffDoc->setModified(false);
165 m_fileDiffDoc->closeUrl(true);
166
167 QString diffOut;
168 HgWrapper *hgWrapper = HgWrapper::instance();
169 hgWrapper->executeCommand(QLatin1String("diff"), QStringList(), diffOut);
170 m_fileDiffDoc->setHighlightingMode("diff");
171 m_fileDiffDoc->setText(diffOut);
172 m_fileDiffView->setCursorPosition( KTextEditor::Cursor(0, 0) );
173 m_fileDiffDoc->setReadWrite(false);
174 }
175
slotItemSelectionChanged(const char status,const QString & fileName)176 void HgCommitDialog::slotItemSelectionChanged(const char status,
177 const QString &fileName)
178 {
179 m_fileDiffDoc->setReadWrite(true);
180 m_fileDiffDoc->setModified(false);
181 m_fileDiffDoc->closeUrl(true);
182
183 if (status != '?') {
184 QStringList arguments;
185 QString diffOut;
186 HgWrapper *hgWrapper = HgWrapper::instance();
187
188 arguments << fileName;
189 hgWrapper->executeCommand(QLatin1String("diff"), arguments, diffOut);
190 m_fileDiffDoc->setText(diffOut);
191 m_fileDiffDoc->setHighlightingMode("diff");
192 }
193 else {
194 QUrl url = QUrl::fromLocalFile(HgWrapper::instance()->getBaseDir());
195 url = url.adjusted(QUrl::StripTrailingSlash);
196 url.setPath(url.path() + '/' + fileName);
197 m_fileDiffDoc->openUrl(url);
198 }
199
200 m_fileDiffDoc->setReadWrite(false);
201 m_fileDiffView->setCursorPosition( KTextEditor::Cursor(0, 0) );
202 }
203
slotMessageChanged()204 void HgCommitDialog::slotMessageChanged()
205 {
206 okButton()->setDisabled(m_commitMessage->isEmpty());
207 }
208
done(int r)209 void HgCommitDialog::done(int r)
210 {
211 if (r == QDialog::Accepted) {
212 QStringList files;
213 if (m_statusList->getSelectionForCommit(files)) {
214 HgWrapper *hgWrapper = HgWrapper::instance();
215 if (m_branchAction == NewBranch) {
216 if (!hgWrapper->createBranch(m_newBranchName)) {
217 KMessageBox::error(this,
218 i18n("Could not create branch! Aborting commit!"));
219 return;
220 }
221 }
222 bool success = hgWrapper->commit(m_commitMessage->text(),
223 files, m_branchAction==CloseBranch);
224 if (success) {
225 QDialog::done(r);
226 }
227 else {
228 KMessageBox::error(this, i18n("Commit unsuccessful!"));
229 }
230 }
231 else {
232 KMessageBox::error(this, i18n("No files for commit!"));
233 }
234 }
235 else {
236 QDialog::done(r);
237 }
238 }
239
saveGeometry()240 void HgCommitDialog::saveGeometry()
241 {
242 FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self();
243 settings->setCommitDialogHeight(this->height());
244 settings->setCommitDialogWidth(this->width());
245 settings->setHorizontalSplitterSizes(m_horizontalSplitter->sizes());
246 settings->setVerticalSplitterSizes(m_verticalSplitter->sizes());
247 settings->save();
248 }
249
slotBranchActions(QAction * action)250 void HgCommitDialog::slotBranchActions(QAction *action)
251 {
252 HgWrapper *hgWrapper = HgWrapper::instance();
253 QString currentBranch;
254 hgWrapper->executeCommand(QLatin1String("branch"), QStringList(), currentBranch);
255 currentBranch.replace('\n', QString());
256 currentBranch = " (" + currentBranch + ')';
257 if (action == m_useCurrentBranch) {
258 m_branchAction = NoChanges;
259 m_branchButton->setText(i18n("Branch: Current Branch") + currentBranch);
260 }
261 else if (action == m_newBranch) {
262 NewBranchDialog diag;
263 if (diag.exec() == QDialog::Accepted) {
264 m_branchAction = NewBranch;
265 m_newBranchName = diag.getBranchName();
266 m_branchButton->setText(i18n("Branch: ") + m_newBranchName);
267 }
268 else { // restore previous check state
269 if (m_branchAction == NoChanges) {
270 m_useCurrentBranch->setChecked(true);
271 }
272 else if (m_branchAction == CloseBranch) {
273 m_closeBranch->setChecked(true);
274 }
275 }
276 }
277 else if (action == m_closeBranch) {
278 m_branchAction = CloseBranch;
279 m_branchButton->setText(i18n("Branch: Close Current") + currentBranch);
280 }
281 }
282
283 /*****************/
284 /* Branch Dialog */
285 /*****************/
286
NewBranchDialog(QWidget * parent)287 NewBranchDialog::NewBranchDialog(QWidget *parent):
288 QDialog(parent)
289 {
290 // dialog properties
291 this->setWindowTitle(xi18nc("@title:window",
292 "<application>Hg</application> Commit: New Branch"));
293 QDialogButtonBox *buttonBox= new QDialogButtonBox(QDialogButtonBox::Cancel, this);
294 m_okButton = buttonBox->addButton(QDialogButtonBox::Ok);
295 m_okButton->setDisabled(true);
296 m_okButton->setDefault(true);
297
298 m_branchList = HgWrapper::instance()->getBranches();
299
300 QLabel *message = new QLabel(xi18nc("@label", "Enter new branch name"));
301 m_branchNameInput = new QLineEdit;
302 m_errorLabel = new QLabel;
303
304 QVBoxLayout *layout = new QVBoxLayout;
305 layout->addWidget(message);
306 layout->addWidget(m_branchNameInput);
307 layout->addWidget(m_errorLabel);
308 layout->addWidget(buttonBox);
309
310 setLayout(layout);
311
312 connect(m_branchNameInput, &QLineEdit::textChanged,
313 this, &NewBranchDialog::slotTextChanged);
314 connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
315 connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
316 }
317
slotTextChanged(const QString & text)318 void NewBranchDialog::slotTextChanged(const QString &text)
319 {
320 if (m_branchList.contains(text)) {
321 m_errorLabel->setText(xi18nc("@label", "<b>Branch already exists!</b>"));
322 m_okButton->setDisabled(true);
323 }
324 else if (text.length() > 0) {
325 m_errorLabel->clear();
326 m_okButton->setDisabled(false);
327 }
328 else {
329 m_errorLabel->setText(xi18nc("@label", "<b>Enter some text!</b>"));
330 m_okButton->setDisabled(true);
331 }
332 }
333
getBranchName() const334 QString NewBranchDialog::getBranchName() const
335 {
336 return m_branchNameInput->text();
337 }
338
createCopyMessageMenu()339 void HgCommitDialog::createCopyMessageMenu()
340 {
341 QActionGroup *actionGroup = new QActionGroup(this);
342 connect(actionGroup, &QActionGroup::triggered,
343 this, &HgCommitDialog::slotInsertCopyMessage);
344
345 QStringList args;
346 args << QLatin1String("--limit");
347 args << QLatin1String("7");
348 args << QLatin1String("--template");
349 args << QLatin1String("{desc}\n");
350 // args << QLatin1String("{desc|short}\n");
351
352 HgWrapper *hgw = HgWrapper::instance();
353 QString output;
354 hgw->executeCommand(QLatin1String("log"), args, output);
355
356 const QStringList messages = output.split('\n', Qt::SkipEmptyParts);
357 for (const QString &msg : messages) {
358 QAction *action = m_copyMessageMenu->addAction(msg.left(40)); // max 40 characters
359 action->setData(msg); // entire description into action data
360 actionGroup->addAction(action);
361 }
362 }
363
slotInsertCopyMessage(QAction * action)364 void HgCommitDialog::slotInsertCopyMessage(QAction *action)
365 {
366 m_commitMessage->setText(action->data().toString());
367 }
368
369
370