joachim99@8
|
1 /***************************************************************************
|
joachim99@8
|
2 directorymergewindow.h
|
joachim99@8
|
3 -------------------
|
joachim99@8
|
4 begin : Sat Oct 19 2002
|
joachim99@77
|
5 copyright : (C) 2002-2007 by Joachim Eibl
|
joachim99@69
|
6 email : joachim.eibl at gmx.de
|
joachim99@8
|
7 ***************************************************************************/
|
joachim99@8
|
8
|
joachim99@8
|
9 /***************************************************************************
|
joachim99@8
|
10 * *
|
joachim99@8
|
11 * This program is free software; you can redistribute it and/or modify *
|
joachim99@8
|
12 * it under the terms of the GNU General Public License as published by *
|
joachim99@8
|
13 * the Free Software Foundation; either version 2 of the License, or *
|
joachim99@8
|
14 * (at your option) any later version. *
|
joachim99@8
|
15 * *
|
joachim99@8
|
16 ***************************************************************************/
|
joachim99@8
|
17
|
joachim99@8
|
18 #ifndef DIRECTORY_MERGE_WINDOW_H
|
joachim99@8
|
19 #define DIRECTORY_MERGE_WINDOW_H
|
joachim99@8
|
20
|
joachim99@75
|
21 #include <QTreeWidget>
|
joachim99@80
|
22 #include <QEvent>
|
joachim99@8
|
23 #include <list>
|
joachim99@8
|
24 #include <map>
|
joachim99@8
|
25 #include "common.h"
|
joachim99@8
|
26 #include "fileaccess.h"
|
joachim99@66
|
27 #include "diff.h" //TotalDiffStatus
|
joachim99@8
|
28
|
joachim99@8
|
29 class OptionDialog;
|
joachim99@8
|
30 class KIconLoader;
|
joachim99@8
|
31 class StatusInfo;
|
joachim99@8
|
32 class DirectoryMergeInfo;
|
joachim99@8
|
33 class OneDirectoryInfo;
|
joachim99@8
|
34 class QLabel;
|
joachim99@51
|
35 class KAction;
|
joachim99@51
|
36 class KToggleAction;
|
joachim99@51
|
37 class KActionCollection;
|
joachim99@66
|
38 class TotalDiffStatus;
|
joachim99@8
|
39
|
joachim99@8
|
40 enum e_MergeOperation
|
joachim99@8
|
41 {
|
joachim99@8
|
42 eTitleId,
|
joachim99@8
|
43 eNoOperation,
|
joachim99@8
|
44 // Operations in sync mode (with only two directories):
|
joachim99@8
|
45 eCopyAToB, eCopyBToA, eDeleteA, eDeleteB, eDeleteAB, eMergeToA, eMergeToB, eMergeToAB,
|
joachim99@8
|
46
|
joachim99@8
|
47 // Operations in merge mode (with two or three directories)
|
joachim99@8
|
48 eCopyAToDest, eCopyBToDest, eCopyCToDest, eDeleteFromDest, eMergeABCToDest,
|
joachim99@8
|
49 eMergeABToDest,
|
joachim99@8
|
50 eConflictingFileTypes, // Error
|
joachim99@92
|
51 eChangedAndDeleted, // Error
|
joachim99@8
|
52 eConflictingAges // Equal age but files are not!
|
joachim99@8
|
53 };
|
joachim99@8
|
54
|
joachim99@8
|
55 class DirMergeItem;
|
joachim99@8
|
56
|
joachim99@8
|
57 enum e_Age { eNew, eMiddle, eOld, eNotThere, eAgeEnd };
|
joachim99@8
|
58
|
joachim99@8
|
59 class MergeFileInfos
|
joachim99@8
|
60 {
|
joachim99@8
|
61 public:
|
joachim99@8
|
62 MergeFileInfos(){ m_bEqualAB=false; m_bEqualAC=false; m_bEqualBC=false;
|
joachim99@8
|
63 m_pDMI=0; m_pParent=0;
|
joachim99@8
|
64 m_bExistsInA=false;m_bExistsInB=false;m_bExistsInC=false;
|
joachim99@8
|
65 m_bDirA=false; m_bDirB=false; m_bDirC=false;
|
joachim99@8
|
66 m_bLinkA=false; m_bLinkB=false; m_bLinkC=false;
|
joachim99@8
|
67 m_bOperationComplete=false; m_bSimOpComplete = false;
|
joachim99@8
|
68 m_eMergeOperation=eNoOperation;
|
joachim99@8
|
69 m_ageA = eNotThere; m_ageB=eNotThere; m_ageC=eNotThere;
|
joachim99@8
|
70 m_bConflictingAges=false; }
|
joachim99@8
|
71 bool operator>( const MergeFileInfos& );
|
joachim99@8
|
72 QString m_subPath;
|
joachim99@8
|
73
|
joachim99@8
|
74 bool m_bExistsInA;
|
joachim99@8
|
75 bool m_bExistsInB;
|
joachim99@8
|
76 bool m_bExistsInC;
|
joachim99@8
|
77 bool m_bEqualAB;
|
joachim99@8
|
78 bool m_bEqualAC;
|
joachim99@8
|
79 bool m_bEqualBC;
|
joachim99@8
|
80 DirMergeItem* m_pDMI;
|
joachim99@8
|
81 MergeFileInfos* m_pParent;
|
joachim99@8
|
82 e_MergeOperation m_eMergeOperation;
|
joachim99@77
|
83 void setMergeOperation( e_MergeOperation eMOp, bool bRecursive=true );
|
joachim99@8
|
84 bool m_bDirA;
|
joachim99@8
|
85 bool m_bDirB;
|
joachim99@8
|
86 bool m_bDirC;
|
joachim99@8
|
87 bool m_bLinkA;
|
joachim99@8
|
88 bool m_bLinkB;
|
joachim99@8
|
89 bool m_bLinkC;
|
joachim99@8
|
90 bool m_bOperationComplete;
|
joachim99@8
|
91 bool m_bSimOpComplete;
|
joachim99@8
|
92 e_Age m_ageA;
|
joachim99@8
|
93 e_Age m_ageB;
|
joachim99@8
|
94 e_Age m_ageC;
|
joachim99@8
|
95 bool m_bConflictingAges; // Equal age but files are not!
|
joachim99@8
|
96
|
joachim99@8
|
97 FileAccess m_fileInfoA;
|
joachim99@8
|
98 FileAccess m_fileInfoB;
|
joachim99@8
|
99 FileAccess m_fileInfoC;
|
joachim99@66
|
100
|
joachim99@66
|
101 TotalDiffStatus m_totalDiffStatus;
|
joachim99@8
|
102 };
|
joachim99@8
|
103
|
joachim99@75
|
104 class DirMergeItem : public QTreeWidgetItem
|
joachim99@8
|
105 {
|
joachim99@8
|
106 public:
|
joachim99@75
|
107 DirMergeItem( QTreeWidget* pParent, const QString&, MergeFileInfos*);
|
joachim99@8
|
108 DirMergeItem( DirMergeItem* pParent, const QString&, MergeFileInfos*);
|
joachim99@8
|
109 ~DirMergeItem();
|
joachim99@8
|
110 MergeFileInfos* m_pMFI;
|
joachim99@75
|
111 virtual bool operator<( const QTreeWidgetItem& other ) const;
|
joachim99@75
|
112 //virtual void paintCell(QPainter * p, const QColorGroup & cg, int column, int width, int align );
|
joachim99@66
|
113 void init(MergeFileInfos* pMFI);
|
joachim99@8
|
114 };
|
joachim99@8
|
115
|
joachim99@75
|
116 class DirectoryMergeWindow : public QTreeWidget
|
joachim99@8
|
117 {
|
joachim99@8
|
118 Q_OBJECT
|
joachim99@8
|
119 public:
|
joachim99@8
|
120 DirectoryMergeWindow( QWidget* pParent, OptionDialog* pOptions, KIconLoader* pIconLoader );
|
joachim99@8
|
121 ~DirectoryMergeWindow();
|
joachim99@8
|
122 void setDirectoryMergeInfo(DirectoryMergeInfo* p){ m_pDirectoryMergeInfo=p; }
|
joachim99@8
|
123 bool init(
|
joachim99@8
|
124 FileAccess& dirA,
|
joachim99@8
|
125 FileAccess& dirB,
|
joachim99@8
|
126 FileAccess& dirC,
|
joachim99@8
|
127 FileAccess& dirDest,
|
joachim99@77
|
128 bool bDirectoryMerge,
|
joachim99@77
|
129 bool bReload = false
|
joachim99@8
|
130 );
|
joachim99@8
|
131 bool isFileSelected();
|
joachim99@8
|
132 bool isDirectoryMergeInProgress() { return m_bRealMergeStarted; }
|
joachim99@8
|
133 int totalColumnWidth();
|
joachim99@8
|
134 bool isSyncMode() { return m_bSyncMode; }
|
joachim99@66
|
135 bool isScanning() { return m_bScanning; }
|
joachim99@51
|
136 void initDirectoryMergeActions( QObject* pKDiff3App, KActionCollection* ac );
|
joachim99@53
|
137 void updateAvailabilities( bool bDirCompare, bool bDiffWindowVisible,
|
joachim99@53
|
138 KToggleAction* chooseA, KToggleAction* chooseB, KToggleAction* chooseC );
|
joachim99@69
|
139 void updateFileVisibilities();
|
joachim99@51
|
140
|
joachim99@75
|
141 virtual void mousePressEvent( QMouseEvent* e );
|
joachim99@51
|
142 virtual void keyPressEvent( QKeyEvent* e );
|
joachim99@53
|
143 virtual void focusInEvent( QFocusEvent* e );
|
joachim99@53
|
144 virtual void focusOutEvent( QFocusEvent* e );
|
joachim99@75
|
145 virtual void contextMenuEvent( QContextMenuEvent* e );
|
joachim99@75
|
146 QString getDirNameA(){ return m_dirA.prettyAbsPath(); }
|
joachim99@75
|
147 QString getDirNameB(){ return m_dirB.prettyAbsPath(); }
|
joachim99@75
|
148 QString getDirNameC(){ return m_dirC.prettyAbsPath(); }
|
joachim99@75
|
149 QString getDirNameDest(){ return m_dirDest.prettyAbsPath(); }
|
joachim99@8
|
150
|
joachim99@8
|
151 public slots:
|
joachim99@8
|
152 void reload();
|
joachim99@8
|
153 void mergeCurrentFile();
|
joachim99@8
|
154 void compareCurrentFile();
|
joachim99@51
|
155 void slotRunOperationForAllItems();
|
joachim99@51
|
156 void slotRunOperationForCurrentItem();
|
joachim99@8
|
157 void mergeResultSaved(const QString& fileName);
|
joachim99@8
|
158 void slotChooseAEverywhere();
|
joachim99@8
|
159 void slotChooseBEverywhere();
|
joachim99@8
|
160 void slotChooseCEverywhere();
|
joachim99@8
|
161 void slotAutoChooseEverywhere();
|
joachim99@8
|
162 void slotNoOpEverywhere();
|
joachim99@8
|
163 void slotFoldAllSubdirs();
|
joachim99@8
|
164 void slotUnfoldAllSubdirs();
|
joachim99@69
|
165 void slotShowIdenticalFiles();
|
joachim99@69
|
166 void slotShowDifferentFiles();
|
joachim99@69
|
167 void slotShowFilesOnlyInA();
|
joachim99@69
|
168 void slotShowFilesOnlyInB();
|
joachim99@69
|
169 void slotShowFilesOnlyInC();
|
joachim99@69
|
170
|
joachim99@69
|
171 void slotSynchronizeDirectories();
|
joachim99@69
|
172 void slotChooseNewerFiles();
|
joachim99@69
|
173
|
joachim99@69
|
174 void slotCompareExplicitlySelectedFiles();
|
joachim99@69
|
175 void slotMergeExplicitlySelectedFiles();
|
joachim99@69
|
176
|
joachim99@51
|
177 // Merge current item (merge mode)
|
joachim99@51
|
178 void slotCurrentDoNothing();
|
joachim99@51
|
179 void slotCurrentChooseA();
|
joachim99@51
|
180 void slotCurrentChooseB();
|
joachim99@51
|
181 void slotCurrentChooseC();
|
joachim99@51
|
182 void slotCurrentMerge();
|
joachim99@51
|
183 void slotCurrentDelete();
|
joachim99@51
|
184 // Sync current item
|
joachim99@51
|
185 void slotCurrentCopyAToB();
|
joachim99@51
|
186 void slotCurrentCopyBToA();
|
joachim99@51
|
187 void slotCurrentDeleteA();
|
joachim99@51
|
188 void slotCurrentDeleteB();
|
joachim99@51
|
189 void slotCurrentDeleteAAndB();
|
joachim99@51
|
190 void slotCurrentMergeToA();
|
joachim99@51
|
191 void slotCurrentMergeToB();
|
joachim99@51
|
192 void slotCurrentMergeToAAndB();
|
joachim99@8
|
193
|
joachim99@69
|
194 void slotSaveMergeState();
|
joachim99@69
|
195 void slotLoadMergeState();
|
joachim99@69
|
196
|
joachim99@8
|
197 protected:
|
joachim99@75
|
198 class DirMergeItemDelegate;
|
joachim99@51
|
199 void mergeContinue( bool bStart, bool bVerbose );
|
joachim99@8
|
200
|
joachim99@66
|
201 void prepareListView(ProgressProxy& pp);
|
joachim99@8
|
202 void calcSuggestedOperation( MergeFileInfos& mfi, e_MergeOperation eDefaultOperation );
|
joachim99@8
|
203 void setAllMergeOperations( e_MergeOperation eDefaultOperation );
|
joachim99@8
|
204 friend class MergeFileInfos;
|
joachim99@8
|
205
|
joachim99@8
|
206 bool canContinue();
|
joachim99@75
|
207 void prepareMergeStart( QTreeWidgetItem* pBegin, QTreeWidgetItem* pEnd, bool bVerbose );
|
joachim99@51
|
208 bool executeMergeOperation( MergeFileInfos& mfi, bool& bSingleFileMerge );
|
joachim99@8
|
209
|
joachim99@8
|
210 void scanDirectory( const QString& dirName, t_DirectoryList& dirList );
|
joachim99@8
|
211 void scanLocalDirectory( const QString& dirName, t_DirectoryList& dirList );
|
joachim99@8
|
212 void fastFileComparison( FileAccess& fi1, FileAccess& fi2,
|
joachim99@8
|
213 bool& bEqual, bool& bError, QString& status );
|
joachim99@8
|
214 void compareFilesAndCalcAges( MergeFileInfos& mfi );
|
joachim99@8
|
215
|
joachim99@8
|
216 QString fullNameA( const MergeFileInfos& mfi )
|
joachim99@80
|
217 { return mfi.m_bExistsInA ? mfi.m_fileInfoA.absoluteFilePath() : m_dirA.absoluteFilePath() + "/" + mfi.m_subPath; }
|
joachim99@8
|
218 QString fullNameB( const MergeFileInfos& mfi )
|
joachim99@80
|
219 { return mfi.m_bExistsInB ? mfi.m_fileInfoB.absoluteFilePath() : m_dirB.absoluteFilePath() + "/" + mfi.m_subPath; }
|
joachim99@8
|
220 QString fullNameC( const MergeFileInfos& mfi )
|
joachim99@80
|
221 { return mfi.m_bExistsInC ? mfi.m_fileInfoC.absoluteFilePath() : m_dirC.absoluteFilePath() + "/" + mfi.m_subPath; }
|
joachim99@8
|
222 QString fullNameDest( const MergeFileInfos& mfi )
|
joachim99@77
|
223 { if ( m_dirDestInternal.prettyAbsPath() == m_dirC.prettyAbsPath() ) return fullNameC(mfi);
|
joachim99@77
|
224 else if ( m_dirDestInternal.prettyAbsPath() == m_dirB.prettyAbsPath() ) return fullNameB(mfi);
|
joachim99@80
|
225 else return m_dirDestInternal.absoluteFilePath() + "/" + mfi.m_subPath;
|
joachim99@77
|
226 }
|
joachim99@8
|
227
|
joachim99@8
|
228 bool copyFLD( const QString& srcName, const QString& destName );
|
joachim99@8
|
229 bool deleteFLD( const QString& name, bool bCreateBackup );
|
joachim99@8
|
230 bool makeDir( const QString& name, bool bQuiet=false );
|
joachim99@8
|
231 bool renameFLD( const QString& srcName, const QString& destName );
|
joachim99@8
|
232 bool mergeFLD( const QString& nameA,const QString& nameB,const QString& nameC,
|
joachim99@8
|
233 const QString& nameDest, bool& bSingleFileMerge );
|
joachim99@8
|
234
|
joachim99@8
|
235 FileAccess m_dirA;
|
joachim99@8
|
236 FileAccess m_dirB;
|
joachim99@8
|
237 FileAccess m_dirC;
|
joachim99@8
|
238 FileAccess m_dirDest;
|
joachim99@8
|
239 FileAccess m_dirDestInternal;
|
joachim99@8
|
240
|
joachim99@69
|
241 QString m_dirMergeStateFilename;
|
joachim99@69
|
242
|
joachim99@8
|
243 std::map<QString, MergeFileInfos> m_fileMergeMap;
|
joachim99@8
|
244
|
joachim99@8
|
245 bool m_bFollowDirLinks;
|
joachim99@8
|
246 bool m_bFollowFileLinks;
|
joachim99@8
|
247 bool m_bSimulatedMergeStarted;
|
joachim99@8
|
248 bool m_bRealMergeStarted;
|
joachim99@8
|
249 bool m_bError;
|
joachim99@8
|
250 bool m_bSyncMode;
|
joachim99@8
|
251 bool m_bDirectoryMerge; // if true, then merge is the default operation, otherwise it's diff.
|
joachim99@69
|
252 bool m_bCaseSensitive;
|
joachim99@66
|
253
|
joachim99@66
|
254 bool m_bScanning; // true while in init()
|
joachim99@8
|
255
|
joachim99@8
|
256 OptionDialog* m_pOptions;
|
joachim99@8
|
257 KIconLoader* m_pIconLoader;
|
joachim99@8
|
258 DirectoryMergeInfo* m_pDirectoryMergeInfo;
|
joachim99@8
|
259 StatusInfo* m_pStatusInfo;
|
joachim99@51
|
260
|
joachim99@51
|
261 typedef std::list<DirMergeItem*> MergeItemList;
|
joachim99@51
|
262 MergeItemList m_mergeItemList;
|
joachim99@51
|
263 MergeItemList::iterator m_currentItemForOperation;
|
joachim99@51
|
264
|
joachim99@69
|
265 DirMergeItem* m_pSelection1Item;
|
joachim99@69
|
266 int m_selection1Column;
|
joachim99@69
|
267 DirMergeItem* m_pSelection2Item;
|
joachim99@69
|
268 int m_selection2Column;
|
joachim99@69
|
269 DirMergeItem* m_pSelection3Item;
|
joachim99@69
|
270 int m_selection3Column;
|
joachim99@69
|
271 void selectItemAndColumn(DirMergeItem* pDMI, int c, bool bContextMenu);
|
joachim99@69
|
272 friend class DirMergeItem;
|
joachim99@51
|
273
|
joachim99@69
|
274 KAction* m_pDirStartOperation;
|
joachim99@69
|
275 KAction* m_pDirRunOperationForCurrentItem;
|
joachim99@69
|
276 KAction* m_pDirCompareCurrent;
|
joachim99@69
|
277 KAction* m_pDirMergeCurrent;
|
joachim99@69
|
278 KAction* m_pDirRescan;
|
joachim99@69
|
279 KAction* m_pDirChooseAEverywhere;
|
joachim99@69
|
280 KAction* m_pDirChooseBEverywhere;
|
joachim99@69
|
281 KAction* m_pDirChooseCEverywhere;
|
joachim99@69
|
282 KAction* m_pDirAutoChoiceEverywhere;
|
joachim99@69
|
283 KAction* m_pDirDoNothingEverywhere;
|
joachim99@69
|
284 KAction* m_pDirFoldAll;
|
joachim99@69
|
285 KAction* m_pDirUnfoldAll;
|
joachim99@51
|
286
|
joachim99@69
|
287 KToggleAction* m_pDirShowIdenticalFiles;
|
joachim99@69
|
288 KToggleAction* m_pDirShowDifferentFiles;
|
joachim99@69
|
289 KToggleAction* m_pDirShowFilesOnlyInA;
|
joachim99@69
|
290 KToggleAction* m_pDirShowFilesOnlyInB;
|
joachim99@69
|
291 KToggleAction* m_pDirShowFilesOnlyInC;
|
joachim99@69
|
292
|
joachim99@69
|
293 KToggleAction* m_pDirSynchronizeDirectories;
|
joachim99@69
|
294 KToggleAction* m_pDirChooseNewerFiles;
|
joachim99@69
|
295
|
joachim99@69
|
296 KAction* m_pDirCompareExplicit;
|
joachim99@69
|
297 KAction* m_pDirMergeExplicit;
|
joachim99@69
|
298
|
joachim99@69
|
299 KAction* m_pDirCurrentDoNothing;
|
joachim99@69
|
300 KAction* m_pDirCurrentChooseA;
|
joachim99@69
|
301 KAction* m_pDirCurrentChooseB;
|
joachim99@69
|
302 KAction* m_pDirCurrentChooseC;
|
joachim99@69
|
303 KAction* m_pDirCurrentMerge;
|
joachim99@69
|
304 KAction* m_pDirCurrentDelete;
|
joachim99@69
|
305
|
joachim99@69
|
306 KAction* m_pDirCurrentSyncDoNothing;
|
joachim99@69
|
307 KAction* m_pDirCurrentSyncCopyAToB;
|
joachim99@69
|
308 KAction* m_pDirCurrentSyncCopyBToA;
|
joachim99@69
|
309 KAction* m_pDirCurrentSyncDeleteA;
|
joachim99@69
|
310 KAction* m_pDirCurrentSyncDeleteB;
|
joachim99@69
|
311 KAction* m_pDirCurrentSyncDeleteAAndB;
|
joachim99@69
|
312 KAction* m_pDirCurrentSyncMergeToA;
|
joachim99@69
|
313 KAction* m_pDirCurrentSyncMergeToB;
|
joachim99@69
|
314 KAction* m_pDirCurrentSyncMergeToAAndB;
|
joachim99@69
|
315
|
joachim99@69
|
316 KAction* m_pDirSaveMergeState;
|
joachim99@69
|
317 KAction* m_pDirLoadMergeState;
|
joachim99@8
|
318 signals:
|
joachim99@66
|
319 void startDiffMerge(QString fn1,QString fn2, QString fn3, QString ofn, QString,QString,QString,TotalDiffStatus*);
|
joachim99@8
|
320 void checkIfCanContinue( bool* pbContinue );
|
joachim99@8
|
321 void updateAvailabilities();
|
joachim99@66
|
322 void statusBarMessage( const QString& msg );
|
joachim99@8
|
323 protected slots:
|
joachim99@75
|
324 void onDoubleClick( QTreeWidgetItem* lvi );
|
joachim99@75
|
325 void onCurrentChanged(QTreeWidgetItem*);
|
joachim99@75
|
326 void onExpanded();
|
joachim99@8
|
327 };
|
joachim99@8
|
328
|
joachim99@75
|
329 class DirectoryMergeInfo : public QFrame
|
joachim99@8
|
330 {
|
joachim99@53
|
331 Q_OBJECT
|
joachim99@8
|
332 public:
|
joachim99@8
|
333 DirectoryMergeInfo( QWidget* pParent );
|
joachim99@8
|
334 void setInfo(
|
joachim99@8
|
335 const FileAccess& APath,
|
joachim99@8
|
336 const FileAccess& BPath,
|
joachim99@8
|
337 const FileAccess& CPath,
|
joachim99@8
|
338 const FileAccess& DestPath,
|
joachim99@8
|
339 MergeFileInfos& mfi );
|
joachim99@75
|
340 QTreeWidget* getInfoList() {return m_pInfoList;}
|
joachim99@66
|
341 virtual bool eventFilter( QObject* o, QEvent* e );
|
joachim99@66
|
342 signals:
|
joachim99@66
|
343 void gotFocus();
|
joachim99@8
|
344 private:
|
joachim99@8
|
345 QLabel* m_pInfoA;
|
joachim99@8
|
346 QLabel* m_pInfoB;
|
joachim99@8
|
347 QLabel* m_pInfoC;
|
joachim99@8
|
348 QLabel* m_pInfoDest;
|
joachim99@8
|
349
|
joachim99@8
|
350 QLabel* m_pA;
|
joachim99@8
|
351 QLabel* m_pB;
|
joachim99@8
|
352 QLabel* m_pC;
|
joachim99@8
|
353 QLabel* m_pDest;
|
joachim99@8
|
354
|
joachim99@75
|
355 QTreeWidget* m_pInfoList;
|
joachim99@8
|
356 };
|
joachim99@8
|
357
|
joachim99@8
|
358
|
joachim99@8
|
359 #endif
|