1
/* This file is part of the KDE project
2
Copyright (C) 2010 Thomas Fjellstrom <thomas@fjellstrom.ca>
4
This library is free software; you can redistribute it and/or
5
modify it under the terms of the GNU Library General Public
6
License as published by the Free Software Foundation; either
7
version 2 of the License, or (at your option) any later version.
9
This library is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
Library General Public License for more details.
14
You should have received a copy of the GNU Library General Public License
15
along with this library; see the file COPYING.LIB. If not, write to
16
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
Boston, MA 02110-1301, USA.
20
#include "katefiletreemodel.h"
25
#include <QMimeDatabase>
29
#include <KColorScheme>
30
#include <KColorUtils>
31
#include <KLocalizedString>
32
#include <kiconutils.h>
34
#include <ktexteditor/document.h>
35
#include <ktexteditor/editor.h>
36
#include <ktexteditor/application.h>
38
#include "katefiletreedebug.h"
43
friend class KateFileTreeModel;
46
enum Flag { None = 0, Dir = 1, Modified = 2, ModifiedExternally = 4, DeletedExternally = 8, Empty = 16, ShowFullPath = 32, Host = 64 };
47
Q_DECLARE_FLAGS(Flags, Flag)
49
ProxyItem(const QString &n, ProxyItemDir *p = 0, Flags f = ProxyItem::None);
52
int addChild(ProxyItem *p);
53
void remChild(ProxyItem *p);
55
ProxyItemDir *parent() const;
57
ProxyItem *child(int idx) const;
58
int childCount() const;
62
const QString &display() const;
63
const QString &documentName() const;
65
const QString &path() const;
66
void setPath(const QString &str);
68
void setHost(const QString &host);
69
const QString &host() const;
71
void setIcon(const QIcon &i);
72
const QIcon &icon() const;
74
const QList<ProxyItem *> &children() const;
75
QList<ProxyItem *> &children();
77
void setDoc(KTextEditor::Document *doc);
78
KTextEditor::Document *doc() const;
81
* the view usess this to close all the documents under the folder
82
* @returns list of all the (nested) documents under this node
84
QList<KTextEditor::Document *> docTree() const;
86
void setFlags(Flags flags);
87
void setFlag(Flag flag);
88
void clearFlag(Flag flag);
89
bool flag(Flag flag) const;
93
QString m_documentName;
94
ProxyItemDir *m_parent;
95
QList<ProxyItem *> m_children;
101
KTextEditor::Document *m_doc;
105
void updateDisplay();
106
void updateDocumentName();
109
QDebug operator<<(QDebug dbg, ProxyItem *item)
112
dbg.nospace() << "ProxyItem(0x0) ";
113
return dbg.maybeSpace();
116
const void *parent = static_cast<void *>(item->parent());
118
dbg.nospace() << "ProxyItem(" << (void *)item << ",";
119
dbg.nospace() << parent << "," << item->row() << ",";
120
dbg.nospace() << item->doc() << "," << item->path() << ") ";
121
return dbg.maybeSpace();
124
class ProxyItemDir : public ProxyItem
127
ProxyItemDir(QString n, ProxyItemDir *p = 0) : ProxyItem(n, p) {
128
setFlag(ProxyItem::Dir);
131
setIcon(QIcon::fromTheme(QLatin1String("folder")));
135
QDebug operator<<(QDebug dbg, ProxyItemDir *item)
138
dbg.nospace() << "ProxyItemDir(0x0) ";
139
return dbg.maybeSpace();
142
const void *parent = static_cast<void *>(item->parent());
144
dbg.nospace() << "ProxyItemDir(" << (void *)item << ",";
145
dbg.nospace() << parent << "," << item->row() << ",";
146
dbg.nospace() << item->path() << ", children:" << item->childCount() << ") ";
147
return dbg.maybeSpace();
150
Q_DECLARE_OPERATORS_FOR_FLAGS(ProxyItem::Flags)
153
ProxyItem::ProxyItem(const QString &d, ProxyItemDir *p, ProxyItem::Flags f)
154
: m_path(d), m_parent(p), m_row(-1), m_flags(f), m_doc(0)
163
ProxyItem::~ProxyItem()
165
qDeleteAll(m_children);
168
void ProxyItem::updateDisplay()
170
// triggers only if this is a top level node and the root has the show full path flag set.
171
if (flag(ProxyItem::Dir) && m_parent && !m_parent->m_parent && m_parent->flag(ProxyItem::ShowFullPath)) {
173
if (m_display.startsWith(QDir::homePath())) {
174
m_display.replace(0, QDir::homePath().length(), QLatin1String("~"));
177
m_display = m_path.section(QLatin1Char('/'), -1, -1);
178
if (flag(ProxyItem::Host) && (!m_parent || (m_parent && !m_parent->m_parent))) {
179
const QString hostPrefix = QString::fromLatin1("[%1]").arg(host());
180
if (hostPrefix != m_display) {
181
m_display = hostPrefix + m_display;
187
int ProxyItem::addChild(ProxyItem *item)
189
const int item_row = m_children.count();
190
item->m_row = item_row;
191
m_children.append(item);
192
item->m_parent = static_cast<ProxyItemDir *>(this);
194
item->updateDisplay();
199
void ProxyItem::remChild(ProxyItem *item)
201
const int idx = m_children.indexOf(item);
204
m_children.removeAt(idx);
206
for (int i = idx; i < m_children.count(); i++) {
207
m_children[i]->m_row = i;
213
ProxyItemDir *ProxyItem::parent() const
218
ProxyItem *ProxyItem::child(int idx) const
220
return (idx < 0 || idx >= m_children.count()) ? 0 : m_children[idx];
223
int ProxyItem::childCount() const
225
return m_children.count();
228
int ProxyItem::row() const
233
const QIcon &ProxyItem::icon() const
238
void ProxyItem::setIcon(const QIcon &i)
243
const QString &ProxyItem::documentName() const
245
return m_documentName;
248
const QString &ProxyItem::display() const
253
const QString &ProxyItem::path() const
258
void ProxyItem::setPath(const QString &p)
264
const QList<ProxyItem *> &ProxyItem::children() const
269
QList<ProxyItem *> &ProxyItem::children()
274
void ProxyItem::setDoc(KTextEditor::Document *doc)
278
updateDocumentName();
281
KTextEditor::Document *ProxyItem::doc() const
286
QList<KTextEditor::Document *> ProxyItem::docTree() const
288
QList<KTextEditor::Document *> result;
291
result.append(m_doc);
295
foreach(const ProxyItem * item, m_children) {
296
result.append(item->docTree());
302
bool ProxyItem::flag(Flag f) const
307
void ProxyItem::setFlag(Flag f)
312
void ProxyItem::setFlags(Flags f)
317
void ProxyItem::clearFlag(Flag f)
322
void ProxyItem::setHost(const QString &host)
326
if (host.isEmpty()) {
332
updateDocumentName();
336
const QString &ProxyItem::host() const
341
void ProxyItem::updateDocumentName()
343
const QString docName = m_doc ? m_doc->documentName() : QString();
345
if (flag(ProxyItem::Host)) {
346
m_documentName = QString::fromLatin1("[%1]%2").arg(m_host).arg(docName);
348
m_documentName = docName;
354
KateFileTreeModel::KateFileTreeModel(QObject *p)
355
: QAbstractItemModel(p)
356
, m_root(new ProxyItemDir(QLatin1String("m_root"), 0))
358
// setup default settings
359
// session init will set these all soon
360
const KColorScheme colors(QPalette::Active);
361
const QColor bg = colors.background().color();
362
m_editShade = KColorUtils::tint(bg, colors.foreground(KColorScheme::ActiveText).color(), 0.5);
363
m_viewShade = KColorUtils::tint(bg, colors.foreground(KColorScheme::VisitedText).color(), 0.5);
364
m_shadingEnabled = true;
370
KateFileTreeModel::~KateFileTreeModel()
375
bool KateFileTreeModel::shadingEnabled() const
377
return m_shadingEnabled;
380
void KateFileTreeModel::setShadingEnabled(bool se)
382
if (m_shadingEnabled != se) {
383
updateBackgrounds(true);
384
m_shadingEnabled = se;
388
const QColor &KateFileTreeModel::editShade() const
393
void KateFileTreeModel::setEditShade(const QColor &es)
398
const QColor &KateFileTreeModel::viewShade() const
403
void KateFileTreeModel::setViewShade(const QColor &vs)
408
bool KateFileTreeModel::showFullPathOnRoots(void) const
410
return m_root->flag(ProxyItem::ShowFullPath);
413
void KateFileTreeModel::setShowFullPathOnRoots(bool s)
416
m_root->setFlag(ProxyItem::ShowFullPath);
418
m_root->clearFlag(ProxyItem::ShowFullPath);
421
foreach(ProxyItem * root, m_root->children()) {
422
root->updateDisplay();
426
void KateFileTreeModel::initModel()
428
// add already existing documents
429
foreach(KTextEditor::Document * doc, KTextEditor::Editor::instance()->application()->documents()) {
434
void KateFileTreeModel::clearModel()
437
// can safely ignore documentClosed here
439
beginRemoveRows(QModelIndex(), 0, qMax(m_root->childCount() - 1, 0));
442
m_root = new ProxyItemDir(QLatin1String("m_root"), 0);
445
m_viewHistory.clear();
446
m_editHistory.clear();
452
void KateFileTreeModel::connectDocument(const KTextEditor::Document *doc)
454
connect(doc, SIGNAL(documentNameChanged(KTextEditor::Document *)), this, SLOT(documentNameChanged(KTextEditor::Document *)));
455
connect(doc, SIGNAL(documentUrlChanged(KTextEditor::Document *)), this, SLOT(documentNameChanged(KTextEditor::Document *)));
456
connect(doc, SIGNAL(modifiedChanged(KTextEditor::Document *)), this, SLOT(documentModifiedChanged(KTextEditor::Document *)));
457
connect(doc, SIGNAL(modifiedOnDisk(KTextEditor::Document *, bool, KTextEditor::ModificationInterface::ModifiedOnDiskReason)),
458
this, SLOT(documentModifiedOnDisc(KTextEditor::Document *, bool, KTextEditor::ModificationInterface::ModifiedOnDiskReason)));
461
QModelIndex KateFileTreeModel::docIndex(const KTextEditor::Document *doc) const
463
if (!m_docmap.contains(doc)) {
464
return QModelIndex();
467
ProxyItem *item = m_docmap[doc];
469
return createIndex(item->row(), 0, item);
472
Qt::ItemFlags KateFileTreeModel::flags(const QModelIndex &index) const
474
Qt::ItemFlags flags = Qt::ItemIsEnabled;
476
if (!index.isValid()) {
480
const ProxyItem *item = static_cast<ProxyItem *>(index.internalPointer());
481
if (item && !item->childCount()) {
482
flags |= Qt::ItemIsSelectable;
488
Q_DECLARE_METATYPE(QList<KTextEditor::Document *>)
490
QVariant KateFileTreeModel::data(const QModelIndex &index, int role) const
492
if (!index.isValid()) {
496
ProxyItem *item = static_cast<ProxyItem *>(index.internalPointer());
502
case KateFileTreeModel::PathRole:
503
// allow to sort with hostname + path, bug 271488
504
return (item->doc() && !item->doc()->url().isEmpty()) ? item->doc()->url().toString() : item->path();
506
case KateFileTreeModel::DocumentRole:
507
return QVariant::fromValue(item->doc());
509
case KateFileTreeModel::OpeningOrderRole:
512
case KateFileTreeModel::DocumentTreeRole:
513
return QVariant::fromValue(item->docTree());
515
case Qt::DisplayRole:
516
// in list mode we want to use kate's fancy names.
518
return item->documentName();
520
return item->display();
523
case Qt::DecorationRole:
526
case Qt::ToolTipRole: {
527
QString tooltip = item->path();
528
if (item->flag(ProxyItem::DeletedExternally) || item->flag(ProxyItem::ModifiedExternally)) {
529
tooltip = i18nc("%1 is the full path", "<p><b>%1</b></p><p>The document has been modified by another application.</p>").arg(item->path());
535
case Qt::ForegroundRole: {
536
const KColorScheme colors(QPalette::Active);
537
if (!item->flag(ProxyItem::Dir) && (!item->doc() || item->doc()->openingError())) {
538
return colors.foreground(KColorScheme::InactiveText).color();
543
case Qt::BackgroundRole:
544
// TODO: do that funky shading the file list does...
545
if (m_shadingEnabled && m_brushes.contains(item)) {
546
return m_brushes[item];
554
QVariant KateFileTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
556
Q_UNUSED(orientation);
560
return QLatin1String("name");
566
int KateFileTreeModel::rowCount(const QModelIndex &parent) const
568
if (!parent.isValid()) {
569
return m_root->childCount();
572
const ProxyItem *item = static_cast<ProxyItem *>(parent.internalPointer());
577
return item->childCount();
580
int KateFileTreeModel::columnCount(const QModelIndex &parent) const
586
QModelIndex KateFileTreeModel::parent(const QModelIndex &index) const
588
if (!index.isValid()) {
589
return QModelIndex();
592
const ProxyItem *item = static_cast<ProxyItem *>(index.internalPointer());
594
return QModelIndex();
597
if (!item->parent()) {
598
return QModelIndex();
601
if (item->parent() == m_root) {
602
return QModelIndex();
605
return createIndex(item->parent()->row(), 0, item->parent());
608
QModelIndex KateFileTreeModel::index(int row, int column, const QModelIndex &parent) const
610
const ProxyItem *p = 0;
612
return QModelIndex();
615
if (!parent.isValid()) {
618
p = static_cast<ProxyItem *>(parent.internalPointer());
622
return QModelIndex();
625
if (row < 0 || row >= p->childCount()) {
626
return QModelIndex();
629
return createIndex(row, 0, p->child(row));
632
bool KateFileTreeModel::hasChildren(const QModelIndex &parent) const
634
if (!parent.isValid()) {
635
return m_root->childCount() > 0;
638
const ProxyItem *item = static_cast<ProxyItem *>(parent.internalPointer());
643
return item->childCount() > 0;
646
bool KateFileTreeModel::isDir(const QModelIndex &index) const
648
if (!index.isValid()) {
652
const ProxyItem *item = static_cast<ProxyItem *>(index.internalPointer());
657
return item->flag(ProxyItem::Dir);
660
bool KateFileTreeModel::listMode() const
665
void KateFileTreeModel::setListMode(bool lm)
667
if (lm != m_listMode) {
675
void KateFileTreeModel::documentOpened(KTextEditor::Document *doc)
677
ProxyItem *item = new ProxyItem(QString());
680
updateItemPathAndHost(item);
683
m_docmap[doc] = item;
684
connectDocument(doc);
687
void KateFileTreeModel::documentsOpened(const QList<KTextEditor::Document *> &docs)
689
foreach(KTextEditor::Document * doc, docs) {
690
if (m_docmap.contains(doc)) {
691
documentNameChanged(doc);
698
void KateFileTreeModel::documentModifiedChanged(KTextEditor::Document *doc)
700
if (!m_docmap.contains(doc)) {
704
ProxyItem *item = m_docmap[doc];
706
if (doc->isModified()) {
707
item->setFlag(ProxyItem::Modified);
709
item->clearFlag(ProxyItem::Modified);
710
item->clearFlag(ProxyItem::ModifiedExternally);
711
item->clearFlag(ProxyItem::DeletedExternally);
716
const QModelIndex idx = createIndex(item->row(), 0, item);
717
emit dataChanged(idx, idx);
720
void KateFileTreeModel::documentModifiedOnDisc(KTextEditor::Document *doc, bool modified, KTextEditor::ModificationInterface::ModifiedOnDiskReason reason)
723
if (!m_docmap.contains(doc)) {
727
ProxyItem *item = m_docmap[doc];
729
// This didn't do what I thought it did, on an ignore
730
// we'd get !modified causing the warning icons to disappear
732
item->clearFlag(ProxyItem::ModifiedExternally);
733
item->clearFlag(ProxyItem::DeletedExternally);
735
if (reason == KTextEditor::ModificationInterface::OnDiskDeleted) {
736
item->setFlag(ProxyItem::DeletedExternally);
737
} else if (reason == KTextEditor::ModificationInterface::OnDiskModified) {
738
item->setFlag(ProxyItem::ModifiedExternally);
739
} else if (reason == KTextEditor::ModificationInterface::OnDiskCreated) {
740
// with out this, on "reload" we don't get the icons removed :(
741
item->clearFlag(ProxyItem::ModifiedExternally);
742
item->clearFlag(ProxyItem::DeletedExternally);
748
const QModelIndex idx = createIndex(item->row(), 0, item);
749
emit dataChanged(idx, idx);
752
void KateFileTreeModel::documentActivated(const KTextEditor::Document *doc)
754
if (!m_docmap.contains(doc)) {
758
ProxyItem *item = m_docmap[doc];
759
m_viewHistory.removeAll(item);
760
m_viewHistory.prepend(item);
762
while (m_viewHistory.count() > 10) {
763
m_viewHistory.removeLast();
769
void KateFileTreeModel::documentEdited(const KTextEditor::Document *doc)
771
if (!m_docmap.contains(doc)) {
775
ProxyItem *item = m_docmap[doc];
776
m_editHistory.removeAll(item);
777
m_editHistory.prepend(item);
778
while (m_editHistory.count() > 10) {
779
m_editHistory.removeLast();
785
void KateFileTreeModel::slotAboutToDeleteDocuments(const QList<KTextEditor::Document *> &docs)
787
foreach(const KTextEditor::Document * doc, docs) {
788
disconnect(doc, SIGNAL(documentNameChanged(KTextEditor::Document *)), this, SLOT(documentNameChanged(KTextEditor::Document *)));
789
disconnect(doc, SIGNAL(documentUrlChanged(KTextEditor::Document *)), this, SLOT(documentNameChanged(KTextEditor::Document *)));
790
disconnect(doc, SIGNAL(modifiedChanged(KTextEditor::Document *)), this, SLOT(documentModifiedChanged(KTextEditor::Document *)));
791
disconnect(doc, SIGNAL(modifiedOnDisk(KTextEditor::Document *, bool, KTextEditor::ModificationInterface::ModifiedOnDiskReason)),
792
this, SLOT(documentModifiedOnDisc(KTextEditor::Document *, bool, KTextEditor::ModificationInterface::ModifiedOnDiskReason)));
796
void KateFileTreeModel::slotDocumentsDeleted(const QList<KTextEditor::Document *> &docs)
798
foreach(const KTextEditor::Document * doc, docs) {
799
connectDocument(doc);
806
EditViewCount(): edit(0), view(0)
812
void KateFileTreeModel::updateBackgrounds(bool force)
814
if (!m_shadingEnabled && !force) {
818
QMap <ProxyItem *, EditViewCount> helper;
821
foreach(ProxyItem * item, m_viewHistory) {
822
helper[item].view = i;
827
foreach(ProxyItem * item, m_editHistory) {
828
helper[item].edit = i;
832
QMap<ProxyItem *, QBrush> oldBrushes = m_brushes;
835
const int hc = m_viewHistory.count();
836
const int ec = m_editHistory.count();
838
for (QMap<ProxyItem *, EditViewCount>::iterator it = helper.begin(); it != helper.end(); ++it) {
839
QColor shade(m_viewShade);
840
QColor eshade(m_editShade);
842
if (it.value().edit > 0) {
843
int v = hc - it.value().view;
844
int e = ec - it.value().edit + 1;
848
const int n = qMax(v + e, 1);
851
((shade.red() * v) + (eshade.red() * e)) / n,
852
((shade.green() * v) + (eshade.green() * e)) / n,
853
((shade.blue() * v) + (eshade.blue() * e)) / n
857
// blend in the shade color; latest is most colored.
858
const double t = double(hc - it.value().view + 1) / double(hc);
860
m_brushes[it.key()] = QBrush(KColorUtils::mix(QPalette().color(QPalette::Base), shade, t));
863
foreach(ProxyItem * item, m_brushes.keys()) {
864
oldBrushes.remove(item);
865
const QModelIndex idx = createIndex(item->row(), 0, item);
866
dataChanged(idx, idx);
869
foreach(ProxyItem * item, oldBrushes.keys()) {
870
const QModelIndex idx = createIndex(item->row(), 0, item);
871
dataChanged(idx, idx);
875
void KateFileTreeModel::handleEmptyParents(ProxyItemDir *item)
879
if (!item->parent()) {
883
ProxyItemDir *parent = item->parent();
886
if (!item->childCount()) {
887
const QModelIndex parent_index = (parent == m_root) ? QModelIndex() : createIndex(parent->row(), 0, parent);
888
beginRemoveRows(parent_index, item->row(), item->row());
889
parent->remChild(item);
893
// breakout early, if this node isn't empty, theres no use in checking its parents
898
parent = item->parent();
902
void KateFileTreeModel::documentClosed(KTextEditor::Document *doc)
904
if (!m_docmap.contains(doc)) {
908
if (m_shadingEnabled) {
909
ProxyItem *toRemove = m_docmap[doc];
910
if (m_brushes.contains(toRemove)) {
911
m_brushes.remove(toRemove);
914
if (m_viewHistory.contains(toRemove)) {
915
m_viewHistory.removeAll(toRemove);
918
if (m_editHistory.contains(toRemove)) {
919
m_editHistory.removeAll(toRemove);
923
ProxyItem *node = m_docmap[doc];
924
ProxyItemDir *parent = node->parent();
926
const QModelIndex parent_index = (parent == m_root) ? QModelIndex() : createIndex(parent->row(), 0, parent);
927
beginRemoveRows(parent_index, node->row(), node->row());
928
node->parent()->remChild(node);
932
handleEmptyParents(parent);
934
m_docmap.remove(doc);
937
void KateFileTreeModel::documentNameChanged(KTextEditor::Document *doc)
939
if (!m_docmap.contains(doc)) {
943
ProxyItem *item = m_docmap[doc];
945
if (m_shadingEnabled) {
946
ProxyItem *toRemove = m_docmap[doc];
947
if (m_brushes.contains(toRemove)) {
948
QBrush brush = m_brushes[toRemove];
949
m_brushes.remove(toRemove);
950
m_brushes.insert(item, brush);
953
if (m_viewHistory.contains(toRemove)) {
954
const int idx = m_viewHistory.indexOf(toRemove);
956
m_viewHistory.replace(idx, item);
960
if (m_editHistory.contains(toRemove)) {
961
const int idx = m_editHistory.indexOf(toRemove);
963
m_editHistory.replace(idx, item);
968
handleNameChange(item);
969
emit triggerViewChangeAfterNameChange(); // FIXME: heh, non-standard signal?
972
ProxyItemDir *KateFileTreeModel::findRootNode(const QString &name, const int r) const
974
foreach(ProxyItem * item, m_root->children()) {
975
if (!item->flag(ProxyItem::Dir)) {
979
// make sure we're actually matching against the right dir,
980
// previously the check below would match /foo/xy against /foo/x
981
// and return /foo/x rather than /foo/xy
982
// this seems a bit hackish, but is the simplest way to solve the
984
QString path = item->path().section(QLatin1Char('/'), 0, -r) + QLatin1Char('/');
986
if (name.startsWith(path)) {
987
return static_cast<ProxyItemDir *>(item);
994
ProxyItemDir *KateFileTreeModel::findChildNode(const ProxyItemDir *parent, const QString &name) const
996
Q_ASSERT(parent != 0);
997
Q_ASSERT(!name.isEmpty());
999
if (!parent->childCount()) {
1003
foreach(ProxyItem * item, parent->children()) {
1004
if (!item->flag(ProxyItem::Dir)) {
1008
if (item->display() == name) {
1009
return static_cast<ProxyItemDir *>(item);
1016
void KateFileTreeModel::insertItemInto(ProxyItemDir *root, ProxyItem *item)
1018
Q_ASSERT(root != 0);
1019
Q_ASSERT(item != 0);
1021
QString tail = item->path();
1022
tail.remove(0, root->path().length());
1023
QStringList parts = tail.split(QLatin1Char('/'), QString::SkipEmptyParts);
1024
ProxyItemDir *ptr = root;
1025
QStringList current_parts;
1026
current_parts.append(root->path());
1028
// seems this can be empty, see bug 286191
1029
if (!parts.isEmpty()) {
1033
foreach(const QString & part, parts) {
1034
current_parts.append(part);
1035
ProxyItemDir *find = findChildNode(ptr, part);
1037
const QString new_name = current_parts.join(QLatin1String("/"));
1038
const QModelIndex parent_index = (ptr == m_root) ? QModelIndex() : createIndex(ptr->row(), 0, ptr);
1039
beginInsertRows(parent_index, ptr->childCount(), ptr->childCount());
1040
ptr = new ProxyItemDir(new_name, ptr);
1047
const QModelIndex parent_index = (ptr == m_root) ? QModelIndex() : createIndex(ptr->row(), 0, ptr);
1048
beginInsertRows(parent_index, ptr->childCount(), ptr->childCount());
1049
ptr->addChild(item);
1053
void KateFileTreeModel::handleInsert(ProxyItem *item)
1055
Q_ASSERT(item != 0);
1057
if (m_listMode || item->flag(ProxyItem::Empty)) {
1058
beginInsertRows(QModelIndex(), m_root->childCount(), m_root->childCount());
1059
m_root->addChild(item);
1064
// case (item.path > root.path)
1065
ProxyItemDir *root = findRootNode(item->path());
1067
insertItemInto(root, item);
1071
// trim off trailing file and dir
1072
QString base = item->path().section(QLatin1Char('/'), 0, -2);
1075
ProxyItemDir *new_root = new ProxyItemDir(base);
1076
new_root->setHost(item->host());
1078
// add new root to m_root
1079
beginInsertRows(QModelIndex(), m_root->childCount(), m_root->childCount());
1080
m_root->addChild(new_root);
1083
// same fix as in findRootNode, try to match a full dir, instead of a partial path
1084
base += QLatin1Char('/');
1086
// try and merge existing roots with the new root node (new_root.path < root.path)
1087
foreach(ProxyItem * root, m_root->children()) {
1088
if (root == new_root || !root->flag(ProxyItem::Dir)) {
1092
if (root->path().startsWith(base)) {
1093
beginRemoveRows(QModelIndex(), root->row(), root->row());
1094
m_root->remChild(root);
1097
//beginInsertRows(new_root_index, new_root->childCount(), new_root->childCount());
1098
// this can't use new_root->addChild directly, or it'll potentially miss a bunch of subdirs
1099
insertItemInto(new_root, root);
1104
// add item to new root
1105
// have to call begin/endInsertRows here, or the new item won't show up.
1106
const QModelIndex new_root_index = createIndex(new_root->row(), 0, new_root);
1107
beginInsertRows(new_root_index, new_root->childCount(), new_root->childCount());
1108
new_root->addChild(item);
1111
handleDuplicitRootDisplay(new_root);
1114
void KateFileTreeModel::handleDuplicitRootDisplay(ProxyItemDir *init)
1116
QStack<ProxyItemDir *> rootsToCheck;
1117
rootsToCheck.push(init);
1119
// make sure the roots don't match (recursively)
1120
while (!rootsToCheck.isEmpty()) {
1121
ProxyItemDir *check_root = rootsToCheck.pop();
1123
if (check_root->parent() != m_root) {
1127
foreach(ProxyItem * root, m_root->children()) {
1128
if (root == check_root || !root->flag(ProxyItem::Dir)) {
1132
if (check_root->display() == root->display()) {
1133
bool changed = false;
1135
const QString rdir = root->path().section(QLatin1Char('/'), 0, -2);
1136
if (!rdir.isEmpty()) {
1137
beginRemoveRows(QModelIndex(), root->row(), root->row());
1138
m_root->remChild(root);
1141
ProxyItemDir *irdir = new ProxyItemDir(rdir);
1142
beginInsertRows(QModelIndex(), m_root->childCount(), m_root->childCount());
1143
m_root->addChild(irdir);
1146
insertItemInto(irdir, root);
1148
foreach(ProxyItem * node, m_root->children()) {
1149
if (node == irdir || !root->flag(ProxyItem::Dir)) {
1153
const QString xy = rdir + QLatin1Char('/');
1154
if (node->path().startsWith(xy)) {
1155
beginRemoveRows(QModelIndex(), node->row(), node->row());
1156
m_root->remChild(node);
1158
insertItemInto(irdir, node);
1162
rootsToCheck.push(irdir);
1166
const QString nrdir = check_root->path().section(QLatin1Char('/'), 0, -2);
1167
if (!nrdir.isEmpty()) {
1168
beginRemoveRows(QModelIndex(), check_root->row(), check_root->row());
1169
m_root->remChild(check_root);
1172
ProxyItemDir *irdir = new ProxyItemDir(nrdir);
1173
beginInsertRows(QModelIndex(), m_root->childCount(), m_root->childCount());
1174
m_root->addChild(irdir);
1177
insertItemInto(irdir, check_root);
1179
rootsToCheck.push(irdir);
1193
void KateFileTreeModel::handleNameChange(ProxyItem *item)
1195
Q_ASSERT(item != 0);
1196
Q_ASSERT(item->parent());
1198
updateItemPathAndHost(item);
1201
const QModelIndex idx = createIndex(item->row(), 0, item);
1203
emit dataChanged(idx, idx);
1207
// in either case (new/change) we want to remove the item from its parent
1208
ProxyItemDir *parent = item->parent();
1210
const QModelIndex parent_index = (parent == m_root) ? QModelIndex() : createIndex(parent->row(), 0, parent);
1211
beginRemoveRows(parent_index, item->row(), item->row());
1212
parent->remChild(item);
1215
handleEmptyParents(parent);
1217
// clear all but Empty flag
1218
if (item->flag(ProxyItem::Empty)) {
1219
item->setFlags(ProxyItem::Empty);
1221
item->setFlags(ProxyItem::None);
1228
void KateFileTreeModel::updateItemPathAndHost(ProxyItem *item) const
1230
const KTextEditor::Document *doc = item->doc();
1231
Q_ASSERT(doc); // this method should not be called at directory items
1233
QString path = doc->url().path();
1235
if (doc->url().isEmpty()) {
1236
path = doc->documentName();
1237
item->setFlag(ProxyItem::Empty);
1239
item->clearFlag(ProxyItem::Empty);
1240
host = doc->url().host();
1241
if (!host.isEmpty()) {
1242
path = QString::fromLatin1("[%1]%2").arg(host).arg(path);
1246
// for some reason we get useless name changes [should be fixed in 5.0]
1247
if (item->path() == path) {
1251
item->setPath(path);
1252
item->setHost(host);
1255
void KateFileTreeModel::setupIcon(ProxyItem *item) const
1257
Q_ASSERT(item != 0);
1261
if (item->flag(ProxyItem::Modified)) {
1262
icon_name = QLatin1String("document-save");
1264
const QUrl url(item->path());
1265
icon_name = QMimeDatabase().mimeTypeForFile(url.path(), QMimeDatabase::MatchExtension).iconName();
1268
QIcon icon = QIcon::fromTheme(icon_name);
1270
if (item->flag(ProxyItem::ModifiedExternally) || item->flag(ProxyItem::DeletedExternally)) {
1271
icon = KIconUtils::addOverlay(icon, QIcon(QLatin1String("emblem-important")), Qt::TopLeftCorner);
1274
item->setIcon(icon);
1277
void KateFileTreeModel::resetHistory()
1279
QSet<ProxyItem *> list = QSet<ProxyItem *>::fromList(m_viewHistory);
1280
list += QSet<ProxyItem *>::fromList(m_editHistory);
1282
m_viewHistory.clear();
1283
m_editHistory.clear();
1286
foreach(ProxyItem * item, list) {
1287
QModelIndex idx = createIndex(item->row(), 0, item);
1288
dataChanged(idx, idx, QVector<int>(1, Qt::BackgroundRole));