1 /***************************************************************************
2  *   Copyright (C) 2008 by Rajko Albrecht  ral@alwins-world.de             *
3  *   http://kdesvn.alwins-world.de/                                        *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20 
21 #include "maintreewidget.h"
22 #include "models/svnitemmodel.h"
23 #include "models/svnitemnode.h"
24 #include "models/svnsortfilter.h"
25 #include "models/svndirsortfilter.h"
26 #include "database/dbsettings.h"
27 #include "cursorstack.h"
28 #include "svnactions.h"
29 #include "copymoveview_impl.h"
30 #include "mergedlg_impl.h"
31 #include "checkoutinfo_impl.h"
32 #include "importdir_logmsg.h"
33 #include "settings/kdesvnsettings.h"
34 #include "helpers/sshagent.h"
35 #include "svnqt/targets.h"
36 #include "svnqt/url.h"
37 #include "fronthelpers/rangeinput_impl.h"
38 #include "fronthelpers/widgetblockstack.h"
39 #include "fronthelpers/fronthelpers.h"
40 #include "ksvnwidgets/commitmsg_impl.h"
41 #include "ksvnwidgets/deleteform.h"
42 #include "helpers/kdesvn_debug.h"
43 #include "opencontextmenu.h"
44 #include "EditIgnorePattern.h"
45 
46 #include <kjobwidgets.h>
47 #include <kjobuidelegate.h>
48 #include <kmessagebox.h>
49 #include <kactioncollection.h>
50 #include <kauthorized.h>
51 #include <kmimetypetrader.h>
52 #include <kio/deletejob.h>
53 #include <kio/copyjob.h>
54 #include <unistd.h>
55 
56 #include <QApplication>
57 #include <QCheckBox>
58 #include <QKeyEvent>
59 #include <QUrlQuery>
60 #include <QTimer>
61 
62 class MainTreeWidgetData
63 {
64 public:
MainTreeWidgetData()65     MainTreeWidgetData()
66     {
67         m_Collection = nullptr;
68         m_Model = nullptr;
69         m_SortModel = nullptr;
70         m_DirSortModel = nullptr;
71         m_remoteRevision = svn::Revision::UNDEFINED;
72     }
73 
~MainTreeWidgetData()74     ~MainTreeWidgetData()
75     {
76         delete m_Model;
77         delete m_SortModel;
78         delete m_DirSortModel;
79     }
80 
srcInd(const QModelIndex & ind)81     QModelIndex srcInd(const QModelIndex &ind)
82     {
83         return m_SortModel->mapToSource(ind);
84     }
85 
srcDirInd(const QModelIndex & ind)86     QModelIndex srcDirInd(const QModelIndex &ind)
87     {
88         return m_DirSortModel->mapToSource(ind);
89     }
90 
sourceNode(const QModelIndex & index,bool left)91     SvnItemModelNode *sourceNode(const QModelIndex &index, bool left)
92     {
93         if (!index.isValid()) {
94             return nullptr;
95         }
96         QModelIndex ind = left ? m_DirSortModel->mapToSource(index) : m_SortModel->mapToSource(index);
97         if (ind.isValid()) {
98             return static_cast<SvnItemModelNode *>(ind.internalPointer());
99         }
100         return nullptr;
101     }
102 
103     KActionCollection *m_Collection;
104     SvnItemModel *m_Model;
105     SvnSortFilterProxy *m_SortModel;
106     SvnDirSortFilterProxy *m_DirSortModel;
107     svn::Revision m_remoteRevision;
108     QString merge_Target, merge_Src2, merge_Src1;
109 
110     QTimer m_TimeModified, m_TimeUpdates, m_resizeColumnsTimer;
111 };
112 
MainTreeWidget(KActionCollection * aCollection,QWidget * parent,Qt::WindowFlags f)113 MainTreeWidget::MainTreeWidget(KActionCollection *aCollection, QWidget *parent, Qt::WindowFlags f)
114     : QWidget(parent, f), m_Data(new MainTreeWidgetData)
115 {
116     setupUi(this);
117     setFocusPolicy(Qt::StrongFocus);
118     m_TreeView->setFocusPolicy(Qt::StrongFocus);
119     m_Data->m_Collection = aCollection;
120     m_Data->m_SortModel = new SvnSortFilterProxy();
121     m_Data->m_SortModel->setDynamicSortFilter(true);
122     m_Data->m_SortModel->setSortRole(SORT_ROLE);
123     m_Data->m_SortModel->setSortCaseSensitivity(Kdesvnsettings::case_sensitive_sort() ? Qt::CaseSensitive : Qt::CaseInsensitive);
124     m_Data->m_SortModel->sort(0);
125     m_TreeView->setModel(m_Data->m_SortModel);
126     m_TreeView->sortByColumn(0, Qt::AscendingOrder);
127     m_Data->m_Model = new SvnItemModel(this);
128     m_Data->m_SortModel->setSourceModel(m_Data->m_Model);
129 
130     m_Data->m_DirSortModel = new SvnDirSortFilterProxy();
131     m_Data->m_DirSortModel->setDynamicSortFilter(true);
132     m_Data->m_DirSortModel->setSortRole(SORT_ROLE);
133     m_Data->m_DirSortModel->setSortCaseSensitivity(Kdesvnsettings::case_sensitive_sort() ? Qt::CaseSensitive : Qt::CaseInsensitive);
134 
135     m_DirTreeView->setModel(m_Data->m_DirSortModel);
136     m_Data->m_DirSortModel->setSourceModel(m_Data->m_Model);
137 
138     connect(m_TreeView->selectionModel(),
139             &QItemSelectionModel::selectionChanged,
140             this, &MainTreeWidget::slotSelectionChanged);
141 
142     connect(m_DirTreeView->selectionModel(),
143             &QItemSelectionModel::selectionChanged,
144             this, &MainTreeWidget::slotDirSelectionChanged);
145 
146     connect(m_Data->m_Model->svnWrapper(), &SvnActions::clientException, this, &MainTreeWidget::slotClientException);
147     connect(m_Data->m_Model, &SvnItemModel::clientException, this, &MainTreeWidget::slotClientException);
148     connect(m_Data->m_Model->svnWrapper(), &SvnActions::sendNotify, this, &MainTreeWidget::slotNotifyMessage);
149     connect(m_Data->m_Model->svnWrapper(), &SvnActions::reinitItem, this, &MainTreeWidget::slotReinitItem);
150     connect(m_Data->m_Model->svnWrapper(), &SvnActions::sigRefreshAll, this, &MainTreeWidget::refreshCurrentTree);
151     connect(m_Data->m_Model->svnWrapper(), &SvnActions::sigRefreshCurrent, this, &MainTreeWidget::refreshCurrent);
152     connect(m_Data->m_Model->svnWrapper(), &SvnActions::sigRefreshItem, this, &MainTreeWidget::slotRefreshItem);
153     connect(m_Data->m_Model->svnWrapper(), &SvnActions::sigGotourl, this, &MainTreeWidget::_openUrl);
154     connect(m_Data->m_Model->svnWrapper(), &SvnActions::sigCacheStatus, this, &MainTreeWidget::sigCacheStatus);
155     connect(m_Data->m_Model->svnWrapper(), &SvnActions::sigThreadsChanged, this, &MainTreeWidget::enableActions);
156     connect(m_Data->m_Model->svnWrapper(), &SvnActions::sigCacheDataChanged, this, &MainTreeWidget::slotCacheDataChanged);
157 
158     connect(m_Data->m_Model->svnWrapper(), &SvnActions::sigExtraStatusMessage, this, &MainTreeWidget::sigExtraStatusMessage);
159 
160     connect(m_Data->m_Model, &SvnItemModel::urlDropped,
161             this, &MainTreeWidget::slotUrlDropped);
162 
163     connect(m_Data->m_Model, &SvnItemModel::itemsFetched, this, &MainTreeWidget::slotItemsInserted);
164 
165     m_TreeView->sortByColumn(0, Qt::AscendingOrder);
166     m_DirTreeView->sortByColumn(0, Qt::AscendingOrder);
167 
168     checkUseNavigation(true);
169     setupActions();
170 
171     m_Data->m_TimeModified.setParent(this);
172     connect(&(m_Data->m_TimeModified), &QTimer::timeout, this, &MainTreeWidget::slotCheckModified);
173     m_Data->m_TimeUpdates.setParent(this);
174     connect(&(m_Data->m_TimeUpdates), &QTimer::timeout, this, &MainTreeWidget::slotCheckUpdates);
175     m_Data->m_resizeColumnsTimer.setSingleShot(true);
176     m_Data->m_resizeColumnsTimer.setParent(this);
177     connect(&(m_Data->m_resizeColumnsTimer), &QTimer::timeout, this, &MainTreeWidget::resizeAllColumns);
178 }
179 
~MainTreeWidget()180 MainTreeWidget::~MainTreeWidget()
181 {
182     // make sure to not get signals which affect the mmi
183     m_Data->m_Model->disconnect(this);
184     m_Data->m_Model->svnWrapper()->disconnect(this);
185     delete m_Data;
186 }
187 
_openUrl(const QUrl & url)188 void MainTreeWidget::_openUrl(const QUrl &url)
189 {
190     openUrl(url, true);
191 }
192 
resizeAllColumns()193 void MainTreeWidget::resizeAllColumns()
194 {
195     m_TreeView->resizeColumnToContents(SvnItemModel::Name);
196     m_TreeView->resizeColumnToContents(SvnItemModel::Status);
197     m_TreeView->resizeColumnToContents(SvnItemModel::LastRevision);
198     m_TreeView->resizeColumnToContents(SvnItemModel::LastAuthor);
199     m_TreeView->resizeColumnToContents(SvnItemModel::LastDate);
200     m_DirTreeView->resizeColumnToContents(SvnItemModel::Name);
201 }
202 
openUrl(const QUrl & url,bool noReinit)203 bool MainTreeWidget::openUrl(const QUrl &url, bool noReinit)
204 {
205 
206 #ifdef DEBUG_TIMER
207     QTime _counttime;
208     _counttime.start();
209 #endif
210 
211     CursorStack a;
212     m_Data->m_Model->svnWrapper()->killallThreads();
213     clear();
214     emit sigProplist(svn::PathPropertiesMapListPtr(new svn::PathPropertiesMapList()), false, false, QString());
215 
216     if (!noReinit) {
217         m_Data->m_Model->svnWrapper()->reInitClient();
218     }
219 
220     QUrl _url(url);
221     const QString proto = svn::Url::transformProtokoll(url.scheme());
222     _url = _url.adjusted(QUrl::StripTrailingSlash|QUrl::NormalizePathSegments);
223     _url.setScheme(proto);
224 
225     const QString baseUriString = _url.url(QUrl::StripTrailingSlash);
226     const QVector<QStringRef> s = baseUriString.splitRef(QLatin1Char('?'));
227     if (s.size() > 1) {
228         setBaseUri(s.first().toString());
229     } else {
230         setBaseUri(baseUriString);
231     }
232     setWorkingCopy(false);
233     setNetworked(false);
234     m_Data->m_remoteRevision = svn::Revision::HEAD;
235 
236     if (QLatin1String("svn+file") == url.scheme()) {
237         setBaseUri(url.path());
238     } else {
239         if (url.isLocalFile()) {
240             QFileInfo fi(url.path());
241             if (fi.exists() && fi.isSymLink()) {
242                 const QString sl = fi.symLinkTarget();
243                 if (sl.startsWith(QLatin1Char('/'))) {
244                     setBaseUri(sl);
245                 } else {
246                     fi.setFile(fi.path() + QLatin1Char('/') + sl);
247                     setBaseUri(fi.absoluteFilePath());
248                 }
249             } else {
250                 setBaseUri(url.path());
251             }
252             QUrl _dummy;
253             qCDebug(KDESVN_LOG) << "check if " << baseUri() << " is a local wc ...";
254             if (m_Data->m_Model->svnWrapper()->isLocalWorkingCopy(baseUri(), _dummy)) {
255                 setWorkingCopy(true);
256                 // make sure a valid path is stored as baseuri
257                 setBaseUri(url.toLocalFile());
258                 qCDebug(KDESVN_LOG) << "... yes -> " << baseUri();
259             } else {
260                 setWorkingCopy(false);
261                 // make sure a valid url is stored as baseuri
262                 setBaseUri(url.toString());
263                 qCDebug(KDESVN_LOG) << "... no -> " << baseUri();
264             }
265         } else {
266             setNetworked(true);
267             if (!Kdesvnsettings::network_on()) {
268                 setBaseUri(QString());
269                 setNetworked(false);
270                 clear();
271                 KMessageBox::error(this, i18n("Networked URL to open but networking is disabled."));
272                 emit changeCaption(QString());
273                 emit sigUrlOpened(false);
274                 return false;
275             }
276         }
277     }
278     const QList<QPair<QString, QString>> q = QUrlQuery(url).queryItems();
279     typedef QPair<QString, QString> queryPair;
280     for (const queryPair &p : q) {
281         if (p.first == QLatin1String("rev")) {
282             const QString v = p.second;
283             svn::Revision tmp;
284             m_Data->m_Model->svnWrapper()->svnclient()->url2Revision(v, m_Data->m_remoteRevision, tmp);
285             if (m_Data->m_remoteRevision == svn::Revision::UNDEFINED) {
286                 m_Data->m_remoteRevision = svn::Revision::HEAD;
287             }
288         }
289     }
290     if (url.scheme() == QLatin1String("svn+ssh") ||
291             url.scheme() == QLatin1String("ksvn+ssh")) {
292         SshAgent ssh;
293         ssh.addSshIdentities();
294     }
295     m_Data->m_Model->svnWrapper()->clearUpdateCache();
296     if (isWorkingCopy()) {
297         m_Data->m_Model->initDirWatch();
298     }
299     bool result = m_Data->m_Model->checkDirs(baseUri(), nullptr) > -1;
300     if (result && isWorkingCopy()) {
301         m_Data->m_Model->svnWrapper()->createModifiedCache(baseUri());
302         m_DirTreeView->expandToDepth(0);
303         m_DirTreeView->selectionModel()->select(m_Data->m_DirSortModel->mapFromSource(m_Data->m_Model->firstRootIndex()), QItemSelectionModel::Select);
304     }
305     resizeAllColumns();
306 
307     if (!result) {
308         setBaseUri(QString());
309         setNetworked(false);
310         clear();
311     }
312     if (result && isWorkingCopy()) {
313         m_Data->m_Model->svnWrapper()->createModifiedCache(baseUri());
314         if (Kdesvnsettings::start_updates_check_on_open()) {
315             slotCheckUpdates();
316         }
317     }
318 #ifdef DEBUG_TIMER
319     _counttime.restart();
320 #endif
321 
322     if (result && Kdesvnsettings::log_cache_on_open()) {
323         m_Data->m_Model->svnWrapper()->startFillCache(baseUri(), true);
324     }
325 #ifdef DEBUG_TIMER
326     qCDebug(KDESVN_LOG) << "Starting cache " << _counttime.elapsed();
327     _counttime.restart();
328 #endif
329     emit changeCaption(baseUri());
330     emit sigUrlOpened(result);
331     emit sigUrlChanged(baseUriAsUrl());
332 #ifdef DEBUG_TIMER
333     qCDebug(KDESVN_LOG) << "Fired signals " << _counttime.elapsed();
334     _counttime.restart();
335 #endif
336 
337     QTimer::singleShot(1, this, &MainTreeWidget::readSupportData);
338     enableActions();
339 #ifdef DEBUG_TIMER
340     qCDebug(KDESVN_LOG) << "Enabled actions " << _counttime.elapsed();
341 #endif
342     /*  KNotification * notification=new KNotification("kdesvn-open");
343         notification->setText("Opened url");
344         notification->sendEvent();
345     */
346     return result;
347 }
348 
clear()349 void MainTreeWidget::clear()
350 {
351     m_Data->m_Model->clear();
352 }
353 
baseRevision() const354 svn::Revision MainTreeWidget::baseRevision()const
355 {
356     return m_Data->m_remoteRevision;
357 }
358 
realWidget()359 QWidget *MainTreeWidget::realWidget()
360 {
361     return this;
362 }
363 
selectionCount() const364 int MainTreeWidget::selectionCount()const
365 {
366     int count = m_TreeView->selectionModel()->selectedRows(0).count();
367     if (count == 0) {
368         if (m_TreeView->rootIndex().isValid()) {
369             return 1;
370         }
371     }
372     return count;
373 }
374 
DirselectionCount() const375 int MainTreeWidget::DirselectionCount()const
376 {
377     return m_DirTreeView->selectionModel()->selectedRows(0).count();
378 }
379 
SelectionList() const380 SvnItemList MainTreeWidget::SelectionList()const
381 {
382     SvnItemList ret;
383     const QModelIndexList _mi = m_TreeView->selectionModel()->selectedRows(0);
384     ret.reserve(_mi.size());
385     if (_mi.isEmpty()) {
386         QModelIndex ind = m_TreeView->rootIndex();
387         if (ind.isValid()) {
388             // really! it will remapped to this before setRootIndex! (see below)
389             ret.push_back(m_Data->sourceNode(ind, false));
390         }
391         return ret;
392     }
393     for (int i = 0; i < _mi.count(); ++i) {
394         ret.push_back(m_Data->sourceNode(_mi[i], false));
395     }
396     return ret;
397 }
398 
DirSelectionList() const399 SvnItemList MainTreeWidget::DirSelectionList()const
400 {
401     SvnItemList ret;
402     const QModelIndexList _mi = m_DirTreeView->selectionModel()->selectedRows(0);
403     ret.reserve(_mi.size());
404     for (int i = 0; i < _mi.count(); ++i) {
405         ret.push_back(m_Data->sourceNode(_mi[i], true));
406     }
407     return ret;
408 }
409 
SelectedIndex() const410 QModelIndex MainTreeWidget::SelectedIndex()const
411 {
412     const QModelIndexList _mi = m_TreeView->selectionModel()->selectedRows(0);
413     if (_mi.count() != 1) {
414         if (_mi.isEmpty()) {
415             const QModelIndex ind = m_TreeView->rootIndex();
416             if (ind.isValid()) {
417                 return m_Data->m_SortModel->mapToSource(ind);
418             }
419         }
420         return QModelIndex();
421     }
422     return m_Data->m_SortModel->mapToSource(_mi[0]);
423 }
424 
DirSelectedIndex() const425 QModelIndex MainTreeWidget::DirSelectedIndex()const
426 {
427     const QModelIndexList _mi = m_DirTreeView->selectionModel()->selectedRows(0);
428     if (_mi.count() != 1) {
429         return QModelIndex();
430     }
431     return m_Data->m_DirSortModel->mapToSource(_mi[0]);
432 }
433 
SelectedNode() const434 SvnItemModelNode *MainTreeWidget::SelectedNode()const
435 {
436     const QModelIndex index = SelectedIndex();
437     if (index.isValid()) {
438         SvnItemModelNode *item = static_cast<SvnItemModelNode *>(index.internalPointer());
439         return item;
440     }
441     return nullptr;
442 }
443 
DirSelectedNode() const444 SvnItemModelNode *MainTreeWidget::DirSelectedNode()const
445 {
446     const QModelIndex index = DirSelectedIndex();
447     if (index.isValid()) {
448         SvnItemModelNode *item = static_cast<SvnItemModelNode *>(index.internalPointer());
449         return item;
450     }
451     return nullptr;
452 }
453 
slotSelectionChanged(const QItemSelection &,const QItemSelection &)454 void MainTreeWidget::slotSelectionChanged(const QItemSelection &, const QItemSelection &)
455 {
456     enableActions();
457     QTimer::singleShot(100, this, &MainTreeWidget::_propListTimeout);
458 }
459 
Selected() const460 SvnItem *MainTreeWidget::Selected()const
461 {
462     return SelectedNode();
463 }
464 
DirSelected() const465 SvnItem *MainTreeWidget::DirSelected()const
466 {
467     return DirSelectedNode();
468 }
469 
DirSelectedOrMain() const470 SvnItem *MainTreeWidget::DirSelectedOrMain()const
471 {
472     SvnItem *_item = DirSelected();
473     if (_item == nullptr && isWorkingCopy()) {
474         _item = m_Data->m_Model->firstRootChild();
475     }
476     return _item;
477 }
478 
SelectedOrMain() const479 SvnItem *MainTreeWidget::SelectedOrMain()const
480 {
481     SvnItem *_item = Selected();
482     if (_item == nullptr && isWorkingCopy()) {
483         _item = m_Data->m_Model->firstRootChild();
484     }
485     return _item;
486 }
487 
setupActions()488 void MainTreeWidget::setupActions()
489 {
490     if (!m_Data->m_Collection) {
491         return;
492     }
493     QAction *tmp_action;
494     /* local and remote actions */
495     /* 1. actions on dirs AND files */
496     tmp_action = add_action(QStringLiteral("make_svn_log_full"), i18n("History of item"), QKeySequence(Qt::CTRL | Qt::Key_L), QIcon::fromTheme(QStringLiteral("kdesvnlog")), this, SLOT(slotMakeLog()));
497     tmp_action->setIconText(i18n("History"));
498     tmp_action->setStatusTip(i18n("Displays the history log of selected item"));
499     tmp_action = add_action(QStringLiteral("make_svn_log_nofollow"), i18n("History of item ignoring copies"), QKeySequence(Qt::SHIFT | Qt::CTRL | Qt::Key_L), QIcon::fromTheme(QStringLiteral("kdesvnlog")), this, SLOT(slotMakeLogNoFollow()));
500     tmp_action->setIconText(i18n("History"));
501     tmp_action->setStatusTip(i18n("Displays the history log of selected item without following copies"));
502 
503     tmp_action = add_action(QStringLiteral("make_svn_dir_log_nofollow"), i18n("History of item ignoring copies"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnlog")), this, SLOT(slotDirMakeLogNoFollow()));
504     tmp_action->setIconText(i18n("History"));
505     tmp_action->setStatusTip(i18n("Displays the history log of selected item without following copies"));
506 
507     tmp_action = add_action(QStringLiteral("make_svn_tree"), i18n("Full revision tree"), QKeySequence(Qt::CTRL | Qt::Key_T), QIcon::fromTheme(QStringLiteral("kdesvntree")), this, SLOT(slotMakeTree()));
508     tmp_action->setStatusTip(i18n("Shows history of item as linked tree"));
509     tmp_action = add_action(QStringLiteral("make_svn_partialtree"), i18n("Partial revision tree"), QKeySequence(Qt::SHIFT | Qt::CTRL | Qt::Key_T), QIcon::fromTheme(QStringLiteral("kdesvntree")), this, SLOT(slotMakePartTree()));
510     tmp_action->setStatusTip(i18n("Shows history of item as linked tree for a revision range"));
511 
512     tmp_action = add_action(QStringLiteral("make_svn_property"), i18n("Properties"), QKeySequence(Qt::CTRL | Qt::Key_P), QIcon(), this, SLOT(slotRightProperties()));
513     tmp_action = add_action(QStringLiteral("make_left_svn_property"), i18n("Properties"), QKeySequence(), QIcon(), this, SLOT(slotLeftProperties()));
514     add_action(QStringLiteral("get_svn_property"), i18n("Display Properties"), QKeySequence(Qt::SHIFT | Qt::CTRL | Qt::Key_P), QIcon(), this, SLOT(slotDisplayProperties()));
515     tmp_action = add_action(QStringLiteral("make_last_change"), i18n("Display last changes"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvndiff")), this, SLOT(slotDisplayLastDiff()));
516     tmp_action->setToolTip(i18n("Display last changes as difference to previous commit."));
517     tmp_action = add_action(QStringLiteral("make_svn_info"), i18n("Details"), QKeySequence(Qt::CTRL | Qt::Key_I), QIcon::fromTheme(QStringLiteral("kdesvninfo")), this, SLOT(slotInfo()));
518     tmp_action->setStatusTip(i18n("Show details about selected item"));
519     tmp_action = add_action(QStringLiteral("make_svn_rename"), i18n("Move"), QKeySequence(Qt::Key_F2), QIcon::fromTheme(QStringLiteral("kdesvnmove")), this, SLOT(slotRename()));
520     tmp_action->setStatusTip(i18n("Moves or renames current item"));
521     tmp_action = add_action(QStringLiteral("make_svn_copy"), i18n("Copy"), QKeySequence(Qt::CTRL | Qt::Key_C), QIcon::fromTheme(QStringLiteral("kdesvncopy")), this, SLOT(slotCopy()));
522     tmp_action->setStatusTip(i18n("Create a copy of current item"));
523     tmp_action = add_action(QStringLiteral("make_check_updates"), i18n("Check for updates"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvncheckupdates")), this, SLOT(slotCheckUpdates()));
524     tmp_action->setToolTip(i18n("Check if current working copy has items with newer version in repository"));
525     tmp_action->setStatusTip(tmp_action->toolTip());
526     tmp_action->setIconText(i18n("Check updates"));
527 
528     /* 2. actions only on files */
529     tmp_action = add_action(QStringLiteral("make_svn_blame"), i18n("Blame"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnblame")), this, SLOT(slotBlame()));
530     tmp_action->setToolTip(i18n("Output the content of specified files or URLs with revision and author information in-line."));
531     tmp_action->setStatusTip(tmp_action->toolTip());
532     tmp_action = add_action(QStringLiteral("make_svn_range_blame"), i18n("Blame range"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnblame")), this, SLOT(slotRangeBlame()));
533     tmp_action->setToolTip(i18n("Output the content of specified files or URLs with revision and author information in-line."));
534     tmp_action->setStatusTip(tmp_action->toolTip());
535 
536     tmp_action = add_action(QStringLiteral("make_svn_cat"), i18n("Cat head"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvncat")), this, SLOT(slotCat()));
537     tmp_action->setToolTip(i18n("Output the content of specified files or URLs."));
538     tmp_action->setStatusTip(tmp_action->toolTip());
539     tmp_action = add_action(QStringLiteral("make_revisions_cat"), i18n("Cat revision..."), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvncat")), this, SLOT(slotRevisionCat()));
540     tmp_action->setToolTip(i18n("Output the content of specified files or URLs at specific revision."));
541     tmp_action->setStatusTip(tmp_action->toolTip());
542 
543     tmp_action = add_action(QStringLiteral("make_svn_lock"), i18n("Lock current items"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnlock")), this, SLOT(slotLock()));
544     tmp_action->setToolTip(i18n("Try lock current item against changes from other users"));
545     tmp_action->setStatusTip(tmp_action->toolTip());
546     tmp_action = add_action(QStringLiteral("make_svn_unlock"), i18n("Unlock current items"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnunlock")), this, SLOT(slotUnlock()));
547     tmp_action->setToolTip(i18n("Free existing lock on current item"));
548     tmp_action->setStatusTip(tmp_action->toolTip());
549 
550     /* 3. actions only on dirs */
551     tmp_action = add_action(QStringLiteral("make_svn_mkdir"), i18n("New folder"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnnewfolder")), this, SLOT(slotMkdir()));
552     tmp_action->setStatusTip(i18n("Create a new folder"));
553     tmp_action = add_action(QStringLiteral("make_svn_switch"), i18n("Switch repository"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnswitch")), m_Data->m_Model->svnWrapper(), SLOT(slotSwitch()));
554     tmp_action->setToolTip(i18n("Switch repository path of current working copy path (\"svn switch\")"));
555     tmp_action->setStatusTip(tmp_action->toolTip());
556 
557     tmp_action = add_action(QStringLiteral("make_svn_relocate"), i18n("Relocate current working copy URL"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnrelocate")), this, SLOT(slotRelocate()));
558     tmp_action->setToolTip(i18n("Relocate URL of current working copy path to other URL"));
559     tmp_action->setStatusTip(tmp_action->toolTip());
560 
561     tmp_action = add_action(QStringLiteral("make_check_unversioned"), i18n("Check for unversioned items"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnaddrecursive")), this, SLOT(slotCheckNewItems()));
562     tmp_action->setIconText(i18n("Unversioned"));
563     tmp_action->setToolTip(i18n("Browse folder for unversioned items and add them if wanted."));
564     tmp_action->setStatusTip(tmp_action->toolTip());
565 
566     tmp_action = add_action(QStringLiteral("make_switch_to_repo"), i18n("Open repository of working copy"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnrepository")),
567                             this, SLOT(slotChangeToRepository()));
568     tmp_action->setToolTip(i18n("Opens the repository the current working copy was checked out from"));
569 
570     tmp_action = add_action(QStringLiteral("make_cleanup"), i18n("Cleanup"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvncleanup")), this, SLOT(slotCleanupAction()));
571     tmp_action->setToolTip(i18n("Recursively clean up the working copy, removing locks, resuming unfinished operations, etc."));
572     tmp_action = add_action(QStringLiteral("make_import_dirs_into_current"), i18n("Import folders into current"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnimportfolder")),
573                             this, SLOT(slotImportDirsIntoCurrent()));
574     tmp_action->setToolTip(i18n("Import folder content into current URL"));
575 
576     /* local only actions */
577     /* 1. actions on files AND dirs*/
578     tmp_action = add_action(QStringLiteral("make_svn_add"), i18n("Add selected files/dirs"), QKeySequence(Qt::Key_Insert), QIcon::fromTheme(QStringLiteral("kdesvnadd")), m_Data->m_Model->svnWrapper(), SLOT(slotAdd()));
579     tmp_action->setToolTip(i18n("Adding selected files and/or directories to repository"));
580     tmp_action->setIconText(i18n("Add"));
581     tmp_action = add_action(QStringLiteral("make_svn_addrec"), i18n("Add selected files/dirs recursive"), QKeySequence(Qt::CTRL | Qt::Key_Insert), QIcon::fromTheme(QStringLiteral("kdesvnaddrecursive")),
582                             m_Data->m_Model->svnWrapper(), SLOT(slotAddRec()));
583     tmp_action->setToolTip(i18n("Adding selected files and/or directories to repository and all subitems of folders"));
584 
585     tmp_action = add_action(QStringLiteral("make_svn_remove"), i18n("Delete selected files/dirs"), QKeySequence(Qt::Key_Delete), QIcon::fromTheme(QStringLiteral("kdesvndelete")), this, SLOT(slotDelete()));
586     tmp_action->setIconText(i18n("Delete"));
587     tmp_action->setToolTip(i18n("Deleting selected files and/or directories from repository"));
588     tmp_action = add_action(QStringLiteral("make_svn_remove_left"), i18n("Delete folder"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvndelete")), this, SLOT(slotLeftDelete()));
589     tmp_action->setToolTip(i18n("Deleting selected directories from repository"));
590     tmp_action->setIconText(i18n("Delete"));
591     tmp_action  = add_action(QStringLiteral("make_svn_revert"), i18n("Revert current changes"), QKeySequence(Qt::CTRL | Qt::Key_R), QIcon::fromTheme(QStringLiteral("kdesvnreverse")), m_Data->m_Model->svnWrapper(), SLOT(slotRevert()));
592 
593     tmp_action = add_action(QStringLiteral("make_resolved"), i18n("Mark resolved"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnresolved")), this, SLOT(slotResolved()));
594     tmp_action->setToolTip(i18n("Marking files or dirs resolved"));
595 
596     tmp_action = add_action(QStringLiteral("make_try_resolve"), i18n("Resolve conflicts"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnresolved")), this, SLOT(slotTryResolve()));
597 
598     tmp_action = add_action(QStringLiteral("make_svn_ignore"), i18n("Ignore/Unignore current item"), QKeySequence(), QIcon(), this, SLOT(slotIgnore()));
599     tmp_action = add_action(QStringLiteral("make_left_add_ignore_pattern"), i18n("Add or Remove ignore pattern"), QKeySequence(), QIcon(), this, SLOT(slotLeftRecAddIgnore()));
600     tmp_action = add_action(QStringLiteral("make_right_add_ignore_pattern"), i18n("Add or Remove ignore pattern"), QKeySequence(), QIcon(), this, SLOT(slotRightRecAddIgnore()));
601 
602     tmp_action = add_action(QStringLiteral("make_svn_headupdate"), i18n("Update to head"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnupdate")), m_Data->m_Model->svnWrapper(), SLOT(slotUpdateHeadRec()));
603     tmp_action->setIconText(i18nc("Menu item", "Update"));
604     tmp_action = add_action(QStringLiteral("make_svn_revupdate"), i18n("Update to revision..."), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnupdate")), m_Data->m_Model->svnWrapper(), SLOT(slotUpdateTo()));
605     tmp_action = add_action(QStringLiteral("make_svn_commit"), i18n("Commit"), QKeySequence(QStringLiteral("CTRL+#")), QIcon::fromTheme(QStringLiteral("kdesvncommit")), this, SLOT(slotCommit()));
606     tmp_action->setIconText(i18n("Commit"));
607 
608     tmp_action = add_action(QStringLiteral("make_svn_basediff"), i18n("Diff local changes"), QKeySequence(Qt::CTRL | Qt::Key_D), QIcon::fromTheme(QStringLiteral("kdesvndiff")), this, SLOT(slotSimpleBaseDiff()));
609     tmp_action->setToolTip(i18n("Diff working copy against BASE (last checked out version) - does not require access to repository"));
610     tmp_action = add_action(QStringLiteral("make_svn_dirbasediff"), i18n("Diff local changes"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvndiff")), this, SLOT(slotDirSimpleBaseDiff()));
611     tmp_action->setToolTip(i18n("Diff working copy against BASE (last checked out version) - does not require access to repository"));
612 
613     tmp_action =
614         add_action(QStringLiteral("make_svn_headdiff"), i18n("Diff against HEAD"), QKeySequence(Qt::CTRL | Qt::Key_H), QIcon::fromTheme(QStringLiteral("kdesvndiff")), this, SLOT(slotSimpleHeadDiff()));
615     tmp_action->setToolTip(i18n("Diff working copy against HEAD (last checked in version)- requires access to repository"));
616 
617     tmp_action =
618         add_action(QStringLiteral("make_svn_itemsdiff"), i18n("Diff items"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvndiff")), this, SLOT(slotDiffPathes()));
619     tmp_action->setToolTip(i18n("Diff two items"));
620     tmp_action =
621         add_action(QStringLiteral("make_svn_diritemsdiff"), i18n("Diff items"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvndiff")), this, SLOT(slotDiffPathes()));
622     tmp_action->setToolTip(i18n("Diff two items"));
623 
624 
625     tmp_action =
626         add_action(QStringLiteral("make_svn_merge_revisions"), i18n("Merge two revisions"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnmerge")), this, SLOT(slotMergeRevisions()));
627     tmp_action->setIconText(i18n("Merge"));
628     tmp_action->setToolTip(i18n("Merge two revisions of this entry into itself"));
629 
630     tmp_action =
631         add_action(QStringLiteral("make_svn_merge"), i18n("Merge..."), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnmerge")), this, SLOT(slotMerge()));
632     tmp_action->setToolTip(i18n("Merge repository path into current working copy path or current repository path into a target"));
633     tmp_action = add_action(QStringLiteral("openwith"), i18n("Open With..."), QKeySequence(), QIcon(), this, SLOT(slotOpenWith()));
634 
635     /* remote actions only */
636     tmp_action =
637         add_action(QStringLiteral("make_svn_checkout_current"), i18n("Checkout current repository path"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvncheckout")), m_Data->m_Model->svnWrapper(), SLOT(slotCheckoutCurrent()));
638     tmp_action->setIconText(i18n("Checkout"));
639     tmp_action =
640         add_action(QStringLiteral("make_svn_export_current"), i18n("Export current repository path"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnexport")), m_Data->m_Model->svnWrapper(), SLOT(slotExportCurrent()));
641     add_action(QStringLiteral("switch_browse_revision"), i18n("Select browse revision"), QKeySequence(), QIcon(), this, SLOT(slotSelectBrowsingRevision()));
642 
643     /* independe actions */
644     tmp_action =
645         add_action(QStringLiteral("make_svn_checkout"), i18n("Checkout a repository"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvncheckout")), m_Data->m_Model->svnWrapper(), SLOT(slotCheckout()));
646     tmp_action->setIconText(i18n("Checkout"));
647     tmp_action = add_action(QStringLiteral("make_svn_export"), i18n("Export a repository"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnexport")), m_Data->m_Model->svnWrapper(), SLOT(slotExport()));
648     tmp_action->setIconText(i18n("Export"));
649     tmp_action = add_action(QStringLiteral("make_view_refresh"), i18n("Refresh view"), QKeySequence(Qt::Key_F5), QIcon::fromTheme(QStringLiteral("kdesvnrightreload")), this, SLOT(refreshCurrentTree()));
650     tmp_action->setIconText(i18n("Refresh"));
651 
652     add_action(QStringLiteral("make_revisions_diff"), i18n("Diff revisions"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvndiff")), this, SLOT(slotDiffRevisions()));
653 
654     /* folding options */
655     tmp_action = add_action(QStringLiteral("view_unfold_tree"), i18n("Unfold File Tree"), QKeySequence(), QIcon(), this, SLOT(slotUnfoldTree()));
656     tmp_action->setToolTip(i18n("Opens all branches of the file tree"));
657     tmp_action = add_action(QStringLiteral("view_fold_tree"), i18n("Fold File Tree"), QKeySequence(), QIcon(), this , SLOT(slotFoldTree()));
658     tmp_action->setToolTip(i18n("Closes all branches of the file tree"));
659 
660     /* caching */
661     tmp_action = add_action(QStringLiteral("update_log_cache"), i18n("Update log cache"), QKeySequence(), QIcon(), this, SLOT(slotUpdateLogCache()));
662     tmp_action->setToolTip(i18n("Update the log cache for current repository"));
663 
664     tmp_action = add_action(QStringLiteral("make_dir_commit"), i18n("Commit"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvncommit")), this, SLOT(slotDirCommit()));
665     tmp_action = add_action(QStringLiteral("make_dir_update"), i18n("Update to head"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnupdate")), this, SLOT(slotDirUpdate()));
666     tmp_action = add_action(QStringLiteral("set_rec_property_dir"), i18n("Set property recursive"), QKeySequence(), QIcon(), this, SLOT(slotDirRecProperty()));
667 
668     tmp_action = add_action(QStringLiteral("show_repository_settings"), i18n("Settings for current repository"), QKeySequence(), QIcon(), this, SLOT(slotRepositorySettings()));
669 
670     enableActions();
671 }
672 
uniqueTypeSelected()673 bool MainTreeWidget::uniqueTypeSelected()
674 {
675     QModelIndexList _mi = m_TreeView->selectionModel()->selectedRows(0);
676     if (_mi.count() < 1) {
677         return false;
678     }
679     bool dir = static_cast<SvnItemModelNode *>(m_Data->srcInd(_mi[0]).internalPointer())->isDir();
680     for (int i = 1; i < _mi.count(); ++i) {
681         if (static_cast<SvnItemModelNode *>(m_Data->srcInd(_mi[i]).internalPointer())->isDir() != dir) {
682             return false;
683         }
684     }
685     return true;
686 }
687 
enableAction(const QString & name,bool how)688 void MainTreeWidget::enableAction(const QString &name, bool how)
689 {
690     QAction *temp = filesActions()->action(name);
691     if (temp) {
692         temp->setEnabled(how);
693         temp->setVisible(how);
694     }
695 }
696 
enableActions()697 void MainTreeWidget::enableActions()
698 {
699     const bool isopen = !baseUri().isEmpty();
700     const SvnItemList fileList = SelectionList();
701     const SvnItemList dirList = DirSelectionList();
702     const SvnItemModelNode *si = SelectedNode();
703 
704     const bool single = isopen && fileList.size() == 1;
705     const bool multi = isopen && fileList.size() > 1;
706     const bool none = isopen && fileList.isEmpty();
707     const bool single_dir = single && si && si->isDir();
708     const bool unique = uniqueTypeSelected();
709     const bool remote_enabled =/*isopen&&*/m_Data->m_Model->svnWrapper()->doNetworking();
710     const bool conflicted = single && si && si->isConflicted();
711 
712     bool at_least_one_changed = false;
713     bool at_least_one_conflicted = false;
714     bool at_least_one_local_added = false;
715     bool all_unversioned = true;
716     bool all_versioned = true;
717     bool at_least_one_directory = false;
718     for (auto item : fileList) {
719       if (!item) {
720           // root item
721           continue;
722       }
723       if (item->isChanged()) {
724           at_least_one_changed = true;
725       }
726       if (item->isConflicted()) {
727           at_least_one_conflicted = true;
728       }
729       if (item->isLocalAdded()) {
730           at_least_one_local_added = true;
731       }
732       if (item->isRealVersioned()) {
733           all_unversioned = false;
734       } else {
735           all_versioned = false;
736       }
737       if (item->isDir()) {
738           at_least_one_directory = true;
739           if (item->isChildModified())
740             at_least_one_changed = true;
741       }
742     }
743 
744     //qDebug("single: %d, multi: %d, none: %d, single_dir: %d, unique: %d, remove_enabled: %d, conflicted: %d, changed: %d, added: %d",
745     //       single, multi, none, single_dir, unique, remote_enabled, conflicted, si && si->isChanged(), si && si->isLocalAdded());
746     //qDebug("at_least_one_changed: %d, at_least_one_conflicted: %d, at_least_one_local_added: %d, all_unversioned: %d, all_versioned: %d, at_least_one_directory: %d",
747     //       at_least_one_changed, at_least_one_conflicted, at_least_one_local_added, all_unversioned, all_versioned, at_least_one_directory);
748 
749     /* local and remote actions */
750     /* 1. actions on dirs AND files */
751     enableAction(QStringLiteral("make_svn_log_nofollow"), single || none);
752     enableAction(QStringLiteral("make_svn_dir_log_nofollow"), dirList.size() == 1 && isopen);
753     enableAction(QStringLiteral("make_last_change"), isopen);
754     enableAction(QStringLiteral("make_svn_log_full"), single || none);
755     enableAction(QStringLiteral("make_svn_tree"), single || none);
756     enableAction(QStringLiteral("make_svn_partialtree"), single || none);
757 
758     enableAction(QStringLiteral("make_svn_property"), single);
759     enableAction(QStringLiteral("make_left_svn_property"), dirList.size() == 1);
760     enableAction(QStringLiteral("set_rec_property_dir"), dirList.size() == 1);
761     enableAction(QStringLiteral("get_svn_property"), single);
762     enableAction(QStringLiteral("make_svn_remove"), (multi || single));
763     enableAction(QStringLiteral("make_svn_remove_left"), dirList.size() > 0);
764     enableAction(QStringLiteral("make_svn_lock"), (multi || single));
765     enableAction(QStringLiteral("make_svn_unlock"), (multi || single));
766 
767     enableAction(QStringLiteral("make_svn_ignore"), (single) && si && si->parent() != nullptr && !si->isRealVersioned());
768     enableAction(QStringLiteral("make_left_add_ignore_pattern"), (dirList.size() == 1) && isWorkingCopy());
769     enableAction(QStringLiteral("make_right_add_ignore_pattern"), single_dir && isWorkingCopy());
770 
771     enableAction(QStringLiteral("make_svn_rename"), single && (!isWorkingCopy() || si != m_Data->m_Model->firstRootChild()));
772     enableAction(QStringLiteral("make_svn_copy"), single && (!isWorkingCopy() || si != m_Data->m_Model->firstRootChild()));
773 
774     /* 2. only on files */
775     enableAction(QStringLiteral("make_svn_blame"), single && !single_dir && remote_enabled);
776     enableAction(QStringLiteral("make_svn_range_blame"), single && !single_dir && remote_enabled);
777     enableAction(QStringLiteral("make_svn_cat"), single && !single_dir);
778 
779     /* 3. actions only on dirs */
780     enableAction(QStringLiteral("make_svn_mkdir"), single_dir || (none && isopen));
781     enableAction(QStringLiteral("make_svn_switch"), isWorkingCopy() && (single || none));
782     enableAction(QStringLiteral("make_switch_to_repo"), isWorkingCopy());
783     enableAction(QStringLiteral("make_import_dirs_into_current"), single_dir || dirList.size() == 1);
784     enableAction(QStringLiteral("make_svn_relocate"), isWorkingCopy() && (single || none));
785 
786     enableAction(QStringLiteral("make_svn_export_current"), ((single && single_dir) || none));
787 
788     /* local only actions */
789     /* 1. actions on files AND dirs*/
790     enableAction(QStringLiteral("make_svn_add"), (multi || single) && isWorkingCopy() && all_unversioned);
791     enableAction(QStringLiteral("make_svn_revert"), (multi || single) && isWorkingCopy() && (at_least_one_changed || at_least_one_conflicted || at_least_one_local_added));
792     enableAction(QStringLiteral("make_resolved"), (multi || single) && isWorkingCopy());
793     enableAction(QStringLiteral("make_try_resolve"), conflicted && !single_dir);
794 
795     enableAction(QStringLiteral("make_svn_info"), isopen);
796     enableAction(QStringLiteral("make_svn_merge_revisions"), (single || dirList.size() == 1) && isWorkingCopy());
797     enableAction(QStringLiteral("make_svn_merge"), single || dirList.size() == 1 || none);
798     enableAction(QStringLiteral("make_svn_addrec"), (multi || single) && at_least_one_directory && isWorkingCopy() && all_unversioned);
799     enableAction(QStringLiteral("make_svn_headupdate"), isWorkingCopy() && isopen && remote_enabled);
800     enableAction(QStringLiteral("make_dir_update"), isWorkingCopy() && isopen && remote_enabled);
801 
802     enableAction(QStringLiteral("make_svn_revupdate"), isWorkingCopy() && isopen && remote_enabled);
803     enableAction(QStringLiteral("make_svn_commit"), isWorkingCopy() && isopen && remote_enabled);
804     enableAction(QStringLiteral("make_dir_commit"), isWorkingCopy() && isopen && remote_enabled);
805 
806     enableAction(QStringLiteral("make_svn_basediff"), isWorkingCopy() && (single || none));
807     enableAction(QStringLiteral("make_svn_dirbasediff"), isWorkingCopy() && (dirList.size() < 2));
808     enableAction(QStringLiteral("make_svn_headdiff"), isWorkingCopy() && (single || none) && remote_enabled);
809 
810     /// @todo check if all items have same type
811     enableAction(QStringLiteral("make_svn_itemsdiff"), multi && fileList.size() == 2 && unique && remote_enabled && all_versioned);
812     enableAction(QStringLiteral("make_svn_diritemsdiff"), dirList.size() == 2 && isopen && remote_enabled && all_versioned);
813 
814     /* 2. on dirs only */
815     enableAction(QStringLiteral("make_cleanup"), isWorkingCopy() && (single_dir || none));
816     enableAction(QStringLiteral("make_check_unversioned"), isWorkingCopy() && ((single_dir && single) || none));
817 
818     /* remote actions only */
819     enableAction(QStringLiteral("make_svn_checkout_current"), ((single && single_dir) || none) && !isWorkingCopy() && remote_enabled);
820     /* independ actions */
821     enableAction(QStringLiteral("make_svn_checkout"), remote_enabled);
822     enableAction(QStringLiteral("make_svn_export"), true);
823     enableAction(QStringLiteral("make_view_refresh"), isopen);
824 
825     enableAction(QStringLiteral("make_revisions_diff"), isopen);
826     enableAction(QStringLiteral("make_revisions_cat"), isopen && !single_dir && single);
827     enableAction(QStringLiteral("switch_browse_revision"), !isWorkingCopy() && isopen);
828     enableAction(QStringLiteral("make_check_updates"), isWorkingCopy() && isopen && remote_enabled);
829     enableAction(QStringLiteral("openwith"), KAuthorized::authorizeAction("openwith") && single && !single_dir);
830     enableAction(QStringLiteral("show_repository_settings"), isopen);
831 
832     enableAction(QStringLiteral("repo_statistic"), isopen);
833 
834     QAction *temp = filesActions()->action(QStringLiteral("update_log_cache"));
835     if (temp) {
836         temp->setEnabled(remote_enabled);
837         if (!m_Data->m_Model->svnWrapper()->threadRunning(SvnActions::fillcachethread)) {
838             temp->setText(i18n("Update log cache"));
839         } else {
840             temp->setText(i18n("Stop updating the log cache"));
841         }
842     }
843 }
844 
add_action(const QString & actionname,const QString & text,const QKeySequence & sequ,const QIcon & icon,QObject * target,const char * slot)845 QAction *MainTreeWidget::add_action(const QString &actionname,
846                                     const QString &text,
847                                     const QKeySequence &sequ,
848                                     const QIcon &icon,
849                                     QObject *target,
850                                     const char *slot)
851 {
852     QAction *tmp_action = nullptr;
853     tmp_action = m_Data->m_Collection->addAction(actionname, target, slot);
854     tmp_action->setText(text);
855     m_Data->m_Collection->setDefaultShortcut(tmp_action, sequ);
856     tmp_action->setIcon(icon);
857     return tmp_action;
858 }
859 
filesActions()860 KActionCollection *MainTreeWidget::filesActions()
861 {
862     return m_Data->m_Collection;
863 }
864 
closeMe()865 void MainTreeWidget::closeMe()
866 {
867     m_Data->m_Model->svnWrapper()->killallThreads();
868 
869     clear();
870     setWorkingCopy(true);
871     setNetworked(false);
872     setWorkingCopy(false);
873     setBaseUri(QString());
874 
875     emit changeCaption(QString());
876     emit sigUrlOpened(false);
877     emit sigUrlChanged(QUrl());
878 
879     enableActions();
880     m_Data->m_Model->svnWrapper()->reInitClient();
881 }
882 
refreshCurrentTree()883 void MainTreeWidget::refreshCurrentTree()
884 {
885     m_Data->m_Model->refreshCurrentTree();
886     if (isWorkingCopy()) {
887         m_Data->m_Model->svnWrapper()->createModifiedCache(baseUri());
888     }
889     m_Data->m_SortModel->invalidate();
890     setUpdatesEnabled(true);
891     //viewport()->repaint();
892     QTimer::singleShot(1, this, &MainTreeWidget::readSupportData);
893 }
894 
slotSettingsChanged()895 void MainTreeWidget::slotSettingsChanged()
896 {
897     m_Data->m_SortModel->setSortCaseSensitivity(Kdesvnsettings::case_sensitive_sort() ? Qt::CaseSensitive : Qt::CaseInsensitive);
898     m_Data->m_SortModel->invalidate();
899     m_Data->m_DirSortModel->invalidate();
900     enableActions();
901     if (m_Data->m_Model->svnWrapper() && !m_Data->m_Model->svnWrapper()->doNetworking()) {
902         m_Data->m_Model->svnWrapper()->stopFillCache();
903     }
904     checkUseNavigation();
905 }
906 
offersList(SvnItem * item,bool execOnly) const907 KService::List MainTreeWidget::offersList(SvnItem *item, bool execOnly) const
908 {
909     KService::List offers;
910     if (!item) {
911         return offers;
912     }
913     if (!item->mimeType().isValid()) {
914         return offers;
915     }
916     QString constraint(QLatin1String("(DesktopEntryName != 'kdesvn') and (Type == 'Application')"));
917     if (execOnly) {
918         constraint += QLatin1String(" and (exist Exec)");
919     }
920     offers = KMimeTypeTrader::self()->query(item->mimeType().name(), QString::fromLatin1("Application"), constraint);
921     return offers;
922 }
923 
slotItemActivated(const QModelIndex & _index)924 void MainTreeWidget::slotItemActivated(const QModelIndex &_index)
925 {
926     QModelIndex index = m_Data->m_SortModel->mapToSource(_index);
927     itemActivated(index);
928 }
929 
itemActivated(const QModelIndex & index,bool keypress)930 void MainTreeWidget::itemActivated(const QModelIndex &index, bool keypress)
931 {
932     Q_UNUSED(keypress);
933     SvnItemModelNode *item;
934     if (index.isValid() && (item = static_cast<SvnItemModelNode *>(index.internalPointer()))) {
935         if (!item->isDir()) {
936             svn::Revision rev;
937             QList<QUrl> lst;
938             lst.append(item->kdeName(rev));
939             KService::List li = offersList(item, true);
940             if (li.isEmpty() || li.first()->exec().isEmpty()) {
941                 li = offersList(item);
942             }
943             if (!li.isEmpty() && !li.first()->exec().isEmpty()) {
944                 KService::Ptr ptr = li.first();
945                 KRun::runService(*ptr, lst, QApplication::activeWindow());
946             } else {
947                 KRun::displayOpenWithDialog(lst, QApplication::activeWindow());
948             }
949         } else if (Kdesvnsettings::show_navigation_panel()) {
950             m_DirTreeView->selectionModel()->select(m_Data->m_DirSortModel->mapFromSource(index), QItemSelectionModel::ClearAndSelect);
951             QModelIndex _ind = m_Data->m_Model->parent(index);
952             if (_ind.isValid()) {
953                 m_DirTreeView->expand(m_Data->m_DirSortModel->mapFromSource(_ind));
954             }
955         } else {
956 
957         }
958     }
959 }
960 
slotCheckUpdates()961 void MainTreeWidget::slotCheckUpdates()
962 {
963     if (isWorkingCopy() && m_Data->m_Model->svnWrapper()->doNetworking()) {
964         m_Data->m_TimeUpdates.stop();
965         m_Data->m_Model->svnWrapper()->createUpdateCache(baseUri());
966     }
967 }
968 
slotCheckModified()969 void MainTreeWidget::slotCheckModified()
970 {
971     if (isWorkingCopy()) {
972         m_Data->m_TimeModified.stop();
973         m_Data->m_Model->svnWrapper()->createModifiedCache(baseUri());
974     }
975 }
976 
slotNotifyMessage(const QString & what)977 void MainTreeWidget::slotNotifyMessage(const QString &what)
978 {
979     emit sigLogMessage(what);
980     QCoreApplication::processEvents();
981 }
982 
readSupportData()983 void MainTreeWidget::readSupportData()
984 {
985     /// this moment empty cause no usagedata explicit used by MainTreeWidget
986 }
987 
slotClientException(const QString & what)988 void MainTreeWidget::slotClientException(const QString &what)
989 {
990     emit sigLogMessage(what);
991     KMessageBox::sorry(QApplication::activeModalWidget(), what, i18n("SVN Error"));
992 }
993 
slotCacheDataChanged()994 void MainTreeWidget::slotCacheDataChanged()
995 {
996     m_Data->m_SortModel->invalidate();
997     if (isWorkingCopy()) {
998         if (!m_Data->m_TimeModified.isActive() && Kdesvnsettings::poll_modified()) {
999             m_Data->m_TimeModified.setInterval(MinutesToMsec(Kdesvnsettings::poll_modified_minutes()));
1000             m_Data->m_TimeModified.start();
1001         }
1002         if (!m_Data->m_TimeUpdates.isActive() && Kdesvnsettings::poll_updates()) {
1003             m_Data->m_TimeUpdates.setInterval(MinutesToMsec(Kdesvnsettings::poll_updates_minutes()));
1004             m_Data->m_TimeUpdates.start();
1005         }
1006     }
1007 }
1008 
slotIgnore()1009 void MainTreeWidget::slotIgnore()
1010 {
1011     m_Data->m_Model->makeIgnore(SelectedIndex());
1012     m_Data->m_SortModel->invalidate();
1013 }
1014 
slotLeftRecAddIgnore()1015 void MainTreeWidget::slotLeftRecAddIgnore()
1016 {
1017     SvnItem *item = DirSelected();
1018     if (!item || !item->isDir()) {
1019         return;
1020     }
1021     recAddIgnore(item);
1022 }
1023 
slotRightRecAddIgnore()1024 void MainTreeWidget::slotRightRecAddIgnore()
1025 {
1026     SvnItem *item = Selected();
1027     if (!item || !item->isDir()) {
1028         return;
1029     }
1030     recAddIgnore(item);
1031 }
1032 
recAddIgnore(SvnItem * item)1033 void MainTreeWidget::recAddIgnore(SvnItem *item)
1034 {
1035     QPointer<KSvnSimpleOkDialog> dlg(new KSvnSimpleOkDialog(QStringLiteral("ignore_pattern_dlg")));
1036     dlg->setWindowTitle(i18nc("@title:window", "Edit Pattern to Ignore for \"%1\"", item->shortName()));
1037     dlg->setWithCancelButton();
1038     EditIgnorePattern *ptr(new EditIgnorePattern(dlg));
1039     dlg->addWidget(ptr);
1040     if (dlg->exec() != QDialog::Accepted) {
1041         delete dlg;
1042         return;
1043     }
1044     svn::Depth _d = ptr->depth();
1045     QStringList _pattern = ptr->items();
1046     bool unignore = ptr->unignore();
1047     svn::Revision start(svn::Revision::WORKING);
1048     if (!isWorkingCopy()) {
1049         start = baseRevision();
1050     }
1051 
1052     svn::StatusEntries res;
1053     if (!m_Data->m_Model->svnWrapper()->makeStatus(item->fullName(), res, start, _d, true /* all entries */, false, false)) {
1054         return;
1055     }
1056     for (int i = 0; i < res.count(); ++i) {
1057         if (!res[i]->isRealVersioned() || res[i]->entry().kind() != svn_node_dir) {
1058             continue;
1059         }
1060         m_Data->m_Model->svnWrapper()->makeIgnoreEntry(res[i]->path(), _pattern, unignore);
1061     }
1062     refreshCurrentTree();
1063     delete dlg;
1064 }
1065 
slotMakeLogNoFollow() const1066 void MainTreeWidget::slotMakeLogNoFollow()const
1067 {
1068     doLog(false, false);
1069 }
1070 
slotMakeLog() const1071 void MainTreeWidget::slotMakeLog()const
1072 {
1073     doLog(true, false);
1074 }
1075 
slotDirMakeLogNoFollow() const1076 void MainTreeWidget::slotDirMakeLogNoFollow()const
1077 {
1078     doLog(false, true);
1079 }
1080 
doLog(bool use_follow_settings,bool left) const1081 void MainTreeWidget::doLog(bool use_follow_settings, bool left)const
1082 {
1083     SvnItem *k = left ? DirSelectedOrMain() : SelectedOrMain();
1084     QString what;
1085     if (k) {
1086         what = k->fullName();
1087     } else if (!isWorkingCopy() && selectionCount() == 0) {
1088         what = baseUri();
1089     } else {
1090         return;
1091     }
1092     svn::Revision start(svn::Revision::HEAD);
1093     if (!isWorkingCopy()) {
1094         start = baseRevision();
1095     }
1096     svn::Revision end(svn::Revision::START);
1097     bool list = Kdesvnsettings::self()->log_always_list_changed_files();
1098     bool follow = use_follow_settings ? Kdesvnsettings::log_follows_nodes() : false;
1099     Kdesvnsettings::setLast_node_follow(follow);
1100     int l = 50;
1101     m_Data->m_Model->svnWrapper()->makeLog(start, end, (isWorkingCopy() ? svn::Revision::UNDEFINED : baseRevision()), what, follow, list, l);
1102 }
1103 
1104 
slotContextMenu(const QPoint &)1105 void MainTreeWidget::slotContextMenu(const QPoint &)
1106 {
1107     execContextMenu(SelectionList());
1108 }
1109 
slotDirContextMenu(const QPoint & vp)1110 void MainTreeWidget::slotDirContextMenu(const QPoint &vp)
1111 {
1112     QMenu popup;
1113     QAction *temp = nullptr;
1114     int count = 0;
1115     if ((temp = filesActions()->action(QStringLiteral("make_dir_commit"))) && temp->isEnabled() && ++count) {
1116         popup.addAction(temp);
1117     }
1118     if ((temp = filesActions()->action(QStringLiteral("make_dir_update"))) && temp->isEnabled() && ++count) {
1119         popup.addAction(temp);
1120     }
1121     if ((temp = filesActions()->action(QStringLiteral("make_svn_dirbasediff"))) && temp->isEnabled() && ++count) {
1122         popup.addAction(temp);
1123     }
1124     if ((temp = filesActions()->action(QStringLiteral("make_svn_diritemsdiff"))) && temp->isEnabled() && ++count) {
1125         popup.addAction(temp);
1126     }
1127     if ((temp = filesActions()->action(QStringLiteral("make_svn_dir_log_nofollow"))) && temp->isEnabled() && ++count) {
1128         popup.addAction(temp);
1129     }
1130     if ((temp = filesActions()->action(QStringLiteral("make_left_svn_property"))) && temp->isEnabled() && ++count) {
1131         popup.addAction(temp);
1132     }
1133     if ((temp = filesActions()->action(QStringLiteral("make_svn_remove_left"))) && temp->isEnabled() && ++count) {
1134         popup.addAction(temp);
1135     }
1136     if ((temp = filesActions()->action(QStringLiteral("make_left_add_ignore_pattern"))) && temp->isEnabled() && ++count) {
1137         popup.addAction(temp);
1138     }
1139     if ((temp = filesActions()->action(QStringLiteral("set_rec_property_dir"))) && temp->isEnabled() && ++count) {
1140         popup.addAction(temp);
1141     }
1142 
1143     OpenContextmenu *me = nullptr;
1144     QAction *menuAction = nullptr;
1145     const SvnItemList l = DirSelectionList();
1146     if (l.count() == 1 && l.at(0)) {
1147         const KService::List offers = offersList(l.at(0), l.at(0)->isDir());
1148         if (!offers.isEmpty()) {
1149             svn::Revision rev(isWorkingCopy() ? svn::Revision::UNDEFINED : baseRevision());
1150             me = new OpenContextmenu(l.at(0)->kdeName(rev), offers, nullptr);
1151             me->setTitle(i18n("Open With..."));
1152             menuAction = popup.addMenu(me);
1153             ++count;
1154         }
1155     }
1156     if (count) {
1157         popup.exec(m_DirTreeView->viewport()->mapToGlobal(vp));
1158     }
1159     if (menuAction) {
1160         popup.removeAction(menuAction);
1161         delete menuAction;
1162     }
1163     delete me;
1164 
1165 }
1166 
execContextMenu(const SvnItemList & l)1167 void MainTreeWidget::execContextMenu(const SvnItemList &l)
1168 {
1169     bool isopen = baseUri().length() > 0;
1170     QString menuname;
1171 
1172     if (!isopen) {
1173         menuname = "empty";
1174     } else if (isWorkingCopy()) {
1175         menuname = "local";
1176     } else {
1177         menuname = "remote";
1178     }
1179     if (l.isEmpty()) {
1180         menuname += "_general";
1181     } else if (l.count() > 1) {
1182         menuname += "_context_multi";
1183     } else {
1184         menuname += "_context_single";
1185         if (isWorkingCopy()) {
1186             if (l.at(0)->isRealVersioned()) {
1187                 if (l.at(0)->isConflicted()) {
1188                     menuname += "_conflicted";
1189                 } else {
1190                     menuname += "_versioned";
1191                     if (l.at(0)->isDir()) {
1192                         menuname += "_dir";
1193                     }
1194                 }
1195             } else {
1196                 menuname += "_unversioned";
1197             }
1198         } else if (l.at(0)->isDir()) {
1199             menuname += "_dir";
1200         }
1201     }
1202 
1203     //qDebug("menuname: %s", qPrintable(menuname));
1204     QWidget *target;
1205     emit sigShowPopup(menuname, &target);
1206     QMenu *popup = static_cast<QMenu *>(target);
1207     if (!popup) {
1208         return;
1209     }
1210 
1211     OpenContextmenu *me = nullptr;
1212     QAction *temp = nullptr;
1213     QAction *menuAction = nullptr;
1214     if (l.count() == 1/*&&!l.at(0)->isDir()*/) {
1215         KService::List offers = offersList(l.at(0), l.at(0)->isDir());
1216         if (!offers.isEmpty()) {
1217             svn::Revision rev(isWorkingCopy() ? svn::Revision::UNDEFINED : baseRevision());
1218             me = new OpenContextmenu(l.at(0)->kdeName(rev), offers, nullptr);
1219             me->setTitle(i18n("Open With..."));
1220             menuAction = popup->addMenu(me);
1221         } else {
1222             temp = filesActions()->action(QStringLiteral("openwith"));
1223             if (temp) {
1224                 popup->addAction(temp);
1225             }
1226         }
1227     }
1228     popup->exec(QCursor::pos());
1229     if (menuAction) {
1230         popup->removeAction(menuAction);
1231     }
1232     delete me;
1233     if (temp) {
1234         popup->removeAction(temp);
1235         delete temp;
1236     }
1237 }
1238 
slotUnfoldTree()1239 void MainTreeWidget::slotUnfoldTree()
1240 {
1241     m_TreeView->expandAll();
1242 }
1243 
slotFoldTree()1244 void MainTreeWidget::slotFoldTree()
1245 {
1246     m_TreeView->collapseAll();
1247 }
1248 
slotOpenWith()1249 void MainTreeWidget::slotOpenWith()
1250 {
1251     SvnItem *which = Selected();
1252     if (!which || which->isDir()) {
1253         return;
1254     }
1255     svn::Revision rev(isWorkingCopy() ? svn::Revision::UNDEFINED : baseRevision());
1256     QList<QUrl> lst;
1257     lst.append(which->kdeName(rev));
1258     KRun::displayOpenWithDialog(lst, QApplication::activeWindow());
1259 }
1260 
slotSelectBrowsingRevision()1261 void MainTreeWidget::slotSelectBrowsingRevision()
1262 {
1263     if (isWorkingCopy()) {
1264         return;
1265     }
1266     Rangeinput_impl::revision_range range;
1267     if (Rangeinput_impl::getRevisionRange(range, false)) {
1268         m_Data->m_remoteRevision = range.first;
1269         clear();
1270         m_Data->m_Model->checkDirs(baseUri(), nullptr);
1271         emit changeCaption(baseUri() + QLatin1Char('@') + range.first.toString());
1272     }
1273 }
1274 
slotMakeTree()1275 void MainTreeWidget::slotMakeTree()
1276 {
1277     QString what;
1278     SvnItem *k = SelectedOrMain();
1279     if (k) {
1280         what = k->fullName();
1281     } else if (!isWorkingCopy() && selectionCount() == 0) {
1282         what = baseUri();
1283     } else {
1284         return;
1285     }
1286     svn::Revision rev(isWorkingCopy() ? svn::Revision::WORKING : baseRevision());
1287 
1288     m_Data->m_Model->svnWrapper()->makeTree(what, rev);
1289 }
1290 
slotMakePartTree()1291 void MainTreeWidget::slotMakePartTree()
1292 {
1293     QString what;
1294     SvnItem *k = SelectedOrMain();
1295     if (k) {
1296         what = k->fullName();
1297     } else if (!isWorkingCopy() && selectionCount() == 0) {
1298         what = baseUri();
1299     } else {
1300         return;
1301     }
1302     Rangeinput_impl::revision_range range;
1303     if (Rangeinput_impl::getRevisionRange(range)) {
1304         svn::Revision rev(isWorkingCopy() ? svn::Revision::UNDEFINED : baseRevision());
1305         m_Data->m_Model->svnWrapper()->makeTree(what, rev, range.first, range.second);
1306     }
1307 }
1308 
slotLock()1309 void MainTreeWidget::slotLock()
1310 {
1311     const SvnItemList lst = SelectionList();
1312     if (lst.isEmpty()) {
1313         KMessageBox::error(this, i18n("Nothing selected for unlock"));
1314         return;
1315     }
1316     QPointer<KSvnSimpleOkDialog> dlg(new KSvnSimpleOkDialog(QStringLiteral("locking_log_msg")));
1317     dlg->setWindowTitle(i18nc("@title:window", "Lock Message"));
1318     dlg->setWithCancelButton();
1319     Commitmsg_impl *ptr(new Commitmsg_impl(dlg));
1320     ptr->initHistory();
1321     ptr->hideDepth(true);
1322     ptr->keepsLocks(false);
1323 
1324     QCheckBox *_stealLock = new QCheckBox(i18n("Steal lock?"));
1325     ptr->addItemWidget(_stealLock);
1326 
1327     dlg->addWidget(ptr);
1328     if (dlg->exec() != QDialog::Accepted) {
1329         if (dlg)
1330             ptr->saveHistory(true);
1331         delete dlg;
1332         return;
1333     }
1334 
1335     QString logMessage = ptr->getMessage();
1336     bool steal = _stealLock->isChecked();
1337     ptr->saveHistory(false);
1338 
1339     QStringList displist;
1340     for (int i = 0; i < lst.count(); ++i) {
1341         displist.append(lst[i]->fullName());
1342     }
1343     m_Data->m_Model->svnWrapper()->makeLock(displist, logMessage, steal);
1344     refreshCurrentTree();
1345 
1346     delete dlg;
1347 }
1348 
1349 
1350 /*!
1351     \fn MainTreeWidget::slotUnlock()
1352  */
slotUnlock()1353 void MainTreeWidget::slotUnlock()
1354 {
1355     const SvnItemList lst = SelectionList();
1356     if (lst.isEmpty()) {
1357         KMessageBox::error(this, i18n("Nothing selected for unlock"));
1358         return;
1359     }
1360     KMessageBox::ButtonCode res = KMessageBox::questionYesNoCancel(this,
1361                                                                    i18n("Break lock or ignore missing locks?"),
1362                                                                    i18n("Unlocking items"));
1363     if (res == KMessageBox::Cancel) {
1364         return;
1365     }
1366     bool breakit = res == KMessageBox::Yes;
1367 
1368     QStringList displist;
1369     for (int i = 0; i < lst.count(); ++i) {
1370         displist.append(lst[i]->fullName());
1371     }
1372     m_Data->m_Model->svnWrapper()->makeUnlock(displist, breakit);
1373     refreshCurrentTree();
1374 }
1375 
slotDisplayLastDiff()1376 void MainTreeWidget::slotDisplayLastDiff()
1377 {
1378     SvnItem *kitem = Selected();
1379     QString what;
1380     if (isWorkingCopy()) {
1381         QDir::setCurrent(baseUri());
1382     }
1383     svn::Revision end = svn::Revision::PREV;
1384     if (!kitem) {
1385         if (isWorkingCopy()) {
1386             kitem = m_Data->m_Model->firstRootChild();
1387             if (!kitem) {
1388                 return;
1389             }
1390             what = relativePath(kitem);
1391         } else {
1392             what = baseUri();
1393         }
1394     } else {
1395         what = relativePath(kitem);
1396     }
1397     svn::Revision start;
1398     svn::InfoEntry inf;
1399     if (!kitem) {
1400         // it has to have an item when in working copy, so we know we are in repository view.
1401         if (!m_Data->m_Model->svnWrapper()->singleInfo(what, baseRevision(), inf)) {
1402             return;
1403         }
1404         start = inf.cmtRev();
1405     } else {
1406         start = kitem->cmtRev();
1407     }
1408     if (!isWorkingCopy()) {
1409         if (!m_Data->m_Model->svnWrapper()->singleInfo(what, start.revnum() - 1, inf)) {
1410             return;
1411         }
1412         end = inf.cmtRev();
1413     }
1414     m_Data->m_Model->svnWrapper()->makeDiff(what, end, what, start, realWidget());
1415 }
1416 
slotSimpleBaseDiff()1417 void MainTreeWidget::slotSimpleBaseDiff()
1418 {
1419     simpleWcDiff(Selected(), svn::Revision::BASE, svn::Revision::WORKING);
1420 }
1421 
slotDirSimpleBaseDiff()1422 void MainTreeWidget::slotDirSimpleBaseDiff()
1423 {
1424     simpleWcDiff(DirSelected(), svn::Revision::BASE, svn::Revision::WORKING);
1425 }
1426 
slotSimpleHeadDiff()1427 void MainTreeWidget::slotSimpleHeadDiff()
1428 {
1429     simpleWcDiff(Selected(), svn::Revision::WORKING, svn::Revision::HEAD);
1430 }
1431 
simpleWcDiff(SvnItem * kitem,const svn::Revision & first,const svn::Revision & second)1432 void MainTreeWidget::simpleWcDiff(SvnItem *kitem, const svn::Revision &first, const svn::Revision &second)
1433 {
1434     QString what;
1435     if (isWorkingCopy()) {
1436         QDir::setCurrent(baseUri());
1437     }
1438 
1439     if (!kitem) {
1440         what = QLatin1Char('.');
1441     } else {
1442         what = relativePath(kitem);
1443     }
1444     // only possible on working copies - so we may say this values
1445     m_Data->m_Model->svnWrapper()->makeDiff(what, first, second, svn::Revision::UNDEFINED, kitem ? kitem->isDir() : true);
1446 }
1447 
slotDiffRevisions()1448 void MainTreeWidget::slotDiffRevisions()
1449 {
1450     SvnItem *k = Selected();
1451     QString what;
1452     if (isWorkingCopy()) {
1453         QDir::setCurrent(baseUri());
1454     }
1455 
1456     if (!k) {
1457         what = (isWorkingCopy() ? "." : baseUri());
1458     } else {
1459         what = relativePath(k);
1460     }
1461     Rangeinput_impl::revision_range range;
1462     if (Rangeinput_impl::getRevisionRange(range)) {
1463         svn::Revision _peg = (isWorkingCopy() ? svn::Revision::WORKING : baseRevision());
1464         m_Data->m_Model->svnWrapper()->makeDiff(what, range.first, range.second, _peg, k ? k->isDir() : true);
1465     }
1466 }
1467 
slotDiffPathes()1468 void MainTreeWidget::slotDiffPathes()
1469 {
1470     SvnItemList lst;
1471 
1472     QObject *tr = sender();
1473     bool unique = false;
1474 
1475     if (tr == filesActions()->action(QStringLiteral("make_svn_diritemsdiff"))) {
1476         unique = true;
1477         lst = DirSelectionList();
1478     } else {
1479         lst = SelectionList();
1480     }
1481 
1482     if (lst.count() != 2 || (!unique && !uniqueTypeSelected())) {
1483         return;
1484     }
1485 
1486     SvnItem *k1 = lst.at(0);
1487     SvnItem *k2 = lst.at(1);
1488     QString w1, w2;
1489     svn::Revision r1;
1490 
1491     if (isWorkingCopy()) {
1492         QDir::setCurrent(baseUri());
1493         w1 = relativePath(k1);
1494         w2 = relativePath(k2);
1495         r1 = svn::Revision::WORKING;
1496     } else {
1497         w1 = k1->fullName();
1498         w2 = k2->fullName();
1499         r1 = baseRevision();
1500     }
1501     m_Data->m_Model->svnWrapper()->makeDiff(w1, r1, w2, r1);
1502 }
1503 
slotInfo()1504 void MainTreeWidget::slotInfo()
1505 {
1506     svn::Revision rev(isWorkingCopy() ? svn::Revision::UNDEFINED : baseRevision());
1507     if (!isWorkingCopy()) {
1508         rev = baseRevision();
1509     }
1510     SvnItemList lst = SelectionList();
1511     if (lst.isEmpty()) {
1512         if (!isWorkingCopy()) {
1513             QStringList _sl(baseUri());
1514             m_Data->m_Model->svnWrapper()->makeInfo(_sl, rev, svn::Revision::UNDEFINED, Kdesvnsettings::info_recursive());
1515         } else {
1516             lst.append(SelectedOrMain());
1517         }
1518     }
1519     if (!lst.isEmpty()) {
1520         m_Data->m_Model->svnWrapper()->makeInfo(lst, rev, rev, Kdesvnsettings::info_recursive());
1521     }
1522 }
1523 
slotBlame()1524 void MainTreeWidget::slotBlame()
1525 {
1526     SvnItem *k = Selected();
1527     if (!k) {
1528         return;
1529     }
1530     svn::Revision start(svn::Revision::START);
1531     svn::Revision end(svn::Revision::HEAD);
1532     m_Data->m_Model->svnWrapper()->makeBlame(start, end, k);
1533 }
1534 
slotRangeBlame()1535 void MainTreeWidget::slotRangeBlame()
1536 {
1537     SvnItem *k = Selected();
1538     if (!k) {
1539         return;
1540     }
1541     Rangeinput_impl::revision_range range;
1542     if (Rangeinput_impl::getRevisionRange(range)) {
1543         m_Data->m_Model->svnWrapper()->makeBlame(range.first, range.second, k);
1544     }
1545 }
1546 
_propListTimeout()1547 void MainTreeWidget::_propListTimeout()
1548 {
1549     dispProperties(false);
1550 }
1551 
slotDisplayProperties()1552 void MainTreeWidget::slotDisplayProperties()
1553 {
1554     dispProperties(true);
1555 }
1556 
refreshItem(SvnItemModelNode * node)1557 void MainTreeWidget::refreshItem(SvnItemModelNode *node)
1558 {
1559     if (node) {
1560         m_Data->m_Model->refreshItem(node);
1561     }
1562 }
1563 
slotChangeProperties(const svn::PropertiesMap & pm,const QStringList & dellist,const QString & path)1564 void MainTreeWidget::slotChangeProperties(const svn::PropertiesMap &pm, const QStringList &dellist, const QString &path)
1565 {
1566     m_Data->m_Model->svnWrapper()->changeProperties(pm, dellist, path);
1567     SvnItemModelNode *which = SelectedNode();
1568     if (which && which->fullName() == path) {
1569         m_Data->m_Model->refreshItem(which);
1570         dispProperties(true);
1571     }
1572 }
1573 
dispProperties(bool force)1574 void MainTreeWidget::dispProperties(bool force)
1575 {
1576     CursorStack a(Qt::BusyCursor);
1577     bool cache_Only = (!force && isNetworked() && !Kdesvnsettings::properties_on_remote_items());
1578     svn::PathPropertiesMapListPtr pm;
1579     SvnItem *k = Selected();
1580     if (!k || !k->isRealVersioned()) {
1581         emit sigProplist(svn::PathPropertiesMapListPtr(), false, false, QString(""));
1582         return;
1583     }
1584     svn::Revision rev(isWorkingCopy() ? svn::Revision::WORKING : baseRevision());
1585     pm = m_Data->m_Model->svnWrapper()->propList(k->fullName(), rev, cache_Only);
1586     emit sigProplist(pm, isWorkingCopy(), k->isDir(), k->fullName());
1587 }
1588 
slotCat()1589 void MainTreeWidget::slotCat()
1590 {
1591     SvnItem *k = Selected();
1592     if (!k) {
1593         return;
1594     }
1595     m_Data->m_Model->svnWrapper()->slotMakeCat(isWorkingCopy() ? svn::Revision::HEAD : baseRevision(), k->fullName(), k->shortName(),
1596                                                isWorkingCopy() ? svn::Revision::HEAD : baseRevision(), nullptr);
1597 }
1598 
slotRevisionCat()1599 void MainTreeWidget::slotRevisionCat()
1600 {
1601     SvnItem *k = Selected();
1602     if (!k) {
1603         return;
1604     }
1605     Rangeinput_impl::revision_range range;
1606     if (Rangeinput_impl::getRevisionRange(range, true, true)) {
1607         m_Data->m_Model->svnWrapper()->slotMakeCat(range.first, k->fullName(), k->shortName(), isWorkingCopy() ? svn::Revision::WORKING : baseRevision(), nullptr);
1608     }
1609 }
1610 
slotResolved()1611 void MainTreeWidget::slotResolved()
1612 {
1613     if (!isWorkingCopy()) {
1614         return;
1615     }
1616     SvnItem *which = SelectedOrMain();
1617     if (!which) {
1618         return;
1619     }
1620     m_Data->m_Model->svnWrapper()->slotResolved(which->fullName());
1621     which->refreshStatus(true);
1622 }
1623 
slotTryResolve()1624 void MainTreeWidget::slotTryResolve()
1625 {
1626     if (!isWorkingCopy()) {
1627         return;
1628     }
1629     SvnItem *which = Selected();
1630     if (!which || which->isDir()) {
1631         return;
1632     }
1633     m_Data->m_Model->svnWrapper()->slotResolve(which->fullName());
1634 }
1635 
slotLeftDelete()1636 void MainTreeWidget::slotLeftDelete()
1637 {
1638     makeDelete(DirSelectionList());
1639 }
1640 
slotDelete()1641 void MainTreeWidget::slotDelete()
1642 {
1643     makeDelete(SelectionList());
1644 }
1645 
makeDelete(const SvnItemList & lst)1646 void MainTreeWidget::makeDelete(const SvnItemList &lst)
1647 {
1648     if (lst.isEmpty()) {
1649         KMessageBox::error(this, i18n("Nothing selected for delete"));
1650         return;
1651     }
1652     svn::Paths items;
1653     QStringList displist;
1654     QList<QUrl> kioList;
1655     for (const SvnItem *item : lst) {
1656         if (!item->isRealVersioned()) {
1657             QUrl _uri(QUrl::fromLocalFile(item->fullName()));
1658             kioList.append(_uri);
1659         } else {
1660             items.push_back(item->fullName());
1661         }
1662         displist.append(item->fullName());
1663     }
1664 
1665     QPointer<DeleteForm> dlg(new DeleteForm(displist, QApplication::activeModalWidget()));
1666     dlg->showExtraButtons(isWorkingCopy() && !items.isEmpty());
1667 
1668     if (dlg->exec() == QDialog::Accepted) {
1669         bool force = dlg->force_delete();
1670         bool keep = dlg->keep_local();
1671         WidgetBlockStack st(this);
1672         if (!kioList.isEmpty()) {
1673             KIO::Job *aJob = KIO::del(kioList);
1674             if (!aJob->exec()) {
1675                 KJobWidgets::setWindow(aJob, this);
1676                 aJob->uiDelegate()->showErrorMessage();
1677                 delete dlg;
1678                 return;
1679             }
1680         }
1681         if (!items.isEmpty()) {
1682             m_Data->m_Model->svnWrapper()->makeDelete(svn::Targets(items), keep, force);
1683         }
1684         refreshCurrentTree();
1685     }
1686     delete dlg;
1687 }
1688 
internalDrop(const QList<QUrl> & _lst,Qt::DropAction action,const QModelIndex & index)1689 void MainTreeWidget::internalDrop(const QList<QUrl> &_lst, Qt::DropAction action, const QModelIndex &index)
1690 {
1691     if (_lst.isEmpty()) {
1692         return;
1693     }
1694     QList<QUrl> lst = _lst;
1695     QString target;
1696     QString nProto;
1697 
1698     if (!isWorkingCopy()) {
1699         nProto = svn::Url::transformProtokoll(lst[0].scheme());
1700     }
1701     QList<QUrl>::iterator it = lst.begin();
1702     for (; it != lst.end(); ++it) {
1703         (*it).setQuery(QUrlQuery());
1704         if (!nProto.isEmpty())
1705             (*it).setScheme(nProto);
1706     }
1707 
1708     if (index.isValid()) {
1709         SvnItemModelNode *node = static_cast<SvnItemModelNode *>(index.internalPointer());
1710         target = node->fullName();
1711     } else {
1712         target = baseUri();
1713     }
1714     if (action == Qt::MoveAction) {
1715         m_Data->m_Model->svnWrapper()->makeMove(lst, target);
1716     } else if (action == Qt::CopyAction) {
1717         m_Data->m_Model->svnWrapper()->makeCopy(lst, target, (isWorkingCopy() ? svn::Revision::UNDEFINED : baseRevision()));
1718     }
1719     refreshCurrentTree();
1720 }
1721 
slotUrlDropped(const QList<QUrl> & _lst,Qt::DropAction action,const QModelIndex & index,bool intern)1722 void MainTreeWidget::slotUrlDropped(const QList<QUrl> &_lst, Qt::DropAction action, const QModelIndex &index, bool intern)
1723 {
1724     if (_lst.isEmpty()) {
1725         return;
1726     }
1727     if (intern) {
1728         internalDrop(_lst, action, index);
1729         return;
1730     }
1731     QUrl target;
1732     if (index.isValid()) {
1733         SvnItemModelNode *node = static_cast<SvnItemModelNode *>(index.internalPointer());
1734         target = node->Url();
1735     } else {
1736         target = baseUriAsUrl();
1737     }
1738 
1739     if (baseUri().isEmpty()) {
1740         openUrl(_lst[0]);
1741         return;
1742     }
1743     QString path = _lst[0].path();
1744     QFileInfo fi(path);
1745     if (!isWorkingCopy()) {
1746         if (!fi.isDir()) {
1747             target.setPath(target.path() + QLatin1Char('/') + _lst[0].fileName());
1748         }
1749         slotImportIntoDir(_lst[0].toLocalFile(), target, fi.isDir());
1750     } else {
1751         WidgetBlockStack w(this);
1752         KIO::Job *job = KIO::copy(_lst, target);
1753         connect(job, &KJob::result, this, &MainTreeWidget::slotCopyFinished);
1754         job->exec();
1755     }
1756 }
1757 
slotCopyFinished(KJob * _job)1758 void MainTreeWidget::slotCopyFinished(KJob *_job)
1759 {
1760     KIO::CopyJob *job = dynamic_cast<KIO::CopyJob *>(_job);
1761     if (!job) {
1762         return;
1763     }
1764     bool ok = true;
1765     if (job->error()) {
1766         KJobWidgets::setWindow(job, this);
1767         job->uiDelegate()->showErrorMessage();
1768         ok = false;
1769     }
1770     if (ok) {
1771         const QList<QUrl> lst = job->srcUrls();
1772         const QString base = job->destUrl().toLocalFile() + QLatin1Char('/');
1773         svn::Paths tmp;
1774         tmp.reserve(lst.size());
1775         for (const QUrl &url : lst)
1776             tmp.push_back(svn::Path(base + url.fileName()));
1777 
1778         m_Data->m_Model->svnWrapper()->addItems(tmp, svn::DepthInfinity);
1779     }
1780     refreshCurrentTree();
1781 }
1782 
stopLogCache()1783 void MainTreeWidget::stopLogCache()
1784 {
1785     QAction *temp = filesActions()->action(QStringLiteral("update_log_cache"));
1786     m_Data->m_Model->svnWrapper()->stopFillCache();
1787     if (temp) {
1788         temp->setText(i18n("Update log cache"));
1789     }
1790 }
1791 
slotUpdateLogCache()1792 void MainTreeWidget::slotUpdateLogCache()
1793 {
1794     if (baseUri().length() > 0 && m_Data->m_Model->svnWrapper()->doNetworking()) {
1795         QAction *temp = filesActions()->action(QStringLiteral("update_log_cache"));
1796         if (!m_Data->m_Model->svnWrapper()->threadRunning(SvnActions::fillcachethread)) {
1797             m_Data->m_Model->svnWrapper()->startFillCache(baseUri());
1798             if (temp) {
1799                 temp->setText(i18n("Stop updating the log cache"));
1800             }
1801         } else {
1802             m_Data->m_Model->svnWrapper()->stopFillCache();
1803             if (temp) {
1804                 temp->setText(i18n("Update log cache"));
1805             }
1806         }
1807     }
1808 }
1809 
slotMkBaseDirs()1810 void MainTreeWidget::slotMkBaseDirs()
1811 {
1812     bool isopen = !baseUri().isEmpty();
1813     if (!isopen) {
1814         return;
1815     }
1816     QString parentDir = baseUri();
1817     svn::Paths targets;
1818     targets.append(svn::Path(parentDir + QLatin1String("/trunk")));
1819     targets.append(svn::Path(parentDir + QLatin1String("/branches")));
1820     targets.append(svn::Path(parentDir + QLatin1String("/tags")));
1821     QString msg = i18n("Automatic generated base layout by kdesvn");
1822     isopen = m_Data->m_Model->svnWrapper()->makeMkdir(svn::Targets(targets), msg);
1823     if (isopen) {
1824         refreshCurrentTree();
1825     }
1826 }
1827 
slotMkdir()1828 void MainTreeWidget::slotMkdir()
1829 {
1830     SvnItemModelNode *k = SelectedNode();
1831     QString parentDir;
1832     if (k) {
1833         if (!k->isDir()) {
1834             KMessageBox::sorry(nullptr, i18n("May not make subdirectories of a file"));
1835             return;
1836         }
1837         parentDir = k->fullName();
1838     } else {
1839         parentDir = baseUri();
1840     }
1841     QString ex = m_Data->m_Model->svnWrapper()->makeMkdir(parentDir);
1842     if (!ex.isEmpty()) {
1843         m_Data->m_Model->refreshDirnode(static_cast<SvnItemModelNodeDir *>(k), true, true);
1844     }
1845 }
1846 
slotRename()1847 void MainTreeWidget::slotRename()
1848 {
1849     copy_move(true);
1850 }
slotCopy()1851 void MainTreeWidget::slotCopy()
1852 {
1853     copy_move(false);
1854 }
1855 
copy_move(bool move)1856 void MainTreeWidget::copy_move(bool move)
1857 {
1858     if (isWorkingCopy() && SelectedNode() == m_Data->m_Model->firstRootChild()) {
1859         return;
1860     }
1861     bool ok;
1862     SvnItemModelNode *which = SelectedNode();
1863     if (!which) {
1864         return;
1865     }
1866     QString nName = CopyMoveView_impl::getMoveCopyTo(&ok, move, which->fullName(), baseUri(), this);
1867     if (!ok) {
1868         return;
1869     }
1870     if (move) {
1871         m_Data->m_Model->svnWrapper()->makeMove(which->fullName(), nName);
1872     } else {
1873         m_Data->m_Model->svnWrapper()->makeCopy(which->fullName(), nName, isWorkingCopy() ? svn::Revision::HEAD : baseRevision());
1874     }
1875 }
1876 
slotCleanupAction()1877 void MainTreeWidget::slotCleanupAction()
1878 {
1879     if (!isWorkingCopy()) {
1880         return;
1881     }
1882     SvnItemModelNode *which = SelectedNode();
1883     if (!which) {
1884         which = m_Data->m_Model->firstRootChild();
1885     }
1886     if (!which || !which->isDir()) {
1887         return;
1888     }
1889     if (m_Data->m_Model->svnWrapper()->makeCleanup(which->fullName())) {
1890         which->refreshStatus(true);
1891     }
1892 }
1893 
slotMergeRevisions()1894 void MainTreeWidget::slotMergeRevisions()
1895 {
1896     if (!isWorkingCopy()) {
1897         return;
1898     }
1899     SvnItemModelNode *which = SelectedNode();
1900     if (!which) {
1901         return;
1902     }
1903     bool force, dry, rec, irelated, useExternal, allowmixedrevs;
1904     Rangeinput_impl::revision_range range;
1905     if (!MergeDlg_impl::getMergeRange(range, &force, &rec, &irelated, &dry, &useExternal, &allowmixedrevs, this)) {
1906         return;
1907     }
1908     if (!useExternal) {
1909         m_Data->m_Model->svnWrapper()->slotMergeWcRevisions(which->fullName(), range.first, range.second, rec, !irelated, force, dry, allowmixedrevs);
1910     } else {
1911         m_Data->m_Model->svnWrapper()->slotMergeExternal(which->fullName(), which->fullName(), which->fullName(),
1912                                                          range.first, range.second,
1913                                                          isWorkingCopy() ? svn::Revision::UNDEFINED : m_Data->m_remoteRevision,
1914                                                          rec);
1915     }
1916     refreshItem(which);
1917     if (which->isDir()) {
1918         m_Data->m_Model->refreshDirnode(static_cast<SvnItemModelNodeDir *>(which), true, false);
1919     }
1920 }
1921 
slotMerge()1922 void MainTreeWidget::slotMerge()
1923 {
1924     SvnItemModelNode *which = SelectedNode();
1925     QString src1, src2, target;
1926     if (isWorkingCopy()) {
1927         if (m_Data->merge_Target.isEmpty()) {
1928             target = which ? which->fullName() : baseUri();
1929         } else {
1930             target = m_Data->merge_Target;
1931         }
1932         src1 = m_Data->merge_Src1;
1933     } else {
1934         if (m_Data->merge_Src1.isEmpty()) {
1935             src1 = which ? which->fullName() : baseUri();
1936         } else {
1937             src1 = m_Data->merge_Src1;
1938         }
1939         target = m_Data->merge_Target;
1940     }
1941     src2 = m_Data->merge_Src2;
1942     QPointer<KSvnSimpleOkDialog> dlg(new KSvnSimpleOkDialog(QStringLiteral("merge_dialog")));
1943     dlg->setWindowTitle(i18nc("@title:window", "Merge"));
1944     dlg->setWithCancelButton();
1945     dlg->setHelp(QLatin1String("merging-items"));
1946     MergeDlg_impl *ptr(new MergeDlg_impl(dlg));
1947     ptr->setDest(target);
1948     ptr->setSrc1(src1);
1949     ptr->setSrc2(src1);
1950     dlg->addWidget(ptr);
1951     if (dlg->exec() == QDialog::Accepted) {
1952         src1 = ptr->Src1();
1953         src2 = ptr->Src2();
1954         if (src2.isEmpty()) {
1955             src2 = src1;
1956         }
1957         target = ptr->Dest();
1958         m_Data->merge_Src2 = src2;
1959         m_Data->merge_Src1 = src1;
1960         m_Data->merge_Target = target;
1961         bool force = ptr->force();
1962         bool dry = ptr->dryrun();
1963         bool rec = ptr->recursive();
1964         bool irelated = ptr->ignorerelated();
1965         bool useExternal = ptr->useExtern();
1966         bool allowmixedrevs = ptr->allowmixedrevs();
1967         bool recordOnly = ptr->recordOnly();
1968         Rangeinput_impl::revision_range range = ptr->getRange();
1969         bool reintegrate = ptr->reintegrate();
1970         if (!useExternal) {
1971             m_Data->m_Model->svnWrapper()->slotMerge(src1, src2, target, range.first, range.second,
1972                                                      isWorkingCopy() ? svn::Revision::UNDEFINED : m_Data->m_remoteRevision,
1973                                                      rec, !irelated, force, dry, recordOnly, reintegrate, allowmixedrevs);
1974         } else {
1975             m_Data->m_Model->svnWrapper()->slotMergeExternal(src1, src2, target, range.first, range.second,
1976                                                              isWorkingCopy() ? svn::Revision::UNDEFINED : m_Data->m_remoteRevision,
1977                                                              rec);
1978         }
1979         if (isWorkingCopy()) {
1980             //            refreshItem(which);
1981             //            refreshRecursive(which);
1982             refreshCurrentTree();
1983         }
1984     }
1985     delete dlg;
1986     enableActions();
1987 }
1988 
slotRelocate()1989 void MainTreeWidget::slotRelocate()
1990 {
1991     if (!isWorkingCopy()) {
1992         return;
1993     }
1994     SvnItem *k = SelectedOrMain();
1995     if (!k) {
1996         KMessageBox::error(nullptr, i18n("Error getting entry to relocate"));
1997         return;
1998     }
1999     const QString path = k->fullName();
2000     const QUrl fromUrl = k->Url();
2001     QPointer<KSvnSimpleOkDialog> dlg(new KSvnSimpleOkDialog(QStringLiteral("relocate_dlg")));
2002     dlg->setWindowTitle(i18nc("@title:window", "Relocate Path %1", path));
2003     dlg->setWithCancelButton();
2004     CheckoutInfo_impl *ptr(new CheckoutInfo_impl(dlg));
2005     ptr->setStartUrl(fromUrl);
2006     ptr->disableAppend(true);
2007     ptr->disableTargetDir(true);
2008     ptr->disableRange(true);
2009     ptr->disableOpen(true);
2010     ptr->hideDepth(true);
2011     ptr->hideOverwrite(true);
2012     dlg->addWidget(ptr);
2013     bool done = false;
2014     if (dlg->exec() == QDialog::Accepted) {
2015         if (!ptr->reposURL().isValid()) {
2016             KMessageBox::error(QApplication::activeModalWidget(), i18n("Invalid url given!"),
2017                                i18n("Relocate path %1", path));
2018             delete dlg;
2019             return;
2020         }
2021         done = m_Data->m_Model->svnWrapper()->makeRelocate(fromUrl, ptr->reposURL(), path, ptr->overwrite(), ptr->ignoreExternals());
2022     }
2023     delete dlg;
2024     if (done) {
2025         refreshItem(k->sItem());
2026     }
2027 }
2028 
slotImportDirsIntoCurrent()2029 void MainTreeWidget::slotImportDirsIntoCurrent()
2030 {
2031     slotImportIntoCurrent(true);
2032 }
2033 
2034 /*!
2035 \fn MainTreeWidget::slotImportIntoCurrent()
2036 */
slotImportIntoCurrent(bool dirs)2037 void MainTreeWidget::slotImportIntoCurrent(bool dirs)
2038 {
2039     if (selectionCount() > 1) {
2040         KMessageBox::error(this, i18n("Cannot import into multiple targets"));
2041         return;
2042     }
2043     QUrl targetDir;
2044     if (selectionCount() == 0) {
2045         if (isNetworked())
2046             targetDir = QUrl(baseUri());
2047         else
2048             targetDir = QUrl::fromLocalFile(baseUri());
2049     } else {
2050         targetDir = SelectedNode()->Url();
2051     }
2052     QString source;
2053     if (dirs) {
2054         source = QFileDialog::getExistingDirectory(this, i18n("Import files from folder"));
2055     } else {
2056         source = QFileDialog::getOpenFileName(this, i18n("Import file"), QString());
2057     }
2058 
2059     slotImportIntoDir(source, targetDir, dirs);
2060 }
2061 
slotImportIntoDir(const QString & source,const QUrl & _targetUri,bool dirs)2062 void MainTreeWidget::slotImportIntoDir(const QString &source, const QUrl &_targetUri, bool dirs)
2063 {
2064     QString sourceUri = source;
2065     while (sourceUri.endsWith(QLatin1Char('/'))) {
2066         sourceUri.chop(1);
2067     }
2068     if (sourceUri.isEmpty()) {
2069         return;
2070     }
2071 
2072     if (_targetUri.isEmpty()) {
2073         return;
2074     }
2075     QUrl targetUri(_targetUri);
2076 
2077     QPointer<KSvnSimpleOkDialog> dlg(new KSvnSimpleOkDialog(QStringLiteral("import_log_msg")));
2078     dlg->setWindowTitle(i18nc("@title:window", "Import Log"));
2079     dlg->setWithCancelButton();
2080     Commitmsg_impl *ptr = nullptr;
2081     Importdir_logmsg *ptr2 = nullptr;
2082     if (dirs) {
2083         ptr2 = new Importdir_logmsg(dlg);
2084         ptr2->createDirboxDir(QLatin1Char('"') + QFileInfo(sourceUri).fileName() + QLatin1Char('"'));
2085         ptr = ptr2;
2086     } else {
2087         ptr = new Commitmsg_impl(dlg);
2088     }
2089     ptr->initHistory();
2090     dlg->addWidget(ptr);
2091     if (dlg->exec() != QDialog::Accepted) {
2092         if (dlg) {
2093             ptr->saveHistory(true);
2094             delete dlg;
2095         }
2096         return;
2097     }
2098 
2099     QString logMessage = ptr->getMessage();
2100     svn::Depth rec = ptr->getDepth();
2101     ptr->saveHistory(false);
2102 
2103     if (dirs && ptr2 && ptr2->createDir()) {
2104         targetUri.setPath(targetUri.path() + QLatin1Char('/') + QFileInfo(sourceUri).fileName());
2105     }
2106     if (ptr2) {
2107         m_Data->m_Model->svnWrapper()->slotImport(sourceUri, targetUri, logMessage, rec, ptr2->noIgnore(), ptr2->ignoreUnknownNodes());
2108     } else {
2109         m_Data->m_Model->svnWrapper()->slotImport(sourceUri, targetUri, logMessage, rec, false, false);
2110     }
2111     if (!isWorkingCopy()) {
2112         if (selectionCount() == 0) {
2113             refreshCurrentTree();
2114         } else {
2115             m_Data->m_Model->refreshItem(SelectedNode());
2116         }
2117     }
2118     delete dlg;
2119 }
2120 
slotChangeToRepository()2121 void MainTreeWidget::slotChangeToRepository()
2122 {
2123     if (!isWorkingCopy()) {
2124         return;
2125     }
2126     SvnItemModelNode *k = m_Data->m_Model->firstRootChild();
2127     /* huh... */
2128     if (!k) {
2129         return;
2130     }
2131     svn::InfoEntry i;
2132     if (!m_Data->m_Model->svnWrapper()->singleInfo(k->Url().toString(), svn::Revision::UNDEFINED, i)) {
2133         return;
2134     }
2135     if (i.reposRoot().isEmpty()) {
2136         KMessageBox::sorry(QApplication::activeModalWidget(), i18n("Could not retrieve repository of working copy."), i18n("SVN Error"));
2137     } else {
2138         emit sigSwitchUrl(i.reposRoot());
2139     }
2140 }
2141 
slotCheckNewItems()2142 void MainTreeWidget::slotCheckNewItems()
2143 {
2144     if (!isWorkingCopy()) {
2145         KMessageBox::sorry(nullptr, i18n("Only in working copy possible."), i18n("Error"));
2146         return;
2147     }
2148     if (selectionCount() > 1) {
2149         KMessageBox::sorry(nullptr, i18n("Only on single folder possible"), i18n("Error"));
2150         return;
2151     }
2152     SvnItem *w = SelectedOrMain();
2153     if (!w) {
2154         KMessageBox::sorry(nullptr, i18n("Sorry - internal error"), i18n("Error"));
2155         return;
2156     }
2157     m_Data->m_Model->svnWrapper()->checkAddItems(w->fullName(), true);
2158 }
2159 
refreshCurrent(SvnItem * cur)2160 void MainTreeWidget::refreshCurrent(SvnItem *cur)
2161 {
2162     if (!cur || !cur->sItem()) {
2163         refreshCurrentTree();
2164         return;
2165     }
2166     QCoreApplication::processEvents();
2167     setUpdatesEnabled(false);
2168     if (cur->isDir()) {
2169         m_Data->m_Model->refreshDirnode(static_cast<SvnItemModelNodeDir *>(cur->sItem()));
2170     } else {
2171         m_Data->m_Model->refreshItem(cur->sItem());
2172     }
2173     setUpdatesEnabled(true);
2174     m_TreeView->viewport()->repaint();
2175 }
2176 
slotReinitItem(SvnItem * item)2177 void MainTreeWidget::slotReinitItem(SvnItem *item)
2178 {
2179     if (!item) {
2180         return;
2181     }
2182     SvnItemModelNode *k = item->sItem();
2183     if (!k) {
2184         return;
2185     }
2186     m_Data->m_Model->refreshItem(k);
2187     if (k->isDir()) {
2188         m_Data->m_Model->clearNodeDir(static_cast<SvnItemModelNodeDir *>(k));
2189     }
2190 }
2191 
keyPressEvent(QKeyEvent * event)2192 void MainTreeWidget::keyPressEvent(QKeyEvent *event)
2193 {
2194     if ((event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) && !event->isAutoRepeat()) {
2195         QModelIndex index = SelectedIndex();
2196         if (index.isValid()) {
2197             itemActivated(index, true);
2198             return;
2199         }
2200     }
2201     QWidget::keyPressEvent(event);
2202 }
2203 
slotItemExpanded(const QModelIndex &)2204 void MainTreeWidget::slotItemExpanded(const QModelIndex &)
2205 {
2206 }
2207 
slotItemsInserted(const QModelIndex &)2208 void MainTreeWidget::slotItemsInserted(const QModelIndex &)
2209 {
2210     m_Data->m_resizeColumnsTimer.start(50);
2211 }
2212 
slotDirSelectionChanged(const QItemSelection & _item,const QItemSelection &)2213 void MainTreeWidget::slotDirSelectionChanged(const QItemSelection &_item, const QItemSelection &)
2214 {
2215     const QModelIndexList _indexes = _item.indexes();
2216     switch (DirselectionCount()) {
2217     case 1:
2218         m_DirTreeView->setStatusTip(i18n("Hold Ctrl key while click on selected item for unselect"));
2219         break;
2220     case 2:
2221         m_DirTreeView->setStatusTip(i18n("See context menu for more actions"));
2222         break;
2223     case 0:
2224         m_DirTreeView->setStatusTip(i18n("Click for navigate"));
2225         break;
2226     default:
2227         m_DirTreeView->setStatusTip(i18n("Navigation"));
2228         break;
2229     }
2230     if (_indexes.size() >= 1) {
2231         const QModelIndex _t = m_Data->srcDirInd(_indexes.at(0));
2232         if (m_Data->m_Model->canFetchMore(_t)) {
2233             WidgetBlockStack st(m_TreeView);
2234             WidgetBlockStack st2(m_DirTreeView);
2235             m_Data->m_Model->fetchMore(_t);
2236         }
2237         if (Kdesvnsettings::show_navigation_panel()) {
2238             m_TreeView->setRootIndex(m_Data->m_SortModel->mapFromSource(_t));
2239         }
2240         // Display relative path (including name of the checkout) in the titlebar
2241         auto item = m_Data->m_Model->nodeForIndex(_t);
2242         if (item) {
2243             const QString repoBasePath = baseUri();
2244             const QString relativePath = item->fullName().mid(repoBasePath.lastIndexOf('/') + 1);
2245             emit changeCaption(relativePath);
2246         }
2247 
2248     } else {
2249         checkSyncTreeModel();
2250     }
2251     if (m_TreeView->selectionModel()->hasSelection()) {
2252         m_TreeView->selectionModel()->clearSelection();
2253     } else {
2254         enableActions();
2255     }
2256     resizeAllColumns();
2257 }
2258 
checkSyncTreeModel()2259 void MainTreeWidget::checkSyncTreeModel()
2260 {
2261     // make sure that the treeview shows the contents of the selected directory in the directory tree view
2262     // it can go out of sync when the dir tree model has no current index - then we use the first entry
2263     // or when the filter settings are changed
2264     QModelIndex curIdxDir = m_DirTreeView->currentIndex();
2265     if (!curIdxDir.isValid() && m_Data->m_DirSortModel->columnCount() > 0)
2266     {
2267         m_DirTreeView->setCurrentIndex(m_Data->m_DirSortModel->index(0, 0));
2268         curIdxDir = m_DirTreeView->currentIndex();
2269     }
2270     const QModelIndex curIdxBase = m_Data->srcDirInd(curIdxDir);
2271     m_TreeView->setRootIndex(m_Data->m_SortModel->mapFromSource(curIdxBase));
2272 }
2273 
slotCommit()2274 void MainTreeWidget::slotCommit()
2275 {
2276     m_Data->m_Model->svnWrapper()->doCommit(SelectionList());
2277 }
2278 
slotDirCommit()2279 void MainTreeWidget::slotDirCommit()
2280 {
2281     m_Data->m_Model->svnWrapper()->doCommit(DirSelectionList());
2282 }
2283 
slotDirUpdate()2284 void MainTreeWidget::slotDirUpdate()
2285 {
2286     const SvnItemList which = DirSelectionList();
2287     svn::Paths what;
2288     if (which.isEmpty()) {
2289         what.append(svn::Path(baseUri()));
2290     } else {
2291         what.reserve(which.size());
2292         for (const SvnItem *item : which)
2293             what.append(svn::Path(item->fullName()));
2294     }
2295     m_Data->m_Model->svnWrapper()->makeUpdate(svn::Targets(what), svn::Revision::HEAD, svn::DepthUnknown);
2296 }
2297 
slotRefreshItem(const QString & path)2298 void MainTreeWidget::slotRefreshItem(const QString &path)
2299 {
2300     const QModelIndex idx = m_Data->m_Model->findIndex(path);
2301     if (!idx.isValid())
2302         return;
2303     m_Data->m_Model->emitDataChangedRow(idx);
2304 }
2305 
checkUseNavigation(bool startup)2306 void MainTreeWidget::checkUseNavigation(bool startup)
2307 {
2308     bool use = Kdesvnsettings::show_navigation_panel();
2309     if (use)
2310     {
2311         checkSyncTreeModel();
2312     }
2313     else
2314     {
2315         // tree view is the only visible view, make sure to display all
2316         m_TreeView->setRootIndex(QModelIndex());
2317         m_TreeView->expand(QModelIndex());
2318     }
2319     m_TreeView->setExpandsOnDoubleClick(!use);
2320     m_TreeView->setRootIsDecorated(!use);
2321     m_TreeView->setItemsExpandable(!use);
2322     QList<int> si;
2323     if (use) {
2324         if (!startup) {
2325             si = m_ViewSplitter->sizes();
2326             if (si.size() == 2 && si[0] < 5) {
2327                 si[0] = 200;
2328                 m_ViewSplitter->setSizes(si);
2329             }
2330         }
2331     } else {
2332         si << 0 << 300;
2333         m_ViewSplitter->setSizes(si);
2334 
2335     }
2336 }
2337 
slotRepositorySettings()2338 void MainTreeWidget::slotRepositorySettings()
2339 {
2340     if (baseUri().length() == 0) {
2341         return;
2342     }
2343     svn::InfoEntry inf;
2344     if (!m_Data->m_Model->svnWrapper()->singleInfo(baseUri(), baseRevision(), inf)) {
2345         return;
2346     }
2347     if (inf.reposRoot().isEmpty()) {
2348         KMessageBox::sorry(QApplication::activeModalWidget(), i18n("Could not retrieve repository."), i18n("SVN Error"));
2349     } else {
2350         DbSettings::showSettings(inf.reposRoot().toString(), this);
2351     }
2352 }
2353 
slotRightProperties()2354 void MainTreeWidget::slotRightProperties()
2355 {
2356     SvnItem *k = Selected();
2357     if (!k) {
2358         return;
2359     }
2360     m_Data->m_Model->svnWrapper()->editProperties(k, isWorkingCopy() ? svn::Revision::WORKING : svn::Revision::HEAD);
2361 }
2362 
slotLeftProperties()2363 void MainTreeWidget::slotLeftProperties()
2364 {
2365     SvnItem *k = DirSelected();
2366     if (!k) {
2367         return;
2368     }
2369     m_Data->m_Model->svnWrapper()->editProperties(k, isWorkingCopy() ? svn::Revision::WORKING : svn::Revision::HEAD);
2370 }
2371 
slotDirRecProperty()2372 void MainTreeWidget::slotDirRecProperty()
2373 {
2374     SvnItem *k = DirSelected();
2375     if (!k) {
2376         return;
2377     }
2378     KMessageBox::information(this, i18n("Not yet implemented"), i18n("Edit property recursively"));
2379 }
2380