File indexing completed on 2024-05-19 05:05:51
0001 /*************************************************************************** 0002 * SPDX-License-Identifier: GPL-2.0-or-later 0003 * * 0004 * SPDX-FileCopyrightText: 2004-2019 Thomas Fischer <fischer@unix-ag.uni-kl.de> 0005 * * 0006 * This program is free software; you can redistribute it and/or modify * 0007 * it under the terms of the GNU General Public License as published by * 0008 * the Free Software Foundation; either version 2 of the License, or * 0009 * (at your option) any later version. * 0010 * * 0011 * This program is distributed in the hope that it will be useful, * 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0014 * GNU General Public License for more details. * 0015 * * 0016 * You should have received a copy of the GNU General Public License * 0017 * along with this program; if not, see <https://www.gnu.org/licenses/>. * 0018 ***************************************************************************/ 0019 0020 #include "mdiwidget.h" 0021 0022 #include <QVector> 0023 #include <QPair> 0024 #include <QLabel> 0025 #include <QApplication> 0026 #include <QLayout> 0027 #include <QTreeView> 0028 #include <QAbstractItemModel> 0029 #include <QSortFilterProxyModel> 0030 #include <QHeaderView> 0031 #include <QPushButton> 0032 #include <QIcon> 0033 0034 #include <KParts/Part> 0035 #include <KParts/ReadOnlyPart> 0036 #include <KConfigGroup> 0037 #include <KSharedConfig> 0038 #include <KLocalizedString> 0039 #include <KMessageBox> 0040 0041 #include <KBibTeX> 0042 #include <file/PartWidget> 0043 #include "logging_program.h" 0044 0045 class LRUItemModel : public QAbstractItemModel 0046 { 0047 Q_OBJECT 0048 0049 public: 0050 static const int URLRole = Qt::UserRole + 235; 0051 static const int SortRole = Qt::UserRole + 236; 0052 0053 LRUItemModel(QObject *parent = nullptr) 0054 : QAbstractItemModel(parent) 0055 { 0056 /// nothing 0057 } 0058 0059 void reset() { 0060 beginResetModel(); 0061 endResetModel(); 0062 } 0063 0064 int columnCount(const QModelIndex &) const override { 0065 return 3; 0066 } 0067 0068 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override { 0069 OpenFileInfoManager::OpenFileInfoList ofiList = OpenFileInfoManager::instance().filteredItems(OpenFileInfo::StatusFlag::RecentlyUsed); 0070 if (index.row() < ofiList.count()) { 0071 OpenFileInfo *ofiItem = ofiList[index.row()]; 0072 if (index.column() == 0) { 0073 if (role == Qt::DisplayRole || role == SortRole) { 0074 const QUrl url = ofiItem->url(); 0075 const QString fileName = url.fileName(); 0076 return fileName.isEmpty() ? squeeze_text(url.url(QUrl::PreferLocalFile), 32) : fileName; 0077 } else if (role == Qt::DecorationRole) 0078 return QIcon::fromTheme(ofiItem->mimeType().replace(QLatin1Char('/'), QLatin1Char('-'))); 0079 else if (role == Qt::ToolTipRole) 0080 return squeeze_text(ofiItem->url().url(QUrl::PreferLocalFile), 64); 0081 } else if (index.column() == 1) { 0082 if (role == Qt::DisplayRole || role == Qt::ToolTipRole) 0083 return ofiItem->lastAccess().toString(Qt::TextDate); 0084 else if (role == SortRole) 0085 return ofiItem->lastAccess().toSecsSinceEpoch(); 0086 } else if (index.column() == 2) { 0087 if (role == Qt::DisplayRole || role == Qt::ToolTipRole || role == SortRole) 0088 return ofiItem->url().url(QUrl::PreferLocalFile); 0089 } 0090 if (role == URLRole) 0091 return ofiItem->url(); 0092 } 0093 0094 return QVariant(); 0095 } 0096 0097 QVariant headerData(int section, Qt::Orientation, int role = Qt::DisplayRole) const override { 0098 if (role != Qt::DisplayRole || section > 2) return QVariant(); 0099 else if (section == 0) return i18n("Filename"); 0100 else if (section == 1) return i18n("Date/time of last use"); 0101 else return i18n("Full filename"); 0102 } 0103 0104 QModelIndex index(int row, int column, const QModelIndex &) const override { 0105 return createIndex(row, column, static_cast<quintptr>(row)); 0106 } 0107 0108 QModelIndex parent(const QModelIndex &) const override { 0109 return QModelIndex(); 0110 } 0111 0112 int rowCount(const QModelIndex &parent = QModelIndex()) const override { 0113 if (parent == QModelIndex()) 0114 return OpenFileInfoManager::instance().filteredItems(OpenFileInfo::StatusFlag::RecentlyUsed).count(); 0115 else 0116 return 0; 0117 } 0118 }; 0119 0120 class MDIWidget::MDIWidgetPrivate 0121 { 0122 private: 0123 QTreeView *listLRU; 0124 LRUItemModel *modelLRU; 0125 QVector<QWidget *> welcomeWidgets; 0126 0127 static const QString configGroupName; 0128 static const QString configHeaderState; 0129 0130 void createWelcomeWidget() { 0131 welcomeWidget = new QWidget(p); 0132 QGridLayout *layout = new QGridLayout(welcomeWidget); 0133 layout->setRowStretch(0, 1); 0134 layout->setRowStretch(1, 0); 0135 layout->setRowStretch(2, 0); 0136 layout->setRowStretch(3, 0); 0137 layout->setRowStretch(4, 10); 0138 layout->setRowStretch(5, 1); 0139 layout->setColumnStretch(0, 1); 0140 layout->setColumnStretch(1, 10); 0141 layout->setColumnStretch(2, 1); 0142 layout->setColumnStretch(3, 0); 0143 layout->setColumnStretch(4, 1); 0144 layout->setColumnStretch(5, 10); 0145 layout->setColumnStretch(6, 1); 0146 0147 QLabel *label = new QLabel(i18n("<qt>Welcome to <b>KBibTeX</b></qt>"), welcomeWidget); 0148 layout->addWidget(label, 1, 2, 1, 3, Qt::AlignHCenter | Qt::AlignTop); 0149 0150 QPushButton *buttonNew = new QPushButton(QIcon::fromTheme(QStringLiteral("document-new")), i18n("New"), welcomeWidget); 0151 layout->addWidget(buttonNew, 2, 2, 1, 1, Qt::AlignLeft | Qt::AlignBottom); 0152 connect(buttonNew, &QPushButton::clicked, p, &MDIWidget::documentNew); 0153 0154 QPushButton *buttonOpen = new QPushButton(QIcon::fromTheme(QStringLiteral("document-open")), i18n("Open..."), welcomeWidget); 0155 layout->addWidget(buttonOpen, 2, 4, 1, 1, Qt::AlignRight | Qt::AlignBottom); 0156 connect(buttonOpen, &QPushButton::clicked, p, &MDIWidget::documentOpen); 0157 0158 label = new QLabel(i18n("List of recently used files:"), welcomeWidget); 0159 layout->addWidget(label, 3, 1, 1, 5, Qt::AlignLeft | Qt::AlignBottom); 0160 listLRU = new QTreeView(p); 0161 listLRU->setRootIsDecorated(false); 0162 listLRU->setSortingEnabled(true); 0163 listLRU->header()->setSectionResizeMode(QHeaderView::ResizeToContents); 0164 layout->addWidget(listLRU, 4, 1, 1, 5); 0165 connect(listLRU, &QTreeView::activated, p, &MDIWidget::slotOpenLRU); 0166 label->setBuddy(listLRU); 0167 0168 p->addWidget(welcomeWidget); 0169 } 0170 0171 void saveColumnsState() { 0172 QByteArray headerState = listLRU->header()->saveState(); 0173 KConfigGroup configGroup(config, configGroupName); 0174 configGroup.writeEntry(configHeaderState, headerState); 0175 config->sync(); 0176 } 0177 0178 void restoreColumnsState() { 0179 KConfigGroup configGroup(config, configGroupName); 0180 QByteArray headerState = configGroup.readEntry(configHeaderState, QByteArray()); 0181 if (!headerState.isEmpty()) 0182 listLRU->header()->restoreState(headerState); 0183 } 0184 0185 public: 0186 MDIWidget *p; 0187 OpenFileInfo *currentFile; 0188 QWidget *welcomeWidget; 0189 0190 KSharedConfigPtr config; 0191 0192 MDIWidgetPrivate(MDIWidget *parent) 0193 : p(parent), currentFile(nullptr), config(KSharedConfig::openConfig(QStringLiteral("kbibtexrc"))) { 0194 createWelcomeWidget(); 0195 0196 modelLRU = new LRUItemModel(listLRU); 0197 QSortFilterProxyModel *sfpm = new QSortFilterProxyModel(listLRU); 0198 sfpm->setSourceModel(modelLRU); 0199 sfpm->setSortRole(LRUItemModel::SortRole); 0200 listLRU->setModel(sfpm); 0201 0202 restoreColumnsState(); 0203 } 0204 0205 ~MDIWidgetPrivate() { 0206 saveColumnsState(); 0207 delete welcomeWidget; 0208 } 0209 0210 void addToMapper(OpenFileInfo *openFileInfo) { 0211 KParts::ReadOnlyPart *part = openFileInfo->part(p); 0212 connect(part, static_cast<void(KParts::ReadOnlyPart::*)()>(&KParts::ReadOnlyPart::completed), p, [this, openFileInfo]() { 0213 loadingFinished(openFileInfo); 0214 }); 0215 } 0216 0217 void updateLRU() { 0218 modelLRU->reset(); 0219 } 0220 0221 void loadingFinished(OpenFileInfo *ofi) 0222 { 0223 const QUrl oldUrl = ofi->url(); 0224 const QUrl newUrl = ofi->part(p)->url(); 0225 0226 if (oldUrl != newUrl) { 0227 qCDebug(LOG_KBIBTEX_PROGRAM) << "Url changed from " << oldUrl.url(QUrl::PreferLocalFile) << " to " << newUrl.url(QUrl::PreferLocalFile); 0228 OpenFileInfoManager::instance().changeUrl(ofi, newUrl); 0229 0230 /// completely opened or saved files should be marked as "recently used" 0231 ofi->addFlags(OpenFileInfo::StatusFlag::RecentlyUsed); 0232 0233 /// Instead of an 'emit' ... 0234 #if QT_VERSION < QT_VERSION_CHECK(6, 5, 0) 0235 QMetaObject::invokeMethod(p, "setCaption", Qt::DirectConnection, QGenericReturnArgument(), Q_ARG(QString, QString(QStringLiteral("%1 [%2]")).arg(ofi->shortCaption(), squeeze_text(ofi->fullCaption(), 64)))); 0236 #else // QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) 0237 QMetaObject::invokeMethod(p, "setCaption", Qt::DirectConnection, QMetaMethodReturnArgument(), Q_ARG(QString, QString(QStringLiteral("%1 [%2]")).arg(ofi->shortCaption(), squeeze_text(ofi->fullCaption(), 64)))); 0238 #endif // 0239 } 0240 } 0241 }; 0242 0243 const QString MDIWidget::MDIWidgetPrivate::configGroupName = QStringLiteral("WelcomeWidget"); 0244 const QString MDIWidget::MDIWidgetPrivate::configHeaderState = QStringLiteral("LRUlistHeaderState"); 0245 0246 MDIWidget::MDIWidget(QWidget *parent) 0247 : QStackedWidget(parent), d(new MDIWidgetPrivate(this)) 0248 { 0249 connect(&OpenFileInfoManager::instance(), &OpenFileInfoManager::flagsChanged, this, &MDIWidget::slotStatusFlagsChanged); 0250 } 0251 0252 MDIWidget::~MDIWidget() 0253 { 0254 delete d; 0255 } 0256 0257 void MDIWidget::setFile(OpenFileInfo *openFileInfo, const KPluginMetaData &service) 0258 { 0259 KParts::Part *part = openFileInfo == nullptr ? nullptr : openFileInfo->part(this, service); 0260 /// 'part' will only be NULL if no OpenFileInfo object was given, 0261 /// or if the OpenFileInfo could not locate a KPart 0262 QWidget *widget = d->welcomeWidget; ///< by default use Welcome widget 0263 if (part != nullptr) { 0264 /// KPart object was located, use its object (instead of Welcome widget) 0265 widget = part->widget(); 0266 } else if (openFileInfo != nullptr) { 0267 /// A valid OpenFileInfo was given, but no KPart could be located 0268 0269 OpenFileInfoManager::instance().close(openFileInfo); // FIXME does not close correctly if file is new 0270 const QString filename = openFileInfo->url().fileName(); 0271 if (filename.isEmpty()) 0272 KMessageBox::error(this, i18n("No part available for file of mime type '%1'.", openFileInfo->mimeType()), i18n("No Part Available")); 0273 else 0274 KMessageBox::error(this, i18n("No part available for file '%1'.", filename), i18n("No Part Available")); 0275 return; 0276 } 0277 0278 FileView *oldEditor = nullptr; 0279 bool hasChanged = true; 0280 if (indexOf(widget) >= 0) { 0281 /// Chosen widget is already known (Welcome widget or a previously used KPart) 0282 0283 /// In case previous (still current) widget was a KBibTeX Part, remember its editor 0284 PartWidget *currentPartWidget = qobject_cast<PartWidget *>(currentWidget()); 0285 oldEditor = currentPartWidget == nullptr ? nullptr : currentPartWidget->fileView(); 0286 /// Record if set widget is different from previous (still current) widget 0287 hasChanged = widget != currentWidget(); 0288 } else if (openFileInfo != nullptr) { 0289 /// Widget was not known previously, but a valid (new?) OpenFileInfo was given 0290 addWidget(widget); 0291 d->addToMapper(openFileInfo); 0292 } 0293 setCurrentWidget(widget); 0294 d->currentFile = openFileInfo; 0295 0296 if (hasChanged) { 0297 /// This signal gets forwarded to KParts::MainWindow::createGUI(KParts::Part*) 0298 Q_EMIT activePartChanged(part); 0299 /// If new widget comes from a KBibTeX Part, retrieve its editor 0300 PartWidget *newPartWidget = qobject_cast<PartWidget *>(widget); 0301 FileView *newEditor = newPartWidget == nullptr ? nullptr : newPartWidget->fileView(); 0302 Q_EMIT documentSwitched(oldEditor, newEditor); 0303 } 0304 0305 /// Notify main window about a change of current file, 0306 /// so that the title may contain the current file's URL. 0307 /// This signal will be connected to KMainWindow::setCaption. 0308 if (openFileInfo != nullptr) { 0309 QUrl url = openFileInfo->url(); 0310 if (url.isValid()) 0311 Q_EMIT setCaption(QString(QStringLiteral("%1 [%2]")).arg(openFileInfo->shortCaption(), squeeze_text(openFileInfo->fullCaption(), 64))); 0312 else 0313 Q_EMIT setCaption(openFileInfo->shortCaption()); 0314 } else 0315 Q_EMIT setCaption(QString()); 0316 } 0317 0318 FileView *MDIWidget::fileView() 0319 { 0320 OpenFileInfo *ofi = OpenFileInfoManager::instance().currentFile(); 0321 return qobject_cast<PartWidget *>(ofi->part(this)->widget())->fileView(); 0322 } 0323 0324 OpenFileInfo *MDIWidget::currentFile() 0325 { 0326 return d->currentFile; 0327 } 0328 0329 void MDIWidget::slotStatusFlagsChanged(OpenFileInfo::StatusFlags statusFlags) 0330 { 0331 if (statusFlags.testFlag(OpenFileInfo::StatusFlag::RecentlyUsed)) 0332 d->updateLRU(); 0333 } 0334 0335 void MDIWidget::slotOpenLRU(const QModelIndex &index) 0336 { 0337 QUrl url = index.data(LRUItemModel::URLRole).toUrl(); 0338 if (url.isValid()) 0339 Q_EMIT documentOpenURL(url); 0340 } 0341 0342 #include "mdiwidget.moc"