You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1911 lines
53 KiB
1911 lines
53 KiB
|
|
/*
|
|
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_KP_VIEW 0
|
|
#define DEBUG_KP_VIEW_RENDERER ((DEBUG_KP_VIEW && 0) || 0)
|
|
|
|
|
|
#include <kpview.h>
|
|
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <tqbitmap.h>
|
|
#include <tqcursor.h>
|
|
#include <tqdragobject.h>
|
|
#include <tqguardedptr.h>
|
|
#include <tqimage.h>
|
|
#include <tqpainter.h>
|
|
#include <tqpixmap.h>
|
|
#include <tqpoint.h>
|
|
#include <tqrect.h>
|
|
#include <tqregion.h>
|
|
#include <tqmemarray.h>
|
|
|
|
#if DEBUG_KP_VIEW || DEBUG_KP_VIEW_RENDERER
|
|
#include <tqdatetime.h>
|
|
#endif
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <kpdefs.h>
|
|
#include <kpdocument.h>
|
|
#include <kppixmapfx.h>
|
|
#include <kpselection.h>
|
|
#include <kptemppixmap.h>
|
|
#include <kptool.h>
|
|
#include <kptooltoolbar.h>
|
|
#include <kpviewmanager.h>
|
|
#include <kpviewscrollablecontainer.h>
|
|
|
|
|
|
struct kpViewPrivate
|
|
{
|
|
// sync: kpView::paintEvent()
|
|
//
|
|
// Normally, these pointers must be valid while the kpView is alive.
|
|
// Generally, the objects they point to are deleted only after kpView
|
|
// is deleted.
|
|
//
|
|
// However, sometimes we use deleteLater() for the kpView.
|
|
// Before the delayed deletion is executed, those objects are deleted
|
|
// and then our paintEvent() is called. paintEvent() must therefore
|
|
// have some way of realising that those objects have been deleted so
|
|
// we use guardded pointers.
|
|
//
|
|
// For more details, see SVN commit:
|
|
// "r385274 | dang | 2005-02-02 22:08:27 +1100 (Wed, 02 Feb 2005) | 21 lines".
|
|
TQGuardedPtr <kpDocument> m_document;
|
|
TQGuardedPtr <kpToolToolBar> m_toolToolBar;
|
|
TQGuardedPtr <kpViewManager> m_viewManager;
|
|
TQGuardedPtr <kpView> m_buddyView;
|
|
TQGuardedPtr <kpViewScrollableContainer> m_scrollableContainer;
|
|
|
|
int m_hzoom, m_vzoom;
|
|
TQPoint m_origin;
|
|
bool m_showGrid;
|
|
bool m_isBuddyViewScrollableContainerRectangleShown;
|
|
TQRect m_buddyViewScrollableContainerRectangle;
|
|
|
|
TQRegion m_queuedUpdateArea;
|
|
TQPixmap *m_backBuffer;
|
|
};
|
|
|
|
|
|
kpView::kpView (kpDocument *document,
|
|
kpToolToolBar *toolToolBar,
|
|
kpViewManager *viewManager,
|
|
kpView *buddyView,
|
|
kpViewScrollableContainer *scrollableContainer,
|
|
TQWidget *parent, const char *name)
|
|
|
|
: TQWidget (parent, name, TQt::WNoAutoErase/*no flicker*/),
|
|
d (new kpViewPrivate ())
|
|
{
|
|
d->m_document = document;
|
|
d->m_toolToolBar = toolToolBar;
|
|
d->m_viewManager = viewManager;
|
|
d->m_buddyView = buddyView;
|
|
d->m_scrollableContainer = scrollableContainer;
|
|
|
|
d->m_hzoom = 100, d->m_vzoom = 100;
|
|
d->m_origin = TQPoint (0, 0);
|
|
d->m_showGrid = false;
|
|
d->m_isBuddyViewScrollableContainerRectangleShown = false;
|
|
|
|
d->m_backBuffer = 0;
|
|
|
|
|
|
setBackgroundMode (TQt::NoBackground); // no flicker
|
|
setFocusPolicy (TQWidget::WheelFocus);
|
|
setMouseTracking (true); // mouseMoveEvent's even when no mousebtn down
|
|
setKeyCompression (true);
|
|
setInputMethodEnabled (true); // ensure using InputMethod
|
|
}
|
|
|
|
kpView::~kpView ()
|
|
{
|
|
setHasMouse (false);
|
|
|
|
delete d->m_backBuffer;
|
|
delete d;
|
|
}
|
|
|
|
|
|
// public
|
|
kpDocument *kpView::document () const
|
|
{
|
|
return d->m_document;
|
|
}
|
|
|
|
// protected
|
|
kpSelection *kpView::selection () const
|
|
{
|
|
return document () ? document ()->selection () : 0;
|
|
}
|
|
|
|
// public
|
|
kpToolToolBar *kpView::toolToolBar () const
|
|
{
|
|
return d->m_toolToolBar;
|
|
}
|
|
|
|
// protected
|
|
kpTool *kpView::tool () const
|
|
{
|
|
return toolToolBar () ? toolToolBar ()->tool () : 0;
|
|
}
|
|
|
|
// public
|
|
kpViewManager *kpView::viewManager () const
|
|
{
|
|
return d->m_viewManager;
|
|
}
|
|
|
|
// public
|
|
kpView *kpView::buddyView () const
|
|
{
|
|
return d->m_buddyView;
|
|
}
|
|
|
|
// public
|
|
kpViewScrollableContainer *kpView::buddyViewScrollableContainer () const
|
|
{
|
|
return (buddyView () ? buddyView ()->scrollableContainer () : 0);
|
|
}
|
|
|
|
// public
|
|
kpViewScrollableContainer *kpView::scrollableContainer () const
|
|
{
|
|
return d->m_scrollableContainer;
|
|
}
|
|
|
|
|
|
// public
|
|
int kpView::zoomLevelX (void) const
|
|
{
|
|
return d->m_hzoom;
|
|
}
|
|
|
|
// public
|
|
int kpView::zoomLevelY (void) const
|
|
{
|
|
return d->m_vzoom;
|
|
}
|
|
|
|
// public virtual
|
|
void kpView::setZoomLevel (int hzoom, int vzoom)
|
|
{
|
|
if (hzoom == d->m_hzoom && vzoom == d->m_vzoom)
|
|
return;
|
|
|
|
if (hzoom <= 0 || vzoom <= 0)
|
|
return;
|
|
|
|
d->m_hzoom = hzoom;
|
|
d->m_vzoom = vzoom;
|
|
|
|
if (viewManager ())
|
|
viewManager ()->updateView (this);
|
|
|
|
emit zoomLevelChanged (hzoom, vzoom);
|
|
}
|
|
|
|
|
|
// public
|
|
TQPoint kpView::origin () const
|
|
{
|
|
return d->m_origin;
|
|
}
|
|
|
|
// public virtual
|
|
void kpView::setOrigin (const TQPoint &origin)
|
|
{
|
|
#if DEBUG_KP_VIEW
|
|
kdDebug () << "kpView(" << name () << ")::setOrigin" << origin << endl;
|
|
#endif
|
|
|
|
if (origin == d->m_origin)
|
|
{
|
|
#if DEBUG_KP_VIEW
|
|
kdDebug () << "\tNOP" << endl;
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
d->m_origin = origin;
|
|
|
|
if (viewManager ())
|
|
viewManager ()->updateView (this);
|
|
|
|
emit originChanged (origin);
|
|
}
|
|
|
|
|
|
// public
|
|
bool kpView::canShowGrid () const
|
|
{
|
|
// (minimum zoom level < 400% would probably be reported as a bug by
|
|
// users who thought that the grid was a part of the image!)
|
|
return ((zoomLevelX () >= 400 && zoomLevelX () % 100 == 0) &&
|
|
(zoomLevelY () >= 400 && zoomLevelY () % 100 == 0));
|
|
}
|
|
|
|
// public
|
|
bool kpView::isGridShown () const
|
|
{
|
|
return d->m_showGrid;
|
|
}
|
|
|
|
// public
|
|
void kpView::showGrid (bool yes)
|
|
{
|
|
if (d->m_showGrid == yes)
|
|
return;
|
|
|
|
if (yes && !canShowGrid ())
|
|
return;
|
|
|
|
d->m_showGrid = yes;
|
|
|
|
if (viewManager ())
|
|
viewManager ()->updateView (this);
|
|
}
|
|
|
|
|
|
// public
|
|
bool kpView::isBuddyViewScrollableContainerRectangleShown () const
|
|
{
|
|
return d->m_isBuddyViewScrollableContainerRectangleShown;
|
|
}
|
|
|
|
// public
|
|
void kpView::showBuddyViewScrollableContainerRectangle (bool yes)
|
|
{
|
|
if (yes == d->m_isBuddyViewScrollableContainerRectangleShown)
|
|
return;
|
|
|
|
d->m_isBuddyViewScrollableContainerRectangleShown = yes;
|
|
|
|
if (d->m_isBuddyViewScrollableContainerRectangleShown)
|
|
{
|
|
// Got these connect statements by analysing deps of
|
|
// updateBuddyViewScrollableContainerRectangle() rect update code.
|
|
|
|
connect (this, TQT_SIGNAL (zoomLevelChanged (int, int)),
|
|
this, TQT_SLOT (updateBuddyViewScrollableContainerRectangle ()));
|
|
connect (this, TQT_SIGNAL (originChanged (const TQPoint &)),
|
|
this, TQT_SLOT (updateBuddyViewScrollableContainerRectangle ()));
|
|
|
|
if (buddyViewScrollableContainer ())
|
|
{
|
|
connect (buddyViewScrollableContainer (), TQT_SIGNAL (contentsMovingSoon (int, int)),
|
|
this, TQT_SLOT (updateBuddyViewScrollableContainerRectangle ()));
|
|
connect (buddyViewScrollableContainer (), TQT_SIGNAL (resized ()),
|
|
this, TQT_SLOT (updateBuddyViewScrollableContainerRectangle ()));
|
|
}
|
|
|
|
if (buddyView ())
|
|
{
|
|
connect (buddyView (), TQT_SIGNAL (zoomLevelChanged (int, int)),
|
|
this, TQT_SLOT (updateBuddyViewScrollableContainerRectangle ()));
|
|
connect (buddyView (), TQT_SIGNAL (originChanged (const TQPoint &)),
|
|
this, TQT_SLOT (updateBuddyViewScrollableContainerRectangle ()));
|
|
|
|
connect (buddyView (), TQT_SIGNAL (sizeChanged (int, int)),
|
|
this, TQT_SLOT (updateBuddyViewScrollableContainerRectangle ()));
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
disconnect (this, TQT_SIGNAL (zoomLevelChanged (int, int)),
|
|
this, TQT_SLOT (updateBuddyViewScrollableContainerRectangle ()));
|
|
disconnect (this, TQT_SIGNAL (originChanged (const TQPoint &)),
|
|
this, TQT_SLOT (updateBuddyViewScrollableContainerRectangle ()));
|
|
|
|
if (buddyViewScrollableContainer ())
|
|
{
|
|
disconnect (buddyViewScrollableContainer (), TQT_SIGNAL (contentsMovingSoon (int, int)),
|
|
this, TQT_SLOT (updateBuddyViewScrollableContainerRectangle ()));
|
|
disconnect (buddyViewScrollableContainer (), TQT_SIGNAL (resized ()),
|
|
this, TQT_SLOT (updateBuddyViewScrollableContainerRectangle ()));
|
|
}
|
|
|
|
if (buddyView ())
|
|
{
|
|
disconnect (buddyView (), TQT_SIGNAL (zoomLevelChanged (int, int)),
|
|
this, TQT_SLOT (updateBuddyViewScrollableContainerRectangle ()));
|
|
disconnect (buddyView (), TQT_SIGNAL (originChanged (const TQPoint &)),
|
|
this, TQT_SLOT (updateBuddyViewScrollableContainerRectangle ()));
|
|
|
|
disconnect (buddyView (), TQT_SIGNAL (sizeChanged (int, int)),
|
|
this, TQT_SLOT (updateBuddyViewScrollableContainerRectangle ()));
|
|
}
|
|
|
|
}
|
|
|
|
updateBuddyViewScrollableContainerRectangle ();
|
|
}
|
|
|
|
|
|
// protected
|
|
TQRect kpView::buddyViewScrollableContainerRectangle () const
|
|
{
|
|
return d->m_buddyViewScrollableContainerRectangle;
|
|
}
|
|
|
|
// protected slot
|
|
void kpView::updateBuddyViewScrollableContainerRectangle ()
|
|
{
|
|
if (viewManager ())
|
|
viewManager ()->setQueueUpdates ();
|
|
|
|
{
|
|
if (d->m_buddyViewScrollableContainerRectangle.isValid ())
|
|
{
|
|
if (viewManager ())
|
|
{
|
|
// Erase last
|
|
viewManager ()->updateViewRectangleEdges (this,
|
|
d->m_buddyViewScrollableContainerRectangle);
|
|
}
|
|
}
|
|
|
|
|
|
TQRect newRect;
|
|
if (isBuddyViewScrollableContainerRectangleShown () &&
|
|
buddyViewScrollableContainer () && buddyView ())
|
|
{
|
|
TQRect docRect = buddyView ()->transformViewToDoc (
|
|
TQRect (buddyViewScrollableContainer ()->contentsXSoon (),
|
|
buddyViewScrollableContainer ()->contentsYSoon (),
|
|
TQMIN (buddyView ()->width (),
|
|
buddyViewScrollableContainer ()->visibleWidth ()),
|
|
TQMIN (buddyView ()->height (),
|
|
buddyViewScrollableContainer ()->visibleHeight ())));
|
|
|
|
|
|
TQRect viewRect = this->transformDocToView (docRect);
|
|
|
|
|
|
// (Surround the area of interest by moving outwards by 1 pixel in each
|
|
// direction - don't overlap area)
|
|
newRect = TQRect (viewRect.x () - 1,
|
|
viewRect.y () - 1,
|
|
viewRect.width () + 2,
|
|
viewRect.height () + 2);
|
|
}
|
|
else
|
|
{
|
|
newRect = TQRect ();
|
|
}
|
|
|
|
if (newRect != d->m_buddyViewScrollableContainerRectangle)
|
|
{
|
|
// (must set before updateView() for paintEvent() to see new
|
|
// rect)
|
|
d->m_buddyViewScrollableContainerRectangle = newRect;
|
|
|
|
if (newRect.isValid ())
|
|
{
|
|
if (viewManager ())
|
|
{
|
|
viewManager ()->updateViewRectangleEdges (this,
|
|
d->m_buddyViewScrollableContainerRectangle);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (viewManager ())
|
|
viewManager ()->restoreQueueUpdates ();
|
|
}
|
|
|
|
|
|
// public
|
|
double kpView::transformViewToDocX (double viewX) const
|
|
{
|
|
return (viewX - origin ().x ()) * 100.0 / zoomLevelX ();
|
|
}
|
|
|
|
// public
|
|
double kpView::transformViewToDocY (double viewY) const
|
|
{
|
|
return (viewY - origin ().y ()) * 100.0 / zoomLevelY ();
|
|
}
|
|
|
|
// public
|
|
TQPoint kpView::transformViewToDoc (const TQPoint &viewPoint) const
|
|
{
|
|
return TQPoint ((int) transformViewToDocX (viewPoint.x ()),
|
|
(int) transformViewToDocY (viewPoint.y ()));
|
|
}
|
|
|
|
// public
|
|
TQRect kpView::transformViewToDoc (const TQRect &viewRect) const
|
|
{
|
|
if (zoomLevelX () == 100 && zoomLevelY () == 100)
|
|
{
|
|
return TQRect (viewRect.x () - origin ().x (),
|
|
viewRect.y () - origin ().y (),
|
|
viewRect.width (),
|
|
viewRect.height ());
|
|
}
|
|
else
|
|
{
|
|
const TQPoint docTopLeft = transformViewToDoc (viewRect.topLeft ());
|
|
|
|
// (don't call transformViewToDoc[XY]() - need to round up dimensions)
|
|
const int docWidth = tqRound (double (viewRect.width ()) * 100.0 / double (zoomLevelX ()));
|
|
const int docHeight = tqRound (double (viewRect.height ()) * 100.0 / double (zoomLevelY ()));
|
|
|
|
// (like TQWMatrix::Areas)
|
|
return TQRect (docTopLeft.x (), docTopLeft.y (), docWidth, docHeight);
|
|
}
|
|
}
|
|
|
|
|
|
// public
|
|
double kpView::transformDocToViewX (double docX) const
|
|
{
|
|
return (docX * zoomLevelX () / 100.0) + origin ().x ();
|
|
}
|
|
|
|
// public
|
|
double kpView::transformDocToViewY (double docY) const
|
|
{
|
|
return (docY * zoomLevelY () / 100.0) + origin ().y ();
|
|
}
|
|
|
|
// public
|
|
TQPoint kpView::transformDocToView (const TQPoint &docPoint) const
|
|
{
|
|
return TQPoint ((int) transformDocToViewX (docPoint.x ()),
|
|
(int) transformDocToViewY (docPoint.y ()));
|
|
}
|
|
|
|
// public
|
|
TQRect kpView::transformDocToView (const TQRect &docRect) const
|
|
{
|
|
if (zoomLevelX () == 100 && zoomLevelY () == 100)
|
|
{
|
|
return TQRect (docRect.x () + origin ().x (),
|
|
docRect.y () + origin ().y (),
|
|
docRect.width (),
|
|
docRect.height ());
|
|
}
|
|
else
|
|
{
|
|
const TQPoint viewTopLeft = transformDocToView (docRect.topLeft ());
|
|
|
|
// (don't call transformDocToView[XY]() - need to round up dimensions)
|
|
const int viewWidth = tqRound (double (docRect.width ()) * double (zoomLevelX ()) / 100.0);
|
|
const int viewHeight = tqRound (double (docRect.height ()) * double (zoomLevelY ()) / 100.0);
|
|
|
|
// (like TQWMatrix::Areas)
|
|
return TQRect (viewTopLeft.x (), viewTopLeft.y (), viewWidth, viewHeight);
|
|
}
|
|
}
|
|
|
|
|
|
// public
|
|
TQPoint kpView::transformViewToOtherView (const TQPoint &viewPoint,
|
|
const kpView *otherView)
|
|
{
|
|
if (this == otherView)
|
|
return viewPoint;
|
|
|
|
const double docX = transformViewToDocX (viewPoint.x ());
|
|
const double docY = transformViewToDocY (viewPoint.y ());
|
|
|
|
const double otherViewX = otherView->transformDocToViewX (docX);
|
|
const double otherViewY = otherView->transformDocToViewY (docY);
|
|
|
|
return TQPoint ((int) otherViewX, (int) otherViewY);
|
|
}
|
|
|
|
|
|
// public
|
|
int kpView::zoomedDocWidth () const
|
|
{
|
|
return document () ? document ()->width () * zoomLevelX () / 100 : 0;
|
|
}
|
|
|
|
// public
|
|
int kpView::zoomedDocHeight () const
|
|
{
|
|
return document () ? document ()->height () * zoomLevelY () / 100 : 0;
|
|
}
|
|
|
|
|
|
// public
|
|
void kpView::setHasMouse (bool yes)
|
|
{
|
|
kpViewManager *vm = viewManager ();
|
|
if (!vm)
|
|
return;
|
|
|
|
#if DEBUG_KP_VIEW && 0
|
|
kdDebug () << "kpView(" << name ()
|
|
<< ")::setHasMouse(" << yes
|
|
<< ") existing viewUnderCursor="
|
|
<< (vm->viewUnderCursor () ? vm->viewUnderCursor ()->name () : "(none)")
|
|
<< endl;
|
|
#endif
|
|
if (yes && vm->viewUnderCursor () != this)
|
|
vm->setViewUnderCursor (this);
|
|
else if (!yes && vm->viewUnderCursor () == this)
|
|
vm->setViewUnderCursor (0);
|
|
}
|
|
|
|
|
|
// public
|
|
void kpView::addToQueuedArea (const TQRegion ®ion)
|
|
{
|
|
#if DEBUG_KP_VIEW && 0
|
|
kdDebug () << "kpView(" << name ()
|
|
<< ")::addToQueuedArea() already=" << d->m_queuedUpdateArea
|
|
<< " - plus - " << region
|
|
<< endl;
|
|
#endif
|
|
d->m_queuedUpdateArea += region;
|
|
}
|
|
|
|
// public
|
|
void kpView::addToQueuedArea (const TQRect &rect)
|
|
{
|
|
#if DEBUG_KP_VIEW && 0
|
|
kdDebug () << "kpView(" << name ()
|
|
<< ")::addToQueuedArea() already=" << d->m_queuedUpdateArea
|
|
<< " - plus - " << rect
|
|
<< endl;
|
|
#endif
|
|
d->m_queuedUpdateArea += rect;
|
|
}
|
|
|
|
// public
|
|
void kpView::invalidateQueuedArea ()
|
|
{
|
|
#if DEBUG_KP_VIEW && 0
|
|
kdDebug () << "kpView::invalidateQueuedArea()" << endl;
|
|
#endif
|
|
|
|
d->m_queuedUpdateArea = TQRegion ();
|
|
}
|
|
|
|
// public
|
|
void kpView::updateQueuedArea ()
|
|
{
|
|
kpViewManager *vm = viewManager ();
|
|
#if DEBUG_KP_VIEW && 0
|
|
kdDebug () << "kpView(" << name ()
|
|
<< ")::updateQueuedArea() vm=" << (bool) vm
|
|
<< " queueUpdates=" << (vm && vm->queueUpdates ())
|
|
<< " fastUpdates=" << (vm && vm->fastUpdates ())
|
|
<< " area=" << d->m_queuedUpdateArea
|
|
<< endl;
|
|
#endif
|
|
|
|
if (!vm)
|
|
return;
|
|
|
|
if (vm->queueUpdates ())
|
|
return;
|
|
|
|
if (!d->m_queuedUpdateArea.isNull ())
|
|
vm->updateView (this, d->m_queuedUpdateArea);
|
|
|
|
invalidateQueuedArea ();
|
|
}
|
|
|
|
// public
|
|
void kpView::updateMicroFocusHint (const TQRect µFocusHint)
|
|
{
|
|
int x = microFocusHint.topLeft().x();
|
|
int y = microFocusHint.topLeft().y();
|
|
int width = microFocusHint.width();
|
|
int height = microFocusHint.height();
|
|
setMicroFocusHint (x, y, width, height);
|
|
}
|
|
|
|
// public
|
|
TQRect kpView::selectionViewRect () const
|
|
{
|
|
return selection () ?
|
|
transformDocToView (selection ()->boundingRect ()) :
|
|
TQRect ();
|
|
|
|
}
|
|
|
|
// public
|
|
TQPoint kpView::mouseViewPoint (const TQPoint &returnViewPoint) const
|
|
{
|
|
if (returnViewPoint != KP_INVALID_POINT)
|
|
return returnViewPoint;
|
|
else
|
|
return mapFromGlobal (TQCursor::pos ());
|
|
}
|
|
|
|
// public
|
|
TQPoint kpView::mouseViewPointRelativeToSelection (const TQPoint &viewPoint) const
|
|
{
|
|
if (!selection ())
|
|
return KP_INVALID_POINT;
|
|
|
|
return mouseViewPoint (viewPoint) - transformDocToView (selection ()->topLeft ());
|
|
}
|
|
|
|
// public
|
|
bool kpView::mouseOnSelection (const TQPoint &viewPoint) const
|
|
{
|
|
const TQRect selViewRect = selectionViewRect ();
|
|
if (!selViewRect.isValid ())
|
|
return false;
|
|
|
|
return selViewRect.contains (mouseViewPoint (viewPoint));
|
|
}
|
|
|
|
|
|
// public
|
|
int kpView::textSelectionMoveBorderAtomicSize () const
|
|
{
|
|
if (!selection () || !selection ()->isText ())
|
|
return 0;
|
|
|
|
return TQMAX (4, zoomLevelX () / 100);
|
|
}
|
|
|
|
// public
|
|
bool kpView::mouseOnSelectionToMove (const TQPoint &viewPoint) const
|
|
{
|
|
if (!mouseOnSelection (viewPoint))
|
|
return false;
|
|
|
|
if (!selection ()->isText ())
|
|
return true;
|
|
|
|
if (mouseOnSelectionResizeHandle (viewPoint))
|
|
return false;
|
|
|
|
|
|
const TQPoint viewPointRelSel = mouseViewPointRelativeToSelection (viewPoint);
|
|
|
|
// Middle point should always be selectable
|
|
const TQPoint selCenterDocPoint = selection ()->boundingRect ().center ();
|
|
if (tool () &&
|
|
tool ()->currentPoint () == selCenterDocPoint)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
const int atomicSize = textSelectionMoveBorderAtomicSize ();
|
|
const TQRect selViewRect = selectionViewRect ();
|
|
|
|
return (viewPointRelSel.x () < atomicSize ||
|
|
viewPointRelSel.x () >= selViewRect.width () - atomicSize ||
|
|
viewPointRelSel.y () < atomicSize ||
|
|
viewPointRelSel.y () >= selViewRect.height () - atomicSize);
|
|
}
|
|
|
|
|
|
// protected
|
|
bool kpView::selectionLargeEnoughToHaveResizeHandlesIfAtomicSize (int atomicSize) const
|
|
{
|
|
if (!selection ())
|
|
return false;
|
|
|
|
const TQRect selViewRect = selectionViewRect ();
|
|
|
|
return (selViewRect.width () >= atomicSize * 5 ||
|
|
selViewRect.height () >= atomicSize * 5);
|
|
}
|
|
|
|
// public
|
|
int kpView::selectionResizeHandleAtomicSize () const
|
|
{
|
|
int atomicSize = TQMIN (7, TQMAX (4, zoomLevelX () / 100));
|
|
while (atomicSize > 0 &&
|
|
!selectionLargeEnoughToHaveResizeHandlesIfAtomicSize (atomicSize))
|
|
{
|
|
atomicSize--;
|
|
}
|
|
|
|
return atomicSize;
|
|
}
|
|
|
|
// public
|
|
bool kpView::selectionLargeEnoughToHaveResizeHandles () const
|
|
{
|
|
return (selectionResizeHandleAtomicSize () > 0);
|
|
}
|
|
|
|
// public
|
|
TQRegion kpView::selectionResizeHandlesViewRegion (bool forRenderer) const
|
|
{
|
|
TQRegion ret;
|
|
|
|
const int atomicLength = selectionResizeHandleAtomicSize ();
|
|
if (atomicLength <= 0)
|
|
return TQRegion ();
|
|
|
|
|
|
// HACK: At low zoom (e.g. 100%), resize handles will probably be too
|
|
// big and overlap text / cursor / too much of selection.
|
|
//
|
|
// So limit the _visual_ size of handles at low zoom. The
|
|
// handles' grab area remains the same for usability; so yes,
|
|
// there are a few pixels that don't look grabable but they are.
|
|
//
|
|
// The real solution is to be able to partially render the
|
|
// handles outside of the selection view rect. If not possible,
|
|
// at least for text boxes, render text on top of handles.
|
|
int normalAtomicLength = atomicLength;
|
|
int vertEdgeAtomicLength = atomicLength;
|
|
if (forRenderer && selection ())
|
|
{
|
|
if (zoomLevelX () <= 150)
|
|
{
|
|
if (normalAtomicLength > 1)
|
|
normalAtomicLength--;
|
|
|
|
if (vertEdgeAtomicLength > 1)
|
|
vertEdgeAtomicLength--;
|
|
}
|
|
|
|
// 1 line of text?
|
|
if (selection ()->isText () && selection ()->textLines ().size () == 1)
|
|
{
|
|
if (zoomLevelX () <= 150)
|
|
vertEdgeAtomicLength = TQMIN (vertEdgeAtomicLength, TQMAX (2, zoomLevelX () / 100));
|
|
else if (zoomLevelX () <= 250)
|
|
vertEdgeAtomicLength = TQMIN (vertEdgeAtomicLength, TQMAX (3, zoomLevelX () / 100));
|
|
}
|
|
}
|
|
|
|
|
|
const TQRect selViewRect = selectionViewRect ();
|
|
|
|
#define ADD_BOX_RELATIVE_TO_SELECTION(type,x,y) \
|
|
ret += TQRect ((x), (y), type##AtomicLength, type##AtomicLength)
|
|
|
|
ADD_BOX_RELATIVE_TO_SELECTION (normal,
|
|
selViewRect.width () - normalAtomicLength,
|
|
selViewRect.height () - normalAtomicLength);
|
|
ADD_BOX_RELATIVE_TO_SELECTION (normal,
|
|
selViewRect.width () - normalAtomicLength,
|
|
0);
|
|
ADD_BOX_RELATIVE_TO_SELECTION (normal,
|
|
0,
|
|
selViewRect.height () - normalAtomicLength);
|
|
ADD_BOX_RELATIVE_TO_SELECTION (normal,
|
|
0,
|
|
0);
|
|
|
|
ADD_BOX_RELATIVE_TO_SELECTION (vertEdge,
|
|
selViewRect.width () - vertEdgeAtomicLength,
|
|
(selViewRect.height () - vertEdgeAtomicLength) / 2);
|
|
ADD_BOX_RELATIVE_TO_SELECTION (normal,
|
|
(selViewRect.width () - normalAtomicLength) / 2,
|
|
selViewRect.height () - normalAtomicLength);
|
|
ADD_BOX_RELATIVE_TO_SELECTION (normal,
|
|
(selViewRect.width () - normalAtomicLength) / 2,
|
|
0);
|
|
ADD_BOX_RELATIVE_TO_SELECTION (vertEdge,
|
|
0,
|
|
(selViewRect.height () - vertEdgeAtomicLength) / 2);
|
|
|
|
#undef ADD_BOX_RELATIVE_TO_SELECTION
|
|
|
|
ret.translate (selViewRect.x (), selViewRect.y ());
|
|
ret = ret.intersect (selViewRect);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// public
|
|
int kpView::mouseOnSelectionResizeHandle (const TQPoint &viewPoint) const
|
|
{
|
|
#if DEBUG_KP_VIEW
|
|
kdDebug () << "kpView::mouseOnSelectionResizeHandle(viewPoint="
|
|
<< viewPoint << ")" << endl;
|
|
#endif
|
|
|
|
if (!mouseOnSelection (viewPoint))
|
|
{
|
|
#if DEBUG_KP_VIEW
|
|
kdDebug () << "\tmouse not on sel" << endl;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
|
|
const TQRect selViewRect = selectionViewRect ();
|
|
#if DEBUG_KP_VIEW
|
|
kdDebug () << "\tselViewRect=" << selViewRect << endl;
|
|
#endif
|
|
|
|
|
|
const int atomicLength = selectionResizeHandleAtomicSize ();
|
|
#if DEBUG_KP_VIEW
|
|
kdDebug () << "\tatomicLength=" << atomicLength << endl;
|
|
#endif
|
|
|
|
if (atomicLength <= 0)
|
|
{
|
|
#if DEBUG_KP_VIEW
|
|
kdDebug () << "\tsel not large enough to have resize handles" << endl;
|
|
#endif
|
|
// Want to make it possible to move a small selection
|
|
return 0;
|
|
}
|
|
|
|
|
|
const TQPoint viewPointRelSel = mouseViewPointRelativeToSelection (viewPoint);
|
|
#if DEBUG_KP_VIEW
|
|
kdDebug () << "\tviewPointRelSel=" << viewPointRelSel << endl;
|
|
#endif
|
|
|
|
|
|
#define LOCAL_POINT_IN_BOX_AT(x,y) \
|
|
TQRect ((x), (y), atomicLength, atomicLength).contains (viewPointRelSel)
|
|
|
|
// Favour the bottom & right and the corners.
|
|
if (LOCAL_POINT_IN_BOX_AT (selViewRect.width () - atomicLength,
|
|
selViewRect.height () - atomicLength))
|
|
{
|
|
return Bottom | Right;
|
|
}
|
|
else if (LOCAL_POINT_IN_BOX_AT (selViewRect.width () - atomicLength, 0))
|
|
{
|
|
return Top | Right;
|
|
}
|
|
else if (LOCAL_POINT_IN_BOX_AT (0, selViewRect.height () - atomicLength))
|
|
{
|
|
return Bottom | Left;
|
|
}
|
|
else if (LOCAL_POINT_IN_BOX_AT (0, 0))
|
|
{
|
|
return Top | Left;
|
|
}
|
|
else if (LOCAL_POINT_IN_BOX_AT (selViewRect.width () - atomicLength,
|
|
(selViewRect.height () - atomicLength) / 2))
|
|
{
|
|
return Right;
|
|
}
|
|
else if (LOCAL_POINT_IN_BOX_AT ((selViewRect.width () - atomicLength) / 2,
|
|
selViewRect.height () - atomicLength))
|
|
{
|
|
return Bottom;
|
|
}
|
|
else if (LOCAL_POINT_IN_BOX_AT ((selViewRect.width () - atomicLength) / 2, 0))
|
|
{
|
|
return Top;
|
|
}
|
|
else if (LOCAL_POINT_IN_BOX_AT (0, (selViewRect.height () - atomicLength) / 2))
|
|
{
|
|
return Left;
|
|
}
|
|
else
|
|
{
|
|
#if DEBUG_KP_VIEW
|
|
kdDebug () << "\tnot on sel resize handle" << endl;
|
|
#endif
|
|
return 0;
|
|
}
|
|
#undef LOCAL_POINT_IN_BOX_AT
|
|
}
|
|
|
|
// public
|
|
bool kpView::mouseOnSelectionToSelectText (const TQPoint &viewPoint) const
|
|
{
|
|
#if DEBUG_KP_VIEW
|
|
kdDebug () << "kpView::mouseOnSelectionToSelectText(viewPoint="
|
|
<< viewPoint << ")" << endl;
|
|
#endif
|
|
|
|
if (!mouseOnSelection (viewPoint))
|
|
{
|
|
#if DEBUG_KP_VIEW
|
|
kdDebug () << "\tmouse non on sel" << endl;
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
if (!selection ()->isText ())
|
|
{
|
|
#if DEBUG_KP_VIEW
|
|
kdDebug () << "\tsel not text" << endl;
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
#if DEBUG_KP_VIEW
|
|
kdDebug () << "\tmouse on sel: to move=" << mouseOnSelectionToMove ()
|
|
<< " to resize=" << mouseOnSelectionResizeHandle ()
|
|
<< endl;
|
|
#endif
|
|
|
|
return (!mouseOnSelectionToMove (viewPoint) &&
|
|
!mouseOnSelectionResizeHandle (viewPoint));
|
|
}
|
|
|
|
|
|
// protected virtual [base TQWidget]
|
|
void kpView::mouseMoveEvent (TQMouseEvent *e)
|
|
{
|
|
#if DEBUG_KP_VIEW && 0
|
|
kdDebug () << "kpView(" << name () << ")::mouseMoveEvent ("
|
|
<< e->x () << "," << e->y () << ")"
|
|
<< endl;
|
|
#endif
|
|
|
|
// TODO: This is wrong if you leaveEvent the mainView by mouseMoving on the
|
|
// mainView, landing on top of the thumbnailView cleverly put on top
|
|
// of the mainView.
|
|
setHasMouse (rect ().contains (e->pos ()));
|
|
|
|
if (tool ())
|
|
tool ()->mouseMoveEvent (e);
|
|
|
|
e->accept ();
|
|
}
|
|
|
|
// protected virtual [base TQWidget]
|
|
void kpView::mousePressEvent (TQMouseEvent *e)
|
|
{
|
|
#if DEBUG_KP_VIEW && 0
|
|
kdDebug () << "kpView(" << name () << ")::mousePressEvent ("
|
|
<< e->x () << "," << e->y () << ")"
|
|
<< endl;
|
|
#endif
|
|
|
|
setHasMouse (true);
|
|
|
|
if (tool ())
|
|
tool ()->mousePressEvent (e);
|
|
|
|
e->accept ();
|
|
}
|
|
|
|
// protected virtual [base TQWidget]
|
|
void kpView::mouseReleaseEvent (TQMouseEvent *e)
|
|
{
|
|
#if DEBUG_KP_VIEW && 0
|
|
kdDebug () << "kpView(" << name () << ")::mouseReleaseEvent ("
|
|
<< e->x () << "," << e->y () << ")"
|
|
<< endl;
|
|
#endif
|
|
|
|
setHasMouse (rect ().contains (e->pos ()));
|
|
|
|
if (tool ())
|
|
tool ()->mouseReleaseEvent (e);
|
|
|
|
e->accept ();
|
|
}
|
|
|
|
// public virtual [base TQWidget]
|
|
void kpView::wheelEvent (TQWheelEvent *e)
|
|
{
|
|
if (tool ())
|
|
tool ()->wheelEvent (e);
|
|
}
|
|
|
|
|
|
// protected virtual [base TQWidget]
|
|
void kpView::keyPressEvent (TQKeyEvent *e)
|
|
{
|
|
#if DEBUG_KP_VIEW && 0
|
|
kdDebug () << "kpView(" << name () << ")::keyPressEvent()" << endl;
|
|
#endif
|
|
|
|
if (tool ())
|
|
tool ()->keyPressEvent (e);
|
|
|
|
e->accept ();
|
|
}
|
|
|
|
// protected virtual [base TQWidget]
|
|
void kpView::keyReleaseEvent (TQKeyEvent *e)
|
|
{
|
|
#if DEBUG_KP_VIEW && 0
|
|
kdDebug () << "kpView(" << name () << ")::keyReleaseEvent()" << endl;
|
|
#endif
|
|
|
|
if (tool ())
|
|
tool ()->keyReleaseEvent (e);
|
|
|
|
e->accept ();
|
|
}
|
|
|
|
|
|
// protected virtual [base TQWidget]
|
|
void kpView::focusInEvent (TQFocusEvent *e)
|
|
{
|
|
#if DEBUG_KP_VIEW && 0
|
|
kdDebug () << "kpView(" << name () << ")::focusInEvent()" << endl;
|
|
#endif
|
|
if (tool ())
|
|
tool ()->focusInEvent (e);
|
|
}
|
|
|
|
// protected virtual [base TQWidget]
|
|
void kpView::focusOutEvent (TQFocusEvent *e)
|
|
{
|
|
#if DEBUG_KP_VIEW && 0
|
|
kdDebug () << "kpView(" << name () << ")::focusOutEvent()" << endl;
|
|
#endif
|
|
if (tool ())
|
|
tool ()->focusOutEvent (e);
|
|
}
|
|
|
|
|
|
// protected virtual [base TQWidget]
|
|
void kpView::enterEvent (TQEvent *e)
|
|
{
|
|
#if DEBUG_KP_VIEW && 0
|
|
kdDebug () << "kpView(" << name () << ")::enterEvent()" << endl;
|
|
#endif
|
|
|
|
// Don't call setHasMouse(true) as it displays the brush cursor (if
|
|
// active) when dragging open a menu and then dragging
|
|
// past the extents of the menu due to TQt sending us an EnterEvent.
|
|
// We're already covered by MouseMoveEvent anyway.
|
|
//
|
|
// But disabling this causes a more serious problem: RMB on a text
|
|
// box and Esc. We have no other reliable way to determine if the
|
|
// mouse is still above the view (user could have moved mouse out
|
|
// while RMB menu was up) and hence the cursor is not updated.
|
|
setHasMouse (true);
|
|
|
|
if (tool ())
|
|
tool ()->enterEvent (e);
|
|
}
|
|
|
|
// protected virtual [base TQWidget]
|
|
void kpView::leaveEvent (TQEvent *e)
|
|
{
|
|
#if DEBUG_KP_VIEW && 0
|
|
kdDebug () << "kpView(" << name () << ")::leaveEvent()" << endl;
|
|
#endif
|
|
|
|
setHasMouse (false);
|
|
if (tool ())
|
|
tool ()->leaveEvent (e);
|
|
}
|
|
|
|
|
|
// protected virtual [base TQWidget]
|
|
void kpView::dragEnterEvent (TQDragEnterEvent *)
|
|
{
|
|
#if DEBUG_KP_VIEW && 1
|
|
kdDebug () << "kpView(" << name () << ")::dragEnterEvent()" << endl;
|
|
#endif
|
|
|
|
setHasMouse (true);
|
|
}
|
|
|
|
// protected virtual [base TQWidget]
|
|
void kpView::dragLeaveEvent (TQDragLeaveEvent *)
|
|
{
|
|
#if DEBUG_KP_VIEW && 1
|
|
kdDebug () << "kpView(" << name () << ")::dragLeaveEvent" << endl;
|
|
#endif
|
|
|
|
setHasMouse (false);
|
|
}
|
|
|
|
|
|
// public virtual [base TQWidget]
|
|
void kpView::resize (int w, int h)
|
|
{
|
|
#if DEBUG_KP_VIEW && 1
|
|
kdDebug () << "kpView(" << name ()
|
|
<< ")::resize(" << w << "," << h << ")"
|
|
<< endl;
|
|
#endif
|
|
|
|
TQWidget::resize (w, h);
|
|
}
|
|
|
|
// protected virtual [base TQWidget]
|
|
void kpView::resizeEvent (TQResizeEvent *e)
|
|
{
|
|
#if DEBUG_KP_VIEW && 1
|
|
kdDebug () << "kpView(" << name () << ")::resizeEvent("
|
|
<< e->size ()
|
|
<< " vs actual=" << size ()
|
|
<< ") old=" << e->oldSize () << endl;
|
|
#endif
|
|
|
|
TQWidget::resizeEvent (e);
|
|
|
|
emit sizeChanged (width (), height ());
|
|
emit sizeChanged (size ());
|
|
}
|
|
|
|
|
|
// private virtual
|
|
void kpView::imStartEvent (TQIMEvent *e)
|
|
{
|
|
#if DEBUG_KP_VIEW && 1
|
|
kdDebug () << "kpView(" << name () << ")::imStartEvent" << endl;
|
|
#endif
|
|
|
|
if (tool ())
|
|
tool ()->imStartEvent (e);
|
|
e->accept();
|
|
}
|
|
|
|
// private virtual
|
|
void kpView::imComposeEvent (TQIMEvent *e)
|
|
{
|
|
#if DEBUG_KP_VIEW && 1
|
|
kdDebug () << "kpView(" << name () << ")::imComposeEvent" << endl;
|
|
#endif
|
|
|
|
if (tool ())
|
|
tool ()->imComposeEvent (e);
|
|
e->accept();
|
|
}
|
|
|
|
// private virtual
|
|
void kpView::imEndEvent (TQIMEvent *e)
|
|
{
|
|
#if DEBUG_KP_VIEW && 1
|
|
kdDebug () << "kpView(" << name () << ")::imEndEvent" << endl;
|
|
#endif
|
|
|
|
if (tool ())
|
|
tool ()->imEndEvent (e);
|
|
e->accept();
|
|
}
|
|
|
|
|
|
//
|
|
// Renderer
|
|
//
|
|
|
|
// protected
|
|
TQRect kpView::paintEventGetDocRect (const TQRect &viewRect) const
|
|
{
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "kpView::paintEventGetDocRect(" << viewRect << ")" << endl;
|
|
#endif
|
|
|
|
TQRect docRect;
|
|
|
|
// From the "we aren't sure whether to round up or round down" department:
|
|
|
|
if (zoomLevelX () < 100 || zoomLevelY () < 100)
|
|
docRect = transformViewToDoc (viewRect);
|
|
else
|
|
{
|
|
// think of a grid - you need to fully cover the zoomed-in pixels
|
|
// when docRect is zoomed back to the view later
|
|
docRect = TQRect (transformViewToDoc (viewRect.topLeft ()), // round down
|
|
transformViewToDoc (viewRect.bottomRight ())); // round down
|
|
}
|
|
|
|
if (zoomLevelX () % 100 || zoomLevelY () % 100)
|
|
{
|
|
// at least round up the bottom-right point and deal with matrix weirdness:
|
|
// - helpful because it ensures we at least cover the required area
|
|
// at e.g. 67% or 573%
|
|
// - harmless since paintEventDrawRect() clips for us anyway
|
|
docRect.setBottomRight (docRect.bottomRight () + TQPoint (2, 2));
|
|
}
|
|
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\tdocRect=" << docRect << endl;
|
|
#endif
|
|
kpDocument *doc = document ();
|
|
if (doc)
|
|
{
|
|
docRect = docRect.intersect (doc->rect ());
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\tintersect with doc=" << docRect << endl;
|
|
#endif
|
|
}
|
|
|
|
return docRect;
|
|
}
|
|
|
|
// public
|
|
void kpView::drawTransparentBackground (TQPainter *painter,
|
|
int /*viewWidth*/, int /*viewHeight*/,
|
|
const TQRect &rect,
|
|
bool isPreview)
|
|
{
|
|
const int cellSize = !isPreview ? 16 : 10;
|
|
|
|
int starty = rect.y ();
|
|
if (starty % cellSize)
|
|
starty -= (starty % cellSize);
|
|
|
|
int startx = rect.x ();
|
|
if (startx % cellSize)
|
|
startx -= (startx % cellSize);
|
|
|
|
painter->save ();
|
|
for (int y = starty; y <= rect.bottom (); y += cellSize)
|
|
{
|
|
for (int x = startx; x <= rect.right (); x += cellSize)
|
|
{
|
|
bool parity = (x / cellSize + y / cellSize) % 2;
|
|
TQColor col;
|
|
|
|
if (parity)
|
|
{
|
|
if (!isPreview)
|
|
col = TQColor (213, 213, 213);
|
|
else
|
|
col = TQColor (224, 224, 224);
|
|
}
|
|
else
|
|
col = TQt::white;
|
|
|
|
painter->fillRect (x - rect.x (), y - rect.y (), cellSize, cellSize,
|
|
col);
|
|
}
|
|
}
|
|
painter->restore ();
|
|
}
|
|
|
|
// protected
|
|
void kpView::paintEventDrawCheckerBoard (TQPainter *painter, const TQRect &viewRect)
|
|
{
|
|
kpDocument *doc = document ();
|
|
if (!doc)
|
|
return;
|
|
|
|
drawTransparentBackground (painter,
|
|
doc->width () * zoomLevelX () / 100,
|
|
doc->height () * zoomLevelY () / 100,
|
|
viewRect);
|
|
}
|
|
|
|
// protected
|
|
void kpView::paintEventDrawSelection (TQPixmap *destPixmap, const TQRect &docRect)
|
|
{
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "kpView::paintEventDrawSelection() docRect=" << docRect << endl;
|
|
#endif
|
|
|
|
kpDocument *doc = document ();
|
|
if (!doc)
|
|
{
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\tno doc - abort" << endl;
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
kpSelection *sel = doc->selection ();
|
|
if (!sel)
|
|
{
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\tno sel - abort" << endl;
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Draw selection pixmap (if there is one)
|
|
//
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\tdraw sel pixmap @ " << sel->topLeft () << endl;
|
|
#endif
|
|
sel->paint (destPixmap, docRect);
|
|
|
|
|
|
//
|
|
// Draw selection border
|
|
//
|
|
|
|
kpViewManager *vm = viewManager ();
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\tsel border visible="
|
|
<< vm->selectionBorderVisible ()
|
|
<< endl;
|
|
#endif
|
|
if (vm->selectionBorderVisible ())
|
|
{
|
|
TQPainter destPixmapPainter (destPixmap);
|
|
destPixmapPainter.setRasterOp (TQt::XorROP);
|
|
destPixmapPainter.setPen (TQPen (TQt::white, 1, TQt::DotLine));
|
|
|
|
destPixmapPainter.setBackgroundMode (Qt::OpaqueMode);
|
|
destPixmapPainter.setBackgroundColor (TQt::blue);
|
|
|
|
TQBitmap maskBitmap;
|
|
TQPainter maskBitmapPainter;
|
|
if (destPixmap->mask ())
|
|
{
|
|
maskBitmap = *destPixmap->mask ();
|
|
maskBitmapPainter.begin (&maskBitmap);
|
|
maskBitmapPainter.setPen (TQt::color1/*opaque*/);
|
|
}
|
|
|
|
|
|
#define PAINTER_CMD(cmd) \
|
|
{ \
|
|
destPixmapPainter . cmd; \
|
|
if (maskBitmapPainter.isActive ()) \
|
|
maskBitmapPainter . cmd; \
|
|
}
|
|
|
|
TQRect boundingRect = sel->boundingRect ();
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\tsel boundingRect="
|
|
<< boundingRect
|
|
<< endl;
|
|
#endif
|
|
|
|
if (boundingRect.topLeft () != boundingRect.bottomRight ())
|
|
{
|
|
switch (sel->type ())
|
|
{
|
|
case kpSelection::Rectangle:
|
|
case kpSelection::Text:
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\tselection border = rectangle" << endl;
|
|
kdDebug () << "\t\tx=" << boundingRect.x () - docRect.x ()
|
|
<< " y=" << boundingRect.y () - docRect.y ()
|
|
<< " w=" << boundingRect.width ()
|
|
<< " h=" << boundingRect.height ()
|
|
<< endl;
|
|
#endif
|
|
PAINTER_CMD (drawRect (boundingRect.x () - docRect.x (),
|
|
boundingRect.y () - docRect.y (),
|
|
boundingRect.width (),
|
|
boundingRect.height ()));
|
|
break;
|
|
|
|
case kpSelection::Ellipse:
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\tselection border = ellipse" << endl;
|
|
#endif
|
|
PAINTER_CMD (drawEllipse (boundingRect.x () - docRect.x (),
|
|
boundingRect.y () - docRect.y (),
|
|
boundingRect.width (),
|
|
boundingRect.height ()));
|
|
break;
|
|
|
|
case kpSelection::Points:
|
|
{
|
|
#if DEBUG_KP_VIEW_RENDERER
|
|
kdDebug () << "\tselection border = freeForm" << endl;
|
|
#endif
|
|
TQPointArray points = sel->points ();
|
|
points.detach ();
|
|
points.translate (-docRect.x (), -docRect.y ());
|
|
if (vm->selectionBorderFinished ())
|
|
{
|
|
PAINTER_CMD (drawPolygon (points));
|
|
}
|
|
else
|
|
{
|
|
PAINTER_CMD (drawPolyline (points));
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
kdError () << "kpView::paintEventDrawSelection() unknown sel border type" << endl;
|
|
break;
|
|
}
|
|
|
|
|
|
if (vm->selectionBorderFinished () &&
|
|
(sel->type () == kpSelection::Ellipse ||
|
|
sel->type () == kpSelection::Points))
|
|
{
|
|
destPixmapPainter.save ();
|
|
|
|
destPixmapPainter.setRasterOp (TQt::NotROP);
|
|
PAINTER_CMD (drawRect (boundingRect.x () - docRect.x (),
|
|
boundingRect.y () - docRect.y (),
|
|
boundingRect.width (),
|
|
boundingRect.height ()));
|
|
|
|
destPixmapPainter.restore ();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// SYNC: Work around TQt bug: can't draw 1x1 rectangle
|
|
PAINTER_CMD (drawPoint (boundingRect.topLeft () - docRect.topLeft ()));
|
|
}
|
|
|
|
#undef PAINTER_CMD
|
|
|
|
destPixmapPainter.end ();
|
|
if (maskBitmapPainter.isActive ())
|
|
maskBitmapPainter.end ();
|
|
|
|
destPixmap->setMask (maskBitmap);
|
|
}
|
|
|
|
|
|
//
|
|
// Draw text cursor
|
|
//
|
|
|
|
if (sel->isText () &&
|
|
vm->textCursorEnabled () &&
|
|
(vm->textCursorBlinkState () ||
|
|
// For the current main window:
|
|
// As long as _any_ view has focus, blink _all_ views not just the
|
|
// one with focus // !this->isActiveWindow ()
|
|
!vm->activeView ())) // sync: call will break when vm is not held by 1 mainWindow
|
|
{
|
|
// TODO: Fix code duplication: kpViewManager::{setTextCursorPosition,updateTextCursor}() & kpView::paintEventDrawSelection()
|
|
TQPoint topLeft = sel->pointForTextRowCol (vm->textCursorRow (), vm->textCursorCol ());
|
|
if (topLeft != KP_INVALID_POINT)
|
|
{
|
|
TQRect rect = TQRect (topLeft.x (), topLeft.y (),
|
|
1, sel->textStyle ().fontMetrics ().height ());
|
|
rect = rect.intersect (sel->textAreaRect ());
|
|
if (!rect.isEmpty ())
|
|
{
|
|
rect.moveBy (-docRect.x (), -docRect.y ());
|
|
|
|
TQBitmap maskBitmap;
|
|
TQPainter destPixmapPainter, maskBitmapPainter;
|
|
|
|
if (destPixmap->mask ())
|
|
{
|
|
maskBitmap = *destPixmap->mask ();
|
|
maskBitmapPainter.begin (&maskBitmap);
|
|
maskBitmapPainter.fillRect (rect, TQt::color1/*opaque*/);
|
|
maskBitmapPainter.end ();
|
|
}
|
|
|
|
destPixmapPainter.begin (destPixmap);
|
|
destPixmapPainter.setRasterOp (TQt::XorROP);
|
|
destPixmapPainter.fillRect (rect, TQt::white);
|
|
destPixmapPainter.end ();
|
|
|
|
if (!maskBitmap.isNull ())
|
|
destPixmap->setMask (maskBitmap);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// protected
|
|
bool kpView::selectionResizeHandleAtomicSizeCloseToZoomLevel () const
|
|
{
|
|
return (abs (selectionResizeHandleAtomicSize () - zoomLevelX () / 100) < 3);
|
|
}
|
|
|
|
// protected
|
|
void kpView::paintEventDrawSelectionResizeHandles (TQPainter *painter, const TQRect &viewRect)
|
|
{
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "kpView::paintEventDrawSelectionResizeHandles("
|
|
<< viewRect << ")" << endl;
|
|
#endif
|
|
|
|
if (!selectionLargeEnoughToHaveResizeHandles ())
|
|
{
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\tsel not large enough to have resize handles" << endl;
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
kpViewManager *vm = viewManager ();
|
|
if (!vm || !vm->selectionBorderVisible () || !vm->selectionBorderFinished ())
|
|
{
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\tsel border not visible or not finished" << endl;
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
const TQRect selViewRect = selectionViewRect ();
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\tselViewRect=" << selViewRect << endl;
|
|
#endif
|
|
if (!selViewRect.intersects (viewRect))
|
|
{
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\tdoesn't intersect viewRect" << endl;
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
TQRegion selResizeHandlesRegion = selectionResizeHandlesViewRegion (true/*for renderer*/);
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\tsel resize handles view region="
|
|
<< selResizeHandlesRegion << endl;
|
|
#endif
|
|
selResizeHandlesRegion.translate (-viewRect.x (), -viewRect.y ());
|
|
|
|
painter->save ();
|
|
|
|
TQColor fillColor;
|
|
if (selectionResizeHandleAtomicSizeCloseToZoomLevel ())
|
|
{
|
|
fillColor = TQt::blue;
|
|
painter->setRasterOp (TQt::CopyROP);
|
|
}
|
|
else
|
|
{
|
|
fillColor = TQt::white;
|
|
painter->setRasterOp (TQt::XorROP);
|
|
}
|
|
|
|
TQMemArray <TQRect> rects = selResizeHandlesRegion.rects ();
|
|
for (TQMemArray <TQRect>::ConstIterator it = rects.begin ();
|
|
it != rects.end ();
|
|
it++)
|
|
{
|
|
painter->fillRect (*it, fillColor);
|
|
}
|
|
|
|
painter->restore ();
|
|
}
|
|
|
|
// protected
|
|
void kpView::paintEventDrawTempPixmap (TQPixmap *destPixmap, const TQRect &docRect)
|
|
{
|
|
kpViewManager *vm = viewManager ();
|
|
if (!vm)
|
|
return;
|
|
|
|
const kpTempPixmap *tpm = vm->tempPixmap ();
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "kpView::paintEventDrawTempPixmap() tempPixmap="
|
|
<< tpm
|
|
<< " isVisible="
|
|
<< (tpm ? tpm->isVisible (vm) : false)
|
|
<< endl;
|
|
#endif
|
|
|
|
if (!tpm || !tpm->isVisible (vm))
|
|
return;
|
|
|
|
tpm->paint (destPixmap, docRect);
|
|
}
|
|
|
|
// protected
|
|
void kpView::paintEventDrawGridLines (TQPainter *painter, const TQRect &viewRect)
|
|
{
|
|
int hzoomMultiple = zoomLevelX () / 100;
|
|
int vzoomMultiple = zoomLevelY () / 100;
|
|
|
|
TQPen ordinaryPen (TQt::gray);
|
|
TQPen tileBoundaryPen (TQt::lightGray);
|
|
|
|
painter->setPen (ordinaryPen);
|
|
|
|
// horizontal lines
|
|
int starty = viewRect.top ();
|
|
if (starty % vzoomMultiple)
|
|
starty = (starty + vzoomMultiple) / vzoomMultiple * vzoomMultiple;
|
|
int tileHeight = 16 * vzoomMultiple; // CONFIG
|
|
for (int y = starty - viewRect.y (); y <= viewRect.bottom () - viewRect.y (); y += vzoomMultiple)
|
|
{
|
|
if (0 && tileHeight > 0 && y % tileHeight == 0)
|
|
{
|
|
painter->setPen (tileBoundaryPen);
|
|
//painter.setRasterOp (TQt::XorROP);
|
|
}
|
|
|
|
painter->drawLine (0, y, viewRect.right () - viewRect.left (), y);
|
|
|
|
if (0 && tileHeight > 0 && y % tileHeight == 0)
|
|
{
|
|
painter->setPen (ordinaryPen);
|
|
//painter.setRasterOp (TQt::CopyROP);
|
|
}
|
|
}
|
|
|
|
// vertical lines
|
|
int startx = viewRect.left ();
|
|
if (startx % hzoomMultiple)
|
|
startx = (startx + hzoomMultiple) / hzoomMultiple * hzoomMultiple;
|
|
int tileWidth = 16 * hzoomMultiple; // CONFIG
|
|
for (int x = startx - viewRect.x (); x <= viewRect.right () - viewRect.x (); x += hzoomMultiple)
|
|
{
|
|
if (0 && tileWidth > 0 && x % tileWidth == 0)
|
|
{
|
|
painter->setPen (tileBoundaryPen);
|
|
//painter.setRasterOp (TQt::XorROP);
|
|
}
|
|
|
|
painter->drawLine (x, 0, x, viewRect.bottom () - viewRect.top ());
|
|
|
|
if (0 && tileWidth > 0 && x % tileWidth == 0)
|
|
{
|
|
painter->setPen (ordinaryPen);
|
|
//painter.setRasterOp (TQt::CopyROP);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void kpView::paintEventDrawRect (const TQRect &viewRect)
|
|
{
|
|
#if DEBUG_KP_VIEW_RENDERER
|
|
kdDebug () << "\tkpView::paintEventDrawRect(viewRect=" << viewRect
|
|
<< ")" << endl;
|
|
#endif
|
|
|
|
kpViewManager *vm = viewManager ();
|
|
const kpDocument *doc = document ();
|
|
|
|
if (!vm || !doc)
|
|
return;
|
|
|
|
|
|
if (viewRect.isEmpty ())
|
|
return;
|
|
|
|
|
|
TQRect docRect = paintEventGetDocRect (viewRect);
|
|
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\tdocRect=" << docRect << endl;
|
|
#endif
|
|
|
|
// uncomment to cause deliberate flicker (identifies needless updates)
|
|
#if DEBUG_KP_VIEW_RENDERER && 0
|
|
TQPainter flickerPainter (this);
|
|
flickerPainter.fillRect (viewRect, TQt::red);
|
|
flickerPainter.end ();
|
|
#endif
|
|
|
|
|
|
//
|
|
// Prepare Back Buffer
|
|
//
|
|
|
|
if (!d->m_backBuffer ||
|
|
d->m_backBuffer->width () < viewRect.width () ||
|
|
d->m_backBuffer->height () < viewRect.height () ||
|
|
d->m_backBuffer->width () > width () ||
|
|
d->m_backBuffer->height () > height ())
|
|
{
|
|
// don't use TQPixmap::resize() as that wastes time copying pixels
|
|
// that will be overwritten anyway
|
|
//
|
|
// OPT: Should use doubling trick or at least go up in multiples
|
|
// to reduce X server pressure.
|
|
delete d->m_backBuffer;
|
|
d->m_backBuffer = new TQPixmap (viewRect.width (), viewRect.height ());
|
|
}
|
|
|
|
// uncomment to catch bits of the view that the renderer forgot to update
|
|
#if 0
|
|
d->m_backBuffer->fill (TQt::green);
|
|
#endif
|
|
|
|
TQPainter backBufferPainter;
|
|
backBufferPainter.begin (d->m_backBuffer);
|
|
|
|
|
|
//
|
|
// Draw checkboard for transparent images and/or views with borders
|
|
//
|
|
|
|
TQPixmap docPixmap;
|
|
|
|
bool tempPixmapWillBeRendered = false;
|
|
|
|
if (!docRect.isEmpty ())
|
|
{
|
|
docPixmap = doc->getPixmapAt (docRect);
|
|
|
|
tempPixmapWillBeRendered =
|
|
(!doc->selection () &&
|
|
vm->tempPixmap () &&
|
|
vm->tempPixmap ()->isVisible (vm) &&
|
|
docRect.intersects (vm->tempPixmap ()->rect ()));
|
|
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\ttempPixmapWillBeRendered=" << tempPixmapWillBeRendered
|
|
<< " (sel=" << doc->selection ()
|
|
<< " tempPixmap=" << vm->tempPixmap ()
|
|
<< " tempPixmap.isVisible=" << (vm->tempPixmap () ? vm->tempPixmap ()->isVisible (vm) : false)
|
|
<< " docRect.intersects(tempPixmap.rect)=" << (vm->tempPixmap () ? docRect.intersects (vm->tempPixmap ()->rect ()) : false)
|
|
<< ")"
|
|
<< endl;
|
|
#endif
|
|
}
|
|
|
|
if (docPixmap.mask () ||
|
|
(tempPixmapWillBeRendered && vm->tempPixmap ()->mayChangeDocumentMask ()))
|
|
{
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\tmask=" << (bool) docPixmap.mask ()
|
|
<< endl;
|
|
#endif
|
|
paintEventDrawCheckerBoard (&backBufferPainter, viewRect);
|
|
}
|
|
else
|
|
{
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\tno mask" << endl;
|
|
#endif
|
|
}
|
|
|
|
|
|
if (!docRect.isEmpty ())
|
|
{
|
|
//
|
|
// Draw docPixmap + tempPixmap
|
|
//
|
|
|
|
if (doc->selection ())
|
|
{
|
|
paintEventDrawSelection (&docPixmap, docRect);
|
|
}
|
|
else if (tempPixmapWillBeRendered)
|
|
{
|
|
paintEventDrawTempPixmap (&docPixmap, docRect);
|
|
}
|
|
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\torigin=" << origin () << endl;
|
|
#endif
|
|
// blit scaled version of docPixmap + tempPixmap onto Back Buffer
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
TQTime scaleTimer; scaleTimer.start ();
|
|
#endif
|
|
backBufferPainter.translate (origin ().x () - viewRect.x (),
|
|
origin ().y () - viewRect.y ());
|
|
backBufferPainter.scale (double (zoomLevelX ()) / 100.0,
|
|
double (zoomLevelY ()) / 100.0);
|
|
backBufferPainter.drawPixmap (docRect, docPixmap);
|
|
backBufferPainter.resetXForm (); // back to 1-1 scaling
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\tscale time=" << scaleTimer.elapsed () << endl;
|
|
#endif
|
|
|
|
} // if (!docRect.isEmpty ()) {
|
|
|
|
|
|
//
|
|
// Draw Grid Lines
|
|
//
|
|
|
|
if (isGridShown ())
|
|
{
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
TQTime gridTimer; gridTimer.start ();
|
|
#endif
|
|
paintEventDrawGridLines (&backBufferPainter, viewRect);
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\tgrid time=" << gridTimer.elapsed () << endl;
|
|
#endif
|
|
}
|
|
|
|
|
|
const TQRect bvsvRect = buddyViewScrollableContainerRectangle ();
|
|
if (!bvsvRect.isEmpty ())
|
|
{
|
|
backBufferPainter.save ();
|
|
|
|
backBufferPainter.setRasterOp (TQt::XorROP);
|
|
backBufferPainter.setPen (TQt::white);
|
|
backBufferPainter.translate (-viewRect.x (), -viewRect.y ());
|
|
backBufferPainter.drawRect (bvsvRect);
|
|
|
|
backBufferPainter.restore ();
|
|
}
|
|
|
|
|
|
if (!docRect.isEmpty ())
|
|
{
|
|
if (doc->selection ())
|
|
{
|
|
// Draw resize handles on top of possible grid lines
|
|
paintEventDrawSelectionResizeHandles (&backBufferPainter, viewRect);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Blit Back Buffer to View
|
|
//
|
|
|
|
backBufferPainter.end ();
|
|
|
|
bitBlt (this, viewRect.topLeft (),
|
|
d->m_backBuffer, TQRect (0, 0, viewRect.width (), viewRect.height ()));
|
|
}
|
|
|
|
|
|
// protected virtual [base TQWidget]
|
|
void kpView::paintEvent (TQPaintEvent *e)
|
|
{
|
|
// sync: kpViewPrivate
|
|
// WARNING: document(), viewManager() and friends might be 0 in this method.
|
|
// TODO: I'm not 100% convinced that we always check if their friends are 0.
|
|
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
TQTime timer;
|
|
timer.start ();
|
|
#endif
|
|
|
|
kpViewManager *vm = viewManager ();
|
|
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "kpView(" << name () << ")::paintEvent() vm=" << (bool) vm
|
|
<< " queueUpdates=" << (vm && vm->queueUpdates ())
|
|
<< " fastUpdates=" << (vm && vm->fastUpdates ())
|
|
<< " viewRect=" << e->rect ()
|
|
<< " erased=" << e->erased ()
|
|
<< " topLeft=" << TQPoint (x (), y ())
|
|
<< endl;
|
|
#endif
|
|
|
|
if (!vm)
|
|
return;
|
|
|
|
if (vm->queueUpdates ())
|
|
{
|
|
// OPT: if this update was due to the document,
|
|
// use document coordinates (in case of a zoom change in
|
|
// which view coordinates become out of date)
|
|
addToQueuedArea (e->region ());
|
|
return;
|
|
}
|
|
|
|
|
|
TQRegion viewRegion = clipRegion ().intersect (e->region ());
|
|
TQMemArray <TQRect> rects = viewRegion.rects ();
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\t#rects = " << rects.count () << endl;
|
|
#endif
|
|
|
|
for (TQMemArray <TQRect>::ConstIterator it = rects.begin ();
|
|
it != rects.end ();
|
|
it++)
|
|
{
|
|
paintEventDrawRect (*it);
|
|
}
|
|
|
|
|
|
#if DEBUG_KP_VIEW_RENDERER && 1
|
|
kdDebug () << "\tall done in: " << timer.restart () << "ms" << endl;
|
|
#endif
|
|
}
|
|
|
|
|
|
#include <kpview.moc>
|