* Copyright 2006-2009, 2023, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
* Zardshard
*/
#include "PerspectiveBox.h"
#include <stdio.h>
#include <agg_trans_affine.h>
#include <agg_math.h>
#include <View.h>
#include "CanvasView.h"
#include "StateView.h"
#include "support.h"
#include "PerspectiveBoxStates.h"
#include "PerspectiveCommand.h"
#include "PerspectiveTransformer.h"
#define INSET 8.0
using std::nothrow;
using namespace PerspectiveBoxStates;
PerspectiveBox::PerspectiveBox(CanvasView* view,
PerspectiveTransformer* parent)
:
Manipulator(NULL),
fLeftTop(parent->LeftTop()),
fRightTop(parent->RightTop()),
fLeftBottom(parent->LeftBottom()),
fRightBottom(parent->RightBottom()),
fCurrentCommand(NULL),
fCurrentState(NULL),
fDragging(false),
fMousePos(-10000.0, -10000.0),
fModifiers(0),
fPreviousBox(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN),
fCanvasView(view),
fPerspective(parent),
fDragLTState(new DragCornerState(this, &fLeftTop)),
fDragRTState(new DragCornerState(this, &fRightTop)),
fDragLBState(new DragCornerState(this, &fLeftBottom)),
fDragRBState(new DragCornerState(this, &fRightBottom))
{
}
PerspectiveBox::~PerspectiveBox()
{
_NotifyDeleted();
delete fCurrentCommand;
delete fDragLTState;
delete fDragRTState;
delete fDragLBState;
delete fDragRBState;
}
void
PerspectiveBox::Draw(BView* into, BRect updateRect)
{
BPoint lt = fLeftTop;
BPoint rt = fRightTop;
BPoint lb = fLeftBottom;
BPoint rb = fRightBottom;
fCanvasView->ConvertFromCanvas(<);
fCanvasView->ConvertFromCanvas(&rt);
fCanvasView->ConvertFromCanvas(&lb);
fCanvasView->ConvertFromCanvas(&rb);
into->SetDrawingMode(B_OP_COPY);
into->SetHighColor(255, 255, 255, 255);
into->SetLowColor(0, 0, 0, 255);
_StrokeBWLine(into, lt, rt);
_StrokeBWLine(into, rt, rb);
_StrokeBWLine(into, rb, lb);
_StrokeBWLine(into, lb, lt);
_StrokeBWPoint(into, lt, 0.0);
_StrokeBWPoint(into, rt, 90.0);
_StrokeBWPoint(into, rb, 180.0);
_StrokeBWPoint(into, lb, 270.0);
}
bool
PerspectiveBox::MouseDown(BPoint where)
{
fCanvasView->FilterMouse(&where);
fCanvasView->ConvertToCanvas(&where);
fDragging = true;
if (fCurrentState) {
fCurrentState->SetOrigin(where);
delete fCurrentCommand;
fCurrentCommand = new (nothrow) PerspectiveCommand(this, fPerspective,
fPerspective->LeftTop(), fPerspective->RightTop(),
fPerspective->LeftBottom(), fPerspective->RightBottom());
}
return true;
}
void
PerspectiveBox::MouseMoved(BPoint where)
{
fCanvasView->FilterMouse(&where);
fCanvasView->ConvertToCanvas(&where);
if (fMousePos != where) {
fMousePos = where;
if (fCurrentState) {
fCurrentState->DragTo(fMousePos, fModifiers);
fCurrentState->UpdateViewCursor(fCanvasView, fMousePos);
}
}
}
Command*
PerspectiveBox::MouseUp()
{
fDragging = false;
return FinishTransaction();
}
bool
PerspectiveBox::MouseOver(BPoint where)
{
fCanvasView->ConvertToCanvas(&where);
fMousePos = where;
fCurrentState = _DragStateFor(where, fCanvasView->ZoomLevel());
if (fCurrentState) {
fCurrentState->UpdateViewCursor(fCanvasView, fMousePos);
return true;
}
return false;
}
BRect
PerspectiveBox::Bounds()
{
BPoint lt = fLeftTop;
BPoint rt = fRightTop;
BPoint lb = fLeftBottom;
BPoint rb = fRightBottom;
fCanvasView->ConvertFromCanvas(<);
fCanvasView->ConvertFromCanvas(&rt);
fCanvasView->ConvertFromCanvas(&lb);
fCanvasView->ConvertFromCanvas(&rb);
BRect bounds;
bounds.left = min4(lt.x, rt.x, lb.x, rb.x);
bounds.top = min4(lt.y, rt.y, lb.y, rb.y);
bounds.right = max4(lt.x, rt.x, lb.x, rb.x);
bounds.bottom = max4(lt.y, rt.y, lb.y, rb.y);
return bounds;
}
BRect
PerspectiveBox::TrackingBounds(BView* withinView)
{
return withinView->Bounds();
}
void
PerspectiveBox::ModifiersChanged(uint32 modifiers)
{
fModifiers = modifiers;
if (fDragging && fCurrentState) {
fCurrentState->DragTo(fMousePos, fModifiers);
}
}
bool
PerspectiveBox::UpdateCursor()
{
if (fCurrentState) {
fCurrentState->UpdateViewCursor(fCanvasView, fMousePos);
return true;
}
return false;
}
void
PerspectiveBox::AttachedToView(BView* view)
{
view->Invalidate(Bounds().InsetByCopy(-INSET, -INSET));
}
void
PerspectiveBox::DetachedFromView(BView* view)
{
view->Invalidate(Bounds().InsetByCopy(-INSET, -INSET));
}
void
PerspectiveBox::ObjectChanged(const Observable* object)
{
}
void
PerspectiveBox::TransformTo(
BPoint leftTop, BPoint rightTop, BPoint leftBottom, BPoint rightBottom)
{
if (fLeftTop == leftTop
&& fRightTop == rightTop
&& fLeftBottom == leftBottom
&& fRightBottom == rightBottom)
return;
fLeftTop = leftTop;
fRightTop = rightTop;
fLeftBottom = leftBottom;
fRightBottom = rightBottom;
Update();
}
void
PerspectiveBox::Update(bool deep)
{
BRect r = Bounds();
BRect dirty(r | fPreviousBox);
dirty.InsetBy(-INSET, -INSET);
fCanvasView->Invalidate(dirty);
fPreviousBox = r;
if (deep)
fPerspective->TransformTo(fLeftTop, fRightTop, fLeftBottom, fRightBottom);
}
Command*
PerspectiveBox::FinishTransaction()
{
Command* command = fCurrentCommand;
if (fCurrentCommand) {
fCurrentCommand->SetNewPerspective(
fPerspective->LeftTop(), fPerspective->RightTop(),
fPerspective->LeftBottom(), fPerspective->RightBottom());
fCurrentCommand = NULL;
}
return command;
}
bool
PerspectiveBox::AddListener(PerspectiveBoxListener* listener)
{
if (listener && !fListeners.HasItem((void*)listener))
return fListeners.AddItem((void*)listener);
return false;
}
bool
PerspectiveBox::RemoveListener(PerspectiveBoxListener* listener)
{
return fListeners.RemoveItem((void*)listener);
}
void
PerspectiveBox::_NotifyDeleted() const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
PerspectiveBoxListener* listener
= (PerspectiveBoxListener*)listeners.ItemAtFast(i);
listener->PerspectiveBoxDeleted(this);
}
}
DragState*
PerspectiveBox::_DragStateFor(BPoint where, float canvasZoom)
{
DragState* state = NULL;
float inset = INSET / canvasZoom;
float dLT = point_point_distance(fLeftTop, where);
float dRT = point_point_distance(fRightTop, where);
float dLB = point_point_distance(fLeftBottom, where);
float dRB = point_point_distance(fRightBottom, where);
float d = min4(dLT, dRT, dLB, dRB);
if (d < inset) {
if (d == dLT)
state = fDragLTState;
else if (d == dRT)
state = fDragRTState;
else if (d == dLB)
state = fDragLBState;
else if (d == dRB)
state = fDragRBState;
}
return state;
}
void
PerspectiveBox::_StrokeBWLine(BView* into, BPoint from, BPoint to) const
{
BPoint offset(0.0, 0.0);
float xDiff = to.x - from.x;
float yDiff = to.y - from.y;
if (fabs(xDiff) > fabs(yDiff)) {
if (xDiff > 0.0) {
offset.y = -1.0;
} else {
offset.y = 1.0;
}
} else {
if (yDiff < 0.0) {
offset.x = -1.0;
} else {
offset.x = 1.0;
}
}
into->StrokeLine(from, to, B_SOLID_LOW);
from += offset;
to += offset;
into->StrokeLine(from, to, B_SOLID_HIGH);
}
void
PerspectiveBox::_StrokeBWPoint(BView* into, BPoint point, double angle) const
{
double x = point.x;
double y = point.y;
double x1 = x;
double y1 = y - 5.0;
double x2 = x - 5.0;
double y2 = y - 5.0;
double x3 = x - 5.0;
double y3 = y;
agg::trans_affine m;
double xOffset = -x;
double yOffset = -y;
agg::trans_affine_rotation r(angle * M_PI / 180.0);
r.transform(&xOffset, &yOffset);
xOffset = x + xOffset;
yOffset = y + yOffset;
m.multiply(r);
m.multiply(agg::trans_affine_translation(xOffset, yOffset));
m.transform(&x, &y);
m.transform(&x1, &y1);
m.transform(&x2, &y2);
m.transform(&x3, &y3);
BPoint p[4];
p[0] = BPoint(x, y);
p[1] = BPoint(x1, y1);
p[2] = BPoint(x2, y2);
p[3] = BPoint(x3, y3);
into->FillPolygon(p, 4, B_SOLID_HIGH);
into->StrokeLine(p[0], p[1], B_SOLID_LOW);
into->StrokeLine(p[1], p[2], B_SOLID_LOW);
into->StrokeLine(p[2], p[3], B_SOLID_LOW);
into->StrokeLine(p[3], p[0], B_SOLID_LOW);
}