directorymergewindow.cpp 120 KB
Newer Older
Joachim Eibl's avatar
Joachim Eibl committed
1
2
/***************************************************************************
                          directorymergewindow.cpp
Joachim Eibl's avatar
Joachim Eibl committed
3
                             -----------------
Joachim Eibl's avatar
Joachim Eibl committed
4
    begin                : Sat Oct 19 2002
5
    copyright            : (C) 2002-2011 by Joachim Eibl
Joachim Eibl's avatar
Joachim Eibl committed
6
    email                : joachim.eibl at gmx.de
Joachim Eibl's avatar
Joachim Eibl committed
7
8
9
10
11
12
13
14
15
16
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
17
#include "stable.h"
Joachim Eibl's avatar
Joachim Eibl committed
18
#include "directorymergewindow.h"
19
20
#include "options.h"
#include "progress.h"
Joachim Eibl's avatar
Joachim Eibl committed
21
22
23
#include <vector>
#include <map>

Joachim Eibl's avatar
Joachim Eibl committed
24
25
26
27
#include <QDir>
#include <QApplication>
#include <QPixmap>
#include <QImage>
Joachim Eibl's avatar
Joachim Eibl committed
28
29
#include <QTextStream>
#include <QKeyEvent>
Joachim Eibl's avatar
Joachim Eibl committed
30
31
32
33
34
35
36
37
#include <QMenu>
#include <QRegExp>
#include <QMessageBox>
#include <QLayout>
#include <QLabel>
#include <QSplitter>
#include <QTextEdit>
#include <QItemDelegate>
38
#include <QPushButton>
39
#include <algorithm>
Joachim Eibl's avatar
Joachim Eibl committed
40

Michael Reeves's avatar
Michael Reeves committed
41
#include <QMenu>
Michael Reeves's avatar
Michael Reeves committed
42
#include <QAction>
Joachim Eibl's avatar
Joachim Eibl committed
43
#include <kmessagebox.h>
Joachim Eibl's avatar
Joachim Eibl committed
44
#include <kfiledialog.h>
Joachim Eibl's avatar
Joachim Eibl committed
45
46
#include <kiconloader.h>
#include <klocale.h>
Joachim Eibl's avatar
0.9.93    
Joachim Eibl committed
47
#include <ktoggleaction.h>
Joachim Eibl's avatar
Joachim Eibl committed
48

Joachim Eibl's avatar
Joachim Eibl committed
49
#include <assert.h>
Joachim Eibl's avatar
Joachim Eibl committed
50
//#include <konq_popupmenu.h>
Michael Reeves's avatar
Michael Reeves committed
51
#include <QFileDialog>
Joachim Eibl's avatar
Joachim Eibl committed
52

Joachim Eibl's avatar
0.9.93    
Joachim Eibl committed
53
#include "guiutils.h"
Joachim Eibl's avatar
Joachim Eibl committed
54

Joachim Eibl's avatar
0.9.93    
Joachim Eibl committed
55
static bool conflictingFileTypes(MergeFileInfos& mfi);
56
static QPixmap getOnePixmap( e_Age eAge, bool bLink, bool bDir );
Joachim Eibl's avatar
Joachim Eibl committed
57

58
class StatusInfo : public QDialog
Joachim Eibl's avatar
Joachim Eibl committed
59
{
60
   QTextEdit* m_pTextEdit;
Joachim Eibl's avatar
Joachim Eibl committed
61
public:
62
   StatusInfo(QWidget* pParent) : QDialog( pParent )
Joachim Eibl's avatar
Joachim Eibl committed
63
   {
64
65
66
      QVBoxLayout* pVLayout = new QVBoxLayout( this );     
      m_pTextEdit = new QTextEdit(this);
      pVLayout->addWidget( m_pTextEdit );
Joachim Eibl's avatar
Joachim Eibl committed
67
      setObjectName("StatusInfo");
Joachim Eibl's avatar
Joachim Eibl committed
68
      setWindowFlags(Qt::Dialog);
69
70
      m_pTextEdit->setWordWrapMode(QTextOption::NoWrap);
      m_pTextEdit->setReadOnly(true);
71
      QPushButton* pClose = new QPushButton(tr("Close"));
Michael Reeves's avatar
Michael Reeves committed
72
      connect(pClose, &QPushButton::clicked, this, &QDialog::accept);
73
      pVLayout->addWidget(pClose);
Joachim Eibl's avatar
Joachim Eibl committed
74
75
   }

Joachim Eibl's avatar
Joachim Eibl committed
76
   bool isEmpty(){ 
77
78
      return m_pTextEdit->toPlainText().isEmpty(); 
   }
Joachim Eibl's avatar
Joachim Eibl committed
79
80
81

   void addText(const QString& s )
   {
82
83
84
85
86
87
      m_pTextEdit->append(s);
   }

   void clear()
   {
      m_pTextEdit->clear();
Joachim Eibl's avatar
Joachim Eibl committed
88
89
   }

Joachim Eibl's avatar
0.9.93    
Joachim Eibl committed
90
   void setVisible(bool bVisible)
Joachim Eibl's avatar
Joachim Eibl committed
91
   {
Joachim Eibl's avatar
0.9.93    
Joachim Eibl committed
92
93
      if (bVisible)
      {
94
95
96
         m_pTextEdit->moveCursor ( QTextCursor::End );
         m_pTextEdit->moveCursor ( QTextCursor::StartOfLine );
         m_pTextEdit->ensureCursorVisible();
Joachim Eibl's avatar
0.9.93    
Joachim Eibl committed
97
      }
Joachim Eibl's avatar
Joachim Eibl committed
98

99
      QDialog::setVisible(bVisible);
100
101
      if ( bVisible )
         setWindowState( windowState() | Qt::WindowMaximized );
Joachim Eibl's avatar
Joachim Eibl committed
102
103
   }
};
Joachim Eibl's avatar
Joachim Eibl committed
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135


class TempRemover
{
public:
   TempRemover( const QString& origName, FileAccess& fa );
   ~TempRemover();
   QString name() { return m_name; }
   bool success() { return m_bSuccess; }
private:
   QString m_name;
   bool m_bTemp;
   bool m_bSuccess;
};
TempRemover::TempRemover(const QString& origName, FileAccess& fa)
{
   if ( fa.isLocal() )
   {
      m_name = origName;
      m_bTemp = false;
      m_bSuccess = true;
   }
   else
   {
      m_name = FileAccess::tempFileName();
      m_bSuccess = fa.copyFile( m_name );
      m_bTemp = m_bSuccess;
   }
}
TempRemover::~TempRemover()
{
   if ( m_bTemp && ! m_name.isEmpty() )
Joachim Eibl's avatar
Joachim Eibl committed
136
      FileAccess::removeTempFile(m_name);
Joachim Eibl's avatar
Joachim Eibl committed
137
138
}

139
140

enum Columns
Joachim Eibl's avatar
Joachim Eibl committed
141
{
142
143
144
145
146
147
148
149
150
151
152
   s_NameCol = 0,
   s_ACol = 1,
   s_BCol = 2,
   s_CCol = 3,
   s_OpCol = 4,
   s_OpStatusCol = 5,
   s_UnsolvedCol = 6,    // Nr of unsolved conflicts (for 3 input files)
   s_SolvedCol = 7,      // Nr of auto-solvable conflicts (for 3 input files)
   s_NonWhiteCol = 8,    // Nr of nonwhite deltas (for 2 input files)
   s_WhiteCol = 9        // Nr of white deltas (for 2 input files)
};
Joachim Eibl's avatar
Joachim Eibl committed
153

154
155
156
157
158
159
160
161
162
163
enum e_OperationStatus
{
   eOpStatusNone,
   eOpStatusDone,
   eOpStatusError,
   eOpStatusSkipped,
   eOpStatusNotSaved,
   eOpStatusInProgress,
   eOpStatusToDo
};
Joachim Eibl's avatar
Joachim Eibl committed
164

165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
class MergeFileInfos
{
public:
   MergeFileInfos()
   { 
      m_bEqualAB=false; m_bEqualAC=false; m_bEqualBC=false;
      m_pParent=0;                                         
      m_bOperationComplete=false; m_bSimOpComplete = false;
      m_eMergeOperation=eNoOperation;
      m_eOpStatus = eOpStatusNone;
      m_ageA = eNotThere; m_ageB=eNotThere; m_ageC=eNotThere;
      m_bConflictingAges=false; 
      m_pFileInfoA = 0; m_pFileInfoB = 0; m_pFileInfoC = 0; 
   }
   ~MergeFileInfos()
   {
      //for( int i=0; i<m_children.count(); ++i )
      //   delete m_children[i];
      m_children.clear();
   }
   //bool operator>( const MergeFileInfos& );
   QString subPath() const
   {
      return m_pFileInfoA && m_pFileInfoA->exists() ? m_pFileInfoA->filePath() :
             m_pFileInfoB && m_pFileInfoB->exists() ? m_pFileInfoB->filePath() :
             m_pFileInfoC && m_pFileInfoC->exists() ? m_pFileInfoC->filePath() :
             QString("");
Joachim Eibl's avatar
0.9.80    
Joachim Eibl committed
192
   }
193
   QString fileName() const
Joachim Eibl's avatar
0.9.80    
Joachim Eibl committed
194
   {
195
196
197
198
      return m_pFileInfoA && m_pFileInfoA->exists() ? m_pFileInfoA->fileName() :
             m_pFileInfoB && m_pFileInfoB->exists() ? m_pFileInfoB->fileName() :
             m_pFileInfoC && m_pFileInfoC->exists() ? m_pFileInfoC->fileName() :
             QString("");
Joachim Eibl's avatar
Joachim Eibl committed
199
   }
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
   bool dirA() const { return m_pFileInfoA ? m_pFileInfoA->isDir() : false; }
   bool dirB() const { return m_pFileInfoB ? m_pFileInfoB->isDir() : false; }
   bool dirC() const { return m_pFileInfoC ? m_pFileInfoC->isDir() : false; }
   bool isLinkA() const { return m_pFileInfoA ? m_pFileInfoA->isSymLink() : false; }
   bool isLinkB() const { return m_pFileInfoB ? m_pFileInfoB->isSymLink() : false; }
   bool isLinkC() const { return m_pFileInfoC ? m_pFileInfoC->isSymLink() : false; }
   bool existsInA() const { return m_pFileInfoA!=0; }
   bool existsInB() const { return m_pFileInfoB!=0; }
   bool existsInC() const { return m_pFileInfoC!=0; }
   MergeFileInfos* m_pParent;
   FileAccess*     m_pFileInfoA;
   FileAccess*     m_pFileInfoB;
   FileAccess*     m_pFileInfoC;
   TotalDiffStatus m_totalDiffStatus;
   QList<MergeFileInfos*> m_children;

   e_MergeOperation m_eMergeOperation : 5;
   e_OperationStatus m_eOpStatus : 4;

   e_Age m_ageA                  : 3;
   e_Age m_ageB                  : 3;
   e_Age m_ageC                  : 3;

   bool m_bOperationComplete     : 1;
   bool m_bSimOpComplete         : 1;

   bool m_bEqualAB               : 1;
   bool m_bEqualAC               : 1;
   bool m_bEqualBC               : 1;
   bool m_bConflictingAges       : 1;       // Equal age but files are not!
};
Joachim Eibl's avatar
Joachim Eibl committed
231

232
static Qt::CaseSensitivity s_eCaseSensitivity = Qt::CaseSensitive;
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268

class DirectoryMergeWindow::Data : public QAbstractItemModel
{
public:
   DirectoryMergeWindow* q;
   Data( DirectoryMergeWindow* pDMW )
   {
      q = pDMW;
      m_pOptions = 0;
      m_pIconLoader = 0;
      m_pDirectoryMergeInfo = 0;
      m_bSimulatedMergeStarted=false;
      m_bRealMergeStarted=false;
      m_bError = false;
      m_bSyncMode = false;
      m_pStatusInfo = new StatusInfo(q);
      m_pStatusInfo->hide();
      m_bScanning = false;
      m_bCaseSensitive = true;
      m_bUnfoldSubdirs = false;
      m_bSkipDirStatus = false;
      m_pRoot = new MergeFileInfos;
   }
   ~Data()
   {
      delete m_pRoot;
   }
   // Implement QAbstractItemModel
   QVariant	data ( const QModelIndex & index, int role = Qt::DisplayRole ) const;
   //Qt::ItemFlags flags ( const QModelIndex & index ) const
   QModelIndex	parent ( const QModelIndex & index ) const
   {
      MergeFileInfos* pMFI = getMFI( index );
      if ( pMFI == 0 || pMFI==m_pRoot || pMFI->m_pParent==m_pRoot )
         return QModelIndex();
      else
Joachim Eibl's avatar
Joachim Eibl committed
269
      {
270
271
         MergeFileInfos* pParentsParent = pMFI->m_pParent->m_pParent;
         return createIndex( pParentsParent->m_children.indexOf(pMFI->m_pParent), 0, pMFI->m_pParent );
Joachim Eibl's avatar
Joachim Eibl committed
272
273
      }
   }
274
   int	rowCount ( const QModelIndex & parent = QModelIndex() ) const
Joachim Eibl's avatar
Joachim Eibl committed
275
   {
276
277
278
279
280
      MergeFileInfos* pParentMFI = getMFI( parent );
      if ( pParentMFI!=0 )
         return pParentMFI->m_children.count();
      else
         return m_pRoot->m_children.count();
Joachim Eibl's avatar
Joachim Eibl committed
281
   }
282
   int	columnCount ( const QModelIndex & /*parent*/ ) const 
