diff --git a/src/program/docklets/zoterobrowser.cpp b/src/program/docklets/zoterobrowser.cpp index e42e602f..bb393438 100644 --- a/src/program/docklets/zoterobrowser.cpp +++ b/src/program/docklets/zoterobrowser.cpp @@ -1,442 +1,440 @@ /*************************************************************************** * Copyright (C) 2004-2017 by Thomas Fischer * * * * 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. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see . * ***************************************************************************/ #include "zoterobrowser.h" #include #include #include #include #include #include #include #include #include #include #include #include -#include -#include #include #include #include "element.h" #include "searchresults.h" #include "zotero/collectionmodel.h" #include "zotero/collection.h" #include "zotero/items.h" #include "zotero/groups.h" #include "zotero/tags.h" #include "zotero/tagmodel.h" #include "zotero/api.h" #include "zotero/oauthwizard.h" using KWallet::Wallet; class ZoteroBrowser::Private { private: ZoteroBrowser *p; public: Zotero::Items *items; Zotero::Groups *groups; Zotero::Tags *tags; Zotero::TagModel *tagModel; Zotero::Collection *collection; Zotero::CollectionModel *collectionModel; QSharedPointer api; bool needToApplyCredentials; SearchResults *searchResults; QTabWidget *tabWidget; QTreeView *collectionBrowser; QListView *tagBrowser; KLineEdit *lineEditNumericUserId; KLineEdit *lineEditApiKey; QRadioButton *radioPersonalLibrary; QRadioButton *radioGroupLibrary; bool comboBoxGroupListInitialized; KComboBox *comboBoxGroupList; QCursor nonBusyCursor; Wallet *wallet; static const QString walletFolderOAuth, walletEntryKBibTeXZotero, walletKeyZoteroId, walletKeyZoteroApiKey; Private(SearchResults *sr, ZoteroBrowser *parent) : p(parent), items(nullptr), groups(nullptr), tags(nullptr), tagModel(nullptr), collection(nullptr), collectionModel(nullptr), needToApplyCredentials(true), searchResults(sr), comboBoxGroupListInitialized(false), nonBusyCursor(p->cursor()), wallet(nullptr) { setupGUI(); } ~Private() { if (wallet != nullptr) delete wallet; if (items != nullptr) delete items; if (groups != nullptr) delete groups; if (tags != nullptr) delete tags; if (tagModel != nullptr) delete tagModel; if (collection != nullptr) delete collection; if (collectionModel != nullptr) delete collectionModel; api.clear(); } void setupGUI() { QBoxLayout *layout = new QVBoxLayout(p); tabWidget = new QTabWidget(p); layout->addWidget(tabWidget); QWidget *container = new QWidget(tabWidget); tabWidget->addTab(container, QIcon::fromTheme(QStringLiteral("preferences-web-browser-identification")), i18n("Library")); connect(tabWidget, &QTabWidget::currentChanged, p, &ZoteroBrowser::tabChanged); QBoxLayout *containerLayout = new QVBoxLayout(container); /// Personal or Group Library QGridLayout *gridLayout = new QGridLayout(); containerLayout->addLayout(gridLayout); gridLayout->setMargin(0); gridLayout->setColumnMinimumWidth(0, 16); // TODO determine size of a radio button radioPersonalLibrary = new QRadioButton(i18n("Personal library"), container); gridLayout->addWidget(radioPersonalLibrary, 0, 0, 1, 2); radioGroupLibrary = new QRadioButton(i18n("Group library"), container); gridLayout->addWidget(radioGroupLibrary, 1, 0, 1, 2); comboBoxGroupList = new KComboBox(false, container); gridLayout->addWidget(comboBoxGroupList, 2, 1, 1, 1); QSizePolicy sizePolicy = comboBoxGroupList->sizePolicy(); sizePolicy.setHorizontalPolicy(QSizePolicy::MinimumExpanding); comboBoxGroupList->setSizePolicy(sizePolicy); radioPersonalLibrary->setChecked(true); comboBoxGroupList->setEnabled(false); comboBoxGroupList->addItem(i18n("No groups available")); connect(radioGroupLibrary, &QRadioButton::toggled, p, &ZoteroBrowser::radioButtonsToggled); connect(radioPersonalLibrary, &QRadioButton::toggled, p, &ZoteroBrowser::radioButtonsToggled); connect(comboBoxGroupList, static_cast(&QComboBox::currentIndexChanged), p, &ZoteroBrowser::groupListChanged); containerLayout->addStretch(10); /// Credentials QFormLayout *containerForm = new QFormLayout(); containerLayout->addLayout(containerForm, 1); containerForm->setMargin(0); lineEditNumericUserId = new KLineEdit(container); lineEditNumericUserId->setSizePolicy(sizePolicy); lineEditNumericUserId->setReadOnly(true); containerForm->addRow(i18n("Numeric user id:"), lineEditNumericUserId); connect(lineEditNumericUserId, &KLineEdit::textChanged, p, &ZoteroBrowser::invalidateGroupList); lineEditApiKey = new KLineEdit(container); lineEditApiKey->setSizePolicy(sizePolicy); lineEditApiKey->setReadOnly(true); containerForm->addRow(i18n("API key:"), lineEditApiKey); connect(lineEditApiKey, &KLineEdit::textChanged, p, &ZoteroBrowser::invalidateGroupList); QBoxLayout *containerButtonLayout = new QHBoxLayout(); containerLayout->addLayout(containerButtonLayout, 0); containerButtonLayout->setMargin(0); QPushButton *buttonGetOAuthCredentials = new QPushButton(QIcon::fromTheme(QStringLiteral("preferences-web-browser-identification")), i18n("Get New Credentials"), container); containerButtonLayout->addWidget(buttonGetOAuthCredentials, 0); connect(buttonGetOAuthCredentials, &QPushButton::clicked, p, &ZoteroBrowser::getOAuthCredentials); containerButtonLayout->addStretch(1); /// Collection browser collectionBrowser = new QTreeView(tabWidget); tabWidget->addTab(collectionBrowser, QIcon::fromTheme(QStringLiteral("folder-yellow")), i18n("Collections")); collectionBrowser->setHeaderHidden(true); collectionBrowser->setExpandsOnDoubleClick(false); connect(collectionBrowser, &QTreeView::doubleClicked, p, &ZoteroBrowser::collectionDoubleClicked); /// Tag browser tagBrowser = new QListView(tabWidget); tabWidget->addTab(tagBrowser, QIcon::fromTheme(QStringLiteral("mail-tagged")), i18n("Tags")); connect(tagBrowser, &QListView::doubleClicked, p, &ZoteroBrowser::tagDoubleClicked); } void queueReadOAuthCredentials() { if (wallet != nullptr && wallet->isOpen()) p->readOAuthCredentials(true); else { /// Wallet is closed or not initialized if (wallet != nullptr) /// Delete existing but closed wallet, will be replaced by new, open wallet soon delete wallet; p->setEnabled(false); p->setCursor(Qt::WaitCursor); wallet = Wallet::openWallet(Wallet::NetworkWallet(), p->winId(), Wallet::Asynchronous); connect(wallet, &Wallet::walletOpened, p, &ZoteroBrowser::readOAuthCredentials); } } void queueWriteOAuthCredentials() { if (wallet != nullptr && wallet->isOpen()) p->writeOAuthCredentials(true); else { /// Wallet is closed or not initialized if (wallet != nullptr) /// Delete existing but closed wallet, will be replaced by new, open wallet soon delete wallet; p->setEnabled(false); p->setCursor(Qt::WaitCursor); wallet = Wallet::openWallet(Wallet::NetworkWallet(), p->winId(), Wallet::Asynchronous); connect(wallet, &Wallet::walletOpened, p, &ZoteroBrowser::writeOAuthCredentials); } } }; const QString ZoteroBrowser::Private::walletFolderOAuth = QStringLiteral("OAuth"); const QString ZoteroBrowser::Private::walletEntryKBibTeXZotero = QStringLiteral("KBibTeX/Zotero"); const QString ZoteroBrowser::Private::walletKeyZoteroId = QStringLiteral("UserId"); const QString ZoteroBrowser::Private::walletKeyZoteroApiKey = QStringLiteral("ApiKey"); ZoteroBrowser::ZoteroBrowser(SearchResults *searchResults, QWidget *parent) : QWidget(parent), d(new ZoteroBrowser::Private(searchResults, this)) { /// Forece GUI update updateButtons(); radioButtonsToggled(); } ZoteroBrowser::~ZoteroBrowser() { delete d; } void ZoteroBrowser::visibiltyChanged(bool v) { if (v && d->lineEditApiKey->text().isEmpty()) /// If Zotero dock became visible and no API key is set, check KWallet for credentials d->queueReadOAuthCredentials(); } void ZoteroBrowser::modelReset() { if (!d->collection->busy() && !d->tags->busy()) { setCursor(d->nonBusyCursor); setEnabled(true); } else { setCursor(Qt::WaitCursor); setEnabled(false); } if (!d->tags->busy() && !d->collection->busy() && !(d->collection->initialized() && d->tags->initialized())) KMessageBox::information(this, i18n("KBibTeX failed to retrieve the bibliography from Zotero. Please check that the provided user id and API key are valid."), i18n("Failed to retrieve data from Zotero")); } void ZoteroBrowser::collectionDoubleClicked(const QModelIndex &index) { setCursor(Qt::WaitCursor); setEnabled(false); ///< will be re-enabled when item retrieve got finished (slot reenableWidget) const QString collectionId = index.data(Zotero::CollectionModel::CollectionIdRole).toString(); d->searchResults->clear(); d->items->retrieveItemsByCollection(collectionId); } void ZoteroBrowser::tagDoubleClicked(const QModelIndex &index) { setCursor(Qt::WaitCursor); setEnabled(false); ///< will be re-enabled when item retrieve got finished (slot reenableWidget) const QString tag = index.data(Zotero::TagModel::TagRole).toString(); d->searchResults->clear(); d->items->retrieveItemsByTag(tag); } void ZoteroBrowser::showItem(QSharedPointer e) { d->searchResults->insertElement(e); emit itemToShow(); } void ZoteroBrowser::reenableWidget() { setCursor(d->nonBusyCursor); setEnabled(true); } void ZoteroBrowser::updateButtons() { const bool validNumericIdAndApiKey = !d->lineEditNumericUserId->text().isEmpty() && !d->lineEditApiKey->text().isEmpty(); d->radioGroupLibrary->setEnabled(validNumericIdAndApiKey); d->radioPersonalLibrary->setEnabled(validNumericIdAndApiKey); d->needToApplyCredentials = true; } bool ZoteroBrowser::applyCredentials() { bool ok = false; const int userId = d->lineEditNumericUserId->text().toInt(&ok); const QString apiKey = d->lineEditApiKey->text(); if (ok && !apiKey.isEmpty()) { setCursor(Qt::WaitCursor); setEnabled(false); ok = false; int groupId = d->comboBoxGroupList->itemData(d->comboBoxGroupList->currentIndex()).toInt(&ok); if (!ok) groupId = -1; disconnect(d->tags, &Zotero::Tags::finishedLoading, this, &ZoteroBrowser::reenableWidget); disconnect(d->items, &Zotero::Items::stoppedSearch, this, &ZoteroBrowser::reenableWidget); disconnect(d->items, &Zotero::Items::foundElement, this, &ZoteroBrowser::showItem); disconnect(d->tagModel, &Zotero::TagModel::modelReset, this, &ZoteroBrowser::modelReset); disconnect(d->collectionModel, &Zotero::CollectionModel::modelReset, this, &ZoteroBrowser::modelReset); d->collection->deleteLater(); d->items->deleteLater(); d->tags->deleteLater(); d->collectionModel->deleteLater(); d->tagModel->deleteLater(); d->api.clear(); const bool makeGroupRequest = d->radioGroupLibrary->isChecked() && groupId > 0; d->api = QSharedPointer(new Zotero::API(makeGroupRequest ? Zotero::API::GroupRequest : Zotero::API::UserRequest, makeGroupRequest ? groupId : userId, d->lineEditApiKey->text(), this)); d->items = new Zotero::Items(d->api, this); d->tags = new Zotero::Tags(d->api, this); d->tagModel = new Zotero::TagModel(d->tags, this); d->tagBrowser->setModel(d->tagModel); d->collection = new Zotero::Collection(d->api, this); d->collectionModel = new Zotero::CollectionModel(d->collection, this); d->collectionBrowser->setModel(d->collectionModel); connect(d->collectionModel, &Zotero::CollectionModel::modelReset, this, &ZoteroBrowser::modelReset); connect(d->tagModel, &Zotero::TagModel::modelReset, this, &ZoteroBrowser::modelReset); connect(d->items, &Zotero::Items::foundElement, this, &ZoteroBrowser::showItem); connect(d->items, &Zotero::Items::stoppedSearch, this, &ZoteroBrowser::reenableWidget); connect(d->tags, &Zotero::Tags::finishedLoading, this, &ZoteroBrowser::reenableWidget); d->needToApplyCredentials = false; return true; } else return false; } void ZoteroBrowser::radioButtonsToggled() { d->comboBoxGroupList->setEnabled(d->comboBoxGroupListInitialized && d->comboBoxGroupList->count() > 0 && d->radioGroupLibrary->isChecked()); if (!d->comboBoxGroupListInitialized && d->radioGroupLibrary->isChecked()) retrieveGroupList(); d->needToApplyCredentials = true; } void ZoteroBrowser::groupListChanged() { d->needToApplyCredentials = true; } void ZoteroBrowser::retrieveGroupList() { bool ok = false; const int userId = d->lineEditNumericUserId->text().toInt(&ok); if (ok) { setCursor(Qt::WaitCursor); setEnabled(false); d->comboBoxGroupList->clear(); d->comboBoxGroupListInitialized = false; disconnect(d->groups, &Zotero::Groups::finishedLoading, this, &ZoteroBrowser::gotGroupList); d->groups->deleteLater(); d->api.clear(); d->api = QSharedPointer(new Zotero::API(Zotero::API::UserRequest, userId, d->lineEditApiKey->text(), this)); d->groups = new Zotero::Groups(d->api, this); connect(d->groups, &Zotero::Groups::finishedLoading, this, &ZoteroBrowser::gotGroupList); } } void ZoteroBrowser::invalidateGroupList() { d->comboBoxGroupList->clear(); d->comboBoxGroupListInitialized = false; d->comboBoxGroupList->addItem(i18n("No groups available or no permissions")); d->comboBoxGroupList->setEnabled(false); d->radioPersonalLibrary->setChecked(true); } void ZoteroBrowser::gotGroupList() { const QMap groupMap = d->groups->groups(); for (QMap::ConstIterator it = groupMap.constBegin(); it != groupMap.constEnd(); ++it) { d->comboBoxGroupList->addItem(it.value(), QVariant::fromValue(it.key())); } if (groupMap.isEmpty()) { invalidateGroupList(); } else { d->comboBoxGroupListInitialized = true; d->comboBoxGroupList->setEnabled(true); d->needToApplyCredentials = true; } reenableWidget(); } void ZoteroBrowser::getOAuthCredentials() { QPointer wizard = new Zotero::OAuthWizard(this); if (wizard->exec() && !wizard->apiKey().isEmpty() && wizard->userId() >= 0) { d->lineEditApiKey->setText(wizard->apiKey()); d->lineEditNumericUserId->setText(QString::number(wizard->userId())); d->queueWriteOAuthCredentials(); updateButtons(); retrieveGroupList(); } delete wizard; } void ZoteroBrowser::readOAuthCredentials(bool ok) { /// Do not call this slot a second time disconnect(d->wallet, &Wallet::walletOpened, this, &ZoteroBrowser::readOAuthCredentials); if (ok && (d->wallet->hasFolder(ZoteroBrowser::Private::walletFolderOAuth) || d->wallet->createFolder(ZoteroBrowser::Private::walletFolderOAuth)) && d->wallet->setFolder(ZoteroBrowser::Private::walletFolderOAuth)) { if (d->wallet->hasEntry(ZoteroBrowser::Private::walletEntryKBibTeXZotero)) { QMap map; if (d->wallet->readMap(ZoteroBrowser::Private::walletEntryKBibTeXZotero, map) == 0) { if (map.contains(ZoteroBrowser::Private::walletKeyZoteroId) && map.contains(ZoteroBrowser::Private::walletKeyZoteroApiKey)) { d->lineEditNumericUserId->setText(map.value(ZoteroBrowser::Private::walletKeyZoteroId, QString())); d->lineEditApiKey->setText(map.value(ZoteroBrowser::Private::walletKeyZoteroApiKey, QString())); updateButtons(); retrieveGroupList(); } else qWarning() << "Failed to locate Zotero Id and/or API key in KWallet"; } else qWarning() << "Failed to access Zotero data in KWallet"; } else qDebug() << "No Zotero credentials stored in KWallet"; } else qWarning() << "Accessing KWallet to sync API key did not succeed"; reenableWidget(); } void ZoteroBrowser::writeOAuthCredentials(bool ok) { disconnect(d->wallet, &Wallet::walletOpened, this, &ZoteroBrowser::writeOAuthCredentials); if (ok && (d->wallet->hasFolder(ZoteroBrowser::Private::walletFolderOAuth) || d->wallet->createFolder(ZoteroBrowser::Private::walletFolderOAuth)) && d->wallet->setFolder(ZoteroBrowser::Private::walletFolderOAuth)) { QMap map; map.insert(ZoteroBrowser::Private::walletKeyZoteroId, d->lineEditNumericUserId->text()); map.insert(ZoteroBrowser::Private::walletKeyZoteroApiKey, d->lineEditApiKey->text()); if (d->wallet->writeMap(ZoteroBrowser::Private::walletEntryKBibTeXZotero, map) != 0) qWarning() << "Writing API key to KWallet failed"; } else qWarning() << "Accessing KWallet to sync API key did not succeed"; reenableWidget(); } void ZoteroBrowser::tabChanged(int newTabIndex) { if (newTabIndex > 0 /** tabs after credential tab*/ && d->needToApplyCredentials) { const bool success = applyCredentials(); for (int i = 1; i < d->tabWidget->count(); ++i) d->tabWidget->widget(i)->setEnabled(success); } } diff --git a/src/program/mainwindow.cpp b/src/program/mainwindow.cpp index c02c45c7..92be71aa 100644 --- a/src/program/mainwindow.cpp +++ b/src/program/mainwindow.cpp @@ -1,498 +1,490 @@ /*************************************************************************** * Copyright (C) 2004-2018 by Thomas Fischer * * * * 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. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see . * ***************************************************************************/ #include "mainwindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include "kbibtex.h" #include "preferences/kbibtexpreferencesdialog.h" #include "valuelist.h" #ifdef HAVE_ZOTERO #include "zoterobrowser.h" #endif // HAVE_ZOTERO #include "statistics.h" #include "documentlist.h" #include "mdiwidget.h" #include "referencepreview.h" #include "documentpreview.h" #include "searchform.h" #include "searchresults.h" #include "elementform.h" #include "fileview.h" #include "filesettings.h" #include "xsltransform.h" #include "bibliographyservice.h" #include "bibutils.h" class KBibTeXMainWindow::KBibTeXMainWindowPrivate { private: KBibTeXMainWindow *p; public: QAction *actionClose; QDockWidget *dockDocumentList; QDockWidget *dockReferencePreview; QDockWidget *dockDocumentPreview; QDockWidget *dockValueList; #ifdef HAVE_ZOTERO QDockWidget *dockZotero; #endif // HAVE_ZOTERO QDockWidget *dockStatistics; QDockWidget *dockSearchForm; QDockWidget *dockSearchResults; QDockWidget *dockElementForm; QDockWidget *dockFileSettings; DocumentList *listDocumentList; MDIWidget *mdiWidget; ReferencePreview *referencePreview; DocumentPreview *documentPreview; FileSettings *fileSettings; ValueList *valueList; #ifdef HAVE_ZOTERO ZoteroBrowser *zotero; #endif // HAVE_ZOTERO Statistics *statistics; SearchForm *searchForm; SearchResults *searchResults; ElementForm *elementForm; QMenu *actionMenuRecentFilesMenu; KBibTeXMainWindowPrivate(KBibTeXMainWindow *parent) : p(parent) { mdiWidget = new MDIWidget(p); KActionMenu *showPanelsAction = new KActionMenu(i18n("Show Panels"), p); p->actionCollection()->addAction(QStringLiteral("settings_shown_panels"), showPanelsAction); QMenu *showPanelsMenu = new QMenu(showPanelsAction->text(), p->widget()); showPanelsAction->setMenu(showPanelsMenu); KActionMenu *actionMenuRecentFiles = new KActionMenu(QIcon::fromTheme(QStringLiteral("document-open-recent")), i18n("Recently used files"), p); p->actionCollection()->addAction(QStringLiteral("file_open_recent"), actionMenuRecentFiles); actionMenuRecentFilesMenu = new QMenu(actionMenuRecentFiles->text(), p->widget()); actionMenuRecentFiles->setMenu(actionMenuRecentFilesMenu); /** * Docklets (a.k.a. panels) will be added by default to the following * positions unless otherwise configured by the user. * - "List of Values" on the left * - "Statistics" on the left * - "List of Documents" on the left in the same tab * - "Online Search" on the left in a new tab * - "Reference Preview" on the left in the same tab * - "Search Results" on the bottom * - "Document Preview" is hidden * - "Element Editor" is hidden */ dockDocumentList = new QDockWidget(i18n("List of Documents"), p); dockDocumentList->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); p->addDockWidget(Qt::LeftDockWidgetArea, dockDocumentList); listDocumentList = new DocumentList(dockDocumentList); dockDocumentList->setWidget(listDocumentList); dockDocumentList->setObjectName(QStringLiteral("dockDocumentList")); dockDocumentList->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); connect(listDocumentList, &DocumentList::openFile, p, &KBibTeXMainWindow::openDocument); showPanelsMenu->addAction(dockDocumentList->toggleViewAction()); dockValueList = new QDockWidget(i18n("List of Values"), p); dockValueList->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea | Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); p->addDockWidget(Qt::LeftDockWidgetArea, dockValueList); valueList = new ValueList(dockValueList); dockValueList->setWidget(valueList); dockValueList->setObjectName(QStringLiteral("dockValueList")); dockValueList->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); showPanelsMenu->addAction(dockValueList->toggleViewAction()); dockStatistics = new QDockWidget(i18n("Statistics"), p); dockStatistics->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea | Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); p->addDockWidget(Qt::LeftDockWidgetArea, dockStatistics); statistics = new Statistics(dockStatistics); dockStatistics->setWidget(statistics); dockStatistics->setObjectName(QStringLiteral("dockStatistics")); dockStatistics->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); showPanelsMenu->addAction(dockStatistics->toggleViewAction()); dockSearchResults = new QDockWidget(i18n("Search Results"), p); dockSearchResults->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea | Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); p->addDockWidget(Qt::BottomDockWidgetArea, dockSearchResults); dockSearchResults->hide(); searchResults = new SearchResults(mdiWidget, dockSearchResults); dockSearchResults->setWidget(searchResults); dockSearchResults->setObjectName(QStringLiteral("dockResultsFrom")); dockSearchResults->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); showPanelsMenu->addAction(dockSearchResults->toggleViewAction()); connect(mdiWidget, &MDIWidget::documentSwitched, searchResults, &SearchResults::documentSwitched); dockSearchForm = new QDockWidget(i18n("Online Search"), p); dockSearchForm->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea | Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); p->addDockWidget(Qt::LeftDockWidgetArea, dockSearchForm); searchForm = new SearchForm(searchResults, dockSearchForm); connect(searchForm, &SearchForm::doneSearching, p, &KBibTeXMainWindow::showSearchResults); dockSearchForm->setWidget(searchForm); dockSearchForm->setObjectName(QStringLiteral("dockSearchFrom")); dockSearchForm->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); showPanelsMenu->addAction(dockSearchForm->toggleViewAction()); #ifdef HAVE_ZOTERO dockZotero = new QDockWidget(i18n("Zotero"), p); dockZotero->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea | Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); p->addDockWidget(Qt::LeftDockWidgetArea, dockZotero); zotero = new ZoteroBrowser(searchResults, dockZotero); connect(dockZotero, &QDockWidget::visibilityChanged, zotero, &ZoteroBrowser::visibiltyChanged); connect(zotero, &ZoteroBrowser::itemToShow, p, &KBibTeXMainWindow::showSearchResults); dockZotero->setWidget(zotero); dockZotero->setObjectName(QStringLiteral("dockZotero")); dockZotero->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); showPanelsMenu->addAction(dockZotero->toggleViewAction()); #endif // HAVE_ZOTERO dockReferencePreview = new QDockWidget(i18n("Reference Preview"), p); dockReferencePreview->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea | Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); p->addDockWidget(Qt::LeftDockWidgetArea, dockReferencePreview); referencePreview = new ReferencePreview(dockReferencePreview); dockReferencePreview->setWidget(referencePreview); dockReferencePreview->setObjectName(QStringLiteral("dockReferencePreview")); dockReferencePreview->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); showPanelsMenu->addAction(dockReferencePreview->toggleViewAction()); dockDocumentPreview = new QDockWidget(i18n("Document Preview"), p); dockDocumentPreview->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea | Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); p->addDockWidget(Qt::RightDockWidgetArea, dockDocumentPreview); dockDocumentPreview->hide(); documentPreview = new DocumentPreview(dockDocumentPreview); dockDocumentPreview->setWidget(documentPreview); dockDocumentPreview->setObjectName(QStringLiteral("dockDocumentPreview")); dockDocumentPreview->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); showPanelsMenu->addAction(dockDocumentPreview->toggleViewAction()); p->actionCollection()->setDefaultShortcut(dockDocumentPreview->toggleViewAction(), Qt::CTRL + Qt::SHIFT + Qt::Key_D); dockElementForm = new QDockWidget(i18n("Element Editor"), p); dockElementForm->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea | Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); p->addDockWidget(Qt::BottomDockWidgetArea, dockElementForm); dockElementForm->hide(); elementForm = new ElementForm(mdiWidget, dockElementForm); dockElementForm->setWidget(elementForm); dockElementForm->setObjectName(QStringLiteral("dockElementFrom")); dockElementForm->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); showPanelsMenu->addAction(dockElementForm->toggleViewAction()); dockFileSettings = new QDockWidget(i18n("File Settings"), p); dockFileSettings->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea | Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); p->addDockWidget(Qt::LeftDockWidgetArea, dockFileSettings); fileSettings = new FileSettings(dockFileSettings); dockFileSettings->setWidget(fileSettings); dockFileSettings->setObjectName(QStringLiteral("dockFileSettings")); dockFileSettings->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); showPanelsMenu->addAction(dockFileSettings->toggleViewAction()); p->tabifyDockWidget(dockFileSettings, dockSearchForm); #ifdef HAVE_ZOTERO p->tabifyDockWidget(dockZotero, dockSearchForm); #endif // HAVE_ZOTERO p->tabifyDockWidget(dockValueList, dockStatistics); p->tabifyDockWidget(dockStatistics, dockFileSettings); p->tabifyDockWidget(dockSearchForm, dockReferencePreview); p->tabifyDockWidget(dockFileSettings, dockDocumentList); QAction *action = p->actionCollection()->addAction(KStandardAction::New); connect(action, &QAction::triggered, p, &KBibTeXMainWindow::newDocument); action = p->actionCollection()->addAction(KStandardAction::Open); connect(action, &QAction::triggered, p, &KBibTeXMainWindow::openDocumentDialog); actionClose = p->actionCollection()->addAction(KStandardAction::Close); connect(actionClose, &QAction::triggered, p, &KBibTeXMainWindow::closeDocument); actionClose->setEnabled(false); action = p->actionCollection()->addAction(KStandardAction::Quit); connect(action, &QAction::triggered, p, &KBibTeXMainWindow::queryCloseAll); action = p->actionCollection()->addAction(KStandardAction::Preferences); connect(action, &QAction::triggered, p, &KBibTeXMainWindow::showPreferences); } ~KBibTeXMainWindowPrivate() { elementForm->deleteLater(); delete mdiWidget; // TODO other deletes } }; KBibTeXMainWindow::KBibTeXMainWindow(QWidget *parent) : KParts::MainWindow(parent, (Qt::WindowFlags)KDE_DEFAULT_WINDOWFLAGS), d(new KBibTeXMainWindowPrivate(this)) { setObjectName(QStringLiteral("KBibTeXShell")); - /* - const char mainWindowStateKey[] = "State"; - KConfigGroup group( KSharedConfig::openConfig(), "MainWindow" ); - if( !group.hasKey(mainWindowStateKey) ) - group.writeEntry( mainWindowStateKey, mainWindowState ); - */ - setXMLFile(QStringLiteral("kbibtexui.rc")); setCentralWidget(d->mdiWidget); connect(d->mdiWidget, &MDIWidget::documentSwitched, this, &KBibTeXMainWindow::documentSwitched); connect(d->mdiWidget, &MDIWidget::activePartChanged, this, &KBibTeXMainWindow::createGUI); ///< actually: KParts::MainWindow::createGUI connect(d->mdiWidget, &MDIWidget::documentNew, this, &KBibTeXMainWindow::newDocument); connect(d->mdiWidget, &MDIWidget::documentOpen, this, &KBibTeXMainWindow::openDocumentDialog); connect(d->mdiWidget, &MDIWidget::documentOpenURL, this, &KBibTeXMainWindow::openDocument); connect(&OpenFileInfoManager::instance(), &OpenFileInfoManager::currentChanged, d->mdiWidget, &MDIWidget::setFile); connect(&OpenFileInfoManager::instance(), &OpenFileInfoManager::flagsChanged, this, &KBibTeXMainWindow::documentListsChanged); connect(d->mdiWidget, &MDIWidget::setCaption, this, static_cast(&KMainWindow::setCaption)); ///< actually: KMainWindow::setCaption documentListsChanged(OpenFileInfo::RecentlyUsed); /// force initialization of menu of recently used files setupControllers(); setupGUI(KXmlGuiWindow::Create | KXmlGuiWindow::Save | KXmlGuiWindow::Keys | KXmlGuiWindow::ToolBar); setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea); setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea); setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea); setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea); setAcceptDrops(true); QTimer::singleShot(500, this, &KBibTeXMainWindow::delayed); } KBibTeXMainWindow::~KBibTeXMainWindow() { delete d; } void KBibTeXMainWindow::setupControllers() { // TODO } void KBibTeXMainWindow::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasUrls()) event->acceptProposedAction(); } void KBibTeXMainWindow::dropEvent(QDropEvent *event) { QList urlList = event->mimeData()->urls(); if (urlList.isEmpty()) { const QUrl url(event->mimeData()->text()); if (url.isValid()) urlList << url; } if (!urlList.isEmpty()) for (const QUrl &url : const_cast &>(urlList)) openDocument(url); } void KBibTeXMainWindow::newDocument() { const QString mimeType = FileInfo::mimetypeBibTeX; OpenFileInfo *openFileInfo = OpenFileInfoManager::instance().createNew(mimeType); if (openFileInfo) OpenFileInfoManager::instance().setCurrentFile(openFileInfo); else KMessageBox::error(this, i18n("Creating a new document of mime type '%1' failed as no editor component could be instantiated.", mimeType), i18n("Creating document failed")); } void KBibTeXMainWindow::openDocumentDialog() { OpenFileInfo *currFile = OpenFileInfoManager::instance().currentFile(); QUrl currFileUrl = currFile == nullptr ? QUrl() : currFile->url(); QString startDir = currFileUrl.isValid() ? QUrl(currFileUrl.url()).path() : QString(); OpenFileInfo *ofi = OpenFileInfoManager::instance().currentFile(); if (ofi != nullptr) { QUrl url = ofi->url(); if (url.isValid()) startDir = url.path(); } /// Assemble list of supported mimetypes QStringList supportedMimeTypes {QStringLiteral("text/x-bibtex"), QStringLiteral("application/x-research-info-systems"), QStringLiteral("application/xml")}; if (BibUtils::available()) { supportedMimeTypes.append(QStringLiteral("application/x-isi-export-format")); supportedMimeTypes.append(QStringLiteral("application/x-endnote-refer")); } supportedMimeTypes.append(QStringLiteral("application/pdf")); supportedMimeTypes.append(QStringLiteral("all/all")); QPointer dlg = new QFileDialog(this, i18n("Open file") /* TODO better text */, startDir); dlg->setMimeTypeFilters(supportedMimeTypes); dlg->setFileMode(QFileDialog::ExistingFile); const bool dialogAccepted = dlg->exec() != 0; const QUrl url = (dialogAccepted && !dlg->selectedUrls().isEmpty()) ? dlg->selectedUrls().first() : QUrl(); delete dlg; if (!url.isEmpty()) openDocument(url); } void KBibTeXMainWindow::openDocument(const QUrl &url) { OpenFileInfo *openFileInfo = OpenFileInfoManager::instance().open(url); OpenFileInfoManager::instance().setCurrentFile(openFileInfo); } void KBibTeXMainWindow::closeDocument() { OpenFileInfoManager::instance().close(OpenFileInfoManager::instance().currentFile()); } void KBibTeXMainWindow::closeEvent(QCloseEvent *event) { KMainWindow::closeEvent(event); if (OpenFileInfoManager::instance().queryCloseAll()) event->accept(); else event->ignore(); } void KBibTeXMainWindow::showPreferences() { QPointer dlg = new KBibTeXPreferencesDialog(this); dlg->exec(); delete dlg; } void KBibTeXMainWindow::documentSwitched(FileView *oldFileView, FileView *newFileView) { OpenFileInfo *openFileInfo = d->mdiWidget->currentFile(); bool validFile = openFileInfo != nullptr; d->actionClose->setEnabled(validFile); setCaption(validFile ? i18n("%1 - KBibTeX", openFileInfo->shortCaption()) : i18n("KBibTeX")); d->fileSettings->setEnabled(newFileView != nullptr); d->referencePreview->setEnabled(newFileView != nullptr); d->elementForm->setEnabled(newFileView != nullptr); d->documentPreview->setEnabled(newFileView != nullptr); if (oldFileView != nullptr) { disconnect(newFileView, &FileView::currentElementChanged, d->referencePreview, &ReferencePreview::setElement); disconnect(newFileView, &FileView::currentElementChanged, d->elementForm, &ElementForm::setElement); disconnect(newFileView, &FileView::currentElementChanged, d->documentPreview, &DocumentPreview::setElement); disconnect(newFileView, &FileView::currentElementChanged, d->searchForm, &SearchForm::setElement); disconnect(newFileView, &FileView::modified, d->valueList, &ValueList::update); disconnect(newFileView, &FileView::modified, d->statistics, &Statistics::update); // FIXME disconnect(oldEditor, SIGNAL(modified()), d->elementForm, SLOT(refreshElement())); disconnect(d->elementForm, &ElementForm::elementModified, newFileView, &FileView::externalModification); } if (newFileView != nullptr) { connect(newFileView, &FileView::currentElementChanged, d->referencePreview, &ReferencePreview::setElement); connect(newFileView, &FileView::currentElementChanged, d->elementForm, &ElementForm::setElement); connect(newFileView, &FileView::currentElementChanged, d->documentPreview, &DocumentPreview::setElement); connect(newFileView, &FileView::currentElementChanged, d->searchForm, &SearchForm::setElement); connect(newFileView, &FileView::modified, d->valueList, &ValueList::update); connect(newFileView, &FileView::modified, d->statistics, &Statistics::update); // FIXME connect(newEditor, SIGNAL(modified()), d->elementForm, SLOT(refreshElement())); connect(d->elementForm, &ElementForm::elementModified, newFileView, &FileView::externalModification); connect(d->elementForm, &ElementForm::elementModified, newFileView, &FileView::externalModification); } d->documentPreview->setBibTeXUrl(validFile ? openFileInfo->url() : QUrl()); d->referencePreview->setElement(QSharedPointer(), nullptr); d->elementForm->setElement(QSharedPointer(), nullptr); d->documentPreview->setElement(QSharedPointer(), nullptr); d->valueList->setFileView(newFileView); d->fileSettings->setFileView(newFileView); d->statistics->setFileView(newFileView); d->referencePreview->setFileView(newFileView); } void KBibTeXMainWindow::showSearchResults() { d->dockSearchResults->show(); } void KBibTeXMainWindow::documentListsChanged(OpenFileInfo::StatusFlags statusFlags) { if (statusFlags.testFlag(OpenFileInfo::RecentlyUsed)) { const OpenFileInfoManager::OpenFileInfoList list = OpenFileInfoManager::instance().filteredItems(OpenFileInfo::RecentlyUsed); d->actionMenuRecentFilesMenu->clear(); for (OpenFileInfo *cur : list) { /// Fixing bug 19511: too long filenames make menu too large, /// therefore squeeze text if it is longer than squeezeLen. const int squeezeLen = 64; const QString squeezedShortCap = squeeze_text(cur->shortCaption(), squeezeLen); const QString squeezedFullCap = squeeze_text(cur->fullCaption(), squeezeLen); QAction *action = new QAction(QString(QStringLiteral("%1 [%2]")).arg(squeezedShortCap, squeezedFullCap), this); action->setData(cur->url()); action->setIcon(QIcon::fromTheme(cur->mimeType().replace(QLatin1Char('/'), QLatin1Char('-')))); d->actionMenuRecentFilesMenu->addAction(action); connect(action, &QAction::triggered, this, &KBibTeXMainWindow::openRecentFile); } } } void KBibTeXMainWindow::openRecentFile() { QAction *action = static_cast(sender()); QUrl url = action->data().toUrl(); openDocument(url); } void KBibTeXMainWindow::queryCloseAll() { if (OpenFileInfoManager::instance().queryCloseAll()) qApp->quit(); } void KBibTeXMainWindow::delayed() { /// Static variable, memorizes the dynamically created /// BibliographyService instance and allows to tell if /// this slot was called for the first or second time. static BibliographyService *bs = nullptr; if (bs == nullptr) { /// First call to this slot bs = new BibliographyService(this); if (!bs->isKBibTeXdefault() && KMessageBox::questionYesNo(this, i18n("KBibTeX is not the default editor for its bibliography formats like BibTeX or RIS."), i18n("Default Bibliography Editor"), KGuiItem(i18n("Set as Default Editor")), KGuiItem(i18n("Keep settings unchanged"))) == KMessageBox::Yes) { bs->setKBibTeXasDefault(); /// QTimer calls this slot again, but as 'bs' will not be NULL, /// the 'if' construct's 'else' path will be followed. QTimer::singleShot(5000, this, &KBibTeXMainWindow::delayed); } else { /// KBibTeX is default application or user doesn't care, /// therefore clean up memory delete bs; bs = nullptr; } } else { /// Second call to this slot. This time, clean up memory. bs->deleteLater(); bs = nullptr; } }