katefiletree.cpp 27.3 KB
Newer Older
1
/* This file is part of the KDE project
2
3
4
5
   SPDX-FileCopyrightText: 2010 Thomas Fjellstrom <thomas@fjellstrom.ca>
   SPDX-FileCopyrightText: 2014 Joseph Wenninger <jowenn@kde.org>

   SPDX-License-Identifier: LGPL-2.0-or-later
6
7
*/

8
// BEGIN Includes
9
#include "katefiletree.h"
Christoph Cullmann's avatar
Christoph Cullmann committed
10

11
#include "katefiletreedebug.h"
12
#include "katefiletreemodel.h"
13
#include "katefiletreeproxymodel.h"
14

15
#include <ktexteditor/application.h>
16
#include <ktexteditor/document.h>
17
#include <ktexteditor/editor.h>
18

Christoph Cullmann's avatar
Christoph Cullmann committed
19
#include <KApplicationTrader>
Christoph Cullmann's avatar
Christoph Cullmann committed
20
#include <KIO/ApplicationLauncherJob>
21
22
#include <KIO/CopyJob>
#include <KIO/DeleteJob>
Christoph Cullmann's avatar
Christoph Cullmann committed
23
#include <KIO/JobUiDelegate>
24
25
26
#include <KIO/OpenFileManagerWindowJob>
#include <KLocalizedString>
#include <KMessageBox>
27
#include <KStandardAction>
28

29
#include <QApplication>
Michal Humpula's avatar
Michal Humpula committed
30
31
#include <QClipboard>
#include <QContextMenuEvent>
32
#include <QDir>
33
#include <QHeaderView>
34
35
#include <QInputDialog>
#include <QLineEdit>
36
37
#include <QMenu>
#include <QMimeDatabase>
38
#include <QStyledItemDelegate>
39
// END Includes
40

41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class StyleDelegate : public QStyledItemDelegate
{
public:
    StyleDelegate(QObject *parent = nullptr)
        : QStyledItemDelegate(parent)
    {
    }

    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
        QStyledItemDelegate::paint(painter, option, index);

        if (!m_closeBtn) {
            return;
        }

        auto doc = index.data(KateFileTreeModel::DocumentRole).value<KTextEditor::Document *>();
        if (doc && index.column() == 1 && option.state & QStyle::State_MouseOver) {
Waqar Ahmed's avatar
Waqar Ahmed committed
59
60
61
            const QIcon icon = QIcon::fromTheme(QStringLiteral("tab-close"));
            int w = option.decorationSize.width();
            QRect iconRect(option.rect.right() - w, option.rect.top(), w, option.rect.height());
62
63
64
65
66
67
68
69
70
71
            icon.paint(painter, iconRect, Qt::AlignRight | Qt::AlignVCenter);
        }
    }

    void setShowCloseButton(bool s)
    {
        m_closeBtn = s;
    }

private:
Waqar Ahmed's avatar
Waqar Ahmed committed
72
    bool m_closeBtn = false;
73
74
};

75
// BEGIN KateFileTree
76

77
78
KateFileTree::KateFileTree(QWidget *parent)
    : QTreeView(parent)