Joachim Eibl's avatar
Joachim Eibl committed
283
   {
284
      return 10;
Joachim Eibl's avatar
Joachim Eibl committed
285
   }
286
   QModelIndex	index ( int row, int column, const QModelIndex & parent ) const
Joachim Eibl's avatar
Joachim Eibl committed
287
   {
288
289
290
291
292
293
294
      MergeFileInfos* pParentMFI = getMFI( parent );
      if ( pParentMFI == 0 && row < m_pRoot->m_children.count() )
         return createIndex( row, column, m_pRoot->m_children[row] );
      else if ( pParentMFI != 0 && row < pParentMFI->m_children.count() )
         return createIndex( row, column, pParentMFI->m_children[row] );
      else
         return QModelIndex();
Joachim Eibl's avatar
Joachim Eibl committed
295
   }
296
297
298
299
   QVariant	headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const;
   void sort( int column, Qt::SortOrder order );
   // private data and helper methods
   MergeFileInfos* getMFI( const QModelIndex& mi ) const
Joachim Eibl's avatar
Joachim Eibl committed
300
   {
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
      if ( mi.isValid() )
         return (MergeFileInfos*)mi.internalPointer();
      else
         return 0;
   }
   MergeFileInfos* m_pRoot;

   QString fullNameA( const MergeFileInfos& mfi )
   { return mfi.existsInA() ? mfi.m_pFileInfoA->absoluteFilePath() : m_dirA.absoluteFilePath() + "/" + mfi.subPath(); }
   QString fullNameB( const MergeFileInfos& mfi )
   { return mfi.existsInB() ? mfi.m_pFileInfoB->absoluteFilePath() : m_dirB.absoluteFilePath() + "/" + mfi.subPath(); }
   QString fullNameC( const MergeFileInfos& mfi )
   { return mfi.existsInC() ? mfi.m_pFileInfoC->absoluteFilePath() : m_dirC.absoluteFilePath() + "/" + mfi.subPath(); }
   QString fullNameDest( const MergeFileInfos& mfi )
   { if       ( m_dirDestInternal.prettyAbsPath() == m_dirC.prettyAbsPath() ) return fullNameC(mfi);
     else if ( m_dirDestInternal.prettyAbsPath() == m_dirB.prettyAbsPath() ) return fullNameB(mfi);
     else return m_dirDestInternal.absoluteFilePath() + "/" + mfi.subPath(); 
   }

   FileAccess m_dirA;
   FileAccess m_dirB;
   FileAccess m_dirC;
   FileAccess m_dirDest;
   FileAccess m_dirDestInternal;
