File indexing completed on 2024-04-28 13:39:47

0001 /***************************************************************************
0002  *   Copyright (C) 2005-2006 David Saxton <david@bluehaze.org>             *
0003  *                                                                         *
0004  *   This program is free software; you can redistribute it and/or modify  *
0005  *   it under the terms of the GNU General Public License as published by  *
0006  *   the Free Software Foundation; either version 2 of the License, or     *
0007  *   (at your option) any later version.                                   *
0008  ***************************************************************************/
0009 
0010 #include "viewcontainer.h"
0011 #include "docmanager.h"
0012 #include "document.h"
0013 #include "itemview.h"
0014 #include "ktechlab.h"
0015 #include "view.h"
0016 
0017 #include <KConfigGroup>
0018 #include <KLocalizedString>
0019 
0020 #include <QHBoxLayout>
0021 #include <QPushButton>
0022 #include <QTabWidget>
0023 //#include <qobjectlist.h>
0024 
0025 #include <ktechlab_debug.h>
0026 
0027 // BEGIN class ViewContainer
0028 ViewContainer::ViewContainer(const QString &caption, QWidget *parent)
0029     : QWidget(parent ? parent : KTechlab::self()->tabWidget())
0030 {
0031     b_deleted = false;
0032     connect(KTechlab::self(), &KTechlab::needUpdateCaptions, this, &ViewContainer::updateCaption);
0033 
0034     QHBoxLayout *layout = new QHBoxLayout(this);
0035     layout->setMargin(0);
0036     m_baseViewArea = new ViewArea(this, this, 0, false);
0037     m_baseViewArea->setObjectName("viewarea_0");
0038     connect(m_baseViewArea, &ViewArea::destroyed, this, &ViewContainer::baseViewAreaDestroyed);
0039 
0040     layout->addWidget(m_baseViewArea);
0041 
0042     m_activeViewArea = 0;
0043     setFocusProxy(m_baseViewArea);
0044 
0045     if (!parent) {
0046         KTechlab::self()->tabWidget()->addTab(this, caption);
0047         QTabWidget *tabWidget = KTechlab::self()->tabWidget();
0048         tabWidget->setCurrentIndex(tabWidget->indexOf(this));
0049     }
0050 
0051     show();
0052 }
0053 
0054 ViewContainer::~ViewContainer()
0055 {
0056     b_deleted = true;
0057 }
0058 
0059 void ViewContainer::setActiveViewArea(uint id)
0060 {
0061     if (m_activeViewArea == int(id))
0062         return;
0063 
0064     m_activeViewArea = id;
0065     View *newView = view(id);
0066     setFocusProxy(newView);
0067 
0068     if (newView) {
0069         setWindowTitle(newView->windowTitle());
0070 
0071         if (!DocManager::self()->getFocusedView() && newView->isVisible())
0072             newView->setFocus();
0073     }
0074 }
0075 
0076 View *ViewContainer::view(uint id) const
0077 {
0078     ViewArea *va = viewArea(id);
0079     if (!va)
0080         return nullptr;
0081 
0082     // We do not want a recursive search as ViewAreas also hold other ViewAreas
0083     // QObjectList l = va->queryList( "View", 0, false, false ); // 2018.12.02
0084     QList<View *> l = va->findChildren<View *>();
0085     View *view = nullptr;
0086     if (!l.isEmpty())
0087         view = dynamic_cast<View *>(l.first());
0088     // delete l;
0089 
0090     return view;
0091 }
0092 
0093 ViewArea *ViewContainer::viewArea(uint id) const
0094 {
0095     if (!m_viewAreaMap.contains(id))
0096         return nullptr;
0097 
0098     return m_viewAreaMap[id];
0099 }
0100 
0101 bool ViewContainer::closeViewContainer()
0102 {
0103     bool didClose = true;
0104     while (didClose && !m_viewAreaMap.isEmpty()) {
0105         didClose = closeViewArea(m_viewAreaMap.begin().key());
0106     }
0107 
0108     return m_viewAreaMap.isEmpty();
0109 }
0110 
0111 bool ViewContainer::closeViewArea(uint id)
0112 {
0113     ViewArea *va = viewArea(id);
0114     if (!va)
0115         return true;
0116 
0117     bool doClose = false;
0118     View *v = view(id);
0119     if (v && v->document()) {
0120         doClose = v->document()->numberOfViews() > 1;
0121         if (!doClose)
0122             doClose = v->document()->fileClose();
0123     } else
0124         doClose = true;
0125 
0126     if (!doClose)
0127         return false;
0128 
0129     m_viewAreaMap.remove(id);
0130     va->deleteLater();
0131 
0132     if (m_activeViewArea == int(id)) {
0133         m_activeViewArea = -1;
0134         findActiveViewArea();
0135     }
0136 
0137     return true;
0138 }
0139 
0140 int ViewContainer::createViewArea(int relativeViewArea, ViewArea::Position position, bool showOpenButton)
0141 {
0142     if (relativeViewArea == -1)
0143         relativeViewArea = activeViewArea();
0144 
0145     ViewArea *relative = viewArea(relativeViewArea);
0146     if (!relative) {
0147         qCCritical(KTL_LOG) << "Could not find relative view area";
0148         return -1;
0149     }
0150 
0151     uint id = uniqueNewId();
0152     //  setActiveViewArea(id);
0153 
0154     ViewArea *viewArea = relative->createViewArea(position, id, showOpenButton);
0155     //  ViewArea *viewArea = new ViewArea( m_splitter, id, (const char*)("viewarea_"+QString::number(id)) );
0156     viewArea->show(); // remove?
0157 
0158     return id;
0159 }
0160 
0161 void ViewContainer::setViewAreaId(ViewArea *viewArea, uint id)
0162 {
0163     m_viewAreaMap[id] = viewArea;
0164     m_usedIDs.append(id);
0165 }
0166 
0167 void ViewContainer::setViewAreaRemoved(uint id)
0168 {
0169     if (b_deleted)
0170         return;
0171 
0172     ViewAreaMap::iterator it = m_viewAreaMap.find(id);
0173     if (it == m_viewAreaMap.end())
0174         return;
0175 
0176     m_viewAreaMap.erase(it);
0177 
0178     if (m_activeViewArea == int(id))
0179         findActiveViewArea();
0180 }
0181 
0182 void ViewContainer::findActiveViewArea()
0183 {
0184     if (m_viewAreaMap.isEmpty())
0185         return;
0186 
0187     setActiveViewArea((--m_viewAreaMap.end()).key());
0188 }
0189 
0190 void ViewContainer::baseViewAreaDestroyed(QObject *obj)
0191 {
0192     if (!obj)
0193         return;
0194 
0195     if (!b_deleted) {
0196         b_deleted = true;
0197         close();
0198         deleteLater();
0199     }
0200 }
0201 
0202 bool ViewContainer::canSaveUsefulStateInfo() const
0203 {
0204     return m_baseViewArea && m_baseViewArea->canSaveUsefulStateInfo();
0205 }
0206 
0207 void ViewContainer::saveState(KConfigGroup *config)
0208 {
0209     if (!m_baseViewArea)
0210         return;
0211 
0212     config->writeEntry("BaseViewArea", m_baseViewArea->id());
0213     m_baseViewArea->saveState(config);
0214 }
0215 
0216 void ViewContainer::restoreState(KConfigGroup *config, const QString &groupName)
0217 {
0218     // config->setGroup(groupName);
0219     int baseAreaId = config->readEntry("BaseViewArea", 0);
0220     m_baseViewArea->restoreState(config, baseAreaId, groupName);
0221 }
0222 
0223 int ViewContainer::uniqueParentId()
0224 {
0225     int lowest = -1;
0226     const IntList::iterator end = m_usedIDs.end();
0227     for (IntList::iterator it = m_usedIDs.begin(); it != end; ++it) {
0228         if (*it < lowest)
0229             lowest = *it;
0230     }
0231     int newId = lowest - 1;
0232     m_usedIDs.append(newId);
0233     return newId;
0234 }
0235 
0236 int ViewContainer::uniqueNewId()
0237 {
0238     int highest = 0;
0239     const IntList::iterator end = m_usedIDs.end();
0240     for (IntList::iterator it = m_usedIDs.begin(); it != end; ++it) {
0241         if (*it > highest)
0242             highest = *it;
0243     }
0244     int newId = highest + 1;
0245     m_usedIDs.append(newId);
0246     return newId;
0247 }
0248 
0249 void ViewContainer::setIdUsed(int id)
0250 {
0251     m_usedIDs.append(id);
0252 }
0253 
0254 void ViewContainer::updateCaption()
0255 {
0256     QString caption;
0257 
0258     if (!activeView() || !activeView()->document())
0259         caption = i18n("(empty)");
0260 
0261     else {
0262         Document *doc = activeView()->document();
0263         caption = doc->url().isEmpty() ? doc->caption() : doc->url().fileName();
0264         if (viewCount() > 1)
0265             caption += " ...";
0266     }
0267 
0268     setWindowTitle(caption);
0269     // KTechlab::self()->tabWidget()->setTabLabel( this, caption ); // 2018.12.02
0270     KTechlab::self()->tabWidget()->setTabText(KTechlab::self()->tabWidget()->indexOf(this), caption);
0271 }
0272 // END class ViewContainer
0273 
0274 // BEGIN class ViewArea
0275 ViewArea::ViewArea(QWidget *parent, ViewContainer *viewContainer, int id, bool showOpenButton)
0276     : QSplitter(parent)
0277 {
0278     p_viewContainer = viewContainer;
0279     m_id = id;
0280     p_view = nullptr;
0281     p_viewArea1 = nullptr;
0282     p_viewArea2 = nullptr;
0283 
0284     if (id >= 0)
0285         p_viewContainer->setViewAreaId(this, uint(id));
0286 
0287     p_viewContainer->setIdUsed(id);
0288 
0289     m_pEmptyViewArea = nullptr;
0290     if (showOpenButton)
0291         m_pEmptyViewArea = new EmptyViewArea(this);
0292 }
0293 
0294 ViewArea::~ViewArea()
0295 {
0296     if (m_id >= 0)
0297         p_viewContainer->setViewAreaRemoved(uint(m_id));
0298 }
0299 
0300 ViewArea *ViewArea::createViewArea(Position position, uint id, bool showOpenButton)
0301 {
0302     if (p_viewArea1 || p_viewArea2) {
0303         qCCritical(KTL_LOG) << "Attempting to create ViewArea when already containing ViewAreas!";
0304         return nullptr;
0305     }
0306     if (!p_view) {
0307         qCCritical(KTL_LOG) << "We don't have a view yet, so creating a new ViewArea is redundant";
0308         return nullptr;
0309     }
0310 
0311     setOrientation((position == Right) ? Qt::Horizontal : Qt::Vertical);
0312 
0313     p_viewArea1 = new ViewArea(this, p_viewContainer, m_id, false);
0314     p_viewArea1->setObjectName("viewarea_" + QString::number(m_id));
0315     p_viewArea2 = new ViewArea(this, p_viewContainer, id, showOpenButton);
0316     p_viewArea2->setObjectName("viewarea_" + QString::number(id));
0317 
0318     connect(p_viewArea1, &ViewArea::destroyed, this, &ViewArea::viewAreaDestroyed);
0319     connect(p_viewArea2, &ViewArea::destroyed, this, &ViewArea::viewAreaDestroyed);
0320 
0321     p_view->clearFocus();
0322     // p_view->reparent( p_viewArea1, QPoint(), true ); // 2018.12.02
0323     p_view->setParent(p_viewArea1);
0324     p_view->move(QPoint());
0325     p_view->show();
0326     p_viewArea1->setView(p_view);
0327     setView(nullptr);
0328 
0329     m_id = p_viewContainer->uniqueParentId();
0330 
0331     QList<int> splitPos;
0332     int pos = ((orientation() == Qt::Horizontal) ? width() / 2 : height() / 2);
0333     splitPos << pos << pos;
0334     setSizes(splitPos);
0335 
0336     p_viewArea1->show();
0337     p_viewArea2->show();
0338     return p_viewArea2;
0339 }
0340 
0341 void ViewArea::viewAreaDestroyed(QObject *obj)
0342 {
0343     ViewArea *viewArea = static_cast<ViewArea *>(obj);
0344 
0345     if (viewArea == p_viewArea1)
0346         p_viewArea1 = nullptr;
0347 
0348     if (viewArea == p_viewArea2)
0349         p_viewArea2 = nullptr;
0350 
0351     if (!p_viewArea1 && !p_viewArea2)
0352         deleteLater();
0353 }
0354 
0355 void ViewArea::setView(View *view)
0356 {
0357     if (!view) {
0358         p_view = nullptr;
0359         setFocusProxy(nullptr);
0360         return;
0361     }
0362 
0363     delete m_pEmptyViewArea;
0364 
0365     if (p_view) {
0366         qCCritical(KTL_LOG) << "Attempting to set already contained view!";
0367         return;
0368     }
0369 
0370     p_view = view;
0371 
0372     //  qCDebug(KTL_LOG) << "p_view->isFocusEnabled()="<<p_view->isFocusEnabled()<<" p_view->isHidden()="<<p_view->isHidden();
0373 
0374     connect(view, &View::destroyed, this, &ViewArea::viewDestroyed);
0375     bool hadFocus = hasFocus();
0376     setFocusProxy(p_view);
0377     if (hadFocus && !p_view->isHidden())
0378         p_view->setFocus();
0379 
0380     // The ViewContainer by default has a view area as its focus proxy.
0381     // This is because there is no view when it is constructed. So give
0382     // it our view as the focus proxy if it doesn't have one.
0383     if (!dynamic_cast<View *>(p_viewContainer->focusProxy()))
0384         p_viewContainer->setFocusProxy(p_view);
0385 }
0386 
0387 void ViewArea::viewDestroyed()
0388 {
0389     if (!p_view && !p_viewArea1 && !p_viewArea2)
0390         deleteLater();
0391 }
0392 
0393 bool ViewArea::canSaveUsefulStateInfo() const
0394 {
0395     if (p_viewArea1 && p_viewArea1->canSaveUsefulStateInfo())
0396         return true;
0397 
0398     if (p_viewArea2 && p_viewArea2->canSaveUsefulStateInfo())
0399         return true;
0400 
0401     if (p_view && p_view->document() && !p_view->document()->url().isEmpty())
0402         return true;
0403 
0404     return false;
0405 }
0406 
0407 void ViewArea::saveState(KConfigGroup *config)
0408 {
0409     bool va1Ok = p_viewArea1 && p_viewArea1->canSaveUsefulStateInfo();
0410     bool va2Ok = p_viewArea2 && p_viewArea2->canSaveUsefulStateInfo();
0411 
0412     if (va1Ok || va2Ok) {
0413         config->writeEntry(orientationKey(m_id), (orientation() == Qt::Horizontal) ? "LeftRight" : "TopBottom");
0414 
0415         QList<int> contains;
0416         if (va1Ok)
0417             contains << p_viewArea1->id();
0418         if (va2Ok)
0419             contains << p_viewArea2->id();
0420 
0421         config->writeEntry(containsKey(m_id), contains);
0422         if (va1Ok)
0423             p_viewArea1->saveState(config);
0424         if (va2Ok)
0425             p_viewArea2->saveState(config);
0426     } else if (p_view && !p_view->document()->url().isEmpty()) {
0427         config->writePathEntry(fileKey(m_id), p_view->document()->url().toDisplayString(QUrl::PreferLocalFile));
0428     }
0429 }
0430 
0431 void ViewArea::restoreState(KConfigGroup *config, int id, const QString &groupName)
0432 {
0433     if (!config)
0434         return;
0435 
0436     if (id != m_id) {
0437         if (m_id >= 0)
0438             p_viewContainer->setViewAreaRemoved(uint(m_id));
0439 
0440         m_id = id;
0441 
0442         if (m_id >= 0)
0443             p_viewContainer->setViewAreaId(this, uint(m_id));
0444 
0445         p_viewContainer->setIdUsed(id);
0446     }
0447 
0448     // config->setGroup(groupName);
0449     if (config->hasKey(orientationKey(id))) {
0450         QString orientation = config->readEntry(orientationKey(m_id));
0451         setOrientation((orientation == "LeftRight") ? Qt::Horizontal : Qt::Vertical);
0452     }
0453 
0454     // config->setGroup(groupName);
0455     if (config->hasKey(containsKey(m_id))) {
0456         typedef QList<int> IntList;
0457         IntList contains = config->readEntry(containsKey(m_id), IntList());
0458 
0459         if (contains.isEmpty() || contains.size() > 2)
0460             qCCritical(KTL_LOG) << "Contained list has wrong size of " << contains.size();
0461 
0462         else {
0463             if (contains.size() >= 1) {
0464                 int viewArea1Id = contains[0];
0465                 p_viewArea1 = new ViewArea(this, p_viewContainer, viewArea1Id, false);
0466                 p_viewArea1->setObjectName("viewarea_" + QString::number(viewArea1Id));
0467                 connect(p_viewArea1, &ViewArea::destroyed, this, &ViewArea::viewAreaDestroyed);
0468                 p_viewArea1->restoreState(config, viewArea1Id, groupName);
0469                 p_viewArea1->show();
0470             }
0471 
0472             if (contains.size() >= 2) {
0473                 int viewArea2Id = contains[1];
0474                 p_viewArea2 = new ViewArea(this, p_viewContainer, viewArea2Id, false);
0475                 p_viewArea2->setObjectName("viewarea_" + QString::number(viewArea2Id));
0476                 connect(p_viewArea2, &ViewArea::destroyed, this, &ViewArea::viewAreaDestroyed);
0477                 p_viewArea2->restoreState(config, viewArea2Id, groupName);
0478                 p_viewArea2->show();
0479             }
0480         }
0481     }
0482 
0483     // config->setGroup(groupName);
0484     if (config->hasKey(fileKey(m_id))) {
0485         const QUrl url = QUrl::fromUserInput(config->readPathEntry(fileKey(m_id), QString()));
0486         bool openedOk = DocManager::self()->openURL(url, this);
0487         if (!openedOk)
0488             deleteLater();
0489     }
0490 }
0491 
0492 QString ViewArea::fileKey(int id)
0493 {
0494     return QString("ViewArea ") + QString::number(id) + QString(" file");
0495 }
0496 QString ViewArea::containsKey(int id)
0497 {
0498     return QString("ViewArea ") + QString::number(id) + QString(" contains");
0499 }
0500 QString ViewArea::orientationKey(int id)
0501 {
0502     return QString("ViewArea ") + QString::number(id) + QString(" orientation");
0503 }
0504 // END class ViewArea
0505 
0506 // BEGIN class EmptyViewArea
0507 EmptyViewArea::EmptyViewArea(ViewArea *parent)
0508     : QWidget(parent)
0509 {
0510     m_pViewArea = parent;
0511 
0512     QGridLayout *layout = new QGridLayout(this /*, 5, 3, 0, 6 */);
0513     layout->setMargin(0);
0514     layout->setSpacing(6);
0515 
0516     layout->setRowStretch(0, 20);
0517     layout->setRowStretch(2, 1);
0518     layout->setRowStretch(4, 20);
0519 
0520     layout->setColumnStretch(0, 1);
0521     layout->setColumnStretch(2, 1);
0522 
0523     QPushButton *newDocButton = new QPushButton(QIcon::fromTheme(QStringLiteral("document-open")), i18n("Open Document"), this);
0524     layout->addWidget(newDocButton, 1, 1);
0525     connect(newDocButton, &QPushButton::clicked, this, &EmptyViewArea::openDocument);
0526 
0527     QPushButton *cancelButton = new QPushButton(QIcon::fromTheme(QStringLiteral("dialog-cancel")), i18n("Cancel"), this);
0528     layout->addWidget(cancelButton, 3, 1);
0529     connect(cancelButton, &QPushButton::clicked, m_pViewArea, &EmptyViewArea::deleteLater);
0530 }
0531 
0532 EmptyViewArea::~EmptyViewArea()
0533 {
0534 }
0535 
0536 void EmptyViewArea::openDocument()
0537 {
0538     KTechlab::self()->openFile(m_pViewArea);
0539 }
0540 // END class EmptyViewArea
0541 
0542 #include "moc_viewcontainer.cpp"