79
{
Dominik Haumann's avatar
Dominik Haumann committed
80
81
82
    setAcceptDrops(false);
    setIndentation(12);
    setAllColumnsShowFocus(true);
83
    setFocusPolicy(Qt::NoFocus);
84
85
    setDragEnabled(true);
    setDragDropMode(QAbstractItemView::DragOnly);
86
87
88
    setSelectionBehavior(QAbstractItemView::SelectRows);

    setItemDelegate(new StyleDelegate(this));
89

90
    // handle activated (e.g. for pressing enter) + clicked (to avoid to need to do double-click e.g. on Windows)
91
92
    connect(this, &KateFileTree::activated, this, &KateFileTree::mouseClicked);
    connect(this, &KateFileTree::clicked, this, &KateFileTree::mouseClicked);
93

Laurent Montel's avatar
Laurent Montel committed
94
    m_filelistReloadDocument = new QAction(QIcon::fromTheme(QStringLiteral("view-refresh")), i18nc("@action:inmenu", "Reloa&d"), this);
95
    connect(m_filelistReloadDocument, &QAction::triggered, this, &KateFileTree::slotDocumentReload);
Dominik Haumann's avatar
Dominik Haumann committed
96
    m_filelistReloadDocument->setWhatsThis(i18n("Reload selected document(s) from disk."));
97

Laurent Montel's avatar
Laurent Montel committed
98
    m_filelistCloseDocument = new QAction(QIcon::fromTheme(QStringLiteral("document-close")), i18nc("@action:inmenu", "Close"), this);
99
    connect(m_filelistCloseDocument, &QAction::triggered, this, &KateFileTree::slotDocumentClose);
Dominik Haumann's avatar
Dominik Haumann committed
100
    m_filelistCloseDocument->setWhatsThis(i18n("Close the current document."));
101

Laurent Montel's avatar
Laurent Montel committed
102
    m_filelistExpandRecursive = new QAction(QIcon::fromTheme(QStringLiteral("view-list-tree")), i18nc("@action:inmenu", "Expand recursively"), this);
103
    connect(m_filelistExpandRecursive, &QAction::triggered, this, &KateFileTree::slotExpandRecursive);
104
105
    m_filelistExpandRecursive->setWhatsThis(i18n("Expand the file list sub tree recursively."));

Laurent Montel's avatar
Laurent Montel committed
106
    m_filelistCollapseRecursive = new QAction(QIcon::fromTheme(QStringLiteral("view-list-tree")), i18nc("@action:inmenu", "Collapse recursively"), this);
107
    connect(m_filelistCollapseRecursive, &QAction::triggered, this, &KateFileTree::slotCollapseRecursive);
108
109
    m_filelistCollapseRecursive->setWhatsThis(i18n("Collapse the file list sub tree recursively."));

Laurent Montel's avatar
Laurent Montel committed
110
    m_filelistCloseOtherDocument = new QAction(QIcon::fromTheme(QStringLiteral("document-close")), i18nc("@action:inmenu", "Close Other"), this);
111
    connect(m_filelistCloseOtherDocument, &QAction::triggered, this, &KateFileTree::slotDocumentCloseOther);
Dominik Haumann's avatar
Dominik Haumann committed
112
    m_filelistCloseOtherDocument->setWhatsThis(i18n("Close other documents in this folder."));
113

Alexander Lohnau's avatar
Alexander Lohnau committed
114
115
    m_filelistOpenContainingFolder =
        new QAction(QIcon::fromTheme(QStringLiteral("document-open-folder")), i18nc("@action:inmenu", "Open Containing Folder"), this);
116
117
118
    connect(m_filelistOpenContainingFolder, &QAction::triggered, this, &KateFileTree::slotOpenContainingFolder);
    m_filelistOpenContainingFolder->setWhatsThis(i18n("Open the folder this file is located in."));

Laurent Montel's avatar
Laurent Montel committed
119
    m_filelistCopyFilename = new QAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18nc("@action:inmenu", "Copy File Path"), this);
120
    connect(m_filelistCopyFilename, &QAction::triggered, this, &KateFileTree::slotCopyFilename);
121
    m_filelistCopyFilename->setWhatsThis(i18n("Copy path and filename to the clipboard."));
122

123
    m_filelistRenameFile = new QAction(QIcon::fromTheme(QStringLiteral("edit-rename")), i18nc("@action:inmenu", "Rename..."), this);
124
    connect(m_filelistRenameFile, &QAction::triggered, this, &KateFileTree::slotRenameFile);
125
    m_filelistRenameFile->setWhatsThis(i18n("Rename the selected file."));
126

127
    m_filelistPrintDocument = KStandardAction::print(this, &KateFileTree::slotPrintDocument, this);
Dominik Haumann's avatar
Dominik Haumann committed
128
    m_filelistPrintDocument->setWhatsThis(i18n("Print selected document."));
129

130
    m_filelistPrintDocumentPreview = KStandardAction::printPreview(this, &KateFileTree::slotPrintDocumentPreview, this);
Dominik Haumann's avatar
Dominik Haumann committed
131
    m_filelistPrintDocumentPreview->setWhatsThis(i18n("Show print preview of current document"));
132

133
    m_filelistDeleteDocument = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18nc("@action:inmenu", "Delete"), this);
134
    connect(m_filelistDeleteDocument, &QAction::triggered, this, &KateFileTree::slotDocumentDelete);
Dominik Haumann's avatar
Dominik Haumann committed
135
    m_filelistDeleteDocument->setWhatsThis(i18n("Close and delete selected file from storage."));
136

Dominik Haumann's avatar
Dominik Haumann committed
137
    QActionGroup *modeGroup = new QActionGroup(this);
138

Alexander Lohnau's avatar
Alexander Lohnau committed
139
140
141
142
143
144
    m_treeModeAction = setupOption(modeGroup,
                                   QIcon::fromTheme(QStringLiteral("view-list-tree")),
                                   i18nc("@action:inmenu", "Tree Mode"),
                                   i18n("Set view style to Tree Mode"),
                                   SLOT(slotTreeMode()),
                                   true);
145

Alexander Lohnau's avatar
Alexander Lohnau committed
146
147
148
149
150
151
    m_listModeAction = setupOption(modeGroup,
                                   QIcon::fromTheme(QStringLiteral("view-list-text")),
                                   i18nc("@action:inmenu", "List Mode"),
                                   i18n("Set view style to List Mode"),
                                   SLOT(slotListMode()),
                                   false);
152

Dominik Haumann's avatar
Dominik Haumann committed
153
    QActionGroup *sortGroup = new QActionGroup(this);
