File indexing completed on 2025-02-16 11:41:20
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"