comparison kdiff3/src/directorymergewindow.cpp @ 8:86d21651c8db

KDiff3 version 0.9.70
author joachim99
date Mon, 06 Oct 2003 18:50:45 +0000
parents
children d101155f5c52
comparison
equal deleted inserted replaced
7:ff98a43bbfea 8:86d21651c8db
1 /***************************************************************************
2 directorymergewindow.cpp
3 -------------------
4 begin : Sat Oct 19 2002
5 copyright : (C) 2002 by Joachim Eibl
6 email : joachim.eibl@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 /***************************************************************************
19 * $Log$
20 * Revision 1.1 2003/10/06 18:38:48 joachim99
21 * KDiff3 version 0.9.70
22 * *
23 ***************************************************************************/
24
25 #include "directorymergewindow.h"
26 #include "optiondialog.h"
27 #include <vector>
28 #include <map>
29
30 #include <qdir.h>
31 #include <qapplication.h>
32 #include <qpixmap.h>
33 #include <qimage.h>
34 #include <kpopupmenu.h>
35 #include <qregexp.h>
36 #include <qmessagebox.h>
37 #include <qlayout.h>
38 #include <qlabel.h>
39 #include <qtable.h>
40 #include <qsplitter.h>
41 #include <qprogressdialog.h>
42 #include <kmessagebox.h>
43 #include <kiconloader.h>
44 #include <klocale.h>
45 #include <iostream>
46 #include <assert.h>
47
48
49 class StatusInfo : public QListView
50 {
51 public:
52 StatusInfo(QWidget* pParent) : QListView( pParent )
53 {
54 addColumn("");
55 setSorting(-1); //disable sorting
56 }
57
58 QListViewItem* m_pLast;
59 QListViewItem* last()
60 {
61 if (firstChild()==0) return 0;
62 else return m_pLast;
63 }
64
65 void addText(const QString& s )
66 {
67 if (firstChild()==0) m_pLast = new QListViewItem( this, s );
68 else m_pLast = new QListViewItem( this, last(), s );
69 }
70 };
71
72
73 class TempRemover
74 {
75 public:
76 TempRemover( const QString& origName, FileAccess& fa );
77 ~TempRemover();
78 QString name() { return m_name; }
79 bool success() { return m_bSuccess; }
80 private:
81 QString m_name;
82 bool m_bTemp;
83 bool m_bSuccess;
84 };
85 TempRemover::TempRemover(const QString& origName, FileAccess& fa)
86 {
87 if ( fa.isLocal() )
88 {
89 m_name = origName;
90 m_bTemp = false;
91 m_bSuccess = true;
92 }
93 else
94 {
95 m_name = FileAccess::tempFileName();
96 m_bSuccess = fa.copyFile( m_name );
97 m_bTemp = m_bSuccess;
98 }
99 }
100 TempRemover::~TempRemover()
101 {
102 if ( m_bTemp && ! m_name.isEmpty() )
103 FileAccess::removeFile(m_name);
104 }
105
106 void DirectoryMergeWindow::fastFileComparison(
107 FileAccess& fi1, FileAccess& fi2,
108 bool& bEqual, bool& bError, QString& status )
109 {
110 status = "";
111 bEqual = false;
112 bError = true;
113
114 if ( !m_bFollowFileLinks )
115 {
116 if ( fi1.isSymLink() != fi2.isSymLink() )
117 {
118 status = "Mix of links and normal files.";
119 return;
120 }
121 else if ( fi1.isSymLink() && fi2.isSymLink() )
122 {
123 bError = false;
124 bEqual = fi1.readLink() == fi2.readLink();
125 status = "Link: ";
126 return;
127 }
128 }
129
130 if ( fi1.size()!=fi2.size() )
131 {
132 bEqual = false;
133 status = "Size. ";
134 return;
135 }
136
137 if ( m_pOptions->m_bDmTrustDate )
138 {
139 bEqual = ( fi1.lastModified() == fi2.lastModified() && fi1.size()==fi2.size() );
140 bError = false;
141 status = "Date&Size: ";
142 return;
143 }
144
145 QString fileName1 = fi1.absFilePath();
146 QString fileName2 = fi2.absFilePath();
147 TempRemover tr1( fileName1, fi1 );
148 if ( !tr1.success() )
149 {
150 status = "Creating temp copy of " + fileName1 + " failed.";
151 return;
152 }
153 TempRemover tr2( fileName2, fi2 );
154 if ( !tr2.success() )
155 {
156 status = "Creating temp copy of " + fileName2 + " failed.";
157 return;
158 }
159
160 std::vector<char> buf1(100000);
161 std::vector<char> buf2(buf1.size());
162
163 QFile file1( tr1.name() );
164
165 if ( ! file1.open(IO_ReadOnly) )
166 {
167 status = "Opening " + fileName1 + " failed.";
168 return;
169 }
170
171 QFile file2( tr2.name() );
172
173 if ( ! file2.open(IO_ReadOnly) )
174 {
175 status = "Opening " + fileName2 + " failed.";
176 return;
177 }
178
179 #if QT_VERSION==230
180 typedef int t_FileSize;
181 #else
182 typedef QFile::Offset t_FileSize;
183 #endif
184 t_FileSize size = file1.size();
185
186 while( size>0 )
187 {
188 int len = min2( size, (t_FileSize)buf1.size() );
189 if( len != file1.readBlock( &buf1[0], len ) )
190 {
191 status = "Error reading from " + fileName1;
192 return;
193 }
194
195 if( len != file2.readBlock( &buf2[0], len ) )
196 {
197 status = "Error reading from " + fileName2;
198 return;
199 }
200
201 if ( memcmp( &buf1[0], &buf2[0], len ) != 0 )
202 {
203 bError = false;
204 return;
205 }
206 size-=len;
207 }
208
209 // If the program really arrives here, then the files are really equal.
210 bError = false;
211 bEqual = true;
212 }
213
214
215
216
217
218 static int s_nameCol = 0;
219 static int s_ACol = 1;
220 static int s_BCol = 2;
221 static int s_CCol = 3;
222 static int s_OpCol = 4;
223 static int s_OpStatusCol = 5;
224 DirectoryMergeWindow::DirectoryMergeWindow( QWidget* pParent, OptionDialog* pOptions, KIconLoader* pIconLoader )
225 : QListView( pParent )
226 {
227 connect( this, SIGNAL(doubleClicked(QListViewItem*)), this, SLOT(onDoubleClick(QListViewItem*)));
228 connect( this, SIGNAL(returnPressed(QListViewItem*)), this, SLOT(onDoubleClick(QListViewItem*)));
229 connect( this, SIGNAL( pressed(QListViewItem*,const QPoint&, int)),
230 this, SLOT( onClick(QListViewItem*,const QPoint&, int)) );
231 connect( this, SIGNAL(selectionChanged(QListViewItem*)), this, SLOT(onSelectionChanged(QListViewItem*)));
232 m_pOptions = pOptions;
233 m_pIconLoader = pIconLoader;
234 m_pDirectoryMergeInfo = 0;
235 m_bAllowResizeEvents = true;
236 m_bSimulatedMergeStarted=false;
237 m_bRealMergeStarted=false;
238 m_bSingleFileOperationStarted=false;
239 m_bError = false;
240 m_bSyncMode = false;
241 m_pStatusInfo = new StatusInfo(0);
242 m_pStatusInfo->hide();
243
244 addColumn("Name");
245 addColumn("A");
246 addColumn("B");
247 addColumn("C");
248 addColumn("Operation");
249 addColumn("Status");
250 }
251
252 DirectoryMergeWindow::~DirectoryMergeWindow()
253 {
254 }
255
256
257 int DirectoryMergeWindow::totalColumnWidth()
258 {
259 int w=0;
260 for (int i=0; i<s_OpStatusCol; ++i)
261 {
262 w += columnWidth(i);
263 }
264 return w;
265 }
266
267 void DirectoryMergeWindow::reload()
268 {
269 if ( isDirectoryMergeInProgress() )
270 {
271 int result = KMessageBox::warningYesNo(this,
272 i18n("You are currently doing a directory merge. Are you sure, you want to abort the merge and rescan the directory?"),
273 i18n("Warning"), i18n("Yes - Rescan"), i18n("No - Continue merging") );
274 if ( result!=KMessageBox::Yes )
275 return;
276 }
277 init( m_dirA, m_dirB, m_dirC, m_dirDest, m_bDirectoryMerge );
278 }
279
280 // Copy pm2 onto pm1, but preserve the alpha value from pm1 where pm2 is transparent.
281 static QPixmap pixCombiner( const QPixmap& pm1, const QPixmap& pm2 )
282 {
283 QImage img1 = pm1.convertToImage().convertDepth(32);
284 QImage img2 = pm2.convertToImage().convertDepth(32);
285
286 for (int y = 0; y < img1.height(); y++)
287 {
288 Q_UINT32 *line1 = reinterpret_cast<Q_UINT32 *>(img1.scanLine(y));
289 Q_UINT32 *line2 = reinterpret_cast<Q_UINT32 *>(img2.scanLine(y));
290 for (int x = 0; x < img1.width(); x++)
291 {
292 if ( qAlpha( line2[x] ) >0 )
293 line1[x] = (line2[x] | 0xff000000);
294 }
295 }
296 QPixmap pix;
297 pix.convertFromImage(img1);
298 return pix;
299 }
300
301 // like pixCombiner but let the pm1 color shine through
302 static QPixmap pixCombiner2( const QPixmap& pm1, const QPixmap& pm2 )
303 {
304 QImage img1 = pm1.convertToImage().convertDepth(32);
305 QImage img2 = pm2.convertToImage().convertDepth(32);
306
307 for (int y = 0; y < img1.height(); y++)
308 {
309 Q_UINT32 *line1 = reinterpret_cast<Q_UINT32 *>(img1.scanLine(y));
310 Q_UINT32 *line2 = reinterpret_cast<Q_UINT32 *>(img2.scanLine(y));
311 for (int x = 0; x < img1.width(); x++)
312 {
313 if ( qAlpha( line2[x] ) >0 )
314 {
315 int r = ( qRed( line1[x] ) + qRed( line2[x] ))/2;
316 int g = ( qGreen( line1[x] ) + qGreen( line2[x] ))/2;
317 int b = ( qBlue( line1[x] ) + qBlue( line2[x] ))/2;
318 line1[x] = qRgba( r,g,b, 0xff );
319 }
320 }
321 }
322 QPixmap pix;
323 pix.convertFromImage(img1);
324 return pix;
325 }
326
327 static void calcDirStatus( bool bThreeDirs, DirMergeItem* i, int& nofFiles,
328 int& nofDirs, int& nofEqualFiles, int& nofManualMerges )
329 {
330 if ( i->m_pMFI->m_bDirA || i->m_pMFI->m_bDirB || i->m_pMFI->m_bDirC )
331 {
332 ++nofDirs;
333 }
334 else
335 {
336 ++nofFiles;
337 if ( i->m_pMFI->m_bEqualAB && (!bThreeDirs || i->m_pMFI->m_bEqualAC ))
338 {
339 ++nofEqualFiles;
340 }
341 else
342 {
343 if ( i->m_pMFI->m_eMergeOperation==eMergeABCToDest || i->m_pMFI->m_eMergeOperation==eMergeABToDest )
344 ++nofManualMerges;
345 }
346 }
347 for( QListViewItem* p = i->firstChild(); p!=0; p = p->nextSibling() )
348 calcDirStatus( bThreeDirs, static_cast<DirMergeItem*>(p), nofFiles, nofDirs, nofEqualFiles, nofManualMerges );
349 }
350
351 bool DirectoryMergeWindow::init
352 (
353 FileAccess& dirA,
354 FileAccess& dirB,
355 FileAccess& dirC,
356 FileAccess& dirDest,
357 bool bDirectoryMerge
358 )
359 {
360 m_bFollowDirLinks = m_pOptions->m_bDmFollowDirLinks;
361 m_bFollowFileLinks = m_pOptions->m_bDmFollowFileLinks;
362 m_bSimulatedMergeStarted=false;
363 m_bRealMergeStarted=false;
364 m_bError=false;
365 m_bDirectoryMerge = bDirectoryMerge;
366
367 clear();
368
369 m_pCurrentItemForOperation = 0;
370
371 m_dirA = dirA;
372 m_dirB = dirB;
373 m_dirC = dirC;
374 m_dirDest = dirDest;
375
376 // Check if all input directories exist and are valid. The dest dir is not tested now.
377 // The test will happen only when we are going to write to it.
378 if ( !m_dirA.isDir() || !m_dirB.isDir() ||
379 (m_dirC.isValid() && !m_dirC.isDir()) )
380 {
381 QString text( i18n("Opening of directories failed:") );
382 text += "\n\n";
383 if ( !dirA.isDir() )
384 { text += "Dir A \"" + m_dirA.prettyAbsPath() + "\" does not exist or is not a directory.\n"; }
385
386 if ( !dirB.isDir() )
387 { text += "Dir B \"" + m_dirB.prettyAbsPath() + "\" does not exist or is not a directory.\n"; }
388
389 if ( m_dirC.isValid() && !m_dirC.isDir() )
390 { text += "Dir C \"" + m_dirC.prettyAbsPath() + "\" does not exist or is not a directory.\n"; }
391
392 KMessageBox::sorry( this, text, i18n("Directory open error") );
393 return false;
394 }
395
396 if ( m_dirC.isValid() &&
397 (m_dirDest.prettyAbsPath() == m_dirA.prettyAbsPath() || m_dirDest.prettyAbsPath()==m_dirB.prettyAbsPath() ) )
398 {
399 KMessageBox::error(this,
400 i18n( "The destination directory must not be the same as A or B when"
401 "three directories are merged.\nCheck again before continuing."),
402 "KDiff3: Parameter warning");
403 return false;
404 }
405
406 m_bSyncMode = m_pOptions->m_bDmSyncMode && !m_dirC.isValid() && !m_dirDest.isValid();
407
408 if ( m_dirDest.isValid() )
409 m_dirDestInternal = m_dirDest;
410 else
411 m_dirDestInternal = m_dirC.isValid() ? m_dirC : m_dirB;
412
413 QString origCurrentDirectory = QDir::currentDirPath();
414
415 g_pProgressDialog->start();
416
417 m_fileMergeMap.clear();
418 t_DirectoryList::iterator i;
419
420 // calc how many directories will be read:
421 double nofScans = ( m_dirA.isValid() ? 1 : 0 )+( m_dirB.isValid() ? 1 : 0 )+( m_dirC.isValid() ? 1 : 0 );
422 int currentScan = 0;
423
424 bool bListDirSuccessA = true;
425 bool bListDirSuccessB = true;
426 bool bListDirSuccessC = true;
427 if ( m_dirA.isValid() )
428 {
429 g_pProgressDialog->setInformation("Reading Directory A");
430 g_pProgressDialog->setSubRangeTransformation(currentScan/nofScans, (currentScan+1)/nofScans);
431 ++currentScan;
432
433 t_DirectoryList dirListA;
434 bListDirSuccessA = m_dirA.listDir( &dirListA,
435 m_pOptions->m_bDmRecursiveDirs, m_pOptions->m_bDmFindHidden,
436 m_pOptions->m_DmFilePattern, m_pOptions->m_DmFileAntiPattern,
437 m_pOptions->m_DmDirAntiPattern, m_pOptions->m_bDmFollowDirLinks,
438 m_pOptions->m_bDmUseCvsIgnore);
439
440 for (i=dirListA.begin(); i!=dirListA.end();++i )
441 {
442 MergeFileInfos& mfi = m_fileMergeMap[i->filePath()];
443 //std::cout <<i->filePath()<<std::endl;
444 mfi.m_bExistsInA = true;
445 mfi.m_fileInfoA = *i;
446 }
447 }
448
449 if ( m_dirB.isValid() )
450 {
451 g_pProgressDialog->setInformation("Reading Directory B");
452 g_pProgressDialog->setSubRangeTransformation(currentScan/nofScans, (currentScan+1)/nofScans);
453 ++currentScan;
454
455 t_DirectoryList dirListB;
456 bListDirSuccessB = m_dirB.listDir( &dirListB,
457 m_pOptions->m_bDmRecursiveDirs, m_pOptions->m_bDmFindHidden,
458 m_pOptions->m_DmFilePattern, m_pOptions->m_DmFileAntiPattern,
459 m_pOptions->m_DmDirAntiPattern, m_pOptions->m_bDmFollowDirLinks,
460 m_pOptions->m_bDmUseCvsIgnore);
461
462 for (i=dirListB.begin(); i!=dirListB.end();++i )
463 {
464 MergeFileInfos& mfi = m_fileMergeMap[i->filePath()];
465 mfi.m_bExistsInB = true;
466 mfi.m_fileInfoB = *i;
467 }
468 }
469
470 e_MergeOperation eDefaultMergeOp;
471 if ( m_dirC.isValid() )
472 {
473 g_pProgressDialog->setInformation("Reading Directory C");
474 g_pProgressDialog->setSubRangeTransformation(currentScan/nofScans, (currentScan+1)/nofScans);
475 ++currentScan;
476
477 t_DirectoryList dirListC;
478 bListDirSuccessC = m_dirC.listDir( &dirListC,
479 m_pOptions->m_bDmRecursiveDirs, m_pOptions->m_bDmFindHidden,
480 m_pOptions->m_DmFilePattern, m_pOptions->m_DmFileAntiPattern,
481 m_pOptions->m_DmDirAntiPattern, m_pOptions->m_bDmFollowDirLinks,
482 m_pOptions->m_bDmUseCvsIgnore);
483
484 for (i=dirListC.begin(); i!=dirListC.end();++i )
485 {
486 MergeFileInfos& mfi = m_fileMergeMap[i->filePath()];
487 mfi.m_bExistsInC = true;
488 mfi.m_fileInfoC = *i;
489 }
490
491 eDefaultMergeOp = eMergeABCToDest;
492 }
493 else
494 eDefaultMergeOp = m_bSyncMode ? eMergeToAB : eMergeABToDest;
495
496 bool bContinue = true;
497 if ( !bListDirSuccessA || !bListDirSuccessB || !bListDirSuccessC )
498 {
499 QString s = i18n("Some subdirectories were not readable in");
500 if ( !bListDirSuccessA ) s += "\nA: " + m_dirA.prettyAbsPath();
501 if ( !bListDirSuccessB ) s += "\nB: " + m_dirB.prettyAbsPath();
502 if ( !bListDirSuccessC ) s += "\nC: " + m_dirC.prettyAbsPath();
503 s+="\n";
504 s+= i18n("Check the permissions of the subdirectories.");
505 bContinue = KMessageBox::Continue == KMessageBox::warningContinueCancel( this, s );
506 }
507
508 if ( bContinue )
509 {
510 prepareListView();
511 g_pProgressDialog->hide();
512
513 for( QListViewItem* p = firstChild(); p!=0; p = p->nextSibling() )
514 {
515 DirMergeItem* pDMI = static_cast<DirMergeItem*>( p );
516 calcSuggestedOperation( *pDMI->m_pMFI, eDefaultMergeOp );
517 }
518 }
519 else
520 {
521 g_pProgressDialog->hide();
522 setSelected( 0, true );
523 }
524
525 QDir::setCurrent(origCurrentDirectory);
526
527 // Try to improve the view a little bit.
528 QWidget* pParent = parentWidget();
529 QSplitter* pSplitter = static_cast<QSplitter*>(pParent);
530 if (pSplitter!=0)
531 {
532 QValueList<int> sizes = pSplitter->sizes();
533 int total = sizes[0] + sizes[1];
534 sizes[0]=total*6/10;
535 sizes[1]=total - sizes[0];
536 pSplitter->setSizes( sizes );
537 }
538
539 if ( bContinue )
540 {
541 // Generate a status report
542 int nofFiles=0;
543 int nofDirs=0;
544 int nofEqualFiles=0;
545 int nofManualMerges=0;
546 for( QListViewItem* p = firstChild(); p!=0; p = p->nextSibling() )
547 calcDirStatus( m_dirC.isValid(), static_cast<DirMergeItem*>(p),
548 nofFiles, nofDirs, nofEqualFiles, nofManualMerges );
549
550 QString s;
551 s = i18n("Directory Comparison Status:") + "\n\n" +
552 i18n("Number of subdirectories:") +" "+ QString::number(nofDirs) + "\n"+
553 i18n("Number of equal files:") +" "+ QString::number(nofEqualFiles) + "\n"+
554 i18n("Number of different files:") +" "+ QString::number(nofFiles-nofEqualFiles);
555
556 if ( m_dirC.isValid() )
557 s += "\n" + i18n("Number of manual merges:") +" "+ QString::number(nofManualMerges);
558 KMessageBox::information( this, s );
559 setSelected( firstChild(), true );
560 }
561
562 return true;
563 }
564
565
566
567 void DirectoryMergeWindow::slotChooseAEverywhere(){ setAllMergeOperations( eCopyAToDest ); }
568
569 void DirectoryMergeWindow::slotChooseBEverywhere(){ setAllMergeOperations( eCopyBToDest ); }
570
571 void DirectoryMergeWindow::slotChooseCEverywhere(){ setAllMergeOperations( eCopyCToDest ); }
572
573 void DirectoryMergeWindow::slotAutoChooseEverywhere()
574 {
575 e_MergeOperation eDefaultMergeOp = m_dirC.isValid() ? eMergeABCToDest :
576 m_bSyncMode ? eMergeToAB : eMergeABToDest;
577 setAllMergeOperations(eDefaultMergeOp );
578 }
579
580 void DirectoryMergeWindow::slotNoOpEverywhere(){ setAllMergeOperations(eNoOperation); }
581
582 static void setListViewItemOpen( QListViewItem* p, bool bOpen )
583 {
584 for( QListViewItem* pChild = p->firstChild(); pChild!=0; pChild = pChild->nextSibling() )
585 setListViewItemOpen( pChild, bOpen );
586
587 p->setOpen( bOpen );
588 }
589
590 void DirectoryMergeWindow::slotFoldAllSubdirs()
591 {
592 for( QListViewItem* p = firstChild(); p!=0; p = p->nextSibling() )
593 setListViewItemOpen( p, false );
594 }
595
596 void DirectoryMergeWindow::slotUnfoldAllSubdirs()
597 {
598 for( QListViewItem* p = firstChild(); p!=0; p = p->nextSibling() )
599 setListViewItemOpen( p, true );
600 }
601
602 void DirectoryMergeWindow::setAllMergeOperations( e_MergeOperation eDefaultOperation )
603 {
604 if ( KMessageBox::Yes == KMessageBox::warningYesNo(this,
605 i18n("This affects all merge operations."),
606 i18n("KDiff3: Changing all merge operations"),i18n("Continue"), i18n("Cancel") ) )
607 {
608 for( QListViewItem* p = firstChild(); p!=0; p = p->nextSibling() )
609 {
610 DirMergeItem* pDMI = static_cast<DirMergeItem*>( p );
611 calcSuggestedOperation( *pDMI->m_pMFI, eDefaultOperation );
612 }
613 }
614 }
615
616
617 void DirectoryMergeWindow::compareFilesAndCalcAges( MergeFileInfos& mfi )
618 {
619 std::map<QDateTime,int> dateMap;
620
621 if( mfi.m_bExistsInA )
622 {
623 mfi.m_bLinkA = mfi.m_fileInfoA.isSymLink();
624 mfi.m_bDirA = mfi.m_fileInfoA.isDir();
625 dateMap[ mfi.m_fileInfoA.lastModified() ] = 0;
626 }
627 if( mfi.m_bExistsInB )
628 {
629 mfi.m_bLinkB = mfi.m_fileInfoB.isSymLink();
630 mfi.m_bDirB = mfi.m_fileInfoB.isDir();
631 dateMap[ mfi.m_fileInfoB.lastModified() ] = 1;
632 }
633 if( mfi.m_bExistsInC )
634 {
635 mfi.m_bLinkC = mfi.m_fileInfoC.isSymLink();
636 mfi.m_bDirC = mfi.m_fileInfoC.isDir();
637 dateMap[ mfi.m_fileInfoC.lastModified() ] = 2;
638 }
639
640 bool bError;
641 QString eqStatus;
642 if( mfi.m_bExistsInA && mfi.m_bExistsInB )
643 {
644 if( mfi.m_bDirA ) mfi.m_bEqualAB=true;
645 else fastFileComparison( mfi.m_fileInfoA, mfi.m_fileInfoB, mfi.m_bEqualAB, bError, eqStatus );
646 }
647 if( mfi.m_bExistsInA && mfi.m_bExistsInC )
648 {
649 if( mfi.m_bDirA ) mfi.m_bEqualAC=true;
650 else fastFileComparison( mfi.m_fileInfoA, mfi.m_fileInfoC, mfi.m_bEqualAC, bError, eqStatus );
651 }
652 if( mfi.m_bExistsInB && mfi.m_bExistsInC )
653 {
654 if (mfi.m_bEqualAB && mfi.m_bEqualAC)
655 mfi.m_bEqualBC = true;
656 else
657 {
658 if( mfi.m_bDirB ) mfi.m_bEqualBC=true;
659 else fastFileComparison( mfi.m_fileInfoB, mfi.m_fileInfoC, mfi.m_bEqualBC, bError, eqStatus );
660 }
661 }
662
663 if (mfi.m_bLinkA!=mfi.m_bLinkB) mfi.m_bEqualAB=false;
664 if (mfi.m_bLinkA!=mfi.m_bLinkC) mfi.m_bEqualAC=false;
665 if (mfi.m_bLinkB!=mfi.m_bLinkC) mfi.m_bEqualBC=false;
666
667 if (mfi.m_bDirA!=mfi.m_bDirB) mfi.m_bEqualAB=false;
668 if (mfi.m_bDirA!=mfi.m_bDirC) mfi.m_bEqualAC=false;
669 if (mfi.m_bDirB!=mfi.m_bDirC) mfi.m_bEqualBC=false;
670
671 assert(eNew==0 && eMiddle==1 && eOld==2);
672
673 // The map automatically sorts the keys.
674 int age = eNew;
675 std::map<QDateTime,int>::reverse_iterator i;
676 for( i=dateMap.rbegin(); i!=dateMap.rend(); ++i )
677 {
678 int n = i->second;
679 if ( n==0 && mfi.m_ageA==eNotThere )
680 {
681 mfi.m_ageA = (e_Age)age; ++age;
682 if ( mfi.m_bEqualAB ) { mfi.m_ageB = mfi.m_ageA; ++age; }
683 if ( mfi.m_bEqualAC ) { mfi.m_ageC = mfi.m_ageA; ++age; }
684 }
685 else if ( n==1 && mfi.m_ageB==eNotThere )
686 {
687 mfi.m_ageB = (e_Age)age; ++age;
688 if ( mfi.m_bEqualAB ) { mfi.m_ageA = mfi.m_ageB; ++age; }
689 if ( mfi.m_bEqualBC ) { mfi.m_ageC = mfi.m_ageB; ++age; }
690 }
691 else if ( n==2 && mfi.m_ageC==eNotThere)
692 {
693 mfi.m_ageC = (e_Age)age; ++age;
694 if ( mfi.m_bEqualAC ) { mfi.m_ageA = mfi.m_ageC; ++age; }
695 if ( mfi.m_bEqualBC ) { mfi.m_ageB = mfi.m_ageC; ++age; }
696 }
697 }
698
699 // The checks below are necessary when the dates of the file are equal but the
700 // files are not. One wouldn't expect this to happen, yet it happens sometimes.
701 if ( mfi.m_bExistsInC && mfi.m_ageC==eNotThere )
702 {
703 mfi.m_ageC = (e_Age)age; ++age;
704 mfi.m_bConflictingAges = true;
705 }
706 if ( mfi.m_bExistsInB && mfi.m_ageB==eNotThere )
707 {
708 mfi.m_ageB = (e_Age)age; ++age;
709 mfi.m_bConflictingAges = true;
710 }
711 if ( mfi.m_bExistsInA && mfi.m_ageA==eNotThere )
712 {
713 mfi.m_ageA = (e_Age)age; ++age;
714 mfi.m_bConflictingAges = true;
715 }
716
717 if ( mfi.m_ageA != eOld && mfi.m_ageB != eOld && mfi.m_ageC != eOld )
718 {
719 if (mfi.m_ageA == eMiddle) mfi.m_ageA = eOld;
720 if (mfi.m_ageB == eMiddle) mfi.m_ageB = eOld;
721 if (mfi.m_ageC == eMiddle) mfi.m_ageC = eOld;
722 }
723 }
724
725 static QPixmap* s_pm_dir;
726 static QPixmap* s_pm_file;
727
728 static void setOnePixmap( QListViewItem* pLVI, int col, e_Age eAge, bool bLink, bool bDir )
729 {
730 #include "xpm/equal.xpm"
731 #include "xpm/not_equal.xpm"
732 #include "xpm/not_everywhere.xpm"
733 #include "xpm/not_there.xpm"
734 #include "xpm/link_arrow.xpm"
735
736 static QPixmap pmLink( link_arrow );
737
738 static QPixmap pmDirLink( pixCombiner( *s_pm_dir, pmLink) );
739 static QPixmap pmFileLink( pixCombiner( *s_pm_file, pmLink ) );
740
741 static QPixmap pmNotThere( not_there_pm );
742
743 static QPixmap pmNew( equal_pm );
744 static QPixmap pmOld( not_equal_pm );
745 static QPixmap pmMiddle( not_everywhere_pm );
746
747 static QPixmap pmNewLink( pixCombiner( pmNew, pmLink) );
748 static QPixmap pmOldLink( pixCombiner( pmOld, pmLink) );
749 static QPixmap pmMiddleLink( pixCombiner( pmMiddle, pmLink) );
750
751 static QPixmap pmNewDir( pixCombiner2( pmNew, *s_pm_dir) );
752 static QPixmap pmMiddleDir( pixCombiner2( pmMiddle, *s_pm_dir) );
753 static QPixmap pmOldDir( pixCombiner2( pmOld, *s_pm_dir) );
754
755 static QPixmap pmNewDirLink( pixCombiner( pmNewDir, pmLink) );
756 static QPixmap pmMiddleDirLink( pixCombiner( pmMiddleDir, pmLink) );
757 static QPixmap pmOldDirLink( pixCombiner( pmOldDir, pmLink) );
758
759 static QPixmap* ageToPm[]= { &pmNew, &pmMiddle, &pmOld, &pmNotThere, s_pm_file };
760 static QPixmap* ageToPmLink[]= { &pmNewLink, &pmMiddleLink, &pmOldLink, &pmNotThere, &pmFileLink };
761 static QPixmap* ageToPmDir[]= { &pmNewDir, &pmMiddleDir, &pmOldDir, &pmNotThere, s_pm_dir };
762 static QPixmap* ageToPmDirLink[]={ &pmNewDirLink, &pmMiddleDirLink, &pmOldDirLink, &pmNotThere, &pmDirLink };
763
764 QPixmap** ppPm = bDir ? ( bLink ? ageToPmDirLink : ageToPmDir ):
765 ( bLink ? ageToPmLink : ageToPm );
766
767 pLVI->setPixmap( col, *ppPm[eAge] );
768 }
769
770 static void setPixmaps( MergeFileInfos& mfi, bool bCheckC )
771 {
772 setOnePixmap( mfi.m_pDMI, s_nameCol, eAgeEnd,
773 mfi.m_bLinkA || mfi.m_bLinkB || mfi.m_bLinkC,
774 mfi.m_bDirA || mfi.m_bDirB || mfi.m_bDirC
775 );
776
777 if ( mfi.m_bDirA || mfi.m_bDirB || mfi.m_bDirC )
778 {
779 mfi.m_ageA=eNotThere;
780 mfi.m_ageB=eNotThere;
781 mfi.m_ageC=eNotThere;
782 int age = eNew;
783 if ( mfi.m_bExistsInC )
784 {
785 mfi.m_ageC = (e_Age)age;
786 if (mfi.m_bEqualAC) mfi.m_ageA = (e_Age)age;
787 if (mfi.m_bEqualBC) mfi.m_ageB = (e_Age)age;
788 ++age;
789 }
790 if ( mfi.m_bExistsInB && mfi.m_ageB==eNotThere )
791 {
792 mfi.m_ageB = (e_Age)age;
793 if (mfi.m_bEqualAB) mfi.m_ageA = (e_Age)age;
794 ++age;
795 }
796 if ( mfi.m_bExistsInA && mfi.m_ageA==eNotThere )
797 {
798 mfi.m_ageA = (e_Age)age;
799 }
800 if ( mfi.m_ageA != eOld && mfi.m_ageB != eOld && mfi.m_ageC != eOld )
801 {
802 if (mfi.m_ageA == eMiddle) mfi.m_ageA = eOld;
803 if (mfi.m_ageB == eMiddle) mfi.m_ageB = eOld;
804 if (mfi.m_ageC == eMiddle) mfi.m_ageC = eOld;
805 }
806 }
807
808 setOnePixmap( mfi.m_pDMI, s_ACol, mfi.m_ageA, mfi.m_bLinkA, mfi.m_bDirA );
809 setOnePixmap( mfi.m_pDMI, s_BCol, mfi.m_ageB, mfi.m_bLinkB, mfi.m_bDirB );
810 if ( bCheckC )
811 setOnePixmap( mfi.m_pDMI, s_CCol, mfi.m_ageC, mfi.m_bLinkC, mfi.m_bDirC );
812 }
813
814 // Iterate through the complete tree. Start by specifying QListView::firstChild().
815 static QListViewItem* treeIterator( QListViewItem* p, bool bVisitChildren=true )
816 {
817 if( p!=0 )
818 {
819 if ( bVisitChildren && p->firstChild() != 0 ) p = p->firstChild();
820 else if ( p->nextSibling() !=0 ) p = p->nextSibling();
821 else
822 {
823 p = p->parent();
824 while ( p!=0 )
825 {
826 if( p->nextSibling()!=0 ) { p = p->nextSibling(); break; }
827 else { p = p->parent(); }
828 }
829 }
830 }
831 return p;
832 }
833
834 void DirectoryMergeWindow::prepareListView()
835 {
836 static bool bFirstTime = true;
837 if (bFirstTime)
838 {
839 #include "xpm/file.xpm"
840 #include "xpm/folder.xpm"
841 s_pm_dir = new QPixmap( m_pIconLoader->loadIcon("folder", KIcon::Small ) );
842 if (s_pm_dir->size()!=QSize(16,16))
843 {
844 delete s_pm_dir;
845 s_pm_dir = new QPixmap( folder_pm );
846 }
847 s_pm_file= new QPixmap( file_pm );
848 bFirstTime=false;
849 }
850
851 clear();
852
853 setRootIsDecorated( true );
854
855 bool bCheckC = m_dirC.isValid();
856
857 std::map<QString, MergeFileInfos>::iterator j;
858 int nrOfFiles = m_fileMergeMap.size();
859 int currentIdx = 1;
860 QTime t;
861 t.start();
862 for( j=m_fileMergeMap.begin(); j!=m_fileMergeMap.end(); ++j )
863 {
864 const QString& fileName = j->first;
865 MergeFileInfos& mfi = j->second;
866
867 mfi.m_subPath = mfi.m_fileInfoA.exists() ? mfi.m_fileInfoA.filePath() :
868 mfi.m_fileInfoB.exists() ? mfi.m_fileInfoB.filePath() :
869 mfi.m_fileInfoC.exists() ? mfi.m_fileInfoC.filePath() : "";
870
871 g_pProgressDialog->setInformation(
872 "Processing " + QString::number(currentIdx) +" / "+ QString::number(nrOfFiles)
873 +"\n" + fileName, double(currentIdx) / nrOfFiles, false );
874 if ( g_pProgressDialog->wasCancelled() ) break;
875 ++currentIdx;
876
877
878 // The comparisons and calculations for each file take place here.
879 compareFilesAndCalcAges( mfi );
880
881 bool bEqual = bCheckC ? mfi.m_bEqualAB && mfi.m_bEqualAC : mfi.m_bEqualAB;
882 bool bDir = mfi.m_bDirA || mfi.m_bDirB || mfi.m_bDirC;
883
884 if ( m_pOptions->m_bDmShowOnlyDeltas && !bDir && bEqual )
885 continue;
886
887 // Get dirname from fileName: Search for "/" from end:
888 int pos = fileName.findRev('/');
889 QString dirPart;
890 QString filePart;
891 if (pos==-1)
892 {
893 // Top dir
894 filePart = fileName;
895 }
896 else
897 {
898 dirPart = fileName.left(pos);
899 filePart = fileName.mid(pos+1);
900 }
901
902 if ( dirPart.isEmpty() ) // Top level
903 {
904 new DirMergeItem( this, filePart, &mfi );
905 }
906 else
907 {
908 MergeFileInfos& dirMfi = m_fileMergeMap[dirPart]; // parent
909 assert(dirMfi.m_pDMI!=0);
910 new DirMergeItem( dirMfi.m_pDMI, filePart, &mfi );
911 mfi.m_pParent = &dirMfi;
912
913 if ( !bEqual ) // Set all parents to "not equal"
914 {
915 MergeFileInfos* p = mfi.m_pParent;
916 while(p!=0)
917 {
918 bool bChange = false;
919 if ( !mfi.m_bEqualAB && p->m_bEqualAB ){ p->m_bEqualAB = false; bChange=true; }
920 if ( !mfi.m_bEqualAC && p->m_bEqualAC ){ p->m_bEqualAC = false; bChange=true; }
921 if ( !mfi.m_bEqualBC && p->m_bEqualBC ){ p->m_bEqualBC = false; bChange=true; }
922
923 if ( bChange )
924 setPixmaps( *p, bCheckC );
925 else
926 break;
927
928 p = p->m_pParent;
929 }
930 }
931 }
932
933 setPixmaps( mfi, bCheckC );
934 }
935
936 if ( m_pOptions->m_bDmShowOnlyDeltas )
937 {
938 // Remove all equals. (Search tree depth first)
939 QListViewItem* p = firstChild();
940 while( p!=0 && firstChild() != 0 )
941 {
942 QListViewItem* pParent = p->parent();
943 QListViewItem* pNextSibling = p->nextSibling();
944
945 DirMergeItem* pDMI = static_cast<DirMergeItem*>(p);
946 bool bDirEqual = bCheckC ? pDMI->m_pMFI->m_bEqualAB && pDMI->m_pMFI->m_bEqualAC
947 : pDMI->m_pMFI->m_bEqualAB;
948 if ( pDMI!=0 && pDMI->m_pMFI->m_bDirA && bDirEqual )
949 {
950 delete p;
951 p=0;
952 }
953
954 if ( p!=0 && p->firstChild() != 0 ) p = p->firstChild();
955 else if ( pNextSibling!=0 ) p = pNextSibling;
956 else
957 {
958 p=pParent;
959 while ( p!=0 )
960 {
961 if( p->nextSibling()!=0 ) { p = p->nextSibling(); break; }
962 else { p = p->parent(); }
963 }
964 }
965 }
966 }
967 }
968
969 static bool conflictingFileTypes(MergeFileInfos& mfi)
970 {
971 // Now check if file/dir-types fit.
972 if ( mfi.m_bLinkA || mfi.m_bLinkB || mfi.m_bLinkC )
973 {
974 if ( mfi.m_bExistsInA && ! mfi.m_bLinkA ||
975 mfi.m_bExistsInB && ! mfi.m_bLinkB ||
976 mfi.m_bExistsInC && ! mfi.m_bLinkC )
977 {
978 return true;
979 }
980 }
981
982 if ( mfi.m_bDirA || mfi.m_bDirB || mfi.m_bDirC )
983 {
984 if ( mfi.m_bExistsInA && ! mfi.m_bDirA ||
985 mfi.m_bExistsInB && ! mfi.m_bDirB ||
986 mfi.m_bExistsInC && ! mfi.m_bDirC )
987 {
988 return true;
989 }
990 }
991 return false;
992 }
993
994 void DirectoryMergeWindow::calcSuggestedOperation( MergeFileInfos& mfi, e_MergeOperation eDefaultMergeOp )
995 {
996 bool bCheckC = m_dirC.isValid();
997 bool bCopyNewer = m_pOptions->m_bDmCopyNewer;
998
999 if ( eDefaultMergeOp == eMergeABCToDest && !bCheckC ) { eDefaultMergeOp = eMergeABToDest; }
1000 if ( eDefaultMergeOp == eMergeToAB && bCheckC ) { assert(false); }
1001
1002 if ( eDefaultMergeOp == eMergeToA || eDefaultMergeOp == eMergeToB ||
1003 eDefaultMergeOp == eMergeABCToDest || eDefaultMergeOp == eMergeABToDest || eDefaultMergeOp == eMergeToAB )
1004 {
1005 if ( !bCheckC )
1006 {
1007 if ( mfi.m_bEqualAB )
1008 {
1009 mfi.setMergeOperation( eNoOperation ); // All is well, nothing to do.
1010 }
1011 else if ( mfi.m_bExistsInA && mfi.m_bExistsInB )
1012 {
1013 if ( !bCopyNewer || mfi.m_bDirA )
1014 mfi.setMergeOperation( eDefaultMergeOp );
1015 else if ( bCopyNewer && mfi.m_bConflictingAges )
1016 {
1017 mfi.setMergeOperation( eConflictingAges );
1018 }
1019 else
1020 {
1021 if ( mfi.m_ageA == eNew )
1022 mfi.setMergeOperation( eDefaultMergeOp == eMergeToAB ? eCopyAToB : eCopyAToDest );
1023 else
1024 mfi.setMergeOperation( eDefaultMergeOp == eMergeToAB ? eCopyBToA : eCopyBToDest );
1025 }
1026 }
1027 else if ( !mfi.m_bExistsInA && mfi.m_bExistsInB )
1028 {
1029 if ( eDefaultMergeOp==eMergeABToDest ) mfi.setMergeOperation( eCopyBToDest );
1030 else if ( eDefaultMergeOp==eMergeToB ) mfi.setMergeOperation( eNoOperation );
1031 else mfi.setMergeOperation( eCopyBToA );
1032 }
1033 else if ( mfi.m_bExistsInA && !mfi.m_bExistsInB )
1034 {
1035 if ( eDefaultMergeOp==eMergeABToDest ) mfi.setMergeOperation( eCopyAToDest );
1036 else if ( eDefaultMergeOp==eMergeToA ) mfi.setMergeOperation( eNoOperation );
1037 else mfi.setMergeOperation( eCopyAToB );
1038 }
1039 else //if ( !mfi.m_bExistsInA && !mfi.m_bExistsInB )
1040 {
1041 mfi.setMergeOperation( eNoOperation ); assert(false);
1042 }
1043 }
1044 else
1045 {
1046 if ( mfi.m_bEqualAB && mfi.m_bEqualAC )
1047 {
1048 mfi.setMergeOperation( eCopyCToDest );
1049 }
1050 else if ( mfi.m_bExistsInA && mfi.m_bExistsInB && mfi.m_bExistsInC)
1051 {
1052 if ( mfi.m_bEqualAB )
1053 mfi.setMergeOperation( eCopyCToDest );
1054 else if ( mfi.m_bEqualAC )
1055 mfi.setMergeOperation( eCopyBToDest );
1056 else if ( mfi.m_bEqualBC )
1057 mfi.setMergeOperation( eCopyCToDest );
1058 else
1059 mfi.setMergeOperation( eMergeABCToDest );
1060 }
1061 else if ( mfi.m_bExistsInA && mfi.m_bExistsInB && !mfi.m_bExistsInC )
1062 {
1063 if ( mfi.m_bEqualAB )
1064 mfi.setMergeOperation( eDeleteFromDest );
1065 else
1066 mfi.setMergeOperation( eCopyBToDest );
1067 }
1068 else if ( mfi.m_bExistsInA && !mfi.m_bExistsInB && mfi.m_bExistsInC )
1069 {
1070 if ( mfi.m_bEqualAC )
1071 mfi.setMergeOperation( eDeleteFromDest );
1072 else
1073 mfi.setMergeOperation( eCopyCToDest );
1074 }
1075 else if ( !mfi.m_bExistsInA && mfi.m_bExistsInB && mfi.m_bExistsInC )
1076 {
1077 if ( mfi.m_bEqualBC )
1078 mfi.setMergeOperation( eCopyCToDest );
1079 else
1080 mfi.setMergeOperation( eMergeABCToDest );
1081 }
1082 else if ( !mfi.m_bExistsInA && !mfi.m_bExistsInB && mfi.m_bExistsInC )
1083 {
1084 mfi.setMergeOperation( eCopyCToDest );
1085 }
1086 else if ( !mfi.m_bExistsInA && mfi.m_bExistsInB && !mfi.m_bExistsInC )
1087 {
1088 mfi.setMergeOperation( eCopyBToDest );
1089 }
1090 else if ( mfi.m_bExistsInA && !mfi.m_bExistsInB && !mfi.m_bExistsInC)
1091 {
1092 mfi.setMergeOperation( eDeleteFromDest );
1093 }
1094 else //if ( !mfi.m_bExistsInA && !mfi.m_bExistsInB && !mfi.m_bExistsInC )
1095 {
1096 mfi.setMergeOperation( eNoOperation ); assert(false);
1097 }
1098 }
1099
1100 // Now check if file/dir-types fit.
1101 if ( conflictingFileTypes(mfi) )
1102 {
1103 mfi.setMergeOperation( eConflictingFileTypes );
1104 }
1105 }
1106 else
1107 {
1108 e_MergeOperation eMO = eDefaultMergeOp;
1109 switch ( eDefaultMergeOp )
1110 {
1111 case eConflictingFileTypes:
1112 case eConflictingAges:
1113 case eDeleteA:
1114 case eDeleteB:
1115 case eDeleteAB:
1116 case eDeleteFromDest:
1117 case eNoOperation: break;
1118 case eCopyAToB: if ( !mfi.m_bExistsInA ) { eMO = eDeleteB; } break;
1119 case eCopyBToA: if ( !mfi.m_bExistsInB ) { eMO = eDeleteA; } break;
1120 case eCopyAToDest: if ( !mfi.m_bExistsInA ) { eMO = eDeleteFromDest; } break;
1121 case eCopyBToDest: if ( !mfi.m_bExistsInB ) { eMO = eDeleteFromDest; } break;
1122 case eCopyCToDest: if ( !mfi.m_bExistsInC ) { eMO = eDeleteFromDest; } break;
1123
1124 case eMergeToA:
1125 case eMergeToB:
1126 case eMergeToAB:
1127 case eMergeABCToDest:
1128 case eMergeABToDest:
1129 default:
1130 assert(false);
1131 }
1132 mfi.setMergeOperation( eMO );
1133 }
1134 }
1135
1136 void DirectoryMergeWindow::onDoubleClick( QListViewItem* lvi )
1137 {
1138 if (lvi==0) return;
1139
1140 if ( m_bDirectoryMerge )
1141 mergeCurrentFile();
1142 else
1143 compareCurrentFile();
1144 }
1145
1146 void DirectoryMergeWindow::onSelectionChanged( QListViewItem* lvi )
1147 {
1148 if ( lvi==0 ) return;
1149
1150 DirMergeItem* pDMI = static_cast<DirMergeItem*>(lvi);
1151
1152 MergeFileInfos& mfi = *pDMI->m_pMFI;
1153 assert( mfi.m_pDMI==pDMI );
1154
1155 m_pDirectoryMergeInfo->setInfo( m_dirA, m_dirB, m_dirC, m_dirDestInternal, mfi );
1156 }
1157
1158 void DirectoryMergeWindow::onClick( QListViewItem* lvi, const QPoint& p, int c )
1159 {
1160 if ( lvi==0 ) return;
1161
1162 DirMergeItem* pDMI = static_cast<DirMergeItem*>(lvi);
1163
1164 MergeFileInfos& mfi = *pDMI->m_pMFI;
1165 assert( mfi.m_pDMI==pDMI );
1166
1167 if ( c!=s_OpCol ) return;
1168
1169 bool bThreeDirs = m_dirC.isValid();
1170
1171 //bool bImmediate = (button==Qt::LeftButton);
1172
1173 KPopupMenu m(this);
1174 //if (bImmediate ) m.insertItem("Immediate Mode", eTitleId );
1175 //else m.insertItem("Postponed Mode", eTitleId );
1176 //m.setItemEnabled ( eTitleId, false );
1177 if ( bThreeDirs )
1178 {
1179 m.insertItem("Do nothing", eNoOperation );
1180 int count=0;
1181 if ( mfi.m_bExistsInA ) { m.insertItem("A", eCopyAToDest ); ++count; }
1182 if ( mfi.m_bExistsInB ) { m.insertItem("B", eCopyBToDest ); ++count; }
1183 if ( mfi.m_bExistsInC ) { m.insertItem("C", eCopyCToDest ); ++count; }
1184 if ( !conflictingFileTypes(mfi) && count>1 ) m.insertItem("Merge", eMergeABCToDest );
1185 m.insertItem("Delete (if exists)", eDeleteFromDest );
1186 }
1187 else if ( m_bSyncMode )
1188 {
1189 m.insertItem("Do nothing", eNoOperation );
1190 if ( mfi.m_bExistsInA ) m.insertItem("Copy A to B", eCopyAToB );
1191 if ( mfi.m_bExistsInB ) m.insertItem("Copy B to A", eCopyBToA );
1192 if ( mfi.m_bExistsInA ) m.insertItem("Delete A", eDeleteA );
1193 if ( mfi.m_bExistsInB ) m.insertItem("Delete B", eDeleteB );
1194 if ( mfi.m_bExistsInA && mfi.m_bExistsInB )
1195 {
1196 m.insertItem("Delete A and B", eDeleteAB );
1197 if ( !conflictingFileTypes(mfi))
1198 {
1199 m.insertItem("Merge to A", eMergeToA );
1200 m.insertItem("Merge to B", eMergeToB );
1201 m.insertItem("Merge to A and B", eMergeToAB );
1202 }
1203 }
1204 }
1205 else
1206 {
1207 m.insertItem("Do nothing", eNoOperation );
1208 if ( mfi.m_bExistsInA ) m.insertItem("A", eCopyAToDest );
1209 if ( mfi.m_bExistsInB ) m.insertItem("B", eCopyBToDest );
1210 if ( mfi.m_bExistsInA && mfi.m_bExistsInB && !conflictingFileTypes(mfi) )
1211 m.insertItem("Merge", eMergeABToDest );
1212 m.insertItem("Delete (if exists)", eDeleteFromDest );
1213 }
1214
1215 int result = m.exec( p );
1216 if (result>=0 )
1217 mfi.setMergeOperation( (e_MergeOperation)result );
1218 }
1219
1220 // Since Qt 2.3.0 doesn't allow the specification of a compare operator, this trick emulates it.
1221 #if QT_VERSION==230
1222 #define DIRSORT(x) ( pMFI->m_bDirA ? " " : "" )+x
1223 #else
1224 #define DIRSORT(x) x
1225 #endif
1226
1227 DirMergeItem::DirMergeItem( QListView* pParent, const QString& fileName, MergeFileInfos* pMFI )
1228 : QListViewItem( pParent, DIRSORT( fileName ), "","","", "To do." )
1229 {
1230 pMFI->m_pDMI = this;
1231 m_pMFI = pMFI;
1232 }
1233
1234 DirMergeItem::DirMergeItem( DirMergeItem* pParent, const QString& fileName, MergeFileInfos* pMFI )
1235 : QListViewItem( pParent, DIRSORT( fileName ), "","","", "To do." )
1236 {
1237 pMFI->m_pDMI = this;
1238 m_pMFI = pMFI;
1239 }
1240
1241 DirMergeItem::~DirMergeItem()
1242 {
1243 m_pMFI->m_pDMI = 0;
1244 }
1245
1246 void MergeFileInfos::setMergeOperation( e_MergeOperation eMOp )
1247 {
1248 m_eMergeOperation = eMOp;
1249 const char* s=0;
1250 bool bDir = m_bDirA || m_bDirB || m_bDirC;
1251 if( m_pDMI!=0 )
1252 {
1253 switch( m_eMergeOperation )
1254 {
1255 case eNoOperation: s=""; m_pDMI->setText(s_OpCol,""); break;
1256 case eCopyAToB: s="Copy A to B"; break;
1257 case eCopyBToA: s="Copy B to A"; break;
1258 case eDeleteA: s="Delete A"; break;
1259 case eDeleteB: s="Delete B"; break;
1260 case eDeleteAB: s="Delete A and B"; break;
1261 case eMergeToA: s="Merge to A"; break;
1262 case eMergeToB: s="Merge to B"; break;
1263 case eMergeToAB: s="Merge to A and B"; break;
1264 case eCopyAToDest: s="A"; break;
1265 case eCopyBToDest: s="B"; break;
1266 case eCopyCToDest: s="C"; break;
1267 case eDeleteFromDest: s="Delete (if exists)"; break;
1268 case eMergeABCToDest: s= bDir ? "Merge" : "Merge (manual)"; break;
1269 case eMergeABToDest: s= bDir ? "Merge" : "Merge (manual)"; break;
1270 case eConflictingFileTypes: s="Error: Conflicting File Types"; break;
1271 case eConflictingAges: s="Error: Dates are equal but files are not."; break;
1272 default: assert(false); break;
1273 }
1274 m_pDMI->setText(s_OpCol,s);
1275
1276 e_MergeOperation eChildrenMergeOp = m_eMergeOperation;
1277 if ( eChildrenMergeOp == eConflictingFileTypes ) eChildrenMergeOp = eMergeABCToDest;
1278 QListViewItem* p = m_pDMI->firstChild();
1279 while ( p!=0 )
1280 {
1281 DirMergeItem* pDMI = static_cast<DirMergeItem*>( p );
1282 DirectoryMergeWindow* pDMW = static_cast<DirectoryMergeWindow*>( p->listView() );
1283 pDMW->calcSuggestedOperation( *pDMI->m_pMFI, eChildrenMergeOp );
1284 p = p->nextSibling();
1285 }
1286 }
1287 }
1288
1289 void DirectoryMergeWindow::compareCurrentFile()
1290 {
1291 if (!canContinue()) return;
1292
1293 if ( m_bRealMergeStarted )
1294 {
1295 KMessageBox::sorry(this,"This operation is currently not possible.","KDiff3");
1296 return;
1297 }
1298
1299 DirMergeItem* pDMI = static_cast<DirMergeItem*>( selectedItem() );
1300 if ( pDMI != 0 )
1301 {
1302 MergeFileInfos& mfi = *pDMI->m_pMFI;
1303 if ( !(mfi.m_bDirA || mfi.m_bDirB || mfi.m_bDirC) )
1304 {
1305 emit startDiffMerge(
1306 mfi.m_bExistsInA ? mfi.m_fileInfoA.absFilePath() : QString(""),
1307 mfi.m_bExistsInB ? mfi.m_fileInfoB.absFilePath() : QString(""),
1308 mfi.m_bExistsInC ? mfi.m_fileInfoC.absFilePath() : QString(""),
1309 "",
1310 "","",""
1311 );
1312 }
1313 }
1314 emit updateAvailabilities();
1315 }
1316
1317 void DirectoryMergeWindow::mergeCurrentFile()
1318 {
1319 if (!canContinue()) return;
1320
1321 if ( m_bRealMergeStarted )
1322 {
1323 KMessageBox::sorry(this,"This operation is currently not possible because diff merge currently runs.","KDiff3");
1324 return;
1325 }
1326
1327 if ( isFileSelected() )
1328 {
1329 DirMergeItem* pDMI = static_cast<DirMergeItem*>( selectedItem() );
1330 if ( pDMI != 0 )
1331 {
1332 MergeFileInfos& mfi = *pDMI->m_pMFI;
1333 m_bSingleFileOperationStarted = true;
1334 emit startDiffMerge(
1335 mfi.m_bExistsInA ? mfi.m_fileInfoA.absFilePath() : QString(""),
1336 mfi.m_bExistsInB ? mfi.m_fileInfoB.absFilePath() : QString(""),
1337 mfi.m_bExistsInC ? mfi.m_fileInfoC.absFilePath() : QString(""),
1338 fullNameDest(mfi),
1339 "","",""
1340 );
1341 m_pCurrentItemForOperation = pDMI;
1342 m_pCurrentItemForOperation->setText( s_OpStatusCol, "In progress ..." );
1343 }
1344 }
1345 emit updateAvailabilities();
1346 }
1347
1348
1349 bool DirectoryMergeWindow::isFileSelected()
1350 {
1351 DirMergeItem* pDMI = static_cast<DirMergeItem*>( selectedItem() );
1352 if ( pDMI != 0 )
1353 {
1354 MergeFileInfos& mfi = *pDMI->m_pMFI;
1355 return ! (mfi.m_bDirA || mfi.m_bDirB || mfi.m_bDirC || conflictingFileTypes(mfi) );
1356 }
1357 return false;
1358 }
1359
1360 void DirectoryMergeWindow::mergeResultSaved(const QString& fileName)
1361 {
1362 m_bSingleFileOperationStarted = false;
1363 if ( m_pCurrentItemForOperation!=0 && m_pCurrentItemForOperation->m_pMFI==0 )
1364 {
1365 KMessageBox::error( this, "This should never happen: \n\nmergeResultSaved: m_pMFI=0\n\nIf you know how to reproduce this, please contact the program author.","Program Error" );
1366 return;
1367 }
1368 if ( m_pCurrentItemForOperation!=0 && fileName == fullNameDest(*m_pCurrentItemForOperation->m_pMFI) )
1369 {
1370 if ( m_pCurrentItemForOperation->m_pMFI->m_eMergeOperation==eMergeToAB )
1371 {
1372 MergeFileInfos& mfi = *m_pCurrentItemForOperation->m_pMFI;
1373 bool bSuccess = copyFLD( fullNameB(mfi), fullNameA(mfi) );
1374 if (!bSuccess)
1375 {
1376 KMessageBox::error(this, i18n("An error occurred while copying.\n"), "KDiff3: Error" );
1377 m_pStatusInfo->setCaption("KDiff3: Merge-Error");
1378 m_pStatusInfo->show();
1379 if ( m_pStatusInfo->firstChild()!=0 )
1380 m_pStatusInfo->ensureItemVisible( m_pStatusInfo->last() );
1381 m_bError = true;
1382 m_pCurrentItemForOperation->setText( s_OpStatusCol, "Error." );
1383 mfi.m_eMergeOperation = eCopyBToA;
1384 return;
1385 }
1386 }
1387 m_pCurrentItemForOperation->setText( s_OpStatusCol, "Done." );
1388 m_pCurrentItemForOperation->m_pMFI->m_bOperationComplete = true;
1389 }
1390
1391 emit updateAvailabilities();
1392 }
1393
1394 bool DirectoryMergeWindow::canContinue()
1395 {
1396 bool bCanContinue=false;
1397 checkIfCanContinue( &bCanContinue );
1398 if ( bCanContinue && !m_bError )
1399 {
1400 if ( m_bRealMergeStarted && m_pCurrentItemForOperation!=0 && ! m_pCurrentItemForOperation->m_pMFI->m_bOperationComplete )
1401 {
1402 m_pCurrentItemForOperation->setText( s_OpStatusCol, "Not saved." );
1403 m_pCurrentItemForOperation->m_pMFI->m_bOperationComplete = true;
1404 }
1405 }
1406 return bCanContinue;
1407 }
1408
1409 void DirectoryMergeWindow::mergeContinue()
1410 {
1411 if ( ! canContinue() ) return;
1412
1413 m_bSingleFileOperationStarted = false;
1414
1415 int nrOfItems = 0;
1416 int nrOfCompletedItems = 0;
1417 int nrOfCompletedSimItems = 0;
1418
1419 if ( !m_bSimulatedMergeStarted && !m_bRealMergeStarted )
1420 {
1421 // First check if an invalid merge operations exist.
1422 for( QListViewItem* p=firstChild(); p!=0; p = treeIterator( p ) )
1423 {
1424 DirMergeItem* pDMI = static_cast<DirMergeItem*>(p);
1425 if (pDMI!=0 && pDMI->m_pMFI->m_eMergeOperation == eConflictingFileTypes )
1426 {
1427 ensureItemVisible( pDMI );
1428 setSelected( pDMI, true );
1429 KMessageBox::error(this, i18n("The highlighted item has a different type in the different directories. Select what to do."), "Error");
1430 return;
1431 }
1432 if (pDMI!=0 && pDMI->m_pMFI->m_eMergeOperation == eConflictingAges )
1433 {
1434 ensureItemVisible( pDMI );
1435 setSelected( pDMI, true );
1436 KMessageBox::error(this, i18n("The modification dates of the file are equal but the files are not. Select what to do."), "Error");
1437 return;
1438 }
1439 ++nrOfItems;
1440 if ( pDMI->m_pMFI->m_bSimOpComplete )
1441 ++nrOfCompletedSimItems;
1442 if ( pDMI->m_pMFI->m_bOperationComplete )
1443 ++nrOfCompletedItems;
1444 }
1445
1446 int status = KMessageBox::warningYesNoCancel(this,
1447 i18n("The merge is about to begin.\n\n"
1448 "Choose \"Do it\" if you have read the instructions and know what you are doing.\n"
1449 "Choosing \"Simulate it\" will tell you what would happen.\n\n"
1450 "Be aware that this program still has beta status "
1451 "and there is NO WARRANTY whatsoever! Make backups of your vital data!"),
1452 i18n("KDiff3: Starting the Merge"), i18n("Do it"), i18n("Simulate it") );
1453 if (status==KMessageBox::Yes) m_bRealMergeStarted = true;
1454 else if (status==KMessageBox::No ) m_bSimulatedMergeStarted = true;
1455 else return;
1456
1457 m_pStatusInfo->hide();
1458 m_pStatusInfo->clear();
1459 }
1460
1461 bool bContinueWithCurrentItem = false;
1462 bool bSkipItem = false;
1463 if ( m_bError && m_pCurrentItemForOperation!=0 )
1464 {
1465 int status = KMessageBox::warningYesNoCancel(this,
1466 i18n("There was an error in the last step.\n"
1467 "Do you want to continue with the item that caused the error or do you want to skip this item?"),
1468 i18n("KDiff3: Continue merge after an error"), i18n("Continue with last item"), i18n("Skip item") );
1469 if (status==KMessageBox::Yes) bContinueWithCurrentItem = true;
1470 else if (status==KMessageBox::No ) bSkipItem = true;
1471 else return;
1472 m_bError = false;
1473 }
1474
1475 g_pProgressDialog->start();
1476
1477 bool bSuccess = true;
1478 bool bSingleFileMerge = false;
1479 bool bSim = m_bSimulatedMergeStarted;
1480 while( bSuccess )
1481 {
1482 if ( m_pCurrentItemForOperation!=0 && !bContinueWithCurrentItem )
1483 {
1484 if ( bSim )
1485 {
1486 if( m_pCurrentItemForOperation->firstChild()==0 )
1487 {
1488 m_pCurrentItemForOperation->m_pMFI->m_bSimOpComplete = true;
1489 }
1490 }
1491 else
1492 {
1493 if( m_pCurrentItemForOperation->firstChild()==0 )
1494 {
1495 if( !m_pCurrentItemForOperation->m_pMFI->m_bOperationComplete )
1496 {
1497 m_pCurrentItemForOperation->setText( s_OpStatusCol, bSkipItem ? "Skipped." : "Done." );
1498 m_pCurrentItemForOperation->m_pMFI->m_bOperationComplete = true;
1499 bSkipItem = false;
1500 }
1501 }
1502 else
1503 {
1504 m_pCurrentItemForOperation->setText( s_OpStatusCol, "In progress ..." );
1505 }
1506 }
1507 }
1508
1509 if ( m_pCurrentItemForOperation==0 )
1510 {
1511 m_pCurrentItemForOperation = static_cast<DirMergeItem*>( firstChild() );
1512 }
1513 else
1514 {
1515 if ( ! bContinueWithCurrentItem )
1516 {
1517 // Depth first
1518 QListViewItem* pNextItem = m_pCurrentItemForOperation->firstChild();
1519 QListViewItem* pCurrentItem = m_pCurrentItemForOperation;
1520 while( pCurrentItem!=0 && pNextItem==0 )
1521 {
1522 // Find an item on this level that hasn't been operated yet. (Always start at beginning.)
1523 if ( pCurrentItem->parent()==0 )
1524 pNextItem = firstChild();
1525 else
1526 pNextItem = pCurrentItem->parent()->firstChild();
1527
1528 while( pNextItem!=0 && ( static_cast<DirMergeItem*>(pNextItem)->m_pMFI->m_bOperationComplete ||
1529 ( bSim && static_cast<DirMergeItem*>(pNextItem)->m_pMFI->m_bSimOpComplete ) ) )
1530 {
1531 pNextItem = pNextItem->nextSibling();
1532 }
1533
1534 if ( pNextItem == 0 )
1535 {
1536 pCurrentItem = pCurrentItem->parent();
1537 if ( pCurrentItem!=0 )
1538 {
1539 if (bSim)
1540 static_cast<DirMergeItem*>(pCurrentItem)->m_pMFI->m_bSimOpComplete = true;
1541 else
1542 {
1543 pCurrentItem->setText( s_OpStatusCol, "Done." );
1544 static_cast<DirMergeItem*>(pCurrentItem)->m_pMFI->m_bOperationComplete = true;
1545 }
1546 }
1547 }
1548 }
1549 m_pCurrentItemForOperation = static_cast<DirMergeItem*>(pNextItem);
1550 }
1551 }
1552
1553 if( m_pCurrentItemForOperation !=0 &&
1554 (bSim ? m_pCurrentItemForOperation->m_pMFI->m_bSimOpComplete :
1555 m_pCurrentItemForOperation->m_pMFI->m_bOperationComplete ))
1556 m_pCurrentItemForOperation = 0;
1557
1558 if ( m_pCurrentItemForOperation==0 )
1559 {
1560 if ( m_bRealMergeStarted )
1561 {
1562 KMessageBox::information( this, "Merge operation complete.", "The merge is complete." );
1563 m_bRealMergeStarted = false;
1564 m_pStatusInfo->setCaption("KDiff3: Merge complete.");
1565 }
1566 if ( m_bSimulatedMergeStarted )
1567 {
1568 m_bSimulatedMergeStarted = false;
1569 for( QListViewItem* p=firstChild(); p!=0; p=treeIterator(p) )
1570 {
1571 static_cast<DirMergeItem*>(p)->m_pMFI->m_bSimOpComplete = false;
1572 }
1573 m_pStatusInfo->setCaption("KDiff3: Simulated merge complete: Check if you agree with the proposed operations.");
1574 m_pStatusInfo->show();
1575 }
1576 g_pProgressDialog->hide();
1577 return;
1578 }
1579
1580 MergeFileInfos& mfi = *m_pCurrentItemForOperation->m_pMFI;
1581 bool bCreateBackups = m_pOptions->m_bDmCreateBakFiles;
1582
1583 g_pProgressDialog->setInformation( mfi.m_subPath,
1584 bSim ? double(nrOfCompletedSimItems)/nrOfItems : double(nrOfCompletedItems)/nrOfItems,
1585 true
1586 );
1587 g_pProgressDialog->show();
1588
1589 // First decide destname
1590 QString destName;
1591 switch( mfi.m_eMergeOperation )
1592 {
1593 case eNoOperation: break;
1594 case eMergeToAB: // let the user save in B. In mergeResultSaved() the file will be copied to A.
1595 case eMergeToB:
1596 case eDeleteB:
1597 case eCopyAToB: destName = fullNameB(mfi); break;
1598 case eMergeToA:
1599 case eDeleteA:
1600 case eCopyBToA: destName = fullNameA(mfi); break;
1601 case eMergeABToDest:
1602 case eMergeABCToDest:
1603 case eCopyAToDest:
1604 case eCopyBToDest:
1605 case eCopyCToDest:
1606 case eDeleteFromDest: destName = fullNameDest(mfi); break;
1607 default:
1608 KMessageBox::error( this, "Unknown merge operation.(This must never happen!)", "Error" );
1609 assert(false);
1610 }
1611
1612 bSuccess = false;
1613 bSingleFileMerge = false;
1614 switch( mfi.m_eMergeOperation )
1615 {
1616 case eNoOperation: bSuccess = true; break;
1617 case eCopyAToDest:
1618 case eCopyAToB: bSuccess = copyFLD( fullNameA(mfi), destName ); break;
1619 case eCopyBToDest:
1620 case eCopyBToA: bSuccess = copyFLD( fullNameB(mfi), destName ); break;
1621 case eCopyCToDest: bSuccess = copyFLD( fullNameC(mfi), destName ); break;
1622 case eDeleteFromDest:
1623 case eDeleteA:
1624 case eDeleteB: bSuccess = deleteFLD( destName, bCreateBackups ); break;
1625 case eDeleteAB: bSuccess = deleteFLD( fullNameA(mfi), bCreateBackups ) &&
1626 deleteFLD( fullNameB(mfi), bCreateBackups ); break;
1627 case eMergeABToDest:
1628 case eMergeToA:
1629 case eMergeToAB:
1630 case eMergeToB: bSuccess = mergeFLD( fullNameA(mfi), fullNameB(mfi), "",
1631 destName, bSingleFileMerge );
1632 break;
1633 case eMergeABCToDest:bSuccess = mergeFLD(
1634 mfi.m_bExistsInA ? fullNameA(mfi) : QString(""),
1635 mfi.m_bExistsInB ? fullNameB(mfi) : QString(""),
1636 mfi.m_bExistsInC ? fullNameC(mfi) : QString(""),
1637 destName, bSingleFileMerge );
1638 break;
1639 default:
1640 KMessageBox::error( this, "Unknown merge operation.", "Error" );
1641 assert(false);
1642 }
1643 if ( bSuccess )
1644 {
1645 if(bSim) ++nrOfCompletedSimItems;
1646 else ++nrOfCompletedItems;
1647 bContinueWithCurrentItem = false;
1648 }
1649 } // end while
1650
1651 g_pProgressDialog->hide();
1652
1653 setCurrentItem( m_pCurrentItemForOperation );
1654 ensureItemVisible( m_pCurrentItemForOperation );
1655 if ( !bSuccess && !bSingleFileMerge )
1656 {
1657 KMessageBox::error(this, i18n("An error occurred. Press OK to see detailed information.\n"), "KDiff3: Error" );
1658 m_pStatusInfo->setCaption("KDiff3: Merge-Error");
1659 m_pStatusInfo->show();
1660 if ( m_pStatusInfo->firstChild()!=0 )
1661 m_pStatusInfo->ensureItemVisible( m_pStatusInfo->last() );
1662 m_bError = true;
1663 m_pCurrentItemForOperation->setText( s_OpStatusCol, "Error." );
1664 }
1665 else
1666 {
1667 m_bError = false;
1668 }
1669 emit updateAvailabilities();
1670 }
1671
1672 void DirectoryMergeWindow::allowResizeEvents(bool bAllowResizeEvents )
1673 {
1674 m_bAllowResizeEvents = bAllowResizeEvents;
1675 }
1676
1677 void DirectoryMergeWindow::resizeEvent( QResizeEvent* e )
1678 {
1679 if (m_bAllowResizeEvents)
1680 QListView::resizeEvent(e);
1681 }
1682
1683 bool DirectoryMergeWindow::deleteFLD( const QString& name, bool bCreateBackup )
1684 {
1685 FileAccess fi(name, true);
1686 if ( !fi.exists() )
1687 return true;
1688
1689 if ( bCreateBackup )
1690 {
1691 bool bSuccess = renameFLD( name, name+".orig" );
1692 if (!bSuccess)
1693 {
1694 m_pStatusInfo->addText( "Error: While deleting "+name+": Creating backup failed." );
1695 return false;
1696 }
1697 }
1698 else
1699 {
1700 if ( fi.isDir() && !fi.isSymLink() )
1701 m_pStatusInfo->addText("delete directory recursively( "+name+" )");
1702 else
1703 m_pStatusInfo->addText("delete( "+name+" )");
1704
1705 if ( m_bSimulatedMergeStarted )
1706 {
1707 return true;
1708 }
1709
1710 if ( fi.isDir() && !fi.isSymLink() )// recursive directory delete only for real dirs, not symlinks
1711 {
1712 t_DirectoryList dirList;
1713 bool bSuccess = fi.listDir( &dirList, false, true, "*", "", "", false, false ); // not recursive, find hidden files
1714
1715 if ( !bSuccess )
1716 {
1717 // No Permission to read directory or other error.
1718 m_pStatusInfo->addText( "Error: delete dir operation failed while trying to read the directory." );
1719 return false;
1720 }
1721
1722 t_DirectoryList::iterator it; // create list iterator
1723
1724 for ( it=dirList.begin(); it!=dirList.end(); ++it ) // for each file...
1725 {
1726 FileAccess& fi2 = *it;
1727 if ( fi2.fileName() == "." || fi2.fileName()==".." )
1728 continue;
1729 bSuccess = deleteFLD( fi2.absFilePath(), false );
1730 if (!bSuccess) break;
1731 }
1732 if (bSuccess)
1733 {
1734 bSuccess = FileAccess::removeDir( name );
1735 if ( !bSuccess )
1736 {
1737 m_pStatusInfo->addText( "Error: rmdir( "+name+" ) operation failed." );
1738 return false;
1739 }
1740 }
1741 }
1742 else
1743 {
1744 bool bSuccess = FileAccess::removeFile( name );
1745 if ( !bSuccess )
1746 {
1747 m_pStatusInfo->addText( "Error: delete operation failed." );
1748 return false;
1749 }
1750 }
1751 }
1752 return true;
1753 }
1754
1755 bool DirectoryMergeWindow::mergeFLD( const QString& nameA,const QString& nameB,const QString& nameC,const QString& nameDest, bool& bSingleFileMerge )
1756 {
1757 FileAccess fi(nameA);
1758 if (fi.isDir())
1759 {
1760 return makeDir(nameDest);
1761 }
1762
1763 // Make sure that the dir exists, into which we will save the file later.
1764 int pos=nameDest.findRev('/');
1765 if ( pos>0 )
1766 {
1767 QString parentName = nameDest.left(pos);
1768 bool bSuccess = makeDir(parentName, true /*quiet*/);
1769 if (!bSuccess)
1770 return false;
1771 }
1772
1773 m_pStatusInfo->addText("manual merge( "+nameA+ ", "+nameB+","+nameC+" -> " + nameDest +" )" );
1774 if ( m_bSimulatedMergeStarted )
1775 {
1776 m_pStatusInfo->addText(" Note: After a manual merge the user should continue via F5." );
1777 return true;
1778 }
1779
1780 bSingleFileMerge = true;
1781 m_pCurrentItemForOperation->setText( s_OpStatusCol, "In progress ..." );
1782 ensureItemVisible( m_pCurrentItemForOperation );
1783
1784 emit startDiffMerge( nameA, nameB, nameC, nameDest, "","","" );
1785
1786 return false;
1787 }
1788
1789 bool DirectoryMergeWindow::copyFLD( const QString& srcName, const QString& destName )
1790 {
1791 if ( srcName == destName )
1792 return true;
1793
1794 if ( FileAccess(destName, true).exists() )
1795 {
1796 bool bSuccess = deleteFLD( destName, m_pOptions->m_bDmCreateBakFiles );
1797 if ( !bSuccess )
1798 {
1799 m_pStatusInfo->addText("Error: copy( "+srcName+ " -> " + destName +" ) failed."
1800 "Deleting existing destination failed.");
1801 return false;
1802 }
1803 }
1804
1805 FileAccess fi( srcName );
1806
1807 if ( fi.isSymLink() && (fi.isDir() && !m_bFollowDirLinks || !fi.isDir() && !m_bFollowDirLinks) )
1808 {
1809 m_pStatusInfo->addText("copyLink( "+srcName+ " -> " + destName +" )");
1810 #ifdef _WIN32
1811 // What are links?
1812 #else
1813 if ( m_bSimulatedMergeStarted )
1814 {
1815 return true;
1816 }
1817 FileAccess destFi(destName);
1818 if ( !destFi.isLocal() || !fi.isLocal() )
1819 {
1820 m_pStatusInfo->addText("Error: copyLink failed: Remote links are not yet supported.");
1821 return false;
1822 }
1823 QString linkTarget = fi.readLink();
1824 bool bSuccess = FileAccess::symLink( linkTarget, destName );
1825 if (!bSuccess)
1826 m_pStatusInfo->addText("Error: copyLink failed.");
1827 return bSuccess;
1828 #endif
1829 }
1830
1831 if ( fi.isDir() )
1832 {
1833 bool bSuccess = makeDir( destName );
1834 return bSuccess;
1835 }
1836
1837 int pos=destName.findRev('/');
1838 if ( pos>0 )
1839 {
1840 QString parentName = destName.left(pos);
1841 bool bSuccess = makeDir(parentName, true /*quiet*/);
1842 if (!bSuccess)
1843 return false;
1844 }
1845
1846 m_pStatusInfo->addText("copy( "+srcName+ " -> " + destName +" )");
1847
1848 if ( m_bSimulatedMergeStarted )
1849 {
1850 return true;
1851 }
1852
1853 FileAccess faSrc ( srcName );
1854 bool bSuccess = faSrc.copyFile( destName );
1855 if (! bSuccess ) m_pStatusInfo->addText( faSrc.getStatusText() );
1856 return bSuccess;
1857 }
1858
1859 // Rename is not an operation that can be selected by the user.
1860 // It will only be used to create backups.
1861 // Hence it will delete an existing destination without making a backup (of the old backup.)
1862 bool DirectoryMergeWindow::renameFLD( const QString& srcName, const QString& destName )
1863 {
1864 if ( srcName == destName )
1865 return true;
1866
1867 if ( FileAccess(destName, true).exists() )
1868 {
1869 bool bSuccess = deleteFLD( destName, false /*no backup*/ );
1870 if (!bSuccess)
1871 {
1872 m_pStatusInfo->addText( "Error during rename( "+srcName+" -> " + destName + " ): "
1873 "Cannot delete existing destination." );
1874 return false;
1875 }
1876 }
1877
1878 m_pStatusInfo->addText("rename( "+srcName+ " -> " + destName +" )");
1879 if ( m_bSimulatedMergeStarted )
1880 {
1881 return true;
1882 }
1883
1884 bool bSuccess = FileAccess( srcName ).rename( destName );
1885 if (!bSuccess)
1886 {
1887 m_pStatusInfo->addText( "Error: Rename failed." );
1888 return false;
1889 }
1890
1891 return true;
1892 }
1893
1894 bool DirectoryMergeWindow::makeDir( const QString& name, bool bQuiet )
1895 {
1896 FileAccess fi(name, true);
1897 if( fi.exists() && fi.isDir() )
1898 return true;
1899
1900 if( fi.exists() && !fi.isDir() )
1901 {
1902 bool bSuccess = deleteFLD( name, true );
1903 if (!bSuccess)
1904 {
1905 m_pStatusInfo->addText( "Error during makeDir of "+name+
1906 "Cannot delete existing file." );
1907 return false;
1908 }
1909 }
1910
1911 int pos=name.findRev('/');
1912 if ( pos>0 )
1913 {
1914 QString parentName = name.left(pos);
1915 bool bSuccess = makeDir(parentName,true);
1916 if (!bSuccess)
1917 return false;
1918 }
1919
1920 if ( ! bQuiet )
1921 m_pStatusInfo->addText("makeDir( " + name + " )");
1922
1923 if ( m_bSimulatedMergeStarted )
1924 {
1925 return true;
1926 }
1927
1928 bool bSuccess = FileAccess::makeDir( name );
1929 if ( bSuccess == false )
1930 {
1931 m_pStatusInfo->addText( "Error while creating directory." );
1932 return false;
1933 }
1934 return true;
1935 }
1936
1937
1938
1939
1940
1941 DirectoryMergeInfo::DirectoryMergeInfo( QWidget* pParent )
1942 : QFrame(pParent)
1943 {
1944 QVBoxLayout *topLayout = new QVBoxLayout( this );
1945
1946
1947 QGridLayout *grid = new QGridLayout( topLayout );
1948 grid->setColStretch(1,10);
1949
1950 int line=0;
1951
1952 m_pA = new QLabel("A",this); grid->addWidget( m_pA,line, 0 );
1953 m_pInfoA = new QLabel(this); grid->addWidget( m_pInfoA,line,1 ); ++line;
1954 m_pB = new QLabel("B",this); grid->addWidget( m_pB,line, 0 );
1955 m_pInfoB = new QLabel(this); grid->addWidget( m_pInfoB,line,1 ); ++line;
1956 m_pC = new QLabel("C",this); grid->addWidget( m_pC,line, 0 );
1957 m_pInfoC = new QLabel(this); grid->addWidget( m_pInfoC,line,1 ); ++line;
1958 m_pDest = new QLabel("Dest",this); grid->addWidget( m_pDest,line, 0 );
1959 m_pInfoDest = new QLabel(this); grid->addWidget( m_pInfoDest,line,1 ); ++line;
1960
1961 m_pInfoList = new QListView(this); topLayout->addWidget( m_pInfoList );
1962 m_pInfoList->addColumn("Dir");
1963 m_pInfoList->addColumn("Type");
1964 m_pInfoList->addColumn("Size");
1965 m_pInfoList->addColumn("Attr");
1966 m_pInfoList->addColumn("Last Modification");
1967 m_pInfoList->addColumn("Link-Destination");
1968 setMinimumSize( 100,100 );
1969 }
1970
1971 static void addListViewItem( QListView* pListView, const QString& dir,
1972 const QString& basePath, FileAccess& fi )
1973 {
1974 if ( basePath.isEmpty() )
1975 {
1976 return;
1977 }
1978 else
1979 {
1980 if ( fi.exists() )
1981 {
1982 #if QT_VERSION==230
1983 QString dateString = fi.lastModified().toString();
1984 #else
1985 QString dateString = fi.lastModified().toString("yyyy-MM-dd hh:mm:ss");
1986 #endif
1987
1988 new QListViewItem(
1989 pListView,
1990 dir,
1991 QString( fi.isDir() ? "Dir" : "File" ) + (fi.isSymLink() ? "-Link" : ""),
1992 QString::number(fi.size()),
1993 QString(fi.isReadable() ? "r" : " ") + (fi.isWritable()?"w" : " ")
1994 #ifdef _WIN32
1995 /*Future: Use GetFileAttributes()*/,
1996 #else
1997 + (fi.isExecutable()?"x" : " "),
1998 #endif
1999 dateString,
2000 QString(fi.isSymLink() ? (" -> " + fi.readLink()) : QString(""))
2001 );
2002 }
2003 else
2004 {
2005 new QListViewItem(
2006 pListView,
2007 dir,
2008 "not available",
2009 "",
2010 "",
2011 "",
2012 ""
2013 );
2014 }
2015 }
2016 }
2017
2018 void DirectoryMergeInfo::setInfo(
2019 const FileAccess& dirA,
2020 const FileAccess& dirB,
2021 const FileAccess& dirC,
2022 const FileAccess& dirDest,
2023 MergeFileInfos& mfi )
2024 {
2025 bool bHideDest = false;
2026 if ( dirA.absFilePath()==dirDest.absFilePath() )
2027 {
2028 m_pA->setText( "A (Dest): " ); bHideDest=true;
2029 }
2030 else
2031 m_pA->setText( !dirC.isValid() ? "A: " : "A (Base): ");
2032
2033 m_pInfoA->setText( dirA.prettyAbsPath() );
2034
2035 if ( dirB.absFilePath()==dirDest.absFilePath() )
2036 {
2037 m_pB->setText( "B (Dest): " ); bHideDest=true;
2038 }
2039 else
2040 m_pB->setText( "B: " );
2041 m_pInfoB->setText( dirB.prettyAbsPath() );
2042
2043 if ( dirC.absFilePath()==dirDest.absFilePath() )
2044 {
2045 m_pC->setText( "C (Dest): " ); bHideDest=true;
2046 }
2047 else
2048 m_pC->setText( "C: " );
2049 m_pInfoC->setText( dirC.prettyAbsPath() );
2050
2051 m_pDest->setText( "Dest: " ); m_pInfoDest->setText( dirDest.prettyAbsPath() );
2052
2053 if (!dirC.isValid()) { m_pC->hide(); m_pInfoC->hide(); }
2054 else { m_pC->show(); m_pInfoC->show(); }
2055
2056 if (!dirDest.isValid()||bHideDest) { m_pDest->hide(); m_pInfoDest->hide(); }
2057 else { m_pDest->show(); m_pInfoDest->show(); }
2058
2059 m_pInfoList->clear();
2060 addListViewItem( m_pInfoList, "A", dirA.prettyAbsPath(), mfi.m_fileInfoA );
2061 addListViewItem( m_pInfoList, "B", dirB.prettyAbsPath(), mfi.m_fileInfoB );
2062 addListViewItem( m_pInfoList, "C", dirC.prettyAbsPath(), mfi.m_fileInfoC );
2063 if (!bHideDest)
2064 {
2065 FileAccess fiDest( dirDest.prettyAbsPath() + "/" + mfi.m_subPath, true );
2066 addListViewItem( m_pInfoList, "Dest", dirDest.prettyAbsPath(), fiDest );
2067 }
2068 }
2069
2070 #include "directorymergewindow.moc"