/* GNUPLOT - QtGnuplotScene.cpp */
/*[
* Copyright 2009 Jérôme Lodewyck
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose with or without fee is hereby granted,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear
* in supporting documentation.
*
* Permission to modify the software is granted, but not the right to
* distribute the complete modified source code. Modifications are to
* be distributed as patches to the released version. Permission to
* distribute binaries produced by compiling modified sources is granted,
* provided you
* 1. distribute the corresponding source modifications from the
* released version in the form of a patch file along with the binaries,
* 2. add special version identification to distinguish your version
* in addition to the base release version number,
* 3. provide your name and address as the primary contact for the
* support of your modified version, and
* 4. retain our contact information in regard to use of the base
* software.
* Permission to distribute the released version of the source code along
* with corresponding source modifications in the form of a patch file is
* granted with same provisions 2 through 4 for binary distributions.
*
* This software is provided "as is" without express or implied warranty
* to the extent permitted by applicable law.
*
*
* Alternatively, the contents of this file may be used under the terms of the
* GNU General Public License Version 2 or later (the "GPL"), in which case the
* provisions of GPL are applicable instead of those above. If you wish to allow
* use of your version of this file only under the terms of the GPL and not
* to allow others to use your version of this file under the above gnuplot
* license, indicate your decision by deleting the provisions above and replace
* them with the notice and other provisions required by the GPL. If you do not
* delete the provisions above, a recipient may use your version of this file
* under either the GPL or the gnuplot license.
]*/
#include "QtGnuplotWidget.h"
#include "QtGnuplotScene.h"
#include "QtGnuplotEvent.h"
#include "QtGnuplotItems.h"
extern "C" {
#include "../mousecmn.h"
// This is defined in term_api.h, but including it there fails to compile
typedef enum t_fillstyle { FS_EMPTY, FS_SOLID, FS_PATTERN, FS_DEFAULT, FS_TRANSPARENT_SOLID, FS_TRANSPARENT_PATTERN } t_fillstyle;
}
#include <QtGui>
#include <QDebug>
QtGnuplotScene::QtGnuplotScene(QtGnuplotEventHandler* eventHandler, QObject* parent)
: QGraphicsScene(parent)
{
m_widget = dynamic_cast<QtGnuplotWidget*>(parent);
m_eventHandler = eventHandler;
m_lastModifierMask = 0;
m_textAngle = 0.;
m_textAlignment = Qt::AlignLeft;
m_textOffset = QPoint(10,10);
m_currentZ = 1.;
m_currentPointSize = 1.;
m_enhanced = 0;
m_currentPlotNumber = 0;
m_inKeySample = false;
m_preserve_visibility = false;
m_inTextBox = false;
m_currentPointsItem = new QtGnuplotPoints;
m_currentGroup.clear();
resetItems();
}
QtGnuplotScene::~QtGnuplotScene()
{
delete m_currentPointsItem;
}
/////////////////////////////////////////////////
// Gnuplot events
// Vertical and horizontal lines do not render well with antialiasing
// Clip then to the nearest integer pixel
QPolygonF& QtGnuplotScene::clipPolygon(QPolygonF& polygon, bool checkDiag) const
{
if (checkDiag)
for (int i = 1; i < polygon.size(); i++)
if ((polygon[i].x() != polygon[i-1].x()) &&
(polygon[i].y() != polygon[i-1].y()))
return polygon;
for (int i = 0; i < polygon.size(); i++)
{
polygon[i].setX(qRound(polygon[i].x() + 0.5) - 0.5);
polygon[i].setY(qRound(polygon[i].y() + 0.5) - 0.5);
}
return polygon;
}
QPointF& QtGnuplotScene::clipPoint(QPointF& point) const
{
point.setX(qRound(point.x() + 0.5) - 0.5);
point.setY(qRound(point.y() + 0.5) - 0.5);
return point;
}
QRectF& QtGnuplotScene::clipRect(QRectF& rect) const
{
rect.setTop (qRound(rect.top () + 0.5) - 0.5);
rect.setBottom(qRound(rect.bottom() + 0.5) - 0.5);
rect.setLeft (qRound(rect.left () + 0.5) - 0.5);
rect.setRight (qRound(rect.right () + 0.5) - 0.5);
return rect;
}
// A succession of move and vector is gathered in a single polygon
// to make drawing faster
void QtGnuplotScene::flushCurrentPolygon()
{
if (m_currentPolygon.size() < 2) {
m_currentPolygon.clear();
return;
}
clipPolygon(m_currentPolygon);
if (!m_inKeySample)
m_currentPointsItem->addPolygon(m_currentPolygon, m_currentPen);
else
{
flushCurrentPointsItem();
// Including the polygon in a path is necessary to avoid closing the polygon
QPainterPath path;
path.addPolygon(m_currentPolygon);
QGraphicsPathItem *pathItem;
pathItem = addPath(path, m_currentPen, Qt::NoBrush);
pathItem->setZValue(m_currentZ++);
}
m_currentPolygon.clear();
}
void QtGnuplotScene::flushCurrentPointsItem()
{
if (m_currentPointsItem->isEmpty())
return;
m_currentPointsItem->setZValue(m_currentZ++);
addItem(m_currentPointsItem);
m_currentGroup.append(m_currentPointsItem);
m_currentPointsItem = new QtGnuplotPoints();
}
void QtGnuplotScene::update_key_box(const QRectF rect)
{
if (m_currentPlotNumber > m_key_boxes.count()) {
// DEBUG Feb 2018 should no longer trigger
// because m_key_box insertion is done in layer code for GEAfterPlot
m_key_boxes.insert(m_currentPlotNumber, QtGnuplotKeybox(rect));
} else if (m_key_boxes[m_currentPlotNumber-1].isEmpty()) {
// Retain the visible/hidden flag when re-initializing the Keybox
bool tmp = m_key_boxes[m_currentPlotNumber-1].ishidden();
m_key_boxes[m_currentPlotNumber-1] = QtGnuplotKeybox(rect);
m_key_boxes[m_currentPlotNumber-1].setHidden(tmp);
} else {
m_key_boxes[m_currentPlotNumber-1] |= rect;
}
}
void QtGnuplotScene::processEvent(QtGnuplotEventType type, QDataStream& in)
{
if ((type != GEMove) && (type != GEVector) && !m_currentPolygon.empty())
{
QPointF point = m_currentPolygon.last();
flushCurrentPolygon();
m_currentPolygon << point;
}
if (type == GEClear)
{
resetItems();
}
else if (type == GELineWidth)
{
double width; in >> width;
m_currentPen.setWidthF(width);
}
else if (type == GEMove)
{
QPointF point; in >> point;
if (!m_currentPolygon.empty() && (m_currentPolygon.last() != point))
{
flushCurrentPolygon();
m_currentPolygon.clear();
}
m_currentPolygon << point;
}
else if (type == GEVector)
{
QPointF point; in >> point;
m_currentPolygon << point;
if (m_inKeySample)
{
flushCurrentPointsItem();
update_key_box( QRectF(point, QSize(0,1)) );
}
}
else if (type == GEPenColor)
{
QColor color; in >> color;
m_currentPen.setColor(color);
}
else if (type == GEBackgroundColor)
{
m_currentPen.setColor(m_widget->backgroundColor());
}
else if (type == GEPenStyle)
{
int style; in >> style;
m_currentPen.setStyle(Qt::PenStyle(style));
if (m_widget->rounded()) {
m_currentPen.setJoinStyle(Qt::RoundJoin);
m_currentPen.setCapStyle(Qt::RoundCap);
} else {
m_currentPen.setJoinStyle(Qt::MiterJoin);
m_currentPen.setCapStyle(Qt::FlatCap);
}
}
else if (type == GEDashPattern)
{
QVector<qreal> dashpattern; in >> dashpattern;
m_currentPen.setDashPattern(dashpattern);
}
else if (type == GEPointSize)
in >> m_currentPointSize;
else if (type == GETextAngle)
in >> m_textAngle;
else if (type == GETextAlignment)
{
int alignment;
in >> alignment;
m_textAlignment = Qt::Alignment(alignment);
}
else if (type == GEFillBox)
{
flushCurrentPointsItem();
QRect rect; in >> rect;
QGraphicsRectItem *rectItem;
rectItem = addRect(rect, Qt::NoPen, m_currentBrush);
rectItem->setZValue(m_currentZ++);
if (m_inKeySample)
update_key_box(rect);
else
m_currentGroup.append(rectItem);
}
else if (type == GEFilledPolygon)
{
QPolygonF polygon; in >> polygon;
if (!m_inKeySample)
{
// Distinguish between opaque and transparent pattern fill
if (m_currentFillStyle == FS_PATTERN)
m_currentPointsItem->addFilledPolygon(clipPolygon(polygon, false),
QBrush(m_widget->backgroundColor()));
m_currentPointsItem->addFilledPolygon(clipPolygon(polygon, false), m_currentBrush);
}
else
{
flushCurrentPointsItem();
QPen pen = Qt::NoPen;
if (m_currentBrush.style() == Qt::SolidPattern)
pen = m_currentBrush.color();
clipPolygon(polygon, false);
QGraphicsPolygonItem *path;
path = addPolygon(polygon, pen, m_currentBrush);
path->setZValue(m_currentZ++);
}
}
else if (type == GEBrushStyle)
{
int style; in >> style;
setBrushStyle(style);
}
else if (type == GERuler)
{
QPoint point; in >> point;
updateRuler(point);
}
else if (type == GESetFont)
{
QString fontName; in >> fontName;
int size ; in >> size;
m_font.setFamily(fontName);
m_font.setPointSize(size);
m_font.setStyleStrategy(QFont::ForceOutline); // pcf fonts die if rotated
}
else if (type == GEPoint)
{
QPointF point; in >> point;
int style ; in >> style;
// Make sure point symbols are always drawn with a solid line
m_currentPen.setStyle(Qt::SolidLine);
// Append the point to a points item to speed-up drawing
if (!m_inKeySample)
m_currentPointsItem->addPoint(clipPoint(point), style, m_currentPointSize, m_currentPen);
else
{
flushCurrentPointsItem();
QtGnuplotPoint* pointItem = new QtGnuplotPoint(style, m_currentPointSize, m_currentPen);
pointItem->setPos(clipPoint(point));
pointItem->setZValue(m_currentZ++);
addItem(pointItem);
update_key_box( QRectF(point, QSize(2,2)) );
}
// Create a hypertext label that will become visible on mouseover.
// The Z offset is a kludge to force the label into the foreground.
if (!m_currentHypertext.isEmpty()) {
QGraphicsTextItem* textItem = addText(m_currentHypertext, m_font);
textItem->setPos(point + m_textOffset);
textItem->setZValue(m_currentZ+10000);
textItem->setVisible(false);
m_hypertextList.append(textItem);
m_currentHypertext.clear();
}
}
else if (type == GEPutText)
{
flushCurrentPointsItem();
QPoint point; in >> point;
QString text; in >> text;
QGraphicsTextItem* textItem = addText(text, m_font);
textItem->setDefaultTextColor(m_currentPen.color());
positionText(textItem, point);
QRectF rect = textItem->boundingRect();
if (m_textAlignment & Qt::AlignCenter) {
rect.moveCenter(point);
rect.moveBottom(point.y());
} else if (m_textAlignment & Qt::AlignRight)
rect.moveBottomRight(point);
else
rect.moveBottomLeft(point);
rect.adjust(0, rect.height()*0.67, 0, rect.height()*0.25);
if (m_inKeySample)
update_key_box(rect);
else
m_currentGroup.append(textItem);
#ifdef EAM_BOXED_TEXT
if (m_inTextBox) {
m_currentTextBox |= rect;
m_currentBoxRotation = m_textAngle;
m_currentBoxOrigin = point;
}
#endif
}
else if (type == GEEnhancedFlush)
{
flushCurrentPointsItem();
QString fontName; in >> fontName;
double fontSize ; in >> fontSize;
int fontStyle ; in >> fontStyle; // really QFont::Style
int fontWeight ; in >> fontWeight; // really QFont::Weight
double base ; in >> base;
bool widthFlag ; in >> widthFlag;
bool showFlag ; in >> showFlag;
int overprint ; in >> overprint;
QString text ; in >> text;
if (m_enhanced == 0)
m_enhanced = new QtGnuplotEnhanced();
m_enhanced->addText(fontName, fontSize,
(QFont::Style)fontStyle, (QFont::Weight)fontWeight,
base, widthFlag, showFlag,
overprint, text, m_currentPen.color());
}
else if (type == GEEnhancedFinish)
{
flushCurrentPointsItem();
QPoint point; in >> point;
positionText(m_enhanced, point);
m_enhanced->setZValue(m_currentZ++);
addItem(m_enhanced);
QRectF rect = m_enhanced->boundingRect();
if (m_textAlignment & Qt::AlignCenter) {
rect.moveCenter(point);
rect.moveBottom(point.y());
} else if (m_textAlignment & Qt::AlignRight)
rect.moveBottomRight(point);
else
rect.moveBottomLeft(point);
rect.adjust(-m_currentPen.width()*2, rect.height()/2,
m_currentPen.width()*2, rect.height()/2);
if (m_inKeySample)
update_key_box(rect);
else
m_currentGroup.append(m_enhanced);
#ifdef EAM_BOXED_TEXT
if (m_inTextBox) {
m_currentTextBox |= rect;
m_currentBoxRotation = m_textAngle;
m_currentBoxOrigin = point;
}
#endif
m_enhanced = 0;
}
else if (type == GEImage)
{
flushCurrentPointsItem();
QPointF p0; in >> p0;
QPointF p1; in >> p1;
QPointF p2; in >> p2;
QPointF p3; in >> p3;
QImage image; in >> image;
QSize size(p1.x() - p0.x(), p1.y() - p0.y());
QRectF clipRect(p2 - p0, p3 - p0);
QPixmap pixmap = QPixmap::fromImage(image).scaled(size, Qt::IgnoreAspectRatio, Qt::FastTransformation);
QtGnuplotClippedPixmap* item = new QtGnuplotClippedPixmap(clipRect, pixmap);
addItem(item);
item->setZValue(m_currentZ++);
item->setPos(p0);
m_currentGroup.append(item);
}
else if (type == GEZoomStart)
{
QString text; in >> text;
m_zoomBoxCorner = m_lastMousePos;
m_zoomRect->setRect(QRectF());
m_zoomRect->setVisible(true);
m_zoomStartText->setVisible(true);
m_zoomStartText->setPlainText(text); /// @todo font
QSizeF size = m_zoomStartText->boundingRect().size();
m_zoomStartText->setPos(m_zoomBoxCorner - QPoint(size.width(), size.height()));
}
else if (type == GEZoomStop)
{
QString text; in >> text;
if (text.isEmpty())
{
m_zoomRect->setVisible(false);
m_zoomStartText->setVisible(false);
m_zoomStopText->setVisible(false);
}
else
{
m_zoomStopText->setVisible(true);
m_zoomStopText->setPlainText(text); /// @todo font
m_zoomStopText->setPos(m_lastMousePos);
m_zoomRect->setRect(QRectF(m_zoomBoxCorner + QPointF(0.5, 0.5), m_lastMousePos + QPointF(0.5, 0.5)).normalized());
}
}
else if (type == GELineTo)
{
bool visible; in >> visible;
m_lineTo->setVisible(visible);
if (visible)
{
QLineF line = m_lineTo->line();
line.setP2(m_lastMousePos);
m_lineTo->setLine(line);
}
}
else if (type == GESetSceneSize)
{
QSize size; in >> size;
setSceneRect(QRect(QPoint(0, 0), size));
}
else if (type == GEScale)
{
for (int i = 0; i < 4; i++)
in >> m_axisValid[i] >> m_axisMin[i] >> m_axisLower[i] >> m_axisScale[i] >> m_axisLog[i];
in >> m_axisValid[4];
}
else if (type == GEAfterPlot)
{
flushCurrentPointsItem();
if (m_currentPlotNumber >= m_plot_group.count()) {
// End of previous plot, create group holding the accumulated elements
QGraphicsItemGroup *newgroup;
newgroup = createItemGroup(m_currentGroup);
newgroup->setZValue(m_currentZ++);
// Copy the visible/hidden status from the previous plot/replot
if (0 < m_currentPlotNumber && m_currentPlotNumber <= m_key_boxes.count())
newgroup->setVisible( !(m_key_boxes[m_currentPlotNumber-1].ishidden()) );
// Store it in an ordered list so we can toggle it by index
m_plot_group.insert(m_currentPlotNumber, newgroup);
}
if (m_currentPlotNumber >= m_key_boxes.count()) {
QRectF empty( QPointF(0,0), QPointF(0,0));
m_key_boxes.insert(m_currentPlotNumber, empty);
m_key_boxes[m_currentPlotNumber-1].resetStatus();
}
m_currentPlotNumber = 0;
}
else if (type == GEPlotNumber)
{
flushCurrentPointsItem();
int newPlotNumber; in >> newPlotNumber;
if (newPlotNumber >= m_plot_group.count()) {
// Initialize list of elements for next group
m_currentGroup.clear();
}
// Otherwise we are making a second pass through the same plots
// (currently used only to draw key samples on an opaque key box)
m_currentPlotNumber = newPlotNumber;
}
else if (type == GEModPlots)
{
int i = m_key_boxes.count();
unsigned int ops_i;
int plotno;
in >> ops_i;
in >> plotno;
enum QtGnuplotModPlots ops = (enum QtGnuplotModPlots) ops_i;
/* FIXME: This shouldn't happen, but it does. */
if (i > m_plot_group.count())
i = m_plot_group.count();
while (i-- > 0) {
/* Does operation only affect a single plot? */
if (plotno >= 0 && i != plotno)
continue;
bool isVisible = m_plot_group[i]->isVisible();
switch (ops) {
case QTMODPLOTS_INVERT_VISIBILITIES:
isVisible = !isVisible;
break;
case QTMODPLOTS_SET_VISIBLE:
isVisible = true;
break;
case QTMODPLOTS_SET_INVISIBLE:
isVisible = false;
break;
}
m_plot_group[i]->setVisible(isVisible);
m_key_boxes[i].setHidden(!isVisible);
}
}
else if (type == GELayer)
{
flushCurrentPointsItem();
int layer; in >> layer;
if (layer == QTLAYER_BEFORE_ZOOM) m_preserve_visibility = true;
if (layer == QTLAYER_BEGIN_KEYSAMPLE) m_inKeySample = true;
if (layer == QTLAYER_END_KEYSAMPLE)
{
m_inKeySample = false;
// FIXME: this catches mislabeled opaque keyboxes in multiplot mode
if (m_currentPlotNumber > m_key_boxes.length()) {
return;
}
// Draw an invisible grey rectangle in the key box.
// It will be set to visible if the plot is toggled off.
QtGnuplotKeybox *keybox = &m_key_boxes[m_currentPlotNumber-1];
m_currentBrush.setColor(Qt::lightGray);
m_currentBrush.setStyle(Qt::Dense4Pattern);
QGraphicsRectItem *statusBox = addRect(*keybox, Qt::NoPen, m_currentBrush);
statusBox->setZValue(m_currentZ-1);
keybox->showStatus(statusBox);
}
}
else if (type == GEHypertext)
{
m_currentHypertext.clear();
in >> m_currentHypertext;
}
#ifdef EAM_BOXED_TEXT
else if (type == GETextBox)
{
flushCurrentPointsItem();
QPointF point; in >> point;
int option; in >> option;
QGraphicsRectItem *rectItem;
QRectF outline;
/* Must match the definition in ../term_api.h */
enum t_textbox_options {
TEXTBOX_INIT = 0,
TEXTBOX_OUTLINE,
TEXTBOX_BACKGROUNDFILL,
TEXTBOX_MARGINS,
TEXTBOX_FINISH,
TEXTBOX_GREY
};
switch (option) {
case TEXTBOX_INIT:
/* Initialize bounding box */
m_currentTextBox = QRectF(point, point);
m_inTextBox = true;
break;
case TEXTBOX_OUTLINE:
/* Stroke bounding box */
outline = m_currentTextBox.adjusted(
-m_textMargin.x(), -m_textMargin.y(),
m_textMargin.x(), m_textMargin.y());
rectItem = addRect(outline, m_currentPen, Qt::NoBrush);
rectItem->setZValue(m_currentZ++);
rectItem->setTransformOriginPoint(m_currentBoxOrigin);
rectItem->setRotation(-m_currentBoxRotation);
m_currentGroup.append(rectItem);
m_inTextBox = false;
break;
case TEXTBOX_BACKGROUNDFILL:
/* Fill bounding box */
m_currentBrush.setColor(m_currentPen.color());
m_currentBrush.setStyle(Qt::SolidPattern);
outline = m_currentTextBox.adjusted(
-m_textMargin.x(), -m_textMargin.y(),
m_textMargin.x(), m_textMargin.y());
rectItem = addRect(outline, Qt::NoPen, m_currentBrush);
rectItem->setZValue(m_currentZ++);
rectItem->setTransformOriginPoint(m_currentBoxOrigin);
rectItem->setRotation(-m_currentBoxRotation);
m_currentGroup.append(rectItem);
m_inTextBox = false;
break;
case TEXTBOX_MARGINS:
/* Set margins of bounding box */
m_textMargin = point;
m_textMargin *= QFontMetrics(m_font).averageCharWidth();
break;
}
}
#endif
else if (type == GEFontMetricRequest)
{
QFontMetrics metrics(m_font);
int par1 = (metrics.ascent() + metrics.descent());
int par2 = metrics.width("0123456789")/10.;
m_eventHandler->postTermEvent(GE_fontprops, 0, 0, par1, par2, m_widget);
}
else if (type == GEDone)
{
flushCurrentPointsItem();
m_eventHandler->postTermEvent(GE_plotdone, 0, 0, 0, 0, m_widget);
}
else
swallowEvent(type, in);
}
void QtGnuplotScene::resetItems()
{
clear();
delete m_currentPointsItem;
m_currentPointsItem = new QtGnuplotPoints;
m_currentZ = 1.;
m_zoomRect = addRect(QRect(), QPen(QColor(0, 0, 0, 200)), QBrush(QColor(0, 0, 255, 40)));
m_zoomRect->setVisible(false);
m_zoomRect->setZValue(32767); // make sure guide box is on top
m_zoomStartText = addText("");
m_zoomStopText = addText("");
m_zoomStartText->setVisible(false);
m_zoomStopText->setVisible(false);
m_zoomStartText->setZValue(32767); // make sure guide box annotation is on top
m_zoomStopText->setZValue(32767);
m_horizontalRuler = addLine(QLine(0, 0, width(), 0) , QPen(QColor(0, 0, 0, 200)));
m_verticalRuler = addLine(QLine(0, 0, 0, height()), QPen(QColor(0, 0, 0, 200)));
m_lineTo = addLine(QLine() , QPen(QColor(0, 0, 0, 200)));
m_horizontalRuler->setVisible(false);
m_verticalRuler->setVisible(false);
m_lineTo->setVisible(false);
int i = m_key_boxes.count();
while (i-- > 0) {
m_key_boxes[i].setSize( QSizeF(0,0) );
m_key_boxes[i].resetStatus();
if (!m_preserve_visibility)
m_key_boxes[i].setHidden(false);
}
m_hypertextList.clear();
m_hypertextList.append(addRect(QRect(), QPen(QColor(0, 0, 0, 100)), QBrush(QColor(225, 225, 225, 200))));
m_hypertextList[0]->setVisible(false);
m_hyperimage = addPixmap(QPixmap());
m_hyperimage->setVisible(false);
m_plot_group.clear(); // Memory leak? Destroy groups first?
}
namespace QtGnuplot {
const Qt::BrushStyle brushes[8] = {
Qt::NoBrush, Qt::DiagCrossPattern, Qt::Dense3Pattern, Qt::SolidPattern,
Qt::FDiagPattern, Qt::BDiagPattern, Qt::Dense4Pattern, Qt::Dense5Pattern};
}
void QtGnuplotScene::setBrushStyle(int style)
{
int fillpar = style >> 4;
int fillstyle = style & 0xf;
m_currentBrush.setStyle(Qt::SolidPattern);
m_currentFillStyle = fillstyle;
QColor color = m_currentPen.color();
if (fillstyle == FS_TRANSPARENT_SOLID)
color.setAlphaF(double(fillpar)/100.);
else if (fillstyle == FS_SOLID && (fillpar < 100))
{
double fact = double(100 - fillpar)/100.;
double factc = 1. - fact;
if ((fact >= 0.) && (factc >= 0.))
{
color.setRedF (color.redF() *factc + fact);
color.setGreenF(color.greenF()*factc + fact);
color.setBlueF (color.blueF() *factc + fact);
}
}
else if ((fillstyle == FS_TRANSPARENT_PATTERN) || (fillstyle == FS_PATTERN))
m_currentBrush.setStyle(QtGnuplot::brushes[abs(fillpar) % 8]);
else if (fillstyle == FS_EMPTY) // fill with background plot->color
color = m_widget->backgroundColor();
m_currentBrush.setColor(color);
}
void QtGnuplotScene::positionText(QGraphicsItem* item, const QPoint& point)
{
item->setZValue(m_currentZ++);
double cx = 0.;
double cy = (item->boundingRect().bottom() + item->boundingRect().top())/2.;
if (m_textAlignment & Qt::AlignLeft)
cx = item->boundingRect().left();
else if (m_textAlignment & Qt::AlignRight)
cx = item->boundingRect().right();
else if (m_textAlignment & Qt::AlignCenter)
cx = (item->boundingRect().right() + item->boundingRect().left())/2.;
item->setTransformOriginPoint(cx, cy);
item->setRotation(-m_textAngle);
item->setPos(point.x() - cx, point.y() - cy);
}
void QtGnuplotScene::updateRuler(const QPoint& point)
{
if (point.x() < 0)
{
m_horizontalRuler->setVisible(false);
m_verticalRuler->setVisible(false);
m_lineTo->setVisible(false);
return;
}
QPointF pointF = QPointF(point) + QPointF(0.5, 0.5);
m_horizontalRuler->setVisible(true);
m_verticalRuler->setVisible(true);
m_horizontalRuler->setPos(0, pointF.y());
m_verticalRuler->setPos(pointF.x(), 0);
QLineF line = m_lineTo->line();
line.setP1(pointF);
m_lineTo->setLine(line);
}
/////////////////////////////////////////////////
// User events
void QtGnuplotScene::updateModifiers()
{
const int modifierMask = (QApplication::keyboardModifiers() >> 25);
if (modifierMask != m_lastModifierMask)
{
m_lastModifierMask = modifierMask;
m_eventHandler->postTermEvent(GE_modifier, 0, 0, modifierMask, 0, m_widget);
}
}
void QtGnuplotScene::mousePressEvent(QGraphicsSceneMouseEvent* event)
{
m_lastMousePos = event->scenePos();
updateModifiers();
int button = 0;
if (event->button()== Qt::LeftButton) button = 1;
else if (event->button()== Qt::MidButton) button = 2;
else if (event->button()== Qt::RightButton) button = 3;
m_eventHandler->postTermEvent(GE_buttonpress,
int(event->scenePos().x()), int(event->scenePos().y()),
button, 0, m_widget);
QGraphicsScene::mousePressEvent(event);
}
void QtGnuplotScene::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{
// Mousing for inactive widgets
if (!m_widget->isActive())
{
if (m_axisValid[4]) {
; // 3D plot - no mouse coordinate update
} else {
m_lineTo->hide();
QString status;
if (m_axisValid[0])
status += QString("x = ")
+ QString::number(sceneToGraph(0,event->scenePos().x()));
if (m_axisValid[1])
status += QString(" y = ")
+ QString::number(sceneToGraph(1,event->scenePos().y()));
if (m_axisValid[2])
status += QString(" x2 = ")
+ QString::number(sceneToGraph(2,event->scenePos().x()));
if (m_axisValid[3])
status += QString(" y2 = ")
+ QString::number(sceneToGraph(3,event->scenePos().y()));
m_widget->setStatusText(status);
}
QGraphicsScene::mouseMoveEvent(event);
return;
}
m_lastMousePos = event->scenePos();
updateModifiers();
if (m_lineTo->isVisible())
{
QLineF line = m_lineTo->line();
line.setP2(event->scenePos());
m_lineTo->setLine(line);
}
// The first item in m_hypertextList is always a background rectangle for the text
int i = m_hypertextList.count();
bool hit = false;
while (i-- > 1) {
if (!hit && ((m_hypertextList[i]->pos() - m_textOffset)
- m_lastMousePos).manhattanLength() <= 5) {
hit = true;
m_hypertextList[i]->setVisible(true);
((QGraphicsRectItem *)m_hypertextList[0])->setRect(m_hypertextList[i]->boundingRect());
m_hypertextList[0]->setPos(m_hypertextList[i]->pos());
m_hypertextList[0]->setZValue(m_hypertextList[i]->zValue()-1);
// Special hypertext "image{(xsize,ysize)}:filename"
QString current_text = ((QGraphicsTextItem *)(m_hypertextList[i]))->toPlainText();
if (current_text.startsWith("image")) {
int sep = current_text.indexOf(":");
QString imagename = current_text.mid(sep+1);
sep = imagename.indexOf("\n");
if (sep > 0)
imagename.truncate(sep);
m_hyperimage->setPixmap(QPixmap(imagename));
m_hyperimage->setVisible(true);
break;
}
} else {
m_hypertextList[i]->setVisible(false);
m_hyperimage->setVisible(false);
}
}
m_hypertextList[0]->setVisible(hit);
m_eventHandler->postTermEvent(GE_motion, int(event->scenePos().x()), int(event->scenePos().y()), 0, 0, m_widget);
QGraphicsScene::mouseMoveEvent(event);
}
void QtGnuplotScene::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
{
m_lastMousePos = event->scenePos();
updateModifiers();
int button = 0;
if (event->button()== Qt::LeftButton) button = 1;
else if (event->button()== Qt::MidButton) button = 2;
else if (event->button()== Qt::RightButton) button = 3;
qint64 time = 301; /* Only used the first time in, when timer not yet running */
if (m_watches[button].isValid())
time = m_watches[button].elapsed();
m_eventHandler->postTermEvent(GE_buttonrelease,
int(event->scenePos().x()), int(event->scenePos().y()), button, time, m_widget);
m_watches[button].start();
/* Check for left click in one of the keysample boxes */
if (button == 1) {
int n = m_key_boxes.count();
for (int i = 0; i < n; i++) {
if (m_key_boxes[i].contains(m_lastMousePos)) {
if (m_plot_group[i]->isVisible()) {
m_plot_group[i]->setVisible(false);
m_key_boxes[i].setHidden(true);
} else {
m_plot_group[i]->setVisible(true);
m_key_boxes[i].setHidden(false);
}
m_preserve_visibility = true;
break;
}
}
}
QGraphicsScene::mouseReleaseEvent(event);
}
/* http://developer.qt.nokia.com/doc/qt-4.8/qgraphicsscenewheelevent.html */
void QtGnuplotScene::wheelEvent(QGraphicsSceneWheelEvent* event)
{
updateModifiers();
if (event->orientation() == Qt::Horizontal) {
// 6 = scroll left, 7 = scroll right
m_eventHandler->postTermEvent(GE_buttonpress,
int(event->scenePos().x()), int(event->scenePos().y()),
event->delta() > 0 ? 6 : 7, 0, m_widget);
} else { /* if (event->orientation() == Qt::Vertical) */
// 4 = scroll up, 5 = scroll down
m_eventHandler->postTermEvent(GE_buttonpress,
int(event->scenePos().x()), int(event->scenePos().y()),
event->delta() > 0 ? 4 : 5, 0, m_widget);
}
}
void QtGnuplotScene::keyPressEvent(QKeyEvent* event)
{
updateModifiers();
int key = -1;
int live;
/// @todo quit on 'q' or Ctrl+'q'
// Keypad keys
if (event->modifiers() & Qt::KeypadModifier)
switch (event->key())
{
case Qt::Key_Space : key = GP_KP_Space ; break;
case Qt::Key_Tab : key = GP_KP_Tab ; break;
case Qt::Key_Enter : key = GP_KP_Enter ; break;
case Qt::Key_F1 : key = GP_KP_F1 ; break;
case Qt::Key_F2 : key = GP_KP_F2 ; break;
case Qt::Key_F3 : key = GP_KP_F3 ; break;
case Qt::Key_F4 : key = GP_KP_F4 ; break;
case Qt::Key_Insert : key = GP_KP_Insert ; break;
case Qt::Key_End : key = GP_KP_End ; break;
case Qt::Key_Home : key = GP_KP_Home ; break;
#ifdef __APPLE__
case Qt::Key_Down : key = GP_Down ; break;
case Qt::Key_Left : key = GP_Left ; break;
case Qt::Key_Right : key = GP_Right ; break;
case Qt::Key_Up : key = GP_Up ; break;
#else
case Qt::Key_Down : key = GP_KP_Down ; break;
case Qt::Key_Left : key = GP_KP_Left ; break;
case Qt::Key_Right : key = GP_KP_Right ; break;
case Qt::Key_Up : key = GP_KP_Up ; break;
#endif
case Qt::Key_PageDown : key = GP_KP_Page_Down; break;
case Qt::Key_PageUp : key = GP_KP_Page_Up ; break;
case Qt::Key_Delete : key = GP_KP_Delete ; break;
case Qt::Key_Equal : key = GP_KP_Equal ; break;
case Qt::Key_Asterisk : key = GP_KP_Multiply ; break;
case Qt::Key_Plus : key = GP_KP_Add ; break;
case Qt::Key_Comma : key = GP_KP_Separator; break;
case Qt::Key_Minus : key = GP_KP_Subtract ; break;
case Qt::Key_Period : key = GP_KP_Decimal ; break;
case Qt::Key_Slash : key = GP_KP_Divide ; break;
case Qt::Key_0 : key = GP_KP_0 ; break;
case Qt::Key_1 : key = GP_KP_1 ; break;
case Qt::Key_2 : key = GP_KP_2 ; break;
case Qt::Key_3 : key = GP_KP_3 ; break;
case Qt::Key_4 : key = GP_KP_4 ; break;
case Qt::Key_5 : key = GP_KP_5 ; break;
case Qt::Key_6 : key = GP_KP_6 ; break;
case Qt::Key_7 : key = GP_KP_7 ; break;
case Qt::Key_8 : key = GP_KP_8 ; break;
case Qt::Key_9 : key = GP_KP_9 ; break;
}
// ASCII keys
else if ((event->key() <= 0xff) && (!event->text().isEmpty()))
// event->key() does not respect the case
key = event->text()[0].toLatin1();
// Special keys
else
switch (event->key())
{
case Qt::Key_Backspace : key = GP_BackSpace ; break;
case Qt::Key_Tab : key = GP_Tab ; break;
case Qt::Key_Return : key = GP_Return ; break;
case Qt::Key_Escape : key = GP_Escape ; break;
case Qt::Key_Delete : key = GP_Delete ; break;
case Qt::Key_Pause : key = GP_Pause ; break;
case Qt::Key_ScrollLock : key = GP_Scroll_Lock; break;
case Qt::Key_Insert : key = GP_Insert ; break;
case Qt::Key_Home : key = GP_Home ; break;
case Qt::Key_Left : key = GP_Left ; break;
case Qt::Key_Up : key = GP_Up ; break;
case Qt::Key_Right : key = GP_Right ; break;
case Qt::Key_Down : key = GP_Down ; break;
case Qt::Key_PageUp : key = GP_PageUp ; break;
case Qt::Key_PageDown : key = GP_PageDown ; break;
case Qt::Key_End : key = GP_End ; break;
case Qt::Key_Enter : key = GP_KP_Enter ; break;
case Qt::Key_F1 : key = GP_F1 ; break;
case Qt::Key_F2 : key = GP_F2 ; break;
case Qt::Key_F3 : key = GP_F3 ; break;
case Qt::Key_F4 : key = GP_F4 ; break;
case Qt::Key_F5 : key = GP_F5 ; break;
case Qt::Key_F6 : key = GP_F6 ; break;
case Qt::Key_F7 : key = GP_F7 ; break;
case Qt::Key_F8 : key = GP_F8 ; break;
case Qt::Key_F9 : key = GP_F9 ; break;
case Qt::Key_F10 : key = GP_F10 ; break;
case Qt::Key_F11 : key = GP_F11 ; break;
case Qt::Key_F12 : key = GP_F12 ; break;
}
if (key >= 0)
live = m_eventHandler->postTermEvent(GE_keypress,
int(m_lastMousePos.x()), int(m_lastMousePos.y()), key, 0, m_widget);
else
live = true;
// Key handling in persist mode
// !live means (I think!) that we are in persist mode
if (!live) {
switch (key) {
default:
break;
case 'i':
int i = m_key_boxes.count();
/* FIXME: This shouldn't happen, but it does. */
if (i > m_plot_group.count())
i = m_plot_group.count();
while (i-- > 0) {
bool isVisible = m_plot_group[i]->isVisible();
isVisible = !isVisible;
m_plot_group[i]->setVisible(isVisible);
m_key_boxes[i].setHidden(!isVisible);
}
break;
}
}
QGraphicsScene::keyPressEvent(event);
}
double QtGnuplotScene::sceneToGraph(int axis, double coord) const
{
if (m_axisScale[axis] == 0.)
return 0;
double result = m_axisMin[axis] + (coord - m_axisLower[axis])/m_axisScale[axis];
if (m_axisLog[axis] > 0.)
result = exp(result * m_axisLog[axis]);
return result;
}