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.

1272 lines
40 KiB

/***************************************************************************
qsctools.cpp
-------------------
begin : Sun Jan 30 2000
copyright : (C) 2000 by Kamil Dobkowski
email : kamildbk@friko.onet.pl
***************************************************************************/
/***************************************************************************
* *
* 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 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include"qsctools.h"
#include"qscobjects.h"
#include"kspanelmanager.h"
#include"ksworkbook.h"
#include"kscommands.h"
#include"widgets/qsdrvqt.h"
#include"widgets/qsdrvhittest.h"
#include"widgets/qsplotview.h"
#include"widgets/qsaxes2d.h"
#include"widgets/qsaxes3d.h"
#include"widgets/qsaxis.h"
#include"dialogs/kstextedit.h"
#include<qcursor.h>
#include<qlabel.h>
#include<qpainter.h>
#include<qpopupmenu.h>
#include<qpen.h>
#include<math.h>
#include"kmatplotshell.h"
//-------------------------------------------------------------//
QSToolLabel::QSToolLabel( QObject *parent )
: QSTool( parent )
{
}
//-------------------------------------------------------------//
QSToolLabel::~QSToolLabel()
{
}
//-------------------------------------------------------------//
void QSToolLabel::activate( QSPlotView *p )
{
QSTool::activate( p );
m_view->showUserMessage( tr("Label tool\n"
"Click on a canvas to create a new label. "
"If the axis object is currently selected the "
"newly created label will became its child object "
"and can be positioned relative to its parents position. "
"Click on a existing label to edit it. ") );
}
//-------------------------------------------------------------//
void QSToolLabel::deactivate()
{
// WE CANT set selection in deactivate.
// selection get first sigPageRemoved - it clear itself, next PlotView gets page removed,
// calls deactivate, and in deactivate we select an object which was removed with this page.
//m_view->selection()->set( active );
QSTool::deactivate();
}
//-------------------------------------------------------------//
void QSToolLabel::canvasClicked( const QPoint& pos, int )
{
QSCLabel *clicked = dynamic_cast<QSCLabel*>(m_view->objectAt(pos,true));
QSCLabel *edited_label;
if ( !clicked ) {
edited_label = new QSCLabel();
edited_label->setBox( QSRectf( pos.x(), pos.y(), 10, 10 ), driver() );
} else {
edited_label = clicked;
}
KSTextEditDlg dlg(m_view,edited_label->text(),edited_label->font());
if ( dlg.exec() ) edited_label->setText( dlg.text() );
if ( !clicked ) {
if ( edited_label->text().isEmpty() ) {
delete edited_label;
} else {
QSRectf box = edited_label->box(driver());
if ( dynamic_cast<KSWorkbook*>(m_view->workbook())->execute( new KSCmdAddCObject(edited_label,m_view->activeCollection()) ) )
edited_label->setBox( box, driver() );
}
} else {
if ( edited_label->text().isEmpty() ) {
dynamic_cast<KSWorkbook*>(m_view->workbook())->execute( new KSCmdRemoveCObject(edited_label) );
}
}
}
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
class QSToolSelect::Handle {
public:
enum Type {
Selected,
Resize,
Rotate,
RCenter,
Special
};
enum AlignFlags {
HCenter = 1U<<0,
VCenter = 1U<<1,
Left = 1U<<2,
Right = 1U<<3,
Top = 1U<<4,
Bottom = 1U<<5
};
Type m_type;
int m_align;
QSCObject *m_object;
QPoint m_pos;
QRect m_rect;
int m_number;
QCursor m_cursor;
//----------------------------------------------//
Handle( const QPoint& pos, QSCObject *object, Type type, int align=HCenter|VCenter, int number=-1 ) {
m_pos = pos;
m_object = object;
m_type = type;
m_align = align;
m_number = number;
switch( m_type ) {
case Special: m_rect = QRect( 0, 0, 5, 5 ); break;
default: m_rect = QRect( 0, 0, 7, 7 ); break;
}
m_rect.moveCenter( m_pos );
if ( align & Left ) m_rect.moveTopRight( QPoint(m_pos.x(),m_rect.top()) );
if ( align & Right ) m_rect.moveTopLeft( QPoint(m_pos.x(),m_rect.top()) );
if ( align & Top ) m_rect.moveBottomLeft( QPoint(m_rect.left(),m_pos.y()) );
if ( align & Bottom ) m_rect.moveTopLeft( QPoint(m_rect.left(),m_pos.y()) );
}
~Handle() {
}
//--------------------------------------------//
void paint( QPainter *p ) {
p->setPen( Qt::SolidLine );
p->setBrush( Qt::NoBrush );
switch( m_type ) {
case Resize: p->fillRect( m_rect, black ); break;
case Selected: p->drawRect( m_rect ); break;
case Rotate: p->drawEllipse( m_rect ); break;
case Special: p->drawEllipse( m_rect ); break;
case RCenter: p->drawEllipse( m_rect );
p->drawPoint( m_pos ); break;
}
}
//-------------------------------------------//
QCursor cursor() {
if ( m_type == Rotate ) {
return QCursor(Qt::crossCursor);
}
if ( m_type == Resize ) {
if ( m_align & HCenter ) return QCursor(Qt::sizeVerCursor);
if ( m_align & VCenter ) return QCursor(Qt::sizeHorCursor);
if ( m_align & Top ) {
if ( m_align & Left ) return QCursor(Qt::sizeFDiagCursor);
if ( m_align & Right ) return QCursor(Qt::sizeBDiagCursor);
}
if ( m_align & Bottom ) {
if ( m_align & Left ) return QCursor(Qt::sizeBDiagCursor);
if ( m_align & Right ) return QCursor(Qt::sizeFDiagCursor);
}
}
return QCursor(Qt::arrowCursor);
}
};
//-------------------------------------------------------------//
QSToolSelect::QSToolSelect( KMatplotShell *shell, QObject *parent )
: QSTool( parent )
{
m_state = StateNormal;
m_shell = shell;
m_handles.setAutoDelete( TRUE );
m_handles_visible = false;
}
//-------------------------------------------------------------//
QSToolSelect::~QSToolSelect()
{
}
//-------------------------------------------------------------//
void QSToolSelect::make_new_handles()
{
m_handles.clear();
QSSelection *curr_sel = m_view->selection();
for( int object_nr=0; object_nr<curr_sel->count(); object_nr++ ) {
QSCObject *curr_obj = curr_sel->object(object_nr);
QSRectf curr_box = curr_obj->box(driver()).normalize();
QSPt2f curr_rcenter = curr_obj->rCenter(driver());
QRect rect = curr_box.rect();
QPoint rcenter = curr_rcenter.point();
bool is_resizeable = curr_obj->style() & QSCObject::Resizeable;
bool is_rotateable = curr_obj->style() & QSCObject::Rotateable;
if ( m_state == StateNormal || !is_rotateable ) {
Handle::Type type = is_resizeable ? Handle::Resize : Handle::Selected;
m_handles.append( new Handle( QPoint(rect.center().x(),rect.top()), curr_obj, type, Handle::HCenter|Handle::Top ) );
m_handles.append( new Handle( QPoint(rect.center().x(),rect.bottom()), curr_obj, type, Handle::HCenter|Handle::Bottom ) );
m_handles.append( new Handle( QPoint(rect.left(),rect.center().y()), curr_obj, type, Handle::VCenter|Handle::Left ) );
m_handles.append( new Handle( QPoint(rect.right(),rect.center().y()), curr_obj, type, Handle::VCenter|Handle::Right ) );
m_handles.append( new Handle( rect.topLeft(), curr_obj, type, Handle::Top|Handle::Left ) );
m_handles.append( new Handle( rect.topRight(), curr_obj, type, Handle::Top|Handle::Right ) );
m_handles.append( new Handle( rect.bottomLeft(), curr_obj, type, Handle::Bottom|Handle::Left ) );
m_handles.append( new Handle( rect.bottomRight(), curr_obj, type, Handle::Bottom|Handle::Right ) );
}
else
if ( m_state == StateRotate ) {
m_handles.append( new Handle( rect.topLeft(), curr_obj, Handle::Rotate, Handle::Top|Handle::Left ) );
m_handles.append( new Handle( rect.topRight(), curr_obj, Handle::Rotate, Handle::Top|Handle::Right ) );
m_handles.append( new Handle( rect.bottomLeft(), curr_obj, Handle::Rotate, Handle::Bottom|Handle::Left ) );
m_handles.append( new Handle( rect.bottomRight(), curr_obj, Handle::Rotate, Handle::Bottom|Handle::Right ) );
m_handles.append( new Handle( rcenter, curr_obj, Handle::RCenter ) );
}
}
}
//-------------------------------------------------------------//
void QSToolSelect::paint_handles()
{
if ( m_view->fullPage() ) {
QPainter p(m_view->canvasWidget());
p.setRasterOp( Qt::NotXorROP );
for( unsigned int i=0; i<m_handles.count(); i++ ) m_handles.at(i)->paint(&p);
m_handles_visible = !m_handles_visible;
}
}
//-------------------------------------------------------------//
void QSToolSelect::show_handles()
{
if ( !m_handles_visible ) paint_handles();
}
//-------------------------------------------------------------//
void QSToolSelect::hide_handles()
{
if ( m_handles_visible ) paint_handles();
}
//-------------------------------------------------------------//
QSToolSelect::Handle *QSToolSelect::handle_at( const QPoint& pos )
{
if ( m_handles_visible && m_view->currentPage() && m_view->selection()->rootCollection() == m_view->currentPage()->objects() )
for ( unsigned int i=0; i<m_handles.count(); i++ )
if ( m_handles.at(i)->m_rect.contains(pos) ) return m_handles.at(i);
return NULL;
}
//-------------------------------------------------------------//
QSCObject *QSToolSelect::selected_at( const QPoint& pos )
{
if ( m_view->currentPage() && m_view->selection()->rootCollection() == m_view->currentPage()->objects() ) {
return m_view->selection()->objectAt( QSPt2f(pos.x(),pos.y()), driver() );
}
return NULL;
}
//-------------------------------------------------------------//
void QSToolSelect::activate( QSPlotView *p )
{
QSTool::activate( p );
m_state = StateNormal;
connect( m_view->selection(), SIGNAL(sigListChanged()), this, SLOT(slot_selection_changed()) );
m_view->showUserMessage(tr("Select tool\n"
"Click to select an object pointed by cursor. "
"CTRL+Click selects an object inside a group. "
"SHIFT+Click adds/removes an object from the selection. "
"Drag with SHIFT button pressed to move smoothly. "
"Click again on the selected object to turn on a rotate mode. "
"Right-button click shows menu. "
"Middle-button click selects a property panel of the element under mouse pointer.") );
make_new_handles();
show_handles();
}
//-------------------------------------------------------------//
void QSToolSelect::deactivate()
{
disconnect( m_view->selection(), SIGNAL(sigListChanged()), this, SLOT(slot_selection_changed()) );
hide_handles();
m_handles.clear();
QSTool::deactivate();
}
//-------------------------------------------------------------//
void QSToolSelect::slot_selection_changed()
{
hide_handles();
make_new_handles();
show_handles();
m_view->showUserMessage( QString(tr(" %1 objects selected.")).arg(m_view->selection()->count()) );
}
//-------------------------------------------------------------//
void QSToolSelect::draw()
{
m_handles_visible = false;
if ( m_view->fullPage() && m_view->currentPage() && m_view->selection()->rootCollection() == m_view->currentPage()->objects() ) {
make_new_handles();
show_handles();
}
}
//-------------------------------------------------------------//
void QSToolSelect::canvasMove( const QPoint& pos )
{
if ( !m_view->fullPage() ) return;
Handle *handle = handle_at(pos);
if ( handle ) {
m_view->canvasWidget()->setCursor( handle->cursor() );
return;
}
/*
QSCObject *object = selected_at(pos);
if ( object ) {
m_view->canvasWidget()->setCursor( sizeAllCursor );
return;
}
*/
m_view->canvasWidget()->setCursor( arrowCursor );
}
//-------------------------------------------------------------//
void QSToolSelect::canvasMiddleButtonClicked( const QPoint& pos, int )
{
if ( m_view->activeObject() && m_view->activeObject()->busy() ) m_view->activeObject()->stop();
if ( m_view->activeObject() && !m_view->activeObject()->busy() ) {
KSPanelManager *panel_manager = dynamic_cast<KSPanelManager*>(m_shell->propertyContainer()->widget());
// test hit - select panel
if ( panel_manager ) {
QSDrvHitTest drv( QSPt2f(pos.x(),pos.y()) );
drv.setDC(new QPainter(m_view->canvasWidget()),m_view->dpi(),true);
connect(&drv,SIGNAL(hitDetected(int,int)),panel_manager,SLOT(selectPanel(int,int)));
// hack - axes can be in a single view mode and redrawing axes object
// redraw object on page, not zoomed in a single view...
// Driver is not created on the stack ( why ? ) so do blocking redraw
if ( m_view->activeObject()->isAxesShadow() ) {
m_view->activeObject()->parentAxes()->drawPlot( &drv, true, true );
} else {
m_view->activeObject()->draw( &drv, true, true );
}
}
}
}
//-------------------------------------------------------------//
void QSToolSelect::canvasClicked( const QPoint& pos, int keyState )
{
if ( m_view->fullPage() ) {
QSCObject *clicked_object = selected_at(pos);
if ( clicked_object == NULL || (keyState&ControlButton) ) clicked_object = m_view->objectAt(pos,(keyState&ControlButton));
// user clicked on a new object with Shift pressed - add/remove object from selection
if ( keyState & Qt::ShiftButton ) {
m_state = StateNormal;
m_view->selection()->turn( clicked_object );
}
// user clicked on selected object - turn on/off rotation mode
else if ( m_view->selection()->contains(clicked_object) ) {
m_state = m_state==StateNormal?StateRotate:StateNormal;
m_view->selection()->set( clicked_object );
}
// user clicked on some object - select it
else {
m_state = StateNormal;
m_view->selection()->set( clicked_object );
}
}
}
//-------------------------------------------------------------//
bool QSToolSelect::canvasDragStart( const QPoint& pos, int keyState )
{
m_view->currentPage()->objects()->stop();
m_drag_handle = NULL;
m_drag_object = NULL;
m_move_prev_d = QSPt2f();
// check if handle clicked
if ( handle_at(pos) ) {
m_drag_handle = handle_at(pos);
create_transform_cmd();
paint_selected_objects();
}
// check if selected object clicked - moving objects
else if ( selected_at(pos) ) {
m_drag_object = selected_at(pos);
create_transform_cmd();
paint_selected_objects();
}
// check if other object was clicked
else if ( m_view->objectAt(pos,(keyState&ControlButton)) ) {
QSCObject *clicked_object = m_view->objectAt(pos,(keyState&ControlButton));
m_view->selection()->set(clicked_object);
m_drag_object = clicked_object;
create_transform_cmd();
paint_selected_objects();
}
// selecting using bounding frame
else if ( keyState & Qt::ShiftButton ) {
paint_frame( pos, pos );
}
// selecting using bounding frame
else {
m_view->selection()->clear();
paint_frame( pos, pos );
}
return true;
}
//-------------------------------------------------------------//
void QSToolSelect::canvasDragMove( const QPoint& pos, const QPoint& prevPos, const QPoint& startPos, int keyState, int, int )
{
// draging some handle
if ( m_drag_handle ) {
switch( m_drag_handle->m_type ) {
case Handle::Resize: drag_resize_handle_by( pos-startPos, keyState, m_drag_handle ); break;
case Handle::Rotate: drag_rotate_handle( startPos, pos, keyState, m_drag_handle ); break;
}
}
// moving a group of objects
else if ( m_drag_object ) {
move_selected_by( pos-startPos, keyState );
}
// selecting using bounding frame
else {
paint_frame( prevPos, startPos );
paint_frame( pos, startPos );
}
}
//-------------------------------------------------------------//
void QSToolSelect::canvasDragEnd( const QPoint& pos, const QPoint& startPos, int, int )
{
// dragging some handle
if ( m_drag_handle ) {
paint_selected_objects();
m_transform_cmd->commit();
dynamic_cast<KSWorkbook*>(m_view->workbook())->execute( m_transform_cmd );
m_transform_cmd = NULL;
}
// moving a group of objects
else if ( m_drag_object ) {
paint_selected_objects();
m_transform_cmd->commit();
dynamic_cast<KSWorkbook*>(m_view->workbook())->execute( m_transform_cmd );
m_transform_cmd = NULL;
}
// selecting using bounding frame
else {
paint_frame( pos, startPos );
QSRectf bounding_rect = QSRectf( QSPt2f(startPos), QSPt2f(pos), true );
for( int i=0; i<m_view->currentPage()->objects()->count(); i++ ) {
QSCObject *object = m_view->currentPage()->objects()->object(i);
QSRectf curr_rect = object->box(driver());
if ( bounding_rect.contains(curr_rect.pos) &&
bounding_rect.contains(curr_rect.pos+curr_rect.size) ) {
m_view->selection()->add(object);
}
}
}
}
//-------------------------------------------------------------//
bool QSToolSelect::eventMousePress( QMouseEvent* e )
{
if ( e->button() == Qt::RightButton ) {
m_shell->doAction( m_shell->m_object_menu );
return TRUE;
}
return QSTool::eventMousePress( e );
}
//-------------------------------------------------------------//
void QSToolSelect::drag_rotate_handle( const QPoint& startPos, const QPoint& currPos, int keyState, Handle *handle )
{
QSCObject *object = handle->m_object;
if ( handle->m_type == Handle::Rotate ) {
QSPt2f center = object->rCenter(driver());
int new_angle = snapAngle( int(angle( center, startPos, currPos )) + m_transform_cmd->objectAngle(object), keyState );
if ( new_angle != object->angle() ) {
paint_selected_objects();
object->setAutoUpdates(false);
object->setAngle(new_angle);
object->setAutoUpdates(true);
paint_selected_objects();
}
}
}
//-------------------------------------------------------------//
void QSToolSelect::drag_resize_handle_by( const QPoint& mouseShift, int keyState, Handle *handle )
{
QSCObject *object = handle->m_object;
if ( handle->m_type == Handle::Resize ) {
QSRectf rect = m_transform_cmd->objectRect( object );
if ( handle->m_align & Handle::Top ) {
rect.setTopLeft( QSPt2f( rect.topLeft().x, snapToGridY( rect.topLeft().y + mouseShift.y(), keyState ) ) );
}
if ( handle->m_align & Handle::Bottom ) {
rect.setBottomRight( QSPt2f( rect.bottomRight().x, snapToGridY( rect.bottomRight().y + mouseShift.y(), keyState ) ) );
}
if ( handle->m_align & Handle::Left ) {
rect.setTopLeft( QSPt2f( snapToGridX( rect.topLeft().x + mouseShift.x(), keyState ), rect.topLeft().y ) );
}
if ( handle->m_align & Handle::Right ) {
rect.setBottomRight( QSPt2f( snapToGridX( rect.bottomRight().x + mouseShift.x(), keyState ), rect.bottomRight().y ) );
}
if ( rect.normalize() != object->box(driver()).normalize() ) {
paint_selected_objects();
object->setAutoUpdates(false);
object->setBox(rect,driver());
object->setAutoUpdates(true);
paint_selected_objects();
}
}
}
//-------------------------------------------------------------//
void QSToolSelect::move_selected_by( const QPoint& mouseShift, int keyState )
{
// find top-left corner of selection
QSPt2f selection_pos;
for( int i=0; i<m_view->selection()->count(); i++ ) {
QSCObject *object = m_view->selection()->object(i);
QSPt2f pos = m_transform_cmd->objectRect( object ).normalize().pos;
if ( i==0 ) selection_pos = pos;
selection_pos.x = QMIN( pos.x, selection_pos.x );
selection_pos.y = QMIN( pos.y, selection_pos.y );
}
// shift the corner, snap to grid, and calculate resulting move distance
QSPt2f new_pos = snapToGrid( selection_pos+QSPt2f(mouseShift), keyState );
QSPt2f d = new_pos-selection_pos;
// move all object by this distance
if ( m_move_prev_d != d ) {
paint_selected_objects();
for( int i=0; i<m_view->selection()->count(); i++ ) {
QSCObject *object = m_view->selection()->object(i);
QSRectf box = m_transform_cmd->objectRect( object );
box.pos = box.pos + d;
object->setAutoUpdates(false);
object->setBox(box,driver());
object->setAutoUpdates(true);
}
m_move_prev_d = d;
paint_selected_objects();
}
}
//-------------------------------------------------------------//
void QSToolSelect::create_transform_cmd()
{
QSDrvQt *drv = new QSDrvQt();
drv->setDC(new QPainter(m_view->canvasWidget()),m_view->dpi(),true);
m_transform_cmd = new KSCmdTransformCObjects( drv );
for( int i=0; i<m_view->selection()->count(); i++ ) {
QSCObject *object = m_view->selection()->object(i);
m_transform_cmd->addObject( object );
}
}
//-------------------------------------------------------------//
int QSToolSelect::angle( const QSPt2f& rcenter, const QPoint& click_pos, const QPoint& mouse_pos )
{
double A2 = mouse_pos.y() - rcenter.y;
double B2 = rcenter.x - mouse_pos.x();
double A1 = click_pos.y() - rcenter.y;
double B1 = rcenter.x - click_pos.x();
return int( atan2(A1*B2-A2*B1,A1*A2+B1*B2)*180.0/3.141592 + 0.5 );
}
//-------------------------------------------------------------//
void QSToolSelect::paint_object( QSCObject* object )
{
object->stop();
QPainter p ( m_view->canvasWidget() );
p.setRasterOp( Qt::NotXorROP );
//QSRectf r = object->box(driver());
object->paintSkeleton( &p, driver()->dpi );
}
//-------------------------------------------------------------//
void QSToolSelect::paint_selected_objects()
{
m_view->selection()->stop();
QPainter p ( m_view->canvasWidget() );
p.setRasterOp( Qt::NotXorROP );
m_view->selection()->paintSkeleton( &p, driver()->dpi );
/*
for( int i=0; i<m_view->selection()->objectCount(); i++ ) {
paint_object( m_view->selection()->object(i) );
}
*/
}
//-------------------------------------------------------------//
void QSToolSelect::paint_frame( const QPoint& p1, const QPoint& p2 )
{
QRect r( p1, p2 );
QPainter p ( m_view->canvasWidget() );
p.setRasterOp( Qt::NotXorROP );
p.setPen( Qt::DotLine );
p.setBrush( Qt::NoBrush );
p.drawRect( r );
}
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
QSToolZoom::QSToolZoom( QObject *parent )
:QSTool( parent )
{
m_active_axes = NULL;
}
//-------------------------------------------------------------//
QSToolZoom::~QSToolZoom()
{
}
//-------------------------------------------------------------//
void QSToolZoom::activate( QSPlotView *init_view )
{
QSTool::activate( init_view );
erase_crossmark = false;
// is the selected object on the current page ?
if ( m_view->currentPage() &&
m_view->currentPage()->objects()->find( m_view->activeObject()) >= 0 )
m_active_axes = m_view->activeObject()->parentAxes();
m_view->showUserMessage(tr("Zoom tool\n"
"Click and drag over selected axes to set a new range."
"Press a right button to zoom out. "
"Double click to set a default zoom. "
"Only scrollable axes are zoomed. ") );
}
//-------------------------------------------------------------//
void QSToolZoom::deactivate()
{
if ( erase_crossmark ) draw_crossmark(p1);
m_active_axes = NULL;
QSTool::deactivate();
}
//-------------------------------------------------------------//
void QSToolZoom::draw()
{
erase_crossmark = false;
}
//-------------------------------------------------------------//
void QSToolZoom::canvasRightButtonClicked( const QPoint& pos, int )
{
bool busy = ( m_active_axes && m_active_axes->state() == QSAxes::Busy );
if ( erase_crossmark && !busy ) draw_crossmark(p1);
erase_crossmark = false;
zoom_out(pos);
}
//-------------------------------------------------------------//
bool QSToolZoom::canvasDragStart( const QPoint& pos, int )
{
if ( find_plane(pos) ) {
p1 = p2 = world_point( pos );
if ( erase_crossmark ) draw_crossmark(p1);
draw_frame();
return true;
}
return false;
}
//-------------------------------------------------------------//
void QSToolZoom::canvasDragMove( const QPoint& pos, const QPoint&, const QPoint&, int, int, int )
{
draw_frame();
p2 = world_point( pos );
draw_frame();
}
//-------------------------------------------------------------//
void QSToolZoom::canvasDragEnd( const QPoint&, const QPoint&, int, int )
{
draw_frame();
erase_crossmark = false;
if ( p1 != p2 ) zoom_in( p1, p2 );
}
//-------------------------------------------------------------//
void QSToolZoom::canvasMove( const QPoint& pos )
{
bool busy = ( m_active_axes && m_active_axes->state() == QSAxes::Busy );
if ( erase_crossmark && !busy ) draw_crossmark(p1);
erase_crossmark = false;
if ( find_plane(pos) ) {
p1=world_point(pos);
if ( !busy ) { draw_crossmark(p1); erase_crossmark = true; }
m_view->showUserMessage(message(p1));
} else {
m_view->showUserMessage( QString(tr("OUT OF AREA ")) );
}
}
//-------------------------------------------------------------//
void QSToolZoom::canvasDoubleClicked( const QPoint&, int )
{
p1 = p2 = QSPt3f( 0.0, 0.0, 0.0 );
zoom_in( p1, p2 );
}
//-------------------------------------------------------------//
QSPt3f QSToolZoom::world_point( const QPoint& click_pos )
// returns point in world coordinates
{
QSPt3f result;
QSPt2 canvas_pos( click_pos.x(), click_pos.y() );
QSAxes *axes = m_active_axes;
QSAxes3D *axes3d = dynamic_cast<QSAxes3D*>(axes);
if ( axes3d ) {
// find a straight line crossing focuspoint
// and point on the plane of the screen
QSPt3f pos1 = axes->proj()->canvas3ToWorld3D(QSPt3f(canvas_pos.x,canvas_pos.y,0.0));
QSPt3f pos2 = axes->proj()->canvas3ToWorld3D(QSPt3f(canvas_pos.x,canvas_pos.y,1.0));
// focuspoint
if ( axes3d->perspective() ) pos2 = axes3d->p3D()->eye;
// 'f' - it is a point where all three planes cross.
QSPt3f f = axes3d->proj()->furthest();
// Inf will be good for other functions to mark that the point is invalid
QSPt3f inf(-1.0,-1.0,-1.0);
if ( plane == PlaneYZ && pos1.x == pos2.x ) return inf;
if ( plane == PlaneXZ && pos1.y == pos2.y ) return inf;
if ( plane == PlaneXY && pos1.z == pos2.z ) return inf;
// Where our straightline crosses the choosen plane ?
double t = 0.0;
if ( plane == PlaneYZ ) t = ( f.x - pos1.x ) / ( pos2.x - pos1.x );
if ( plane == PlaneXZ ) t = ( f.y - pos1.y ) / ( pos2.y - pos1.y );
if ( plane == PlaneXY ) t = ( f.z - pos1.z ) / ( pos2.z - pos1.z );
// Calculate x,y,z from t
result = f;
if ( plane != PlaneYZ ) result.x = pos1.x + t * ( pos2.x - pos1.x );
if ( plane != PlaneXZ ) result.y = pos1.y + t * ( pos2.y - pos1.y );
if ( plane != PlaneXY ) result.z = pos1.z + t * ( pos2.z - pos1.z );
} else {
result = axes->proj()->canvas3ToWorld3D( QSPt3f(canvas_pos.x,canvas_pos.y,0.0) );
}
return result;
}
//-------------------------------------------------------------//
void QSToolZoom::draw_frame()
{
QSPt3f p[4];
QSPt2 cp[4];
p[0] = p1;
p[1] = (plane == PlaneXY) ? QSPt3f( p1.x, p2.y, p1.z ) : QSPt3f( p1.x, p1.y, p2.z );
p[2] = p2;
p[3] = (plane == PlaneXY) ? QSPt3f( p2.x, p1.y, p1.z ) : QSPt3f( p2.x, p2.y, p1.z );
for( int i=0; i<4; i++ ) {
QSPt3f curr_p = m_active_axes->proj()->world3DToCanvas3(p[i]);
cp[i].x = int(curr_p.x+0.5);
cp[i].y = int(curr_p.y+0.5);
}
QPainter paint( m_view->canvasWidget() );
paint.setRasterOp( Qt::NotXorROP ); paint.setPen( Qt::DotLine );
for ( int i=0; i<4; i++ ) paint.drawLine(cp[i].x,cp[i].y,cp[(i+1)%4].x,cp[(i+1)%4].y);
draw_crossmark( p1 );
draw_crossmark( p2 );
QString info = QString(tr("From: \n"))+message(p1)+QString("\n")+QString(tr("To: \n"))+message(p2);
m_view->showUserMessage(info);
}
//-------------------------------------------------------------//
void QSToolZoom::zoom_out( const QPoint& click_pos )
{
if ( find_plane(click_pos) ) {
QSPt3f pos = world_point( click_pos );
QSPt3f min( pos.x-1.0, pos.y-1.0, pos.z-1.0);
QSPt3f max( pos.x+1.0, pos.y+1.0, pos.z+1.0);
zoom_in(min,max);
}
}
//-------------------------------------------------------------//
void QSToolZoom::zoom_in( const QSPt3f& p1, const QSPt3f& p2 )
// zoom all axes
{
QSAxes *axes = m_active_axes;
if ( axes ) {
KSCmdSetRanges *cmd = new KSCmdSetRanges( axes );
for( int axis_nr=0; axis_nr<axes->axisCount(); axis_nr++ ) {
QSAxis *axis = axes->axis(axis_nr);
if ( axis->scrollable() ) {
if ( plane != PlaneYZ && axis->type() == QSAxis::XAxisType ) axis->setRange( axis->worldToData(p1.x), axis->worldToData(p2.x) );
if ( plane != PlaneXZ && axis->type() == QSAxis::YAxisType ) axis->setRange( axis->worldToData(p1.y), axis->worldToData(p2.y) );
if ( plane != PlaneXY && axis->type() == QSAxis::ZAxisType ) axis->setRange( axis->worldToData(p1.z), axis->worldToData(p2.z) );
}
}
cmd->commit();
KSWorkbook *workbook = dynamic_cast<KSWorkbook*>(m_view->workbook());
workbook->execute( cmd );
}
}
//-------------------------------------------------------------//
bool QSToolZoom::find_plane( const QPoint& canvas_pos )
// sets an internal variable plane;
{
if ( m_active_axes )
if (dynamic_cast<QSAxes3D*>(m_active_axes)) {
for( int i=0; i<3; i++ ) {
plane = (Plane )i;
QSPt3f pos = world_point( canvas_pos );
switch( plane ) {
case PlaneXY: if ( pos.x>0.0 && pos.x<1.0 && pos.y>0.0 && pos.y<1.0 ) return true;
case PlaneXZ: if ( pos.x>0.0 && pos.x<1.0 && pos.z>0.0 && pos.z<1.0 ) return true;
case PlaneYZ: if ( pos.y>0.0 && pos.y<1.0 && pos.z>0.0 && pos.z<1.0 ) return true;
}
}
} else {
QSPt3f pos = world_point( canvas_pos );
plane = PlaneXY;
//if ( pos.x>0.0 && pos.x<1.0 && pos.y>0.0 && pos.y<1.0 ) return true;
//return false;
return true;
}
return false;
}
//-------------------------------------------------------------//
void QSToolZoom::draw_crossmark( const QSPt3f& pos )
{
if ( !m_active_axes ) return;
QSPt3f f;
QSPt3f min[2];
QSPt3f max[2];
min[0] = min[1] = QSPt3f(-0.02,-0.02,-0.02);
max[0] = max[1] = QSPt3f( 1.02, 1.02, 1.02);
QSAxes3D *axes3d = dynamic_cast<QSAxes3D*>(m_active_axes);
if ( axes3d ) f=axes3d->proj()->furthest();
switch( plane ) {
case PlaneXY: min[0].z=min[1].z=max[0].z=max[1].z=f.z;
min[0].x=max[0].x=pos.x;
min[1].y=max[1].y=pos.y;
break;
case PlaneXZ: min[0].y=min[1].y=max[0].y=max[1].y=f.y;
min[0].x=max[0].x=pos.x;
min[1].z=max[1].z=pos.z;
break;
case PlaneYZ: min[0].x=min[1].x=max[0].x=max[1].x=f.x;
min[0].y=max[0].y=pos.y;
min[1].z=max[1].z=pos.z;
break;
}
QSPt3f p[2];
QSPt3f q[2];
p[0] = m_active_axes->proj()->world3DToCanvas3(min[0]);
p[1] = m_active_axes->proj()->world3DToCanvas3(min[1]);
q[0] = m_active_axes->proj()->world3DToCanvas3(max[0]);
q[1] = m_active_axes->proj()->world3DToCanvas3(max[1]);
QPainter paint( m_view->canvasWidget() );
paint.setRasterOp( Qt::NotXorROP ); paint.setPen( Qt::SolidLine );
paint.drawLine( int(p[0].x+0.5), int(p[0].y+0.5), int(q[0].x+0.5), int(q[0].y+0.5) );
paint.drawLine( int(p[1].x+0.5), int(p[1].y+0.5), int(q[1].x+0.5), int(q[1].y+0.5) );
}
//-------------------------------------------------------------//
QString QSToolZoom::message( const QSPt3f& p )
{
QString message;
//int x_nr = 1;
//int y_nr = 1;
//int z_nr = 1;
QSAxes *axes = m_active_axes;
if ( axes )
for ( int axis_nr=0; axis_nr<axes->axisCount(); axis_nr++ ) {
QSAxis *axis = axes->axis(axis_nr);
if ( axis->scrollable() )
switch(axis->type()) {
case QSAxis::XAxisType: message += QString("X \"")+axis->title()+QString("\" = ")+QString::number(axis->worldToData(p.x))+QString("\n");break;
case QSAxis::YAxisType: message += QString("Y \"")+axis->title()+QString("\" = ")+QString::number(axis->worldToData(p.y))+QString("\n");break;
case QSAxis::ZAxisType: message += QString("Z \"")+axis->title()+QString("\" = ")+QString::number(axis->worldToData(p.z))+QString("\n");break;
default: break;
}
}
return message;
}
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
QSToolArrow::QSToolArrow( QObject *parent )
:QSTool( parent )
{
m_new_object = NULL;
}
//-------------------------------------------------------------//
QSToolArrow::~QSToolArrow()
{
}
//-------------------------------------------------------------//
void QSToolArrow::activate( QSPlotView *init_view )
{
QSTool::activate( init_view );
m_view->showUserMessage(tr("Arrow tool. \n"
"If the axis object is currently selected the "
"newly created arrow will became its child object "
"and can be positioned relative to its parents position. ") );
m_view->canvasWidget()->setCursor( crossCursor );
}
//-------------------------------------------------------------//
void QSToolArrow::deactivate()
{
QSTool::deactivate();
delete m_new_object; m_new_object = NULL;
}
//-------------------------------------------------------------//
void QSToolArrow::draw()
{
}
//-------------------------------------------------------------//
bool QSToolArrow::canvasDragStart( const QPoint& pos, int keyState )
{
m_new_object = new QSCArrow();
m_new_object->setBox( QSRectf( snapToGrid(pos,keyState), snapToGrid(pos,keyState) ), driver() );
paint_object();
return true;
}
//-------------------------------------------------------------//
void QSToolArrow::canvasDragMove( const QPoint& pos, const QPoint&, const QPoint& startPos, int keyState, int, int startKeyState )
{
paint_object();
m_new_object->setBox( QSRectf( snapToGrid(pos,keyState), snapToGrid(startPos,startKeyState), false ), driver() );
paint_object();
}
//-------------------------------------------------------------//
void QSToolArrow::canvasDragEnd( const QPoint& pos, const QPoint& startPos, int keyState, int startKeyState )
{
paint_object();
if ( snapToGrid(pos,keyState) != snapToGrid(startPos,startKeyState) ) {
if ( dynamic_cast<KSWorkbook*>(m_view->workbook())->execute( new KSCmdAddCObject(m_new_object,m_view->activeCollection()) ) ) {
m_new_object->setBox( QSRectf( snapToGrid(pos,keyState), snapToGrid(startPos,startKeyState), false ), driver() );
}
} else {
delete m_new_object;
}
m_new_object = NULL;
}
//-------------------------------------------------------------//
void QSToolArrow::paint_object()
{
QPainter p ( m_view->canvasWidget() );
p.setRasterOp( Qt::NotXorROP );
m_new_object->paint( &p, driver()->dpi );
}
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
QSToolRect::QSToolRect( QObject *parent )
:QSTool( parent )
{
}
//-------------------------------------------------------------//
QSToolRect::~QSToolRect()
{
}
//-------------------------------------------------------------//
void QSToolRect::activate( QSPlotView *init_view )
{
QSTool::activate( init_view );
m_view->showUserMessage(tr("Rectangle/Ellipse tool."
"If the axis object is currently selected the "
"newly created rectangle will became its child object "
"and can be positioned relative to its parents position. "));
m_view->canvasWidget()->setCursor( crossCursor );
m_new_object = NULL;
}
//-------------------------------------------------------------//
void QSToolRect::deactivate()
{
delete m_new_object; m_new_object = NULL;
QSTool::deactivate();
}
//-------------------------------------------------------------//
void QSToolRect::draw()
{
}
//-------------------------------------------------------------//
bool QSToolRect::canvasDragStart( const QPoint& pos, int keyState )
{
m_new_object = new QSCRect();
m_new_object->setBox( QSRectf(snapToGrid(pos,keyState),QSPt2f()), driver() );
paint_object();
return true;
}
//-------------------------------------------------------------//
void QSToolRect::canvasDragMove( const QPoint& pos, const QPoint&, const QPoint& startPos, int keyState, int, int startKeyState )
{
paint_object();
m_new_object->setBox( QSRectf(snapToGrid(pos,keyState),snapToGrid(startPos,startKeyState),false), driver() );
paint_object();
}
//-------------------------------------------------------------//
void QSToolRect::canvasDragEnd( const QPoint& pos, const QPoint& startPos, int keyState, int startKeyState )
{
paint_object();
if ( snapToGrid(pos,keyState) != snapToGrid(startPos,startKeyState) ) {
if ( dynamic_cast<KSWorkbook*>(m_view->workbook())->execute( new KSCmdAddCObject(m_new_object,m_view->activeCollection()) ) ) {
m_new_object->setBox( QSRectf(snapToGrid(pos,keyState),snapToGrid(startPos,startKeyState),false), driver() );
}
} else {
delete m_new_object;
}
m_new_object = NULL;
}
//-------------------------------------------------------------//
void QSToolRect::paint_object()
{
QPainter p ( m_view->canvasWidget() );
p.setRasterOp( Qt::NotXorROP );
if ( m_new_object ) m_new_object->paintSkeleton( &p, driver()->dpi );
}
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
QSToolLocate::QSToolLocate( QObject *parent )
:QSTool( parent )
{
m_info = QString();
m_popup = NULL;
}
//-------------------------------------------------------------//
QSToolLocate::~QSToolLocate()
{
}
//-------------------------------------------------------------//
void QSToolLocate::activate( QSPlotView *init_view )
{
QSTool::activate( init_view );
m_view->showUserMessage(tr("Locate tool\n"
"Click on a datapoint inside the active axes.."));
draw();
}
//-------------------------------------------------------------//
void QSToolLocate::deactivate()
{
draw_info( false );
QSTool::deactivate();
}
//-------------------------------------------------------------//
void QSToolLocate::draw()
{
m_info = QString();
delete m_popup; m_popup = NULL;
}
//-------------------------------------------------------------//
void QSToolLocate::canvasMove( const QPoint& pos )
{
if ( m_view->currentPage() &&
m_view->activeAxes() &&
m_view->activeAxes()->shadowObject()->box(driver()).contains(QSPt2f(pos.x(),pos.y())) &&
m_view->currentPage()->objects()->find(m_view->activeAxes()->shadowObject()) >= 0 ) {
m_view->canvasWidget()->setCursor( pointingHandCursor );
} else {
m_view->canvasWidget()->setCursor( arrowCursor );
}
}
//-------------------------------------------------------------//
void QSToolLocate::canvasClicked( const QPoint& pos, int )
{
draw_info( false );
if ( m_view->activeAxes() ) {
QSPt2f p( pos.x(), pos.y() );
if ( (m_info=m_view->activeAxes()->posInfo(p)) == QString::null ) m_info = QString("?");
m_pos = QPoint( int(p.x+0.5), int(p.y+0.5) );
draw_info();
m_view->showUserMessage( m_info );
}
}
//-------------------------------------------------------------//
void QSToolLocate::canvasRightButtonClicked( const QPoint&, int )
{
}
//-------------------------------------------------------------//
void QSToolLocate::draw_info( bool show_popup )
{
if ( m_popup ) { delete m_popup; m_popup = NULL; }
if ( m_info.isEmpty() || m_view->activeAxes()->state() != QSAxes::Waiting ) return;
QWidget *canvas = m_view->canvasWidget();
QPainter p ( canvas ) ;
p.setRasterOp( Qt::NotXorROP );
p.drawLine( 0, m_pos.y(), canvas->width()-1, m_pos.y() );
p.drawLine( m_pos.x(), 0, m_pos.x(), canvas->height()-1 );
p.end();
if ( show_popup ) {
m_popup = new QLabel( m_info, canvas, "locate", Qt::WStyle_Customize | Qt::WType_Popup );
m_popup->setFrameStyle( QFrame::Panel | QFrame::Raised );
m_popup->setCursor( pointingHandCursor );
m_popup->move( canvas->mapToGlobal(QPoint(m_pos.x()+20,m_pos.y()+20)) );
m_popup->show();
}
}