154

Alexander Lohnau's avatar
Alexander Lohnau committed
155
156
    m_sortByFile =
        setupOption(sortGroup, QIcon(), i18nc("@action:inmenu sorting option", "Document Name"), i18n("Sort by Document Name"), SLOT(slotSortName()), true);
157

Alexander Lohnau's avatar
Alexander Lohnau committed
158
159
    m_sortByPath =
        setupOption(sortGroup, QIcon(), i18nc("@action:inmenu sorting option", "Document Path"), i18n("Sort by Document Path"), SLOT(slotSortPath()), false);
160

Alexander Lohnau's avatar
Alexander Lohnau committed
161
162
163
164
165
166
    m_sortByOpeningOrder = setupOption(sortGroup,
                                       QIcon(),
                                       i18nc("@action:inmenu sorting option", "Opening Order"),
                                       i18n("Sort by Opening Order"),
                                       SLOT(slotSortOpeningOrder()),
                                       false);
167

Laurent Montel's avatar
Laurent Montel committed
168
    m_resetHistory = new QAction(QIcon::fromTheme(QStringLiteral("edit-clear-history")), i18nc("@action:inmenu", "Clear History"), this);
169
    connect(m_resetHistory, &QAction::triggered, this, &KateFileTree::slotResetHistory);
Dominik Haumann's avatar
Dominik Haumann committed
170
    m_resetHistory->setWhatsThis(i18n("Clear edit/view history."));
171

Dominik Haumann's avatar
Dominik Haumann committed
172
173
174
175
    QPalette p = palette();
    p.setColor(QPalette::Inactive, QPalette::Highlight, p.color(QPalette::Active, QPalette::Highlight));
    p.setColor(QPalette::Inactive, QPalette::HighlightedText, p.color(QPalette::Active, QPalette::HighlightedText));
    setPalette(p);
176
177
178
}

KateFileTree::~KateFileTree()
179
180
{
}
181

182
183
void KateFileTree::setModel(QAbstractItemModel *model)
{
Yuri Chornoivan's avatar
Yuri Chornoivan committed
184
    Q_ASSERT(qobject_cast<KateFileTreeProxyModel *>(model)); // we don't really work with anything else
Dominik Haumann's avatar
Dominik Haumann committed
185
    QTreeView::setModel(model);
186
187
188
189
190
191
192
193
194
195
196
197

    header()->hide();
    header()->setStretchLastSection(false);
    header()->setSectionResizeMode(0, QHeaderView::Stretch);
    header()->setSectionResizeMode(1, QHeaderView::Fixed);
    header()->resizeSection(1, 16);
}

void KateFileTree::setShowCloseButton(bool show)
{
    m_hasCloseButton = show;
    static_cast<StyleDelegate *>(itemDelegate())->setShowCloseButton(show);
198
199
}

200
QAction *KateFileTree::setupOption(QActionGroup *group, const QIcon &icon, const QString &label, const QString &whatsThis, const char *slot, bool checked)
201
{
Dominik Haumann's avatar
Dominik Haumann committed
202
203
204
205
206
207
208
    QAction *new_action = new QAction(icon, label, this);
    new_action->setWhatsThis(whatsThis);
    new_action->setActionGroup(group);
    new_action->setCheckable(true);
    new_action->setChecked(checked);
    connect(new_action, SIGNAL(triggered()), this, slot);
    return new_action;
209
210
211
212
}

void KateFileTree::slotListMode()
{
Christoph Cullmann's avatar
Christoph Cullmann committed
213
    Q_EMIT viewModeChanged(true);
214
215
216
217
}

void KateFileTree::slotTreeMode()
{
Christoph Cullmann's avatar
Christoph Cullmann committed
218
    Q_EMIT viewModeChanged(false);
219
220
221
222
}

void KateFileTree::slotSortName()
{
Christoph Cullmann's avatar
Christoph Cullmann committed
223
    Q_EMIT sortRoleChanged(Qt::DisplayRole);
224
225
226
227
}

void KateFileTree::slotSortPath()
{
Christoph Cullmann's avatar
Christoph Cullmann committed
228
    Q_EMIT sortRoleChanged(KateFileTreeModel::PathRole);
229
230
231
232
}

void KateFileTree::slotSortOpeningOrder()
{
Christoph Cullmann's avatar
Christoph Cullmann committed
233
    Q_EMIT sortRoleChanged(KateFileTreeModel::OpeningOrderRole);
234
235
}

Dominik Haumann's avatar
Dominik Haumann committed
236
void KateFileTree::slotCurrentChanged(const QModelIndex &current, const QModelIndex &previous)
237
{
Dominik Haumann's avatar
Dominik Haumann committed
238
239
240
241
242
243
244
245
246
    Q_UNUSED(previous);
    if (!current.isValid()) {
        return;
    }

    KTextEditor::Document *doc = model()->data(current, KateFileTreeModel::DocumentRole).value<KTextEditor::Document *>();
    if (doc) {
        m_previouslySelected = current;
    }
247
248
}

