File indexing completed on 2024-04-28 04:20:19
0001 0002 /* 0003 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org> 0004 All rights reserved. 0005 0006 Redistribution and use in source and binary forms, with or without 0007 modification, are permitted provided that the following conditions 0008 are met: 0009 0010 1. Redistributions of source code must retain the above copyright 0011 notice, this list of conditions and the following disclaimer. 0012 2. Redistributions in binary form must reproduce the above copyright 0013 notice, this list of conditions and the following disclaimer in the 0014 documentation and/or other materials provided with the distribution. 0015 0016 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 0017 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 0018 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 0019 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 0020 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 0021 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 0022 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 0023 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 0024 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 0025 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 0026 */ 0027 0028 0029 #include "kpMainWindow.h" 0030 #include "kpMainWindowPrivate.h" 0031 0032 #include "layers/selections/image/kpAbstractImageSelection.h" 0033 #include "environments/commands/kpCommandEnvironment.h" 0034 #include "environments/tools/kpToolEnvironment.h" 0035 #include "widgets/kpColorCells.h" 0036 #include "widgets/toolbars/kpColorToolBar.h" 0037 #include "commands/kpCommandHistory.h" 0038 #include "document/kpDocument.h" 0039 #include "environments/document/kpDocumentEnvironment.h" 0040 #include "layers/selections/kpSelectionDrag.h" 0041 #include "kpThumbnail.h" 0042 #include "tools/kpTool.h" 0043 #include "widgets/toolbars/kpToolToolBar.h" 0044 #include "views/manager/kpViewManager.h" 0045 #include "kpViewScrollableContainer.h" 0046 #include "generic/kpWidgetMapper.h" 0047 #include "views/kpZoomedThumbnailView.h" 0048 #include "views/kpZoomedView.h" 0049 0050 #include <KSharedConfig> 0051 #include <KConfigGroup> 0052 #include <KLocalizedString> 0053 0054 #include <QMenu> 0055 #include <QDropEvent> 0056 0057 #include "kpLogCategories.h" 0058 0059 0060 //--------------------------------------------------------------------- 0061 0062 kpMainWindow::kpMainWindow () 0063 : KXmlGuiWindow (nullptr/*parent*/) 0064 { 0065 init (); 0066 open (QUrl (), true/*create an empty doc*/); 0067 0068 d->isFullyConstructed = true; 0069 } 0070 0071 //--------------------------------------------------------------------- 0072 0073 kpMainWindow::kpMainWindow (const QUrl &url) 0074 : KXmlGuiWindow (nullptr/*parent*/) 0075 { 0076 init (); 0077 open (url, true/*create an empty doc with the same url if url !exist*/); 0078 0079 d->isFullyConstructed = true; 0080 } 0081 0082 //--------------------------------------------------------------------- 0083 0084 kpMainWindow::kpMainWindow (kpDocument *newDoc) 0085 : KXmlGuiWindow (nullptr/*parent*/) 0086 { 0087 init (); 0088 setDocument (newDoc); 0089 0090 d->isFullyConstructed = true; 0091 } 0092 0093 //--------------------------------------------------------------------- 0094 0095 0096 // TODO: Move into appropriate kpMainWindow_*.cpp or another class 0097 0098 // private 0099 void kpMainWindow::readGeneralSettings () 0100 { 0101 #if DEBUG_KP_MAIN_WINDOW 0102 qCDebug(kpLogMainWindow) << "\tkpMainWindow(" << objectName () << ")::readGeneralSettings()"; 0103 #endif 0104 0105 KConfigGroup cfg (KSharedConfig::openConfig (), QStringLiteral(kpSettingsGroupGeneral)); 0106 0107 d->configFirstTime = cfg.readEntry (kpSettingFirstTime, true); 0108 d->configShowGrid = cfg.readEntry (kpSettingShowGrid, false); 0109 d->configShowPath = cfg.readEntry (kpSettingShowPath, false); 0110 d->moreEffectsDialogLastEffect = cfg.readEntry (kpSettingMoreEffectsLastEffect, 0); 0111 kpToolEnvironment::drawAntiAliased = cfg.readEntry(kpSettingDrawAntiAliased, true); 0112 0113 if (cfg.hasKey (kpSettingOpenImagesInSameWindow)) 0114 { 0115 d->configOpenImagesInSameWindow = cfg.readEntry (kpSettingOpenImagesInSameWindow, false); 0116 } 0117 else 0118 { 0119 d->configOpenImagesInSameWindow = false; 0120 #if DEBUG_KP_MAIN_WINDOW 0121 qCDebug(kpLogMainWindow) << "\tconfigOpenImagesInSameWindow: first time" 0122 << " - writing default: " << d->configOpenImagesInSameWindow; 0123 #endif 0124 // TODO: More hidden options have to write themselves out on startup, 0125 // not on use, to be discoverable (e.g. printing centered on page). 0126 cfg.writeEntry (kpSettingOpenImagesInSameWindow, 0127 d->configOpenImagesInSameWindow); 0128 cfg.sync (); 0129 } 0130 0131 d->configPrintImageCenteredOnPage = cfg.readEntry (kpSettingPrintImageCenteredOnPage, true); 0132 0133 0134 #if DEBUG_KP_MAIN_WINDOW 0135 qCDebug(kpLogMainWindow) << "\t\tGeneral Settings: firstTime=" << d->configFirstTime 0136 << " showGrid=" << d->configShowGrid 0137 << " showPath=" << d->configShowPath 0138 << " moreEffectsDialogLastEffect=" << d->moreEffectsDialogLastEffect 0139 << " openImagesInSameWindow=" << d->configOpenImagesInSameWindow 0140 << " printImageCenteredOnPage=" << d->configPrintImageCenteredOnPage; 0141 #endif 0142 } 0143 0144 //--------------------------------------------------------------------- 0145 0146 // private 0147 void kpMainWindow::readThumbnailSettings () 0148 { 0149 #if DEBUG_KP_MAIN_WINDOW 0150 qCDebug(kpLogMainWindow) << "\tkpMainWindow(" << objectName () << ")::readThumbnailSettings()"; 0151 #endif 0152 0153 KConfigGroup cfg (KSharedConfig::openConfig (), QStringLiteral(kpSettingsGroupThumbnail)); 0154 0155 d->configThumbnailShown = cfg.readEntry (kpSettingThumbnailShown, false); 0156 d->configThumbnailGeometry = cfg.readEntry (kpSettingThumbnailGeometry, QRect ()); 0157 d->configZoomedThumbnail = cfg.readEntry (kpSettingThumbnailZoomed, true); 0158 d->configThumbnailShowRectangle = cfg.readEntry (kpSettingThumbnailShowRectangle, true); 0159 0160 #if DEBUG_KP_MAIN_WINDOW 0161 qCDebug(kpLogMainWindow) << "\t\tThumbnail Settings: shown=" << d->configThumbnailShown 0162 << " geometry=" << d->configThumbnailGeometry 0163 << " zoomed=" << d->configZoomedThumbnail 0164 << " showRectangle=" << d->configThumbnailShowRectangle; 0165 #endif 0166 } 0167 0168 //--------------------------------------------------------------------- 0169 0170 void kpMainWindow::finalizeGUI(KXMLGUIClient *client) 0171 { 0172 if ( client == this ) 0173 { 0174 const QList<QMenu *> menuToHide = findChildren<QMenu *>(QStringLiteral("toolToolBarHiddenMenu")); 0175 // should only contain one but... 0176 for (auto *menu : menuToHide) 0177 { 0178 menu->menuAction()->setVisible(false); 0179 } 0180 } 0181 } 0182 0183 //--------------------------------------------------------------------- 0184 0185 // private 0186 void kpMainWindow::init () 0187 { 0188 #if DEBUG_KP_MAIN_WINDOW 0189 qCDebug(kpLogMainWindow) << "kpMainWindow(" << objectName () << ")::init()"; 0190 QTime totalTime; totalTime.start (); 0191 #endif 0192 0193 d = new kpMainWindowPrivate; 0194 0195 // 0196 // set mainwindow properties 0197 // 0198 0199 setMinimumSize (320, 260); 0200 setAcceptDrops (true); 0201 0202 // 0203 // read config 0204 // 0205 0206 // KConfig::readEntry() does not actually reread from disk, hence doesn't 0207 // realize what other processes have done e.g. Settings / Show Path 0208 KSharedConfig::openConfig ()->reparseConfiguration (); 0209 0210 readGeneralSettings (); 0211 readThumbnailSettings (); 0212 0213 // 0214 // create GUI 0215 // 0216 setupActions (); 0217 createStatusBar (); 0218 createGUI (); 0219 0220 createColorBox (); 0221 createToolBox (); 0222 0223 0224 // Let the Tool Box take all the vertical space, since it can be quite 0225 // tall with all its tool option widgets. This also avoids occasional 0226 // bugs like the Tool Box overlapping the Color Tool Bar. 0227 setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea); 0228 setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea); 0229 0230 // no tabbed docks; does not make sense with only 2 dock widgets 0231 setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks); 0232 0233 addDockWidget(Qt::BottomDockWidgetArea, d->colorToolBar, Qt::Horizontal); 0234 0235 d->scrollView = new kpViewScrollableContainer (this); 0236 d->scrollView->setObjectName ( QStringLiteral("scrollView" )); 0237 0238 connect (d->scrollView, &kpViewScrollableContainer::beganDocResize, 0239 this, &kpMainWindow::slotBeganDocResize); 0240 0241 connect (d->scrollView, &kpViewScrollableContainer::continuedDocResize, 0242 this, &kpMainWindow::slotContinuedDocResize); 0243 0244 connect (d->scrollView, &kpViewScrollableContainer::cancelledDocResize, 0245 this, &kpMainWindow::slotCancelledDocResize); 0246 0247 connect (d->scrollView, &kpViewScrollableContainer::endedDocResize, 0248 this, &kpMainWindow::slotEndedDocResize); 0249 0250 connect (d->scrollView, &kpViewScrollableContainer::statusMessageChanged, 0251 this, &kpMainWindow::slotDocResizeMessageChanged); 0252 0253 connect (d->scrollView, &kpViewScrollableContainer::contentsMoved, 0254 this, &kpMainWindow::slotScrollViewAfterScroll); 0255 0256 setCentralWidget (d->scrollView); 0257 0258 // 0259 // set initial pos/size of GUI 0260 // 0261 0262 setAutoSaveSettings (); 0263 0264 // our non-XMLGUI tools-toolbar will get initially the toolButtonStyle as 0265 // all other toolbars, but we want to show only icons for the tools by default 0266 // (have to do this _after_ setAutoSaveSettings as that applies the default settings) 0267 if (d->configFirstTime) 0268 { 0269 d->toolToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly); 0270 0271 KConfigGroup cfg(KSharedConfig::openConfig(), QStringLiteral(kpSettingsGroupGeneral)); 0272 0273 cfg.writeEntry(kpSettingFirstTime, d->configFirstTime = false); 0274 cfg.sync(); 0275 } 0276 0277 0278 #if DEBUG_KP_MAIN_WINDOW 0279 qCDebug(kpLogMainWindow) << "\tall done in " << totalTime.elapsed () << "msec"; 0280 #endif 0281 } 0282 0283 //--------------------------------------------------------------------- 0284 0285 // private virtual [base KMainWindow] 0286 void kpMainWindow::readProperties (const KConfigGroup &configGroup) 0287 { 0288 #if DEBUG_KP_MAIN_WINDOW 0289 qCDebug(kpLogMainWindow) << "kpMainWindow<" << this << ">::readProperties()"; 0290 #endif 0291 0292 // No document at all? 0293 if (!configGroup.hasKey (kpSessionSettingDocumentUrl)) 0294 { 0295 #if DEBUG_KP_MAIN_WINDOW 0296 qCDebug(kpLogMainWindow) << "\tno url - no document"; 0297 #endif 0298 setDocument (nullptr); 0299 } 0300 // Have a document. 0301 else 0302 { 0303 const QUrl url = QUrl (configGroup.readEntry (kpSessionSettingDocumentUrl, 0304 QString ())); 0305 #if DEBUG_KP_MAIN_WINDOW 0306 qCDebug(kpLogMainWindow) << "\turl=" << url; 0307 #endif 0308 0309 const QSize notFromURLDocSize = 0310 configGroup.readEntry (kpSessionSettingNotFromUrlDocumentSize, 0311 QSize ()); 0312 0313 // Is from URL? 0314 if (notFromURLDocSize.isEmpty ()) 0315 { 0316 // If this fails, the empty document that kpMainWindow::kpMainWindow() 0317 // created is left untouched. 0318 openInternal (url, defaultDocSize (), 0319 false/*show error message if url !exist*/); 0320 } 0321 // Not from URL? 0322 else 0323 { 0324 #if DEBUG_KP_MAIN_WINDOW 0325 qCDebug(kpLogMainWindow) << "\tnot from url; doc size=" << notFromURLDocSize; 0326 #endif 0327 // Either we have an empty URL or we have a "kolourpaint doesnotexist.png" 0328 // URL. Regarding the latter case, if a file now actually exists at that 0329 // URL, we do open it - ignoring notFromURLDocSize - to avoid putting 0330 // the user in a situation where he might accidentally overwrite an 0331 // existing file. 0332 openInternal (url, notFromURLDocSize, 0333 true/*create an empty doc with the same url if url !exist*/); 0334 } 0335 } 0336 0337 } 0338 0339 //--------------------------------------------------------------------- 0340 0341 // private virtual [base KMainWindow] 0342 // WARNING: KMainWindow API Doc says "No user interaction is allowed 0343 // in this function!" 0344 void kpMainWindow::saveProperties (KConfigGroup &configGroup) 0345 { 0346 #if DEBUG_KP_MAIN_WINDOW 0347 qCDebug(kpLogMainWindow) << "kpMainWindow<" << this << ">::saveProperties()"; 0348 #endif 0349 0350 // No document at all? 0351 if (!d->document) 0352 { 0353 #if DEBUG_KP_MAIN_WINDOW 0354 qCDebug(kpLogMainWindow) << "\tno url - no document"; 0355 #endif 0356 } 0357 // Have a document. 0358 else 0359 { 0360 // Save URL in all cases: 0361 // 0362 // a) d->document->isFromExistingURL() 0363 // b) !d->document->isFromExistingURL() [save size in this case] 0364 // i) No URL 0365 // ii) URL (from "kolourpaint doesnotexist.png") 0366 0367 const QUrl url = d->document->url (); 0368 #if DEBUG_KP_MAIN_WINDOW 0369 qCDebug(kpLogMainWindow) << "\turl=" << url; 0370 #endif 0371 configGroup.writeEntry (kpSessionSettingDocumentUrl, url.url ()); 0372 0373 // Not from URL e.g. "kolourpaint doesnotexist.png"? 0374 // 0375 // Note that "kolourpaint doesexist.png" is considered to be from 0376 // a URL even if it was deleted in the background. This is because the user expects 0377 // it to be from a URL, so when we session restore, we pop up a 0378 // "cannot find file" dialog, instead of silently creating a new, 0379 // blank document. 0380 if (!d->document->isFromExistingURL ()) 0381 { 0382 // If we don't have a URL either: 0383 // 0384 // a) it was not modified - so we can use either width() or 0385 // constructorWidth() (they'll be equal). 0386 // b) the changes were discarded so we use the initial width, 0387 // constructorWidth(). 0388 // 0389 // Similarly for height() and constructorHeight(). 0390 const QSize docSize (d->document->constructorWidth (), 0391 d->document->constructorHeight ()); 0392 #if DEBUG_KP_MAIN_WINDOW 0393 qCDebug(kpLogMainWindow) << "\tnot from url; doc size=" << docSize; 0394 #endif 0395 configGroup.writeEntry (kpSessionSettingNotFromUrlDocumentSize, docSize); 0396 } 0397 } 0398 } 0399 0400 //--------------------------------------------------------------------- 0401 0402 0403 kpMainWindow::~kpMainWindow () 0404 { 0405 d->isFullyConstructed = false; 0406 0407 // Get the kpTool to finish up. This makes sure that the kpTool destructor 0408 // will not need to access any other class (that might be deleted before 0409 // the destructor is called by the QObject child-deletion mechanism). 0410 if (tool ()) { 0411 tool ()->endInternal (); 0412 } 0413 0414 // Delete document & views. 0415 // Note: This will disconnects signals from the current kpTool, so kpTool 0416 // must not be destructed yet. 0417 setDocument (nullptr); 0418 0419 delete d->commandHistory; d->commandHistory = nullptr; 0420 delete d->scrollView; d->scrollView = nullptr; 0421 0422 delete d; d = nullptr; 0423 } 0424 0425 //--------------------------------------------------------------------- 0426 0427 0428 // public 0429 kpDocument *kpMainWindow::document () const 0430 { 0431 return d->document; 0432 } 0433 0434 //--------------------------------------------------------------------- 0435 0436 // public 0437 kpDocumentEnvironment *kpMainWindow::documentEnvironment () 0438 { 0439 if (!d->documentEnvironment) { 0440 d->documentEnvironment = new kpDocumentEnvironment (this); 0441 } 0442 0443 return d->documentEnvironment; 0444 } 0445 0446 //--------------------------------------------------------------------- 0447 0448 // public 0449 kpViewManager *kpMainWindow::viewManager () const 0450 { 0451 return d->viewManager; 0452 } 0453 0454 //--------------------------------------------------------------------- 0455 0456 // public 0457 kpColorToolBar *kpMainWindow::colorToolBar () const 0458 { 0459 return d->colorToolBar; 0460 } 0461 0462 //--------------------------------------------------------------------- 0463 0464 // public 0465 kpColorCells *kpMainWindow::colorCells () const 0466 { 0467 return d->colorToolBar ? d->colorToolBar->colorCells () : nullptr; 0468 } 0469 0470 //--------------------------------------------------------------------- 0471 0472 // public 0473 kpToolToolBar *kpMainWindow::toolToolBar () const 0474 { 0475 return d->toolToolBar; 0476 } 0477 0478 //--------------------------------------------------------------------- 0479 0480 // public 0481 kpCommandHistory *kpMainWindow::commandHistory () const 0482 { 0483 return d->commandHistory; 0484 } 0485 0486 //--------------------------------------------------------------------- 0487 0488 kpCommandEnvironment *kpMainWindow::commandEnvironment () 0489 { 0490 if (!d->commandEnvironment) { 0491 d->commandEnvironment = new kpCommandEnvironment (this); 0492 } 0493 0494 return d->commandEnvironment; 0495 } 0496 0497 //--------------------------------------------------------------------- 0498 0499 // private 0500 void kpMainWindow::setupActions () 0501 { 0502 setupFileMenuActions (); 0503 setupEditMenuActions (); 0504 setupViewMenuActions (); 0505 setupImageMenuActions (); 0506 setupColorsMenuActions (); 0507 setupSettingsMenuActions (); 0508 0509 setupTextToolBarActions (); 0510 setupToolActions (); 0511 } 0512 0513 //--------------------------------------------------------------------- 0514 0515 // private 0516 void kpMainWindow::enableDocumentActions (bool enable) 0517 { 0518 enableFileMenuDocumentActions (enable); 0519 enableEditMenuDocumentActions (enable); 0520 enableViewMenuDocumentActions (enable); 0521 enableImageMenuDocumentActions (enable); 0522 enableColorsMenuDocumentActions (enable); 0523 enableSettingsMenuDocumentActions (enable); 0524 } 0525 0526 //--------------------------------------------------------------------- 0527 0528 // private 0529 void kpMainWindow::setDocument (kpDocument *newDoc) 0530 { 0531 //qCDebug(kpLogMainWindow) << newDoc; 0532 0533 // is it a close operation? 0534 if (!newDoc) 0535 { 0536 #if DEBUG_KP_MAIN_WINDOW 0537 qCDebug(kpLogMainWindow) << "\tdisabling actions"; 0538 #endif 0539 0540 // sync with the bit marked "sync" below 0541 0542 // TODO: Never disable the Color Box because the user should be 0543 // able to manipulate the colors, even without a currently 0544 // open document. 0545 // 0546 // We just have to make sure that signals from the Color 0547 // Box aren't fired and received unexpectedly when there's 0548 // no document. 0549 Q_ASSERT (d->colorToolBar); 0550 d->colorToolBar->setEnabled (false); 0551 0552 enableTextToolBarActions (false); 0553 } 0554 0555 // Always disable the tools. 0556 // If we decide to open a new document/mainView we want 0557 // kpTool::begin() to be called again e.g. in case it sets the cursor. 0558 // kpViewManager won't do this because we nuke it to avoid stale state. 0559 enableToolsDocumentActions (false); 0560 0561 if (!newDoc) 0562 { 0563 enableDocumentActions (false); 0564 } 0565 0566 delete d->mainView; d->mainView = nullptr; 0567 slotDestroyThumbnail (); 0568 0569 // viewManager will die and so will the selection 0570 d->actionCopy->setEnabled (false); 0571 d->actionCut->setEnabled (false); 0572 d->actionDelete->setEnabled (false); 0573 d->actionDeselect->setEnabled (false); 0574 d->actionCopyToFile->setEnabled (false); 0575 0576 delete d->viewManager; d->viewManager = nullptr; 0577 0578 #if DEBUG_KP_MAIN_WINDOW 0579 qCDebug(kpLogMainWindow) << "\tdestroying document"; 0580 qCDebug(kpLogMainWindow) << "\t\td->document=" << d->document; 0581 #endif 0582 // destroy current document 0583 delete d->document; 0584 d->document = newDoc; 0585 0586 0587 if (!d->lastCopyToURL.isEmpty ()) 0588 { 0589 // remove file name from path 0590 QString path = d->lastCopyToURL.path (); 0591 path = path.left (path.lastIndexOf (QLatin1Char ('/')) + 1); 0592 d->lastCopyToURL.setPath (path); 0593 } 0594 d->copyToFirstTime = true; 0595 0596 if (!d->lastExportURL.isEmpty ()) 0597 { 0598 QString path = d->lastExportURL.path (); 0599 path = path.left (path.lastIndexOf (QLatin1Char ('/')) + 1); 0600 d->lastExportURL.setPath (path); 0601 } 0602 d->exportFirstTime = true; 0603 0604 0605 // not a close operation? 0606 if (d->document) 0607 { 0608 #if DEBUG_KP_MAIN_WINDOW 0609 qCDebug(kpLogMainWindow) << "\treparenting doc that may have been created into a" 0610 << " different mainWindiow"; 0611 #endif 0612 d->document->setEnviron (documentEnvironment ()); 0613 0614 d->viewManager = new kpViewManager (this); 0615 0616 d->mainView = new kpZoomedView (d->document, d->toolToolBar, d->viewManager, 0617 nullptr/*buddyView*/, 0618 d->scrollView, 0619 d->scrollView->viewport ()); 0620 d->mainView->setObjectName ( QStringLiteral("mainView" )); 0621 0622 d->viewManager->registerView (d->mainView); 0623 d->scrollView->setView (d->mainView); 0624 d->mainView->show (); 0625 0626 #if DEBUG_KP_MAIN_WINDOW 0627 qCDebug(kpLogMainWindow) << "\thooking up document signals"; 0628 #endif 0629 0630 // Copy/Cut/Deselect/Delete 0631 connect (d->document, &kpDocument::selectionEnabled, 0632 d->actionCut, &QAction::setEnabled); 0633 0634 connect (d->document, &kpDocument::selectionEnabled, 0635 d->actionCopy, &QAction::setEnabled); 0636 0637 connect (d->document, &kpDocument::selectionEnabled, 0638 d->actionDelete, &QAction::setEnabled); 0639 0640 connect (d->document, &kpDocument::selectionEnabled, 0641 d->actionDeselect, &QAction::setEnabled); 0642 0643 connect (d->document, &kpDocument::selectionEnabled, 0644 d->actionCopyToFile, &QAction::setEnabled); 0645 0646 // this code won't actually enable any actions at this stage 0647 // (fresh document) but better safe than sorry 0648 d->actionCopy->setEnabled (d->document->selection ()); 0649 d->actionCut->setEnabled (d->document->selection ()); 0650 d->actionDeselect->setEnabled (d->document->selection ()); 0651 d->actionDelete->setEnabled (d->document->selection ()); 0652 d->actionCopyToFile->setEnabled (d->document->selection ()); 0653 0654 connect (d->document, &kpDocument::selectionEnabled, 0655 this, &kpMainWindow::slotImageMenuUpdateDueToSelection); 0656 0657 connect (d->document, &kpDocument::selectionIsTextChanged, 0658 this, &kpMainWindow::slotImageMenuUpdateDueToSelection); 0659 0660 // Status bar 0661 connect (d->document, &kpDocument::documentOpened, 0662 this, &kpMainWindow::recalculateStatusBar); 0663 0664 connect (d->document, SIGNAL (sizeChanged(QSize)), 0665 this, SLOT (setStatusBarDocSize(QSize))); 0666 0667 // Caption (url, modified) 0668 connect (d->document, &kpDocument::documentModified, 0669 this, &kpMainWindow::slotUpdateCaption); 0670 0671 connect (d->document, &kpDocument::documentOpened, 0672 this, &kpMainWindow::slotUpdateCaption); 0673 0674 connect (d->document, &kpDocument::documentSaved, 0675 this, &kpMainWindow::slotUpdateCaption); 0676 0677 // File/Reload action only available with non-empty URL 0678 connect (d->document, &kpDocument::documentSaved, 0679 this, &kpMainWindow::slotEnableReload); 0680 0681 connect (d->document, &kpDocument::documentSaved, 0682 this, &kpMainWindow::slotEnableSettingsShowPath); 0683 0684 // Command history 0685 Q_ASSERT (d->commandHistory); 0686 connect (d->commandHistory, &kpCommandHistory::documentRestored, 0687 this, &kpMainWindow::slotDocumentRestored); // caption "!modified" 0688 0689 connect (d->document, &kpDocument::documentSaved, 0690 d->commandHistory, &kpCommandHistory::documentSaved); 0691 0692 // Sync document -> views 0693 connect (d->document, &kpDocument::contentsChanged, 0694 d->viewManager, &kpViewManager::updateViews); 0695 0696 connect (d->document, static_cast<void (kpDocument::*)(int, int)>(&kpDocument::sizeChanged), 0697 d->viewManager, &kpViewManager::adjustViewsToEnvironment); 0698 0699 connect (d->document, 0700 static_cast<void (kpDocument::*)(int, int)>(&kpDocument::sizeChanged), 0701 d->viewManager, &kpViewManager::adjustViewsToEnvironment); 0702 0703 #if DEBUG_KP_MAIN_WINDOW 0704 qCDebug(kpLogMainWindow) << "\tenabling actions"; 0705 #endif 0706 0707 // sync with the bit marked "sync" above 0708 0709 Q_ASSERT (d->colorToolBar); 0710 d->colorToolBar->setEnabled (true); 0711 0712 0713 // Hide the text toolbar - it will be shown by kpToolText::begin() 0714 enableTextToolBarActions (false); 0715 0716 enableToolsDocumentActions (true); 0717 0718 enableDocumentActions (true); 0719 } 0720 0721 #if DEBUG_KP_MAIN_WINDOW 0722 qCDebug(kpLogMainWindow) << "\tupdating mainWindow elements"; 0723 #endif 0724 0725 slotImageMenuUpdateDueToSelection (); 0726 recalculateStatusBar (); 0727 slotUpdateCaption (); // Untitled to start with 0728 slotEnableReload (); 0729 slotEnableSettingsShowPath (); 0730 0731 if (d->commandHistory) { 0732 d->commandHistory->clear (); 0733 } 0734 0735 #if DEBUG_KP_MAIN_WINDOW 0736 qCDebug(kpLogMainWindow) << "\tdocument and views ready to go!"; 0737 #endif 0738 } 0739 0740 //--------------------------------------------------------------------- 0741 0742 // private virtual [base QWidget] 0743 void kpMainWindow::dragEnterEvent (QDragEnterEvent *e) 0744 { 0745 // It's faster to test for QMimeData::hasText() first due to the 0746 // lazy evaluation of the '||' operator. 0747 e->setAccepted (e->mimeData ()->hasText () || 0748 e->mimeData ()->hasUrls () || 0749 kpSelectionDrag::canDecode (e->mimeData ())); 0750 } 0751 0752 //--------------------------------------------------------------------- 0753 0754 // private virtual [base QWidget] 0755 void kpMainWindow::dropEvent (QDropEvent *e) 0756 { 0757 #if DEBUG_KP_MAIN_WINDOW 0758 qCDebug(kpLogMainWindow) << "kpMainWindow::dropEvent" << e->pos (); 0759 #endif 0760 0761 QList<QUrl> urls; 0762 0763 kpAbstractImageSelection *sel = kpSelectionDrag::decode (e->mimeData ()); 0764 if (sel) 0765 { 0766 // TODO: How do you actually drop a selection or ordinary images on 0767 // the clipboard)? Will this code path _ever_ execute? 0768 sel->setTransparency (imageSelectionTransparency ()); 0769 // TODO: drop at point like with QTextDrag below? 0770 paste (*sel); 0771 delete sel; 0772 } 0773 else if (!(urls = e->mimeData ()->urls ()).isEmpty ()) 0774 { 0775 // LOTODO: kpSetOverrideCursorSaver cursorSaver (Qt::waitCursor); 0776 // 0777 // However, you would need to prefix all possible error/warning 0778 // dialogs that might be called, with Qt::arrowCursor e.g. in 0779 // kpDocument and probably a lot more places. 0780 for (const auto &u : urls) 0781 open (u); 0782 } 0783 else if (e->mimeData ()->hasText ()) 0784 { 0785 const QString text = e->mimeData ()->text (); 0786 0787 QPoint selTopLeft = KP_INVALID_POINT; 0788 const QPoint globalPos = QWidget::mapToGlobal (e->pos ()); 0789 #if DEBUG_KP_MAIN_WINDOW 0790 qCDebug(kpLogMainWindow) << "\tpos toGlobal=" << globalPos; 0791 #endif 0792 0793 kpView *view = nullptr; 0794 0795 if (d->viewManager) 0796 { 0797 view = d->viewManager->viewUnderCursor (); 0798 #if DEBUG_KP_MAIN_WINDOW 0799 qCDebug(kpLogMainWindow) << "\t\tviewUnderCursor=" << view; 0800 #endif 0801 if (!view) 0802 { 0803 // HACK: see kpViewManager::setViewUnderCursor() to see why 0804 // it's not reliable 0805 #if DEBUG_KP_MAIN_WINDOW 0806 qCDebug(kpLogMainWindow) << "\t\tattempting to discover view"; 0807 0808 if (d->mainView && d->scrollView) 0809 { 0810 qCDebug(kpLogMainWindow) << "\t\t\tmainView->globalRect=" 0811 << kpWidgetMapper::toGlobal (d->mainView, d->mainView->rect ()) 0812 << " scrollView->globalRect=" 0813 << kpWidgetMapper::toGlobal (d->scrollView, 0814 QRect (0, 0, 0815 d->scrollView->viewport()->width (), 0816 d->scrollView->viewport()->height ())); 0817 } 0818 #endif 0819 if (d->thumbnailView && 0820 kpWidgetMapper::toGlobal (d->thumbnailView, d->thumbnailView->rect ()) 0821 .contains (globalPos)) 0822 { 0823 // TODO: Code will never get executed. 0824 // Thumbnail doesn't accept drops. 0825 view = d->thumbnailView; 0826 } 0827 else if (d->mainView && 0828 kpWidgetMapper::toGlobal (d->mainView, d->mainView->rect ()) 0829 .contains (globalPos) && 0830 d->scrollView && 0831 kpWidgetMapper::toGlobal (d->scrollView, 0832 QRect (0, 0, 0833 d->scrollView->viewport()->width (), 0834 d->scrollView->viewport()->height ())) 0835 .contains (globalPos)) 0836 { 0837 view = d->mainView; 0838 } 0839 } 0840 } 0841 0842 if (view) 0843 { 0844 const QPoint viewPos = view->mapFromGlobal (globalPos); 0845 const QPoint docPoint = view->transformViewToDoc (viewPos); 0846 0847 // viewUnderCursor() is hacky and can return a view when we aren't 0848 // over one thanks to drags. 0849 if (d->document && d->document->rect ().contains (docPoint)) 0850 { 0851 selTopLeft = docPoint; 0852 0853 // TODO: In terms of doc pixels, would be inconsistent behaviour 0854 // based on zoomLevel of view. 0855 // selTopLeft -= QPoint (-view->selectionResizeHandleAtomicSize (), 0856 // -view->selectionResizeHandleAtomicSize ()); 0857 } 0858 } 0859 0860 pasteText (text, true/*force new text selection*/, selTopLeft); 0861 } 0862 } 0863 0864 //--------------------------------------------------------------------- 0865 0866 // private slot 0867 void kpMainWindow::slotScrollViewAfterScroll () 0868 { 0869 // OPT: Why can't this be moved into kpViewScrollableContainer::slotDragScroll(), 0870 // grouping all drag-scroll-related repaints, which would potentially avoid 0871 // double repainting? 0872 if (tool ()) 0873 { 0874 tool ()->somethingBelowTheCursorChanged (); 0875 } 0876 } 0877 0878 //--------------------------------------------------------------------- 0879 0880 // private virtual [base QWidget] 0881 void kpMainWindow::moveEvent (QMoveEvent * /*e*/) 0882 { 0883 if (d->thumbnail) 0884 { 0885 // Disabled because it lags too far behind the mainWindow 0886 // d->thumbnail->move (d->thumbnail->pos () + (e->pos () - e->oldPos ())); 0887 0888 notifyThumbnailGeometryChanged (); 0889 } 0890 } 0891 0892 //--------------------------------------------------------------------- 0893 0894 // private slot 0895 void kpMainWindow::slotUpdateCaption () 0896 { 0897 if (d->document) 0898 { 0899 setCaption (d->configShowPath ? d->document->prettyUrl () 0900 : d->document->prettyFilename (), 0901 d->document->isModified ()); 0902 } 0903 else 0904 { 0905 setCaption (QString(), false); 0906 } 0907 } 0908 0909 //--------------------------------------------------------------------- 0910 0911 // private slot 0912 void kpMainWindow::slotDocumentRestored () 0913 { 0914 if (d->document) { 0915 d->document->setModified (false); 0916 } 0917 slotUpdateCaption (); 0918 } 0919 0920 //--------------------------------------------------------------------- 0921 0922 #include "moc_kpMainWindow.cpp"