325
   Options* m_pOptions;
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392

   void calcDirStatus( bool bThreeDirs, const QModelIndex& mi, 
      int& nofFiles, int& nofDirs, int& nofEqualFiles, int& nofManualMerges );

   void mergeContinue( bool bStart, bool bVerbose );

   void prepareListView(ProgressProxy& pp);
   void calcSuggestedOperation( const QModelIndex& mi, e_MergeOperation eDefaultOperation );
   void setAllMergeOperations( e_MergeOperation eDefaultOperation );
   friend class MergeFileInfos;

   bool canContinue();
   QModelIndex treeIterator( QModelIndex mi, bool bVisitChildren=true, bool bFindInvisible=false );
   void prepareMergeStart( const QModelIndex& miBegin, const QModelIndex& miEnd, bool bVerbose );
   bool executeMergeOperation( MergeFileInfos& mfi, bool& bSingleFileMerge );

   void scanDirectory( const QString& dirName, t_DirectoryList& dirList );
   void scanLocalDirectory( const QString& dirName, t_DirectoryList& dirList );
   bool fastFileComparison( FileAccess& fi1, FileAccess& fi2,
                            bool& bError, QString& status );
   void compareFilesAndCalcAges( MergeFileInfos& mfi );

   void setMergeOperation( const QModelIndex& mi, e_MergeOperation eMergeOp, bool bRecursive = true );
   bool isDir( const QModelIndex& mi );
   QString getFileName( const QModelIndex& mi );

   bool copyFLD( const QString& srcName, const QString& destName );
   bool deleteFLD( const QString& name, bool bCreateBackup );
   bool makeDir( const QString& name, bool bQuiet=false );
   bool renameFLD( const QString& srcName, const QString& destName );
   bool mergeFLD( const QString& nameA,const QString& nameB,const QString& nameC,
                  const QString& nameDest, bool& bSingleFileMerge );

   t_DirectoryList m_dirListA;
   t_DirectoryList m_dirListB;
   t_DirectoryList m_dirListC;

   QString m_dirMergeStateFilename;

   class FileKey
   {
   public:
      const FileAccess* m_pFA;
      FileKey( const FileAccess& fa ) : m_pFA( &fa ){}

      int getParents( const FileAccess* pFA, const FileAccess* v[] ) const
      {
         int s = 0;
         for(s=0; pFA->parent() != 0 ; pFA=pFA->parent(), ++s )
            v[s] = pFA ;
         return s;
      }

      // This is essentially the same as 
      // int r = filePath().compare( fa.filePath() )
      // if ( r<0 ) return true;
      // if ( r==0 ) return m_col < fa.m_col;
      // return false;
      bool operator< ( const FileKey& fk ) const
      {
         const FileAccess* v1[100];
         const FileAccess* v2[100];
         int v1Size = getParents(m_pFA, v1);
         int v2Size = getParents(fk.m_pFA, v2);
        
         for( int i=0; i<v1Size && i<v2Size; ++i )
         {
393
            int r = v1[v1Size-i-1]->fileName().compare( v2[v2Size-i-1]->fileName(), s_eCaseSensitivity );
394
395
396
397
398
            if (  r < 0  )
               return true;
            else if ( r > 0  )
               return false;
         }
Joachim Eibl's avatar
Joachim Eibl committed
399

400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
         if ( v1Size < v2Size )
            return true;
         return false;
      }
   };
   typedef QMap<FileKey, MergeFileInfos> t_fileMergeMap;
   t_fileMergeMap m_fileMergeMap;

   bool m_bFollowDirLinks;
   bool m_bFollowFileLinks;
   bool m_bSimulatedMergeStarted;
   bool m_bRealMergeStarted;
   bool m_bError;
   bool m_bSyncMode;
   bool m_bDirectoryMerge; // if true, then merge is the default operation, otherwise it's diff.
   bool m_bCaseSensitive;
   bool m_bUnfoldSubdirs;
   bool m_bSkipDirStatus;   
   bool m_bScanning; // true while in init()

   KIconLoader* m_pIconLoader;
   DirectoryMergeInfo* m_pDirectoryMergeInfo;
   StatusInfo* m_pStatusInfo;

   typedef std::list< QModelIndex > MergeItemList; // linked list
   MergeItemList m_mergeItemList;
   MergeItemList::iterator m_currentIndexForOperation;

   QModelIndex m_selection1Index;
   QModelIndex m_selection2Index;
   QModelIndex m_selection3Index;
   void selectItemAndColumn( const QModelIndex& mi, bool bContextMenu);
   friend class DirMergeItem;

Michael Reeves's avatar
Michael Reeves committed
434
435
436
437
438
439
440
441
442
443
444
445
   QAction * m_pDirStartOperation;
   QAction * m_pDirRunOperationForCurrentItem;
   QAction * m_pDirCompareCurrent;
   QAction * m_pDirMergeCurrent;
   QAction * m_pDirRescan;
   QAction * m_pDirChooseAEverywhere;
   QAction * m_pDirChooseBEverywhere;
   QAction * m_pDirChooseCEverywhere;
   QAction * m_pDirAutoChoiceEverywhere;
   QAction * m_pDirDoNothingEverywhere;
   QAction * m_pDirFoldAll;
   QAction * m_pDirUnfoldAll;
446
447
448
449
450
451
452
453
454
455

   KToggleAction* m_pDirShowIdenticalFiles;
   KToggleAction* m_pDirShowDifferentFiles;
   KToggleAction* m_pDirShowFilesOnlyInA;
   KToggleAction* m_pDirShowFilesOnlyInB;
   KToggleAction* m_pDirShowFilesOnlyInC;

   KToggleAction* m_pDirSynchronizeDirectories;
   KToggleAction* m_pDirChooseNewerFiles;

Michael Reeves's avatar
Michael Reeves committed
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
   QAction * m_pDirCompareExplicit;
   QAction * m_pDirMergeExplicit;

   QAction * m_pDirCurrentDoNothing;
   QAction * m_pDirCurrentChooseA;
   QAction * m_pDirCurrentChooseB;
   QAction * m_pDirCurrentChooseC;
   QAction * m_pDirCurrentMerge;
   QAction * m_pDirCurrentDelete;

   QAction * m_pDirCurrentSyncDoNothing;
   QAction * m_pDirCurrentSyncCopyAToB;
   QAction * m_pDirCurrentSyncCopyBToA;
   QAction * m_pDirCurrentSyncDeleteA;
   QAction * m_pDirCurrentSyncDeleteB;
   QAction * m_pDirCurrentSyncDeleteAAndB;
   QAction * m_pDirCurrentSyncMergeToA;
   QAction * m_pDirCurrentSyncMergeToB;
   QAction * m_pDirCurrentSyncMergeToAAndB;

   QAction * m_pDirSaveMergeState;
   QAction * m_pDirLoadMergeState;
478
479
480
481
482
483
484
485
486
487
488

   bool init( FileAccess& dirA, FileAccess& dirB, FileAccess& dirC, FileAccess& dirDest, bool bDirectoryMerge, bool bReload );
   void setOpStatus( const QModelIndex& mi, e_OperationStatus eOpStatus )
   {
      if ( MergeFileInfos* pMFI = getMFI(mi) )
      {
         pMFI->m_eOpStatus = eOpStatus;
         emit dataChanged( mi, mi );
      }
   }
};
Joachim Eibl's avatar
Joachim Eibl committed
489

490
491
492
493
QVariant DirectoryMergeWindow::Data::data( const QModelIndex & index, int role ) const
{
   MergeFileInfos* pMFI = getMFI( index );
   if ( pMFI )
Joachim Eibl's avatar
Joachim Eibl committed
494
   {
495
      if ( role == Qt::DisplayRole )
Joachim Eibl's avatar
Joachim Eibl committed
496
      {
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
         switch ( index.column() )
         {
         case s_NameCol:     return QFileInfo(pMFI->subPath()).fileName();
         case s_ACol:        return "A";
         case s_BCol:        return "B";
         case s_CCol:        return "C";
         //case s_OpCol:       return i18n("Operation");
         //case s_OpStatusCol: return i18n("Status");
         case s_UnsolvedCol: return i18n("Unsolved");
         case s_SolvedCol:   return i18n("Solved");
         case s_NonWhiteCol: return i18n("Nonwhite");
         case s_WhiteCol:    return i18n("White");
         //default :           return QVariant();
         }

         if ( s_OpCol == index.column() )
         {            
            bool bDir = pMFI->dirA() || pMFI->dirB() || pMFI->dirC();
            switch( pMFI->m_eMergeOperation )
            {
            case eNoOperation:      return ""; break;
            case eCopyAToB:         return i18n("Copy A to B");     break;
            case eCopyBToA:         return i18n("Copy B to A");     break;
            case eDeleteA:          return i18n("Delete A");        break;
            case eDeleteB:          return i18n("Delete B");        break;
            case eDeleteAB:         return i18n("Delete A & B");    break;
            case eMergeToA:         return i18n("Merge to A");      break;
            case eMergeToB:         return i18n("Merge to B");      break;
            case eMergeToAB:        return i18n("Merge to A & B");  break;
            case eCopyAToDest:      return "A";    break;
            case eCopyBToDest:      return "B";    break;
            case eCopyCToDest:      return "C";    break;
            case eDeleteFromDest:   return i18n("Delete (if exists)");  break;
            case eMergeABCToDest:   return bDir ? i18n("Merge") : i18n("Merge (manual)");    break;
            case eMergeABToDest:    return bDir ? i18n("Merge") : i18n("Merge (manual)");    break;
            case eConflictingFileTypes: return i18n("Error: Conflicting File Types");         break;
            case eChangedAndDeleted: return i18n("Error: Changed and Deleted");               break;
            case eConflictingAges:  return i18n("Error: Dates are equal but files are not."); break;
            default:                assert(false); break;
            }
         }
         if ( s_OpStatusCol == index.column() )
         {
            switch( pMFI->m_eOpStatus )
            {
            case eOpStatusNone: return "";
            case eOpStatusDone: return i18n("Done");
            case eOpStatusError: return i18n("Error");
            case eOpStatusSkipped: return i18n("Skipped.");
            case eOpStatusNotSaved:return i18n("Not saved.");
            case eOpStatusInProgress: return i18n("In progress...");
            case eOpStatusToDo: return i18n("To do.");
            }
         }
Joachim Eibl's avatar
Joachim Eibl committed
551
      }
552
553
554
555
556
557
558
      else if ( role == Qt::DecorationRole )
      {
         if ( s_NameCol == index.column() )
         {
            return getOnePixmap( eAgeEnd, pMFI->isLinkA() || pMFI->isLinkB() || pMFI->isLinkC(),
                                 pMFI->dirA()  || pMFI->dirB()  || pMFI->dirC() );
         }
Joachim Eibl's avatar
Joachim Eibl committed
559

560
561
562
563
564
565
566
567
568
569
570
571
572
573
         if ( s_ACol == index.column() )
         {
            return getOnePixmap( pMFI->m_ageA, pMFI->isLinkA(), pMFI->dirA() );
         }
         if ( s_BCol == index.column() )
         {
            return getOnePixmap( pMFI->m_ageB, pMFI->isLinkB(), pMFI->dirB() );
         }
         if ( s_CCol == index.column() )
         {
            return getOnePixmap( pMFI->m_ageC, pMFI->isLinkC(), pMFI->dirC() );
         }
      }
      else if ( role == Qt::TextAlignmentRole )
Joachim Eibl's avatar
Joachim Eibl committed
574
      {
575
576
577
         if ( s_UnsolvedCol == index.column() || s_SolvedCol == index.column() 
            || s_NonWhiteCol == index.column() || s_WhiteCol == index.column() )
            return Qt::AlignRight;
Joachim Eibl's avatar
Joachim Eibl committed
578
      }
579
580
581
   }
   return QVariant();
}
Joachim Eibl's avatar
Joachim Eibl committed
582