Dominik Haumann's avatar
Dominik Haumann committed
249
void KateFileTree::mouseClicked(const QModelIndex &index)
250
{
251
    if (auto doc = model()->data(index, KateFileTreeModel::DocumentRole).value<KTextEditor::Document *>()) {
252
253
254
255
        if (m_hasCloseButton && index.column() == 1) {
            KTextEditor::Editor::instance()->application()->closeDocuments({doc});
            return;
        }
Christoph Cullmann's avatar
Christoph Cullmann committed
256
        Q_EMIT activateDocument(doc);
Dominik Haumann's avatar
Dominik Haumann committed
257
    }
258
259
}

Dominik Haumann's avatar
Dominik Haumann committed
260
void KateFileTree::contextMenuEvent(QContextMenuEvent *event)
261
{
Dominik Haumann's avatar
Dominik Haumann committed
262
    m_indexContextMenu = selectionModel()->currentIndex();
263

264
    selectionModel()->setCurrentIndex(m_indexContextMenu, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
265

Dominik Haumann's avatar
Dominik Haumann committed
266
267
    KateFileTreeProxyModel *ftpm = static_cast<KateFileTreeProxyModel *>(model());
    KateFileTreeModel *ftm = static_cast<KateFileTreeModel *>(ftpm->sourceModel());
268

Dominik Haumann's avatar
Dominik Haumann committed
269
270
271
    bool listMode = ftm->listMode();
    m_treeModeAction->setChecked(!listMode);
    m_listModeAction->setChecked(listMode);
272

Dominik Haumann's avatar
Dominik Haumann committed
273
274
275
276
    int sortRole = ftpm->sortRole();
    m_sortByFile->setChecked(sortRole == Qt::DisplayRole);
    m_sortByPath->setChecked(sortRole == KateFileTreeModel::PathRole);
    m_sortByOpeningOrder->setChecked(sortRole == KateFileTreeModel::OpeningOrderRole);
277

Dominik Haumann's avatar
Dominik Haumann committed
278
    KTextEditor::Document *doc = m_indexContextMenu.data(KateFileTreeModel::DocumentRole).value<KTextEditor::Document *>();
279
    const bool isFile = (nullptr != doc);
280

Dominik Haumann's avatar
Dominik Haumann committed
281
282
283
    QMenu menu;
    menu.addAction(m_filelistReloadDocument);
    menu.addAction(m_filelistCloseDocument);
284
285
    menu.addAction(m_filelistExpandRecursive);
    menu.addAction(m_filelistCollapseRecursive);
286

Dominik Haumann's avatar
Dominik Haumann committed
287
288
289
    if (isFile) {
        menu.addAction(m_filelistCloseOtherDocument);
        menu.addSeparator();
290
        menu.addAction(m_filelistOpenContainingFolder);
Dominik Haumann's avatar
Dominik Haumann committed
291
        menu.addAction(m_filelistCopyFilename);
292
        menu.addAction(m_filelistRenameFile);
Dominik Haumann's avatar
Dominik Haumann committed
293
294
295
        menu.addAction(m_filelistPrintDocument);
        menu.addAction(m_filelistPrintDocumentPreview);
        QMenu *openWithMenu = menu.addMenu(i18nc("@action:inmenu", "Open With"));
296
297
        connect(openWithMenu, &QMenu::aboutToShow, this, &KateFileTree::slotFixOpenWithMenu);
        connect(openWithMenu, &QMenu::triggered, this, &KateFileTree::slotOpenWithMenuAction);
298

299
        const bool hasFileName = doc->url().isValid();
300
        m_filelistOpenContainingFolder->setEnabled(hasFileName);
301
302
303
        m_filelistCopyFilename->setEnabled(hasFileName);
        m_filelistRenameFile->setEnabled(hasFileName);
        m_filelistDeleteDocument->setEnabled(hasFileName);
Dominik Haumann's avatar
Dominik Haumann committed
304
305
        menu.addAction(m_filelistDeleteDocument);
    }
306

Dominik Haumann's avatar
Dominik Haumann committed
307
308
309
310
    menu.addSeparator();
    QMenu *view_menu = menu.addMenu(i18nc("@action:inmenu", "View Mode"));
    view_menu->addAction(m_treeModeAction);
    view_menu->addAction(m_listModeAction);
311

312
    QMenu *sort_menu = menu.addMenu(QIcon::fromTheme(QStringLiteral("view-sort")), i18nc("@action:inmenu", "Sort By"));
Dominik Haumann's avatar
Dominik Haumann committed
313
314
315
    sort_menu->addAction(m_sortByFile);
    sort_menu->addAction(m_sortByPath);
    sort_menu->addAction(m_sortByOpeningOrder);
316

Dominik Haumann's avatar
Dominik Haumann committed
317
    menu.addAction(m_resetHistory);
318

Dominik Haumann's avatar
Dominik Haumann committed
319
    menu.exec(viewport()->mapToGlobal(event->pos()));
320

Dominik Haumann's avatar
Dominik Haumann committed
321
    if (m_previouslySelected.isValid()) {
322
        selectionModel()->setCurrentIndex(m_previouslySelected, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
Dominik Haumann's avatar
Dominik Haumann committed
323
    }
324

Dominik Haumann's avatar
Dominik Haumann committed
325
    event->accept();
326
327
}

328
329
void KateFileTree::slotFixOpenWithMenu()
{
330
    QMenu *menu = static_cast<QMenu *>(sender());
Dominik Haumann's avatar
Dominik Haumann committed
331
332
333
334
335
336
337
338
339
340
341
    menu->clear();

    KTextEditor::Document *doc = model()->data(m_indexContextMenu, KateFileTreeModel::DocumentRole).value<KTextEditor::Document *>();
    if (!doc) {
        return;
    }

    // get a list of appropriate services.
    QMimeDatabase db;
    QMimeType mime = db.mimeTypeForName(doc->mimeType());

342
    QAction *a = nullptr;
Nicolas Fella's avatar
Nicolas Fella committed
343
    const KService::List offers = KApplicationTrader::queryByMimeType(mime.name());
Dominik Haumann's avatar
Dominik Haumann committed
344
    // for each one, insert a menu item...
345
    for (const auto &service : offers) {
Dominik Haumann's avatar
Dominik Haumann committed
346
347
348
349
350
351
352
353
354
        if (service->name() == QLatin1String("Kate")) {
            continue;
        }
        a = menu->addAction(QIcon::fromTheme(service->icon()), service->name());
        a->setData(service->entryPath());
    }
    // append "Other..." to call the KDE "open with" dialog.
    a = menu->addAction(i18n("&Other..."));
    a->setData(QString());
355
356
}

Dominik Haumann's avatar
Dominik Haumann committed
357
void KateFileTree::slotOpenWithMenuAction(QAction *a)
358
{
Dominik Haumann's avatar
Dominik Haumann committed
359
360
361
362
363
    KTextEditor::Document *doc = model()->data(m_indexContextMenu, KateFileTreeModel::DocumentRole).value<KTextEditor::Document *>();
    if (!doc) {
        return;
    }

Ahmad Samir's avatar
Ahmad Samir committed
364
    const QList<QUrl> list({doc->url()});
Dominik Haumann's avatar
Dominik Haumann committed
365

Ahmad Samir's avatar
Ahmad Samir committed
366
367
368
369
370
371
    KService::Ptr app = KService::serviceByDesktopPath(a->data().toString());
    // If app is null, ApplicationLauncherJob will invoke the open-with dialog
    auto *job = new KIO::ApplicationLauncherJob(app);
    job->setUrls(list);
    job->setUiDelegate(new KIO::JobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
    job->start();
372
373
}

Christoph Cullmann's avatar
Christoph Cullmann committed
374
Q_DECLARE_METATYPE(QList<KTextEditor::Document *>)
375

376
377
void KateFileTree::slotDocumentClose()
{
Dominik Haumann's avatar
Dominik Haumann committed
378
379
380
381
382
    m_previouslySelected = QModelIndex();
    QVariant v = m_indexContextMenu.data(KateFileTreeModel::DocumentTreeRole);
    if (!v.isValid()) {
        return;
    }
383
    QList<KTextEditor::Document *> closingDocuments = v.value<QList<KTextEditor::Document *>>();
Dominik Haumann's avatar
Dominik Haumann committed
384
    KTextEditor::Editor::instance()->application()->closeDocuments(closingDocuments);
385
386
}

387
388
void KateFileTree::slotExpandRecursive()
{
389
    if (!m_indexContextMenu.isValid()) {
390
391
392
393
        return;
    }

    // Work list for DFS walk over sub tree
394
    QList<QPersistentModelIndex> worklist = {m_indexContextMenu};
395

396
    while (!worklist.isEmpty()) {
397
398
399
400
401
402
        QPersistentModelIndex index = worklist.takeLast();

        // Expand current item
        expand(index);

        // Append all children of current item
403
        for (int i = 0; i < model()->rowCount(index); ++i) {
404
            worklist.append(model()->index(i, 0, index));
405
406
407
408
409
410
        }
    }
}

void KateFileTree::slotCollapseRecursive()
{
411
    if (!m_indexContextMenu.isValid()) {
412
413
414
415
        return;
    }

    // Work list for DFS walk over sub tree
416
    QList<QPersistentModelIndex> worklist = {m_indexContextMenu};
417

418
    while (!worklist.isEmpty()) {
419
420
421
422
423
424
        QPersistentModelIndex index = worklist.takeLast();

        // Expand current item
        collapse(index);

        // Prepend all children of current item
425
        for (int i = 0; i < model()->rowCount(index); ++i) {
426
            worklist.append(model()->index(i, 0, index));
427
428
429
430
        }
    }
}

431
432
void KateFileTree::slotDocumentCloseOther()
{
Dominik Haumann's avatar
Dominik Haumann committed
433
434
435
436
    QVariant v = model()->data(m_indexContextMenu.parent(), KateFileTreeModel::DocumentTreeRole);
    if (!v.isValid()) {
        return;
    }
437

438
    QList<KTextEditor::Document *> closingDocuments = v.value<QList<KTextEditor::Document *>>();
Dominik Haumann's avatar
Dominik Haumann committed
439
    KTextEditor::Document *doc = model()->data(m_indexContextMenu, KateFileTreeModel::DocumentRole).value<KTextEditor::Document *>();
440

Dominik Haumann's avatar
Dominik Haumann committed
441
    closingDocuments.removeOne(doc);
442

Dominik Haumann's avatar
Dominik Haumann committed
443
    KTextEditor::Editor::instance()->application()->closeDocuments(closingDocuments);
444
445
446
447
}

void KateFileTree::slotDocumentReload()
{
Dominik Haumann's avatar
Dominik Haumann committed
448
449
450
451
452
    QVariant v = m_indexContextMenu.data(KateFileTreeModel::DocumentTreeRole);
    if (!v.isValid()) {
        return;
    }

453
454
    const QList<KTextEditor::Document *> docs = v.value<QList<KTextEditor::Document *>>();
    for (KTextEditor::Document *doc : docs) {
Dominik Haumann's avatar
Dominik Haumann committed
455
456
        doc->documentReload();
    }
457
458
}

459
460
461
462
463
464
465
466
void KateFileTree::slotOpenContainingFolder()
{
    KTextEditor::Document *doc = model()->data(m_indexContextMenu, KateFileTreeModel::DocumentRole).value<KTextEditor::Document *>();
    if (doc) {
        KIO::highlightInFileManager({doc->url()});
    }
}

467
468
void KateFileTree::slotCopyFilename()
{
Dominik Haumann's avatar
Dominik Haumann committed
469
    KTextEditor::Document *doc = model()->data(m_indexContextMenu, KateFileTreeModel::DocumentRole).value<KTextEditor::Document *>();
470
471
472
473

    // TODO: the following code was improved in kate/katefileactions.cpp and should be reused here
    //       (make sure that the mentioned bug 381052 does not reappear)

Dominik Haumann's avatar
Dominik Haumann committed
474
    if (doc) {
475
476
477
478
479
480
        // ensure we prefer native separators, bug 381052
        if (doc->url().isLocalFile()) {
            QApplication::clipboard()->setText(QDir::toNativeSeparators(doc->url().toLocalFile()));
        } else {
            QApplication::clipboard()->setText(doc->url().url());
        }
Dominik Haumann's avatar
Dominik Haumann committed
481
    }
482
483
}

484
485
void KateFileTree::slotRenameFile()
{
486
    KTextEditor::Document *doc = model()->data(m_indexContextMenu, KateFileTreeModel::DocumentRole).value<KTextEditor::Document *>();
487
488
489

    // TODO: the following code was improved in kate/katefileactions.cpp and should be reused here

490
491
492
493
494
495
496
    if (!doc) {
        return;
    }

    const QUrl oldFileUrl = doc->url();
    const QString oldFileName = doc->url().fileName();
    bool ok;
497

498
    QString newFileName = QInputDialog::getText(this, i18n("Rename file"), i18n("New file name"), QLineEdit::Normal, oldFileName, &ok);
499
500
501
502
503
    if (!ok) {
        return;
    }

    QUrl newFileUrl = oldFileUrl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash);
504
    newFileUrl.setPath(newFileUrl.path() + QLatin1Char('/') + newFileName);
505
506
507
508
509
510
511
512
513
514
515

    if (!newFileUrl.isValid()) {
        return;
    }

    if (!doc->closeUrl()) {
        return;
    }

    doc->waitSaveComplete();

516
    KIO::CopyJob *job = KIO::move(oldFileUrl, newFileUrl);
517
    QSharedPointer<QMetaObject::Connection> sc(new QMetaObject::Connection());
518
519
520
521
522
    auto success = [doc, sc](KIO::Job *, const QUrl &, const QUrl &realNewFileUrl, const QDateTime &, bool, bool) {
        doc->openUrl(realNewFileUrl);
        doc->documentSavedOrUploaded(doc, true);
        QObject::disconnect(*sc);
    };
523
524
525
526
527
    *sc = connect(job, &KIO::CopyJob::copyingDone, doc, success);

    if (!job->exec()) {
        KMessageBox::sorry(this, i18n("File \"%1\" could not be moved to \"%2\"", oldFileUrl.toDisplayString(), newFileUrl.toDisplayString()));
        doc->openUrl(oldFileUrl);
528
529
    }
}
530

531
532
void KateFileTree::slotDocumentFirst()
{
533
    KTextEditor::Document *doc = model()->data(model()->index(0, 0), KateFileTreeModel::DocumentRole).value<KTextEditor::Document *>();
Dominik Haumann's avatar
Dominik Haumann committed
534
    if (doc) {
Christoph Cullmann's avatar
Christoph Cullmann committed
535
        Q_EMIT activateDocument(doc);
Dominik Haumann's avatar
Dominik Haumann committed
536
    }
537
538
539
540
}

void KateFileTree::slotDocumentLast()
{
Dominik Haumann's avatar
Dominik Haumann committed
541
    int count = model()->rowCount(model()->parent(currentIndex()));
542
    KTextEditor::Document *doc = model()->data(model()->index(count - 1, 0), KateFileTreeModel::DocumentRole).value<KTextEditor::Document *>();
Dominik Haumann's avatar
Dominik Haumann committed
543
    if (doc) {
Christoph Cullmann's avatar
Christoph Cullmann committed
544
        Q_EMIT activateDocument(doc);
Dominik Haumann's avatar
Dominik Haumann committed
545
    }
546
547
}

548
549
void KateFileTree::slotDocumentPrev()
{
Dominik Haumann's avatar
Dominik Haumann committed
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
    KateFileTreeProxyModel *ftpm = static_cast<KateFileTreeProxyModel *>(model());

    QModelIndex current_index = currentIndex();
    QModelIndex prev;

    // scan up the tree skipping any dir nodes
    while (current_index.isValid()) {
        if (current_index.row() > 0) {
            current_index = ftpm->sibling(current_index.row() - 1, current_index.column(), current_index);
            if (!current_index.isValid()) {
                break;
            }

            if (ftpm->isDir(current_index)) {
                // try and select the last child in this parent
                int children = ftpm->rowCount(current_index);
                current_index = ftpm->index(children - 1, 0, current_index);
                if (ftpm->isDir(current_index)) {
                    // since we're a dir, keep going
                    while (ftpm->isDir(current_index)) {
                        children = ftpm->rowCount(current_index);
                        current_index = ftpm->index(children - 1, 0, current_index);
                    }

                    if (!ftpm->isDir(current_index)) {
                        prev = current_index;
                        break;
                    }

                    continue;
                } else {
                    // we're the previous file, set prev
                    prev = current_index;
                    break;
                }
            } else { // found document item
                prev = current_index;
                break;
            }
589
        } else {
Dominik Haumann's avatar
Dominik Haumann committed
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
            // just select the parent, the logic above will handle the rest
            current_index = ftpm->parent(current_index);
            if (!current_index.isValid()) {
                // paste the root node here, try and wrap around

                int children = ftpm->rowCount(current_index);
                QModelIndex last_index = ftpm->index(children - 1, 0, current_index);
                if (!last_index.isValid()) {
                    break;
                }

                if (ftpm->isDir(last_index)) {
                    // last node is a dir, select last child row
                    int last_children = ftpm->rowCount(last_index);
                    prev = ftpm->index(last_children - 1, 0, last_index);
                    // bug here?
                    break;
                } else {
                    // got last file node
                    prev = last_index;
                    break;
                }
            }
613
614
615
        }
    }

Dominik Haumann's avatar
Dominik Haumann committed
616
617
    if (prev.isValid()) {
        KTextEditor::Document *doc = model()->data(prev, KateFileTreeModel::DocumentRole).value<KTextEditor::Document *>();
Christoph Cullmann's avatar
Christoph Cullmann committed
618
        Q_EMIT activateDocument(doc);
Dominik Haumann's avatar
Dominik Haumann committed
619
    }
620
621
622
623
}

void KateFileTree::slotDocumentNext()
{
Dominik Haumann's avatar
Dominik Haumann committed
624
625
626
627
628
629
630
631
632
633
634
635
    KateFileTreeProxyModel *ftpm = static_cast<KateFileTreeProxyModel *>(model());

    QModelIndex current_index = currentIndex();
    int parent_row_count = ftpm->rowCount(ftpm->parent(current_index));
    QModelIndex next;

    // scan down the tree skipping any dir nodes
    while (current_index.isValid()) {
        if (current_index.row() < parent_row_count - 1) {
            current_index = ftpm->sibling(current_index.row() + 1, current_index.column(), current_index);
            if (!current_index.isValid()) {
                break;
636
            }
637

Dominik Haumann's avatar
Dominik Haumann committed
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
            if (ftpm->isDir(current_index)) {
                // we have a dir node
                while (ftpm->isDir(current_index)) {
                    current_index = ftpm->index(0, 0, current_index);
                }

                parent_row_count = ftpm->rowCount(ftpm->parent(current_index));

                if (!ftpm->isDir(current_index)) {
                    next = current_index;
                    break;
                }
            } else { // found document item
                next = current_index;
                break;
            }
        } else {
            // select the parent's next sibling
            QModelIndex parent_index = ftpm->parent(current_index);
            int grandparent_row_count = ftpm->rowCount(ftpm->parent(parent_index));

            current_index = parent_index;
            parent_row_count = grandparent_row_count;

            // at least if we're not past the last node
            if (!current_index.isValid()) {
                // paste the root node here, try and wrap around
                QModelIndex last_index = ftpm->index(0, 0, QModelIndex());
                if (!last_index.isValid()) {
                    break;
                }

                if (ftpm->isDir(last_index)) {
                    // last node is a dir, select first child row
                    while (ftpm->isDir(last_index)) {
                        if (ftpm->rowCount(last_index)) {
                            // has children, select first
                            last_index = ftpm->index(0, 0, last_index);
                        }
                    }

                    next = last_index;
                    break;
                } else {
                    // got first file node
                    next = last_index;
                    break;
                }
            }
687
688
689
        }
    }

Dominik Haumann's avatar
Dominik Haumann committed
690
691
    if (next.isValid()) {
        KTextEditor::Document *doc = model()->data(next, KateFileTreeModel::DocumentRole).value<KTextEditor::Document *>();
Christoph Cullmann's avatar
Christoph Cullmann committed
692
        Q_EMIT activateDocument(doc);
Dominik Haumann's avatar
Dominik Haumann committed
693
    }
694
}
695
696
697

void KateFileTree::slotPrintDocument()
{
Dominik Haumann's avatar
Dominik Haumann committed
698
    KTextEditor::Document *doc = model()->data(m_indexContextMenu, KateFileTreeModel::DocumentRole).value<KTextEditor::Document *>();
699

Dominik Haumann's avatar
Dominik Haumann committed
700
701
702
    if (!doc) {
        return;
    }
703

Dominik Haumann's avatar
Dominik Haumann committed
704
    doc->print();
705
706
707
708
}

void KateFileTree::slotPrintDocumentPreview()
{
Dominik Haumann's avatar
Dominik Haumann committed
709
    KTextEditor::Document *doc = model()->data(m_indexContextMenu, KateFileTreeModel::DocumentRole).value<KTextEditor::Document *>();
710

Dominik Haumann's avatar
Dominik Haumann committed
711
712
713
    if (!doc) {
        return;
    }
714

Dominik Haumann's avatar
Dominik Haumann committed
715
    doc->printPreview();
716
}
717
718
719

void KateFileTree::slotResetHistory()
{
Dominik Haumann's avatar
Dominik Haumann committed
720
721
722
    KateFileTreeProxyModel *ftpm = static_cast<KateFileTreeProxyModel *>(model());
    KateFileTreeModel *ftm = static_cast<KateFileTreeModel *>(ftpm->sourceModel());
    ftm->resetHistory();
723
724
}

725
726
void KateFileTree::slotDocumentDelete()
{
Dominik Haumann's avatar
Dominik Haumann committed
727
    KTextEditor::Document *doc = model()->data(m_indexContextMenu, KateFileTreeModel::DocumentRole).value<KTextEditor::Document *>();
728

729
730
    // TODO: the following code was improved in kate/katefileactions.cpp and should be reused here

Dominik Haumann's avatar
Dominik Haumann committed
731
732
733
    if (!doc) {
        return;
    }
734

Dominik Haumann's avatar
Dominik Haumann committed
735
    QUrl url = doc->url();
736

Alexander Lohnau's avatar
Alexander Lohnau committed
737
738
739
740
741
742
743
    bool go = (KMessageBox::warningContinueCancel(this,
                                                  i18n("Do you really want to delete file \"%1\" from storage?", url.toDisplayString()),
                                                  i18n("Delete file?"),
                                                  KStandardGuiItem::yes(),
                                                  KStandardGuiItem::no(),
                                                  QStringLiteral("filetreedeletefile"))
               == KMessageBox::Continue);
744

Dominik Haumann's avatar
Dominik Haumann committed
745
746
747
    if (!go) {
        return;
    }
748

Dominik Haumann's avatar
Dominik Haumann committed
749
750
751
    if (!KTextEditor::Editor::instance()->application()->closeDocument(doc)) {
        return; // no extra message, the internals of ktexteditor should take care of that.
    }
752

Dominik Haumann's avatar
Dominik Haumann committed
753
754
755
756
757
    if (url.isValid()) {
        KIO::DeleteJob *job = KIO::del(url);
        if (!job->exec()) {
            KMessageBox::sorry(this, i18n("File \"%1\" could not be deleted.", url.toDisplayString()));
        }
758
759
760
    }
}

761
// END KateFileTree