1 /*
2 SPDX-FileCopyrightText: 2001-2005, 2009 Otto Bruggeman <bruggie@gmail.com>
3 SPDX-FileCopyrightText: 2001-2003 John Firebaugh <jfirebaugh@kde.org>
4 SPDX-FileCopyrightText: 2007-2011 Kevin Kofler <kevin.kofler@chello.at>
5
6 SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #include "komparenavtreepart.h"
10
11 #include <QDebug>
12 #include <QTreeWidgetItemIterator>
13
14 #include <KLocalizedString>
15 #include <KPluginMetaData>
16 #include <KPluginFactory>
17
18 #include <libkomparediff2/difference.h>
19 #include <libkomparediff2/diffmodel.h>
20 #include <libkomparediff2/diffmodellist.h>
21 #include <libkomparediff2/komparemodellist.h>
22
23 #include <komparenavviewdebug.h>
24
25 #define COL_SOURCE 0
26 #define COL_DESTINATION 1
27 #define COL_DIFFERENCE 2
28
29 using namespace Diff2;
30
KompareNavTreePart(QWidget * parentWidget,QObject * parent,const KPluginMetaData & metaData,const QVariantList &)31 KompareNavTreePart::KompareNavTreePart(QWidget* parentWidget, QObject* parent,
32 const KPluginMetaData& metaData, const QVariantList&)
33 : KParts::ReadOnlyPart(parent),
34 m_splitter(nullptr),
35 m_modelList(nullptr),
36 m_srcDirTree(nullptr),
37 m_destDirTree(nullptr),
38 m_fileList(nullptr),
39 m_changesList(nullptr),
40 m_srcRootItem(nullptr),
41 m_destRootItem(nullptr),
42 m_selectedModel(nullptr),
43 m_selectedDifference(nullptr),
44 m_source(),
45 m_destination(),
46 m_info(nullptr)
47 {
48 setMetaData(metaData);
49
50 m_splitter = new QSplitter(Qt::Horizontal, parentWidget);
51
52 setWidget(m_splitter);
53
54 m_srcDirTree = new QTreeWidget(m_splitter);
55 m_srcDirTree->setHeaderLabel(i18nc("@title:column", "Source Folder"));
56 m_srcDirTree->setRootIsDecorated(false);
57 m_srcDirTree->setSortingEnabled(true);
58 m_srcDirTree->sortByColumn(0, Qt::AscendingOrder);
59
60 m_destDirTree = new QTreeWidget(m_splitter);
61 m_destDirTree->setHeaderLabel(i18nc("@title:column", "Destination Folder"));
62 m_destDirTree->setRootIsDecorated(false);
63 m_destDirTree->setSortingEnabled(true);
64 m_destDirTree->sortByColumn(0, Qt::AscendingOrder);
65
66 m_fileList = new QTreeWidget(m_splitter);
67 m_fileList->setHeaderLabels(QStringList {
68 i18nc("@title:column", "Source File"),
69 i18nc("@title:column", "Destination File")
70 } );
71 m_fileList->setAllColumnsShowFocus(true);
72 m_fileList->setRootIsDecorated(false);
73 m_fileList->setSortingEnabled(true);
74 m_fileList->sortByColumn(0, Qt::AscendingOrder);
75
76 m_changesList = new QTreeWidget(m_splitter);
77 m_changesList->setHeaderLabels(QStringList {
78 i18nc("@title:column", "Source Line"),
79 i18nc("@title:column", "Destination Line"),
80 i18nc("@title:column", "Difference"),
81 } );
82 m_changesList->setAllColumnsShowFocus(true);
83 m_changesList->setRootIsDecorated(false);
84 m_changesList->setSortingEnabled(true);
85 m_changesList->sortByColumn(0, Qt::AscendingOrder);
86
87 connect(m_srcDirTree, &QTreeWidget::currentItemChanged,
88 this, &KompareNavTreePart::slotSrcDirTreeSelectionChanged);
89 connect(m_destDirTree, &QTreeWidget::currentItemChanged,
90 this, &KompareNavTreePart::slotDestDirTreeSelectionChanged);
91 connect(m_fileList, &QTreeWidget::currentItemChanged,
92 this, &KompareNavTreePart::slotFileListSelectionChanged);
93 connect(m_changesList, &QTreeWidget::currentItemChanged,
94 this, &KompareNavTreePart::slotChangesListSelectionChanged);
95 }
96
~KompareNavTreePart()97 KompareNavTreePart::~KompareNavTreePart()
98 {
99 m_modelList = nullptr;
100 m_selectedModel = nullptr;
101 m_selectedDifference = nullptr;
102 }
103
slotKompareInfo(struct Kompare::Info * info)104 void KompareNavTreePart::slotKompareInfo(struct Kompare::Info* info)
105 {
106 m_info = info;
107 }
108
slotModelsChanged(const DiffModelList * modelList)109 void KompareNavTreePart::slotModelsChanged(const DiffModelList* modelList)
110 {
111 qCDebug(KOMPARENAVVIEW) << "Models (" << modelList << ") have changed... scanning the models... " ;
112
113 if (modelList)
114 {
115 m_modelList = modelList;
116 m_srcDirTree->clear();
117 m_destDirTree->clear();
118 m_fileList->clear();
119 m_changesList->clear();
120 buildTreeInMemory();
121 }
122 else
123 {
124 m_modelList = modelList;
125 m_srcDirTree->clear();
126 m_destDirTree->clear();
127 m_fileList->clear();
128 m_changesList->clear();
129 }
130 }
131
buildTreeInMemory()132 void KompareNavTreePart::buildTreeInMemory()
133 {
134 qCDebug(KOMPARENAVVIEW) << "BuildTreeInMemory called" ;
135
136 if (m_modelList->count() == 0)
137 {
138 qCDebug(KOMPARENAVVIEW) << "No models... weird shit..." ;
139 return; // avoids a crash on clear()
140 }
141
142 if (!m_info)
143 {
144 qCDebug(KOMPARENAVVIEW) << "No Info... weird shit..." ;
145 return;
146 }
147
148 QString srcBase;
149 QString destBase;
150
151 DiffModel* model;
152 model = m_modelList->first();
153 m_selectedModel = nullptr;
154
155 switch (m_info->mode)
156 {
157 case Kompare::ShowingDiff:
158 // BUG: 107489 No common root because it is a multi directory relative path diff
159 // We need to detect this and create a different rootitem / or so or should we always add this?
160 // Trouble we run into is that the directories do not start with a /
161 // so we have an unknown top root dir
162 // Thinking some more about it i guess it is best to use "" as base and simply show some string
163 // like Unknown filesystem path as root text but only in the case of dirs starting without a /
164 srcBase = model->sourcePath();
165 destBase = model->destinationPath();
166 // FIXME: these tests will not work on windows, we need something else
167 if (srcBase[0] != QLatin1Char('/'))
168 srcBase.clear();
169 if (destBase[0] != QLatin1Char('/'))
170 destBase.clear();
171 break;
172 case Kompare::ComparingFiles:
173 srcBase = model->sourcePath();
174 destBase = model->destinationPath();
175 break;
176 case Kompare::ComparingDirs:
177 srcBase = m_info->localSource;
178 if (!srcBase.endsWith(QLatin1Char('/')))
179 srcBase += QLatin1Char('/');
180 destBase = m_info->localDestination;
181 if (!destBase.endsWith(QLatin1Char('/')))
182 destBase += QLatin1Char('/');
183 break;
184 case Kompare::BlendingFile:
185 case Kompare::BlendingDir:
186 default:
187 qCDebug(KOMPARENAVVIEW) << "Oops needs to implement this..." ;
188 }
189
190 // qCDebug(KOMPARENAVVIEW) << "srcBase = " << srcBase ;
191 // qCDebug(KOMPARENAVVIEW) << "destBase = " << destBase ;
192
193 m_srcRootItem = new KDirLVI(m_srcDirTree, srcBase);
194 m_destRootItem = new KDirLVI(m_destDirTree, destBase);
195
196 QString srcPath;
197 QString destPath;
198
199 // Create the tree from the models
200 DiffModelListConstIterator modelIt = m_modelList->begin();
201 DiffModelListConstIterator mEnd = m_modelList->end();
202
203 for (; modelIt != mEnd; ++modelIt)
204 {
205 model = *modelIt;
206 srcPath = model->sourcePath();
207 destPath = model->destinationPath();
208
209 qCDebug(KOMPARENAVVIEW) << "srcPath = " << srcPath ;
210 qCDebug(KOMPARENAVVIEW) << "destPath = " << destPath ;
211 m_srcRootItem->addModel(srcPath, model, &m_modelToSrcDirItemDict);
212 m_destRootItem->addModel(destPath, model, &m_modelToDestDirItemDict);
213 }
214 // m_srcDirTree->setSelected( m_srcDirTree->firstChild(), true );
215 }
216
buildDirectoryTree()217 void KompareNavTreePart::buildDirectoryTree()
218 {
219 // FIXME: afaict this can be deleted
220 // qCDebug(KOMPARENAVVIEW) << "BuildDirTree called" ;
221 }
222
compareFromEndAndReturnSame(const QString & string1,const QString & string2)223 QString KompareNavTreePart::compareFromEndAndReturnSame(
224 const QString& string1,
225 const QString& string2)
226 {
227 QString result;
228
229 int srcLen = string1.length();
230 int destLen = string2.length();
231
232 while (srcLen != 0 && destLen != 0)
233 {
234 if (string1[--srcLen] == string2[--destLen])
235 result.prepend(string1[srcLen]);
236 else
237 break;
238 }
239
240 if (srcLen != 0 && destLen != 0 && result.startsWith(QLatin1Char('/')))
241 result = result.remove(0, 1); // strip leading /, we need it later
242
243 return result;
244 }
245
slotSetSelection(const DiffModel * model,const Difference * diff)246 void KompareNavTreePart::slotSetSelection(const DiffModel* model, const Difference* diff)
247 {
248 qCDebug(KOMPARENAVVIEW) << "KompareNavTreePart::slotSetSelection model = " << model << ", diff = " << diff ;
249 if (model == m_selectedModel)
250 {
251 // model is the same, so no need to update that...
252 if (diff != m_selectedDifference)
253 {
254 m_selectedDifference = diff;
255 setSelectedDifference(diff);
256 }
257 return;
258 }
259
260 // model is different so we need to find the right dirs, file and changeitems to select
261 // if m_selectedModel == NULL then everything needs to be done as well
262 if (!m_selectedModel || model->sourcePath() != m_selectedModel->sourcePath())
263 { // dirs are different, so we need to update the dirviews as well
264 m_selectedModel = model;
265 m_selectedDifference = diff;
266
267 setSelectedDir(model);
268 setSelectedFile(model);
269 setSelectedDifference(diff);
270 return;
271 }
272
273 if (!m_selectedModel || model->sourceFile() != m_selectedModel->sourceFile())
274 {
275 m_selectedModel = model;
276 setSelectedFile(model);
277
278 m_selectedDifference = diff;
279 setSelectedDifference(diff);
280 }
281 }
282
setSelectedDir(const DiffModel * model)283 void KompareNavTreePart::setSelectedDir(const DiffModel* model)
284 {
285 KDirLVI* currentDir;
286 currentDir = m_modelToSrcDirItemDict[ model ];
287 qCDebug(KOMPARENAVVIEW) << "Manually setting selection in srcdirtree with currentDir = " << currentDir ;
288 m_srcDirTree->blockSignals(true);
289 m_srcDirTree->setCurrentItem(currentDir);
290 m_srcDirTree->scrollToItem(currentDir);
291 m_srcDirTree->blockSignals(false);
292
293 currentDir = m_modelToDestDirItemDict[ model ];
294 qCDebug(KOMPARENAVVIEW) << "Manually setting selection in destdirtree with currentDir = " << currentDir ;
295 m_destDirTree->blockSignals(true);
296 m_destDirTree->setCurrentItem(currentDir);
297 m_destDirTree->scrollToItem(currentDir);
298 m_destDirTree->blockSignals(false);
299
300 m_fileList->blockSignals(true);
301 currentDir->fillFileList(m_fileList, &m_modelToFileItemDict);
302 m_fileList->blockSignals(false);
303 }
304
setSelectedFile(const DiffModel * model)305 void KompareNavTreePart::setSelectedFile(const DiffModel* model)
306 {
307 KFileLVI* currentFile;
308 currentFile = m_modelToFileItemDict[ model ];
309 qCDebug(KOMPARENAVVIEW) << "Manually setting selection in filelist" ;
310 m_fileList->blockSignals(true);
311 m_fileList->setCurrentItem(currentFile);
312 m_fileList->scrollToItem(currentFile);
313 m_fileList->blockSignals(false);
314
315 m_changesList->blockSignals(true);
316 currentFile->fillChangesList(m_changesList, &m_diffToChangeItemDict);
317 m_changesList->blockSignals(false);
318 }
319
setSelectedDifference(const Difference * diff)320 void KompareNavTreePart::setSelectedDifference(const Difference* diff)
321 {
322 KChangeLVI* currentDiff;
323 currentDiff = m_diffToChangeItemDict[ diff ];
324 qCDebug(KOMPARENAVVIEW) << "Manually setting selection in changeslist to " << currentDiff ;
325 m_changesList->blockSignals(true);
326 m_changesList->setCurrentItem(currentDiff);
327 m_changesList->scrollToItem(currentDiff);
328 m_changesList->blockSignals(false);
329 }
330
slotSetSelection(const Difference * diff)331 void KompareNavTreePart::slotSetSelection(const Difference* diff)
332 {
333 // qCDebug(KOMPARENAVVIEW) << "Scotty i need more power !!" ;
334 if (m_selectedDifference != diff)
335 {
336 // qCDebug(KOMPARENAVVIEW) << "But sir, i am giving you all she's got" ;
337 m_selectedDifference = diff;
338 setSelectedDifference(diff);
339 }
340 }
341
slotSrcDirTreeSelectionChanged(QTreeWidgetItem * item)342 void KompareNavTreePart::slotSrcDirTreeSelectionChanged(QTreeWidgetItem* item)
343 {
344 if (!item)
345 return;
346
347 qCDebug(KOMPARENAVVIEW) << "Sent by the sourceDirectoryTree with item = " << item ;
348 m_srcDirTree->scrollToItem(item);
349 KDirLVI* dir = static_cast<KDirLVI*>(item);
350 // order the dest tree view to set its selected item to the same as here
351 QString path;
352 // We start with an empty path and after the call path contains the full path
353 path = dir->fullPath(path);
354 KDirLVI* selItem = m_destRootItem->setSelected(path);
355 m_destDirTree->blockSignals(true);
356 m_destDirTree->setCurrentItem(selItem);
357 m_destDirTree->scrollToItem(selItem);
358 m_destDirTree->blockSignals(false);
359 // fill the changes list
360 dir->fillFileList(m_fileList, &m_modelToFileItemDict);
361 }
362
slotDestDirTreeSelectionChanged(QTreeWidgetItem * item)363 void KompareNavTreePart::slotDestDirTreeSelectionChanged(QTreeWidgetItem* item)
364 {
365 if (!item)
366 return;
367
368 qCDebug(KOMPARENAVVIEW) << "Sent by the destinationDirectoryTree with item = " << item ;
369 m_destDirTree->scrollToItem(item);
370 KDirLVI* dir = static_cast<KDirLVI*>(item);
371 // order the src tree view to set its selected item to the same as here
372 QString path;
373 // We start with an empty path and after the call path contains the full path
374 path = dir->fullPath(path);
375 KDirLVI* selItem = m_srcRootItem->setSelected(path);
376 m_srcDirTree->blockSignals(true);
377 m_srcDirTree->setCurrentItem(selItem);
378 m_srcDirTree->scrollToItem(selItem);
379 m_srcDirTree->blockSignals(false);
380 // fill the changes list
381 dir->fillFileList(m_fileList, &m_modelToFileItemDict);
382 }
383
slotFileListSelectionChanged(QTreeWidgetItem * item)384 void KompareNavTreePart::slotFileListSelectionChanged(QTreeWidgetItem* item)
385 {
386 if (!item)
387 return;
388
389 qCDebug(KOMPARENAVVIEW) << "Sent by the fileList with item = " << item ;
390
391 KFileLVI* file = static_cast<KFileLVI*>(item);
392 m_selectedModel = file->model();
393 m_changesList->blockSignals(true);
394 file->fillChangesList(m_changesList, &m_diffToChangeItemDict);
395 m_changesList->blockSignals(false);
396
397 if (m_changesList->currentItem())
398 {
399 // FIXME: This is ugly...
400 m_selectedDifference = (static_cast<KChangeLVI*>(m_changesList->currentItem()))->difference();
401 }
402
403 Q_EMIT selectionChanged(m_selectedModel, m_selectedDifference);
404 }
405
slotChangesListSelectionChanged(QTreeWidgetItem * item)406 void KompareNavTreePart::slotChangesListSelectionChanged(QTreeWidgetItem* item)
407 {
408 if (!item)
409 return;
410
411 qCDebug(KOMPARENAVVIEW) << "Sent by the changesList" ;
412
413 KChangeLVI* change = static_cast<KChangeLVI*>(item);
414 m_selectedDifference = change->difference();
415
416 Q_EMIT selectionChanged(m_selectedDifference);
417 }
418
slotApplyDifference(bool)419 void KompareNavTreePart::slotApplyDifference(bool /*apply*/)
420 {
421 KChangeLVI* clvi = m_diffToChangeItemDict[m_selectedDifference];
422 if (clvi)
423 clvi->setDifferenceText();
424 }
425
slotApplyAllDifferences(bool)426 void KompareNavTreePart::slotApplyAllDifferences(bool /*apply*/)
427 {
428 QHash<const Diff2::Difference*, KChangeLVI*>::ConstIterator it = m_diffToChangeItemDict.constBegin();
429 QHash<const Diff2::Difference*, KChangeLVI*>::ConstIterator end = m_diffToChangeItemDict.constEnd();
430
431 qCDebug(KOMPARENAVVIEW) << "m_diffToChangeItemDict.count() = " << m_diffToChangeItemDict.count() ;
432
433 for (; it != end ; ++it)
434 {
435 it.value()->setDifferenceText();
436 }
437 }
438
slotApplyDifference(const Difference * diff,bool)439 void KompareNavTreePart::slotApplyDifference(const Difference* diff, bool /*apply*/)
440 {
441 // this applies to the currently selected difference
442 KChangeLVI* clvi = m_diffToChangeItemDict[diff];
443 if (clvi)
444 clvi->setDifferenceText();
445 }
446
setDifferenceText()447 void KChangeLVI::setDifferenceText()
448 {
449 QString text;
450 switch (m_difference->type()) {
451 case Difference::Change:
452 // Shouldn't this simply be diff->sourceLineCount() ?
453 // because you change the _number of lines_ lines in source, not in dest
454 if (m_difference->applied())
455 text = i18np("Applied: Changes made to %1 line undone", "Applied: Changes made to %1 lines undone",
456 m_difference->sourceLineCount());
457 else
458 text = i18np("Changed %1 line", "Changed %1 lines",
459 m_difference->sourceLineCount());
460 break;
461 case Difference::Insert:
462 if (m_difference->applied())
463 text = i18np("Applied: Insertion of %1 line undone", "Applied: Insertion of %1 lines undone",
464 m_difference->destinationLineCount());
465 else
466 text = i18np("Inserted %1 line", "Inserted %1 lines",
467 m_difference->destinationLineCount());
468 break;
469 case Difference::Delete:
470 if (m_difference->applied())
471 text = i18np("Applied: Deletion of %1 line undone", "Applied: Deletion of %1 lines undone",
472 m_difference->sourceLineCount());
473 else
474 text = i18np("Deleted %1 line", "Deleted %1 lines",
475 m_difference->sourceLineCount());
476 break;
477 default:
478 qCDebug(KOMPARENAVVIEW) << "Unknown or Unchanged enum value when checking for diff->type() in KChangeLVI's constructor" ;
479 text.clear();
480 }
481
482 setText(2, text);
483 }
484
KChangeLVI(QTreeWidget * parent,Difference * diff)485 KChangeLVI::KChangeLVI(QTreeWidget* parent, Difference* diff) : QTreeWidgetItem(parent)
486 {
487 m_difference = diff;
488
489 setText(0, QString::number(diff->sourceLineNumber()));
490 setText(1, QString::number(diff->destinationLineNumber()));
491
492 setDifferenceText();
493 }
494
operator <(const QTreeWidgetItem & item) const495 bool KChangeLVI::operator<(const QTreeWidgetItem& item) const
496 {
497 int column = treeWidget()->sortColumn();
498 QString text1 = text(column);
499 QString text2 = item.text(column);
500
501 // Compare the numbers.
502 if (column < 2 && text1.length() != text2.length())
503 return text1.length() < text2.length();
504 return text1 < text2;
505 }
506
~KChangeLVI()507 KChangeLVI::~KChangeLVI()
508 {
509 }
510
KFileLVI(QTreeWidget * parent,DiffModel * model)511 KFileLVI::KFileLVI(QTreeWidget* parent, DiffModel* model) : QTreeWidgetItem(parent)
512 {
513 m_model = model;
514
515 QString src = model->sourceFile();
516 QString dst = model->destinationFile();
517
518 setText(0, src);
519 setText(1, dst);
520 setIcon(0, QIcon::fromTheme(getIcon(src)));
521 setIcon(1, QIcon::fromTheme(getIcon(dst)));
522 }
523
hasExtension(const QString & extensions,const QString & fileName)524 bool KFileLVI::hasExtension(const QString& extensions, const QString& fileName)
525 {
526 const QStringList extList = extensions.split(QLatin1Char(' '));
527 for (const QString& ext : extList) {
528 if (fileName.endsWith(ext, Qt::CaseInsensitive)) {
529 return true;
530 }
531 }
532 return false;
533 }
534
getIcon(const QString & fileName)535 const QString KFileLVI::getIcon(const QString& fileName)
536 {
537 // C++, C
538 if (hasExtension(QStringLiteral(".h .hpp"), fileName)) {
539 return QStringLiteral("text-x-c++hdr");
540 }
541 if (hasExtension(QStringLiteral(".cpp"), fileName)) {
542 return QStringLiteral("text-x-c++src");
543 }
544 if (hasExtension(QStringLiteral(".c"), fileName)) {
545 return QStringLiteral("text-x-csrc");
546 }
547 // Python
548 if (hasExtension(QStringLiteral(".py .pyw"), fileName)) {
549 return QStringLiteral("text-x-python");
550 }
551 // C#
552 if (hasExtension(QStringLiteral(".cs"), fileName)) {
553 return QStringLiteral("text-x-csharp");
554 }
555 // Objective-C
556 if (hasExtension(QStringLiteral(".m"), fileName)) {
557 return QStringLiteral("text-x-objcsrc");
558 }
559 // Java
560 if (hasExtension(QStringLiteral(".java"), fileName)) {
561 return QStringLiteral("text-x-java");
562 }
563 // Script
564 if (hasExtension(QStringLiteral(".sh"), fileName)) {
565 return QStringLiteral("text-x-script");
566 }
567 // Makefile
568 if (hasExtension(QStringLiteral(".cmake Makefile"), fileName)) {
569 return QStringLiteral("text-x-makefile");
570 }
571 // Ada
572 if (hasExtension(QStringLiteral(".ada .ads .adb"), fileName)) {
573 return QStringLiteral("text-x-adasrc");
574 }
575 // Pascal
576 if (hasExtension(QStringLiteral(".pas"), fileName)) {
577 return QStringLiteral("text-x-pascal");
578 }
579 // Patch
580 if (hasExtension(QStringLiteral(".diff"), fileName)) {
581 return QStringLiteral("text-x-patch");
582 }
583 // Tcl
584 if (hasExtension(QStringLiteral(".tcl"), fileName)) {
585 return QStringLiteral("text-x-tcl");
586 }
587 // Text
588 if (hasExtension(QStringLiteral(".txt"), fileName)) {
589 return QStringLiteral("text-plain");
590 }
591 // Xml
592 if (hasExtension(QStringLiteral(".xml"), fileName)) {
593 return QStringLiteral("text-xml");
594 }
595 // unknown or no file extension
596 return QStringLiteral("text-plain");
597 }
598
fillChangesList(QTreeWidget * changesList,QHash<const Diff2::Difference *,KChangeLVI * > * diffToChangeItemDict)599 void KFileLVI::fillChangesList(QTreeWidget* changesList, QHash<const Diff2::Difference*, KChangeLVI*>* diffToChangeItemDict)
600 {
601 changesList->clear();
602 diffToChangeItemDict->clear();
603
604 DifferenceListConstIterator diffIt = m_model->differences()->constBegin();
605 DifferenceListConstIterator dEnd = m_model->differences()->constEnd();
606
607 for (; diffIt != dEnd; ++diffIt)
608 {
609 KChangeLVI* change = new KChangeLVI(changesList, *diffIt);
610 diffToChangeItemDict->insert(*diffIt, change);
611 }
612
613 changesList->setCurrentItem(changesList->topLevelItem(0));
614 }
615
~KFileLVI()616 KFileLVI::~KFileLVI()
617 {
618 }
619
KDirLVI(QTreeWidget * parent,const QString & dir)620 KDirLVI::KDirLVI(QTreeWidget* parent, const QString& dir) : QTreeWidgetItem(parent)
621 {
622 // qCDebug(KOMPARENAVVIEW) << "KDirLVI (QTreeWidget) constructor called with dir = " << dir ;
623 m_rootItem = true;
624 m_dirName = dir;
625 setIcon(0, QIcon::fromTheme(QStringLiteral("folder")));
626 setExpanded(true);
627 if (m_dirName.isEmpty())
628 setText(0, i18nc("@item directory name not known", "Unknown"));
629 else
630 setText(0, m_dirName);
631 }
632
KDirLVI(KDirLVI * parent,const QString & dir)633 KDirLVI::KDirLVI(KDirLVI* parent, const QString& dir) : QTreeWidgetItem(parent)
634 {
635 // qCDebug(KOMPARENAVVIEW) << "KDirLVI (KDirLVI) constructor called with dir = " << dir ;
636 m_rootItem = false;
637 m_dirName = dir;
638 setIcon(0, QIcon::fromTheme(QStringLiteral("folder")));
639 setExpanded(true);
640 setText(0, m_dirName);
641 }
642
643 // addModel always removes it own path from the beginning
addModel(QString & path,DiffModel * model,QHash<const Diff2::DiffModel *,KDirLVI * > * modelToDirItemDict)644 void KDirLVI::addModel(QString& path, DiffModel* model, QHash<const Diff2::DiffModel*, KDirLVI*>* modelToDirItemDict)
645 {
646 // qCDebug(KOMPARENAVVIEW) << "KDirLVI::addModel called with path = " << path << " from KDirLVI with m_dirName = " << m_dirName ;
647
648 if (!m_dirName.isEmpty())
649 {
650 if (path.indexOf(m_dirName) > -1)
651 path = path.remove(path.indexOf(m_dirName), m_dirName.length());
652 }
653
654 // qCDebug(KOMPARENAVVIEW) << "Path after removal of own dir (\"" << m_dirName << "\") = " << path ;
655
656 if (path.isEmpty()) {
657 m_modelList.append(model);
658 modelToDirItemDict->insert(model, this);
659 return;
660 }
661
662 KDirLVI* child;
663
664 QString dir = path.mid(0, path.indexOf(QLatin1Char('/'), 0) + 1);
665 child = findChild(dir);
666 if (!child)
667 {
668 // does not exist yet so make it
669 // qCDebug(KOMPARENAVVIEW) << "KDirLVI::addModel creating new KDirLVI because not found" ;
670 child = new KDirLVI(this, dir);
671 }
672
673 child->addModel(path, model, modelToDirItemDict);
674 }
675
findChild(const QString & dir)676 KDirLVI* KDirLVI::findChild(const QString& dir)
677 {
678 // qCDebug(KOMPARENAVVIEW) << "KDirLVI::findChild called with dir = " << dir ;
679 KDirLVI* child;
680 if ((child = static_cast<KDirLVI*>(this->child(0))) != nullptr)
681 { // has children, check if dir already exists, if so addModel
682 QTreeWidgetItemIterator it(child);
683 while (*it) {
684 child = static_cast<KDirLVI*>(*it);
685
686 if (dir == child->dirName())
687 return child;
688 ++it;
689 }
690 }
691
692 return nullptr;
693 }
694
fillFileList(QTreeWidget * fileList,QHash<const Diff2::DiffModel *,KFileLVI * > * modelToFileItemDict)695 void KDirLVI::fillFileList(QTreeWidget* fileList, QHash<const Diff2::DiffModel*, KFileLVI*>* modelToFileItemDict)
696 {
697 fileList->clear();
698
699 DiffModelListIterator modelIt = m_modelList.begin();
700 DiffModelListIterator mEnd = m_modelList.end();
701 for (; modelIt != mEnd; ++modelIt)
702 {
703 KFileLVI* file = new KFileLVI(fileList, *modelIt);
704 modelToFileItemDict->insert(*modelIt, file);
705 }
706
707 fileList->setCurrentItem(fileList->topLevelItem(0));
708 }
709
fullPath(QString & path)710 QString KDirLVI::fullPath(QString& path)
711 {
712 // if (!path.isEmpty())
713 // qCDebug(KOMPARENAVVIEW) << "KDirLVI::fullPath called with path = " << path ;
714 // else
715 // qCDebug(KOMPARENAVVIEW) << "KDirLVI::fullPath called with empty path..." ;
716
717 if (m_rootItem) // don't bother adding rootItem's dir...
718 return path;
719
720 path = path.prepend(m_dirName);
721
722 KDirLVI* lviParent = dynamic_cast<KDirLVI*>(parent());
723 if (lviParent)
724 {
725 path = lviParent->fullPath(path);
726 }
727
728 return path;
729 }
730
setSelected(const QString & _dir)731 KDirLVI* KDirLVI::setSelected(const QString& _dir)
732 {
733 QString dir(_dir);
734 // qCDebug(KOMPARENAVVIEW) << "KDirLVI::setSelected called with dir = " << dir ;
735
736 // root item's dirName is never taken into account... remember that
737 if (!m_rootItem)
738 {
739 dir = dir.remove(0, m_dirName.length());
740 }
741
742 if (dir.isEmpty())
743 {
744 return this;
745 }
746 KDirLVI* child = static_cast<KDirLVI*>(this->child(0));
747 if (!child)
748 return nullptr;
749
750 QTreeWidgetItemIterator it(child);
751 while (*it) {
752 child = static_cast<KDirLVI*>(*it);
753
754 if (dir.startsWith(child->dirName()))
755 return child->setSelected(dir);
756 ++it;
757 }
758
759 return nullptr;
760 }
761
~KDirLVI()762 KDirLVI::~KDirLVI()
763 {
764 m_modelList.clear();
765 }
766
767 K_PLUGIN_FACTORY_WITH_JSON(KompareNavTreePartFactory, "komparenavtreepart.json",
768 registerPlugin<KompareNavTreePart>();)
769
770 #include "komparenavtreepart.moc"
771