summaryrefslogtreecommitdiffstats
path: root/kolourpaint/kpmainwindow_view.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kolourpaint/kpmainwindow_view.cpp')
-rw-r--r--kolourpaint/kpmainwindow_view.cpp1151
1 files changed, 1151 insertions, 0 deletions
diff --git a/kolourpaint/kpmainwindow_view.cpp b/kolourpaint/kpmainwindow_view.cpp
new file mode 100644
index 00000000..1aa9b5dc
--- /dev/null
+++ b/kolourpaint/kpmainwindow_view.cpp
@@ -0,0 +1,1151 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_ZOOM_FLICKER 0
+
+#include <kpmainwindow.h>
+#include <kpmainwindow_p.h>
+
+#include <qdatetime.h>
+#include <qpainter.h>
+#include <qtimer.h>
+
+#include <kactionclasses.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kstdaction.h>
+
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpthumbnail.h>
+#include <kptool.h>
+#include <kptooltoolbar.h>
+#include <kpunzoomedthumbnailview.h>
+#include <kpviewmanager.h>
+#include <kpviewscrollablecontainer.h>
+#include <kpwidgetmapper.h>
+#include <kpzoomedview.h>
+#include <kpzoomedthumbnailview.h>
+
+
+// private
+void kpMainWindow::setupViewMenuActions ()
+{
+ m_viewMenuDocumentActionsEnabled = false;
+ m_thumbnailSaveConfigTimer = 0;
+
+
+ KActionCollection *ac = actionCollection ();
+
+ /*m_actionFullScreen = KStdAction::fullScreen (0, 0, ac);
+ m_actionFullScreen->setEnabled (false);*/
+
+
+ m_actionActualSize = KStdAction::actualSize (this, SLOT (slotActualSize ()), ac);
+ /*m_actionFitToPage = KStdAction::fitToPage (this, SLOT (slotFitToPage ()), ac);
+ m_actionFitToWidth = KStdAction::fitToWidth (this, SLOT (slotFitToWidth ()), ac);
+ m_actionFitToHeight = KStdAction::fitToHeight (this, SLOT (slotFitToHeight ()), ac);*/
+
+
+ m_actionZoomIn = KStdAction::zoomIn (this, SLOT (slotZoomIn ()), ac);
+ m_actionZoomOut = KStdAction::zoomOut (this, SLOT (slotZoomOut ()), ac);
+
+
+ m_actionZoom = new KSelectAction (i18n ("&Zoom"), 0,
+ this, SLOT (slotZoom ()), actionCollection (), "view_zoom_to");
+ m_actionZoom->setEditable (true);
+
+ // create the zoom list for the 1st call to zoomTo() below
+ m_zoomList.append (10); m_zoomList.append (25); m_zoomList.append (33);
+ m_zoomList.append (50); m_zoomList.append (67); m_zoomList.append (75);
+ m_zoomList.append (100);
+ m_zoomList.append (200); m_zoomList.append (300);
+ m_zoomList.append (400); m_zoomList.append (600); m_zoomList.append (800);
+ m_zoomList.append (1000); m_zoomList.append (1200); m_zoomList.append (1600);
+
+
+ m_actionShowGrid = new KToggleAction (i18n ("Show &Grid"), CTRL + Key_G,
+ this, SLOT (slotShowGridToggled ()), actionCollection (), "view_show_grid");
+ m_actionShowGrid->setCheckedState (i18n ("Hide &Grid"));
+
+
+ // TODO: This doesn't work when the thumbnail has focus.
+ // Testcase: Press CTRL+H twice on a fresh KolourPaint.
+ // The second CTRL+H doesn't close the thumbnail.
+ m_actionShowThumbnail = new KToggleAction (i18n ("Show T&humbnail"), CTRL + Key_H,
+ this, SLOT (slotShowThumbnailToggled ()), actionCollection (), "view_show_thumbnail");
+ m_actionShowThumbnail->setCheckedState (i18n ("Hide T&humbnail"));
+
+ // Please do not use setCheckedState() here - it wouldn't make sense
+ m_actionZoomedThumbnail = new KToggleAction (i18n ("Zoo&med Thumbnail Mode"), 0,
+ this, SLOT (slotZoomedThumbnailToggled ()), actionCollection (), "view_zoomed_thumbnail");
+
+ // For consistency with the above action, don't use setCheckedState()
+ //
+ // Also, don't use "Show Thumbnail Rectangle" because if entire doc
+ // can be seen in scrollView, checking option won't "Show" anything
+ // since rect _surrounds_ entire doc (hence, won't be rendered).
+ d->m_actionShowThumbnailRectangle = new KToggleAction (
+ i18n ("Enable Thumbnail &Rectangle"),
+ 0,
+ this, SLOT (slotThumbnailShowRectangleToggled ()),
+ actionCollection (), "view_show_thumbnail_rectangle");
+
+
+ enableViewMenuDocumentActions (false);
+}
+
+// private
+bool kpMainWindow::viewMenuDocumentActionsEnabled () const
+{
+ return m_viewMenuDocumentActionsEnabled;
+}
+
+// private
+void kpMainWindow::enableViewMenuDocumentActions (bool enable)
+{
+ m_viewMenuDocumentActionsEnabled = enable;
+
+
+ m_actionActualSize->setEnabled (enable);
+ /*m_actionFitToPage->setEnabled (enable);
+ m_actionFitToWidth->setEnabled (enable);
+ m_actionFitToHeight->setEnabled (enable);*/
+
+ m_actionZoomIn->setEnabled (enable);
+ m_actionZoomOut->setEnabled (enable);
+
+ m_actionZoom->setEnabled (enable);
+
+ actionShowGridUpdate ();
+
+ m_actionShowThumbnail->setEnabled (enable);
+ enableThumbnailOptionActions (enable);
+
+
+ // TODO: for the time being, assume that we start at zoom 100%
+ // with no grid
+
+ // This function is only called when a new document is created
+ // or an existing document is closed. So the following will
+ // always be correct:
+
+ zoomTo (100);
+}
+
+// private
+void kpMainWindow::actionShowGridUpdate ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::actionShowGridUpdate()" << endl;
+#endif
+ const bool enable = (viewMenuDocumentActionsEnabled () &&
+ m_mainView && m_mainView->canShowGrid ());
+
+ m_actionShowGrid->setEnabled (enable);
+ m_actionShowGrid->setChecked (enable && m_configShowGrid);
+}
+
+
+// private
+void kpMainWindow::sendZoomListToActionZoom ()
+{
+ QStringList items;
+
+ const QValueVector <int>::ConstIterator zoomListEnd (m_zoomList.end ());
+ for (QValueVector <int>::ConstIterator it = m_zoomList.begin ();
+ it != zoomListEnd;
+ it++)
+ {
+ items << zoomLevelToString (*it);
+ }
+
+ // Work around a KDE bug - KSelectAction::setItems() enables the action.
+ // David Faure said it won't be fixed because it's a feature used by
+ // KRecentFilesAction.
+ bool e = m_actionZoom->isEnabled ();
+ m_actionZoom->setItems (items);
+ if (e != m_actionZoom->isEnabled ())
+ m_actionZoom->setEnabled (e);
+}
+
+// private
+int kpMainWindow::zoomLevelFromString (const QString &string)
+{
+ // loop until not digit
+ int i;
+ for (i = 0; i < (int) string.length () && string.at (i).isDigit (); i++)
+ ;
+
+ // convert zoom level to number
+ bool ok = false;
+ int zoomLevel = string.left (i).toInt (&ok);
+
+ if (!ok || zoomLevel <= 0 || zoomLevel > 3200)
+ return 0; // error
+ else
+ return zoomLevel;
+}
+
+// private
+QString kpMainWindow::zoomLevelToString (int zoomLevel)
+{
+ return i18n ("%1%").arg (zoomLevel);
+}
+
+// private
+void kpMainWindow::zoomTo (int zoomLevel, bool centerUnderCursor)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::zoomTo (" << zoomLevel << ")" << endl;
+#endif
+
+ if (zoomLevel <= 0)
+ zoomLevel = m_mainView ? m_mainView->zoomLevelX () : 100;
+
+// mute point since the thumbnail suffers from this too
+#if 0
+ else if (m_mainView && m_mainView->zoomLevelX () % 100 == 0 && zoomLevel % 100)
+ {
+ if (KMessageBox::warningContinueCancel (this,
+ i18n ("Setting the zoom level to a value that is not a multiple of 100% "
+ "results in imprecise editing and redraw glitches.\n"
+ "Do you really want to set to zoom level to %1%?")
+ .arg (zoomLevel),
+ QString::null/*caption*/,
+ i18n ("Set Zoom Level to %1%").arg (zoomLevel),
+ "DoNotAskAgain_ZoomLevelNotMultipleOf100") != KMessageBox::Continue)
+ {
+ zoomLevel = m_mainView->zoomLevelX ();
+ }
+ }
+#endif
+
+ int index = 0;
+ QValueVector <int>::Iterator it = m_zoomList.begin ();
+
+ while (index < (int) m_zoomList.count () && zoomLevel > *it)
+ it++, index++;
+
+ if (zoomLevel != *it)
+ m_zoomList.insert (it, zoomLevel);
+
+ sendZoomListToActionZoom ();
+ m_actionZoom->setCurrentItem (index);
+
+
+ if (viewMenuDocumentActionsEnabled ())
+ {
+ m_actionActualSize->setEnabled (zoomLevel != 100);
+
+ m_actionZoomIn->setEnabled (m_actionZoom->currentItem () < (int) m_zoomList.count () - 1);
+ m_actionZoomOut->setEnabled (m_actionZoom->currentItem () > 0);
+ }
+
+
+ if (m_viewManager)
+ m_viewManager->setQueueUpdates ();
+
+
+ if (m_scrollView)
+ {
+ m_scrollView->setUpdatesEnabled (false);
+ if (m_scrollView->viewport ())
+ m_scrollView->viewport ()->setUpdatesEnabled (false);
+ }
+
+ if (m_mainView)
+ {
+ m_mainView->setUpdatesEnabled (false);
+
+ if (m_scrollView && m_scrollView->viewport ())
+ {
+ // Ordinary flicker is better than the whole view moving
+ QPainter p (m_mainView);
+ p.fillRect (m_mainView->rect (),
+ m_scrollView->viewport ()->colorGroup ().background ());
+ }
+ }
+
+
+ if (m_scrollView && m_mainView)
+ {
+ #if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "\tscrollView contentsX=" << m_scrollView->contentsX ()
+ << " contentsY=" << m_scrollView->contentsY ()
+ << " contentsWidth=" << m_scrollView->contentsWidth ()
+ << " contentsHeight=" << m_scrollView->contentsHeight ()
+ << " visibleWidth=" << m_scrollView->visibleWidth ()
+ << " visibleHeight=" << m_scrollView->visibleHeight ()
+ << " oldZoomX=" << m_mainView->zoomLevelX ()
+ << " oldZoomY=" << m_mainView->zoomLevelY ()
+ << " newZoom=" << zoomLevel
+ << " mainViewX=" << m_scrollView->childX (m_mainView)
+ << " mainViewY=" << m_scrollView->childY (m_mainView)
+ << endl;
+ #endif
+
+ // TODO: when changing from no scrollbars to scrollbars, Qt lies about
+ // visibleWidth() & visibleHeight() (doesn't take into account the
+ // space taken by the would-be scrollbars) until it updates the
+ // scrollview; hence the centring is off by about 5-10 pixels.
+
+ // TODO: use visibleRect() for greater accuracy?
+
+ int viewX, viewY;
+
+ bool targetDocAvail = false;
+ double targetDocX, targetDocY;
+
+ if (centerUnderCursor &&
+ m_viewManager && m_viewManager->viewUnderCursor ())
+ {
+ kpView *const vuc = m_viewManager->viewUnderCursor ();
+ QPoint viewPoint = vuc->mouseViewPoint ();
+
+ // vuc->transformViewToDoc() returns QPoint which only has int
+ // accuracy so we do X and Y manually.
+ targetDocX = vuc->transformViewToDocX (viewPoint.x ());
+ targetDocY = vuc->transformViewToDocY (viewPoint.y ());
+ targetDocAvail = true;
+
+ if (vuc != m_mainView)
+ viewPoint = vuc->transformViewToOtherView (viewPoint, m_mainView);
+
+ viewX = viewPoint.x ();
+ viewY = viewPoint.y ();
+ }
+ else
+ {
+ viewX = m_scrollView->contentsX () +
+ QMIN (m_mainView->width (),
+ m_scrollView->visibleWidth ()) / 2;
+ viewY = m_scrollView->contentsY () +
+ QMIN (m_mainView->height (),
+ m_scrollView->visibleHeight ()) / 2;
+ }
+
+ int newCenterX = viewX * zoomLevel / m_mainView->zoomLevelX ();
+ int newCenterY = viewY * zoomLevel / m_mainView->zoomLevelY ();
+
+ m_mainView->setZoomLevel (zoomLevel, zoomLevel);
+ #if DEBUG_ZOOM_FLICKER
+ {
+ kdDebug () << "FLICKER: just setZoomLevel" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 1000)
+ ;
+ }
+ #endif
+
+ #if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "\tvisibleWidth=" << m_scrollView->visibleWidth ()
+ << " visibleHeight=" << m_scrollView->visibleHeight ()
+ << endl;
+ kdDebug () << "\tnewCenterX=" << newCenterX
+ << " newCenterY=" << newCenterY << endl;
+ #endif
+
+ m_scrollView->center (newCenterX, newCenterY);
+ #if DEBUG_ZOOM_FLICKER
+ {
+ kdDebug () << "FLICKER: just centred" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 2000)
+ ;
+ }
+ #endif
+
+ if (centerUnderCursor &&
+ targetDocAvail &&
+ m_viewManager && m_viewManager->viewUnderCursor ())
+ {
+ kpView *const vuc = m_viewManager->viewUnderCursor ();
+
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tcenterUnderCursor: reposition cursor; viewUnderCursor="
+ << vuc->name () << endl;
+ #endif
+
+ const double viewX = vuc->transformDocToViewX (targetDocX);
+ const double viewY = vuc->transformDocToViewY (targetDocY);
+ // Rounding error from zooming in and out :(
+ // TODO: do everything in terms of tool doc points in type "double".
+ const QPoint viewPoint ((int) viewX, (int) viewY);
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tdoc: (" << targetDocX << "," << targetDocY << ")"
+ << " viewUnderCursor: (" << viewX << "," << viewY << ")"
+ << endl;
+ #endif
+
+ if (vuc->clipRegion ().contains (viewPoint))
+ {
+ const QPoint globalPoint =
+ kpWidgetMapper::toGlobal (vuc, viewPoint);
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tglobalPoint=" << globalPoint << endl;
+ #endif
+
+ // TODO: Determine some sane cursor flashing indication -
+ // cursor movement is convenient but not conventional.
+ //
+ // Major problem: if using QApplication::setOverrideCursor()
+ // and in some stage of flash and window quits.
+ //
+ // Or if using kpView::setCursor() and change tool.
+ QCursor::setPos (globalPoint);
+ }
+ // e.g. Zoom to 200%, scroll mainView to bottom-right.
+ // Unzoomed Thumbnail shows top-left portion of bottom-right of
+ // mainView.
+ //
+ // Aim cursor at bottom-right of thumbnail and zoom out with
+ // CTRL+Wheel.
+ //
+ // If mainView is now small enough to largely not need scrollbars,
+ // Unzoomed Thumbnail scrolls to show _top-left_ portion
+ // _of top-left_ of mainView.
+ //
+ // Unzoomed Thumbnail no longer contains the point we zoomed out
+ // on top of.
+ else
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\twon't move cursor - would get outside view"
+ << endl;
+ #endif
+
+ // TODO: Sane cursor flashing indication that indicates
+ // that the normal cursor movement didn't happen.
+ }
+ }
+
+ #if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "\t\tcheck (contentsX=" << m_scrollView->contentsX ()
+ << ",contentsY=" << m_scrollView->contentsY ()
+ << ")" << endl;
+ #endif
+ }
+
+ if (m_mainView)
+ {
+ actionShowGridUpdate ();
+ #if DEBUG_ZOOM_FLICKER
+ {
+ kdDebug () << "FLICKER: updated grid action" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 1000)
+ ;
+ }
+ #endif
+
+
+ updateMainViewGrid ();
+ #if DEBUG_ZOOM_FLICKER
+ {
+ kdDebug () << "FLICKER: just updated grid" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 1000)
+ ;
+ }
+ #endif
+
+
+ // Since Zoom Level KSelectAction on ToolBar grabs focus after changing
+ // Zoom, switch back to the Main View.
+ // TODO: back to the last view
+ m_mainView->setFocus ();
+ #if DEBUG_ZOOM_FLICKER
+ {
+ kdDebug () << "FLICKER: just set focus to mainview" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 1000)
+ ;
+ }
+ #endif
+
+ }
+#if 1
+ // The view magnified and moved beneath the cursor
+ if (tool ())
+ tool ()->somethingBelowTheCursorChanged ();
+ #if DEBUG_ZOOM_FLICKER
+ {
+ kdDebug () << "FLICKER: signalled something below cursor" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 1000)
+ ;
+ }
+ #endif
+#endif
+
+ // HACK: make sure all of Qt's update() calls trigger
+ // kpView::paintEvent() _now_ so that they can be queued by us
+ // (until kpViewManager::restoreQueueUpdates()) to reduce flicker
+ // caused mainly by m_scrollView->center()
+ //
+ // TODO: remove flicker completely
+ //QTimer::singleShot (0, this, SLOT (finishZoomTo ()));
+
+ // Later: I don't think there is an update() that needs to be queued
+ // - let's reduce latency instead.
+ finishZoomTo ();
+}
+
+// private slot
+void kpMainWindow::finishZoomTo ()
+{
+#if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "\tkpMainWindow::finishZoomTo enter" << endl;
+#endif
+
+#if DEBUG_ZOOM_FLICKER
+{
+ kdDebug () << "FLICKER: inside finishZoomTo" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 2000)
+ ;
+}
+#endif
+
+ // TODO: setUpdatesEnabled() should really return to old value
+ // - not neccessarily "true"
+
+ if (m_mainView)
+ {
+ m_mainView->setUpdatesEnabled (true);
+ m_mainView->update ();
+ }
+
+#if DEBUG_ZOOM_FLICKER
+{
+ kdDebug () << "FLICKER: just updated mainView" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 1000)
+ ;
+}
+#endif
+
+ if (m_scrollView)
+ {
+ if (m_scrollView->viewport ())
+ {
+ m_scrollView->viewport ()->setUpdatesEnabled (true);
+ m_scrollView->viewport ()->update ();
+ }
+ #if DEBUG_ZOOM_FLICKER
+ {
+ kdDebug () << "FLICKER: just updated scrollView::viewport()" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 1000)
+ ;
+ }
+ #endif
+
+ m_scrollView->setUpdatesEnabled (true);
+ m_scrollView->update ();
+ #if DEBUG_ZOOM_FLICKER
+ {
+ kdDebug () << "FLICKER: just updated scrollView" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 1000)
+ ;
+ }
+ #endif
+
+ }
+
+
+ if (m_viewManager && m_viewManager->queueUpdates ()/*just in case*/)
+ m_viewManager->restoreQueueUpdates ();
+#if DEBUG_ZOOM_FLICKER
+{
+ kdDebug () << "FLICKER: restored vm queued updates" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 1000)
+ ;
+}
+#endif
+
+ setStatusBarZoom (m_mainView ? m_mainView->zoomLevelX () : 0);
+
+#if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "\tkpMainWindow::finishZoomTo done" << endl;
+#endif
+
+#if DEBUG_ZOOM_FLICKER
+{
+ kdDebug () << "FLICKER: finishZoomTo done" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 1000)
+ ;
+}
+#endif
+}
+
+
+// private slot
+void kpMainWindow::slotActualSize ()
+{
+ zoomTo (100);
+}
+
+// private slot
+void kpMainWindow::slotFitToPage ()
+{
+ if (!m_scrollView || !m_document)
+ return;
+
+ // doc_width * zoom / 100 <= view_width &&
+ // doc_height * zoom / 100 <= view_height &&
+ // 1 <= zoom <= 3200
+
+ zoomTo (QMIN (3200, QMAX (1, QMIN (m_scrollView->visibleWidth () * 100 / m_document->width (),
+ m_scrollView->visibleHeight () * 100 / m_document->height ()))));
+}
+
+// private slot
+void kpMainWindow::slotFitToWidth ()
+{
+ if (!m_scrollView || !m_document)
+ return;
+
+ // doc_width * zoom / 100 <= view_width &&
+ // 1 <= zoom <= 3200
+
+ zoomTo (QMIN (3200, QMAX (1, m_scrollView->visibleWidth () * 100 / m_document->width ())));
+}
+
+// private slot
+void kpMainWindow::slotFitToHeight ()
+{
+ if (!m_scrollView || !m_document)
+ return;
+
+ // doc_height * zoom / 100 <= view_height &&
+ // 1 <= zoom <= 3200
+
+ zoomTo (QMIN (3200, QMAX (1, m_scrollView->visibleHeight () * 100 / m_document->height ())));
+}
+
+
+// public
+void kpMainWindow::zoomIn (bool centerUnderCursor)
+{
+ const int targetItem = m_actionZoom->currentItem () + 1;
+
+ if (targetItem >= (int) m_zoomList.count ())
+ return;
+
+ m_actionZoom->setCurrentItem (targetItem);
+ zoomAccordingToZoomAction (centerUnderCursor);
+}
+
+// public
+void kpMainWindow::zoomOut (bool centerUnderCursor)
+{
+ const int targetItem = m_actionZoom->currentItem () - 1;
+
+ if (targetItem < 0)
+ return;
+
+ m_actionZoom->setCurrentItem (targetItem);
+ zoomAccordingToZoomAction (centerUnderCursor);
+}
+
+
+// public slot
+void kpMainWindow::slotZoomIn ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotZoomIn ()" << endl;
+#endif
+
+ zoomIn (false/*don't center under cursor*/);
+}
+
+// public slot
+void kpMainWindow::slotZoomOut ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotZoomOut ()" << endl;
+#endif
+
+ zoomOut (false/*don't center under cursor*/);
+}
+
+
+// public
+void kpMainWindow::zoomAccordingToZoomAction (bool centerUnderCursor)
+{
+ zoomTo (zoomLevelFromString (m_actionZoom->currentText ()),
+ centerUnderCursor);
+}
+
+// private slot
+void kpMainWindow::slotZoom ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotZoom () index=" << m_actionZoom->currentItem ()
+ << " text='" << m_actionZoom->currentText () << "'" << endl;
+#endif
+ zoomAccordingToZoomAction (false/*don't center under cursor*/);
+}
+
+
+// private slot
+void kpMainWindow::slotShowGridToggled ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotActionShowGridToggled()" << endl;
+#endif
+
+ updateMainViewGrid ();
+
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupGeneral);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ cfg->writeEntry (kpSettingShowGrid, m_configShowGrid = m_actionShowGrid->isChecked ());
+ cfg->sync ();
+}
+
+// private
+void kpMainWindow::updateMainViewGrid ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::updateMainViewGrid ()" << endl;
+#endif
+
+ if (m_mainView)
+ m_mainView->showGrid (m_actionShowGrid->isChecked ());
+}
+
+
+// private
+QRect kpMainWindow::mapToGlobal (const QRect &rect) const
+{
+ return kpWidgetMapper::toGlobal (this, rect);
+}
+
+// private
+QRect kpMainWindow::mapFromGlobal (const QRect &rect) const
+{
+ return kpWidgetMapper::fromGlobal (this, rect);
+}
+
+
+// public slot
+void kpMainWindow::slotDestroyThumbnailIfNotVisible (bool tnIsVisible)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotDestroyThumbnailIfNotVisible(isVisible="
+ << tnIsVisible
+ << ")"
+ << endl;
+#endif
+
+ if (!tnIsVisible)
+ {
+ slotDestroyThumbnailInitatedByUser ();
+ }
+}
+
+// private slot
+void kpMainWindow::slotDestroyThumbnail ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotDestroyThumbnail()" << endl;
+#endif
+
+ m_actionShowThumbnail->setChecked (false);
+ enableThumbnailOptionActions (false);
+ updateThumbnail ();
+}
+
+// private slot
+void kpMainWindow::slotDestroyThumbnailInitatedByUser ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotDestroyThumbnailInitiatedByUser()" << endl;
+#endif
+
+ m_actionShowThumbnail->setChecked (false);
+ slotShowThumbnailToggled ();
+}
+
+// private slot
+void kpMainWindow::slotCreateThumbnail ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotCreateThumbnail()" << endl;
+#endif
+
+ m_actionShowThumbnail->setChecked (true);
+ enableThumbnailOptionActions (true);
+ updateThumbnail ();
+}
+
+// public
+void kpMainWindow::notifyThumbnailGeometryChanged ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::notifyThumbnailGeometryChanged()" << endl;
+#endif
+
+ if (!m_thumbnailSaveConfigTimer)
+ {
+ m_thumbnailSaveConfigTimer = new QTimer (this);
+ connect (m_thumbnailSaveConfigTimer, SIGNAL (timeout ()),
+ this, SLOT (slotSaveThumbnailGeometry ()));
+ }
+
+ m_thumbnailSaveConfigTimer->start (500/*msec*/, true/*single shot*/);
+}
+
+// private slot
+void kpMainWindow::slotSaveThumbnailGeometry ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::saveThumbnailGeometry()" << endl;
+#endif
+
+ if (!m_thumbnail)
+ return;
+
+ QRect rect (m_thumbnail->x (), m_thumbnail->y (),
+ m_thumbnail->width (), m_thumbnail->height ());
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tthumbnail relative geometry=" << rect << endl;
+#endif
+
+ m_configThumbnailGeometry = mapFromGlobal (rect);
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tCONFIG: saving thumbnail geometry "
+ << m_configThumbnailGeometry
+ << endl;
+#endif
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupThumbnail);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ cfg->writeEntry (kpSettingThumbnailGeometry, m_configThumbnailGeometry);
+ cfg->sync ();
+}
+
+// private slot
+void kpMainWindow::slotShowThumbnailToggled ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotShowThumbnailToggled()" << endl;
+#endif
+
+ m_configThumbnailShown = m_actionShowThumbnail->isChecked ();
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupThumbnail);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ cfg->writeEntry (kpSettingThumbnailShown, m_configThumbnailShown);
+ cfg->sync ();
+
+
+ enableThumbnailOptionActions (m_actionShowThumbnail->isChecked ());
+ updateThumbnail ();
+}
+
+// private slot
+void kpMainWindow::updateThumbnailZoomed ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::updateThumbnailZoomed() zoomed="
+ << m_actionZoomedThumbnail->isChecked () << endl;
+#endif
+
+ if (!m_thumbnailView)
+ return;
+
+ destroyThumbnailView ();
+ createThumbnailView ();
+}
+
+// private slot
+void kpMainWindow::slotZoomedThumbnailToggled ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotZoomedThumbnailToggled()" << endl;
+#endif
+
+ m_configZoomedThumbnail = m_actionZoomedThumbnail->isChecked ();
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupThumbnail);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ cfg->writeEntry (kpSettingThumbnailZoomed, m_configZoomedThumbnail);
+ cfg->sync ();
+
+
+ updateThumbnailZoomed ();
+}
+
+// private slot
+void kpMainWindow::slotThumbnailShowRectangleToggled ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotThumbnailShowRectangleToggled()" << endl;
+#endif
+
+ d->m_configThumbnailShowRectangle = d->m_actionShowThumbnailRectangle->isChecked ();
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupThumbnail);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ cfg->writeEntry (kpSettingThumbnailShowRectangle, d->m_configThumbnailShowRectangle);
+ cfg->sync ();
+
+
+ if (m_thumbnailView)
+ {
+ m_thumbnailView->showBuddyViewScrollableContainerRectangle (
+ d->m_actionShowThumbnailRectangle->isChecked ());
+ }
+}
+
+// private
+void kpMainWindow::enableViewZoomedThumbnail (bool enable)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::enableSettingsViewZoomedThumbnail()" << endl;
+#endif
+
+ m_actionZoomedThumbnail->setEnabled (enable &&
+ m_actionShowThumbnail->isChecked ());
+
+ // Note: Don't uncheck if disabled - being able to see the zoomed state
+ // before turning on the thumbnail can be useful.
+ m_actionZoomedThumbnail->setChecked (m_configZoomedThumbnail);
+}
+
+// private
+void kpMainWindow::enableViewShowThumbnailRectangle (bool enable)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::enableViewShowThumbnailRectangle()" << endl;
+#endif
+
+ d->m_actionShowThumbnailRectangle->setEnabled (enable &&
+ m_actionShowThumbnail->isChecked ());
+
+ // Note: Don't uncheck if disabled for consistency with
+ // enableViewZoomedThumbnail()
+ d->m_actionShowThumbnailRectangle->setChecked (
+ d->m_configThumbnailShowRectangle);
+}
+
+// private
+void kpMainWindow::enableThumbnailOptionActions (bool enable)
+{
+ enableViewZoomedThumbnail (enable);
+ enableViewShowThumbnailRectangle (enable);
+}
+
+
+// private
+void kpMainWindow::createThumbnailView ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tcreating new kpView:" << endl;
+#endif
+
+ if (m_thumbnailView)
+ {
+ kdDebug () << "kpMainWindow::createThumbnailView() had to destroy view" << endl;
+ destroyThumbnailView ();
+ }
+
+ if (m_actionZoomedThumbnail->isChecked ())
+ {
+ m_thumbnailView = new kpZoomedThumbnailView (
+ m_document, m_toolToolBar, m_viewManager,
+ m_mainView,
+ 0/*scrollableContainer*/,
+ m_thumbnail, "thumbnailView");
+ }
+ else
+ {
+ m_thumbnailView = new kpUnzoomedThumbnailView (
+ m_document, m_toolToolBar, m_viewManager,
+ m_mainView,
+ 0/*scrollableContainer*/,
+ m_thumbnail, "thumbnailView");
+
+ }
+
+ m_thumbnailView->showBuddyViewScrollableContainerRectangle (
+ d->m_actionShowThumbnailRectangle->isChecked ());
+
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tgive kpThumbnail the kpView:" << endl;
+#endif
+ if (m_thumbnail)
+ m_thumbnail->setView (m_thumbnailView);
+ else
+ kdError () << "kpMainWindow::createThumbnailView() no thumbnail" << endl;
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tregistering the kpView:" << endl;
+#endif
+ if (m_viewManager)
+ m_viewManager->registerView (m_thumbnailView);
+}
+
+// private
+void kpMainWindow::destroyThumbnailView ()
+{
+ if (!m_thumbnailView)
+ return;
+
+ if (m_viewManager)
+ m_viewManager->unregisterView (m_thumbnailView);
+
+ if (m_thumbnail)
+ m_thumbnail->setView (0);
+
+ m_thumbnailView->deleteLater (); m_thumbnailView = 0;
+}
+
+
+// private
+void kpMainWindow::updateThumbnail ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::updateThumbnail()" << endl;
+#endif
+ bool enable = m_actionShowThumbnail->isChecked ();
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tthumbnail="
+ << bool (m_thumbnail)
+ << " action_isChecked="
+ << enable
+ << endl;
+#endif
+
+ if (bool (m_thumbnail) == enable)
+ return;
+
+ if (!m_thumbnail)
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tcreating thumbnail" << endl;
+ #endif
+
+ // Read last saved geometry before creating thumbnail & friends
+ // in case they call notifyThumbnailGeometryChanged()
+ QRect thumbnailGeometry = m_configThumbnailGeometry;
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tlast used geometry=" << thumbnailGeometry << endl;
+ #endif
+
+ m_thumbnail = new kpThumbnail (this, "thumbnail");
+
+ createThumbnailView ();
+
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tmoving thumbnail to right place" << endl;
+ #endif
+ if (!thumbnailGeometry.isEmpty () &&
+ QRect (0, 0, width (), height ()).intersects (thumbnailGeometry))
+ {
+ const QRect geometry = mapToGlobal (thumbnailGeometry);
+ m_thumbnail->resize (geometry.size ());
+ m_thumbnail->move (geometry.topLeft ());
+ }
+ else
+ {
+ if (m_scrollView)
+ {
+ const int margin = 20;
+ const int initialWidth = 160, initialHeight = 120;
+
+ QRect geometryRect (width () - initialWidth - margin * 2,
+ m_scrollView->y () + margin,
+ initialWidth,
+ initialHeight);
+
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tcreating geometry=" << geometryRect << endl;
+ #endif
+
+ geometryRect = mapToGlobal (geometryRect);
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tmap to global=" << geometryRect << endl;
+ #endif
+ m_thumbnail->resize (geometryRect.size ());
+ m_thumbnail->move (geometryRect.topLeft ());
+ }
+ }
+
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tshowing thumbnail" << endl;
+ #endif
+ m_thumbnail->show ();
+
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tconnecting thumbnail::visibilityChange to destroy slot" << endl;
+ #endif
+ connect (m_thumbnail, SIGNAL (visibilityChanged (bool)),
+ this, SLOT (slotDestroyThumbnailIfNotVisible (bool)));
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tDONE" << endl;
+ #endif
+ }
+ else
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tdestroying thumbnail" << endl;
+ #endif
+
+ if (m_thumbnailSaveConfigTimer && m_thumbnailSaveConfigTimer->isActive ())
+ {
+ m_thumbnailSaveConfigTimer->stop ();
+ slotSaveThumbnailGeometry ();
+ }
+
+
+ destroyThumbnailView ();
+
+
+ disconnect (m_thumbnail, SIGNAL (visibilityChanged (bool)),
+ this, SLOT (slotDestroyThumbnailIfNotVisible (bool)));
+
+ m_thumbnail->deleteLater (); m_thumbnail = 0;
+ }
+}