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 version 2 as published by the Free Software Foundation.
8
This library is distributed in the hope that it will be useful,
9
but WITHOUT ANY WARRANTY; without even the implied warranty of
10
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
Library General Public License for more details.
13
You should have received a copy of the GNU Library General Public License
14
along with this library; see the file COPYING.LIB. If not, write to
15
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16
Boston, MA 02110-1301, USA.
19
#include "katefiletreemodel.h"
25
#include <KColorScheme>
26
#include <KColorUtils>
29
#include <ktexteditor/document.h>
31
#include <kate/application.h>
32
#include <kate/documentmanager.h>
34
#include "katefiletreedebug.h"
38
friend class KateFileTreeModel;
41
enum Flag { None = 0, Dir = 1, Modified = 2, ModifiedExternally = 4, DeletedExternally = 8, Empty = 16, ShowFullPath = 32, Host=64 };
42
Q_DECLARE_FLAGS(Flags, Flag)
44
ProxyItem(QString n, ProxyItemDir *p = 0, Flags f = ProxyItem::None);
47
int addChild(ProxyItem *p);
48
void remChild(ProxyItem *p);
50
ProxyItemDir *parent();
52
ProxyItem *child(int idx);
59
QString documentName();
60
void setPath(const QString &str);
62
void setIcon(KIcon i);
65
QList<ProxyItem*> &children();
67
void setDoc(KTextEditor::Document *doc);
68
KTextEditor::Document *doc();
69
QList<KTextEditor::Document*> docTree() const;
71
void setFlags(Flags flags);
72
void setFlag(Flag flag);
73
void clearFlag(Flag flag);
75
void setHost(const QString &host);
76
const QString& host() const;
80
QString m_documentName;
81
ProxyItemDir *m_parent;
82
QList<ProxyItem*> m_children;
88
KTextEditor::Document *m_doc;
94
QDebug operator<<(QDebug dbg, ProxyItem *item)
97
dbg.nospace() << "ProxyItem(0x0) ";
98
return dbg.maybeSpace();
101
void *parent = static_cast<void *>(item->parent());
103
dbg.nospace() << "ProxyItem(" << (void*)item << ",";
104
dbg.nospace() << parent << "," << item->row() << ",";
105
dbg.nospace() << item->doc() << "," << item->path() << ") ";
106
return dbg.maybeSpace();
110
class ProxyItemDir : public ProxyItem
113
ProxyItemDir(QString n, ProxyItemDir *p = 0) : ProxyItem(n, p) { setFlag(ProxyItem::Dir); initDisplay();}
116
QDebug operator<<(QDebug dbg, ProxyItemDir *item)
119
dbg.nospace() << "ProxyItemDir(0x0) ";
120
return dbg.maybeSpace();
123
void *parent = static_cast<void *>(item->parent());
125
dbg.nospace() << "ProxyItemDir(" << (void*)item << ",";
126
dbg.nospace() << parent << "," << item->row() << ",";
127
dbg.nospace() << item->path() << ", children:" << item->childCount() << ") ";
128
return dbg.maybeSpace();
131
Q_DECLARE_OPERATORS_FOR_FLAGS(ProxyItem::Flags)
133
ProxyItem::ProxyItem(QString d, ProxyItemDir *p, ProxyItem::Flags f)
134
: m_path(d), m_parent(p), m_row(-1), m_flags(f), m_doc(0)
136
kDebug(debugArea()) << this;
144
ProxyItem::~ProxyItem()
146
foreach(ProxyItem *item, m_children) {
151
void ProxyItem::initDisplay()
153
// triggers only if this is a top level node and the root has the show full path flag set.
154
if (flag(ProxyItem::Dir) && m_parent && !m_parent->m_parent && m_parent->flag(ProxyItem::ShowFullPath)) {
156
if(m_display.startsWith(QDir::homePath())) {
157
m_display.replace(0, QDir::homePath().length(), "~");
160
m_display = m_path.section(QLatin1Char('/'), -1, -1);
161
if (flag(ProxyItem::Host) && (!m_parent || (m_parent && !m_parent->m_parent))) {
162
QString hostPrefix="["+host()+"]";
163
if (hostPrefix!=m_display)
164
m_display=hostPrefix+m_display;
170
int ProxyItem::addChild(ProxyItem *item)
172
int item_row = m_children.count();
173
item->m_row = item_row;
174
m_children.append(item);
175
item->m_parent = static_cast<ProxyItemDir*>(this);
179
kDebug(debugArea()) << "added" << item << "to" << item->m_parent;
183
void ProxyItem::remChild(ProxyItem *item)
185
kDebug(debugArea()) << "remove" << item << "from" << static_cast<ProxyItemDir*>(this);
186
m_children.removeOne(item);
188
// could be done a little better, but this'll work.
189
for(int i = 0; i < m_children.count(); i++) {
190
m_children[i]->m_row = i;
196
ProxyItemDir *ProxyItem::parent()
201
ProxyItem *ProxyItem::child(int idx)
203
if(idx < 0 || idx >= m_children.count()) return 0;
204
return m_children[idx];
207
int ProxyItem::childCount()
209
return m_children.count();
217
KIcon ProxyItem::icon()
219
if(m_children.count())
220
return KIcon("folder");
225
void ProxyItem::setIcon(KIcon i)
230
QString ProxyItem::documentName() {
231
return m_documentName;
234
QString ProxyItem::display()
239
QString ProxyItem::path()
244
void ProxyItem::setPath(const QString &p)
250
QList<ProxyItem*> &ProxyItem::children()
255
void ProxyItem::setDoc(KTextEditor::Document *doc)
259
m_documentName=QString();
261
QString docName=doc->documentName();
262
if (flag(ProxyItem::Host))
263
m_documentName="["+m_host+"]"+docName;
265
m_documentName=docName;
269
KTextEditor::Document *ProxyItem::doc()
274
QList<KTextEditor::Document*> ProxyItem::docTree() const
276
QList<KTextEditor::Document*> result;
278
result.append(m_doc);
280
for (QList<ProxyItem*>::const_iterator iter = m_children.constBegin(); iter != m_children.constEnd(); ++iter) {
281
result.append((*iter)->docTree());
286
bool ProxyItem::flag(Flag f)
291
void ProxyItem::setFlag(Flag f)
296
void ProxyItem::setFlags(Flags f)
301
void ProxyItem::clearFlag(Flag f)
306
void ProxyItem::setHost(const QString& host)
310
docName=m_doc->documentName();
311
if (host.isEmpty()) {
313
m_documentName=docName;
316
m_documentName="["+host+"]"+docName;
323
const QString& ProxyItem::host() const
328
KateFileTreeModel::KateFileTreeModel(QObject *p)
329
: QAbstractItemModel(p),
330
m_root(new ProxyItemDir(QString("m_root"), 0))
333
// setup default settings
334
// session init will set these all soon
335
KColorScheme colors(QPalette::Active);
336
QColor bg = colors.background().color();
337
m_editShade = KColorUtils::tint(bg, colors.foreground(KColorScheme::ActiveText).color(), 0.5);
338
m_viewShade = KColorUtils::tint(bg, colors.foreground(KColorScheme::VisitedText).color(), 0.5);
339
m_shadingEnabled = true;
345
KateFileTreeModel::~KateFileTreeModel()
350
bool KateFileTreeModel::shadingEnabled()
352
return m_shadingEnabled;
355
void KateFileTreeModel::setShadingEnabled(bool se)
357
if(m_shadingEnabled != se) {
358
updateBackgrounds(true);
359
m_shadingEnabled = se;
363
QColor KateFileTreeModel::editShade()
368
void KateFileTreeModel::setEditShade(QColor es)
373
QColor KateFileTreeModel::viewShade()
378
void KateFileTreeModel::setViewShade(QColor vs)
383
bool KateFileTreeModel::showFullPathOnRoots(void)
385
return m_root->flag(ProxyItem::ShowFullPath);
388
void KateFileTreeModel::setShowFullPathOnRoots(bool s)
391
m_root->setFlag(ProxyItem::ShowFullPath);
393
m_root->clearFlag(ProxyItem::ShowFullPath);
395
foreach(ProxyItem *root, m_root->children()) {
400
// FIXME: optimize this later to insert all at once if possible
401
// maybe add a "bool emitSignals" to documentOpened
402
// and possibly use beginResetModel here? I dunno.
404
void KateFileTreeModel::initModel()
406
// add already existing documents
407
foreach( KTextEditor::Document* doc, Kate::application()->documentManager()->documents() )
408
documentOpened( doc );
411
void KateFileTreeModel::clearModel()
414
// can safely ignore documentClosed here
416
beginRemoveRows(QModelIndex(), 0, m_root->childCount()-1);
419
m_root = new ProxyItemDir(QString("m_root"), 0);
422
m_viewHistory.clear();
423
m_editHistory.clear();
429
void KateFileTreeModel::connectDocument(const KTextEditor::Document *doc)
431
connect(doc, SIGNAL(documentNameChanged(KTextEditor::Document*)), this, SLOT(documentNameChanged(KTextEditor::Document*)));
432
connect(doc, SIGNAL(documentUrlChanged(KTextEditor::Document*)), this, SLOT(documentNameChanged(KTextEditor::Document*)));
433
connect(doc, SIGNAL(modifiedChanged(KTextEditor::Document*)), this, SLOT(documentModifiedChanged(KTextEditor::Document*)));
434
connect(doc, SIGNAL(modifiedOnDisk(KTextEditor::Document*,bool,KTextEditor::ModificationInterface::ModifiedOnDiskReason)),
435
this, SLOT(documentModifiedOnDisc(KTextEditor::Document*,bool,KTextEditor::ModificationInterface::ModifiedOnDiskReason)) );
438
QModelIndex KateFileTreeModel::docIndex(KTextEditor::Document *d)
440
kDebug(debugArea()) << "BEGIN!";
441
ProxyItem *item = m_docmap[d];
443
kDebug(debugArea()) << "doc" << d << "does not exist";
444
return QModelIndex();
447
kDebug(debugArea()) << "END!";
448
return createIndex(item->row(), 0, item);
451
Qt::ItemFlags KateFileTreeModel::flags( const QModelIndex &index ) const
453
Qt::ItemFlags flags = Qt::ItemIsEnabled;
458
ProxyItem *item = static_cast<ProxyItem*>(index.internalPointer());
459
if(item && !item->childCount()) {
460
flags |= Qt::ItemIsSelectable;
466
#include "metatype_qlist_ktexteditor_document_pointer.h"
468
QVariant KateFileTreeModel::data( const QModelIndex &index, int role ) const
470
//kDebug(debugArea()) << "BEGIN!";
471
if(!index.isValid()) {
472
kDebug(debugArea()) << "index is invalid!";
476
ProxyItem *item = static_cast<ProxyItem *>(index.internalPointer());
478
kDebug(debugArea()) << "internal pointer is null!";
483
case KateFileTreeModel::PathRole:
484
// allow to sort with hostname + path, bug 271488
485
return (item->doc() && !item->doc()->url().isEmpty()) ? item->doc()->url().pathOrUrl() : item->path();
487
case KateFileTreeModel::DocumentRole:
488
return QVariant::fromValue(item->doc());
490
case KateFileTreeModel::OpeningOrderRole:
493
case KateFileTreeModel::DocumentTreeRole:
494
return QVariant::fromValue(item->docTree());
496
case Qt::DisplayRole:
497
// in list mode we want to use kate's fancy names.
499
return item->documentName();
501
return item->display();
503
case Qt::DecorationRole:
506
case Qt::ToolTipRole: {
507
QString tooltip = item->path();
508
if (item->flag(ProxyItem::DeletedExternally) || item->flag(ProxyItem::ModifiedExternally)) {
509
tooltip = i18nc("%1 is the full path", "<p><b>%1</b></p><p>The document has been modified by another application.</p>", item->path());
515
case Qt::ForegroundRole: {
516
KColorScheme colors(QPalette::Active);
517
if(!item->flag(ProxyItem::Dir) && (!item->doc() || item->doc()->openingError())) return colors.foreground(KColorScheme::InactiveText).color();
520
case Qt::BackgroundRole:
521
// TODO: do that funky shading the file list does...
522
if(m_shadingEnabled && m_brushes.contains(item))
523
return m_brushes[item];
527
//kDebug(debugArea()) << "END!";
531
QVariant KateFileTreeModel::headerData( int section, Qt::Orientation orientation, int role ) const
533
Q_UNUSED(orientation);
537
return QString("a header");
542
int KateFileTreeModel::rowCount( const QModelIndex &parent ) const
544
if(!parent.isValid())
545
return m_root->childCount();
547
ProxyItem *item = static_cast<ProxyItem *>(parent.internalPointer());
549
kDebug(debugArea()) << "internal pointer is invalid";
553
return item->childCount();
556
int KateFileTreeModel::columnCount( const QModelIndex &parent ) const
563
QModelIndex KateFileTreeModel::parent( const QModelIndex &index ) const
565
if(!index.isValid()) {
566
kDebug(debugArea()) << "index is invalid";
567
return QModelIndex();
570
ProxyItem *item = static_cast<ProxyItem *>(index.internalPointer());
572
kDebug(debugArea()) << "internal pointer is invalid";
573
return QModelIndex();
576
if(!item->parent()) {
577
kDebug(debugArea()) << "parent pointer is null";
578
return QModelIndex();
581
if(item->parent() == m_root)
582
return QModelIndex();
584
return createIndex(item->parent()->row(), 0, item->parent());
587
QModelIndex KateFileTreeModel::index( int row, int column, const QModelIndex &parent ) const
591
kDebug(debugArea()) << "column is invalid";
592
return QModelIndex();
595
if(!parent.isValid())
598
p = static_cast<ProxyItem *>(parent.internalPointer());
601
kDebug(debugArea()) << "internal pointer is invalid";
602
return QModelIndex();
605
if(row < 0 || row >= p->childCount()) {
606
kDebug(debugArea()) << "row is out of bounds (" << row << " < 0 || " << row << " >= " << p->childCount() << ")";
607
return QModelIndex();
610
return createIndex(row, 0, p->child(row));
613
bool KateFileTreeModel::hasChildren( const QModelIndex & parent ) const
615
if(!parent.isValid())
616
return m_root->childCount() > 0;
618
ProxyItem *item = static_cast<ProxyItem*>(parent.internalPointer());
620
kDebug(debugArea()) << "internal pointer is null";
624
return item->childCount() > 0;
627
bool KateFileTreeModel::isDir(const QModelIndex &index)
632
ProxyItem *item = static_cast<ProxyItem*>(index.internalPointer());
634
kDebug(debugArea()) << "internal pointer is null";
638
return item->flag(ProxyItem::Dir);
641
bool KateFileTreeModel::listMode()
646
void KateFileTreeModel::setListMode(bool lm)
648
if(lm != m_listMode) {
656
void KateFileTreeModel::documentOpened(KTextEditor::Document *doc)
658
QString path = doc->url().path();
659
bool isEmpty = false;
662
if(doc->url().isEmpty()) {
663
path = doc->documentName();
666
host=doc->url().host();
668
path="["+host+"]"+path;
672
ProxyItem *item = new ProxyItem(path, 0);
675
item->setFlag(ProxyItem::Empty);
677
m_debugmap[item] = item;
681
kDebug(debugArea()) << "before add:" << item;
684
m_docmap[doc] = item;
685
connectDocument(doc);
687
kDebug(debugArea()) << "after add:" << item;
691
void KateFileTreeModel::documentsOpened(const QList<KTextEditor::Document*> &docs)
693
foreach(KTextEditor::Document *doc, docs) {
694
if (m_docmap.contains(doc)) {
695
documentNameChanged(doc);
702
void KateFileTreeModel::documentModifiedChanged(KTextEditor::Document *doc)
704
kDebug(debugArea()) << "BEGIN!";
706
ProxyItem *item = m_docmap[doc];
710
if(doc->isModified()) {
711
item->setFlag(ProxyItem::Modified);
712
kDebug(debugArea()) << "modified!";
715
item->clearFlag(ProxyItem::Modified);
716
item->clearFlag(ProxyItem::ModifiedExternally);
717
item->clearFlag(ProxyItem::DeletedExternally);
718
kDebug(debugArea()) << "saved!";
723
QModelIndex idx = createIndex(item->row(), 0, item);
724
emit dataChanged(idx, idx);
726
kDebug(debugArea()) << "END!";
729
void KateFileTreeModel::documentModifiedOnDisc(KTextEditor::Document *doc, bool modified, KTextEditor::ModificationInterface::ModifiedOnDiskReason reason )
732
kDebug(debugArea()) << "BEGIN!";
733
ProxyItem *item = m_docmap[doc];
737
// This didn't do what I thought it did, on an ignore
738
// we'd get !modified causing the warning icons to disappear
740
item->clearFlag(ProxyItem::ModifiedExternally);
741
item->clearFlag(ProxyItem::DeletedExternally);
743
if(reason == KTextEditor::ModificationInterface::OnDiskDeleted) {
744
item->setFlag(ProxyItem::DeletedExternally);
745
kDebug(debugArea()) << "deleted!";
747
else if(reason == KTextEditor::ModificationInterface::OnDiskModified) {
748
item->setFlag(ProxyItem::ModifiedExternally);
749
kDebug(debugArea()) << "modified!";
751
else if(reason == KTextEditor::ModificationInterface::OnDiskCreated) {
752
kDebug(debugArea()) << "created!";
753
// with out this, on "reload" we don't get the icons removed :(
754
item->clearFlag(ProxyItem::ModifiedExternally);
755
item->clearFlag(ProxyItem::DeletedExternally);
761
QModelIndex idx = createIndex(item->row(), 0, item);
762
emit dataChanged(idx, idx);
763
kDebug(debugArea()) << "END!";
766
void KateFileTreeModel::documentActivated(KTextEditor::Document *doc)
768
kDebug(debugArea()) << "BEGIN!";
770
if(!m_docmap.contains(doc)) {
771
kDebug(debugArea()) << "invalid doc" << doc;
775
ProxyItem *item = m_docmap[doc];
776
kDebug(debugArea()) << "adding viewHistory" << item;
777
m_viewHistory.removeAll(item);
778
m_viewHistory.prepend(item);
780
while (m_viewHistory.count() > 10) m_viewHistory.removeLast();
784
kDebug(debugArea()) << "END!";
787
void KateFileTreeModel::documentEdited(KTextEditor::Document *doc)
789
kDebug(debugArea()) << "BEGIN!";
791
if(!m_docmap.contains(doc)) {
792
kDebug(debugArea()) << "invalid doc" << doc;
796
ProxyItem *item = m_docmap[doc];
797
kDebug(debugArea()) << "adding editHistory" << item;
798
m_editHistory.removeAll(item);
799
m_editHistory.prepend(item);
800
while (m_editHistory.count() > 10) m_editHistory.removeLast();
804
kDebug(debugArea()) << "END!";
807
void KateFileTreeModel::slotAboutToDeleteDocuments(const QList<KTextEditor::Document*> &docs)
809
foreach (const KTextEditor::Document *doc, docs) {
810
disconnect(doc, SIGNAL(documentNameChanged(KTextEditor::Document*)), this, SLOT(documentNameChanged(KTextEditor::Document*)));
811
disconnect(doc, SIGNAL(documentUrlChanged(KTextEditor::Document*)), this, SLOT(documentNameChanged(KTextEditor::Document*)));
812
disconnect(doc, SIGNAL(modifiedChanged(KTextEditor::Document*)), this, SLOT(documentModifiedChanged(KTextEditor::Document*)));
813
disconnect(doc, SIGNAL(modifiedOnDisk(KTextEditor::Document*,bool,KTextEditor::ModificationInterface::ModifiedOnDiskReason)),
814
this, SLOT(documentModifiedOnDisc(KTextEditor::Document*,bool,KTextEditor::ModificationInterface::ModifiedOnDiskReason)) );
818
void KateFileTreeModel::slotDocumentsDeleted(const QList<KTextEditor::Document*> &docs)
820
foreach (const KTextEditor::Document *doc, docs) {
821
connectDocument(doc);
828
EditViewCount(): edit(0), view(0)
834
void KateFileTreeModel::updateBackgrounds(bool force)
836
if (!m_shadingEnabled && !force) return;
838
kDebug(debugArea()) << "BEGIN!";
840
QMap <ProxyItem *, EditViewCount> helper;
843
foreach (ProxyItem *item, m_viewHistory)
845
helper[item].view = i;
846
if(!m_debugmap.contains(item)) {
847
kDebug(debugArea()) << "m_viewHistory contains an item that doesn't exist?" << item;
853
foreach (ProxyItem *item, m_editHistory)
855
helper[item].edit = i;
856
if(!m_debugmap.contains(item)) {
857
kDebug(debugArea()) << "m_editHistory contains an item that doesn't exist?" << item;
862
kDebug(debugArea()) << "m_editHistory contains " << m_editHistory.count()<<" elements";
864
QMap<ProxyItem *, QBrush> oldBrushes = m_brushes;
867
int hc = m_viewHistory.count();
868
int ec = m_editHistory.count();
870
for (QMap<ProxyItem *, EditViewCount>::iterator it = helper.begin();it != helper.end();++it)
872
QColor shade( m_viewShade );
873
QColor eshade( m_editShade );
875
if (it.value().edit > 0)
877
int v = hc - it.value().view;
878
int e = ec - it.value().edit + 1;
882
int n = qMax(v + e, 1);
885
((shade.red()*v) + (eshade.red()*e)) / n,
886
((shade.green()*v) + (eshade.green()*e)) / n,
887
((shade.blue()*v) + (eshade.blue()*e)) / n
891
// blend in the shade color; latest is most colored.
892
double t = double(hc - it.value().view + 1) / double(hc);
894
m_brushes[it.key()] = QBrush(KColorUtils::mix(QPalette().color(QPalette::Base), shade, t));
895
// kdDebug()<<"m_brushes[it.key()]"<<it.key()<<m_brushes[it.key()];
898
foreach(ProxyItem *item, m_brushes.keys())
900
oldBrushes.remove(item);
901
QModelIndex idx = createIndex(item->row(), 0, item);
902
dataChanged(idx, idx);
905
foreach(ProxyItem *item, oldBrushes.keys())
907
QModelIndex idx = createIndex(item->row(), 0, item);
908
dataChanged(idx, idx);
911
kDebug(debugArea()) << "END!";
914
void KateFileTreeModel::handleEmptyParents(ProxyItemDir *item)
916
kDebug(debugArea()) << "BEGIN!";
919
if(!item || !item->parent()) {
920
kDebug(debugArea()) << "parent" << item << "grandparent" << (item ? item->parent() : 0);
924
ProxyItemDir *parent = item->parent();
925
//emit layoutAboutToBeChanged();
927
kDebug(debugArea()) << "item" << item << "parent" << parent;
930
kDebug(debugArea()) << "item" << item << "parent" << parent;
931
if(!item->childCount()) {
932
QModelIndex parent_index = parent == m_root ? QModelIndex() : createIndex(parent->row(), 0, parent);
933
beginRemoveRows(parent_index, item->row(), item->row());
934
parent->remChild(item);
936
kDebug(debugArea()) << "deleted" << item;
940
// breakout early, if this node isn't empty, theres no use in checking its parents
941
kDebug(debugArea()) << "END!";
946
parent = item->parent();
949
//emit layoutChanged();
950
kDebug(debugArea()) << "END!";
953
void KateFileTreeModel::documentClosed(KTextEditor::Document *doc)
955
QString path = doc->url().path();
957
if(!m_docmap.contains(doc)) {
958
kDebug(debugArea()) << "docmap doesn't contain doc" << doc;
962
kDebug(debugArea()) << path << m_docmap[doc];
964
if(m_shadingEnabled) {
965
ProxyItem *toRemove = m_docmap[doc];
966
if(m_brushes.contains(toRemove)) {
967
m_brushes.remove(toRemove);
968
kDebug(debugArea()) << "removing brush" << toRemove;
971
if(m_viewHistory.contains(toRemove)) {
972
m_viewHistory.removeAll(toRemove);
973
kDebug(debugArea()) << "removing viewHistory" << toRemove;
976
if(m_editHistory.contains(toRemove)) {
977
m_editHistory.removeAll(toRemove);
978
kDebug(debugArea()) << "removing editHistory" << toRemove;
982
ProxyItem *node = m_docmap[doc];
983
ProxyItemDir *parent = node->parent();
985
QModelIndex parent_index = parent == m_root ? QModelIndex() : createIndex(parent->row(), 0, parent);
986
beginRemoveRows(parent_index, node->row(), node->row());
987
node->parent()->remChild(node);
990
m_debugmap.remove(node);
993
handleEmptyParents(parent);
995
m_docmap.remove(doc);
998
void KateFileTreeModel::documentNameChanged(KTextEditor::Document *doc)
1000
kDebug(debugArea()) << "BEGIN!";
1002
if(!m_docmap.contains(doc)) {
1003
kDebug(debugArea()) << "docmap doesn't contain doc" << doc;
1007
ProxyItem *item = m_docmap[doc];
1008
QString path = doc->url().path();
1010
if(doc->url().isEmpty()) {
1011
kDebug(debugArea()) << "change to unnamed item";
1012
path = doc->documentName();
1013
item->setFlag(ProxyItem::Empty);
1016
item->clearFlag(ProxyItem::Empty);
1017
host=doc->url().host();
1018
if (!host.isEmpty())
1019
path="["+host+"]"+path;
1022
kDebug(debugArea()) << item;
1023
kDebug(debugArea()) << item->display() << "->" << path;
1026
if(m_shadingEnabled) {
1027
ProxyItem *toRemove = m_docmap[doc];
1028
if(m_brushes.contains(toRemove)) {
1029
QBrush brush=m_brushes[toRemove];
1030
m_brushes.remove(toRemove);
1031
m_brushes.insert(item,brush);
1032
kDebug(debugArea()) << "removing brush" << toRemove;
1035
if(m_viewHistory.contains(toRemove)) {
1036
int idx=m_viewHistory.indexOf(toRemove);
1038
m_viewHistory.replace(idx,item);
1039
kDebug(debugArea()) << "removing/replacing view history" << toRemove;
1042
if(m_editHistory.contains(toRemove)) {
1043
int idx=m_editHistory.indexOf(toRemove);
1045
m_editHistory.replace(idx,item);
1046
kDebug(debugArea()) << "removing/replacing edit history" << toRemove;
1050
handleNameChange(item, path, host);
1052
triggerViewChangeAfterNameChange();
1054
kDebug(debugArea()) << "END!";
1057
ProxyItemDir *KateFileTreeModel::findRootNode(const QString &name, int r)
1059
QString base = name.section(QLatin1Char('/'), 0, -2);
1060
foreach(ProxyItem *item, m_root->children()) {
1061
QString path = item->path().section(QLatin1Char('/'), 0, -r);
1062
if (!item->flag(ProxyItem::Host) && !QFileInfo(path).isAbsolute()) {
1066
// make sure we're actually matching against the right dir,
1067
// previously the check below would match /foo/xy against /foo/x
1068
// and return /foo/x rather than /foo/xy
1069
// this seems a bit hackish, but is the simplest way to solve the
1071
path += QLatin1Char('/');
1073
if(name.startsWith(path) && item->flag(ProxyItem::Dir)) {
1074
return static_cast<ProxyItemDir*>(item);
1081
ProxyItemDir *KateFileTreeModel::findChildNode(ProxyItemDir *parent, const QString &name)
1083
Q_ASSERT(parent != 0);
1085
if(!parent || !parent->childCount()) {
1086
kDebug(debugArea()) << "invalid parent or no children" << parent;
1090
foreach(ProxyItem *item, parent->children()) {
1091
if(item->display() == name) {
1092
if(!item->flag(ProxyItem::Dir)) {
1093
kDebug(debugArea()) << "found" << item << "but its not a dir?";
1097
kDebug(debugArea()) << "found" << item;
1098
return static_cast<ProxyItemDir*>(item);
1102
kDebug(debugArea()) << "!found:" << name;
1106
void KateFileTreeModel::insertItemInto(ProxyItemDir *root, ProxyItem *item)
1108
kDebug(debugArea()) << "BEGIN!";
1110
Q_ASSERT(root != 0);
1111
Q_ASSERT(item != 0);
1114
QString tail = item->path();
1115
tail.remove(0, root->path().length());
1116
QStringList parts = tail.split(QLatin1Char('/'), QString::SkipEmptyParts);
1117
ProxyItemDir *ptr = root;
1118
QStringList current_parts;
1119
current_parts.append(root->path());
1121
// seems this can be empty, see bug 286191
1122
if (!parts.isEmpty())
1125
kDebug(debugArea()) << "creating tree for" << item;
1126
foreach(const QString &part, parts) {
1127
current_parts.append(part);
1128
ProxyItemDir *find = findChildNode(ptr, part);
1130
QString new_name = current_parts.join(QLatin1String("/"));
1131
QModelIndex parent_index = createIndex(ptr->row(), 0, ptr);
1132
kDebug(debugArea()) << "adding" << part << "to" << ptr;
1133
beginInsertRows(ptr == m_root ? QModelIndex() : parent_index, ptr->childCount(), ptr->childCount());
1134
ptr = new ProxyItemDir(new_name, ptr);
1142
kDebug(debugArea()) << "adding" << item << "to" << ptr;
1143
QModelIndex parent_index = createIndex(ptr->row(), 0, ptr);
1144
beginInsertRows(ptr == m_root ? QModelIndex() : parent_index, ptr->childCount(), ptr->childCount());
1145
ptr->addChild(item);
1148
kDebug(debugArea()) << "END!";
1151
void KateFileTreeModel::handleInsert(ProxyItem *item)
1153
kDebug(debugArea()) << "BEGIN!";
1155
Q_ASSERT(item != 0);
1158
kDebug(debugArea()) << "list mode, inserting into m_root";
1159
beginInsertRows(QModelIndex(), m_root->childCount(), m_root->childCount());
1160
m_root->addChild(item);
1165
if(item->flag(ProxyItem::Empty)) {
1166
kDebug(debugArea()) << "empty item";
1167
beginInsertRows(QModelIndex(), m_root->childCount(), m_root->childCount());
1168
m_root->addChild(item);
1173
ProxyItemDir *root = findRootNode(item->path());
1175
kDebug(debugArea()) << "got a root, inserting into it";
1176
insertItemInto(root, item);
1178
kDebug(debugArea()) << "creating a new root";
1180
// trim off trailing file and dir
1181
QString base = item->path().section(QLatin1Char('/'), 0, -2);
1184
ProxyItemDir *new_root = new ProxyItemDir(base, 0);
1185
new_root->setHost(item->host());
1187
// add new root to m_root
1188
kDebug(debugArea()) << "add" << new_root << "to m_root";
1189
beginInsertRows(QModelIndex(), m_root->childCount(), m_root->childCount());
1190
m_root->addChild(new_root);
1193
// same fix as in findRootNode, try to match a full dir, instead of a partial path
1194
base += QLatin1Char('/');
1196
// try and merge existing roots with the new root node.
1197
kDebug(debugArea()) << "attempting to merge some existing roots";
1198
foreach(ProxyItem *root, m_root->children()) {
1199
if(root == new_root || !root->flag(ProxyItem::Dir))
1202
if(root->path().startsWith(base)) {
1203
kDebug(debugArea()) << "removing" << root << "from m_root";
1204
beginRemoveRows(QModelIndex(), root->row(), root->row());
1205
m_root->remChild(root);
1208
kDebug(debugArea()) << "adding" << root << "to" << new_root;
1209
//beginInsertRows(new_root_index, new_root->childCount(), new_root->childCount());
1210
// this can't use new_root->addChild directly, or it'll potentially miss a bunch of subdirs
1211
insertItemInto(new_root, root);
1216
// add item to new root
1217
kDebug(debugArea()) << "adding" << item << "to" << new_root;
1218
// have to call begin/endInsertRows here, or the new item won't show up.
1219
QModelIndex new_root_index = createIndex(new_root->row(), 0, new_root);
1220
beginInsertRows(new_root_index, new_root->childCount(), new_root->childCount());
1221
new_root->addChild(item);
1226
kDebug(debugArea()) << "END!";
1229
void KateFileTreeModel::handleNameChange(ProxyItem *item, const QString &new_name, const QString& new_host)
1231
kDebug(debugArea()) << "BEGIN!";
1233
Q_ASSERT(item != 0);
1236
item->setPath(new_name);
1237
item->setHost(new_host);
1238
QModelIndex idx = createIndex(item->row(), 0, item);
1240
emit dataChanged(idx, idx);
1241
kDebug(debugArea()) << "list mode, short circuit";
1245
// for some reason we get useless name changes
1246
if(item->path() == new_name) {
1247
kDebug(debugArea()) << "bogus name change";
1251
// in either case (new/change) we want to remove the item from its parent
1253
ProxyItemDir *parent = item->parent();
1255
item->setPath(new_name);
1256
item->setHost(new_host);
1257
kDebug(debugArea()) << "ERROR: item" << item << "does not have a parent?";
1261
item->setPath(new_name);
1262
item->setHost(new_host);
1264
kDebug(debugArea()) << "removing" << item << "from" << parent;
1265
QModelIndex parent_index = parent == m_root ? QModelIndex() : createIndex(parent->row(), 0, parent);
1266
beginRemoveRows(parent_index, item->row(), item->row());
1267
parent->remChild(item);
1270
// remove empty parent nodes here, recursively.
1271
handleEmptyParents(parent);
1274
//item->setPath(new_name);
1276
// clear all but Empty flag
1277
if(item->flag(ProxyItem::Empty))
1278
item->setFlags(ProxyItem::Empty);
1280
item->setFlags(ProxyItem::None);
1285
kDebug(debugArea()) << "inserting" << item;
1288
kDebug(debugArea()) << "END!";
1291
void KateFileTreeModel::setupIcon(ProxyItem *item)
1293
kDebug(debugArea()) << "BEGIN!";
1295
Q_ASSERT(item != 0);
1297
QStringList emblems;
1300
if(item->flag(ProxyItem::Modified)) {
1301
icon_name = "document-save";
1304
KUrl url = item->path();
1305
icon_name = KMimeType::findByUrl(url, 0, true, true)->iconName();
1308
if(item->flag(ProxyItem::ModifiedExternally) || item->flag(ProxyItem::DeletedExternally)) {
1309
emblems << "emblem-important";
1310
kDebug(debugArea()) << "modified!";
1313
item->setIcon(KIcon(icon_name, 0, emblems));
1315
kDebug(debugArea()) << "END!";
1318
// kate: space-indent on; indent-width 2; replace-tabs on; mixed-indent off;