File indexing completed on 2024-04-28 17:03:40

0001 // SPDX-FileCopyrightText: 2020 Simon Persson <simon.persson@mykolab.com>
0002 //
0003 // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 
0005 #include "restoredialog.h"
0006 #include "ui_restoredialog.h"
0007 #include "restorejob.h"
0008 #include "dirselector.h"
0009 #include "kuputils.h"
0010 #include "kupfiledigger_debug.h"
0011 
0012 #include <KFileUtils>
0013 #include <KFileWidget>
0014 #include <KIO/CopyJob>
0015 #include <KIO/JobUiDelegate>
0016 #include <KIO/JobUiDelegateFactory>
0017 #include <KIO/ListJob>
0018 #include <KIO/OpenUrlJob>
0019 #include <KIO/UDSEntry>
0020 #include <KLocalizedString>
0021 #include <KMessageBox>
0022 #include <KMessageWidget>
0023 #include <KProcess>
0024 #include <KWidgetJobTracker>
0025 #include <QStorageInfo>
0026 
0027 #include <QDir>
0028 #include <QInputDialog>
0029 #include <QPushButton>
0030 #include <QTimer>
0031 #include <utility>
0032 #include <kio_version.h>
0033 
0034 static const QString cKupTempRestoreFolder = QStringLiteral("_kup_temporary_restore_folder_");
0035 
0036 RestoreDialog::RestoreDialog(BupSourceInfo pPathInfo, QWidget *parent)
0037    : QDialog(parent), mUI(new Ui::RestoreDialog), mSourceInfo(std::move(pPathInfo))
0038 {
0039     mSourceFileName = mSourceInfo.mPathInRepo.section(QDir::separator(), -1);
0040 
0041     qCDebug(KUPFILEDIGGER) << "Starting restore dialog for repo: " << mSourceInfo.mRepoPath
0042                            << ", restoring: " << mSourceInfo.mPathInRepo;
0043 
0044     mUI->setupUi(this);
0045 
0046     mFileWidget = nullptr;
0047     mDirSelector = nullptr;
0048     mJobTracker = nullptr;
0049 
0050     mUI->mRestoreOriginalButton->setMinimumHeight(mUI->mRestoreOriginalButton->sizeHint().height() * 2);
0051     mUI->mRestoreCustomButton->setMinimumHeight(mUI->mRestoreCustomButton->sizeHint().height() * 2);
0052 
0053     connect(mUI->mRestoreOriginalButton, SIGNAL(clicked()), SLOT(setOriginalDestination()));
0054     connect(mUI->mRestoreCustomButton, SIGNAL(clicked()), SLOT(setCustomDestination()));
0055 
0056     mMessageWidget = new KMessageWidget(this);
0057     mMessageWidget->setWordWrap(true);
0058     mUI->mTopLevelVLayout->insertWidget(0, mMessageWidget);
0059     mMessageWidget->hide();
0060     connect(mUI->mDestBackButton, SIGNAL(clicked()), mMessageWidget, SLOT(hide()));
0061     connect(mUI->mDestNextButton, SIGNAL(clicked()), SLOT(checkDestinationSelection()));
0062     connect(mUI->mDestBackButton, &QPushButton::clicked, this, [this]{mUI->mStackedWidget->setCurrentIndex(0);});
0063     connect(mUI->mOverwriteBackButton, &QPushButton::clicked, this, [this]{mUI->mStackedWidget->setCurrentIndex(0);});
0064     connect(mUI->mConfirmButton, SIGNAL(clicked()), SLOT(fileOverwriteConfirmed()));
0065     connect(mUI->mOpenDestinationButton, SIGNAL(clicked()), SLOT(openDestinationFolder()));
0066 }
0067 
0068 RestoreDialog::~RestoreDialog() {
0069     delete mUI;
0070 }
0071 
0072 void RestoreDialog::changeEvent(QEvent *pEvent) {
0073     QDialog::changeEvent(pEvent);
0074     switch (pEvent->type()) {
0075     case QEvent::LanguageChange:
0076         mUI->retranslateUi(this);
0077         break;
0078     default:
0079         break;
0080     }
0081 }
0082 
0083 void RestoreDialog::setOriginalDestination() {
0084     if(mSourceInfo.mIsDirectory) {
0085         // the path in repo could have had slashes appended below, we are back here because user clicked "back"
0086         ensureNoTrailingSlash(mSourceInfo.mPathInRepo);
0087         //select parent of folder to be restored
0088         mDestination.setFile(mSourceInfo.mPathInRepo.section(QDir::separator(), 0, -2));
0089     } else {
0090         mDestination.setFile(mSourceInfo.mPathInRepo);
0091     }
0092     startPrechecks();
0093 }
0094 
0095 void RestoreDialog::setCustomDestination() {
0096     if(mSourceInfo.mIsDirectory && mDirSelector == nullptr) {
0097         mDirSelector = new DirSelector(this);
0098         mDirSelector->setRootUrl(QUrl::fromLocalFile(QStringLiteral("/")));
0099         QString lDirPath = mSourceInfo.mPathInRepo.section(QDir::separator(), 0, -2);
0100         mDirSelector->expandToUrl(QUrl::fromLocalFile(lDirPath));
0101         mUI->mDestinationVLayout->insertWidget(0, mDirSelector);
0102 
0103         auto lNewFolderButton = new QPushButton(QIcon::fromTheme(QStringLiteral("folder-new")),
0104                                                         xi18nc("@action:button","New Folder..."));
0105         connect(lNewFolderButton, SIGNAL(clicked()), SLOT(createNewFolder()));
0106         mUI->mDestinationHLayout->insertWidget(0, lNewFolderButton);
0107     } else if(!mSourceInfo.mIsDirectory && mFileWidget == nullptr) {
0108         QFileInfo lFileInfo(mSourceInfo.mPathInRepo);
0109         do {
0110             lFileInfo.setFile(lFileInfo.absolutePath()); // check the file's directory first, not the file.
0111         } while(!lFileInfo.exists());
0112         QUrl lStartSelection = QUrl::fromLocalFile(lFileInfo.absoluteFilePath() + '/' + mSourceFileName);
0113         mFileWidget = new KFileWidget(lStartSelection, this);
0114         mFileWidget->setOperationMode(KFileWidget::Saving);
0115         mFileWidget->setMode(KFile::File | KFile::LocalOnly);
0116         mUI->mDestinationVLayout->insertWidget(0, mFileWidget);
0117     }
0118     mUI->mDestNextButton->setFocus();
0119     mUI->mStackedWidget->setCurrentIndex(1);
0120 }
0121 
0122 void RestoreDialog::checkDestinationSelection() {
0123     if(mSourceInfo.mIsDirectory) {
0124         QUrl lUrl = mDirSelector->url();
0125         if(!lUrl.isEmpty()) {
0126             mDestination.setFile(lUrl.path());
0127             startPrechecks();
0128         } else {
0129             mMessageWidget->setText(xi18nc("@info message bar appearing on top",
0130                                           "No destination was selected, please select one."));
0131             mMessageWidget->setMessageType(KMessageWidget::Error);
0132             mMessageWidget->animatedShow();
0133         }
0134     } else {
0135         connect(mFileWidget, SIGNAL(accepted()), SLOT(checkDestinationSelection2()));
0136         mFileWidget->slotOk(); // will emit accepted() if selection is valid, continue below then
0137     }
0138 }
0139 
0140 void RestoreDialog::checkDestinationSelection2() {
0141     mFileWidget->accept(); // This call is needed for selectedFile() to return something.
0142 
0143     QString lFilePath = mFileWidget->selectedFile();
0144     if(!lFilePath.isEmpty()) {
0145         mDestination.setFile(lFilePath);
0146         startPrechecks();
0147     } else {
0148         mMessageWidget->setText(xi18nc("@info message bar appearing on top",
0149                                       "No destination was selected, please select one."));
0150         mMessageWidget->setMessageType(KMessageWidget::Error);
0151         mMessageWidget->animatedShow();
0152     }
0153 }
0154 
0155 void RestoreDialog::startPrechecks() {
0156     mUI->mFileConflictList->clear();
0157     mSourceSize = 0;
0158     mFileSizes.clear();
0159 
0160     qCDebug(KUPFILEDIGGER) << "Destination has been selected: " << mDestination.absoluteFilePath();
0161 
0162     if(mSourceInfo.mIsDirectory) {
0163         mDirectoriesCount = 1; // the folder being restored, rest will be added during listing.
0164         mRestorationPath = mDestination.absoluteFilePath();
0165         mFolderToCreate = QFileInfo(mDestination.absoluteFilePath() + QDir::separator() + mSourceFileName);
0166         mSavedWorkingDirectory.clear();
0167         if(mFolderToCreate.exists()) {
0168             if(mFolderToCreate.isDir()) {
0169                 // destination dir exists, first restore to a subfolder, then move files up.
0170                 mRestorationPath = mFolderToCreate.absoluteFilePath();
0171                 QDir lDir(mFolderToCreate.absoluteFilePath());
0172                 lDir.setFilter(QDir::AllEntries | QDir::Hidden | QDir::System | QDir::NoDotAndDotDot);
0173                 if(lDir.count() > 0) { // destination dir exists and is non-empty.
0174                     mRestorationPath.append(QDir::separator());
0175                     mRestorationPath.append(cKupTempRestoreFolder);
0176                 }
0177                 // make bup not restore the source folder itself but instead it's contents
0178                 mSourceInfo.mPathInRepo.append(QDir::separator());
0179                 // folder already exists, need to check for files about to be overwritten.
0180                 // will create QFileInfos with relative paths during listing and compare with listed source entries.
0181                 mSavedWorkingDirectory = QDir::currentPath();
0182                 QDir::setCurrent(mFolderToCreate.absoluteFilePath());
0183             } else {
0184                 mUI->mFileConflictList->addItem(mFolderToCreate.absoluteFilePath());
0185                 mRestorationPath.append(QDir::separator());
0186                 mRestorationPath.append(cKupTempRestoreFolder);
0187             }
0188         }
0189         qCDebug(KUPFILEDIGGER) << "Starting source file listing job on: " << mSourceInfo.mBupKioPath;
0190         KIO::ListJob *lListJob = KIO::listRecursive(mSourceInfo.mBupKioPath, KIO::HideProgressInfo);
0191         auto lJobTracker = new KWidgetJobTracker(this);
0192         lJobTracker->registerJob(lListJob);
0193         QWidget *lProgressWidget = lJobTracker->widget(lListJob);
0194         mUI->mSourceScanLayout->insertWidget(2, lProgressWidget);
0195         lProgressWidget->show();
0196         connect(lListJob, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)),
0197                 SLOT(collectSourceListing(KIO::Job*,KIO::UDSEntryList)));
0198         connect(lListJob, SIGNAL(result(KJob*)), SLOT(sourceListingCompleted(KJob*)));
0199         lListJob->start();
0200         mUI->mStackedWidget->setCurrentIndex(4);
0201     } else {
0202         mDirectoriesCount = 0;
0203         mSourceSize = mSourceInfo.mSize;
0204         mFileSizes.insert(mSourceFileName, mSourceInfo.mSize);
0205         mRestorationPath = mDestination.absolutePath();
0206         if(mDestination.exists() || mDestination.fileName() != mSourceFileName) {
0207             mRestorationPath.append(QDir::separator());
0208             mRestorationPath.append(cKupTempRestoreFolder);
0209             if(mDestination.exists()) {
0210                 mUI->mFileConflictList->addItem(mDestination.absoluteFilePath());
0211             }
0212         }
0213         completePrechecks();
0214     }
0215 }
0216 
0217 void RestoreDialog::collectSourceListing(KIO::Job *pJob, const KIO::UDSEntryList &pEntryList) {
0218     Q_UNUSED(pJob)
0219     KIO::UDSEntryList::ConstIterator it = pEntryList.begin();
0220     const KIO::UDSEntryList::ConstIterator end = pEntryList.end();
0221     for(; it != end; ++it) {
0222         QString lEntryName = it->stringValue(KIO::UDSEntry::UDS_NAME);
0223         if(it->isDir()) {
0224             if(lEntryName != QStringLiteral(".") && lEntryName != QStringLiteral("..")) {
0225                 mDirectoriesCount++;
0226             }
0227         } else {
0228             if(!it->isLink()) {
0229                 auto lEntrySize = it->numberValue(KIO::UDSEntry::UDS_SIZE);
0230                 mSourceSize += lEntrySize;
0231                 mFileSizes.insert(mSourceFileName + QDir::separator() + lEntryName, lEntrySize);
0232             }
0233             if(!mSavedWorkingDirectory.isEmpty()) {
0234                 if(QFileInfo::exists(lEntryName)) {
0235                     mUI->mFileConflictList->addItem(lEntryName);
0236                 }
0237             }
0238         }
0239     }
0240 }
0241 
0242 void RestoreDialog::sourceListingCompleted(KJob *pJob) {
0243     qCDebug(KUPFILEDIGGER) << "Source listing job completed. Exit status: " << pJob->error();
0244     if(!mSavedWorkingDirectory.isEmpty()) {
0245         QDir::setCurrent(mSavedWorkingDirectory);
0246     }
0247     if(pJob->error() != 0) {
0248         mMessageWidget->setText(xi18nc("@info message bar appearing on top",
0249                                       "There was a problem while getting a list of all files to restore: %1",
0250                                       pJob->errorString()));
0251         mMessageWidget->setMessageType(KMessageWidget::Error);
0252         mMessageWidget->animatedShow();
0253         mUI->mStackedWidget->setCurrentIndex(0);
0254     } else {
0255         completePrechecks();
0256     }
0257 }
0258 
0259 void RestoreDialog::completePrechecks() {
0260     qCDebug(KUPFILEDIGGER) << "Starting free disk space check on: " << mDestination.absolutePath();
0261     QStorageInfo storageInfo(mDestination.absolutePath());
0262     if(storageInfo.isValid() && storageInfo.bytesAvailable() < mSourceSize) {
0263         mMessageWidget->setText(xi18nc("@info message bar appearing on top",
0264                                       "The destination does not have enough space available. "
0265                                       "Please choose a different destination or free some space."));
0266         mMessageWidget->setMessageType(KMessageWidget::Error);
0267         mMessageWidget->animatedShow();
0268         mUI->mStackedWidget->setCurrentIndex(0);
0269     } else if(mUI->mFileConflictList->count() > 0) {
0270         qCDebug(KUPFILEDIGGER) << "Detected file conflicts.";
0271         if(mSourceInfo.mIsDirectory) {
0272             QString lDateString = QLocale().toString(QDateTime::fromSecsSinceEpoch(mSourceInfo.mCommitTime).toLocalTime());
0273             lDateString.replace(QLatin1Char('/'), QLatin1Char('-')); // make sure no slashes in suggested folder name
0274             mUI->mNewFolderNameEdit->setText(mSourceFileName +
0275                                              xi18nc("added to the suggested filename when restoring, %1 is the time when backup was saved",
0276                                                    " - saved at %1", lDateString));
0277             mUI->mConflictTitleLabel->setText(xi18nc("@info", "Folder already exists, please choose a solution"));
0278         } else {
0279             mUI->mOverwriteRadioButton->setChecked(true);
0280             mUI->mOverwriteRadioButton->hide();
0281             mUI->mNewNameRadioButton->hide();
0282             mUI->mNewFolderNameEdit->hide();
0283             mUI->mConflictTitleLabel->setText(xi18nc("@info", "File already exists"));
0284         }
0285         mUI->mStackedWidget->setCurrentIndex(2);
0286     } else {
0287         startRestoring();
0288     }
0289 }
0290 
0291 void RestoreDialog::fileOverwriteConfirmed() {
0292     if(mSourceInfo.mIsDirectory && mUI->mNewNameRadioButton->isChecked()) {
0293         QFileInfo lNewFolderInfo(mDestination.absoluteFilePath() + QDir::separator() + mUI->mNewFolderNameEdit->text());
0294         if(lNewFolderInfo.exists()) {
0295             mMessageWidget->setText(xi18nc("@info message bar appearing on top",
0296                                           "The new name entered already exists, please enter a different one."));
0297             mMessageWidget->setMessageType(KMessageWidget::Error);
0298             mMessageWidget->animatedShow();
0299             return;
0300         }
0301         mFolderToCreate = QFileInfo(mDestination.absoluteFilePath() + QDir::separator() + mUI->mNewFolderNameEdit->text());
0302         mRestorationPath = mFolderToCreate.absoluteFilePath();
0303         if(!mSourceInfo.mPathInRepo.endsWith(QDir::separator())) {
0304             mSourceInfo.mPathInRepo.append(QDir::separator());
0305         }
0306     }
0307     startRestoring();
0308 }
0309 
0310 void RestoreDialog::startRestoring() {
0311     QString lSourcePath(QDir::separator());
0312     lSourcePath.append(mSourceInfo.mBranchName);
0313     lSourcePath.append(QDir::separator());
0314     QDateTime lCommitTime = QDateTime::fromSecsSinceEpoch(mSourceInfo.mCommitTime);
0315     lSourcePath.append(lCommitTime.toString(QStringLiteral("yyyy-MM-dd-hhmmss")));
0316     lSourcePath.append(mSourceInfo.mPathInRepo);
0317     qCDebug(KUPFILEDIGGER) << "Starting restore. Source path: " << lSourcePath << ", restore path: " << mRestorationPath;
0318     auto lRestoreJob = new RestoreJob(mSourceInfo.mRepoPath, lSourcePath, mRestorationPath,
0319                                       mDirectoriesCount, mSourceSize, mFileSizes);
0320     if(mJobTracker == nullptr) {
0321         mJobTracker = new KWidgetJobTracker(this);
0322     }
0323     mJobTracker->registerJob(lRestoreJob);
0324     QWidget *lProgressWidget = mJobTracker->widget(lRestoreJob);
0325     mUI->mRestoreProgressLayout->insertWidget(2, lProgressWidget);
0326     lProgressWidget->show();
0327     connect(lRestoreJob, SIGNAL(result(KJob*)), SLOT(restoringCompleted(KJob*)));
0328     lRestoreJob->start();
0329     mUI->mCloseButton->hide();
0330     mUI->mStackedWidget->setCurrentIndex(3);
0331 }
0332 
0333 void RestoreDialog::restoringCompleted(KJob *pJob) {
0334     qCDebug(KUPFILEDIGGER) << "Restore job completed. Exit status: " << pJob->error();
0335     if(pJob->error() != 0) {
0336         mUI->mRestorationOutput->setPlainText(pJob->errorText());
0337         mUI->mRestorationStackWidget->setCurrentIndex(1);
0338         mUI->mCloseButton->show();
0339     } else {
0340         if(!mSourceInfo.mIsDirectory && mSourceFileName != mDestination.fileName()) {
0341             QUrl lSourceUrl = QUrl::fromLocalFile(mRestorationPath + '/' + mSourceFileName);
0342             QUrl lDestinationUrl = QUrl::fromLocalFile(mRestorationPath + '/' + mDestination.fileName());
0343             KIO::CopyJob *lFileMoveJob = KIO::move(lSourceUrl, lDestinationUrl, KIO::HideProgressInfo);
0344             connect(lFileMoveJob, SIGNAL(result(KJob*)), SLOT(fileMoveCompleted(KJob*)));
0345             qCDebug(KUPFILEDIGGER) << "Starting file move job from: " << lSourceUrl << ", to: " << lDestinationUrl;
0346             lFileMoveJob->start();
0347         } else {
0348             moveFolder();
0349         }
0350     }
0351 }
0352 
0353 void RestoreDialog::fileMoveCompleted(KJob *pJob) {
0354     qCDebug(KUPFILEDIGGER) << "File move job completed. Exit status: " << pJob->error();
0355     if(pJob->error() != 0) {
0356         mUI->mRestorationOutput->setPlainText(pJob->errorText());
0357         mUI->mRestorationStackWidget->setCurrentIndex(1);
0358     } else {
0359         moveFolder();
0360     }
0361 }
0362 
0363 void RestoreDialog::createNewFolder() {
0364     bool lUserAccepted;
0365     QUrl lUrl = mDirSelector->url();
0366     QString lNameSuggestion = xi18nc("default folder name when creating a new folder", "New Folder");
0367     if(QFileInfo::exists(lUrl.adjusted(QUrl::StripTrailingSlash).path() + '/' + lNameSuggestion)) {
0368         lNameSuggestion = KFileUtils::suggestName(lUrl, lNameSuggestion);
0369     }
0370 
0371     QString lSelectedName = QInputDialog::getText(this, xi18nc("@title:window", "New Folder" ),
0372                                                   xi18nc("@label:textbox", "Create new folder in:\n%1", lUrl.path()),
0373                                                   QLineEdit::Normal, lNameSuggestion, &lUserAccepted);
0374 
0375     if (!lUserAccepted)
0376         return;
0377 
0378     QUrl lPartialUrl(lUrl);
0379     const QStringList lDirectories = lSelectedName.split(QDir::separator(), Qt::SkipEmptyParts);
0380     foreach(QString lSubDirectory, lDirectories) {
0381         QDir lDir(lPartialUrl.path());
0382         if(lDir.exists(lSubDirectory)) {
0383             lPartialUrl = lPartialUrl.adjusted(QUrl::StripTrailingSlash);
0384             lPartialUrl.setPath(lPartialUrl.path() + '/' + (lSubDirectory));
0385             KMessageBox::error(this, i18n("A folder named %1 already exists.", lPartialUrl.path()));
0386             return;
0387         }
0388         if(!lDir.mkdir(lSubDirectory)) {
0389             lPartialUrl = lPartialUrl.adjusted(QUrl::StripTrailingSlash);
0390             lPartialUrl.setPath(lPartialUrl.path() + '/' + (lSubDirectory));
0391             KMessageBox::error(this, i18n("You do not have permission to create %1.", lPartialUrl.path()));
0392             return;
0393         }
0394         lPartialUrl = lPartialUrl.adjusted(QUrl::StripTrailingSlash);
0395         lPartialUrl.setPath(lPartialUrl.path() + '/' + (lSubDirectory));
0396     }
0397     mDirSelector->expandToUrl(lPartialUrl);
0398 }
0399 
0400 void RestoreDialog::openDestinationFolder() {
0401     auto *job = new KIO::OpenUrlJob(QUrl::fromLocalFile(mSourceInfo.mIsDirectory ?
0402                                         mFolderToCreate.absoluteFilePath() :
0403                                         mDestination.absolutePath()));
0404 #if KIO_VERSION > QT_VERSION_CHECK(5, 98, 0)
0405     auto *delegate = KIO::createDefaultJobUiDelegate(KIO::JobUiDelegate::AutoHandlingEnabled, this);
0406 #else
0407     auto *delegate = new KIO::JobUiDelegate(KIO::JobUiDelegate::AutoHandlingEnabled, this);
0408 #endif
0409     job->setUiDelegate(delegate);
0410     job->start();
0411 }
0412 
0413 void RestoreDialog::moveFolder() {
0414     if(!mRestorationPath.endsWith(cKupTempRestoreFolder)) {
0415         mUI->mRestorationStackWidget->setCurrentIndex(2);
0416         mUI->mCloseButton->show();
0417         qCDebug(KUPFILEDIGGER) << "Overall restore operation completed.";
0418         return;
0419     }
0420     QUrl lSourceUrl = QUrl::fromLocalFile(mRestorationPath);
0421     QUrl lDestinationUrl = QUrl::fromLocalFile(mRestorationPath.section(QDir::separator(), 0, -2));
0422     KIO::CopyJob *lFolderMoveJob = KIO::moveAs(lSourceUrl, lDestinationUrl, KIO::Overwrite | KIO::HideProgressInfo);
0423     connect(lFolderMoveJob, SIGNAL(result(KJob*)), SLOT(folderMoveCompleted(KJob*)));
0424     mJobTracker->registerJob(lFolderMoveJob);
0425     QWidget *lProgressWidget = mJobTracker->widget(lFolderMoveJob);
0426     mUI->mRestoreProgressLayout->insertWidget(1, lProgressWidget);
0427     lProgressWidget->show();
0428     qCDebug(KUPFILEDIGGER) << "Starting folder move job from: " << lSourceUrl << ", to: " << lDestinationUrl;
0429     lFolderMoveJob->start();
0430 }
0431 
0432 void RestoreDialog::folderMoveCompleted(KJob *pJob) {
0433     qCDebug(KUPFILEDIGGER) << "Folder move job completed. Exit status: " << pJob->error();
0434     mUI->mCloseButton->show();
0435     if(pJob->error() != 0) {
0436         mUI->mRestorationOutput->setPlainText(pJob->errorText());
0437         mUI->mRestorationStackWidget->setCurrentIndex(1);
0438     } else {
0439         qCDebug(KUPFILEDIGGER) << "Overall restore operation completed.";
0440         mUI->mRestorationStackWidget->setCurrentIndex(2);
0441     }
0442 }