347 lines
9.9 KiB
C++
347 lines
9.9 KiB
C++
/*
|
|
* Dillo Widget
|
|
*
|
|
* Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
|
|
#include "core.hh"
|
|
#include "../lout/msg.h"
|
|
|
|
using namespace lout;
|
|
|
|
namespace dw {
|
|
namespace core {
|
|
|
|
Rectangle::Rectangle (int x, int y, int width, int height)
|
|
{
|
|
this->x = x;
|
|
this->y = y;
|
|
this->width = width;
|
|
this->height = height;
|
|
}
|
|
|
|
/*
|
|
* Draw rectangle in view relative to point (x,y).
|
|
*/
|
|
void Rectangle::draw (core::View *view, core::style::Style *style, int x,int y)
|
|
{
|
|
const bool filled = false;
|
|
|
|
view->drawRectangle(style->color, core::style::Color::SHADING_NORMAL,filled,
|
|
x + this->x, y + this->y, this->width, this->height);
|
|
}
|
|
|
|
/**
|
|
* Return whether this rectangle and otherRect intersect. If yes,
|
|
* return the intersection rectangle in dest.
|
|
*/
|
|
bool Rectangle::intersectsWith (Rectangle *otherRect, Rectangle *dest)
|
|
{
|
|
bool doIntersect =
|
|
this->x < otherRect->x + otherRect->width &&
|
|
this->y < otherRect->y + otherRect->height &&
|
|
otherRect->x < this->x + this->width &&
|
|
otherRect->y < this->y + this->height;
|
|
|
|
if (doIntersect) {
|
|
dest->x = std::max(this->x, otherRect->x);
|
|
dest->y = std::max(this->y, otherRect->y);
|
|
dest->width = std::min(this->x + this->width,
|
|
otherRect->x + otherRect->width) - dest->x;
|
|
dest->height = std::min(this->y + this->height,
|
|
otherRect->y + otherRect->height) - dest->y;
|
|
} else {
|
|
dest->x = dest->y = dest->width = dest->height = 0;
|
|
}
|
|
|
|
return doIntersect;
|
|
}
|
|
|
|
/*
|
|
* Return whether this is a subset of otherRect.
|
|
*/
|
|
bool Rectangle::isSubsetOf (Rectangle *otherRect)
|
|
{
|
|
return
|
|
x >= otherRect->x &&
|
|
y >= otherRect->y &&
|
|
x + width <= otherRect->x + otherRect->width &&
|
|
y + height <= otherRect->y + otherRect->height;
|
|
}
|
|
|
|
bool Rectangle::isPointWithin (int x, int y)
|
|
{
|
|
return
|
|
x >= this->x && y >= this->y &&
|
|
x < this->x + width && y < this->y + height;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
Circle::Circle (int x, int y, int radius)
|
|
{
|
|
this->x = x;
|
|
this->y = y;
|
|
this->radius = radius;
|
|
}
|
|
|
|
/*
|
|
* Draw circle in view relative to point (x,y).
|
|
*/
|
|
void Circle::draw (core::View *view, core::style::Style *style, int x, int y)
|
|
{
|
|
const bool filled = false;
|
|
|
|
view->drawArc(style->color, core::style::Color::SHADING_NORMAL, filled,
|
|
x + this->x, y + this->y, 2 * this->radius, 2 * this->radius,
|
|
0, 360);
|
|
}
|
|
|
|
bool Circle::isPointWithin (int x, int y)
|
|
{
|
|
return
|
|
(x - this->x) * (x - this->x) + (y - this->y) * (y - this->y)
|
|
<= radius * radius;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
Polygon::Polygon ()
|
|
{
|
|
minx = miny = 0xffffff;
|
|
maxx = maxy = -0xffffff;
|
|
}
|
|
|
|
/*
|
|
* Draw polygon in view relative to point (x,y).
|
|
*/
|
|
void Polygon::draw (core::View *view, core::style::Style *style, int x, int y)
|
|
{
|
|
if (points.size()) {
|
|
int i;
|
|
const bool filled = false, convex = false;
|
|
std::vector< Point > pointArray( points.size() );
|
|
|
|
for (i = 0; i < points.size(); i++) {
|
|
pointArray[i].x = x + points.at(i).x;
|
|
pointArray[i].y = y + points.at(i).y;
|
|
}
|
|
view->drawPolygon(style->color, core::style::Color::SHADING_NORMAL,
|
|
filled, convex, pointArray.data(), i);
|
|
}
|
|
}
|
|
|
|
void Polygon::addPoint (int x, int y)
|
|
{
|
|
points.emplace_back ();
|
|
points.back().x = x;
|
|
points.back().y = y;
|
|
|
|
minx = std::min(minx, x);
|
|
miny = std::min(miny, y);
|
|
maxx = std::max(maxx, x);
|
|
maxy = std::max(maxy, y);
|
|
}
|
|
|
|
/**
|
|
* \brief Return, whether the line, limited by (ax1, ay1) and (ax2, ay2),
|
|
* crosses the unlimited line, determined by two points (bx1, by1) and
|
|
* (bx2, by2).
|
|
*/
|
|
bool Polygon::linesCross0(int ax1, int ay1, int ax2, int ay2,
|
|
int bx1, int by1, int bx2, int by2)
|
|
{
|
|
/** TODO Some more description */
|
|
// If the scalar product is 0, it means that one point is on the second
|
|
// line, so we check for <= 0, not < 0.
|
|
int z1 = zOfVectorProduct (ax1 - bx1, ay1 - by1, bx2 - bx1, by2 - by1);
|
|
int z2 = zOfVectorProduct (ax2 - bx1, ay2 - by1, bx2 - bx1, by2 - by1);
|
|
|
|
return (z1 <= 0 && z2 >= 0) || (z1 >= 0 && z2 <= 0);
|
|
}
|
|
|
|
/**
|
|
* \brief Return, whether the line, limited by (ax1, ay1) and (ax2, ay2),
|
|
* crosses the line, limited by (bx1, by1) and (bx2, by2).
|
|
*/
|
|
bool Polygon::linesCross(int ax1, int ay1, int ax2, int ay2,
|
|
int bx1, int by1, int bx2, int by2)
|
|
{
|
|
bool cross =
|
|
linesCross0 (ax1, ay1, ax2, ay2, bx1, by1, bx2, by2) &&
|
|
linesCross0 (bx1, by1, bx2, by2, ax1, ay1, ax2, ay2);
|
|
_MSG("(%d, %d) - (%d, %d) and (%d, %d) - (%d, %d) cross? %s.\n",
|
|
ax1, ay1, ax2, ay2, bx1, by1, bx2, by2, cross ? "Yes" : "No");
|
|
return cross;
|
|
}
|
|
|
|
bool Polygon::isPointWithin (int x, int y)
|
|
{
|
|
if (points.size () < 3 ||
|
|
(x < minx || x > maxx || y < miny || y >= maxy))
|
|
return false;
|
|
else {
|
|
int numCrosses = 0;
|
|
for (int i = 0; i < points.size () - 1; i++) {
|
|
if (linesCross (minx - 1, miny - 1, x, y,
|
|
points.at(i).x, points.at(i).y,
|
|
points.at(i + 1).x, points.at(i + 1).y))
|
|
numCrosses++;
|
|
}
|
|
if (linesCross (minx - 1, miny - 1, x, y,
|
|
points.back().x,
|
|
points.back().y,
|
|
points.at(0).x, points.at(0).y))
|
|
numCrosses++;
|
|
|
|
return numCrosses % 2 == 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Add a rectangle to the region and combine it with
|
|
* existing rectangles if possible.
|
|
* The number of rectangles is forced to be less than 16
|
|
* by combining excessive rectangles.
|
|
*/
|
|
void Region::addRectangle (Rectangle *rPointer)
|
|
{
|
|
auto r= std::make_unique< Rectangle >( rPointer->x, rPointer->y,
|
|
rPointer->width, rPointer->height );
|
|
|
|
for( auto it= rectangleList.begin(); it != rectangleList.end(); )
|
|
{
|
|
// We have to increment immediately. The removal of the element, near the bottom
|
|
// of this loop will invalidate any iterators pointing into the rectangleList at
|
|
// that position. A range-based for loop will increment the iterator after the
|
|
// current pass, which would cause crashes.
|
|
auto &ownRect= *it++;
|
|
|
|
int combinedHeight =
|
|
std::max(r->y + r->height, ownRect->y + ownRect->height) -
|
|
std::min(r->y, ownRect->y);
|
|
int combinedWidth =
|
|
std::max(r->x + r->width, ownRect->x + ownRect->width) -
|
|
std::min(r->x, ownRect->x);
|
|
|
|
if (rectangleList.size() >= 16 ||
|
|
combinedWidth * combinedHeight <=
|
|
ownRect->width * ownRect->height + r->width * r->height) {
|
|
|
|
r->x = std::min(r->x, ownRect->x);
|
|
r->y = std::min(r->y, ownRect->y);
|
|
r->width = combinedWidth;
|
|
r->height = combinedHeight;
|
|
|
|
rectangleList.remove( ownRect );
|
|
}
|
|
}
|
|
|
|
rectangleList.push_back( std::move( r ) );
|
|
}
|
|
|
|
Content::Type Content::maskForSelection (bool followReferences)
|
|
{
|
|
Content::Type widgetMask = (Content::Type)
|
|
(Content::WIDGET_IN_FLOW |
|
|
(followReferences ? Content::WIDGET_OOF_REF : Content::WIDGET_OOF_CONT));
|
|
return (Content::Type)(Content::SELECTION_CONTENT | widgetMask);
|
|
}
|
|
|
|
void Content::intoStringBuffer(Content *content, misc::StringBuffer *sb)
|
|
{
|
|
switch(content->type) {
|
|
case START:
|
|
sb->append ("<start>");
|
|
break;
|
|
case END:
|
|
sb->append ("<end>");
|
|
break;
|
|
case TEXT:
|
|
sb->append ("\"");
|
|
sb->append (content->text);
|
|
sb->append ("\"");
|
|
break;
|
|
case WIDGET_IN_FLOW:
|
|
sb->append ("<widget in flow: ");
|
|
sb->appendPointer (content->widget);
|
|
sb->append (" (");
|
|
sb->append (content->widget->getClassName());
|
|
sb->append (")>");
|
|
break;
|
|
case WIDGET_OOF_REF:
|
|
sb->append ("<widget oof ref: ");
|
|
sb->appendPointer (content->widgetReference->widget);
|
|
sb->append (" (");
|
|
sb->append (content->widgetReference->widget->getClassName());
|
|
sb->append (", ");
|
|
sb->appendInt (content->widgetReference->parentRef);
|
|
sb->append (")>");
|
|
break;
|
|
case WIDGET_OOF_CONT:
|
|
sb->append ("<widget oof cont: ");
|
|
sb->appendPointer (content->widget);
|
|
sb->append (" (");
|
|
sb->append (content->widget->getClassName());
|
|
sb->append (")>");
|
|
break;
|
|
case BREAK:
|
|
sb->append ("<break>");
|
|
break;
|
|
default:
|
|
sb->append ("<");
|
|
sb->appendInt (content->type);
|
|
sb->append ("?>");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Content::maskIntoStringBuffer(Type mask, misc::StringBuffer *sb)
|
|
{
|
|
sb->append ((mask & START) ? "st" : "--");
|
|
sb->append (":");
|
|
sb->append ((mask & END) ? "en" : "--");
|
|
sb->append (":");
|
|
sb->append ((mask & TEXT) ? "tx" : "--");
|
|
sb->append (":");
|
|
sb->append ((mask & WIDGET_IN_FLOW) ? "wf" : "--");
|
|
sb->append (":");
|
|
sb->append ((mask & WIDGET_OOF_REF) ? "Wr" : "--");
|
|
sb->append (":");
|
|
sb->append ((mask & WIDGET_OOF_CONT) ? "Wc" : "--");
|
|
sb->append (":");
|
|
sb->append ((mask & BREAK) ? "br" : "--");
|
|
}
|
|
|
|
void Content::print (Content *content)
|
|
{
|
|
misc::StringBuffer sb;
|
|
intoStringBuffer (content, &sb);
|
|
printf ("%s", sb.getChars ());
|
|
}
|
|
|
|
void Content::printMask (Type mask)
|
|
{
|
|
misc::StringBuffer sb;
|
|
maskIntoStringBuffer (mask, &sb);
|
|
printf ("%s", sb.getChars ());
|
|
}
|
|
|
|
} // namespace core
|
|
} // namespace dw
|