File indexing completed on 2024-05-12 16:01:45
0001 /* This file is part of the KDE project 0002 SPDX-FileCopyrightText: 2012 Boudewijn Rempt <boud@valdyas.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "KisAutoSaveRecoveryDialog.h" 0008 0009 #include <KoStore.h> 0010 0011 #include <kwidgetitemdelegate.h> 0012 #include <klocalizedstring.h> 0013 0014 #include <QVBoxLayout> 0015 #include <QHBoxLayout> 0016 #include <QListView> 0017 #include <QAbstractTableModel> 0018 #include <QLabel> 0019 #include <QDir> 0020 #include <QFileInfo> 0021 #include <QDateTime> 0022 #include <QImage> 0023 #include <QPixmap> 0024 #include <QHeaderView> 0025 #include <QStyledItemDelegate> 0026 #include <QStandardPaths> 0027 #include <QPainter> 0028 #include <QCheckBox> 0029 #include <kis_debug.h> 0030 0031 0032 struct FileItem { 0033 0034 FileItem() : checked(true) {} 0035 0036 QImage thumbnail; 0037 QString name; 0038 QString date; 0039 bool checked; 0040 }; 0041 0042 class FileItemDelegate : public KWidgetItemDelegate 0043 { 0044 public: 0045 0046 FileItemDelegate(QAbstractItemView *itemView, KisAutoSaveRecoveryDialog *dlg) 0047 : KWidgetItemDelegate(itemView, dlg) 0048 , m_parent(dlg) 0049 { 0050 } 0051 0052 QList<QWidget*> createItemWidgets(const QModelIndex& index) const override 0053 { 0054 // a lump of coal and a piece of elastic will get you through the world 0055 QWidget *page = new QWidget; 0056 QHBoxLayout *layout = new QHBoxLayout(page); 0057 0058 QCheckBox *checkBox = new QCheckBox; 0059 checkBox->setProperty("fileitem", index.data()); 0060 0061 connect(checkBox, SIGNAL(toggled(bool)), m_parent, SLOT(toggleFileItem(bool))); 0062 QLabel *thumbnail = new QLabel; 0063 QLabel *filename = new QLabel; 0064 QLabel *dateModified = new QLabel; 0065 0066 layout->addWidget(checkBox); 0067 layout->addWidget(thumbnail); 0068 layout->addWidget(filename); 0069 layout->addWidget(dateModified); 0070 0071 page->setFixedSize(600, 200); 0072 0073 return QList<QWidget*>() << page; 0074 } 0075 0076 void updateItemWidgets(const QList<QWidget*> widgets, 0077 const QStyleOptionViewItem &option, 0078 const QPersistentModelIndex &index) const override 0079 { 0080 FileItem *fileItem = (FileItem*)index.data().value<void*>(); 0081 0082 QWidget* page= widgets[0]; 0083 QHBoxLayout* layout = qobject_cast<QHBoxLayout*>(page->layout()); 0084 QCheckBox *checkBox = qobject_cast<QCheckBox*>(layout->itemAt(0)->widget()); 0085 QLabel *thumbnail = qobject_cast<QLabel*>(layout->itemAt(1)->widget()); 0086 QLabel *filename = qobject_cast<QLabel*>(layout->itemAt(2)->widget()); 0087 QLabel *modified = qobject_cast<QLabel*>(layout->itemAt(3)->widget()); 0088 0089 checkBox->setChecked(fileItem->checked); 0090 thumbnail->setPixmap(QPixmap::fromImage(fileItem->thumbnail)); 0091 filename->setText(fileItem->name); 0092 modified->setText(fileItem->date); 0093 0094 // move the page _up_ otherwise it will draw relative to the actual position 0095 page->setGeometry(option.rect.translated(0, -option.rect.y())); 0096 } 0097 0098 void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &/*index*/) const override 0099 { 0100 //paint background for selected or hovered item 0101 QStyleOptionViewItem opt = option; 0102 itemView()->style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, 0); 0103 } 0104 0105 QSize sizeHint(const QStyleOptionViewItem&, const QModelIndex&) const override 0106 { 0107 return QSize(600, 200); 0108 } 0109 0110 0111 KisAutoSaveRecoveryDialog *m_parent; 0112 }; 0113 0114 class KisAutoSaveRecoveryDialog::FileItemModel : public QAbstractListModel 0115 { 0116 public: 0117 FileItemModel(QList<FileItem*> fileItems, QObject *parent) 0118 : QAbstractListModel(parent) 0119 , m_fileItems(fileItems){} 0120 0121 ~FileItemModel() override 0122 { 0123 qDeleteAll(m_fileItems); 0124 m_fileItems.clear(); 0125 } 0126 0127 int rowCount(const QModelIndex &/*parent*/) const override { return m_fileItems.size(); } 0128 0129 Qt::ItemFlags flags(const QModelIndex& /*index*/) const override 0130 { 0131 Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; 0132 return flags; 0133 } 0134 0135 QVariant data(const QModelIndex& index, int role) const override 0136 { 0137 if (index.isValid() && index.row() < m_fileItems.size()) { 0138 0139 FileItem *item = m_fileItems.at(index.row()); 0140 0141 switch (role) { 0142 case Qt::DisplayRole: 0143 { 0144 return QVariant::fromValue<void*>((void*)item); 0145 } 0146 case Qt::SizeHintRole: 0147 return QSize(600, 200); 0148 } 0149 } 0150 return QVariant(); 0151 } 0152 0153 bool setData(const QModelIndex& index, const QVariant& /*value*/, int role) override 0154 { 0155 if (index.isValid() && index.row() < m_fileItems.size()) { 0156 if (role == Qt::CheckStateRole) { 0157 m_fileItems.at(index.row())->checked = !m_fileItems.at(index.row())->checked; 0158 return true; 0159 } 0160 } 0161 return false; 0162 } 0163 QList<FileItem *> m_fileItems; 0164 }; 0165 0166 KisAutoSaveRecoveryDialog::KisAutoSaveRecoveryDialog(const QStringList &filenames, QWidget *parent) : 0167 KoDialog(parent) 0168 { 0169 setCaption(i18nc("@title:window", "Recover Files")); 0170 setButtons( KoDialog::Ok | KoDialog::Cancel | KoDialog::User1 ); 0171 setButtonText(KoDialog::User1, i18n("Discard All")); 0172 setMinimumSize(650, 500); 0173 QWidget *page = new QWidget(this); 0174 QVBoxLayout *layout = new QVBoxLayout(page); 0175 if (filenames.size() == 1) { 0176 layout->addWidget(new QLabel(i18n("The following autosave file can be recovered:"))); 0177 } 0178 else { 0179 layout->addWidget(new QLabel(i18n("The following autosave files can be recovered:"))); 0180 } 0181 0182 m_listView = new QListView(); 0183 m_listView->setAcceptDrops(false); 0184 KWidgetItemDelegate *delegate = new FileItemDelegate(m_listView, this); 0185 m_listView->setItemDelegate(delegate); 0186 0187 QList<FileItem*> fileItems; 0188 Q_FOREACH (const QString &filename, filenames) { 0189 0190 FileItem *file = new FileItem(); 0191 file->name = filename; 0192 0193 QString path = autoSaveLocation() + "/" + filename; 0194 // get thumbnail -- almost all Krita-supported formats save a thumbnail 0195 KoStore* store = KoStore::createStore(path, KoStore::Read); 0196 0197 if (store) { 0198 QString thumbnailPath = QLatin1String("Thumbnails/thumbnail.png"); 0199 QString previewPath = QLatin1String("preview.png"); 0200 bool thumbnailExists = store->hasFile(thumbnailPath); 0201 bool previewExists = store->hasFile(previewPath); 0202 QString pathToUse = thumbnailExists ? thumbnailPath : (previewExists ? previewPath : ""); 0203 0204 if (!pathToUse.isEmpty() && store->open(pathToUse)) { 0205 // Hooray! No long delay for the user... 0206 QByteArray bytes = store->read(store->size()); 0207 store->close(); 0208 QImage img; 0209 img.loadFromData(bytes); 0210 file->thumbnail = img.scaled(QSize(200,200), Qt::KeepAspectRatio, Qt::SmoothTransformation); 0211 } 0212 0213 delete store; 0214 } 0215 0216 // get the date 0217 QDateTime date = QFileInfo(path).lastModified(); 0218 file->date = "(" + date.toString(Qt::LocalDate) + ")"; 0219 0220 fileItems.append(file); 0221 } 0222 0223 m_model = new FileItemModel(fileItems, m_listView); 0224 m_listView->setModel(m_model); 0225 layout->addWidget(m_listView); 0226 layout->addWidget(new QLabel(i18n("If you select Cancel, all recoverable files will be kept.\nIf you press OK, selected files will be recovered, the unselected files discarded."))); 0227 setMainWidget(page); 0228 0229 setAttribute(Qt::WA_DeleteOnClose, false); 0230 connect( this, SIGNAL(user1Clicked()), this, SLOT(slotDeleteAll()) ); 0231 } 0232 0233 KisAutoSaveRecoveryDialog::~KisAutoSaveRecoveryDialog() 0234 { 0235 delete m_listView->itemDelegate(); 0236 delete m_model; 0237 delete m_listView; 0238 } 0239 0240 void KisAutoSaveRecoveryDialog::slotDeleteAll() 0241 { 0242 foreach(FileItem* fileItem, m_model->m_fileItems) { 0243 fileItem->checked = false; 0244 } 0245 accept(); 0246 } 0247 0248 QStringList KisAutoSaveRecoveryDialog::recoverableFiles() 0249 { 0250 QStringList files; 0251 Q_FOREACH (FileItem* fileItem, m_model->m_fileItems) { 0252 if (fileItem->checked) { 0253 files << fileItem->name; 0254 } 0255 } 0256 return files; 0257 } 0258 0259 QString KisAutoSaveRecoveryDialog::autoSaveLocation() 0260 { 0261 #if defined(Q_OS_WIN) 0262 // On Windows, use the temp location (https://bugs.kde.org/show_bug.cgi?id=314921) 0263 return QDir::tempPath(); 0264 #elif defined(Q_OS_ANDROID) 0265 QString path = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation).append("/krita-backup"); 0266 if (!QDir(path).exists()) { 0267 QDir().mkpath(path); 0268 } 0269 return path; 0270 #else 0271 // On Linux, use a temp file in $HOME then. Mark it with the pid so two instances don't overwrite each other's 0272 // autosave file 0273 return QDir::homePath(); 0274 #endif 0275 } 0276 0277 void KisAutoSaveRecoveryDialog::toggleFileItem(bool toggle) 0278 { 0279 // I've made better man from a piece of putty and matchstick! 0280 QVariant v = sender()->property("fileitem") ; 0281 if (v.isValid()) { 0282 FileItem *fileItem = (FileItem*)v.value<void*>(); 0283 fileItem->checked = toggle; 0284 } 0285 }