1 // REFACTOR: Clean up bits of this file
2 
3 /*
4    Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
5    All rights reserved.
6 
7    Redistribution and use in source and binary forms, with or without
8    modification, are permitted provided that the following conditions
9    are met:
10 
11    1. Redistributions of source code must retain the above copyright
12       notice, this list of conditions and the following disclaimer.
13    2. Redistributions in binary form must reproduce the above copyright
14       notice, this list of conditions and the following disclaimer in the
15       documentation and/or other materials provided with the distribution.
16 
17    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20    IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28 
29 
30 #include "mainWindow/kpMainWindow.h"
31 #include "kpMainWindowPrivate.h"
32 #include "kpLogCategories.h"
33 
34 #include <QScrollBar>
35 
36 #include <KSelectAction>
37 #include <KStandardAction>
38 #include <KToggleAction>
39 #include <KActionCollection>
40 #include <KLocalizedString>
41 
42 #include "kpDefs.h"
43 #include "document/kpDocument.h"
44 #include "kpThumbnail.h"
45 #include "tools/kpTool.h"
46 #include "widgets/toolbars/kpToolToolBar.h"
47 #include "views/kpUnzoomedThumbnailView.h"
48 #include "views/manager/kpViewManager.h"
49 #include "kpViewScrollableContainer.h"
50 #include "generic/kpWidgetMapper.h"
51 #include "views/kpZoomedView.h"
52 #include "views/kpZoomedThumbnailView.h"
53 
ZoomLevelFromString(const QString & stringIn)54 static int ZoomLevelFromString (const QString &stringIn)
55 {
56 #if DEBUG_KP_MAIN_WINDOW
57     qCDebug(kpLogMainWindow) << "kpMainWindow_View.cpp:ZoomLevelFromString(" << stringIn << ")";
58 #endif
59 
60     // Remove any non-digits kdelibs sometimes adds behind our back :( e.g.:
61     //
62     // 1. kdelibs adds accelerators to actions' text directly
63     // 2. ',' is automatically added to change "1000%" to "1,000%"
64     QString string = stringIn;
65     string.remove (QRegExp ("[^0-9]"));
66 #if DEBUG_KP_MAIN_WINDOW
67     qCDebug(kpLogMainWindow) << "\twithout non-digits='" << string << "'";
68 #endif
69 
70     // Convert zoom level to number.
71     bool ok = false;
72     int zoomLevel = string.toInt (&ok);
73 #if DEBUG_KP_MAIN_WINDOW
74     qCDebug(kpLogMainWindow) << "\tzoomLevel=" << zoomLevel;
75 #endif
76 
77     if (!ok || zoomLevel < kpView::MinZoomLevel || zoomLevel > kpView::MaxZoomLevel) {
78         return 0;  // error
79     }
80 
81     return zoomLevel;
82 }
83 
84 //---------------------------------------------------------------------
85 
ZoomLevelToString(int zoomLevel)86 static QString ZoomLevelToString (int zoomLevel)
87 {
88     return i18n ("%1%", zoomLevel);
89 }
90 
91 //---------------------------------------------------------------------
92 
93 // private
setupViewMenuZoomActions()94 void kpMainWindow::setupViewMenuZoomActions ()
95 {
96     KActionCollection *ac = actionCollection ();
97 
98 
99     d->actionActualSize = KStandardAction::actualSize (this, SLOT (slotActualSize()), ac);
100     d->actionFitToPage = KStandardAction::fitToPage (this, SLOT (slotFitToPage()), ac);
101     d->actionFitToWidth = KStandardAction::fitToWidth (this, SLOT (slotFitToWidth()), ac);
102     d->actionFitToHeight = KStandardAction::fitToHeight (this, SLOT (slotFitToHeight()), ac);
103 
104 
105     d->actionZoomIn = KStandardAction::zoomIn (this, SLOT (slotZoomIn()), ac);
106     d->actionZoomOut = KStandardAction::zoomOut (this, SLOT (slotZoomOut()), ac);
107 
108 
109     d->actionZoom = ac->add <KSelectAction> (QStringLiteral("view_zoom_to"));
110     d->actionZoom->setText (i18n ("&Zoom"));
111     connect (d->actionZoom,
112              static_cast<void (KSelectAction::*)(QAction*)>(&KSelectAction::triggered),
113              this, &kpMainWindow::slotZoom);
114     d->actionZoom->setEditable (true);
115 
116     // create the zoom list for the 1st call to zoomTo() below
117     d->zoomList.append (10); d->zoomList.append (25); d->zoomList.append (33);
118     d->zoomList.append (50); d->zoomList.append (67); d->zoomList.append (75);
119     d->zoomList.append (100);
120     d->zoomList.append (200); d->zoomList.append (300);
121     d->zoomList.append (400); d->zoomList.append (600); d->zoomList.append (800);
122     d->zoomList.append (1000); d->zoomList.append (1200); d->zoomList.append (1600);
123 }
124 
125 //---------------------------------------------------------------------
126 
127 // private
enableViewMenuZoomDocumentActions(bool enable)128 void kpMainWindow::enableViewMenuZoomDocumentActions (bool enable)
129 {
130     d->actionActualSize->setEnabled (enable);
131     d->actionFitToPage->setEnabled (enable);
132     d->actionFitToWidth->setEnabled (enable);
133     d->actionFitToHeight->setEnabled (enable);
134 
135     d->actionZoomIn->setEnabled (enable);
136     d->actionZoomOut->setEnabled (enable);
137     d->actionZoom->setEnabled (enable);
138 
139 
140     // TODO: for the time being, assume that we start at zoom 100%
141     //       with no grid
142 
143     // This function is only called when a new document is created
144     // or an existing document is closed.  So the following will
145     // always be correct:
146 
147     zoomTo (100);
148 }
149 
150 //---------------------------------------------------------------------
151 
152 // private
sendZoomListToActionZoom()153 void kpMainWindow::sendZoomListToActionZoom ()
154 {
155     QStringList items;
156 
157     const QList <int>::ConstIterator zoomListEnd (d->zoomList.end ());
158     for (QList <int>::ConstIterator it = d->zoomList.constBegin ();
159          it != zoomListEnd;
160          ++it)
161     {
162         items << ::ZoomLevelToString (*it);
163     }
164 
165     // Work around a KDE bug - KSelectAction::setItems() enables the action.
166     // David Faure said it won't be fixed because it's a feature used by
167     // KRecentFilesAction.
168     bool e = d->actionZoom->isEnabled ();
169     d->actionZoom->setItems (items);
170     if (e != d->actionZoom->isEnabled ()) {
171         d->actionZoom->setEnabled (e);
172     }
173 }
174 
175 //---------------------------------------------------------------------
176 
177 // private
zoomToPre(int zoomLevel)178 void kpMainWindow::zoomToPre (int zoomLevel)
179 {
180     // We're called quite early in the init process and/or when there might
181     // not be a document or a view so we have a lot of "if (ptr)" guards.
182 
183 #if DEBUG_KP_MAIN_WINDOW
184     qCDebug(kpLogMainWindow) << "kpMainWindow::zoomToPre(" << zoomLevel << ")";
185 #endif
186 
187     zoomLevel = qBound (kpView::MinZoomLevel, zoomLevel, kpView::MaxZoomLevel);
188 
189     int index = 0;
190     QList <int>::Iterator it = d->zoomList.begin ();
191 
192     while (index < d->zoomList.count () && zoomLevel > *it)
193     {
194         it++;
195         index++;
196     }
197 
198     if (zoomLevel != *it) {
199         d->zoomList.insert (it, zoomLevel);
200     }
201 
202     // OPT: We get called twice on startup.  sendZoomListToActionZoom() is very slow.
203     sendZoomListToActionZoom ();
204 
205 #if DEBUG_KP_MAIN_WINDOW
206     qCDebug(kpLogMainWindow) << "\tsetCurrentItem(" << index << ")";
207 #endif
208     d->actionZoom->setCurrentItem (index);
209 #if DEBUG_KP_MAIN_WINDOW
210     qCDebug(kpLogMainWindow) << "\tcurrentItem="
211               << d->actionZoom->currentItem ()
212               << " action="
213               << d->actionZoom->action (d->actionZoom->currentItem ())
214               << " checkedAction"
215               << d->actionZoom->selectableActionGroup ()->checkedAction ();
216 #endif
217 
218 
219     if (viewMenuDocumentActionsEnabled ())
220     {
221         d->actionActualSize->setEnabled (zoomLevel != 100);
222 
223         d->actionZoomIn->setEnabled (d->actionZoom->currentItem () < d->zoomList.count () - 1);
224         d->actionZoomOut->setEnabled (d->actionZoom->currentItem () > 0);
225     }
226 
227 
228     // TODO: Is this actually needed?
229     if (d->viewManager) {
230         d->viewManager->setQueueUpdates ();
231     }
232 
233     if (d->scrollView)
234     {
235         d->scrollView->setUpdatesEnabled (false);
236     }
237 }
238 
239 //---------------------------------------------------------------------
240 
241 // private
zoomToPost()242 void kpMainWindow::zoomToPost ()
243 {
244 #if DEBUG_KP_MAIN_WINDOW && 1
245     qCDebug(kpLogMainWindow) << "kpMainWindow::zoomToPost()";
246 #endif
247 
248     if (d->mainView)
249     {
250         actionShowGridUpdate ();
251         updateMainViewGrid ();
252 
253         // Since Zoom Level KSelectAction on ToolBar grabs focus after changing
254         // Zoom, switch back to the Main View.
255         // TODO: back to the last view
256         d->mainView->setFocus ();
257 
258     }
259 
260 
261     // The view magnified and moved beneath the cursor
262     if (tool ()) {
263         tool ()->somethingBelowTheCursorChanged ();
264     }
265 
266 
267     if (d->scrollView)
268     {
269         // TODO: setUpdatesEnabled() should really return to old value
270         //       - not necessarily "true"
271         d->scrollView->setUpdatesEnabled (true);
272     }
273 
274     if (d->viewManager && d->viewManager->queueUpdates ()/*just in case*/) {
275         d->viewManager->restoreQueueUpdates ();
276     }
277 
278     setStatusBarZoom (d->mainView ? d->mainView->zoomLevelX () : 0);
279 
280 #if DEBUG_KP_MAIN_WINDOW && 1
281     qCDebug(kpLogMainWindow) << "kpMainWindow::zoomToPost() done";
282 #endif
283 }
284 
285 //---------------------------------------------------------------------
286 
287 // private
zoomTo(int zoomLevel,bool centerUnderCursor)288 void kpMainWindow::zoomTo (int zoomLevel, bool centerUnderCursor)
289 {
290     zoomToPre (zoomLevel);
291 
292 
293     if (d->scrollView && d->mainView)
294     {
295     #if DEBUG_KP_MAIN_WINDOW && 1
296         qCDebug(kpLogMainWindow) << "\tscrollView   contentsX=" << d->scrollView->horizontalScrollBar()->value ()
297                    << " contentsY=" << d->scrollView->verticalScrollBar()->value ()
298                    << " contentsWidth=" << d->scrollView->widget()->width ()
299                    << " contentsHeight=" << d->scrollView->widget()->height ()
300                    << " visibleWidth=" << d->scrollView->viewport()->width ()
301                    << " visibleHeight=" << d->scrollView->viewport()->height ()
302                    << " oldZoomX=" << d->mainView->zoomLevelX ()
303                    << " oldZoomY=" << d->mainView->zoomLevelY ()
304                    << " newZoom=" << zoomLevel;
305     #endif
306 
307         // TODO: when changing from no scrollbars to scrollbars, Qt lies about
308         //       visibleWidth() & visibleHeight() (doesn't take into account the
309         //       space taken by the would-be scrollbars) until it updates the
310         //       scrollview; hence the centering is off by about 5-10 pixels.
311 
312         // TODO: Use visibleRect() for greater accuracy?
313         //       Or use kpAbstractScrollAreaUtils::EstimateUsableArea()
314         //       instead of ScrollView::visible{Width,Height}(), as
315         //       per zoomToRect()?
316 
317         int viewX, viewY;
318 
319         bool targetDocAvail = false;
320         double targetDocX = -1, targetDocY = -1;
321 
322         if (centerUnderCursor &&
323             d->viewManager && d->viewManager->viewUnderCursor ())
324         {
325             kpView *const vuc = d->viewManager->viewUnderCursor ();
326             QPoint viewPoint = vuc->mouseViewPoint ();
327 
328             // vuc->transformViewToDoc() returns QPoint which only has int
329             // accuracy so we do X and Y manually.
330             targetDocX = vuc->transformViewToDocX (viewPoint.x ());
331             targetDocY = vuc->transformViewToDocY (viewPoint.y ());
332             targetDocAvail = true;
333 
334             if (vuc != d->mainView) {
335                 viewPoint = vuc->transformViewToOtherView (viewPoint, d->mainView);
336             }
337 
338             viewX = viewPoint.x ();
339             viewY = viewPoint.y ();
340         }
341         else
342         {
343             viewX = d->scrollView->horizontalScrollBar()->value () +
344                         qMin (d->mainView->width (),
345                               d->scrollView->viewport()->width ()) / 2;
346             viewY = d->scrollView->verticalScrollBar()->value () +
347                         qMin (d->mainView->height (),
348                               d->scrollView->viewport()->height ()) / 2;
349         }
350 
351 
352         int newCenterX = viewX * zoomLevel / d->mainView->zoomLevelX ();
353         int newCenterY = viewY * zoomLevel / d->mainView->zoomLevelY ();
354 
355         // Do the zoom.
356         d->mainView->setZoomLevel (zoomLevel, zoomLevel);
357 
358     #if DEBUG_KP_MAIN_WINDOW && 1
359         qCDebug(kpLogMainWindow) << "\tvisibleWidth=" << d->scrollView->viewport()->width ()
360                     << " visibleHeight=" << d->scrollView->viewport()->height ();
361         qCDebug(kpLogMainWindow) << "\tnewCenterX=" << newCenterX
362                     << " newCenterY=" << newCenterY;
363     #endif
364 
365         d->scrollView->horizontalScrollBar()->setValue(newCenterX - (d->scrollView->viewport()->width() / 2));
366         d->scrollView->verticalScrollBar()->setValue(newCenterY - (d->scrollView->viewport()->height() / 2));
367 
368         if (centerUnderCursor &&
369             targetDocAvail &&
370             d->viewManager && d->viewManager->viewUnderCursor ())
371         {
372             // Move the mouse cursor so that it is still above the same
373             // document pixel as before the zoom.
374 
375             kpView *const vuc = d->viewManager->viewUnderCursor ();
376 
377         #if DEBUG_KP_MAIN_WINDOW
378             qCDebug(kpLogMainWindow) << "\tcenterUnderCursor: reposition cursor; viewUnderCursor="
379                        << vuc->objectName ();
380         #endif
381 
382             const auto viewX = vuc->transformDocToViewX (targetDocX);
383             const auto viewY = vuc->transformDocToViewY (targetDocY);
384             // Rounding error from zooming in and out :(
385             // TODO: do everything in terms of tool doc points in type "double".
386             const QPoint viewPoint (static_cast<int> (viewX), static_cast<int> (viewY));
387         #if DEBUG_KP_MAIN_WINDOW
388             qCDebug(kpLogMainWindow) << "\t\tdoc: (" << targetDocX << "," << targetDocY << ")"
389                        << " viewUnderCursor: (" << viewX << "," << viewY << ")";
390         #endif
391 
392             if (vuc->visibleRegion ().contains (viewPoint))
393             {
394                 const QPoint globalPoint =
395                     kpWidgetMapper::toGlobal (vuc, viewPoint);
396             #if DEBUG_KP_MAIN_WINDOW
397                 qCDebug(kpLogMainWindow) << "\t\tglobalPoint=" << globalPoint;
398             #endif
399 
400                 // TODO: Determine some sane cursor flashing indication -
401                 //       cursor movement is convenient but not conventional.
402                 //
403                 //       Major problem: if using QApplication::setOverrideCursor()
404                 //           and in some stage of flash and window quits.
405                 //
406                 //           Or if using kpView::setCursor() and change tool.
407                 QCursor::setPos (globalPoint);
408             }
409             // e.g. Zoom to 200%, scroll mainView to bottom-right.
410             // Unzoomed Thumbnail shows top-left portion of bottom-right of
411             // mainView.
412             //
413             // Aim cursor at bottom-right of thumbnail and zoom out with
414             // CTRL+Wheel.
415             //
416             // If mainView is now small enough to largely not need scrollbars,
417             // Unzoomed Thumbnail scrolls to show _top-left_ portion
418             // _of top-left_ of mainView.
419             //
420             // Unzoomed Thumbnail no longer contains the point we zoomed out
421             // on top of.
422             else
423             {
424             #if DEBUG_KP_MAIN_WINDOW
425                 qCDebug(kpLogMainWindow) << "\t\twon't move cursor - would get outside view";
426             #endif
427 
428                 // TODO: Sane cursor flashing indication that indicates
429                 //       that the normal cursor movement didn't happen.
430             }
431         }
432 
433     #if DEBUG_KP_MAIN_WINDOW && 1
434         qCDebug(kpLogMainWindow) << "\t\tcheck (contentsX=" << d->scrollView->horizontalScrollBar()->value ()
435                     << ",contentsY=" << d->scrollView->verticalScrollBar()->value ()
436                     << ")";
437     #endif
438     }
439 
440 
441     zoomToPost ();
442 }
443 
444 //---------------------------------------------------------------------
445 
446 // private
zoomToRect(const QRect & normalizedDocRect,bool accountForGrips,bool careAboutWidth,bool careAboutHeight)447 void kpMainWindow::zoomToRect (const QRect &normalizedDocRect,
448         bool accountForGrips,
449         bool careAboutWidth, bool careAboutHeight)
450 {
451 #if DEBUG_KP_MAIN_WINDOW
452     qCDebug(kpLogMainWindow) << "kpMainWindow::zoomToRect(normalizedDocRect="
453               << normalizedDocRect
454               << ",accountForGrips=" << accountForGrips
455               << ",careAboutWidth=" << careAboutWidth
456               << ",careAboutHeight=" << careAboutHeight
457               << ")";
458 #endif
459     // You can't care about nothing.
460     Q_ASSERT (careAboutWidth || careAboutHeight);
461 
462     // The size of the scroll view minus the current or future scrollbars.
463     const QSize usableScrollArea
464       (d->scrollView->maximumViewportSize().width() - d->scrollView->verticalScrollBar()->sizeHint().width(),
465        d->scrollView->maximumViewportSize().height() - d->scrollView->horizontalScrollBar()->sizeHint().height());
466 
467 #if DEBUG_KP_MAIN_WINDOW
468     qCDebug(kpLogMainWindow) << "size=" << d->scrollView->maximumViewportSize()
469               << "scrollbar w=" << d->scrollView->verticalScrollBar()->sizeHint().width()
470               << "usableSize=" << usableScrollArea;
471 #endif
472     // Handle rounding error, mis-estimating the scroll view size and
473     // cosmic rays.  We do this because we really don't want unnecessary
474     // scrollbars.  This seems to need to be at least 2 for slotFitToWidth()
475     // and friends.
476     // least 2.
477     // TODO: I might have fixed this but check later.
478     const int slack = 0;
479 
480     // The grip and slack are in view coordinates but are never zoomed.
481     const int viewWidth =
482         usableScrollArea.width () -
483         (accountForGrips ? kpGrip::Size : 0) -
484         slack;
485     const int viewHeight =
486         usableScrollArea.height () -
487         (accountForGrips ? kpGrip::Size : 0) -
488         slack;
489 
490     // We want the selected document rectangle to fill the scroll view.
491     //
492     // The integer arithmetic rounds down, rather than to the nearest zoom
493     // level, as rounding down guarantees that the view, at the zoom level,
494     // will fit inside <viewWidth> x <viewHeight>.
495     const int zoomX =
496         careAboutWidth ?
497             qMax (1, viewWidth * 100 / normalizedDocRect.width ()) :
498             INT_MAX;
499     const int zoomY =
500         careAboutHeight ?
501             qMax (1, viewHeight * 100 / normalizedDocRect.height ()) :
502             INT_MAX;
503 
504     // Since kpView only supports identical horizontal and vertical zooms,
505     // choose the one that will show the greatest amount of document
506     // content.
507     const int zoomLevel = qMin (zoomX, zoomY);
508 
509 #if DEBUG_KP_MAIN_WINDOW
510     qCDebug(kpLogMainWindow) << "\tzoomX=" << zoomX
511         << " zoomY=" << zoomY
512         << " -> zoomLevel=" << zoomLevel;
513 #endif
514 
515     zoomToPre (zoomLevel);
516     {
517         d->mainView->setZoomLevel (zoomLevel, zoomLevel);
518 
519         const QPoint viewPoint =
520             d->mainView->transformDocToView (normalizedDocRect.topLeft ());
521 
522         d->scrollView->horizontalScrollBar()->setValue(viewPoint.x());
523         d->scrollView->verticalScrollBar()->setValue(viewPoint.y());
524     }
525     zoomToPost ();
526 }
527 
528 //---------------------------------------------------------------------
529 
530 // public slot
slotActualSize()531 void kpMainWindow::slotActualSize ()
532 {
533     zoomTo (100);
534 }
535 
536 //---------------------------------------------------------------------
537 
538 // public slot
slotFitToPage()539 void kpMainWindow::slotFitToPage ()
540 {
541   if ( d->document )
542   {
543     zoomToRect (
544         d->document->rect (),
545         true/*account for grips*/,
546         true/*care about width*/, true/*care about height*/);
547   }
548 }
549 
550 //---------------------------------------------------------------------
551 
552 // public slot
slotFitToWidth()553 void kpMainWindow::slotFitToWidth ()
554 {
555   if ( d->document )
556   {
557     const QRect docRect (
558         0/*x*/,
559         static_cast<int> (d->mainView->transformViewToDocY (d->scrollView->verticalScrollBar()->value ()))/*maintain y*/,
560                 d->document->width (),
561                 1/*don't care about height*/);
562     zoomToRect (
563         docRect,
564         true/*account for grips*/,
565         true/*care about width*/, false/*don't care about height*/);
566   }
567 }
568 
569 //---------------------------------------------------------------------
570 
571 // public slot
slotFitToHeight()572 void kpMainWindow::slotFitToHeight ()
573 {
574   if ( d->document )
575   {
576     const QRect docRect (
577         static_cast<int> (d->mainView->transformViewToDocX (d->scrollView->horizontalScrollBar()->value ()))/*maintain x*/,
578                 0/*y*/,
579                 1/*don't care about width*/,
580                 d->document->height ());
581     zoomToRect (
582         docRect,
583         true/*account for grips*/,
584         false/*don't care about width*/, true/*care about height*/);
585   }
586 }
587 
588 //---------------------------------------------------------------------
589 
590 // public
zoomIn(bool centerUnderCursor)591 void kpMainWindow::zoomIn (bool centerUnderCursor)
592 {
593 #if DEBUG_KP_MAIN_WINDOW
594     qCDebug(kpLogMainWindow) << "kpMainWindow::zoomIn(centerUnderCursor="
595               << centerUnderCursor << ") currentItem="
596               << d->actionZoom->currentItem ();
597 #endif
598     const int targetItem = d->actionZoom->currentItem () + 1;
599 
600     if (targetItem >= static_cast<int> (d->zoomList.count ())) {
601         return;
602     }
603 
604     d->actionZoom->setCurrentItem (targetItem);
605 
606 #if DEBUG_KP_MAIN_WINDOW
607     qCDebug(kpLogMainWindow) << "\tnew currentItem=" << d->actionZoom->currentItem ();
608 #endif
609 
610     zoomAccordingToZoomAction (centerUnderCursor);
611 }
612 
613 //---------------------------------------------------------------------
614 
615 // public
zoomOut(bool centerUnderCursor)616 void kpMainWindow::zoomOut (bool centerUnderCursor)
617 {
618 #if DEBUG_KP_MAIN_WINDOW
619     qCDebug(kpLogMainWindow) << "kpMainWindow::zoomOut(centerUnderCursor="
620               << centerUnderCursor << ") currentItem="
621               << d->actionZoom->currentItem ();
622 #endif
623     const int targetItem = d->actionZoom->currentItem () - 1;
624 
625     if (targetItem < 0) {
626         return;
627     }
628 
629     d->actionZoom->setCurrentItem (targetItem);
630 
631 #if DEBUG_KP_MAIN_WINDOW
632     qCDebug(kpLogMainWindow) << "\tnew currentItem=" << d->actionZoom->currentItem ();
633 #endif
634 
635     zoomAccordingToZoomAction (centerUnderCursor);
636 }
637 
638 //---------------------------------------------------------------------
639 
640 // public slot
slotZoomIn()641 void kpMainWindow::slotZoomIn ()
642 {
643 #if DEBUG_KP_MAIN_WINDOW
644     qCDebug(kpLogMainWindow) << "kpMainWindow::slotZoomIn ()";
645 #endif
646 
647     zoomIn (false/*don't center under cursor*/);
648 }
649 
650 //---------------------------------------------------------------------
651 
652 // public slot
slotZoomOut()653 void kpMainWindow::slotZoomOut ()
654 {
655 #if DEBUG_KP_MAIN_WINDOW
656     qCDebug(kpLogMainWindow) << "kpMainWindow::slotZoomOut ()";
657 #endif
658 
659     zoomOut (false/*don't center under cursor*/);
660 }
661 
662 //---------------------------------------------------------------------
663 
664 // public
zoomAccordingToZoomAction(bool centerUnderCursor)665 void kpMainWindow::zoomAccordingToZoomAction (bool centerUnderCursor)
666 {
667 #if DEBUG_KP_MAIN_WINDOW
668     qCDebug(kpLogMainWindow) << "kpMainWindow::zoomAccordingToZoomAction(centerUnderCursor="
669               << centerUnderCursor
670               << ") currentItem=" << d->actionZoom->currentItem ()
671               << " currentText=" << d->actionZoom->currentText ();
672 #endif
673 
674     // This might be a new zoom level the user has typed in.
675     zoomTo (::ZoomLevelFromString (d->actionZoom->currentText ()),
676                                    centerUnderCursor);
677 }
678 
679 //---------------------------------------------------------------------
680 
681 // private slot
slotZoom()682 void kpMainWindow::slotZoom ()
683 {
684 #if DEBUG_KP_MAIN_WINDOW
685     qCDebug(kpLogMainWindow) << "kpMainWindow::slotZoom () index=" << d->actionZoom->currentItem ()
686                << " text='" << d->actionZoom->currentText () << "'";
687 #endif
688 
689     zoomAccordingToZoomAction (false/*don't center under cursor*/);
690 }
691 
692 //---------------------------------------------------------------------
693