583
584
585
586
587
QVariant DirectoryMergeWindow::Data::headerData ( int section, Qt::Orientation orientation, int role ) const
{
   if ( orientation == Qt::Horizontal && section>=0 && section<columnCount(QModelIndex()) && role==Qt::DisplayRole )
   {
      switch ( section )
Joachim Eibl's avatar
Joachim Eibl committed
588
      {
589
590
591
592
593
594
595
596
597
598
599
      case s_NameCol:     return i18n("Name");
      case s_ACol:        return "A";
      case s_BCol:        return "B";
      case s_CCol:        return "C";
      case s_OpCol:       return i18n("Operation");
      case s_OpStatusCol: return i18n("Status");
      case s_UnsolvedCol: return i18n("Unsolved");
      case s_SolvedCol:   return i18n("Solved");
      case s_NonWhiteCol: return i18n("Nonwhite");
      case s_WhiteCol:    return i18n("White");
      default :           return QVariant();
Joachim Eibl's avatar
Joachim Eibl committed
600
601
      }
   }
602
   return QVariant();
Joachim Eibl's avatar
Joachim Eibl committed
603
604
}

Joachim Eibl's avatar
Joachim Eibl committed
605
606
607
608
// Previously  Q3ListViewItem::paintCell(p,cg,column,width,align);
class DirectoryMergeWindow::DirMergeItemDelegate : public QItemDelegate
{
   DirectoryMergeWindow* m_pDMW;
609
   DirectoryMergeWindow::Data* d;
Joachim Eibl's avatar
Joachim Eibl committed
610
611
public:
   DirMergeItemDelegate(DirectoryMergeWindow* pParent) 
612
      : QItemDelegate(pParent), m_pDMW(pParent), d(pParent->d)
Joachim Eibl's avatar
Joachim Eibl committed
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
   {
   }
   void paint( QPainter * p, const QStyleOptionViewItem & option, const QModelIndex & index ) const 
   {
      int column = index.column();
      if (column == s_ACol || column == s_BCol || column == s_CCol )
      {
         QVariant value = index.data( Qt::DecorationRole );
         QPixmap icon;
         if ( value.isValid() ) 
         {
            if (value.type() == QVariant::Icon) 
            {
               icon = qvariant_cast<QIcon>(value).pixmap(16,16);
               //icon = qvariant_cast<QIcon>(value);
               //decorationRect = QRect(QPoint(0, 0), icon.actualSize(option.decorationSize, iconMode, iconState));
            } 
            else 
            {
               icon = qvariant_cast<QPixmap>(value);
               //decorationRect = QRect(QPoint(0, 0), option.decorationSize).intersected(pixmap.rect());
            }
         }

         int x = option.rect.left();
         int y = option.rect.top();
         //QPixmap icon = value.value<QPixmap>(); //pixmap(column);
         if ( !icon.isNull() )
         {
            int yOffset = (sizeHint(option,index).height() - icon.height()) / 2;
            p->drawPixmap( x+2, y+yOffset, icon );

645
646
647
648
            int i = index==d->m_selection1Index ? 1 :
                    index==d->m_selection2Index ? 2 :
                    index==d->m_selection3Index ? 3 :
                                                   0 ;
Joachim Eibl's avatar
Joachim Eibl committed
649
650
            if ( i!=0 )
            {
651
652
               Options* pOpts = d->m_pOptions;
               QColor c ( i==1 ? pOpts->m_colorA : i==2 ? pOpts->m_colorB : pOpts->m_colorC );
Joachim Eibl's avatar
Joachim Eibl committed
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
               p->setPen( c );// highlight() );
               p->drawRect( x+2, y+yOffset, icon.width(), icon.height());
               p->setPen( QPen( c, 0, Qt::DotLine) );
               p->drawRect( x+1, y+yOffset-1, icon.width()+2, icon.height()+2);
               p->setPen( Qt::white );
               QString s( QChar('A'+i-1) );
               p->drawText( x+2 + (icon.width() - p->fontMetrics().width(s))/2,
                           y+yOffset + (icon.height() + p->fontMetrics().ascent())/2-1,
                           s );
            }
            else
            {
               p->setPen( m_pDMW->palette().background().color() );
               p->drawRect( x+1, y+yOffset-1, icon.width()+2, icon.height()+2);
            }
            return;
         }
      }
      QStyleOptionViewItem option2 = option;
      if ( column>=s_UnsolvedCol )
      {
         option2.displayAlignment = Qt::AlignRight;
      }
      QItemDelegate::paint( p, option2, index );
   }
678
679
680
681
682
   QSize sizeHint( const QStyleOptionViewItem & option, const QModelIndex & index ) const 
   {
      QSize sz = QItemDelegate::sizeHint( option, index );
      return sz.expandedTo( QSize(0,18) );
   }
Joachim Eibl's avatar
Joachim Eibl committed
683
684
685
};


686
DirectoryMergeWindow::DirectoryMergeWindow( QWidget* pParent, Options* pOptions, KIconLoader* pIconLoader )
687
   : QTreeView( pParent )
Joachim Eibl's avatar
Joachim Eibl committed
688
{
689
690
   d = new Data(this);
   setModel( d );
Joachim Eibl's avatar
Joachim Eibl committed
691
   setItemDelegate( new DirMergeItemDelegate(this) );
692
693
   connect(this, &DirectoryMergeWindow::doubleClicked, this, &DirectoryMergeWindow::onDoubleClick);
   connect(this, &DirectoryMergeWindow::expanded, this, &DirectoryMergeWindow::onExpanded);
Joachim Eibl's avatar
Joachim Eibl committed
694

695
696
   d->m_pOptions = pOptions;
   d->m_pIconLoader = pIconLoader;
Joachim Eibl's avatar
Joachim Eibl committed
697

698
   setSortingEnabled(true);
Joachim Eibl's avatar
Joachim Eibl committed
699
700
701
702
}

DirectoryMergeWindow::~DirectoryMergeWindow()
{
703
   delete d;
Joachim Eibl's avatar
Joachim Eibl committed
704
705
}

706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
QString DirectoryMergeWindow::getDirNameA(){ return d->m_dirA.prettyAbsPath(); }
QString DirectoryMergeWindow::getDirNameB(){ return d->m_dirB.prettyAbsPath(); }
QString DirectoryMergeWindow::getDirNameC(){ return d->m_dirC.prettyAbsPath(); }
QString DirectoryMergeWindow::getDirNameDest(){ return d->m_dirDest.prettyAbsPath(); }
void DirectoryMergeWindow::setDirectoryMergeInfo(DirectoryMergeInfo* p){ d->m_pDirectoryMergeInfo=p; }
bool DirectoryMergeWindow::isDirectoryMergeInProgress() { return d->m_bRealMergeStarted; }
bool DirectoryMergeWindow::isSyncMode() { return d->m_bSyncMode; }
bool DirectoryMergeWindow::isScanning() { return d->m_bScanning; }

