Mercurial > hg > easyhg-kdiff3
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" |