comparison kdiff3/src-QT4/directorymergewindow.cpp @ 75:08ea9b86c12c

KDiff3-0.9.91
author joachim99
date Sat, 04 Nov 2006 00:05:00 +0000
parents kdiff3/src/directorymergewindow.cpp@f7dad0aa6146
children 1184fc843210
comparison
equal deleted inserted replaced
74:069521efec1a 75:08ea9b86c12c
1 /***************************************************************************
2 directorymergewindow.cpp
3 -----------------
4 begin : Sat Oct 19 2002
5 copyright : (C) 2002-2005 by Joachim Eibl
6 email : joachim.eibl at gmx.de
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "directorymergewindow.h"
19 #include "optiondialog.h"
20 #include <vector>
21 #include <map>
22
23 #include <QDir>
24 #include <QApplication>
25 #include <QPixmap>
26 #include <QImage>
27 #include <QTextStream>
28 #include <QKeyEvent>
29 #include <QMenu>
30 #include <QRegExp>
31 #include <QMessageBox>
32 #include <QLayout>
33 #include <QLabel>
34 #include <QSplitter>
35 #include <QTextEdit>
36 #include <QItemDelegate>
37
38
39 #include <kpopupmenu.h>
40 #include <kaction.h>
41 #include <kmessagebox.h>
42 #include <kfiledialog.h>
43 #include <kiconloader.h>
44 #include <klocale.h>
45
46 #include <iostream>
47 #include <assert.h>
48 //#include <konq_popupmenu.h>
49
50 static bool conflictingFileTypes(MergeFileInfos& mfi);
51 /*
52 class StatusInfo : public QListView
53 {
54 public:
55 StatusInfo(QWidget* pParent) : QListView( pParent, "StatusInfo", Qt::WShowModal )
56 {
57 addColumn("");
58 setSorting(-1); //disable sorting
59 }
60
61 QListViewItem* m_pLast;
62 QListViewItem* last()
63 {
64 if (firstChild()==0) return 0;
65 else return m_pLast;
66 }
67
68 void addText(const QString& s )
69 {
70 if (firstChild()==0) m_pLast = new QListViewItem( this, s );
71 else m_pLast = new QListViewItem( this, last(), s );
72 }
73 };
74 */
75 class StatusInfo : public QTextEdit
76 {
77 public:
78 StatusInfo(QWidget* pParent) : QTextEdit( pParent )
79 {
80 setObjectName("StatusInfo");
81 setWindowFlags(Qt::Dialog);
82 setWordWrapMode(QTextOption::NoWrap);
83 setReadOnly(true);
84 showMaximized();
85 }
86
87 bool isEmpty(){
88 return toPlainText().isEmpty(); }
89
90 void addText(const QString& s )
91 {
92 append(s);
93 }
94
95 void show()
96 {
97 moveCursor ( QTextCursor::End );
98 ensureCursorVisible();
99
100 QTextEdit::show();
101 }
102 };
103
104
105 class TempRemover
106 {
107 public:
108 TempRemover( const QString& origName, FileAccess& fa );
109 ~TempRemover();
110 QString name() { return m_name; }
111 bool success() { return m_bSuccess; }
112 private:
113 QString m_name;
114 bool m_bTemp;
115 bool m_bSuccess;
116 };
117 TempRemover::TempRemover(const QString& origName, FileAccess& fa)
118 {
119 if ( fa.isLocal() )
120 {
121 m_name = origName;
122 m_bTemp = false;
123 m_bSuccess = true;
124 }
125 else
126 {
127 m_name = FileAccess::tempFileName();
128 m_bSuccess = fa.copyFile( m_name );
129 m_bTemp = m_bSuccess;
130 }
131 }
132 TempRemover::~TempRemover()
133 {
134 if ( m_bTemp && ! m_name.isEmpty() )
135 FileAccess::removeTempFile(m_name);
136 }
137
138 void DirectoryMergeWindow::fastFileComparison(
139 FileAccess& fi1, FileAccess& fi2,
140 bool& bEqual, bool& bError, QString& status )
141 {
142 ProgressProxy pp;
143 status = "";
144 bEqual = false;
145 bError = true;
146
147 if ( !m_bFollowFileLinks )
148 {
149 if ( fi1.isSymLink() != fi2.isSymLink() )
150 {
151 status = i18n("Mix of links and normal files.");
152 return;
153 }
154 else if ( fi1.isSymLink() && fi2.isSymLink() )
155 {
156 bError = false;
157 bEqual = fi1.readLink() == fi2.readLink();
158 status = i18n("Link: ");
159 return;
160 }
161 }
162
163 if ( fi1.size()!=fi2.size() )
164 {
165 bEqual = false;
166 status = i18n("Size. ");
167 return;
168 }
169 else if ( m_pOptions->m_bDmTrustSize )
170 {
171 bEqual = true;
172 return;
173 }
174
175 if ( m_pOptions->m_bDmTrustDate )
176 {
177 bEqual = ( fi1.lastModified() == fi2.lastModified() && fi1.size()==fi2.size() );
178 bError = false;
179 status = i18n("Date & Size: ");
180 return;
181 }
182
183 QString fileName1 = fi1.absFilePath();
184 QString fileName2 = fi2.absFilePath();
185 TempRemover tr1( fileName1, fi1 );
186 if ( !tr1.success() )
187 {
188 status = i18n("Creating temp copy of %1 failed.").arg(fileName1);
189 return;
190 }
191 TempRemover tr2( fileName2, fi2 );
192 if ( !tr2.success() )
193 {
194 status = i18n("Creating temp copy of %1 failed.").arg(fileName2);
195 return;
196 }
197
198 std::vector<char> buf1(100000);
199 std::vector<char> buf2(buf1.size());
200
201 QFile file1( tr1.name() );
202
203 if ( ! file1.open(QIODevice::ReadOnly) )
204 {
205 status = i18n("Opening %1 failed.").arg(fileName1);
206 return;
207 }
208
209 QFile file2( tr2.name() );
210
211 if ( ! file2.open(QIODevice::ReadOnly) )
212 {
213 status = i18n("Opening %1 failed.").arg(fileName2);
214 return;
215 }
216
217 pp.setInformation( i18n("Comparing file..."), 0, false );
218 typedef qint64 t_FileSize;
219 t_FileSize fullSize = file1.size();
220 t_FileSize sizeLeft = fullSize;
221
222 while( sizeLeft>0 && ! pp.wasCancelled() )
223 {
224 int len = min2( sizeLeft, (t_FileSize)buf1.size() );
225 if( len != file1.read( &buf1[0], len ) )
226 {
227 status = i18n("Error reading from %1").arg(fileName1);
228 return;
229 }
230
231 if( len != file2.read( &buf2[0], len ) )
232 {
233 status = i18n("Error reading from %1").arg(fileName2);
234 return;
235 }
236
237 if ( memcmp( &buf1[0], &buf2[0], len ) != 0 )
238 {
239 bError = false;
240 return;
241 }
242 sizeLeft-=len;
243 pp.setCurrent(double(fullSize-sizeLeft)/fullSize, false );
244 }
245
246 // If the program really arrives here, then the files are really equal.
247 bError = false;
248 bEqual = true;
249 }
250
251 static int s_NameCol = 0;
252 static int s_ACol = 1;
253 static int s_BCol = 2;
254 static int s_CCol = 3;
255 static int s_OpCol = 4;
256 static int s_OpStatusCol = 5;
257 static int s_UnsolvedCol = 6; // Nr of unsolved conflicts (for 3 input files)
258 static int s_SolvedCol = 7; // Nr of auto-solvable conflicts (for 3 input files)
259 static int s_NonWhiteCol = 8; // Nr of nonwhite deltas (for 2 input files)
260 static int s_WhiteCol = 9; // Nr of white deltas (for 2 input files)
261
262 // Previously Q3ListViewItem::paintCell(p,cg,column,width,align);
263 class DirectoryMergeWindow::DirMergeItemDelegate : public QItemDelegate
264 {
265 DirectoryMergeWindow* m_pDMW;
266 public:
267 DirMergeItemDelegate(DirectoryMergeWindow* pParent)
268 : QItemDelegate(pParent), m_pDMW(pParent)
269 {
270 }
271 void paint( QPainter * p, const QStyleOptionViewItem & option, const QModelIndex & index ) const
272 {
273 int column = index.column();
274 if (column == s_ACol || column == s_BCol || column == s_CCol )
275 {
276 QVariant value = index.data( Qt::DecorationRole );
277 QPixmap icon;
278 if ( value.isValid() )
279 {
280 if (value.type() == QVariant::Icon)
281 {
282 icon = qvariant_cast<QIcon>(value).pixmap(16,16);
283 //icon = qvariant_cast<QIcon>(value);
284 //decorationRect = QRect(QPoint(0, 0), icon.actualSize(option.decorationSize, iconMode, iconState));
285 }
286 else
287 {
288 icon = qvariant_cast<QPixmap>(value);
289 //decorationRect = QRect(QPoint(0, 0), option.decorationSize).intersected(pixmap.rect());
290 }
291 }
292
293 int x = option.rect.left();
294 int y = option.rect.top();
295 //QPixmap icon = value.value<QPixmap>(); //pixmap(column);
296 if ( !icon.isNull() )
297 {
298 int yOffset = (sizeHint(option,index).height() - icon.height()) / 2;
299 p->drawPixmap( x+2, y+yOffset, icon );
300
301 QTreeWidgetItem* pTWI = reinterpret_cast<QTreeWidgetItem*>( index.internalPointer() );
302 DirMergeItem* pDMI = static_cast<DirMergeItem*>(pTWI);
303 int i = pDMI==m_pDMW->m_pSelection1Item && column == m_pDMW->m_selection1Column ? 1 :
304 pDMI==m_pDMW->m_pSelection2Item && column == m_pDMW->m_selection2Column ? 2 :
305 pDMI==m_pDMW->m_pSelection3Item && column == m_pDMW->m_selection3Column ? 3 :
306 0;
307 if ( i!=0 )
308 {
309 OptionDialog* pOD = m_pDMW->m_pOptions;
310 QColor c ( i==1 ? pOD->m_colorA : i==2 ? pOD->m_colorB : pOD->m_colorC );
311 p->setPen( c );// highlight() );
312 p->drawRect( x+2, y+yOffset, icon.width(), icon.height());
313 p->setPen( QPen( c, 0, Qt::DotLine) );
314 p->drawRect( x+1, y+yOffset-1, icon.width()+2, icon.height()+2);
315 p->setPen( Qt::white );
316 QString s( QChar('A'+i-1) );
317 p->drawText( x+2 + (icon.width() - p->fontMetrics().width(s))/2,
318 y+yOffset + (icon.height() + p->fontMetrics().ascent())/2-1,
319 s );
320 }
321 else
322 {
323 p->setPen( m_pDMW->palette().background().color() );
324 p->drawRect( x+1, y+yOffset-1, icon.width()+2, icon.height()+2);
325 }
326 return;
327 }
328 }
329 QStyleOptionViewItem option2 = option;
330 if ( column>=s_UnsolvedCol )
331 {
332 option2.displayAlignment = Qt::AlignRight;
333 }
334 QItemDelegate::paint( p, option2, index );
335 }
336 };
337
338
339 DirectoryMergeWindow::DirectoryMergeWindow( QWidget* pParent, OptionDialog* pOptions, KIconLoader* pIconLoader )
340 : QTreeWidget( pParent )
341 {
342 setItemDelegate( new DirMergeItemDelegate(this) );
343 connect( this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(onDoubleClick(QTreeWidgetItem*)));
344 connect( this, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(onCurrentChanged(QTreeWidgetItem*)));
345 connect( this, SIGNAL(expanded(const QModelIndex&)), this, SLOT(onExpanded()));
346 m_pOptions = pOptions;
347 m_pIconLoader = pIconLoader;
348 m_pDirectoryMergeInfo = 0;
349 m_bAllowResizeEvents = true;
350 m_bSimulatedMergeStarted=false;
351 m_bRealMergeStarted=false;
352 m_bError = false;
353 m_bSyncMode = false;
354 m_pStatusInfo = new StatusInfo(0);
355 m_pStatusInfo->hide();
356 m_bScanning = false;
357 m_pSelection1Item = 0;
358 m_pSelection2Item = 0;
359 m_pSelection3Item = 0;
360 m_bCaseSensitive = true;
361
362 QStringList sl;
363 sl << i18n("Name") << "A" << "B" << "C" << i18n("Operation") << i18n("Status")
364 << i18n("Unsolved") << i18n("Solved") << i18n("Nonwhite") << i18n("White") << "";
365 setHeaderLabels(sl);
366
367 //TODO setColumnAlignment( s_UnsolvedCol, Qt::AlignRight );
368 //setColumnAlignment( s_SolvedCol, Qt::AlignRight );
369 //setColumnAlignment( s_NonWhiteCol, Qt::AlignRight );
370 //setColumnAlignment( s_WhiteCol, Qt::AlignRight );
371 }
372
373 DirectoryMergeWindow::~DirectoryMergeWindow()
374 {
375 }
376
377
378 int DirectoryMergeWindow::totalColumnWidth()
379 {
380 int w=0;
381 for (int i=0; i<s_OpStatusCol; ++i)
382 {
383 w += columnWidth(i);
384 }
385 return w;
386 }
387
388 void DirectoryMergeWindow::reload()
389 {
390 if ( isDirectoryMergeInProgress() )
391 {
392 int result = KMessageBox::warningYesNo(this,
393 i18n("You are currently doing a directory merge. Are you sure, you want to abort the merge and rescan the directory?"),
394 i18n("Warning"), i18n("Rescan"), i18n("Continue Merging") );
395 if ( result!=KMessageBox::Yes )
396 return;
397 }
398
399 init( m_dirA, m_dirB, m_dirC, m_dirDest, m_bDirectoryMerge );
400 }
401
402 // Copy pm2 onto pm1, but preserve the alpha value from pm1 where pm2 is transparent.
403 static QPixmap pixCombiner( const QPixmap* pm1, const QPixmap* pm2 )
404 {
405 QImage img1 = pm1->toImage().convertToFormat(QImage::Format_ARGB32);
406 QImage img2 = pm2->toImage().convertToFormat(QImage::Format_ARGB32);
407
408 for (int y = 0; y < img1.height(); y++)
409 {
410 quint32 *line1 = reinterpret_cast<quint32 *>(img1.scanLine(y));
411 quint32 *line2 = reinterpret_cast<quint32 *>(img2.scanLine(y));
412 for (int x = 0; x < img1.width(); x++)
413 {
414 if ( qAlpha( line2[x] ) >0 )
415 line1[x] = (line2[x] | 0xff000000);
416 }
417 }
418 return QPixmap::fromImage(img1);
419 }
420
421 // like pixCombiner but let the pm1 color shine through
422 static QPixmap pixCombiner2( const QPixmap* pm1, const QPixmap* pm2 )
423 {
424 QPixmap pix=*pm1;
425 QPainter p(&pix);
426 p.setOpacity(0.5);
427 p.drawPixmap( 0,0,*pm2 );
428 p.end();
429
430 return pix;
431 }
432
433 static void calcDirStatus( bool bThreeDirs, DirMergeItem* i, int& nofFiles,
434 int& nofDirs, int& nofEqualFiles, int& nofManualMerges )
435 {
436 if ( i->m_pMFI->m_bDirA || i->m_pMFI->m_bDirB || i->m_pMFI->m_bDirC )
437 {
438 ++nofDirs;
439 }
440 else
441 {
442 ++nofFiles;
443 if ( i->m_pMFI->m_bEqualAB && (!bThreeDirs || i->m_pMFI->m_bEqualAC ))
444 {
445 ++nofEqualFiles;
446 }
447 else
448 {
449 if ( i->m_pMFI->m_eMergeOperation==eMergeABCToDest || i->m_pMFI->m_eMergeOperation==eMergeABToDest )
450 ++nofManualMerges;
451 }
452 }
453 for( int childIdx=0; childIdx<i->childCount(); ++childIdx )
454 calcDirStatus( bThreeDirs, static_cast<DirMergeItem*>(i->child(childIdx)), nofFiles, nofDirs, nofEqualFiles, nofManualMerges );
455 }
456
457 static QString sortString(const QString& s, bool bCaseSensitive)
458 {
459 if (bCaseSensitive)
460 return s;
461 else
462 return s.toUpper();
463 }
464
465 bool DirectoryMergeWindow::init
466 (
467 FileAccess& dirA,
468 FileAccess& dirB,
469 FileAccess& dirC,
470 FileAccess& dirDest,
471 bool bDirectoryMerge
472 )
473 {
474 if ( m_pOptions->m_bDmFullAnalysis )
475 {
476 // A full analysis uses the same ressources that a normal text-diff/merge uses.
477 // So make sure that the user saves his data first.
478 bool bCanContinue=false;
479 checkIfCanContinue( &bCanContinue );
480 if ( !bCanContinue )
481 return false;
482 startDiffMerge("","","","","","","",0); // hide main window
483 }
484
485 show();
486
487 ProgressProxy pp;
488 m_bFollowDirLinks = m_pOptions->m_bDmFollowDirLinks;
489 m_bFollowFileLinks = m_pOptions->m_bDmFollowFileLinks;
490 m_bSimulatedMergeStarted=false;
491 m_bRealMergeStarted=false;
492 m_bError=false;
493 m_bDirectoryMerge = bDirectoryMerge;
494 m_pSelection1Item = 0;
495 m_pSelection2Item = 0;
496 m_pSelection3Item = 0;
497 m_bCaseSensitive = m_pOptions->m_bDmCaseSensitiveFilenameComparison;
498
499 clear();
500
501 m_mergeItemList.clear();
502 m_currentItemForOperation = m_mergeItemList.end();
503
504 m_dirA = dirA;
505 m_dirB = dirB;
506 m_dirC = dirC;
507 m_dirDest = dirDest;
508
509 m_pDirShowIdenticalFiles->setChecked(true);
510 m_pDirShowDifferentFiles->setChecked(true);
511 m_pDirShowFilesOnlyInA->setChecked(true);
512 m_pDirShowFilesOnlyInB->setChecked(true);
513 m_pDirShowFilesOnlyInC->setChecked(true);
514
515 // Check if all input directories exist and are valid. The dest dir is not tested now.
516 // The test will happen only when we are going to write to it.
517 if ( !m_dirA.isDir() || !m_dirB.isDir() ||
518 (m_dirC.isValid() && !m_dirC.isDir()) )
519 {
520 QString text( i18n("Opening of directories failed:") );
521 text += "\n\n";
522 if ( !dirA.isDir() )
523 { text += i18n("Dir A \"%1\" does not exist or is not a directory.\n").arg(m_dirA.prettyAbsPath()); }
524
525 if ( !dirB.isDir() )
526 { text += i18n("Dir B \"%1\" does not exist or is not a directory.\n").arg(m_dirB.prettyAbsPath()); }
527
528 if ( m_dirC.isValid() && !m_dirC.isDir() )
529 { text += i18n("Dir C \"%1\" does not exist or is not a directory.\n").arg(m_dirC.prettyAbsPath()); }
530
531 KMessageBox::sorry( this, text, i18n("Directory Open Error") );
532 return false;
533 }
534
535 if ( m_dirC.isValid() &&
536 (m_dirDest.prettyAbsPath() == m_dirA.prettyAbsPath() || m_dirDest.prettyAbsPath()==m_dirB.prettyAbsPath() ) )
537 {
538 KMessageBox::error(this,
539 i18n( "The destination directory must not be the same as A or B when "
540 "three directories are merged.\nCheck again before continuing."),
541 i18n("Parameter Warning"));
542 return false;
543 }
544
545 m_bScanning = true;
546 statusBarMessage(i18n("Scanning directories..."));
547
548 m_bSyncMode = m_pOptions->m_bDmSyncMode && !m_dirC.isValid() && !m_dirDest.isValid();
549
550 if ( m_dirDest.isValid() )
551 m_dirDestInternal = m_dirDest;
552 else
553 m_dirDestInternal = m_dirC.isValid() ? m_dirC : m_dirB;
554
555 QString origCurrentDirectory = QDir::currentPath();
556
557 m_fileMergeMap.clear();
558 t_DirectoryList::iterator i;
559
560 // calc how many directories will be read:
561 double nofScans = ( m_dirA.isValid() ? 1 : 0 )+( m_dirB.isValid() ? 1 : 0 )+( m_dirC.isValid() ? 1 : 0 );
562 int currentScan = 0;
563
564 //TODO setColumnWidthMode(s_UnsolvedCol, Q3ListView::Manual);
565 // setColumnWidthMode(s_SolvedCol, Q3ListView::Manual);
566 // setColumnWidthMode(s_WhiteCol, Q3ListView::Manual);
567 // setColumnWidthMode(s_NonWhiteCol, Q3ListView::Manual);
568 setColumnHidden( s_CCol, !m_dirC.isValid() );
569 setColumnHidden( s_WhiteCol, !m_pOptions->m_bDmFullAnalysis );
570 setColumnHidden( s_NonWhiteCol, !m_pOptions->m_bDmFullAnalysis );
571 setColumnHidden( s_UnsolvedCol, !m_pOptions->m_bDmFullAnalysis );
572 setColumnHidden( s_SolvedCol, !( m_pOptions->m_bDmFullAnalysis && m_dirC.isValid() ) );
573
574 bool bListDirSuccessA = true;
575 bool bListDirSuccessB = true;
576 bool bListDirSuccessC = true;
577 if ( m_dirA.isValid() )
578 {
579 pp.setInformation(i18n("Reading Directory A"));
580 pp.setSubRangeTransformation(currentScan/nofScans, (currentScan+1)/nofScans);
581 ++currentScan;
582
583 t_DirectoryList dirListA;
584 bListDirSuccessA = m_dirA.listDir( &dirListA,
585 m_pOptions->m_bDmRecursiveDirs, m_pOptions->m_bDmFindHidden,
586 m_pOptions->m_DmFilePattern, m_pOptions->m_DmFileAntiPattern,
587 m_pOptions->m_DmDirAntiPattern, m_pOptions->m_bDmFollowDirLinks,
588 m_pOptions->m_bDmUseCvsIgnore);
589
590 for (i=dirListA.begin(); i!=dirListA.end();++i )
591 {
592 MergeFileInfos& mfi = m_fileMergeMap[sortString(i->filePath(), m_bCaseSensitive)];
593 //std::cout <<i->filePath()<<std::endl;
594 mfi.m_bExistsInA = true;
595 mfi.m_fileInfoA = *i;
596 }
597 }
598
599 if ( m_dirB.isValid() )
600 {
601 pp.setInformation(i18n("Reading Directory B"));
602 pp.setSubRangeTransformation(currentScan/nofScans, (currentScan+1)/nofScans);
603 ++currentScan;
604
605 t_DirectoryList dirListB;
606 bListDirSuccessB = m_dirB.listDir( &dirListB,
607 m_pOptions->m_bDmRecursiveDirs, m_pOptions->m_bDmFindHidden,
608 m_pOptions->m_DmFilePattern, m_pOptions->m_DmFileAntiPattern,
609 m_pOptions->m_DmDirAntiPattern, m_pOptions->m_bDmFollowDirLinks,
610 m_pOptions->m_bDmUseCvsIgnore);
611
612 for (i=dirListB.begin(); i!=dirListB.end();++i )
613 {
614 MergeFileInfos& mfi = m_fileMergeMap[sortString(i->filePath(), m_bCaseSensitive)];
615 mfi.m_bExistsInB = true;
616 mfi.m_fileInfoB = *i;
617 }
618 }
619
620 e_MergeOperation eDefaultMergeOp;
621 if ( m_dirC.isValid() )
622 {
623 pp.setInformation(i18n("Reading Directory C"));
624 pp.setSubRangeTransformation(currentScan/nofScans, (currentScan+1)/nofScans);
625 ++currentScan;
626
627 t_DirectoryList dirListC;
628 bListDirSuccessC = m_dirC.listDir( &dirListC,
629 m_pOptions->m_bDmRecursiveDirs, m_pOptions->m_bDmFindHidden,
630 m_pOptions->m_DmFilePattern, m_pOptions->m_DmFileAntiPattern,
631 m_pOptions->m_DmDirAntiPattern, m_pOptions->m_bDmFollowDirLinks,
632 m_pOptions->m_bDmUseCvsIgnore);
633
634 for (i=dirListC.begin(); i!=dirListC.end();++i )
635 {
636 MergeFileInfos& mfi = m_fileMergeMap[sortString(i->filePath(),m_bCaseSensitive)];
637 mfi.m_bExistsInC = true;
638 mfi.m_fileInfoC = *i;
639 }
640
641 eDefaultMergeOp = eMergeABCToDest;
642 }
643 else
644 eDefaultMergeOp = m_bSyncMode ? eMergeToAB : eMergeABToDest;
645
646 bool bContinue = true;
647 if ( !bListDirSuccessA || !bListDirSuccessB || !bListDirSuccessC )
648 {
649 QString s = i18n("Some subdirectories were not readable in");
650 if ( !bListDirSuccessA ) s += "\nA: " + m_dirA.prettyAbsPath();
651 if ( !bListDirSuccessB ) s += "\nB: " + m_dirB.prettyAbsPath();
652 if ( !bListDirSuccessC ) s += "\nC: " + m_dirC.prettyAbsPath();
653 s+="\n";
654 s+= i18n("Check the permissions of the subdirectories.");
655 bContinue = KMessageBox::Continue == KMessageBox::warningContinueCancel( this, s );
656 }
657
658 if ( bContinue )
659 {
660 prepareListView(pp);
661
662 for( int childIdx = 0; childIdx<topLevelItemCount(); ++childIdx )//Q3ListViewItem* p = firstChild(); p!=0; p = p->nextSibling() )
663 {
664 DirMergeItem* pDMI = static_cast<DirMergeItem*>( topLevelItem(childIdx) );
665 calcSuggestedOperation( *pDMI->m_pMFI, eDefaultMergeOp );
666 }
667 }
668 //else
669 //{
670 // setSelected( 0, true );
671 //}
672
673 for (int i=0;i<columnCount();++i)
674 resizeColumnToContents(i);
675
676 QDir::setCurrent(origCurrentDirectory);
677
678 // Try to improve the view a little bit.
679 QWidget* pParent = parentWidget();
680 QSplitter* pSplitter = static_cast<QSplitter*>(pParent);
681 if (pSplitter!=0)
682 {
683 QList<int> sizes = pSplitter->sizes();
684 int total = sizes[0] + sizes[1];
685 sizes[0]=total*6/10;
686 sizes[1]=total - sizes[0];
687 pSplitter->setSizes( sizes );
688 }
689
690 sortItems(0,Qt::AscendingOrder);
691
692 m_bScanning = false;
693 statusBarMessage(i18n("Ready."));
694
695 if ( bContinue )
696 {
697 // Generate a status report
698 int nofFiles=0;
699 int nofDirs=0;
700 int nofEqualFiles=0;
701 int nofManualMerges=0;
702 for( int childIdx = 0; childIdx<topLevelItemCount(); ++childIdx )
703 calcDirStatus( m_dirC.isValid(), static_cast<DirMergeItem*>(topLevelItem(childIdx)),
704 nofFiles, nofDirs, nofEqualFiles, nofManualMerges );
705
706 QString s;
707 s = i18n("Directory Comparison Status") + "\n\n" +
708 i18n("Number of subdirectories:") +" "+ QString::number(nofDirs) + "\n"+
709 i18n("Number of equal files:") +" "+ QString::number(nofEqualFiles) + "\n"+
710 i18n("Number of different files:") +" "+ QString::number(nofFiles-nofEqualFiles);
711
712 if ( m_dirC.isValid() )
713 s += "\n" + i18n("Number of manual merges:") +" "+ QString::number(nofManualMerges);
714 KMessageBox::information( this, s );
715 if ( topLevelItemCount()>0 )
716 topLevelItem(0)->setSelected(true);
717 }
718
719 return true;
720 }
721
722 void DirectoryMergeWindow::onExpanded()
723 {
724 resizeColumnToContents(s_NameCol);
725 }
726
727
728 void DirectoryMergeWindow::slotChooseAEverywhere(){ setAllMergeOperations( eCopyAToDest ); }
729
730 void DirectoryMergeWindow::slotChooseBEverywhere(){ setAllMergeOperations( eCopyBToDest ); }
731
732 void DirectoryMergeWindow::slotChooseCEverywhere(){ setAllMergeOperations( eCopyCToDest ); }
733
734 void DirectoryMergeWindow::slotAutoChooseEverywhere()
735 {
736 e_MergeOperation eDefaultMergeOp = m_dirC.isValid() ? eMergeABCToDest :
737 m_bSyncMode ? eMergeToAB : eMergeABToDest;
738 setAllMergeOperations(eDefaultMergeOp );
739 }
740
741 void DirectoryMergeWindow::slotNoOpEverywhere(){ setAllMergeOperations(eNoOperation); }
742
743 static void setListViewItemOpen( QTreeWidgetItem* p, bool bOpen )
744 {
745 if ( p->childCount() > 0 )
746 {
747 for( int childIdx=0; childIdx<p->childCount(); ++childIdx )
748 setListViewItemOpen( p->child(childIdx), bOpen );
749
750 p->setExpanded( bOpen );
751 }
752 }
753
754 void DirectoryMergeWindow::slotFoldAllSubdirs()
755 {
756 for( int i=0; i<topLevelItemCount(); ++i )
757 setListViewItemOpen( topLevelItem(i), false );
758 }
759
760 void DirectoryMergeWindow::slotUnfoldAllSubdirs()
761 {
762 for( int i=0; i<topLevelItemCount(); ++i )
763 setListViewItemOpen( topLevelItem(i), true );
764 }
765
766 static void setMergeOperation( QTreeWidgetItem* pLVI, e_MergeOperation eMergeOp )
767 {
768 if ( pLVI==0 ) return;
769
770 DirMergeItem* pDMI = static_cast<DirMergeItem*>(pLVI);
771 MergeFileInfos& mfi = *pDMI->m_pMFI;
772
773 mfi.setMergeOperation(eMergeOp );
774 }
775
776 // Merge current item (merge mode)
777 void DirectoryMergeWindow::slotCurrentDoNothing() { setMergeOperation(currentItem(), eNoOperation ); }
778 void DirectoryMergeWindow::slotCurrentChooseA() { setMergeOperation(currentItem(), m_bSyncMode ? eCopyAToB : eCopyAToDest ); }
779 void DirectoryMergeWindow::slotCurrentChooseB() { setMergeOperation(currentItem(), m_bSyncMode ? eCopyBToA : eCopyBToDest ); }
780 void DirectoryMergeWindow::slotCurrentChooseC() { setMergeOperation(currentItem(), eCopyCToDest ); }
781 void DirectoryMergeWindow::slotCurrentMerge()
782 {
783 bool bThreeDirs = m_dirC.isValid();
784 setMergeOperation(currentItem(), bThreeDirs ? eMergeABCToDest : eMergeABToDest );
785 }
786 void DirectoryMergeWindow::slotCurrentDelete() { setMergeOperation(currentItem(), eDeleteFromDest ); }
787 // Sync current item
788 void DirectoryMergeWindow::slotCurrentCopyAToB() { setMergeOperation(currentItem(), eCopyAToB ); }
789 void DirectoryMergeWindow::slotCurrentCopyBToA() { setMergeOperation(currentItem(), eCopyBToA ); }
790 void DirectoryMergeWindow::slotCurrentDeleteA() { setMergeOperation(currentItem(), eDeleteA ); }
791 void DirectoryMergeWindow::slotCurrentDeleteB() { setMergeOperation(currentItem(), eDeleteB ); }
792 void DirectoryMergeWindow::slotCurrentDeleteAAndB() { setMergeOperation(currentItem(), eDeleteAB ); }
793 void DirectoryMergeWindow::slotCurrentMergeToA() { setMergeOperation(currentItem(), eMergeToA ); }
794 void DirectoryMergeWindow::slotCurrentMergeToB() { setMergeOperation(currentItem(), eMergeToB ); }
795 void DirectoryMergeWindow::slotCurrentMergeToAAndB() { setMergeOperation(currentItem(), eMergeToAB ); }
796
797
798 void DirectoryMergeWindow::keyPressEvent( QKeyEvent* e )
799 {
800 if ( (e->QInputEvent::modifiers() & Qt::ControlModifier)!=0 )
801 {
802 bool bThreeDirs = m_dirC.isValid();
803
804 QTreeWidgetItem* lvi = currentItem();
805 DirMergeItem* pDMI = lvi==0 ? 0 : static_cast<DirMergeItem*>(lvi);
806 MergeFileInfos* pMFI = pDMI==0 ? 0 : pDMI->m_pMFI;
807
808 if ( pMFI==0 ) return;
809 bool bMergeMode = bThreeDirs || !m_bSyncMode;
810 bool bFTConflict = pMFI==0 ? false : conflictingFileTypes(*pMFI);
811
812 if ( bMergeMode )
813 {
814 switch(e->key())
815 {
816 case Qt::Key_1: if(pMFI->m_bExistsInA){ slotCurrentChooseA(); } return;
817 case Qt::Key_2: if(pMFI->m_bExistsInB){ slotCurrentChooseB(); } return;
818 case Qt::Key_3: if(pMFI->m_bExistsInC){ slotCurrentChooseC(); } return;
819 case Qt::Key_Space: slotCurrentDoNothing(); return;
820 case Qt::Key_4: if ( !bFTConflict ) { slotCurrentMerge(); } return;
821 case Qt::Key_Delete: slotCurrentDelete(); return;
822 default: break;
823 }
824 }
825 else
826 {
827 switch(e->key())
828 {
829 case Qt::Key_1: if(pMFI->m_bExistsInA){ slotCurrentCopyAToB(); } return;
830 case Qt::Key_2: if(pMFI->m_bExistsInB){ slotCurrentCopyBToA(); } return;
831 case Qt::Key_Space: slotCurrentDoNothing(); return;
832 case Qt::Key_4: if ( !bFTConflict ) { slotCurrentMergeToAAndB(); } return;
833 case Qt::Key_Delete: if( pMFI->m_bExistsInA && pMFI->m_bExistsInB ) slotCurrentDeleteAAndB();
834 else if( pMFI->m_bExistsInA ) slotCurrentDeleteA();
835 else if( pMFI->m_bExistsInB ) slotCurrentDeleteB();
836 return;
837 default: break;
838 }
839 }
840 }
841 else if ( e->key()==Qt::Key_Return || e->key()==Qt::Key_Enter )
842 {
843 onDoubleClick( currentItem() );
844 return;
845 }
846
847 QTreeWidget::keyPressEvent(e);
848 }
849
850 void DirectoryMergeWindow::focusInEvent(QFocusEvent*)
851 {
852 updateAvailabilities();
853 }
854 void DirectoryMergeWindow::focusOutEvent(QFocusEvent*)
855 {
856 updateAvailabilities();
857 }
858
859 void DirectoryMergeWindow::setAllMergeOperations( e_MergeOperation eDefaultOperation )
860 {
861 if ( KMessageBox::Yes == KMessageBox::warningYesNo(this,
862 i18n("This affects all merge operations."),
863 i18n("Changing All Merge Operations"),i18n("C&ontinue"), i18n("&Cancel") ) )
864 {
865 for( int i=0; i<topLevelItemCount(); ++i )
866 {
867 DirMergeItem* pDMI = static_cast<DirMergeItem*>( topLevelItem(i) );
868 calcSuggestedOperation( *pDMI->m_pMFI, eDefaultOperation );
869 }
870 }
871 }
872
873
874 void DirectoryMergeWindow::compareFilesAndCalcAges( MergeFileInfos& mfi )
875 {
876 std::map<QDateTime,int> dateMap;
877
878 if( mfi.m_bExistsInA )
879 {
880 mfi.m_bLinkA = mfi.m_fileInfoA.isSymLink();
881 mfi.m_bDirA = mfi.m_fileInfoA.isDir();
882 dateMap[ mfi.m_fileInfoA.lastModified() ] = 0;
883 }
884 if( mfi.m_bExistsInB )
885 {
886 mfi.m_bLinkB = mfi.m_fileInfoB.isSymLink();
887 mfi.m_bDirB = mfi.m_fileInfoB.isDir();
888 dateMap[ mfi.m_fileInfoB.lastModified() ] = 1;
889 }
890 if( mfi.m_bExistsInC )
891 {
892 mfi.m_bLinkC = mfi.m_fileInfoC.isSymLink();
893 mfi.m_bDirC = mfi.m_fileInfoC.isDir();
894 dateMap[ mfi.m_fileInfoC.lastModified() ] = 2;
895 }
896
897 if ( m_pOptions->m_bDmFullAnalysis )
898 {
899 if( mfi.m_bExistsInA && mfi.m_bDirA || mfi.m_bExistsInB && mfi.m_bDirB || mfi.m_bExistsInC && mfi.m_bDirC )
900 {
901 // If any input is a directory, don't start any comparison.
902 mfi.m_bEqualAB=mfi.m_bExistsInA && mfi.m_bExistsInB;
903 mfi.m_bEqualAC=mfi.m_bExistsInA && mfi.m_bExistsInC;
904 mfi.m_bEqualBC=mfi.m_bExistsInB && mfi.m_bExistsInC;
905 }
906 else
907 {
908 emit startDiffMerge(
909 mfi.m_bExistsInA ? mfi.m_fileInfoA.absFilePath() : QString(""),
910 mfi.m_bExistsInB ? mfi.m_fileInfoB.absFilePath() : QString(""),
911 mfi.m_bExistsInC ? mfi.m_fileInfoC.absFilePath() : QString(""),
912 "",
913 "","","",&mfi.m_totalDiffStatus
914 );
915 int nofNonwhiteConflicts = mfi.m_totalDiffStatus.nofUnsolvedConflicts +
916 mfi.m_totalDiffStatus.nofSolvedConflicts - mfi.m_totalDiffStatus.nofWhitespaceConflicts;
917
918 if (m_pOptions->m_bDmWhiteSpaceEqual && nofNonwhiteConflicts == 0)
919 {
920 mfi.m_bEqualAB = mfi.m_bEqualBC = mfi.m_bEqualAC = true;
921 }
922 else
923 {
924 mfi.m_bEqualAB = mfi.m_totalDiffStatus.bBinaryAEqB;
925 mfi.m_bEqualBC = mfi.m_totalDiffStatus.bBinaryBEqC;
926 mfi.m_bEqualAC = mfi.m_totalDiffStatus.bBinaryAEqC;
927 }
928 }
929 }
930 else
931 {
932 bool bError;
933 QString eqStatus;
934 if( mfi.m_bExistsInA && mfi.m_bExistsInB )
935 {
936 if( mfi.m_bDirA ) mfi.m_bEqualAB=true;
937 else fastFileComparison( mfi.m_fileInfoA, mfi.m_fileInfoB, mfi.m_bEqualAB, bError, eqStatus );
938 }
939 if( mfi.m_bExistsInA && mfi.m_bExistsInC )
940 {
941 if( mfi.m_bDirA ) mfi.m_bEqualAC=true;
942 else fastFileComparison( mfi.m_fileInfoA, mfi.m_fileInfoC, mfi.m_bEqualAC, bError, eqStatus );
943 }
944 if( mfi.m_bExistsInB && mfi.m_bExistsInC )
945 {
946 if (mfi.m_bEqualAB && mfi.m_bEqualAC)
947 mfi.m_bEqualBC = true;
948 else
949 {
950 if( mfi.m_bDirB ) mfi.m_bEqualBC=true;
951 else fastFileComparison( mfi.m_fileInfoB, mfi.m_fileInfoC, mfi.m_bEqualBC, bError, eqStatus );
952 }
953 }
954 }
955
956 if (mfi.m_bLinkA!=mfi.m_bLinkB) mfi.m_bEqualAB=false;
957 if (mfi.m_bLinkA!=mfi.m_bLinkC) mfi.m_bEqualAC=false;
958 if (mfi.m_bLinkB!=mfi.m_bLinkC) mfi.m_bEqualBC=false;
959
960 if (mfi.m_bDirA!=mfi.m_bDirB) mfi.m_bEqualAB=false;
961 if (mfi.m_bDirA!=mfi.m_bDirC) mfi.m_bEqualAC=false;
962 if (mfi.m_bDirB!=mfi.m_bDirC) mfi.m_bEqualBC=false;
963
964 assert(eNew==0 && eMiddle==1 && eOld==2);
965
966 // The map automatically sorts the keys.
967 int age = eNew;
968 std::map<QDateTime,int>::reverse_iterator i;
969 for( i=dateMap.rbegin(); i!=dateMap.rend(); ++i )
970 {
971 int n = i->second;
972 if ( n==0 && mfi.m_ageA==eNotThere )
973 {
974 mfi.m_ageA = (e_Age)age; ++age;
975 if ( mfi.m_bEqualAB ) { mfi.m_ageB = mfi.m_ageA; ++age; }
976 if ( mfi.m_bEqualAC ) { mfi.m_ageC = mfi.m_ageA; ++age; }
977 }
978 else if ( n==1 && mfi.m_ageB==eNotThere )
979 {
980 mfi.m_ageB = (e_Age)age; ++age;
981 if ( mfi.m_bEqualAB ) { mfi.m_ageA = mfi.m_ageB; ++age; }
982 if ( mfi.m_bEqualBC ) { mfi.m_ageC = mfi.m_ageB; ++age; }
983 }
984 else if ( n==2 && mfi.m_ageC==eNotThere)
985 {
986 mfi.m_ageC = (e_Age)age; ++age;
987 if ( mfi.m_bEqualAC ) { mfi.m_ageA = mfi.m_ageC; ++age; }
988 if ( mfi.m_bEqualBC ) { mfi.m_ageB = mfi.m_ageC; ++age; }
989 }
990 }
991
992 // The checks below are necessary when the dates of the file are equal but the
993 // files are not. One wouldn't expect this to happen, yet it happens sometimes.
994 if ( mfi.m_bExistsInC && mfi.m_ageC==eNotThere )
995 {
996 mfi.m_ageC = (e_Age)age; ++age;
997 mfi.m_bConflictingAges = true;
998 }
999 if ( mfi.m_bExistsInB && mfi.m_ageB==eNotThere )
1000 {
1001 mfi.m_ageB = (e_Age)age; ++age;
1002 mfi.m_bConflictingAges = true;
1003 }
1004 if ( mfi.m_bExistsInA && mfi.m_ageA==eNotThere )
1005 {
1006 mfi.m_ageA = (e_Age)age; ++age;
1007 mfi.m_bConflictingAges = true;
1008 }
1009
1010 if ( mfi.m_ageA != eOld && mfi.m_ageB != eOld && mfi.m_ageC != eOld )
1011 {
1012 if (mfi.m_ageA == eMiddle) mfi.m_ageA = eOld;
1013 if (mfi.m_ageB == eMiddle) mfi.m_ageB = eOld;
1014 if (mfi.m_ageC == eMiddle) mfi.m_ageC = eOld;
1015 }
1016 }
1017
1018 static QPixmap* s_pm_dir;
1019 static QPixmap* s_pm_file;
1020
1021 static QPixmap* pmNotThere;
1022 static QPixmap* pmNew;
1023 static QPixmap* pmOld;
1024 static QPixmap* pmMiddle;
1025
1026 static QPixmap* pmLink;
1027
1028 static QPixmap* pmDirLink;
1029 static QPixmap* pmFileLink;
1030
1031 static QPixmap* pmNewLink;
1032 static QPixmap* pmOldLink;
1033 static QPixmap* pmMiddleLink;
1034
1035 static QPixmap* pmNewDir;
1036 static QPixmap* pmMiddleDir;
1037 static QPixmap* pmOldDir;
1038
1039 static QPixmap* pmNewDirLink;
1040 static QPixmap* pmMiddleDirLink;
1041 static QPixmap* pmOldDirLink;
1042
1043
1044 static QPixmap colorToPixmap(QColor c)
1045 {
1046 QPixmap pm(16,16);
1047 QPainter p(&pm);
1048 p.setPen( Qt::black );
1049 p.setBrush( c );
1050 p.drawRect(0,0,pm.width(),pm.height());
1051 return pm;
1052 }
1053
1054 static void initPixmaps( QColor newest, QColor oldest, QColor middle, QColor notThere )
1055 {
1056 if (pmNew==0)
1057 {
1058 pmNotThere = new QPixmap;
1059 pmNew = new QPixmap;
1060 pmOld = new QPixmap;
1061 pmMiddle = new QPixmap;
1062
1063 #include "xpm/link_arrow.xpm"
1064 pmLink = new QPixmap(link_arrow);
1065
1066 pmDirLink = new QPixmap;
1067 pmFileLink = new QPixmap;
1068
1069 pmNewLink = new QPixmap;
1070 pmOldLink = new QPixmap;
1071 pmMiddleLink = new QPixmap;
1072
1073 pmNewDir = new QPixmap;
1074 pmMiddleDir = new QPixmap;
1075 pmOldDir = new QPixmap;
1076
1077 pmNewDirLink = new QPixmap;
1078 pmMiddleDirLink = new QPixmap;
1079 pmOldDirLink = new QPixmap;
1080 }
1081
1082
1083 *pmNotThere = colorToPixmap(notThere);
1084 *pmNew = colorToPixmap(newest);
1085 *pmOld = colorToPixmap(oldest);
1086 *pmMiddle = colorToPixmap(middle);
1087
1088 *pmDirLink = pixCombiner( s_pm_dir, pmLink);
1089 *pmFileLink = pixCombiner( s_pm_file, pmLink );
1090
1091 *pmNewLink = pixCombiner( pmNew, pmLink);
1092 *pmOldLink = pixCombiner( pmOld, pmLink);
1093 *pmMiddleLink = pixCombiner( pmMiddle, pmLink);
1094
1095 *pmNewDir = pixCombiner2( pmNew, s_pm_dir);
1096 *pmMiddleDir = pixCombiner2( pmMiddle, s_pm_dir);
1097 *pmOldDir = pixCombiner2( pmOld, s_pm_dir);
1098
1099 *pmNewDirLink = pixCombiner( pmNewDir, pmLink);
1100 *pmMiddleDirLink = pixCombiner( pmMiddleDir, pmLink);
1101 *pmOldDirLink = pixCombiner( pmOldDir, pmLink);
1102 }
1103
1104
1105 static void setOnePixmap( QTreeWidgetItem* pLVI, int col, e_Age eAge, bool bLink, bool bDir )
1106 {
1107 static QPixmap* ageToPm[]= { pmNew, pmMiddle, pmOld, pmNotThere, s_pm_file };
1108 static QPixmap* ageToPmLink[]= { pmNewLink, pmMiddleLink, pmOldLink, pmNotThere, pmFileLink };
1109 static QPixmap* ageToPmDir[]= { pmNewDir, pmMiddleDir, pmOldDir, pmNotThere, s_pm_dir };
1110 static QPixmap* ageToPmDirLink[]={ pmNewDirLink, pmMiddleDirLink, pmOldDirLink, pmNotThere, pmDirLink };
1111
1112 QPixmap** ppPm = bDir ? ( bLink ? ageToPmDirLink : ageToPmDir ):
1113 ( bLink ? ageToPmLink : ageToPm );
1114
1115 pLVI->setIcon( col, *ppPm[eAge] );
1116 }
1117
1118 static void setPixmaps( MergeFileInfos& mfi, bool bCheckC )
1119 {
1120 setOnePixmap( mfi.m_pDMI, s_NameCol, eAgeEnd,
1121 mfi.m_bLinkA || mfi.m_bLinkB || mfi.m_bLinkC,
1122 mfi.m_bDirA || mfi.m_bDirB || mfi.m_bDirC
1123 );
1124
1125 if ( mfi.m_bDirA || mfi.m_bDirB || mfi.m_bDirC )
1126 {
1127 mfi.m_ageA=eNotThere;
1128 mfi.m_ageB=eNotThere;
1129 mfi.m_ageC=eNotThere;
1130 int age = eNew;
1131 if ( mfi.m_bExistsInC )
1132 {
1133 mfi.m_ageC = (e_Age)age;
1134 if (mfi.m_bEqualAC) mfi.m_ageA = (e_Age)age;
1135 if (mfi.m_bEqualBC) mfi.m_ageB = (e_Age)age;
1136 ++age;
1137 }
1138 if ( mfi.m_bExistsInB && mfi.m_ageB==eNotThere )
1139 {
1140 mfi.m_ageB = (e_Age)age;
1141 if (mfi.m_bEqualAB) mfi.m_ageA = (e_Age)age;
1142 ++age;
1143 }
1144 if ( mfi.m_bExistsInA && mfi.m_ageA==eNotThere )
1145 {
1146 mfi.m_ageA = (e_Age)age;
1147 }
1148 if ( mfi.m_ageA != eOld && mfi.m_ageB != eOld && mfi.m_ageC != eOld )
1149 {
1150 if (mfi.m_ageA == eMiddle) mfi.m_ageA = eOld;
1151 if (mfi.m_ageB == eMiddle) mfi.m_ageB = eOld;
1152 if (mfi.m_ageC == eMiddle) mfi.m_ageC = eOld;
1153 }
1154 }
1155
1156 setOnePixmap( mfi.m_pDMI, s_ACol, mfi.m_ageA, mfi.m_bLinkA, mfi.m_bDirA );
1157 setOnePixmap( mfi.m_pDMI, s_BCol, mfi.m_ageB, mfi.m_bLinkB, mfi.m_bDirB );
1158 if ( bCheckC )
1159 setOnePixmap( mfi.m_pDMI, s_CCol, mfi.m_ageC, mfi.m_bLinkC, mfi.m_bDirC );
1160 }
1161
1162 static QTreeWidgetItem* nextSibling(const QTreeWidgetItem* p)
1163 {
1164 QTreeWidgetItem* pParent = p->parent();
1165 if ( pParent )
1166 {
1167 int currentIdx = pParent->indexOfChild( const_cast<QTreeWidgetItem*>(p) );
1168 if ( currentIdx+1 < pParent->childCount() )
1169 return pParent->child(currentIdx+1);
1170 }
1171 else
1172 {
1173 QTreeWidget* pParentTreeWidget = p->treeWidget();
1174 if ( pParentTreeWidget )
1175 {
1176 int currentIdx = pParentTreeWidget->indexOfTopLevelItem( const_cast<QTreeWidgetItem*>(p) );
1177 if ( currentIdx+1 < pParentTreeWidget->topLevelItemCount() )
1178 return pParentTreeWidget->topLevelItem(currentIdx+1);
1179 }
1180 }
1181 return 0;
1182 }
1183
1184 // Iterate through the complete tree. Start by specifying QListView::firstChild().
1185 static QTreeWidgetItem* treeIterator( QTreeWidgetItem* p, bool bVisitChildren=true, bool bFindInvisible=false )
1186 {
1187 if( p!=0 )
1188 {
1189 do
1190 {
1191 if ( bVisitChildren && p->childCount() != 0 ) p = p->child(0);
1192 else
1193 {
1194 QTreeWidgetItem* pNextSibling = nextSibling(p);
1195 if ( pNextSibling )
1196 p = pNextSibling;
1197 else
1198 {
1199 p = p->parent();
1200 while ( p!=0 )
1201 {
1202 QTreeWidgetItem* pNextSibling = nextSibling(p);
1203 if( pNextSibling ) { p = pNextSibling; break; }
1204 else { p = p->parent(); }
1205 }
1206 }
1207 }
1208 }
1209 while( p && p->isHidden() && !bFindInvisible );
1210 }
1211 return p;
1212 }
1213
1214 void DirectoryMergeWindow::prepareListView( ProgressProxy& pp )
1215 {
1216 static bool bFirstTime = true;
1217 if (bFirstTime)
1218 {
1219 #include "xpm/file.xpm"
1220 #include "xpm/folder.xpm"
1221 s_pm_dir = new QPixmap( m_pIconLoader->loadIcon("folder", KIcon::Small ) );
1222 if (s_pm_dir->size()!=QSize(16,16))
1223 {
1224 delete s_pm_dir;
1225 s_pm_dir = new QPixmap( folder_pm );
1226 }
1227 s_pm_file= new QPixmap( file_pm );
1228 bFirstTime=false;
1229 }
1230
1231 clear();
1232 initPixmaps( m_pOptions->m_newestFileColor, m_pOptions->m_oldestFileColor,
1233 m_pOptions->m_midAgeFileColor, m_pOptions->m_missingFileColor );
1234
1235 setRootIsDecorated( true );
1236
1237 bool bCheckC = m_dirC.isValid();
1238
1239 std::map<QString, MergeFileInfos>::iterator j;
1240 int nrOfFiles = m_fileMergeMap.size();
1241 int currentIdx = 1;
1242 QTime t;
1243 t.start();
1244 for( j=m_fileMergeMap.begin(); j!=m_fileMergeMap.end(); ++j )
1245 {
1246 MergeFileInfos& mfi = j->second;
1247
1248 mfi.m_subPath = mfi.m_fileInfoA.exists() ? mfi.m_fileInfoA.filePath() :
1249 mfi.m_fileInfoB.exists() ? mfi.m_fileInfoB.filePath() :
1250 mfi.m_fileInfoC.exists() ? mfi.m_fileInfoC.filePath() :
1251 QString("");
1252
1253 // const QString& fileName = j->first;
1254 const QString& fileName = mfi.m_subPath;
1255
1256 pp.setInformation(
1257 i18n("Processing ") + QString::number(currentIdx) +" / "+ QString::number(nrOfFiles)
1258 +"\n" + fileName, double(currentIdx) / nrOfFiles, false );
1259 if ( pp.wasCancelled() ) break;
1260 ++currentIdx;
1261
1262
1263 // The comparisons and calculations for each file take place here.
1264 compareFilesAndCalcAges( mfi );
1265
1266 bool bEqual = bCheckC ? mfi.m_bEqualAB && mfi.m_bEqualAC : mfi.m_bEqualAB;
1267 //bool bDir = mfi.m_bDirA || mfi.m_bDirB || mfi.m_bDirC;
1268
1269 //if ( m_pOptions->m_bDmShowOnlyDeltas && !bDir && bEqual )
1270 // continue;
1271
1272 // Get dirname from fileName: Search for "/" from end:
1273 int pos = fileName.lastIndexOf('/');
1274 QString dirPart;
1275 QString filePart;
1276 if (pos==-1)
1277 {
1278 // Top dir
1279 filePart = fileName;
1280 }
1281 else
1282 {
1283 dirPart = fileName.left(pos);
1284 filePart = fileName.mid(pos+1);
1285 }
1286
1287 if ( dirPart.isEmpty() ) // Top level
1288 {
1289 new DirMergeItem( this, filePart, &mfi );
1290 }
1291 else
1292 {
1293 MergeFileInfos& dirMfi = m_fileMergeMap[sortString(dirPart, m_bCaseSensitive)]; // parent
1294 assert(dirMfi.m_pDMI!=0);
1295 new DirMergeItem( dirMfi.m_pDMI, filePart, &mfi );
1296 mfi.m_pParent = &dirMfi;
1297
1298 if ( !bEqual ) // Set all parents to "not equal"
1299 {
1300 MergeFileInfos* p = mfi.m_pParent;
1301 while(p!=0)
1302 {
1303 bool bChange = false;
1304 if ( !mfi.m_bEqualAB && p->m_bEqualAB ){ p->m_bEqualAB = false; bChange=true; }
1305 if ( !mfi.m_bEqualAC && p->m_bEqualAC ){ p->m_bEqualAC = false; bChange=true; }
1306 if ( !mfi.m_bEqualBC && p->m_bEqualBC ){ p->m_bEqualBC = false; bChange=true; }
1307
1308 if ( bChange )
1309 setPixmaps( *p, bCheckC );
1310 else
1311 break;
1312
1313 p = p->m_pParent;
1314 }
1315 }
1316 }
1317
1318 setPixmaps( mfi, bCheckC );
1319 }
1320
1321 /*if ( m_pOptions->m_bDmShowOnlyDeltas )
1322 {
1323 // Remove all equals. (Search tree depth first)
1324 QListViewItem* p = firstChild();
1325 while( p!=0 && firstChild() != 0 )
1326 {
1327 QListViewItem* pParent = p->parent();
1328 QListViewItem* pNextSibling = p->nextSibling();
1329
1330 DirMergeItem* pDMI = static_cast<DirMergeItem*>(p);
1331 bool bDirEqual = bCheckC ? pDMI->m_pMFI->m_bEqualAB && pDMI->m_pMFI->m_bEqualAC
1332 : pDMI->m_pMFI->m_bEqualAB;
1333 if ( pDMI!=0 && pDMI->m_pMFI->m_bDirA && bDirEqual )
1334 {
1335 delete p;
1336 p=0;
1337 }
1338
1339 if ( p!=0 && p->firstChild() != 0 ) p = p->firstChild();
1340 else if ( pNextSibling!=0 ) p = pNextSibling;
1341 else
1342 {
1343 p=pParent;
1344 while ( p!=0 )
1345 {
1346 if( p->nextSibling()!=0 ) { p = p->nextSibling(); break; }
1347 else { p = p->parent(); }
1348 }
1349 }
1350 }
1351 }*/
1352 }
1353
1354 static bool conflictingFileTypes(MergeFileInfos& mfi)
1355 {
1356 // Now check if file/dir-types fit.
1357 if ( mfi.m_bLinkA || mfi.m_bLinkB || mfi.m_bLinkC )
1358 {
1359 if ( mfi.m_bExistsInA && ! mfi.m_bLinkA ||
1360 mfi.m_bExistsInB && ! mfi.m_bLinkB ||
1361 mfi.m_bExistsInC && ! mfi.m_bLinkC )
1362 {
1363 return true;
1364 }
1365 }
1366
1367 if ( mfi.m_bDirA || mfi.m_bDirB || mfi.m_bDirC )
1368 {
1369 if ( mfi.m_bExistsInA && ! mfi.m_bDirA ||
1370 mfi.m_bExistsInB && ! mfi.m_bDirB ||
1371 mfi.m_bExistsInC && ! mfi.m_bDirC )
1372 {
1373 return true;
1374 }
1375 }
1376 return false;
1377 }
1378
1379 void DirectoryMergeWindow::calcSuggestedOperation( MergeFileInfos& mfi, e_MergeOperation eDefaultMergeOp )
1380 {
1381 bool bCheckC = m_dirC.isValid();
1382 bool bCopyNewer = m_pOptions->m_bDmCopyNewer;
1383 bool bOtherDest = !( m_dirDestInternal.absFilePath() == m_dirA.absFilePath() ||
1384 m_dirDestInternal.absFilePath() == m_dirB.absFilePath() ||
1385 bCheckC && m_dirDestInternal.absFilePath() == m_dirC.absFilePath() );
1386
1387
1388 if ( eDefaultMergeOp == eMergeABCToDest && !bCheckC ) { eDefaultMergeOp = eMergeABToDest; }
1389 if ( eDefaultMergeOp == eMergeToAB && bCheckC ) { assert(false); }
1390
1391 if ( eDefaultMergeOp == eMergeToA || eDefaultMergeOp == eMergeToB ||
1392 eDefaultMergeOp == eMergeABCToDest || eDefaultMergeOp == eMergeABToDest || eDefaultMergeOp == eMergeToAB )
1393 {
1394 if ( !bCheckC )
1395 {
1396 if ( mfi.m_bEqualAB )
1397 {
1398 mfi.setMergeOperation( bOtherDest ? eCopyBToDest : eNoOperation );
1399 }
1400 else if ( mfi.m_bExistsInA && mfi.m_bExistsInB )
1401 {
1402 if ( !bCopyNewer || mfi.m_bDirA )
1403 mfi.setMergeOperation( eDefaultMergeOp );
1404 else if ( bCopyNewer && mfi.m_bConflictingAges )
1405 {
1406 mfi.setMergeOperation( eConflictingAges );
1407 }
1408 else
1409 {
1410 if ( mfi.m_ageA == eNew )
1411 mfi.setMergeOperation( eDefaultMergeOp == eMergeToAB ? eCopyAToB : eCopyAToDest );
1412 else
1413 mfi.setMergeOperation( eDefaultMergeOp == eMergeToAB ? eCopyBToA : eCopyBToDest );
1414 }
1415 }
1416 else if ( !mfi.m_bExistsInA && mfi.m_bExistsInB )
1417 {
1418 if ( eDefaultMergeOp==eMergeABToDest ) mfi.setMergeOperation( eCopyBToDest );
1419 else if ( eDefaultMergeOp==eMergeToB ) mfi.setMergeOperation( eNoOperation );
1420 else mfi.setMergeOperation( eCopyBToA );
1421 }
1422 else if ( mfi.m_bExistsInA && !mfi.m_bExistsInB )
1423 {
1424 if ( eDefaultMergeOp==eMergeABToDest ) mfi.setMergeOperation( eCopyAToDest );
1425 else if ( eDefaultMergeOp==eMergeToA ) mfi.setMergeOperation( eNoOperation );
1426 else mfi.setMergeOperation( eCopyAToB );
1427 }
1428 else //if ( !mfi.m_bExistsInA && !mfi.m_bExistsInB )
1429 {
1430 mfi.setMergeOperation( eNoOperation ); assert(false);
1431 }
1432 }
1433 else
1434 {
1435 if ( mfi.m_bEqualAB && mfi.m_bEqualAC )
1436 {
1437 mfi.setMergeOperation( bOtherDest ? eCopyCToDest : eNoOperation );
1438 }
1439 else if ( mfi.m_bExistsInA && mfi.m_bExistsInB && mfi.m_bExistsInC)
1440 {
1441 if ( mfi.m_bEqualAB )
1442 mfi.setMergeOperation( eCopyCToDest );
1443 else if ( mfi.m_bEqualAC )
1444 mfi.setMergeOperation( eCopyBToDest );
1445 else if ( mfi.m_bEqualBC )
1446 mfi.setMergeOperation( eCopyCToDest );
1447 else
1448 mfi.setMergeOperation( eMergeABCToDest );
1449 }
1450 else if ( mfi.m_bExistsInA && mfi.m_bExistsInB && !mfi.m_bExistsInC )
1451 {
1452 if ( mfi.m_bEqualAB )
1453 mfi.setMergeOperation( eDeleteFromDest );
1454 else
1455 mfi.setMergeOperation( eCopyBToDest );
1456 }
1457 else if ( mfi.m_bExistsInA && !mfi.m_bExistsInB && mfi.m_bExistsInC )
1458 {
1459 if ( mfi.m_bEqualAC )
1460 mfi.setMergeOperation( eDeleteFromDest );
1461 else
1462 mfi.setMergeOperation( eCopyCToDest );
1463 }
1464 else if ( !mfi.m_bExistsInA && mfi.m_bExistsInB && mfi.m_bExistsInC )
1465 {
1466 if ( mfi.m_bEqualBC )
1467 mfi.setMergeOperation( eCopyCToDest );
1468 else
1469 mfi.setMergeOperation( eMergeABCToDest );
1470 }
1471 else if ( !mfi.m_bExistsInA && !mfi.m_bExistsInB && mfi.m_bExistsInC )
1472 {
1473 mfi.setMergeOperation( eCopyCToDest );
1474 }
1475 else if ( !mfi.m_bExistsInA && mfi.m_bExistsInB && !mfi.m_bExistsInC )
1476 {
1477 mfi.setMergeOperation( eCopyBToDest );
1478 }
1479 else if ( mfi.m_bExistsInA && !mfi.m_bExistsInB && !mfi.m_bExistsInC)
1480 {
1481 mfi.setMergeOperation( eDeleteFromDest );
1482 }
1483 else //if ( !mfi.m_bExistsInA && !mfi.m_bExistsInB && !mfi.m_bExistsInC )
1484 {
1485 mfi.setMergeOperation( eNoOperation ); assert(false);
1486 }
1487 }
1488
1489 // Now check if file/dir-types fit.
1490 if ( conflictingFileTypes(mfi) )
1491 {
1492 mfi.setMergeOperation( eConflictingFileTypes );
1493 }
1494 }
1495 else
1496 {
1497 e_MergeOperation eMO = eDefaultMergeOp;
1498 switch ( eDefaultMergeOp )
1499 {
1500 case eConflictingFileTypes:
1501 case eConflictingAges:
1502 case eDeleteA:
1503 case eDeleteB:
1504 case eDeleteAB:
1505 case eDeleteFromDest:
1506 case eNoOperation: break;
1507 case eCopyAToB: if ( !mfi.m_bExistsInA ) { eMO = eDeleteB; } break;
1508 case eCopyBToA: if ( !mfi.m_bExistsInB ) { eMO = eDeleteA; } break;
1509 case eCopyAToDest: if ( !mfi.m_bExistsInA ) { eMO = eDeleteFromDest; } break;
1510 case eCopyBToDest: if ( !mfi.m_bExistsInB ) { eMO = eDeleteFromDest; } break;
1511 case eCopyCToDest: if ( !mfi.m_bExistsInC ) { eMO = eDeleteFromDest; } break;
1512
1513 case eMergeToA:
1514 case eMergeToB:
1515 case eMergeToAB:
1516 case eMergeABCToDest:
1517 case eMergeABToDest:
1518 default:
1519 assert(false);
1520 }
1521 mfi.setMergeOperation( eMO );
1522 }
1523 }
1524
1525 void DirectoryMergeWindow::onDoubleClick( QTreeWidgetItem* lvi )
1526 {
1527 if (lvi==0) return;
1528
1529 if ( m_bDirectoryMerge )
1530 mergeCurrentFile();
1531 else
1532 compareCurrentFile();
1533 }
1534
1535 void DirectoryMergeWindow::onCurrentChanged(QTreeWidgetItem* lvi)
1536 {
1537 if ( lvi==0 ) return;
1538
1539 DirMergeItem* pDMI = static_cast<DirMergeItem*>(lvi);
1540
1541 MergeFileInfos& mfi = *pDMI->m_pMFI;
1542 assert( mfi.m_pDMI==pDMI );
1543
1544 m_pDirectoryMergeInfo->setInfo( m_dirA, m_dirB, m_dirC, m_dirDestInternal, mfi );
1545 }
1546
1547 void DirectoryMergeWindow::mousePressEvent( QMouseEvent* e )
1548 {
1549 QTreeWidget::mousePressEvent(e);
1550 int c = columnAt( e->x() );
1551 QTreeWidgetItem* lvi = itemAt( e->pos() );
1552 QPoint p = e->globalPos();
1553 if ( lvi==0 ) return;
1554
1555 DirMergeItem* pDMI = static_cast<DirMergeItem*>(lvi);
1556
1557 MergeFileInfos& mfi = *pDMI->m_pMFI;
1558 assert( mfi.m_pDMI==pDMI );
1559
1560 if ( c==s_OpCol )
1561 {
1562 bool bThreeDirs = m_dirC.isValid();
1563
1564 KPopupMenu m(this);
1565 if ( bThreeDirs )
1566 {
1567 m_pDirCurrentDoNothing->plug(&m);
1568 int count=0;
1569 if ( mfi.m_bExistsInA ) { m_pDirCurrentChooseA->plug(&m); ++count; }
1570 if ( mfi.m_bExistsInB ) { m_pDirCurrentChooseB->plug(&m); ++count; }
1571 if ( mfi.m_bExistsInC ) { m_pDirCurrentChooseC->plug(&m); ++count; }
1572 if ( !conflictingFileTypes(mfi) && count>1 ) m_pDirCurrentMerge->plug(&m);
1573 m_pDirCurrentDelete->plug(&m);
1574 }
1575 else if ( m_bSyncMode )
1576 {
1577 m_pDirCurrentSyncDoNothing->plug(&m);
1578 if ( mfi.m_bExistsInA ) m_pDirCurrentSyncCopyAToB->plug(&m);
1579 if ( mfi.m_bExistsInB ) m_pDirCurrentSyncCopyBToA->plug(&m);
1580 if ( mfi.m_bExistsInA ) m_pDirCurrentSyncDeleteA->plug(&m);
1581 if ( mfi.m_bExistsInB ) m_pDirCurrentSyncDeleteB->plug(&m);
1582 if ( mfi.m_bExistsInA && mfi.m_bExistsInB )
1583 {
1584 m_pDirCurrentSyncDeleteAAndB->plug(&m);
1585 if ( !conflictingFileTypes(mfi))
1586 {
1587 m_pDirCurrentSyncMergeToA->plug(&m);
1588 m_pDirCurrentSyncMergeToB->plug(&m);
1589 m_pDirCurrentSyncMergeToAAndB->plug(&m);
1590 }
1591 }
1592 }
1593 else
1594 {
1595 m_pDirCurrentDoNothing->plug(&m);
1596 if ( mfi.m_bExistsInA ) { m_pDirCurrentChooseA->plug(&m); }
1597 if ( mfi.m_bExistsInB ) { m_pDirCurrentChooseB->plug(&m); }
1598 if ( !conflictingFileTypes(mfi) && mfi.m_bExistsInA && mfi.m_bExistsInB ) m_pDirCurrentMerge->plug(&m);
1599 m_pDirCurrentDelete->plug(&m);
1600 }
1601
1602 m.exec( p );
1603 }
1604 else if ( c == s_ACol || c==s_BCol || c==s_CCol )
1605 {
1606 QString itemPath;
1607 if ( c == s_ACol && mfi.m_bExistsInA ){ itemPath = fullNameA(mfi); }
1608 else if ( c == s_BCol && mfi.m_bExistsInB ){ itemPath = fullNameB(mfi); }
1609 else if ( c == s_CCol && mfi.m_bExistsInC ){ itemPath = fullNameC(mfi); }
1610
1611 if (!itemPath.isEmpty())
1612 {
1613 selectItemAndColumn( pDMI, c, e->button()==Qt::RightButton );
1614 }
1615 }
1616 }
1617
1618 void DirectoryMergeWindow::contextMenuEvent(QContextMenuEvent* e)
1619 {
1620 QTreeWidgetItem* lvi = itemAt( e->pos() );
1621 int c = columnAt( e->x() );
1622 QPoint p = e->globalPos();
1623 if ( lvi==0 ) return;
1624
1625 DirMergeItem* pDMI = static_cast<DirMergeItem*>(lvi);
1626
1627 MergeFileInfos& mfi = *pDMI->m_pMFI;
1628 assert( mfi.m_pDMI==pDMI );
1629 if ( c == s_ACol || c==s_BCol || c==s_CCol )
1630 {
1631 QString itemPath;
1632 if ( c == s_ACol && mfi.m_bExistsInA ){ itemPath = fullNameA(mfi); }
1633 else if ( c == s_BCol && mfi.m_bExistsInB ){ itemPath = fullNameB(mfi); }
1634 else if ( c == s_CCol && mfi.m_bExistsInC ){ itemPath = fullNameC(mfi); }
1635
1636 if (!itemPath.isEmpty())
1637 {
1638 selectItemAndColumn(pDMI, c, true);
1639 KPopupMenu m(this);
1640 m_pDirCompareExplicit->plug(&m);
1641 m_pDirMergeExplicit->plug(&m);
1642
1643 #ifndef _WIN32
1644 m.exec( p );
1645 #else
1646 void showShellContextMenu( const QString&, QPoint, QWidget*, QMenu* );
1647 showShellContextMenu( itemPath, p, this, &m );
1648 #endif
1649 }
1650 }
1651 }
1652
1653 static QString getFileName( DirMergeItem* pDMI, int column )
1654 {
1655 if ( pDMI != 0 )
1656 {
1657 MergeFileInfos& mfi = *pDMI->m_pMFI;
1658 return column == s_ACol ? mfi.m_fileInfoA.absFilePath() :
1659 column == s_BCol ? mfi.m_fileInfoB.absFilePath() :
1660 column == s_CCol ? mfi.m_fileInfoC.absFilePath() :
1661 QString("");
1662 }
1663 return "";
1664 }
1665
1666 static bool isDir( DirMergeItem* pDMI, int column )
1667 {
1668 if ( pDMI != 0 )
1669 {
1670 MergeFileInfos& mfi = *pDMI->m_pMFI;
1671 return column == s_ACol ? mfi.m_bDirA :
1672 column == s_BCol ? mfi.m_bDirB :
1673 mfi.m_bDirC;
1674 }
1675 return false;
1676 }
1677
1678
1679 void DirectoryMergeWindow::selectItemAndColumn(DirMergeItem* pDMI, int c, bool bContextMenu)
1680 {
1681 if ( bContextMenu && (
1682 pDMI==m_pSelection1Item && c==m_selection1Column && m_pSelection2Item==0 ||
1683 pDMI==m_pSelection2Item && c==m_selection2Column && m_pSelection3Item==0 ||
1684 pDMI==m_pSelection3Item && c==m_selection3Column ) )
1685 return;
1686
1687 DirMergeItem* pOld1=m_pSelection1Item;
1688 DirMergeItem* pOld2=m_pSelection2Item;
1689 DirMergeItem* pOld3=m_pSelection3Item;
1690
1691 bool bReset = false;
1692
1693 if ( m_pSelection1Item )
1694 {
1695 if (isDir( m_pSelection1Item, m_selection1Column )!=isDir( pDMI, c ))
1696 bReset = true;
1697 }
1698
1699 if ( bReset || m_pSelection3Item!=0 ||
1700 pDMI==m_pSelection1Item && c==m_selection1Column ||
1701 pDMI==m_pSelection2Item && c==m_selection2Column ||
1702 pDMI==m_pSelection3Item && c==m_selection3Column)
1703 {
1704 m_pSelection1Item = 0;
1705 m_pSelection2Item = 0;
1706 m_pSelection3Item = 0;
1707 }
1708 else if ( m_pSelection1Item==0 )
1709 {
1710 m_pSelection1Item = pDMI;
1711 m_selection1Column = c;
1712 m_pSelection2Item = 0;
1713 m_pSelection3Item = 0;
1714 }
1715 else if ( m_pSelection2Item==0 )
1716 {
1717 m_pSelection2Item = pDMI;
1718 m_selection2Column = c;
1719 m_pSelection3Item = 0;
1720 }
1721 else if ( m_pSelection3Item==0 )
1722 {
1723 m_pSelection3Item = pDMI;
1724 m_selection3Column = c;
1725 }
1726 if (pOld1) dataChanged( indexFromItem( pOld1, s_ACol ), indexFromItem( pOld1, s_CCol ) );
1727 if (pOld2) dataChanged( indexFromItem( pOld2, s_ACol ), indexFromItem( pOld2, s_CCol ) );
1728 if (pOld3) dataChanged( indexFromItem( pOld3, s_ACol ), indexFromItem( pOld3, s_CCol ) );
1729 if (m_pSelection1Item) dataChanged( indexFromItem( m_pSelection1Item, s_ACol ), indexFromItem( m_pSelection1Item, s_CCol ) );
1730 if (m_pSelection2Item) dataChanged( indexFromItem( m_pSelection2Item, s_ACol ), indexFromItem( m_pSelection2Item, s_CCol ) );
1731 if (m_pSelection3Item) dataChanged( indexFromItem( m_pSelection3Item, s_ACol ), indexFromItem( m_pSelection3Item, s_CCol ) );
1732 emit updateAvailabilities();
1733 }
1734
1735 DirMergeItem::DirMergeItem( QTreeWidget* pParent, const QString& fileName, MergeFileInfos* pMFI )
1736 : QTreeWidgetItem( pParent, QStringList() << fileName << "" << "" << "" << i18n("To do.") << "" )
1737 {
1738 init(pMFI);
1739 }
1740
1741 DirMergeItem::DirMergeItem( DirMergeItem* pParent, const QString& fileName, MergeFileInfos* pMFI )
1742 : QTreeWidgetItem( pParent, QStringList() << fileName << "" << "" << "" << i18n("To do.") << "" )
1743 {
1744 init(pMFI);
1745 }
1746
1747
1748 void DirMergeItem::init(MergeFileInfos* pMFI)
1749 {
1750 pMFI->m_pDMI = this;
1751 m_pMFI = pMFI;
1752 TotalDiffStatus& tds = pMFI->m_totalDiffStatus;
1753 if ( m_pMFI->m_bDirA || m_pMFI->m_bDirB || m_pMFI->m_bDirC )
1754 {
1755 }
1756 else
1757 {
1758 setText( s_UnsolvedCol, QString::number( tds.nofUnsolvedConflicts ) );
1759 setText( s_SolvedCol, QString::number( tds.nofSolvedConflicts ) );
1760 setText( s_NonWhiteCol, QString::number( tds.nofUnsolvedConflicts + tds.nofSolvedConflicts - tds.nofWhitespaceConflicts ) );
1761 setText( s_WhiteCol, QString::number( tds.nofWhitespaceConflicts ) );
1762 }
1763 }
1764
1765 bool DirMergeItem::operator<(const QTreeWidgetItem& i) const
1766 {
1767 int col = treeWidget()->sortColumn();
1768 const DirMergeItem* pDMI = static_cast<const DirMergeItem*>(&i);
1769 bool bDir1 = m_pMFI->m_bDirA || m_pMFI->m_bDirB || m_pMFI->m_bDirC;
1770 bool bDir2 = pDMI->m_pMFI->m_bDirA || pDMI->m_pMFI->m_bDirB || pDMI->m_pMFI->m_bDirC;
1771 if ( m_pMFI==0 || pDMI->m_pMFI==0 || bDir1 == bDir2 )
1772 {
1773 if(col==s_UnsolvedCol || col==s_SolvedCol || col==s_NonWhiteCol || col==s_WhiteCol)
1774 return text(col).toInt() > i.text(col).toInt();
1775 else
1776 return QTreeWidgetItem::operator<(i);
1777 }
1778 else
1779 return bDir1;
1780 }
1781
1782
1783 DirMergeItem::~DirMergeItem()
1784 {
1785 m_pMFI->m_pDMI = 0;
1786 }
1787
1788 void MergeFileInfos::setMergeOperation( e_MergeOperation eMOp )
1789 {
1790 if ( eMOp != m_eMergeOperation )
1791 {
1792 m_bOperationComplete = false;
1793 m_pDMI->setText( s_OpStatusCol, "" );
1794 }
1795
1796 m_eMergeOperation = eMOp;
1797 QString s;
1798 bool bDir = m_bDirA || m_bDirB || m_bDirC;
1799 if( m_pDMI!=0 )
1800 {
1801 switch( m_eMergeOperation )
1802 {
1803 case eNoOperation: s=""; m_pDMI->setText(s_OpCol,""); break;
1804 case eCopyAToB: s=i18n("Copy A to B"); break;
1805 case eCopyBToA: s=i18n("Copy B to A"); break;
1806 case eDeleteA: s=i18n("Delete A"); break;
1807 case eDeleteB: s=i18n("Delete B"); break;
1808 case eDeleteAB: s=i18n("Delete A & B"); break;
1809 case eMergeToA: s=i18n("Merge to A"); break;
1810 case eMergeToB: s=i18n("Merge to B"); break;
1811 case eMergeToAB: s=i18n("Merge to A & B"); break;
1812 case eCopyAToDest: s="A"; break;
1813 case eCopyBToDest: s="B"; break;
1814 case eCopyCToDest: s="C"; break;
1815 case eDeleteFromDest: s=i18n("Delete (if exists)"); break;
1816 case eMergeABCToDest: s= bDir ? i18n("Merge") : i18n("Merge (manual)"); break;
1817 case eMergeABToDest: s= bDir ? i18n("Merge") : i18n("Merge (manual)"); break;
1818 case eConflictingFileTypes: s=i18n("Error: Conflicting File Types"); break;
1819 case eConflictingAges: s=i18n("Error: Dates are equal but files are not."); break;
1820 default: assert(false); break;
1821 }
1822 m_pDMI->setText(s_OpCol,s);
1823
1824 e_MergeOperation eChildrenMergeOp = m_eMergeOperation;
1825 if ( eChildrenMergeOp == eConflictingFileTypes ) eChildrenMergeOp = eMergeABCToDest;
1826 for( int childIdx=0; childIdx<m_pDMI->childCount(); ++childIdx )
1827 {
1828 QTreeWidgetItem* p = m_pDMI->child(childIdx);
1829 DirMergeItem* pDMI = static_cast<DirMergeItem*>( p );
1830 DirectoryMergeWindow* pDMW = static_cast<DirectoryMergeWindow*>( p->treeWidget() );
1831 pDMW->calcSuggestedOperation( *pDMI->m_pMFI, eChildrenMergeOp );
1832 }
1833 }
1834 }
1835
1836 void DirectoryMergeWindow::compareCurrentFile()
1837 {
1838 if (!canContinue()) return;
1839
1840 if ( m_bRealMergeStarted )
1841 {
1842 KMessageBox::sorry(this,i18n("This operation is currently not possible."),i18n("Operation Not Possible"));
1843 return;
1844 }
1845
1846 if ( currentItem() != 0 )
1847 {
1848 DirMergeItem* pDMI = static_cast<DirMergeItem*>( currentItem() );
1849 MergeFileInfos& mfi = *pDMI->m_pMFI;
1850 if ( !(mfi.m_bDirA || mfi.m_bDirB || mfi.m_bDirC) )
1851 {
1852 emit startDiffMerge(
1853 mfi.m_bExistsInA ? mfi.m_fileInfoA.absFilePath() : QString(""),
1854 mfi.m_bExistsInB ? mfi.m_fileInfoB.absFilePath() : QString(""),
1855 mfi.m_bExistsInC ? mfi.m_fileInfoC.absFilePath() : QString(""),
1856 "",
1857 "","","",0
1858 );
1859 }
1860 }
1861 emit updateAvailabilities();
1862 }
1863
1864
1865 void DirectoryMergeWindow::slotCompareExplicitlySelectedFiles()
1866 {
1867 if ( ! isDir(m_pSelection1Item,m_selection1Column) && !canContinue() ) return;
1868
1869 if ( m_bRealMergeStarted )
1870 {
1871 KMessageBox::sorry(this,i18n("This operation is currently not possible."),i18n("Operation Not Possible"));
1872 return;
1873 }
1874
1875 emit startDiffMerge(
1876 getFileName( m_pSelection1Item, m_selection1Column ),
1877 getFileName( m_pSelection2Item, m_selection2Column ),
1878 getFileName( m_pSelection3Item, m_selection3Column ),
1879 "",
1880 "","","",0
1881 );
1882 m_pSelection1Item=0;
1883 m_pSelection2Item=0;
1884 m_pSelection3Item=0;
1885
1886 emit updateAvailabilities();
1887 update();
1888 }
1889
1890 void DirectoryMergeWindow::slotMergeExplicitlySelectedFiles()
1891 {
1892 if ( ! isDir(m_pSelection1Item,m_selection1Column) && !canContinue() ) return;
1893
1894 if ( m_bRealMergeStarted )
1895 {
1896 KMessageBox::sorry(this,i18n("This operation is currently not possible."),i18n("Operation Not Possible"));
1897 return;
1898 }
1899
1900 QString fn1 = getFileName( m_pSelection1Item, m_selection1Column );
1901 QString fn2 = getFileName( m_pSelection2Item, m_selection2Column );
1902 QString fn3 = getFileName( m_pSelection3Item, m_selection3Column );
1903
1904 emit startDiffMerge( fn1, fn2, fn3,
1905 fn3.isEmpty() ? fn2 : fn3,
1906 "","","",0
1907 );
1908 m_pSelection1Item=0;
1909 m_pSelection2Item=0;
1910 m_pSelection3Item=0;
1911
1912 emit updateAvailabilities();
1913 update();
1914 }
1915
1916 bool DirectoryMergeWindow::isFileSelected()
1917 {
1918 if ( currentItem() != 0 )
1919 {
1920 DirMergeItem* pDMI = static_cast<DirMergeItem*>( currentItem() );
1921 MergeFileInfos& mfi = *pDMI->m_pMFI;
1922 return ! (mfi.m_bDirA || mfi.m_bDirB || mfi.m_bDirC || conflictingFileTypes(mfi) );
1923 }
1924 return false;
1925 }
1926
1927 void DirectoryMergeWindow::mergeResultSaved(const QString& fileName)
1928 {
1929 DirMergeItem* pCurrentItemForOperation = (m_mergeItemList.empty() || m_currentItemForOperation==m_mergeItemList.end() )
1930 ? 0
1931 : *m_currentItemForOperation;
1932
1933 if ( pCurrentItemForOperation!=0 && pCurrentItemForOperation->m_pMFI==0 )
1934 {
1935 KMessageBox::error( this, i18n("This should never happen: \n\nmergeResultSaved: m_pMFI=0\n\nIf you know how to reproduce this, please contact the program author."),i18n("Program Error") );
1936 return;
1937 }
1938 if ( pCurrentItemForOperation!=0 && fileName == fullNameDest(*pCurrentItemForOperation->m_pMFI) )
1939 {
1940 if ( pCurrentItemForOperation->m_pMFI->m_eMergeOperation==eMergeToAB )
1941 {
1942 MergeFileInfos& mfi = *pCurrentItemForOperation->m_pMFI;
1943 bool bSuccess = copyFLD( fullNameB(mfi), fullNameA(mfi) );
1944 if (!bSuccess)
1945 {
1946 KMessageBox::error(this, i18n("An error occurred while copying.\n"), i18n("Error") );
1947 m_pStatusInfo->setWindowTitle(i18n("Merge Error"));
1948 m_pStatusInfo->show();
1949 //if ( m_pStatusInfo->firstChild()!=0 )
1950 // m_pStatusInfo->ensureItemVisible( m_pStatusInfo->last() );
1951 m_bError = true;
1952 pCurrentItemForOperation->setText( s_OpStatusCol, i18n("Error.") );
1953 mfi.m_eMergeOperation = eCopyBToA;
1954 return;
1955 }
1956 }
1957 pCurrentItemForOperation->setText( s_OpStatusCol, i18n("Done.") );
1958 pCurrentItemForOperation->m_pMFI->m_bOperationComplete = true;
1959 if ( m_mergeItemList.size()==1 )
1960 {
1961 m_mergeItemList.clear();
1962 m_bRealMergeStarted=false;
1963 }
1964 }
1965
1966 emit updateAvailabilities();
1967 }
1968
1969 bool DirectoryMergeWindow::canContinue()
1970 {
1971 bool bCanContinue=false;
1972 checkIfCanContinue( &bCanContinue );
1973 if ( bCanContinue && !m_bError )
1974 {
1975 DirMergeItem* pCurrentItemForOperation =
1976 (m_mergeItemList.empty() || m_currentItemForOperation==m_mergeItemList.end() ) ? 0 : *m_currentItemForOperation;
1977
1978 if ( pCurrentItemForOperation!=0 && ! pCurrentItemForOperation->m_pMFI->m_bOperationComplete )
1979 {
1980 pCurrentItemForOperation->setText( s_OpStatusCol, i18n("Not saved.") );
1981 pCurrentItemForOperation->m_pMFI->m_bOperationComplete = true;
1982 if ( m_mergeItemList.size()==1 )
1983 {
1984 m_mergeItemList.clear();
1985 m_bRealMergeStarted=false;
1986 }
1987 }
1988 }
1989 return bCanContinue;
1990 }
1991
1992 bool DirectoryMergeWindow::executeMergeOperation( MergeFileInfos& mfi, bool& bSingleFileMerge )
1993 {
1994 bool bCreateBackups = m_pOptions->m_bDmCreateBakFiles;
1995 // First decide destname
1996 QString destName;
1997 switch( mfi.m_eMergeOperation )
1998 {
1999 case eNoOperation: break;
2000 case eDeleteAB: break;
2001 case eMergeToAB: // let the user save in B. In mergeResultSaved() the file will be copied to A.
2002 case eMergeToB:
2003 case eDeleteB:
2004 case eCopyAToB: destName = fullNameB(mfi); break;
2005 case eMergeToA:
2006 case eDeleteA:
2007 case eCopyBToA: destName = fullNameA(mfi); break;
2008 case eMergeABToDest:
2009 case eMergeABCToDest:
2010 case eCopyAToDest:
2011 case eCopyBToDest:
2012 case eCopyCToDest:
2013 case eDeleteFromDest: destName = fullNameDest(mfi); break;
2014 default:
2015 KMessageBox::error( this, i18n("Unknown merge operation. (This must never happen!)"), i18n("Error") );
2016 assert(false);
2017 }
2018
2019 bool bSuccess = false;
2020 bSingleFileMerge = false;
2021 switch( mfi.m_eMergeOperation )
2022 {
2023 case eNoOperation: bSuccess = true; break;
2024 case eCopyAToDest:
2025 case eCopyAToB: bSuccess = copyFLD( fullNameA(mfi), destName ); break;
2026 case eCopyBToDest:
2027 case eCopyBToA: bSuccess = copyFLD( fullNameB(mfi), destName ); break;
2028 case eCopyCToDest: bSuccess = copyFLD( fullNameC(mfi), destName ); break;
2029 case eDeleteFromDest:
2030 case eDeleteA:
2031 case eDeleteB: bSuccess = deleteFLD( destName, bCreateBackups ); break;
2032 case eDeleteAB: bSuccess = deleteFLD( fullNameA(mfi), bCreateBackups ) &&
2033 deleteFLD( fullNameB(mfi), bCreateBackups ); break;
2034 case eMergeABToDest:
2035 case eMergeToA:
2036 case eMergeToAB:
2037 case eMergeToB: bSuccess = mergeFLD( fullNameA(mfi), fullNameB(mfi), "",
2038 destName, bSingleFileMerge );
2039 break;
2040 case eMergeABCToDest:bSuccess = mergeFLD(
2041 mfi.m_bExistsInA ? fullNameA(mfi) : QString(""),
2042 mfi.m_bExistsInB ? fullNameB(mfi) : QString(""),
2043 mfi.m_bExistsInC ? fullNameC(mfi) : QString(""),
2044 destName, bSingleFileMerge );
2045 break;
2046 default:
2047 KMessageBox::error( this, i18n("Unknown merge operation."), i18n("Error") );
2048 assert(false);
2049 }
2050
2051 return bSuccess;
2052 }
2053
2054
2055 // Check if the merge can start, and prepare the m_mergeItemList which then contains all
2056 // items that must be merged.
2057 void DirectoryMergeWindow::prepareMergeStart( QTreeWidgetItem* pBegin, QTreeWidgetItem* pEnd, bool bVerbose )
2058 {
2059 if ( bVerbose )
2060 {
2061 int status = KMessageBox::warningYesNoCancel(this,
2062 i18n("The merge is about to begin.\n\n"
2063 "Choose \"Do it\" if you have read the instructions and know what you are doing.\n"
2064 "Choosing \"Simulate it\" will tell you what would happen.\n\n"
2065 "Be aware that this program still has beta status "
2066 "and there is NO WARRANTY whatsoever! Make backups of your vital data!"),
2067 i18n("Starting Merge"), i18n("Do It"), i18n("Simulate It") );
2068 if (status==KMessageBox::Yes) m_bRealMergeStarted = true;
2069 else if (status==KMessageBox::No ) m_bSimulatedMergeStarted = true;
2070 else return;
2071 }
2072 else
2073 {
2074 m_bRealMergeStarted = true;
2075 }
2076
2077 m_mergeItemList.clear();
2078 if (pBegin == 0)
2079 return;
2080
2081 for( QTreeWidgetItem* p = pBegin; p!= pEnd; p = treeIterator( p ) )
2082 {
2083 DirMergeItem* pDMI = static_cast<DirMergeItem*>(p);
2084
2085 if ( pDMI && ! pDMI->m_pMFI->m_bOperationComplete )
2086 {
2087 m_mergeItemList.push_back(pDMI);
2088
2089 if (pDMI!=0 && pDMI->m_pMFI->m_eMergeOperation == eConflictingFileTypes )
2090 {
2091 scrollToItem( pDMI, QAbstractItemView::EnsureVisible );
2092 pDMI->setSelected( true );
2093 KMessageBox::error(this, i18n("The highlighted item has a different type in the different directories. Select what to do."), i18n("Error"));
2094 m_mergeItemList.clear();
2095 m_bRealMergeStarted=false;
2096 return;
2097 }
2098 if (pDMI!=0 && pDMI->m_pMFI->m_eMergeOperation == eConflictingAges )
2099 {
2100 scrollToItem ( pDMI, QAbstractItemView::EnsureVisible );
2101 pDMI->setSelected( true );
2102 KMessageBox::error(this, i18n("The modification dates of the file are equal but the files are not. Select what to do."), i18n("Error"));
2103 m_mergeItemList.clear();
2104 m_bRealMergeStarted=false;
2105 return;
2106 }
2107 }
2108 }
2109
2110 m_currentItemForOperation = m_mergeItemList.begin();
2111 return;
2112 }
2113
2114 void DirectoryMergeWindow::slotRunOperationForCurrentItem()
2115 {
2116 if ( ! canContinue() ) return;
2117
2118 bool bVerbose = false;
2119 if ( m_mergeItemList.empty() )
2120 {
2121 QTreeWidgetItem* pBegin = currentItem();
2122 QTreeWidgetItem* pEnd = treeIterator(pBegin,false,false); // find next visible sibling (no children)
2123
2124 prepareMergeStart( pBegin, pEnd, bVerbose );
2125 mergeContinue(true, bVerbose);
2126 }
2127 else
2128 mergeContinue(false, bVerbose);
2129 }
2130
2131 void DirectoryMergeWindow::slotRunOperationForAllItems()
2132 {
2133 if ( ! canContinue() ) return;
2134
2135 bool bVerbose = true;
2136 if ( m_mergeItemList.empty() )
2137 {
2138 QTreeWidgetItem* pBegin = topLevelItemCount()>0 ? topLevelItem(0) : 0;
2139
2140 prepareMergeStart( pBegin, 0, bVerbose );
2141 mergeContinue(true, bVerbose);
2142 }
2143 else
2144 mergeContinue(false, bVerbose);
2145 }
2146
2147 void DirectoryMergeWindow::mergeCurrentFile()
2148 {
2149 if (!canContinue()) return;
2150
2151 if ( m_bRealMergeStarted )
2152 {
2153 KMessageBox::sorry(this,i18n("This operation is currently not possible because directory merge is currently running."),i18n("Operation Not Possible"));
2154 return;
2155 }
2156
2157 if ( isFileSelected() )
2158 {
2159 DirMergeItem* pDMI = static_cast<DirMergeItem*>( currentItem() );
2160 if ( pDMI != 0 )
2161 {
2162 MergeFileInfos& mfi = *pDMI->m_pMFI;
2163 m_mergeItemList.clear();
2164 m_mergeItemList.push_back( pDMI );
2165 m_currentItemForOperation=m_mergeItemList.begin();
2166 bool bDummy=false;
2167 mergeFLD(
2168 mfi.m_bExistsInA ? mfi.m_fileInfoA.absFilePath() : QString(""),
2169 mfi.m_bExistsInB ? mfi.m_fileInfoB.absFilePath() : QString(""),
2170 mfi.m_bExistsInC ? mfi.m_fileInfoC.absFilePath() : QString(""),
2171 fullNameDest(mfi),
2172 bDummy
2173 );
2174 }
2175 }
2176 emit updateAvailabilities();
2177 }
2178
2179
2180 // When bStart is true then m_currentItemForOperation must still be processed.
2181 // When bVerbose is true then a messagebox will tell when the merge is complete.
2182 void DirectoryMergeWindow::mergeContinue(bool bStart, bool bVerbose)
2183 {
2184 ProgressProxy pp;
2185 if ( m_mergeItemList.empty() )
2186 return;
2187
2188 int nrOfItems = 0;
2189 int nrOfCompletedItems = 0;
2190 int nrOfCompletedSimItems = 0;
2191
2192 // Count the number of completed items (for the progress bar).
2193 for( MergeItemList::iterator i = m_mergeItemList.begin(); i!=m_mergeItemList.end(); ++i )
2194 {
2195 DirMergeItem* pDMI = *i;
2196 ++nrOfItems;
2197 if ( pDMI->m_pMFI->m_bOperationComplete )
2198 ++nrOfCompletedItems;
2199 if ( pDMI->m_pMFI->m_bSimOpComplete )
2200 ++nrOfCompletedSimItems;
2201 }
2202
2203 m_pStatusInfo->hide();
2204 m_pStatusInfo->clear();
2205
2206 DirMergeItem* pCurrentItemForOperation = m_currentItemForOperation==m_mergeItemList.end() ? 0 : *m_currentItemForOperation;
2207
2208 bool bContinueWithCurrentItem = bStart; // true for first item, else false
2209 bool bSkipItem = false;
2210 if ( !bStart && m_bError && pCurrentItemForOperation!=0 )
2211 {
2212 int status = KMessageBox::warningYesNoCancel(this,
2213 i18n("There was an error in the last step.\n"
2214 "Do you want to continue with the item that caused the error or do you want to skip this item?"),
2215 i18n("Continue merge after an error"), i18n("Continue With Last Item"), i18n("Skip Item") );
2216 if (status==KMessageBox::Yes) bContinueWithCurrentItem = true;
2217 else if (status==KMessageBox::No ) bSkipItem = true;
2218 else return;
2219 m_bError = false;
2220 }
2221
2222 bool bSuccess = true;
2223 bool bSingleFileMerge = false;
2224 bool bSim = m_bSimulatedMergeStarted;
2225 while( bSuccess )
2226 {
2227 if ( pCurrentItemForOperation==0 )
2228 {
2229 m_mergeItemList.clear();
2230 m_bRealMergeStarted=false;
2231 break;
2232 }
2233
2234 if ( pCurrentItemForOperation!=0 && !bContinueWithCurrentItem )
2235 {
2236 if ( bSim )
2237 {
2238 if( pCurrentItemForOperation->childCount()==0 )
2239 {
2240 pCurrentItemForOperation->m_pMFI->m_bSimOpComplete = true;
2241 }
2242 }
2243 else
2244 {
2245 if( pCurrentItemForOperation->childCount()==0 )
2246 {
2247 if( !pCurrentItemForOperation->m_pMFI->m_bOperationComplete )
2248 {
2249 pCurrentItemForOperation->setText( s_OpStatusCol, bSkipItem ? i18n("Skipped.") : i18n("Done.") );
2250 pCurrentItemForOperation->m_pMFI->m_bOperationComplete = true;
2251 bSkipItem = false;
2252 }
2253 }
2254 else
2255 {
2256 pCurrentItemForOperation->setText( s_OpStatusCol, i18n("In progress...") );
2257 }
2258 }
2259 }
2260
2261 if ( ! bContinueWithCurrentItem )
2262 {
2263 // Depth first
2264 QTreeWidgetItem* pPrevItem = pCurrentItemForOperation;
2265 ++m_currentItemForOperation;
2266 pCurrentItemForOperation = m_currentItemForOperation==m_mergeItemList.end() ? 0 : *m_currentItemForOperation;
2267 if ( (pCurrentItemForOperation==0 || pCurrentItemForOperation->parent()!=pPrevItem->parent()) && pPrevItem->parent()!=0 )
2268 {
2269 // Check if the parent may be set to "Done"
2270 QTreeWidgetItem* pParent = pPrevItem->parent();
2271 bool bDone = true;
2272 while ( bDone && pParent!=0 )
2273 {
2274 for( int childIdx = 0; childIdx<pParent->childCount(); ++childIdx )
2275 {
2276 DirMergeItem* pDMI = static_cast<DirMergeItem*>(pParent->child(childIdx));
2277 if ( !bSim && ! pDMI->m_pMFI->m_bOperationComplete || bSim && pDMI->m_pMFI->m_bSimOpComplete )
2278 {
2279 bDone=false;
2280 break;
2281 }
2282 }
2283 if ( bDone )
2284 {
2285 if (bSim)
2286 static_cast<DirMergeItem*>(pParent)->m_pMFI->m_bSimOpComplete = bDone;
2287 else
2288 {
2289 pParent->setText( s_OpStatusCol, i18n("Done.") );
2290 static_cast<DirMergeItem*>(pParent)->m_pMFI->m_bOperationComplete = bDone;
2291 }
2292 }
2293 pParent = pParent->parent();
2294 }
2295 }
2296 }
2297
2298 if ( pCurrentItemForOperation == 0 ) // end?
2299 {
2300 if ( m_bRealMergeStarted )
2301 {
2302 if (bVerbose)
2303 {
2304 KMessageBox::information( this, i18n("Merge operation complete."), i18n("Merge Complete") );
2305 }
2306 m_bRealMergeStarted = false;
2307 m_pStatusInfo->setWindowTitle(i18n("Merge Complete"));
2308 }
2309 if ( m_bSimulatedMergeStarted )
2310 {
2311 m_bSimulatedMergeStarted = false;
2312 QTreeWidgetItem* p = topLevelItemCount()>0 ? topLevelItem(0) : 0;
2313 for( ; p!=0; p=treeIterator(p) )
2314 {
2315 static_cast<DirMergeItem*>(p)->m_pMFI->m_bSimOpComplete = false;
2316 }
2317 m_pStatusInfo->setWindowTitle(i18n("Simulated merge complete: Check if you agree with the proposed operations."));
2318 m_pStatusInfo->show();
2319 }
2320 m_mergeItemList.clear();
2321 m_bRealMergeStarted=false;
2322 return;
2323 }
2324
2325 MergeFileInfos& mfi = *pCurrentItemForOperation->m_pMFI;
2326
2327 pp.setInformation( mfi.m_subPath,
2328 bSim ? double(nrOfCompletedSimItems)/nrOfItems : double(nrOfCompletedItems)/nrOfItems,
2329 false // bRedrawUpdate
2330 );
2331
2332 bSuccess = executeMergeOperation( mfi, bSingleFileMerge ); // Here the real operation happens.
2333
2334 if ( bSuccess )
2335 {
2336 if(bSim) ++nrOfCompletedSimItems;
2337 else ++nrOfCompletedItems;
2338 bContinueWithCurrentItem = false;
2339 }
2340
2341 if( pp.wasCancelled() )
2342 break;
2343 } // end while
2344
2345 //g_pProgressDialog->hide();
2346
2347 setCurrentItem( pCurrentItemForOperation );
2348 scrollToItem( pCurrentItemForOperation, EnsureVisible );
2349 if ( !bSuccess && !bSingleFileMerge )
2350 {
2351 KMessageBox::error(this, i18n("An error occurred. Press OK to see detailed information.\n"), i18n("Error") );
2352 m_pStatusInfo->setWindowTitle(i18n("Merge Error"));
2353 m_pStatusInfo->show();
2354 //if ( m_pStatusInfo->firstChild()!=0 )
2355 // m_pStatusInfo->ensureItemVisible( m_pStatusInfo->last() );
2356 m_bError = true;
2357 pCurrentItemForOperation->setText( s_OpStatusCol, i18n("Error.") );
2358 }
2359 else
2360 {
2361 m_bError = false;
2362 }
2363 emit updateAvailabilities();
2364
2365 if ( m_currentItemForOperation==m_mergeItemList.end() )
2366 {
2367 m_mergeItemList.clear();
2368 m_bRealMergeStarted=false;
2369 }
2370 }
2371
2372 void DirectoryMergeWindow::allowResizeEvents(bool bAllowResizeEvents )
2373 {
2374 m_bAllowResizeEvents = bAllowResizeEvents;
2375 }
2376
2377 void DirectoryMergeWindow::resizeEvent( QResizeEvent* e )
2378 {
2379 if (m_bAllowResizeEvents)
2380 QTreeWidget::resizeEvent(e);
2381 }
2382
2383 bool DirectoryMergeWindow::deleteFLD( const QString& name, bool bCreateBackup )
2384 {
2385 FileAccess fi(name, true);
2386 if ( !fi.exists() )
2387 return true;
2388
2389 if ( bCreateBackup )
2390 {
2391 bool bSuccess = renameFLD( name, name+".orig" );
2392 if (!bSuccess)
2393 {
2394 m_pStatusInfo->addText( i18n("Error: While deleting %1: Creating backup failed.").arg(name) );
2395 return false;
2396 }
2397 }
2398 else
2399 {
2400 if ( fi.isDir() && !fi.isSymLink() )
2401 m_pStatusInfo->addText(i18n("delete directory recursively( %1 )").arg(name));
2402 else
2403 m_pStatusInfo->addText(i18n("delete( %1 )").arg(name));
2404
2405 if ( m_bSimulatedMergeStarted )
2406 {
2407 return true;
2408 }
2409
2410 if ( fi.isDir() && !fi.isSymLink() )// recursive directory delete only for real dirs, not symlinks
2411 {
2412 t_DirectoryList dirList;
2413 bool bSuccess = fi.listDir( &dirList, false, true, "*", "", "", false, false ); // not recursive, find hidden files
2414
2415 if ( !bSuccess )
2416 {
2417 // No Permission to read directory or other error.
2418 m_pStatusInfo->addText( i18n("Error: delete dir operation failed while trying to read the directory.") );
2419 return false;
2420 }
2421
2422 t_DirectoryList::iterator it; // create list iterator
2423
2424 for ( it=dirList.begin(); it!=dirList.end(); ++it ) // for each file...
2425 {
2426 FileAccess& fi2 = *it;
2427 if ( fi2.fileName() == "." || fi2.fileName()==".." )
2428 continue;
2429 bSuccess = deleteFLD( fi2.absFilePath(), false );
2430 if (!bSuccess) break;
2431 }
2432 if (bSuccess)
2433 {
2434 bSuccess = FileAccess::removeDir( name );
2435 if ( !bSuccess )
2436 {
2437 m_pStatusInfo->addText( i18n("Error: rmdir( %1 ) operation failed.").arg(name));
2438 return false;
2439 }
2440 }
2441 }
2442 else
2443 {
2444 bool bSuccess = FileAccess::removeFile( name );
2445 if ( !bSuccess )
2446 {
2447 m_pStatusInfo->addText( i18n("Error: delete operation failed.") );
2448 return false;
2449 }
2450 }
2451 }
2452 return true;
2453 }
2454
2455 bool DirectoryMergeWindow::mergeFLD( const QString& nameA,const QString& nameB,const QString& nameC,const QString& nameDest, bool& bSingleFileMerge )
2456 {
2457 FileAccess fi(nameA);
2458 if (fi.isDir())
2459 {
2460 return makeDir(nameDest);
2461 }
2462
2463 // Make sure that the dir exists, into which we will save the file later.
2464 int pos=nameDest.lastIndexOf('/');
2465 if ( pos>0 )
2466 {
2467 QString parentName = nameDest.left(pos);
2468 bool bSuccess = makeDir(parentName, true /*quiet*/);
2469 if (!bSuccess)
2470 return false;
2471 }
2472
2473 m_pStatusInfo->addText(i18n("manual merge( %1, %2, %3 -> %4)").arg(nameA).arg(nameB).arg(nameC).arg(nameDest));
2474 if ( m_bSimulatedMergeStarted )
2475 {
2476 m_pStatusInfo->addText(i18n(" Note: After a manual merge the user should continue by pressing F7.") );
2477 return true;
2478 }
2479
2480 bSingleFileMerge = true;
2481 (*m_currentItemForOperation)->setText( s_OpStatusCol, i18n("In progress...") );
2482 scrollToItem( *m_currentItemForOperation, EnsureVisible );
2483
2484 emit startDiffMerge( nameA, nameB, nameC, nameDest, "","","",0 );
2485
2486 return false;
2487 }
2488
2489 bool DirectoryMergeWindow::copyFLD( const QString& srcName, const QString& destName )
2490 {
2491 if ( srcName == destName )
2492 return true;
2493
2494 if ( FileAccess(destName, true).exists() )
2495 {
2496 bool bSuccess = deleteFLD( destName, m_pOptions->m_bDmCreateBakFiles );
2497 if ( !bSuccess )
2498 {
2499 m_pStatusInfo->addText(i18n("Error: copy( %1 -> %2 ) failed."
2500 "Deleting existing destination failed.").arg(srcName).arg(destName));
2501 return false;
2502 }
2503 }
2504
2505 FileAccess fi( srcName );
2506
2507 if ( fi.isSymLink() && (fi.isDir() && !m_bFollowDirLinks || !fi.isDir() && !m_bFollowFileLinks) )
2508 {
2509 m_pStatusInfo->addText(i18n("copyLink( %1 -> %2 )").arg(srcName).arg(destName));
2510 #ifdef _WIN32
2511 // What are links?
2512 #else
2513 if ( m_bSimulatedMergeStarted )
2514 {
2515 return true;
2516 }
2517 FileAccess destFi(destName);
2518 if ( !destFi.isLocal() || !fi.isLocal() )
2519 {
2520 m_pStatusInfo->addText(i18n("Error: copyLink failed: Remote links are not yet supported."));
2521 return false;
2522 }
2523 QString linkTarget = fi.readLink();
2524 bool bSuccess = FileAccess::symLink( linkTarget, destName );
2525 if (!bSuccess)
2526 m_pStatusInfo->addText(i18n("Error: copyLink failed."));
2527 return bSuccess;
2528 #endif
2529 }
2530
2531 if ( fi.isDir() )
2532 {
2533 bool bSuccess = makeDir( destName );
2534 return bSuccess;
2535 }
2536
2537 int pos=destName.lastIndexOf('/');
2538 if ( pos>0 )
2539 {
2540 QString parentName = destName.left(pos);
2541 bool bSuccess = makeDir(parentName, true /*quiet*/);
2542 if (!bSuccess)
2543 return false;
2544 }
2545
2546 m_pStatusInfo->addText(i18n("copy( %1 -> %2 )").arg(srcName).arg(destName));
2547
2548 if ( m_bSimulatedMergeStarted )
2549 {
2550 return true;
2551 }
2552
2553 FileAccess faSrc ( srcName );
2554 bool bSuccess = faSrc.copyFile( destName );
2555 if (! bSuccess ) m_pStatusInfo->addText( faSrc.getStatusText() );
2556 return bSuccess;
2557 }
2558
2559 // Rename is not an operation that can be selected by the user.
2560 // It will only be used to create backups.
2561 // Hence it will delete an existing destination without making a backup (of the old backup.)
2562 bool DirectoryMergeWindow::renameFLD( const QString& srcName, const QString& destName )
2563 {
2564 if ( srcName == destName )
2565 return true;
2566
2567 if ( FileAccess(destName, true).exists() )
2568 {
2569 bool bSuccess = deleteFLD( destName, false /*no backup*/ );
2570 if (!bSuccess)
2571 {
2572 m_pStatusInfo->addText( i18n("Error during rename( %1 -> %2 ): "
2573 "Cannot delete existing destination." ).arg(srcName).arg(destName));
2574 return false;
2575 }
2576 }
2577
2578 m_pStatusInfo->addText(i18n("rename( %1 -> %2 )").arg(srcName).arg(destName));
2579 if ( m_bSimulatedMergeStarted )
2580 {
2581 return true;
2582 }
2583
2584 bool bSuccess = FileAccess( srcName ).rename( destName );
2585 if (!bSuccess)
2586 {
2587 m_pStatusInfo->addText( i18n("Error: Rename failed.") );
2588 return false;
2589 }
2590
2591 return true;
2592 }
2593
2594 bool DirectoryMergeWindow::makeDir( const QString& name, bool bQuiet )
2595 {
2596 FileAccess fi(name, true);
2597 if( fi.exists() && fi.isDir() )
2598 return true;
2599
2600 if( fi.exists() && !fi.isDir() )
2601 {
2602 bool bSuccess = deleteFLD( name, true );
2603 if (!bSuccess)
2604 {
2605 m_pStatusInfo->addText( i18n("Error during makeDir of %1. "
2606 "Cannot delete existing file." ).arg(name));
2607 return false;
2608 }
2609 }
2610
2611 int pos=name.lastIndexOf('/');
2612 if ( pos>0 )
2613 {
2614 QString parentName = name.left(pos);
2615 bool bSuccess = makeDir(parentName,true);
2616 if (!bSuccess)
2617 return false;
2618 }
2619
2620 if ( ! bQuiet )
2621 m_pStatusInfo->addText(i18n("makeDir( %1 )").arg(name));
2622
2623 if ( m_bSimulatedMergeStarted )
2624 {
2625 return true;
2626 }
2627
2628 bool bSuccess = FileAccess::makeDir( name );
2629 if ( bSuccess == false )
2630 {
2631 m_pStatusInfo->addText( i18n("Error while creating directory.") );
2632 return false;
2633 }
2634 return true;
2635 }
2636
2637
2638 DirectoryMergeInfo::DirectoryMergeInfo( QWidget* pParent )
2639 : QFrame(pParent)
2640 {
2641 QVBoxLayout *topLayout = new QVBoxLayout( this );
2642 topLayout->setMargin(0);
2643
2644 QGridLayout *grid = new QGridLayout();
2645 topLayout->addLayout(grid);
2646 grid->setColumnStretch(1,10);
2647
2648 int line=0;
2649
2650 m_pA = new QLabel("A",this); grid->addWidget( m_pA,line, 0 );
2651 m_pInfoA = new QLabel(this); grid->addWidget( m_pInfoA,line,1 ); ++line;
2652 m_pB = new QLabel("B",this); grid->addWidget( m_pB,line, 0 );
2653 m_pInfoB = new QLabel(this); grid->addWidget( m_pInfoB,line,1 ); ++line;
2654 m_pC = new QLabel("C",this); grid->addWidget( m_pC,line, 0 );
2655 m_pInfoC = new QLabel(this); grid->addWidget( m_pInfoC,line,1 ); ++line;
2656 m_pDest = new QLabel(i18n("Dest"),this); grid->addWidget( m_pDest,line, 0 );
2657 m_pInfoDest = new QLabel(this); grid->addWidget( m_pInfoDest,line,1 ); ++line;
2658
2659 m_pInfoList = new QTreeWidget(this); topLayout->addWidget( m_pInfoList );
2660 m_pInfoList->setHeaderLabels( QStringList() << i18n("Dir") << i18n("Type") << i18n("Size")
2661 << i18n("Attr") << i18n("Last Modification") << i18n("Link-Destination") );
2662 setMinimumSize( 100,100 );
2663
2664 m_pInfoList->installEventFilter(this);
2665 m_pInfoList->setRootIsDecorated( false );
2666 }
2667
2668 bool DirectoryMergeInfo::eventFilter(QObject*o, QEvent* e)
2669 {
2670 if ( e->type()==QEvent::FocusIn && o==m_pInfoList )
2671 emit gotFocus();
2672 return false;
2673 }
2674
2675 static void addListViewItem( QTreeWidget* pListView, const QString& dir,
2676 const QString& basePath, FileAccess& fi )
2677 {
2678 if ( basePath.isEmpty() )
2679 {
2680 return;
2681 }
2682 else
2683 {
2684 if ( fi.exists() )
2685 {
2686 QString dateString = fi.lastModified().toString("yyyy-MM-dd hh:mm:ss");
2687
2688 new QTreeWidgetItem(
2689 pListView,
2690 QStringList() << dir <<
2691 QString( fi.isDir() ? i18n("Dir") : i18n("File") ) + (fi.isSymLink() ? "-Link" : "") <<
2692 QString::number(fi.size()) <<
2693 QString(fi.isReadable() ? "r" : " ") + (fi.isWritable()?"w" : " ")
2694 #ifdef _WIN32
2695 /*Future: Use GetFileAttributes()*/ <<
2696 #else
2697 + (fi.isExecutable()?"x" : " ") <<
2698 #endif
2699 dateString <<
2700 QString(fi.isSymLink() ? (" -> " + fi.readLink()) : QString(""))
2701 );
2702 }
2703 else
2704 {
2705 new QTreeWidgetItem(
2706 pListView,
2707 QStringList() << dir <<
2708 i18n("not available") <<
2709 "" <<
2710 "" <<
2711 "" <<
2712 ""
2713 );
2714 }
2715 }
2716 }
2717
2718 void DirectoryMergeInfo::setInfo(
2719 const FileAccess& dirA,
2720 const FileAccess& dirB,
2721 const FileAccess& dirC,
2722 const FileAccess& dirDest,
2723 MergeFileInfos& mfi )
2724 {
2725 bool bHideDest = false;
2726 if ( dirA.absFilePath()==dirDest.absFilePath() )
2727 {
2728 m_pA->setText( i18n("A (Dest): ") ); bHideDest=true;
2729 }
2730 else
2731 m_pA->setText( !dirC.isValid() ? QString("A: ") : i18n("A (Base): "));
2732
2733 m_pInfoA->setText( dirA.prettyAbsPath() );
2734
2735 if ( dirB.absFilePath()==dirDest.absFilePath() )
2736 {
2737 m_pB->setText( i18n("B (Dest): ") ); bHideDest=true;
2738 }
2739 else
2740 m_pB->setText( "B: " );
2741 m_pInfoB->setText( dirB.prettyAbsPath() );
2742
2743 if ( dirC.absFilePath()==dirDest.absFilePath() )
2744 {
2745 m_pC->setText( i18n("C (Dest): ") ); bHideDest=true;
2746 }
2747 else
2748 m_pC->setText( "C: " );
2749 m_pInfoC->setText( dirC.prettyAbsPath() );
2750
2751 m_pDest->setText( i18n("Dest: ") ); m_pInfoDest->setText( dirDest.prettyAbsPath() );
2752
2753 if (!dirC.isValid()) { m_pC->hide(); m_pInfoC->hide(); }
2754 else { m_pC->show(); m_pInfoC->show(); }
2755
2756 if (!dirDest.isValid()||bHideDest) { m_pDest->hide(); m_pInfoDest->hide(); }
2757 else { m_pDest->show(); m_pInfoDest->show(); }
2758
2759 m_pInfoList->clear();
2760 addListViewItem( m_pInfoList, "A", dirA.prettyAbsPath(), mfi.m_fileInfoA );
2761 addListViewItem( m_pInfoList, "B", dirB.prettyAbsPath(), mfi.m_fileInfoB );
2762 addListViewItem( m_pInfoList, "C", dirC.prettyAbsPath(), mfi.m_fileInfoC );
2763 if (!bHideDest)
2764 {
2765 FileAccess fiDest( dirDest.prettyAbsPath() + "/" + mfi.m_subPath, true );
2766 addListViewItem( m_pInfoList, i18n("Dest"), dirDest.prettyAbsPath(), fiDest );
2767 }
2768 for (int i=0;i<m_pInfoList->columnCount();++i)
2769 m_pInfoList->resizeColumnToContents ( i );
2770 }
2771
2772 QTextStream& operator<<( QTextStream& ts, MergeFileInfos& mfi )
2773 {
2774 ts << "{\n";
2775 ValueMap vm;
2776 vm.writeEntry( "SubPath", mfi.m_subPath );
2777 vm.writeEntry( "ExistsInA", mfi.m_bExistsInA );
2778 vm.writeEntry( "ExistsInB", mfi.m_bExistsInB );
2779 vm.writeEntry( "ExistsInC", mfi.m_bExistsInC );
2780 vm.writeEntry( "EqualAB", mfi.m_bEqualAB );
2781 vm.writeEntry( "EqualAC", mfi.m_bEqualAC );
2782 vm.writeEntry( "EqualBC", mfi.m_bEqualBC );
2783 //DirMergeItem* m_pDMI;
2784 //MergeFileInfos* m_pParent;
2785 vm.writeEntry( "MergeOperation", (int) mfi.m_eMergeOperation );
2786 vm.writeEntry( "DirA", mfi.m_bDirA );
2787 vm.writeEntry( "DirB", mfi.m_bDirB );
2788 vm.writeEntry( "DirC", mfi.m_bDirC );
2789 vm.writeEntry( "LinkA", mfi.m_bLinkA );
2790 vm.writeEntry( "LinkB", mfi.m_bLinkB );
2791 vm.writeEntry( "LinkC", mfi.m_bLinkC );
2792 vm.writeEntry( "OperationComplete", mfi.m_bOperationComplete );
2793 //bool m_bSimOpComplete );
2794
2795 vm.writeEntry( "AgeA", (int) mfi.m_ageA );
2796 vm.writeEntry( "AgeB", (int) mfi.m_ageB );
2797 vm.writeEntry( "AgeC", (int) mfi.m_ageC );
2798 vm.writeEntry( "ConflictingAges", mfi.m_bConflictingAges ); // Equal age but files are not!
2799
2800 //FileAccess m_fileInfoA;
2801 //FileAccess m_fileInfoB;
2802 //FileAccess m_fileInfoC;
2803
2804 //TotalDiffStatus m_totalDiffStatus;
2805
2806 vm.save(ts);
2807
2808 ts << "}\n";
2809
2810 return ts;
2811 }
2812
2813 void DirectoryMergeWindow::slotSaveMergeState()
2814 {
2815 //slotStatusMsg(i18n("Saving Directory Merge State ..."));
2816
2817 //QString s = KFileDialog::getSaveURL( QDir::currentDirPath(), 0, this, i18n("Save As...") ).url();
2818 QString s = KFileDialog::getSaveFileName( QDir::currentPath(), 0, this, i18n("Save Directory Merge State As...") );
2819 if(!s.isEmpty())
2820 {
2821 m_dirMergeStateFilename = s;
2822
2823
2824 QFile file(m_dirMergeStateFilename);
2825 bool bSuccess = file.open( QIODevice::WriteOnly );
2826 if ( bSuccess )
2827 {
2828 QTextStream ts( &file );
2829
2830 QTreeWidgetItemIterator it( this );
2831 while ( *it ) {
2832 DirMergeItem* item = static_cast<DirMergeItem*>(*it);
2833 MergeFileInfos* pMFI = item->m_pMFI;
2834 ts << *pMFI;
2835 ++it;
2836 }
2837 }
2838 }
2839
2840 //slotStatusMsg(i18n("Ready."));
2841
2842 }
2843
2844 void DirectoryMergeWindow::slotLoadMergeState()
2845 {
2846 }
2847
2848 void DirectoryMergeWindow::updateFileVisibilities()
2849 {
2850 bool bShowIdentical = m_pDirShowIdenticalFiles->isChecked();
2851 bool bShowDifferent = m_pDirShowDifferentFiles->isChecked();
2852 bool bShowOnlyInA = m_pDirShowFilesOnlyInA->isChecked();
2853 bool bShowOnlyInB = m_pDirShowFilesOnlyInB->isChecked();
2854 bool bShowOnlyInC = m_pDirShowFilesOnlyInC->isChecked();
2855 bool bThreeDirs = m_dirC.isValid();
2856 m_pSelection1Item = 0;
2857 m_pSelection2Item = 0;
2858 m_pSelection3Item = 0;
2859
2860 QTreeWidgetItem* p = topLevelItemCount()>0 ? topLevelItem(0) : 0;
2861 while(p)
2862 {
2863 DirMergeItem* pDMI = static_cast<DirMergeItem*>(p);
2864 MergeFileInfos* pMFI = pDMI->m_pMFI;
2865 bool bDir = pMFI->m_bDirA || pMFI->m_bDirB || pMFI->m_bDirC;
2866 bool bExistsEverywhere = pMFI->m_bExistsInA && pMFI->m_bExistsInB && (pMFI->m_bExistsInC || !bThreeDirs);
2867 int existCount = int(pMFI->m_bExistsInA) + int(pMFI->m_bExistsInB) + int(pMFI->m_bExistsInC);
2868 bool bVisible =
2869 ( bShowIdentical && bExistsEverywhere && pMFI->m_bEqualAB && (pMFI->m_bEqualAC || !bThreeDirs) )
2870 || ( (bShowDifferent||bDir) && existCount>=2 && (!pMFI->m_bEqualAB || !(pMFI->m_bEqualAC || !bThreeDirs)))
2871 || ( bShowOnlyInA && pMFI->m_bExistsInA && !pMFI->m_bExistsInB && !pMFI->m_bExistsInC )
2872 || ( bShowOnlyInB && !pMFI->m_bExistsInA && pMFI->m_bExistsInB && !pMFI->m_bExistsInC )
2873 || ( bShowOnlyInC && !pMFI->m_bExistsInA && !pMFI->m_bExistsInB && pMFI->m_bExistsInC );
2874
2875 QString fileName = pMFI->m_subPath.section( '/', -1 );
2876 bVisible = bVisible && (
2877 bDir && ! wildcardMultiMatch( m_pOptions->m_DmDirAntiPattern, fileName, m_bCaseSensitive )
2878 || wildcardMultiMatch( m_pOptions->m_DmFilePattern, fileName, m_bCaseSensitive )
2879 && !wildcardMultiMatch( m_pOptions->m_DmFileAntiPattern, fileName, m_bCaseSensitive ) );
2880
2881 p->setHidden(!bVisible);
2882 p = treeIterator( p, true, true );
2883 }
2884 }
2885
2886 void DirectoryMergeWindow::slotShowIdenticalFiles() { m_pOptions->m_bDmShowIdenticalFiles=m_pDirShowIdenticalFiles->isChecked();
2887 updateFileVisibilities(); }
2888 void DirectoryMergeWindow::slotShowDifferentFiles() { updateFileVisibilities(); }
2889 void DirectoryMergeWindow::slotShowFilesOnlyInA() { updateFileVisibilities(); }
2890 void DirectoryMergeWindow::slotShowFilesOnlyInB() { updateFileVisibilities(); }
2891 void DirectoryMergeWindow::slotShowFilesOnlyInC() { updateFileVisibilities(); }
2892
2893 void DirectoryMergeWindow::slotSynchronizeDirectories() { }
2894 void DirectoryMergeWindow::slotChooseNewerFiles() { }
2895
2896 void DirectoryMergeWindow::initDirectoryMergeActions( QObject* pKDiff3App, KActionCollection* ac )
2897 {
2898 #include "xpm/startmerge.xpm"
2899 #include "xpm/showequalfiles.xpm"
2900 #include "xpm/showfilesonlyina.xpm"
2901 #include "xpm/showfilesonlyinb.xpm"
2902 #include "xpm/showfilesonlyinc.xpm"
2903 DirectoryMergeWindow* p = this;
2904
2905 m_pDirStartOperation = new KAction(i18n("Start/Continue Directory Merge"), Qt::Key_F7, p, SLOT(slotRunOperationForAllItems()), ac, "dir_start_operation");
2906 m_pDirRunOperationForCurrentItem = new KAction(i18n("Run Operation for Current Item"), Qt::Key_F6, p, SLOT(slotRunOperationForCurrentItem()), ac, "dir_run_operation_for_current_item");
2907 m_pDirCompareCurrent = new KAction(i18n("Compare Selected File"), 0, p, SLOT(compareCurrentFile()), ac, "dir_compare_current");
2908 m_pDirMergeCurrent = new KAction(i18n("Merge Current File"), QIcon(QPixmap(startmerge)), 0, pKDiff3App, SLOT(slotMergeCurrentFile()), ac, "merge_current");
2909 m_pDirFoldAll = new KAction(i18n("Fold All Subdirs"), 0, p, SLOT(slotFoldAllSubdirs()), ac, "dir_fold_all");
2910 m_pDirUnfoldAll = new KAction(i18n("Unfold All Subdirs"), 0, p, SLOT(slotUnfoldAllSubdirs()), ac, "dir_unfold_all");
2911 m_pDirRescan = new KAction(i18n("Rescan"), Qt::SHIFT+Qt::Key_F5, p, SLOT(reload()), ac, "dir_rescan");
2912 m_pDirSaveMergeState = 0; //new KAction(i18n("Save Directory Merge State ..."), 0, p, SLOT(slotSaveMergeState()), ac, "dir_save_merge_state");
2913 m_pDirLoadMergeState = 0; //new KAction(i18n("Load Directory Merge State ..."), 0, p, SLOT(slotLoadMergeState()), ac, "dir_load_merge_state");
2914 m_pDirChooseAEverywhere = new KAction(i18n("Choose A for All Items"), 0, p, SLOT(slotChooseAEverywhere()), ac, "dir_choose_a_everywhere");
2915 m_pDirChooseBEverywhere = new KAction(i18n("Choose B for All Items"), 0, p, SLOT(slotChooseBEverywhere()), ac, "dir_choose_b_everywhere");
2916 m_pDirChooseCEverywhere = new KAction(i18n("Choose C for All Items"), 0, p, SLOT(slotChooseCEverywhere()), ac, "dir_choose_c_everywhere");
2917 m_pDirAutoChoiceEverywhere = new KAction(i18n("Auto-Choose Operation for All Items"), 0, p, SLOT(slotAutoChooseEverywhere()), ac, "dir_autochoose_everywhere");
2918 m_pDirDoNothingEverywhere = new KAction(i18n("No Operation for All Items"), 0, p, SLOT(slotNoOpEverywhere()), ac, "dir_nothing_everywhere");
2919
2920 // m_pDirSynchronizeDirectories = new KToggleAction(i18n("Synchronize Directories"), 0, this, SLOT(slotSynchronizeDirectories()), ac, "dir_synchronize_directories");
2921 // m_pDirChooseNewerFiles = new KToggleAction(i18n("Copy Newer Files Instead of Merging"), 0, this, SLOT(slotChooseNewerFiles()), ac, "dir_choose_newer_files");
2922
2923 m_pDirShowIdenticalFiles = new KToggleAction(i18n("Show Identical Files"), QIcon(QPixmap(showequalfiles)), 0, this, SLOT(slotShowIdenticalFiles()), ac, "dir_show_identical_files");
2924 m_pDirShowDifferentFiles = new KToggleAction(i18n("Show Different Files"), 0, this, SLOT(slotShowDifferentFiles()), ac, "dir_show_different_files");
2925 m_pDirShowFilesOnlyInA = new KToggleAction(i18n("Show Files only in A"), QIcon(QPixmap(showfilesonlyina)), 0, this, SLOT(slotShowFilesOnlyInA()), ac, "dir_show_files_only_in_a");
2926 m_pDirShowFilesOnlyInB = new KToggleAction(i18n("Show Files only in B"), QIcon(QPixmap(showfilesonlyinb)), 0, this, SLOT(slotShowFilesOnlyInB()), ac, "dir_show_files_only_in_b");
2927 m_pDirShowFilesOnlyInC = new KToggleAction(i18n("Show Files only in C"), QIcon(QPixmap(showfilesonlyinc)), 0, this, SLOT(slotShowFilesOnlyInC()), ac, "dir_show_files_only_in_c");
2928
2929 m_pDirShowIdenticalFiles->setChecked( m_pOptions->m_bDmShowIdenticalFiles );
2930
2931 m_pDirCompareExplicit = new KAction(i18n("Compare Explicitly Selected Files"), 0, p, SLOT(slotCompareExplicitlySelectedFiles()), ac, "dir_compare_explicitly_selected_files");
2932 m_pDirMergeExplicit = new KAction(i18n("Merge Explicitly Selected Files"), 0, p, SLOT(slotMergeExplicitlySelectedFiles()), ac, "dir_merge_explicitly_selected_files");
2933
2934 m_pDirCurrentDoNothing = new KAction(i18n("Do Nothing"), 0, p, SLOT(slotCurrentDoNothing()), ac, "dir_current_do_nothing");
2935 m_pDirCurrentChooseA = new KAction(i18n("A"), 0, p, SLOT(slotCurrentChooseA()), ac, "dir_current_choose_a");
2936 m_pDirCurrentChooseB = new KAction(i18n("B"), 0, p, SLOT(slotCurrentChooseB()), ac, "dir_current_choose_b");
2937 m_pDirCurrentChooseC = new KAction(i18n("C"), 0, p, SLOT(slotCurrentChooseC()), ac, "dir_current_choose_c");
2938 m_pDirCurrentMerge = new KAction(i18n("Merge"), 0, p, SLOT(slotCurrentMerge()), ac, "dir_current_merge");
2939 m_pDirCurrentDelete = new KAction(i18n("Delete (if exists)"), 0, p, SLOT(slotCurrentDelete()), ac, "dir_current_delete");
2940
2941 m_pDirCurrentSyncDoNothing = new KAction(i18n("Do Nothing"), 0, p, SLOT(slotCurrentDoNothing()), ac, "dir_current_sync_do_nothing");
2942 m_pDirCurrentSyncCopyAToB = new KAction(i18n("Copy A to B"), 0, p, SLOT(slotCurrentCopyAToB()), ac, "dir_current_sync_copy_a_to_b" );
2943 m_pDirCurrentSyncCopyBToA = new KAction(i18n("Copy B to A"), 0, p, SLOT(slotCurrentCopyBToA()), ac, "dir_current_sync_copy_b_to_a" );
2944 m_pDirCurrentSyncDeleteA = new KAction(i18n("Delete A"), 0, p, SLOT(slotCurrentDeleteA()), ac,"dir_current_sync_delete_a");
2945 m_pDirCurrentSyncDeleteB = new KAction(i18n("Delete B"), 0, p, SLOT(slotCurrentDeleteB()), ac,"dir_current_sync_delete_b");
2946 m_pDirCurrentSyncDeleteAAndB = new KAction(i18n("Delete A && B"), 0, p, SLOT(slotCurrentDeleteAAndB()), ac,"dir_current_sync_delete_a_and_b");
2947 m_pDirCurrentSyncMergeToA = new KAction(i18n("Merge to A"), 0, p, SLOT(slotCurrentMergeToA()), ac,"dir_current_sync_merge_to_a");
2948 m_pDirCurrentSyncMergeToB = new KAction(i18n("Merge to B"), 0, p, SLOT(slotCurrentMergeToB()), ac,"dir_current_sync_merge_to_b");
2949 m_pDirCurrentSyncMergeToAAndB = new KAction(i18n("Merge to A && B"), 0, p, SLOT(slotCurrentMergeToAAndB()), ac,"dir_current_sync_merge_to_a_and_b");
2950
2951
2952 }
2953
2954
2955 void DirectoryMergeWindow::updateAvailabilities( bool bDirCompare, bool bDiffWindowVisible,
2956 KToggleAction* chooseA, KToggleAction* chooseB, KToggleAction* chooseC )
2957 {
2958 m_pDirStartOperation->setEnabled( bDirCompare );
2959 m_pDirRunOperationForCurrentItem->setEnabled( bDirCompare );
2960 m_pDirFoldAll->setEnabled( bDirCompare );
2961 m_pDirUnfoldAll->setEnabled( bDirCompare );
2962
2963 m_pDirCompareCurrent->setEnabled( bDirCompare && isVisible() && isFileSelected() );
2964
2965 m_pDirMergeCurrent->setEnabled( bDirCompare && isVisible() && isFileSelected()
2966 || bDiffWindowVisible );
2967
2968 m_pDirRescan->setEnabled( bDirCompare );
2969
2970 m_pDirAutoChoiceEverywhere->setEnabled( bDirCompare && isVisible() );
2971 m_pDirDoNothingEverywhere->setEnabled( bDirCompare && isVisible() );
2972 m_pDirChooseAEverywhere->setEnabled( bDirCompare && isVisible() );
2973 m_pDirChooseBEverywhere->setEnabled( bDirCompare && isVisible() );
2974 m_pDirChooseCEverywhere->setEnabled( bDirCompare && isVisible() );
2975
2976 bool bThreeDirs = m_dirC.isValid();
2977
2978 QTreeWidgetItem* lvi = currentItem();
2979 DirMergeItem* pDMI = lvi==0 ? 0 : static_cast<DirMergeItem*>(lvi);
2980 MergeFileInfos* pMFI = pDMI==0 ? 0 : pDMI->m_pMFI;
2981
2982 bool bItemActive = bDirCompare && isVisible() && pMFI!=0;// && hasFocus();
2983 bool bMergeMode = bThreeDirs || !m_bSyncMode;
2984 bool bFTConflict = pMFI==0 ? false : conflictingFileTypes(*pMFI);
2985
2986 bool bDirWindowHasFocus = isVisible() && hasFocus();
2987
2988 m_pDirShowIdenticalFiles->setEnabled( bDirCompare && isVisible() );
2989 m_pDirShowDifferentFiles->setEnabled( bDirCompare && isVisible() );
2990 m_pDirShowFilesOnlyInA->setEnabled( bDirCompare && isVisible() );
2991 m_pDirShowFilesOnlyInB->setEnabled( bDirCompare && isVisible() );
2992 m_pDirShowFilesOnlyInC->setEnabled( bDirCompare && isVisible() && bThreeDirs );
2993
2994 m_pDirCompareExplicit->setEnabled( bDirCompare && isVisible() && m_pSelection2Item!=0 );
2995 m_pDirMergeExplicit->setEnabled( bDirCompare && isVisible() && m_pSelection2Item!=0 );
2996
2997 m_pDirCurrentDoNothing->setEnabled( bItemActive && bMergeMode );
2998 m_pDirCurrentChooseA->setEnabled( bItemActive && bMergeMode && pMFI->m_bExistsInA );
2999 m_pDirCurrentChooseB->setEnabled( bItemActive && bMergeMode && pMFI->m_bExistsInB );
3000 m_pDirCurrentChooseC->setEnabled( bItemActive && bMergeMode && pMFI->m_bExistsInC );
3001 m_pDirCurrentMerge->setEnabled( bItemActive && bMergeMode && !bFTConflict );
3002 m_pDirCurrentDelete->setEnabled( bItemActive && bMergeMode );
3003 if ( bDirWindowHasFocus )
3004 {
3005 chooseA->setEnabled( bItemActive && pMFI->m_bExistsInA );
3006 chooseB->setEnabled( bItemActive && pMFI->m_bExistsInB );
3007 chooseC->setEnabled( bItemActive && pMFI->m_bExistsInC );
3008 chooseA->setChecked( false );
3009 chooseB->setChecked( false );
3010 chooseC->setChecked( false );
3011 }
3012
3013 m_pDirCurrentSyncDoNothing->setEnabled( bItemActive && !bMergeMode );
3014 m_pDirCurrentSyncCopyAToB->setEnabled( bItemActive && !bMergeMode && pMFI->m_bExistsInA );
3015 m_pDirCurrentSyncCopyBToA->setEnabled( bItemActive && !bMergeMode && pMFI->m_bExistsInB );
3016 m_pDirCurrentSyncDeleteA->setEnabled( bItemActive && !bMergeMode && pMFI->m_bExistsInA );
3017 m_pDirCurrentSyncDeleteB->setEnabled( bItemActive && !bMergeMode && pMFI->m_bExistsInB );
3018 m_pDirCurrentSyncDeleteAAndB->setEnabled( bItemActive && !bMergeMode && pMFI->m_bExistsInB && pMFI->m_bExistsInB );
3019 m_pDirCurrentSyncMergeToA->setEnabled( bItemActive && !bMergeMode && !bFTConflict );
3020 m_pDirCurrentSyncMergeToB->setEnabled( bItemActive && !bMergeMode && !bFTConflict );
3021 m_pDirCurrentSyncMergeToAAndB->setEnabled( bItemActive && !bMergeMode && !bFTConflict );
3022 }
3023
3024
3025 //#include "directorymergewindow.moc"