bool DirectoryMergeWindow::Data::fastFileComparison(
   FileAccess& fi1, FileAccess& fi2,
   bool& bError, QString& status )
{
   ProgressProxy pp;
   status = "";
   bool bEqual = false;
   bError = true;

   if ( !m_bFollowFileLinks )
   {
      if ( fi1.isSymLink() != fi2.isSymLink() )
      {
         status = i18n("Mix of links and normal files.");
         return bEqual;
      }
      else if ( fi1.isSymLink() && fi2.isSymLink() )
      {
         bError = false;
         bEqual = fi1.readLink() == fi2.readLink();
         status = i18n("Link: ");
         return bEqual;
      }
   }

   if ( fi1.size()!=fi2.size() )
   {
      bEqual = false;
      status = i18n("Size. ");
      return bEqual;
   }
   else if ( m_pOptions->m_bDmTrustSize )
   {
      bEqual = true;
      return bEqual;
   }

   if ( m_pOptions->m_bDmTrustDate )
   {
      bEqual = ( fi1.lastModified() == fi2.lastModified()  &&  fi1.size()==fi2.size() );
      bError = false;
      status = i18n("Date & Size: ");
      return bEqual;
   }

   if ( m_pOptions->m_bDmTrustDateFallbackToBinary )
   {
      bEqual = ( fi1.lastModified() == fi2.lastModified()  &&  fi1.size()==fi2.size() );
      if ( bEqual )
      {
         bError = false;
         status = i18n("Date & Size: ");
         return bEqual;
      }
   }

   QString fileName1 = fi1.absoluteFilePath();
   QString fileName2 = fi2.absoluteFilePath();
   TempRemover tr1( fileName1, fi1 );
   if ( !tr1.success() )
   {
      status = i18n("Creating temp copy of %1 failed.",fileName1);
      return bEqual;
   }
   TempRemover tr2( fileName2, fi2 );
   if ( !tr2.success() )
   {
      status = i18n("Creating temp copy of %1 failed.",fileName2);
      return bEqual;
   }

   std::vector<char> buf1(100000);
   std::vector<char> buf2(buf1.size());

   QFile file1( tr1.name() );

   if ( ! file1.open(QIODevice::ReadOnly) )
   {
      status = i18n("Opening %1 failed.",fileName1);
      return bEqual;
   }

   QFile file2( tr2.name() );

   if ( ! file2.open(QIODevice::ReadOnly) )
   {
      status = i18n("Opening %1 failed.",fileName2);
      return bEqual;
   }

   pp.setInformation( i18n("Comparing file..."), 0, false );
   typedef qint64 t_FileSize;
   t_FileSize fullSize = file1.size();
   t_FileSize sizeLeft = fullSize;

   while( sizeLeft>0 && ! pp.wasCancelled() )
   {
      int len = min2( sizeLeft, (t_FileSize)buf1.size() );
      if( len != file1.read( &buf1[0], len ) )
      {
         status = i18n("Error reading from %1",fileName1);
         return bEqual;
      }

      if( len != file2.read( &buf2[0], len ) )
      {
         status = i18n("Error reading from %1",fileName2);
         return bEqual;
      }

      if ( memcmp( &buf1[0], &buf2[0], len ) != 0 )
      {
         bError = false;
         return bEqual;
      }
      sizeLeft-=len;
      pp.setCurrent(double(fullSize-sizeLeft)/fullSize, false );
   }

   // If the program really arrives here, then the files are really equal.
   bError = false;
   bEqual = true;
   return bEqual;
}
Joachim Eibl's avatar
Joachim Eibl committed
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855

int DirectoryMergeWindow::totalColumnWidth()
{
   int w=0;
   for (int i=0; i<s_OpStatusCol; ++i)
   {
      w += columnWidth(i);
   }
   return w;
}

void DirectoryMergeWindow::reload()
{
   if ( isDirectoryMergeInProgress() )
   {
      int result = KMessageBox::warningYesNo(this,
         i18n("You are currently doing a directory merge. Are you sure, you want to abort the merge and rescan the directory?"),
Joachim Eibl's avatar
0.9.93    
Joachim Eibl committed
856
857
858
         i18n("Warning"), 
         KGuiItem( i18n("Rescan") ), 
         KGuiItem( i18n("Continue Merging") ) );
Joachim Eibl's avatar
Joachim Eibl committed
859
860
861
      if ( result!=KMessageBox::Yes )
         return;
   }
Joachim Eibl's avatar
Joachim Eibl committed
862

863
   init( d->m_dirA, d->m_dirB, d->m_dirC, d->m_dirDest, d->m_bDirectoryMerge, true );
Michael Reeves's avatar
Michael Reeves committed
864
865
   //fix file visibilities after reload or menu will be out of sync with display if changed from defaults.
   updateFileVisibilities();
Joachim Eibl's avatar
Joachim Eibl committed
866
867
868
}

// Copy pm2 onto pm1, but preserve the alpha value from pm1 where pm2 is transparent.
Joachim Eibl's avatar
Joachim Eibl committed
869
static QPixmap pixCombiner( const QPixmap* pm1, const QPixmap* pm2 )
Joachim Eibl's avatar
Joachim Eibl committed
870
{
Joachim Eibl's avatar
Joachim Eibl committed
871
872
   QImage img1 = pm1->toImage().convertToFormat(QImage::Format_ARGB32);
   QImage img2 = pm2->toImage().convertToFormat(QImage::Format_ARGB32);
Joachim Eibl's avatar
Joachim Eibl committed
873
874
875

   for (int y = 0; y < img1.height(); y++)
   {
Joachim Eibl's avatar
Joachim Eibl committed
876
877
      quint32 *line1 = reinterpret_cast<quint32 *>(img1.scanLine(y));
      quint32 *line2 = reinterpret_cast<quint32 *>(img2.scanLine(y));
Joachim Eibl's avatar
Joachim Eibl committed
878
879
880
881
882
883
      for (int x = 0; x < img1.width();  x++)
      {
         if ( qAlpha( line2[x] ) >0 )
            line1[x] = (line2[x] | 0xff000000);
      }
   }
Joachim Eibl's avatar
Joachim Eibl committed
884
   return QPixmap::fromImage(img1);
Joachim Eibl's avatar
Joachim Eibl committed
885
886
887
}

// like pixCombiner but let the pm1 color shine through
Joachim Eibl's avatar
Joachim Eibl committed
888
static QPixmap pixCombiner2( const QPixmap* pm1, const QPixmap* pm2 )
Joachim Eibl's avatar
Joachim Eibl committed
889
{
Joachim Eibl's avatar
Joachim Eibl committed
890
891
892
893
894
   QPixmap pix=*pm1;
   QPainter p(&pix);
   p.setOpacity(0.5);
   p.drawPixmap( 0,0,*pm2 );
   p.end();
Joachim Eibl's avatar
Joachim Eibl committed
895
896
897
898

   return pix;
}

899
900
void DirectoryMergeWindow::Data::calcDirStatus( bool bThreeDirs, const QModelIndex& mi, 
   int& nofFiles, int& nofDirs, int& nofEqualFiles, int& nofManualMerges )
Joachim Eibl's avatar
Joachim Eibl committed
901
{
902
903
   MergeFileInfos* pMFI = getMFI(mi);
   if ( pMFI->dirA() || pMFI->dirB() || pMFI->dirC() )
Joachim Eibl's avatar
Joachim Eibl committed
904
905
906
907
908
909
   {
      ++nofDirs;
   }
   else
   {
      ++nofFiles;
910
      if ( pMFI->m_bEqualAB && (!bThreeDirs || pMFI->m_bEqualAC ))
Joachim Eibl's avatar
Joachim Eibl committed
911
912
913
914
915
      {
         ++nofEqualFiles;
      }
      else
      {
916
         if ( pMFI->m_eMergeOperation==eMergeABCToDest || pMFI->m_eMergeOperation==eMergeABToDest )
Joachim Eibl's avatar
Joachim Eibl committed
917
918
919
            ++nofManualMerges;
      }
   }
920
921
   for( int childIdx=0; childIdx<rowCount(mi); ++childIdx )
      calcDirStatus( bThreeDirs, index(childIdx,0,mi), nofFiles, nofDirs, nofEqualFiles, nofManualMerges );
Joachim Eibl's avatar
Joachim Eibl committed
922
923
}

Joachim Eibl's avatar
Joachim Eibl committed
924
925
926
927
928
929
930
931
struct t_ItemInfo 
{
   bool bExpanded; 
   bool bOperationComplete; 
   QString status; 
   e_MergeOperation eMergeOperation; 
};

Joachim Eibl's avatar
Joachim Eibl committed
932
933
934
935
936
937
bool DirectoryMergeWindow::init
   (
   FileAccess& dirA,
   FileAccess& dirB,
   FileAccess& dirC,
   FileAccess& dirDest,
Joachim Eibl's avatar
Joachim Eibl committed
938
939
   bool bDirectoryMerge,
   bool bReload
Joachim Eibl's avatar
Joachim Eibl committed
940
   )
941
942
943
944
945
946
947
948
949
950
951
952
953
{
   return d->init( dirA, dirB, dirC, dirDest, bDirectoryMerge, bReload );
}

bool DirectoryMergeWindow::Data::init
   (
   FileAccess& dirA,
   FileAccess& dirB,
   FileAccess& dirC,
   FileAccess& dirDest,
   bool bDirectoryMerge,
   bool bReload
   )
Joachim Eibl's avatar
Joachim Eibl committed
954
{
Joachim Eibl's avatar
0.9.86    
Joachim Eibl committed
955
956
   if ( m_pOptions->m_bDmFullAnalysis )
   {
Joachim Eibl's avatar
Joachim Eibl committed
957
      // A full analysis uses the same ressources that a normal text-diff/merge uses.
Joachim Eibl's avatar
0.9.86    
Joachim Eibl committed
958
959
      // So make sure that the user saves his data first.
      bool bCanContinue=false;
960
      emit q->checkIfCanContinue( &bCanContinue );
Joachim Eibl's avatar
0.9.86    
Joachim Eibl committed
961
962
      if ( !bCanContinue )
         return false;
963
      emit q->startDiffMerge("","","","","","","",0);  // hide main window
Joachim Eibl's avatar
0.9.86    
Joachim Eibl committed
964
   }
Joachim Eibl's avatar
Joachim Eibl committed
965

966
967
   q->show();
   q->setUpdatesEnabled(true);
Joachim Eibl's avatar
0.9.86    
Joachim Eibl committed
968

Joachim Eibl's avatar
Joachim Eibl committed
969
970
971
972
   std::map<QString,t_ItemInfo> expandedDirsMap;

   if ( bReload )
   {
973
974
975
976
977
978
979
980
981
982
983
984
      // Remember expanded items TODO
      //QTreeWidgetItemIterator it( this );
      //while ( *it )
      //{
      //   DirMergeItem* pDMI = static_cast<DirMergeItem*>( *it );
      //   t_ItemInfo& ii = expandedDirsMap[ pDMI->m_pMFI->subPath() ];
      //   ii.bExpanded = pDMI->isExpanded();
      //   ii.bOperationComplete = pDMI->m_pMFI->m_bOperationComplete;
      //   ii.status = pDMI->text( s_OpStatusCol );
      //   ii.eMergeOperation = pDMI->m_pMFI->m_eMergeOperation;
      //   ++it;
      //}
Joachim Eibl's avatar
Joachim Eibl committed
985
986
   }

Joachim Eibl's avatar
0.9.86    
Joachim Eibl committed
987
   ProgressProxy pp;
Joachim Eibl's avatar
Joachim Eibl committed
988
989
990
991
992
993
   m_bFollowDirLinks = m_pOptions->m_bDmFollowDirLinks;
   m_bFollowFileLinks = m_pOptions->m_bDmFollowFileLinks;
   m_bSimulatedMergeStarted=false;
   m_bRealMergeStarted=false;
   m_bError=false;
   m_bDirectoryMerge = bDirectoryMerge;
994
995
996
   m_selection1Index = QModelIndex();
   m_selection2Index = QModelIndex();
   m_selection3Index = QModelIndex();
Joachim Eibl's avatar
Joachim Eibl committed
997
   m_bCaseSensitive = m_pOptions->m_bDmCaseSensitiveFilenameComparison;
Joachim Eibl's avatar
Joachim Eibl committed
998
999
   m_bUnfoldSubdirs = m_pOptions->m_bDmUnfoldSubdirs;
   m_bSkipDirStatus = m_pOptions->m_bDmSkipDirStatus;
Joachim Eibl's avatar
Joachim Eibl committed
1000

Michael Reeves's avatar
Michael Reeves committed
1001
1002
   
   beginResetModel();
1003
   m_pRoot->m_children.clear();
Joachim Eibl's avatar
Joachim Eibl committed
1004
   m_mergeItemList.clear();
Michael Reeves's avatar
Michael Reeves committed
1005
   endResetModel();
1006
1007
   
   m_currentIndexForOperation = m_mergeItemList.end();
Joachim Eibl's avatar
Joachim Eibl committed
1008
1009
1010
1011
1012
1013

   m_dirA = dirA;
   m_dirB = dirB;
   m_dirC = dirC;
   m_dirDest = dirDest;

Joachim Eibl's avatar
Joachim Eibl committed
1014
1015
1016
1017
1018
1019
1020
1021
   if ( !bReload )
   {
      m_pDirShowIdenticalFiles->setChecked(true);
      m_pDirShowDifferentFiles->setChecked(true);
      m_pDirShowFilesOnlyInA->setChecked(true);
      m_pDirShowFilesOnlyInB->setChecked(true);
      m_pDirShowFilesOnlyInC->setChecked(true);
   }
Joachim Eibl's avatar
Joachim Eibl committed
1022

Joachim Eibl's avatar
Joachim Eibl committed
1023
1024
1025
1026
1027
1028
1029
1030
   // Check if all input directories exist and are valid. The dest dir is not tested now.
   // The test will happen only when we are going to write to it.
   if ( !m_dirA.isDir() || !m_dirB.isDir() ||
        (m_dirC.isValid() && !m_dirC.isDir()) )
   {
       QString text( i18n("Opening of directories failed:") );
       text += "\n\n";
       if ( !dirA.isDir() )
Joachim Eibl's avatar
0.9.93    
Joachim Eibl committed
1031
       {  text += i18n("Dir A \"%1\" does not exist or is not a directory.\n",m_dirA.prettyAbsPath()); }
Joachim Eibl's avatar
Joachim Eibl committed
1032
1033

       if ( !dirB.isDir() )
Joachim Eibl's avatar
0.9.93    
Joachim Eibl committed
1034
       {  text += i18n("Dir B \"%1\" does not exist or is not a directory.\n",m_dirB.prettyAbsPath()); }
Joachim Eibl's avatar
Joachim Eibl committed
1035
1036

       if ( m_dirC.isValid() && !m_dirC.isDir() )
Joachim Eibl's avatar
0.9.93    
Joachim Eibl committed
1037
       {  text += i18n("Dir C \"%1\" does not exist or is not a directory.\n",m_dirC.prettyAbsPath()); }
Joachim Eibl's avatar
Joachim Eibl committed
1038

1039
       KMessageBox::sorry( q, text, i18n("Directory Open Error") );
Joachim Eibl's avatar
Joachim Eibl committed
1040
1041
1042
1043
1044
1045
       return false;
   }

   if ( m_dirC.isValid() &&
        (m_dirDest.prettyAbsPath() == m_dirA.prettyAbsPath()  ||  m_dirDest.prettyAbsPath()==m_dirB.prettyAbsPath() ) )
   {
1046
      KMessageBox::error(q,
Joachim Eibl's avatar
Joachim Eibl committed
1047
         i18n( "The destination directory must not be the same as A or B when "
Joachim Eibl's avatar
Joachim Eibl committed
1048
         "three directories are merged.\nCheck again before continuing."),
Joachim Eibl's avatar
0.9.80    
Joachim Eibl committed
1049
         i18n("Parameter Warning"));
Joachim Eibl's avatar
Joachim Eibl committed
1050
1051
      return false;
   }
Joachim Eibl's avatar
Joachim Eibl committed
1052

Joachim Eibl's avatar
0.9.86    
Joachim Eibl committed
1053
   m_bScanning = true;
1054
   emit q->statusBarMessage(i18n("Scanning directories..."));
Joachim Eibl's avatar
Joachim Eibl committed
1055
1056
1057
1058
1059
1060
1061
1062

   m_bSyncMode = m_pOptions->m_bDmSyncMode && !m_dirC.isValid() && !m_dirDest.isValid();

   if ( m_dirDest.isValid() )
      m_dirDestInternal = m_dirDest;
   else
      m_dirDestInternal = m_dirC.isValid() ? m_dirC : m_dirB;

Joachim Eibl's avatar
Joachim Eibl committed
1063
   QString origCurrentDirectory = QDir::currentPath();
Joachim Eibl's avatar
Joachim Eibl committed
1064
1065

   m_fileMergeMap.clear();
1066
   s_eCaseSensitivity = m_bCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive;
Joachim Eibl's avatar
Joachim Eibl committed
1067
1068
1069
1070
1071
1072
   t_DirectoryList::iterator i;

   // calc how many directories will be read:
   double nofScans = ( m_dirA.isValid() ? 1 : 0 )+( m_dirB.isValid() ? 1 : 0 )+( m_dirC.isValid() ? 1 : 0 );
   int currentScan = 0;

Joachim Eibl's avatar
Joachim Eibl committed
1073
1074
1075
1076
//TODO   setColumnWidthMode(s_UnsolvedCol, Q3ListView::Manual);
//   setColumnWidthMode(s_SolvedCol,   Q3ListView::Manual);
//   setColumnWidthMode(s_WhiteCol,    Q3ListView::Manual);
//   setColumnWidthMode(s_NonWhiteCol, Q3ListView::Manual);
1077
1078
1079
1080
1081
   q->setColumnHidden( s_CCol,    !m_dirC.isValid() );
   q->setColumnHidden( s_WhiteCol,    !m_pOptions->m_bDmFullAnalysis );
   q->setColumnHidden( s_NonWhiteCol, !m_pOptions->m_bDmFullAnalysis );
   q->setColumnHidden( s_UnsolvedCol, !m_pOptions->m_bDmFullAnalysis );
   q->setColumnHidden( s_SolvedCol,   !( m_pOptions->m_bDmFullAnalysis && m_dirC.isValid() ) );
Joachim Eibl's avatar
Joachim Eibl committed
1082

Joachim Eibl's avatar
Joachim Eibl committed
1083
1084
1085
   bool bListDirSuccessA = true;
   bool bListDirSuccessB = true;
   bool bListDirSuccessC = true;
1086
1087
1088
   m_dirListA.clear();
   m_dirListB.clear();
   m_dirListC.clear();
Joachim Eibl's avatar
Joachim Eibl committed
1089
1090
   if ( m_dirA.isValid() )
   {
Joachim Eibl's avatar
0.9.86    
Joachim Eibl committed
1091
1092
      pp.setInformation(i18n("Reading Directory A"));
      pp.setSubRangeTransformation(currentScan/nofScans, (currentScan+1)/nofScans);
Joachim Eibl's avatar
Joachim Eibl committed
1093
1094
      ++currentScan;

1095
      bListDirSuccessA = m_dirA.listDir( &m_dirListA,
Joachim Eibl's avatar
Joachim Eibl committed
1096
1097
1098
1099
1100
         m_pOptions->m_bDmRecursiveDirs, m_pOptions->m_bDmFindHidden,
         m_pOptions->m_DmFilePattern, m_pOptions->m_DmFileAntiPattern,
         m_pOptions->m_DmDirAntiPattern, m_pOptions->m_bDmFollowDirLinks,
         m_pOptions->m_bDmUseCvsIgnore);

1101
      for (i=m_dirListA.begin(); i!=m_dirListA.end();++i )
Joachim Eibl's avatar
Joachim Eibl committed
1102
      {
1103
         MergeFileInfos& mfi = m_fileMergeMap[FileKey(*i)];
Joachim Eibl's avatar
Joachim Eibl committed
1104
         //std::cout <<i->filePath()<<std::endl;
1105
         mfi.m_pFileInfoA = &(*i);
Joachim Eibl's avatar
Joachim Eibl committed
1106
1107
1108
1109
1110
      }
   }

   if ( m_dirB.isValid() )
   {
Joachim Eibl's avatar
0.9.86    
Joachim Eibl committed
1111
1112
      pp.setInformation(i18n("Reading Directory B"));
      pp.setSubRangeTransformation(currentScan/nofScans, (currentScan+1)/nofScans);
Joachim Eibl's avatar
Joachim Eibl committed
1113
1114
      ++currentScan;

1115
      bListDirSuccessB =  m_dirB.listDir( &m_dirListB,
Joachim Eibl's avatar
Joachim Eibl committed
1116
1117
1118
1119
1120
         m_pOptions->m_bDmRecursiveDirs, m_pOptions->m_bDmFindHidden,
         m_pOptions->m_DmFilePattern, m_pOptions->m_DmFileAntiPattern,
         m_pOptions->m_DmDirAntiPattern, m_pOptions->m_bDmFollowDirLinks,
         m_pOptions->m_bDmUseCvsIgnore);

1121
      for (i=m_dirListB.begin(); i!=m_dirListB.end();++i )
Joachim Eibl's avatar
Joachim Eibl committed
1122
      {
1123
1124
1125
1126
         MergeFileInfos& mfi = m_fileMergeMap[FileKey(*i)];
         mfi.m_pFileInfoB = &(*i);
         if ( mfi.m_pFileInfoA && mfi.m_pFileInfoA->fileName() == mfi.m_pFileInfoB->fileName() )
            mfi.m_pFileInfoB->setSharedName(mfi.m_pFileInfoA->fileName()); // Reduce memory by sharing the name.
Joachim Eibl's avatar
Joachim Eibl committed
1127
1128
1129
1130
1131
1132
      }
   }

   e_MergeOperation eDefaultMergeOp;
   if ( m_dirC.isValid() )
   {
Joachim Eibl's avatar
0.9.86    
Joachim Eibl committed
1133
1134
      pp.setInformation(i18n("Reading Directory C"));
      pp.setSubRangeTransformation(currentScan/nofScans, (currentScan+1)/nofScans);
Joachim Eibl's avatar
Joachim Eibl committed
1135
1136
      ++currentScan;

1137
      bListDirSuccessC = m_dirC.listDir( &m_dirListC,
Joachim Eibl's avatar
Joachim Eibl committed
1138
1139
1140
1141
1142
         m_pOptions->m_bDmRecursiveDirs, m_pOptions->m_bDmFindHidden,
         m_pOptions->m_DmFilePattern, m_pOptions->m_DmFileAntiPattern,
         m_pOptions->m_DmDirAntiPattern, m_pOptions->m_bDmFollowDirLinks,
         m_pOptions->m_bDmUseCvsIgnore);

1143
      for (i=m_dirListC.begin(); i!=m_dirListC.end();++i )
Joachim Eibl's avatar
Joachim Eibl committed
1144
      {
1145
1146
         MergeFileInfos& mfi = m_fileMergeMap[FileKey(*i)];
         mfi.m_pFileInfoC = &(*i);
Joachim Eibl's avatar
Joachim Eibl committed
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
      }

      eDefaultMergeOp = eMergeABCToDest;
   }
   else
      eDefaultMergeOp = m_bSyncMode ? eMergeToAB : eMergeABToDest;

   bool bContinue = true;
   if ( !bListDirSuccessA || !bListDirSuccessB || !bListDirSuccessC )
   {
      QString s = i18n("Some subdirectories were not readable in");
      if ( !bListDirSuccessA )   s += "\nA: " + m_dirA.prettyAbsPath();
      if ( !bListDirSuccessB )   s += "\nB: " + m_dirB.prettyAbsPath();
      if ( !bListDirSuccessC )   s += "\nC: " + m_dirC.prettyAbsPath();
      s+="\n";
      s+= i18n("Check the permissions of the subdirectories.");
1163
      bContinue = KMessageBox::Continue == KMessageBox::warningContinueCancel( q, s );
Joachim Eibl's avatar
Joachim Eibl committed
1164
1165
1166
1167
   }

   if ( bContinue )
   {
Joachim Eibl's avatar
0.9.86    
Joachim Eibl committed
1168
      prepareListView(pp);
Joachim Eibl's avatar
Joachim Eibl committed
1169

1170
      q->updateFileVisibilities();
Joachim Eibl's avatar
Joachim Eibl committed
1171

1172
      for( int childIdx = 0; childIdx<rowCount(); ++childIdx )
Joachim Eibl's avatar
Joachim Eibl committed
1173
      {
1174
1175
         QModelIndex mi = index( childIdx, 0, QModelIndex() );
         calcSuggestedOperation( mi, eDefaultMergeOp );
Joachim Eibl's avatar
Joachim Eibl committed
1176
1177
      }
   }
Joachim Eibl's avatar
Joachim Eibl committed
1178

1179
   q->sortByColumn(0,Qt::AscendingOrder);
Joachim Eibl's avatar
Joachim Eibl committed
1180

1181
1182
   for (int i=0;i<columnCount(QModelIndex());++i)
      q->resizeColumnToContents(i);
Joachim Eibl's avatar
Joachim Eibl committed
1183
1184

   // Try to improve the view a little bit.
1185
   QWidget* pParent = q->parentWidget();
Joachim Eibl's avatar
Joachim Eibl committed
1186
1187
1188
   QSplitter* pSplitter = static_cast<QSplitter*>(pParent);
   if (pSplitter!=0)
   {
Joachim Eibl's avatar
Joachim Eibl committed
1189
      QList<int> sizes = pSplitter->sizes();
Joachim Eibl's avatar
Joachim Eibl committed
1190
      int total = sizes[0] + sizes[1];
Joachim Eibl's avatar
Joachim Eibl committed
1191
1192
      if ( total < 10 )
         total = 100;
Joachim Eibl's avatar
Joachim Eibl committed
1193
1194
1195
1196
      sizes[0]=total*6/10;
      sizes[1]=total - sizes[0];
      pSplitter->setSizes( sizes );
   }
Joachim Eibl's avatar
Joachim Eibl committed
1197

Joachim Eibl's avatar
Joachim Eibl committed
1198
   QDir::setCurrent(origCurrentDirectory);
Joachim Eibl's avatar
Joachim Eibl committed
1199

Joachim Eibl's avatar
0.9.86    
Joachim Eibl committed
1200
   m_bScanning = false;
1201
   emit q->statusBarMessage(i18n("Ready."));
Joachim Eibl's avatar
Joachim Eibl committed
1202

Joachim Eibl's avatar
Joachim Eibl committed
1203
   if ( bContinue && !m_bSkipDirStatus )
Joachim Eibl's avatar
Joachim Eibl committed
1204
1205
1206
1207
1208
1209
   {
      // Generate a status report
      int nofFiles=0;
      int nofDirs=0;
      int nofEqualFiles=0;
      int nofManualMerges=0;
1210
1211
1212
//TODO
      for( int childIdx = 0; childIdx<rowCount(); ++childIdx )
         calcDirStatus( m_dirC.isValid(), index(childIdx,0,QModelIndex()),
Joachim Eibl's avatar
Joachim Eibl committed
1213
1214
1215
                        nofFiles, nofDirs, nofEqualFiles, nofManualMerges );

      QString s;
Joachim Eibl's avatar
0.9.80    
Joachim Eibl committed
1216
      s = i18n("Directory Comparison Status") + "\n\n" +
Joachim Eibl's avatar
Joachim Eibl committed
1217
1218
1219
1220
1221
1222
          i18n("Number of subdirectories:") +" "+ QString::number(nofDirs)       + "\n"+
          i18n("Number of equal files:")     +" "+ QString::number(nofEqualFiles) + "\n"+
          i18n("Number of different files:") +" "+ QString::number(nofFiles-nofEqualFiles);

      if ( m_dirC.isValid() )
         s += "\n" + i18n("Number of manual merges:")   +" "+ QString::number(nofManualMerges);
1223
1224
1225
1226
1227
1228
1229
1230
      KMessageBox::information( q, s );
      //
      //TODO
      //if ( topLevelItemCount()>0 )
      //{
      //   topLevelItem(0)->setSelected(true);
      //   setCurrentItem( topLevelItem(0) );
      //}
Joachim Eibl's avatar
Joachim Eibl committed
1231
   }
Joachim Eibl's avatar
Joachim Eibl committed
1232

Joachim Eibl's avatar
Joachim Eibl committed
1233
1234
   if ( bReload )
   {
Joachim Eibl's avatar
Joachim Eibl committed
1235
      // Remember expanded items
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
      //TODO
      //QTreeWidgetItemIterator it( this );
      //while ( *it )
      //{
      //   DirMergeItem* pDMI = static_cast<DirMergeItem*>( *it );
      //   std::map<QString,t_ItemInfo>::iterator i = expandedDirsMap.find( pDMI->m_pMFI->subPath() );
      //   if ( i!=expandedDirsMap.end() )
      //   {
      //      t_ItemInfo& ii = i->second;
      //      pDMI->setExpanded( ii.bExpanded );
      //      //pDMI->m_pMFI->setMergeOperation( ii.eMergeOperation, false ); unsafe, might have changed
      //      pDMI->m_pMFI->m_bOperationComplete = ii.bOperationComplete;
      //      pDMI->setText( s_OpStatusCol, ii.status );
      //   }
      //   ++it;
      //}
Joachim Eibl's avatar
Joachim Eibl committed
1252
   }
Joachim Eibl's avatar
Joachim Eibl committed
1253
1254
1255
1256
1257
   else if (m_bUnfoldSubdirs)
   {
      m_pDirUnfoldAll->trigger();
   }

Joachim Eibl's avatar
Joachim Eibl committed
1258
1259
1260
   return true;
}

Joachim Eibl's avatar
Joachim Eibl committed
1261
1262
1263
1264
void DirectoryMergeWindow::onExpanded()
{
   resizeColumnToContents(s_NameCol);
}
Joachim Eibl's avatar
Joachim Eibl committed
1265
1266


1267
void DirectoryMergeWindow::slotChooseAEverywhere(){ d->setAllMergeOperations( eCopyAToDest ); }
Joachim Eibl's avatar
Joachim Eibl committed
1268

1269
void DirectoryMergeWindow::slotChooseBEverywhere(){ d->setAllMergeOperations( eCopyBToDest ); }
Joachim Eibl's avatar
Joachim Eibl committed
1270

1271
void DirectoryMergeWindow::slotChooseCEverywhere(){ d->setAllMergeOperations( eCopyCToDest ); }
Joachim Eibl's avatar
Joachim Eibl committed
1272
1273
1274

void DirectoryMergeWindow::slotAutoChooseEverywhere()
{
1275
1276
1277
   e_MergeOperation eDefaultMergeOp = d->m_dirC.isValid() ?  eMergeABCToDest :
                                           d->m_bSyncMode ?  eMergeToAB      : eMergeABToDest;
   d->setAllMergeOperations(eDefaultMergeOp );
Joachim Eibl's avatar
Joachim Eibl committed
1278
1279
}

1280
void DirectoryMergeWindow::slotNoOpEverywhere(){ d->setAllMergeOperations(eNoOperation); }
Joachim Eibl's avatar
Joachim Eibl committed
1281
1282
1283

void DirectoryMergeWindow::slotFoldAllSubdirs()
{
1284
   collapseAll();
Joachim Eibl's avatar
Joachim Eibl committed
1285
1286
1287
1288
}

void DirectoryMergeWindow::slotUnfoldAllSubdirs()
{
1289
   expandAll();
Joachim Eibl's avatar
0.9.80    
Joachim Eibl committed
1290
1291
1292
}

// Merge current item (merge mode)
1293
1294
1295
1296
void DirectoryMergeWindow::slotCurrentDoNothing() { d->setMergeOperation(currentIndex(), eNoOperation ); }
void DirectoryMergeWindow::slotCurrentChooseA()   { d->setMergeOperation(currentIndex(), d->m_bSyncMode ? eCopyAToB : eCopyAToDest ); }
void DirectoryMergeWindow::slotCurrentChooseB()   { d->setMergeOperation(currentIndex(), d->m_bSyncMode ? eCopyBToA : eCopyBToDest ); }
void DirectoryMergeWindow::slotCurrentChooseC()   { d->setMergeOperation(currentIndex(), eCopyCToDest ); }
Joachim Eibl's avatar
0.9.80    
Joachim Eibl committed
1297
1298
void DirectoryMergeWindow::slotCurrentMerge()
{
1299
1300
   bool bThreeDirs = d->m_dirC.isValid();
   d->setMergeOperation(currentIndex(), bThreeDirs ? eMergeABCToDest : eMergeABToDest );
Joachim Eibl's avatar
0.9.80    
Joachim Eibl committed
1301
}
1302
void DirectoryMergeWindow::slotCurrentDelete()    { d->setMergeOperation(currentIndex(), eDeleteFromDest ); }
Joachim Eibl's avatar
0.9.80    
Joachim Eibl committed
1303
// Sync current item
1304
1305
1306
1307
1308
1309
1310
1311
void DirectoryMergeWindow::slotCurrentCopyAToB()     { d->setMergeOperation(currentIndex(), eCopyAToB ); }
void DirectoryMergeWindow::slotCurrentCopyBToA()     { d->setMergeOperation(currentIndex(), eCopyBToA ); }
void DirectoryMergeWindow::slotCurrentDeleteA()      { d->setMergeOperation(currentIndex(), eDeleteA );  }
void DirectoryMergeWindow::slotCurrentDeleteB()      { d->setMergeOperation(currentIndex(), eDeleteB );  }
void DirectoryMergeWindow::slotCurrentDeleteAAndB()  { d->setMergeOperation(currentIndex(), eDeleteAB ); }
void DirectoryMergeWindow::slotCurrentMergeToA()     { d->setMergeOperation(currentIndex(), eMergeToA ); }
void DirectoryMergeWindow::slotCurrentMergeToB()     { d->setMergeOperation(currentIndex(), eMergeToB ); }
void DirectoryMergeWindow::slotCurrentMergeToAAndB() { d->setMergeOperation(currentIndex(), eMergeToAB ); }
Joachim Eibl's avatar
0.9.80    
Joachim Eibl committed
1312
1313
1314
1315


void DirectoryMergeWindow::keyPressEvent( QKeyEvent* e )
{
Joachim Eibl's avatar
Joachim Eibl committed
1316
   if ( (e->QInputEvent::modifiers() & Qt::ControlModifier)!=0 )
Joachim Eibl's avatar
0.9.80    
Joachim Eibl committed
1317
   {
1318
1319
1320
      bool bThreeDirs = d->m_dirC.isValid();
      
      MergeFileInfos* pMFI = d->getMFI( currentIndex() );
Joachim Eibl's avatar
0.9.80    
Joachim Eibl committed
1321

1322
1323
1324
      if ( pMFI==0 ) 
         return;
      bool bMergeMode = bThreeDirs || !d->m_bSyncMode;
Joachim Eibl's avatar
0.9.80    
Joachim Eibl committed
1325
1326
1327
1328
1329
1330
      bool bFTConflict = pMFI==0 ? false : conflictingFileTypes(*pMFI);

      if ( bMergeMode )
      {
         switch(e->key())
         {
1331
1332
1333
         case Qt::Key_1:      if(pMFI->existsInA()){ slotCurrentChooseA(); }  return;
         case Qt::Key_2:      if(pMFI->existsInB()){ slotCurrentChooseB(); }  return;
         case Qt::Key_3:      if(pMFI->existsInC()){ slotCurrentChooseC(); }  return;
Joachim Eibl's avatar
Joachim Eibl committed
1334
1335
1336
         case Qt::Key_Space:  slotCurrentDoNothing();                          return;
         case Qt::Key_4:      if ( !bFTConflict )   { slotCurrentMerge();   }  return;
         case Qt::Key_Delete: slotCurrentDelete();                             return;
Joachim Eibl's avatar
0.9.80    
Joachim Eibl committed
1337
1338
1339
1340
1341
1342
1343
         default: break;
         }
      }
      else
      {
         switch(e->key())
         {
1344
1345
         case Qt::Key_1:      if(pMFI->existsInA()){ slotCurrentCopyAToB(); }  return;
         case Qt::Key_2:      if(pMFI->existsInB()){ slotCurrentCopyBToA(); }  return;
Joachim Eibl's avatar
Joachim Eibl committed
1346
1347
         case Qt::Key_Space:  slotCurrentDoNothing();                           return;
         case Qt::Key_4:      if ( !bFTConflict ) { slotCurrentMergeToAAndB(); }  return;
1348
1349
1350
         case Qt::Key_Delete: if( pMFI->existsInA() && pMFI->existsInB() ) slotCurrentDeleteAAndB();
                          else if( pMFI->existsInA() ) slotCurrentDeleteA();
                          else if( pMFI->existsInB() ) slotCurrentDeleteB();
Joachim Eibl's avatar
0.9.80    
Joachim Eibl committed
1351
1352
1353
1354
1355
                          return;
         default: break;
         }
      }
   }
Joachim Eibl's avatar
Joachim Eibl committed
1356
1357
   else if ( e->key()==Qt::Key_Return || e->key()==Qt::Key_Enter )
   {