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.

1446 lines
44 KiB

/***************************************************************************
qsplotview.cpp
-------------------
begin : 01-January-2000
copyright : (C) 2000 by Kamil Dobkowski
email : kamildobk@poczta.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"qsplotview.h"
#include"qsdrvqt.h"
#include"qsdrvhittest.h"
#include"qsruler.h"
#include<qpixmap.h>
#include<qtimer.h>
#include<qapplication.h>
#include<qcursor.h>
#include<qpaintdevicemetrics.h>
#include<qprinter.h>
#include<qslider.h>
#include<qscrollbar.h>
#include<qevent.h>
#include<qlayout.h>
#include<qpushbutton.h>
#include<qpopupmenu.h>
#include<qtabbar.h>
#include<qinputdialog.h>
#include<qmessagebox.h>
#include<math.h>
#ifdef Q_WS_X11
#include<X11/X.h>
#include<X11/Xlib.h>
#endif
#define SHADOW_WIDTH 3
#define SLIDER_WIDTH 16
#define SCROLLBAR_RANGE 10000.0
//-------------------------------------------------------------//
QSSelection::QSSelection( QObject *parent )
: QSCObjectCollection( parent, false )
{
m_workbook = NULL;
m_curr_collection = NULL;
m_root_collection = NULL;
}
//-------------------------------------------------------------//
QSSelection::~QSSelection()
{
}
//-------------------------------------------------------------//
void QSSelection::set( QSCObject *object )
{
parametersChanging();
bool temp = tempAutoUpdatesOff();
clear();
if ( object ) {
set_collection( object );
add( object );
}
tempAutoUpdatesRestore( temp );
listChanged();
parametersChanged();
}
//-------------------------------------------------------------//
void QSSelection::turn( QSCObject *objectToSwitch )
{
if ( find(objectToSwitch) < 0 ) add( objectToSwitch ); else remove( find(objectToSwitch) );
}
//-------------------------------------------------------------//
void QSSelection::insert( int position, QSCObject *objectToAdd )
{
if ( objectToAdd && find(objectToAdd) < 0 ) {
if ( !collection() || collection()->find(objectToAdd) < 0 ) clear();
set_collection( objectToAdd );
QSCObjectCollection::insert( position, objectToAdd );
}
}
//-------------------------------------------------------------//
void QSSelection::remove( int index )
{
if ( object(index) ) {
QSCObjectCollection::remove( index );
if ( isEmpty() ) set_collection( NULL );
}
}
//-------------------------------------------------------------//
void QSSelection::set_collection( QSCObject *object )
{
if ( m_root_collection ) {
disconnect( m_root_collection, SIGNAL(sigRemoved(QSCObjectCollection*,QSCObject*)), this, SLOT(slot_object_removed(QSCObjectCollection*,QSCObject*)) );
}
if ( object ) {
m_curr_collection = object->collection();
m_root_collection = object->rootCollection();
} else {
m_curr_collection = NULL;
m_root_collection = NULL;
}
if ( m_root_collection ) {
connect( m_root_collection, SIGNAL(sigRemoved(QSCObjectCollection*,QSCObject*)), this, SLOT(slot_object_removed(QSCObjectCollection*,QSCObject*)) );
}
}
//-------------------------------------------------------------//
void QSSelection::set_workbook( QSWorkbook *workbook )
{
if ( m_workbook != workbook ) {
if ( !isEmpty() ) clear();
if ( m_workbook ) disconnect( m_workbook, SIGNAL(sigPageRemoved(QSPage*)), this, SLOT(slot_page_removed(QSPage*)) );
m_workbook = workbook;
if ( m_workbook ) connect( m_workbook, SIGNAL(sigPageRemoved(QSPage*)), this, SLOT(slot_page_removed(QSPage*)) );
}
}
//-------------------------------------------------------------//
void QSSelection::slot_object_removed( QSCObjectCollection*, QSCObject *removedObject )
{
if ( find(removedObject) >= 0 ) remove( find(removedObject) );
else
if ( dynamic_cast<QSCGroup*>(removedObject) ) {
// if group contains a one object from our selection
// it must contain all objects so we must clear the whole selection !
if ( ((QSCGroup*)(removedObject))->objects()->contains(object(0),true) ) clear();
}
}
//-------------------------------------------------------------//
void QSSelection::slot_page_removed( QSPage *page )
{
if ( page->objects() == m_root_collection ) clear();
}
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
QSRangeScrollBar::QSRangeScrollBar( QWidget *parent )
: QScrollBar( 0, 0, 1, 1, 0, QScrollBar::Horizontal, parent )
{
m_axes = NULL;
m_type = QSAxis::UnknownAxisType;
m_updating_range = false;
m_updating_scrollbars = false;
m_min = 0.0;
m_max = 1.0;
}
//-------------------------------------------------------------//
QSRangeScrollBar::~QSRangeScrollBar()
{
}
//-------------------------------------------------------------//
void QSRangeScrollBar::setAxes( QSAxes *axes, QSAxis::AxisType type )
{
m_axes = axes;
m_type = type;
updateScrollbar();
}
//-------------------------------------------------------------//
void QSRangeScrollBar::updateScrollbar()
{
if ( m_updating_range ) return;
m_updating_scrollbars = true;
// calculate data range on joined axes in world coordinates
double min = 0.0;
double max = 1.0;
if ( m_axes && m_type != QSAxis::UnknownAxisType )
for( int axis_nr=0; axis_nr<m_axes->axisCount(); axis_nr++ ) {
QSAxis *curr_axis = m_axes->axis(axis_nr);
if ( curr_axis->type() == m_type && curr_axis->scrollable() ) {
double data_min = curr_axis->dataToWorld(curr_axis->min(QSAxis::RangeData));
double data_max = curr_axis->dataToWorld(curr_axis->max(QSAxis::RangeData));
// Not always data_min < data_max, if axis range is reversed it will be reversed
// so real data_min is QMIN( data_min, data_max )
min = QMIN( min, QMIN( data_min, data_max ) );
max = QMAX( max, QMAX( data_min, data_max ) );
}
}
// remember for further processing
m_min = min;
m_max = max;
// visible range in normalized world coordinates ( v_max = v_min + 1.0 )
double v_min = 0.0 - min;
// range on axes in normalized coordinates
max = max - min;
min = min - min; // always 0
// Map to integer: < 0.0, max > -> < 0, SCROLLBAR_RANGE >
// slider range <0,max-1.0>
setRange( 0, int( (max-1.0)/max*SCROLLBAR_RANGE+0.5 ) );
// page_step = 1.0
setPageStep( int( 1.0/max*SCROLLBAR_RANGE+0.5 ) );
// value = v_min
int curr_value = int( v_min/max*SCROLLBAR_RANGE+0.5 );
// reverse direction of the vertical scrollbar
setValue( orientation() == Horizontal ? curr_value : maxValue() - curr_value );
m_updating_scrollbars = false;
}
//-------------------------------------------------------------//
void QSRangeScrollBar::updateRange()
{
if ( m_updating_scrollbars ) return;
m_updating_range = true;
// reverse direction of the vertical scrollbar
int curr_value = orientation() == Horizontal ? value() : maxValue() - value();
// Map to float: < 0, SCROLLBAR_RANGE > -> < m_min, m_max >
double v_min = double(curr_value)/SCROLLBAR_RANGE*(m_max-m_min)+m_min;
double v_max = v_min + 1.0;
// some round-offs
if ( curr_value == maxValue() ) {
v_min = m_max-1.0;
v_max = m_max;
}
if ( m_axes && m_type != QSAxis::UnknownAxisType )
for( int axis_nr=0; axis_nr<m_axes->axisCount(); axis_nr++ ) {
QSAxis *curr_axis = m_axes->axis(axis_nr);
if ( curr_axis->type() == m_type && curr_axis->scrollable() ) {
curr_axis->setRange( curr_axis->worldToData(v_min),
curr_axis->worldToData(v_max) );
}
}
m_updating_range = false;
}
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
QSPlotView::QSPlotView(QWidget *parent, const char *name )
: QWidget(parent,name)
{
m_refresh_buffer = new QTimer( this );
connect( m_refresh_buffer, SIGNAL(timeout()), this, SLOT(slot_refresh_screen_buffer()) );
m_screen_buffer_valid = false;
m_screen_buffer = NULL;
m_workbook = NULL;
m_curr_page = NULL;
m_active_axes = NULL;
m_ctool = NULL;
m_active_object = NULL;
m_tool_activated = false;
m_tool_activating = false;
m_screen_buffering = true;
m_selection = new QSSelection( this );
connect( m_selection, SIGNAL(sigListChanged()), this, SLOT(slot_selection_changed()) );
m_page_margins_visible = true;
m_full_page = true;
m_zoom = 1.0;
m_grid_visible = true;
m_grid_x = 5;
m_grid_y = 5;
m_rulers_visible = true;
m_hruler = NULL;
m_vruler = NULL;
m_sliders_visible = true;
m_hscrollbar = NULL;
m_vscrollbar = NULL;
m_page_list = NULL;
for( int i=0; i<3; i++ ) {
m_sliders[i].slider = NULL;
m_sliders[i].min = 0;
m_sliders[i].max = 0;
}
for( int i=0; i<2; i++ ) {
m_scrollbars[i].scrollbar = NULL;
m_scrollbars[i].type = QSAxis::UnknownAxisType;
}
m_interior = new QWidget( this );
m_interior->installEventFilter(this);
m_interior->setBackgroundColor( lightGray );
m_interior->show();
m_shadow = new QWidget( m_interior );
m_shadow->setBackgroundColor( black );
m_canvas = new QWidget( m_interior );
m_canvas->installEventFilter(this);
m_canvas->setBackgroundMode( NoBackground );
m_layout = new QGridLayout( this, 5, 6 );
// scaled main view
m_layout->setRowStretch( 1, 10 );
m_layout->setColStretch( 1, 0 );
m_layout->setColStretch( 2, 10 );
m_layout->addMultiCellWidget( m_interior, 1, 1, 1, 2 );
m_layout->setColStretch( 0, 0 );
m_layout->setColStretch( 3, 0 );
m_layout->setColStretch( 4, 0 );
m_layout->setColStretch( 5, 0 );
m_layout->setColStretch( 6, 0 );
m_layout->setRowStretch( 0, 0 );
m_layout->setRowStretch( 2, 0 );
m_layout->setRowStretch( 3, 0 );
m_layout->setRowStretch( 4, 0 );
m_pages.setAutoDelete( FALSE );
recreate_gui();
}
//-------------------------------------------------------------//
QSPlotView::~QSPlotView()
{
if ( m_curr_page && m_curr_page->objects()->busy() ) m_curr_page->objects()->stop();
delete m_ctool;
delete m_screen_buffer;
}
//-------------------------------------------------------------//
void QSPlotView::setPixmapBuffering( bool enabled )
{
m_screen_buffering = enabled;
set_backstore_internal( !m_screen_buffering );
}
//-------------------------------------------------------------//
void QSPlotView::slot_selection_changed()
{
if ( selection()->count() == 1 ) {
set_active_object( selection()->object(0), true );
} else {
set_active_object( NULL, true );
}
}
//-------------------------------------------------------------//
void QSPlotView::recreate_gui()
{
recreate_rulers();
recreate_parameter_scrollbars();
recreate_parameter_sliders();
recreate_canvas_scrollbars();
recreate_page_bar();
adjust_canvas_size();
}
//-------------------------------------------------------------//
void QSPlotView::recreate_rulers()
{
if ( m_full_page && m_rulers_visible ) {
if ( !m_hruler ) m_hruler = new QSRuler( QSRuler::Horizontal, this );
if ( !m_vruler ) m_vruler = new QSRuler( QSRuler::Vertical, this );
m_hruler->setUnit( UnitMillimeter );
m_vruler->setUnit( UnitMillimeter );
m_layout->addMultiCellWidget( m_hruler, 0, 0, 1, 2 );
m_layout->addWidget( m_vruler, 1, 0 );
m_hruler->show();
m_vruler->show();
} else {
delete m_hruler;
delete m_vruler;
m_hruler = NULL;
m_vruler = NULL;
}
update_rulers();
}
//-------------------------------------------------------------//
void QSPlotView::update_rulers()
// sync rulers with canvas position
{
if ( m_hruler && m_vruler ) {
m_hruler->setZoom( (float )m_zoom );
m_vruler->setZoom( (float )m_zoom );
m_hruler->updateVisibleArea( -m_canvas->x(), -m_canvas->y() );
m_vruler->updateVisibleArea( -m_canvas->x(), -m_canvas->y() );
}
}
//-------------------------------------------------------------//
void QSPlotView::recreate_parameter_scrollbars()
{
// create only sliders which have type != UnknownAxiType
for( int i=0; i<2; i++ ) {
if ( m_active_axes && m_sliders_visible && !m_full_page && m_scrollbars[i].type != QSAxis::UnknownAxisType ) {
if ( !m_scrollbars[i].scrollbar ) m_scrollbars[i].scrollbar = new QSRangeScrollBar( this );
connect( m_scrollbars[i].scrollbar, SIGNAL(sliderPressed() ), this, SLOT(slot_scrollbar_pressed() ) );
connect( m_scrollbars[i].scrollbar, SIGNAL(sliderReleased()), this, SLOT(slot_scrollbar_released()) );
} else {
delete m_scrollbars[i].scrollbar; m_scrollbars[i].scrollbar = NULL;
}
}
if ( m_scrollbars[0].scrollbar ) {
connect( m_scrollbars[0].scrollbar, SIGNAL(valueChanged(int)), this, SLOT(slot_hscrollbar_moved(int)) );
m_scrollbars[0].scrollbar->setOrientation( QScrollBar::Horizontal );
m_layout->addMultiCellWidget( m_scrollbars[0].scrollbar, 3, 3, 1, 2 );
m_scrollbars[0].scrollbar->show();
}
if ( m_scrollbars[1].scrollbar ) {
connect( m_scrollbars[1].scrollbar, SIGNAL(valueChanged(int)), this, SLOT(slot_vscrollbar_moved(int)) );
m_scrollbars[1].scrollbar->setOrientation( QScrollBar::Vertical );
m_layout->addWidget( m_scrollbars[1].scrollbar, 1, 4 );
m_scrollbars[1].scrollbar->show();
}
update_parameter_scrollbars();
}
//-------------------------------------------------------------//
void QSPlotView::update_parameter_scrollbars()
// sync scrollbars with parameter value
{
if ( m_scrollbars[0].scrollbar ) m_scrollbars[0].scrollbar->setAxes( m_active_axes, m_scrollbars[0].type );
if ( m_scrollbars[1].scrollbar ) m_scrollbars[1].scrollbar->setAxes( m_active_axes, m_scrollbars[1].type );
}
//-------------------------------------------------------------//
void QSPlotView::slot_hscrollbar_moved( int )
{
m_scrollbars[0].scrollbar->updateRange();
}
//-------------------------------------------------------------//
void QSPlotView::slot_vscrollbar_moved( int )
{
m_scrollbars[1].scrollbar->updateRange();
}
//-------------------------------------------------------------//
void QSPlotView::recreate_parameter_sliders()
{
for( int i=0; i<3; i++ ) {
// create only sliders with non empty property value and with min != max
if ( !m_full_page && m_sliders_visible && !m_sliders[i].property.isEmpty() && m_sliders[i].min != m_sliders[i].max ) {
if ( m_sliders[i].slider == NULL ) {
m_sliders[i].slider = new QSlider( m_sliders[i].min, m_sliders[i].max, 1, m_sliders[i].min, QSlider::Vertical, this );
connect( m_sliders[i].slider, SIGNAL(sliderPressed() ), this, SLOT(slot_slider_pressed() ) );
connect( m_sliders[i].slider, SIGNAL(sliderReleased()), this, SLOT(slot_slider_released()) );
}
} else {
delete m_sliders[i].slider; m_sliders[i].slider = NULL;
}
}
// Horizontal slider
if ( m_sliders[0].slider ) {
connect( m_sliders[0].slider, SIGNAL(valueChanged(int)), this, SLOT(slot_hslider_moved(int)) );
m_sliders[0].slider->setFixedHeight( SLIDER_WIDTH );
m_sliders[0].slider->setOrientation( QSlider::Horizontal );
m_layout->addMultiCellWidget( m_sliders[0].slider, 4, 4, 1, 2 );
m_sliders[0].slider->show();
}
// Vertical slider
if ( m_sliders[1].slider ) {
connect( m_sliders[1].slider, SIGNAL(valueChanged(int)), this, SLOT(slot_vslider_moved(int)) );
m_sliders[1].slider->setFixedWidth( SLIDER_WIDTH );
m_layout->addWidget(m_sliders[1].slider, 1, 5 );
m_sliders[1].slider->show();
}
// Additional slider
if ( m_sliders[2].slider ) {
connect( m_sliders[2].slider, SIGNAL(valueChanged(int)), this, SLOT(slot_aslider_moved(int)) );
m_sliders[2].slider->setFixedWidth( SLIDER_WIDTH );
m_layout->addWidget(m_sliders[2].slider, 1, 6 );
m_sliders[2].slider->show();
}
update_parameter_sliders();
}
//-------------------------------------------------------------//
void QSPlotView::update_parameter_sliders()
// sync sliders with parameter values
{
if ( m_active_axes ) for ( int i=0; i<3; i++ )
if ( m_sliders[i].slider ) m_sliders[i].slider->setValue( m_active_axes->property( m_sliders[i].property ).toInt() );
}
//-------------------------------------------------------------//
void QSPlotView::slot_hslider_moved( int value )
{
if ( m_active_axes ) m_active_axes->setProperty( m_sliders[HorizontalSlider].property, value );
}
//-------------------------------------------------------------//
void QSPlotView::slot_vslider_moved( int value )
{
if ( m_active_axes ) m_active_axes->setProperty( m_sliders[VerticalSlider].property, value );
}
//-------------------------------------------------------------//
void QSPlotView::slot_aslider_moved( int value )
{
if ( m_active_axes ) m_active_axes->setProperty( m_sliders[AdditionalSlider].property, value );
}
//-------------------------------------------------------------//
void QSPlotView::slot_slider_pressed()
{
if ( m_active_axes ) m_active_axes->setAxesOnly( true );
emit sigSliderPressed();
}
//-------------------------------------------------------------//
void QSPlotView::slot_slider_released()
{
if ( m_active_axes ) m_active_axes->setAxesOnly( false );
emit sigSliderReleased();
}
//-------------------------------------------------------------//
void QSPlotView::slot_scrollbar_pressed()
{
emit sigScrollBarPressed();
}
//-------------------------------------------------------------//
void QSPlotView::slot_scrollbar_released()
{
emit sigScrollBarReleased();
}
//-------------------------------------------------------------//
void QSPlotView::recreate_canvas_scrollbars()
{
if ( m_full_page ) {
// horizontal scrollbar
if ( !m_hscrollbar ) m_hscrollbar = new QScrollBar( 0, 0, 1, 1, 0, QScrollBar::Horizontal, this );
m_hscrollbar->setOrientation( QScrollBar::Horizontal );
connect( m_hscrollbar, SIGNAL(valueChanged(int)), this, SLOT(slot_update_canvas_pos(int)) );
m_layout->addWidget(m_hscrollbar, 2, 2 );
m_hscrollbar->show();
// veritcal slider
if ( !m_vscrollbar ) m_vscrollbar = new QScrollBar( 0, 0, 1, 1, 0, QScrollBar::Vertical, this );
m_vscrollbar->setOrientation( QScrollBar::Vertical );
connect( m_vscrollbar, SIGNAL(valueChanged(int)), this, SLOT(slot_update_canvas_pos(int)) );
m_layout->addWidget(m_vscrollbar, 1, 3 );
m_vscrollbar->show();
} else {
delete m_hscrollbar; m_hscrollbar = NULL;
delete m_vscrollbar; m_vscrollbar = NULL;
}
update_canvas_scrollbars();
}
//-------------------------------------------------------------//
void QSPlotView::update_canvas_scrollbars()
// sync scrollbars with cancvas pos/size value
{
if ( m_hscrollbar ) {
m_hscrollbar->setRange( -5, QMAX(-5,m_canvas->width()-m_interior->width()+5) );
m_hscrollbar->setPageStep( m_interior->width() );
m_hscrollbar->setValue( -m_canvas->x() );
}
if ( m_vscrollbar ) {
m_vscrollbar->setRange( -5, QMAX(-5,m_canvas->height()-m_interior->height()+5) );
m_vscrollbar->setPageStep( m_interior->height() );
m_vscrollbar->setValue( -m_canvas->y() );
}
}
//-------------------------------------------------------------//
void QSPlotView::slot_update_canvas_pos(int)
// sync canvas pos with sliders
{
if ( m_full_page && m_hscrollbar && m_vscrollbar ) {
if ( m_canvas->width() <= m_interior->width() ) m_canvas->move( (m_interior->width()-m_canvas->width())/2,m_canvas->y() );
else m_canvas->move( -m_hscrollbar->value(),m_canvas->y() );
if ( m_canvas->height() <= m_interior->height() ) m_canvas->move( m_canvas->x(), (m_interior->height()-m_canvas->height())/2 );
else m_canvas->move( m_canvas->x(), -m_vscrollbar->value() );
m_shadow->move( m_canvas->x()+SHADOW_WIDTH, m_canvas->y()+SHADOW_WIDTH );
update_rulers();
}
}
//-------------------------------------------------------------//
void QSPlotView::recreate_page_bar()
{
// create page list
if ( m_full_page ) {
if ( !m_page_list ) {
m_page_list = new QTabBar( this );
m_layout->addWidget( m_page_list, 2, 1 );
m_page_list->setShape( QTabBar::TriangularBelow );
m_page_list->setFixedWidth( 20 );
m_page_list->setFixedHeight( 10 );
m_page_list->installEventFilter( this ); // show page management popup
m_page_list->show();
connect( m_page_list, SIGNAL(selected(int)), this, SLOT(slot_page_tab_selected(int)) );
}
// remove all tabs
QIntDictIterator<QSPage> it( m_pages );
while ( it.current() ) {
QTab *curr_tab = m_page_list->tab( it.currentKey() );
m_page_list->removeTab( curr_tab );
// should I or shouldn't I
//delete curr_tab;
++it;
}
m_pages.clear();
if ( m_workbook ) {
for( int page_nr=0; page_nr<m_workbook->pageCount(); page_nr++ ) {
int id = m_page_list->insertTab( new QTab(m_workbook->page(page_nr)->title()), page_nr );
m_pages.insert( id, m_workbook->page(page_nr) );
}
m_page_list->layoutTabs();
m_page_list->setFixedWidth( QMAX(20,QMIN(m_page_list->sizeHint().width(),250)) );
m_page_list->setFixedHeight( QMAX(10,m_page_list->sizeHint().height()) );
}
} else {
delete m_page_list; m_page_list = NULL; m_pages.clear();
}
update_page_bar();
}
//-------------------------------------------------------------//
void QSPlotView::update_page_bar()
// sync page_bar with current page value
{
QIntDictIterator<QSPage> it( m_pages );
while ( it.current() ) {
if ( it.current() == currentPage() ) {
if ( m_page_list ) m_page_list->setCurrentTab( it.currentKey() );
return;
}
++it;
}
if ( m_page_list ) m_page_list->setCurrentTab( -1 );
}
//-------------------------------------------------------------//
void QSPlotView::slot_page_tab_selected( int tab_id )
{
if ( m_workbook ) setCurrentPage( m_workbook->pageFind(m_pages[tab_id]) );
}
//-------------------------------------------------------------//
void QSPlotView::updateCanvas()
{
m_screen_buffer_valid = false;
m_canvas->update();
}
//-------------------------------------------------------------//
void QSPlotView::adjust_canvas_size()
// resize page to the correct size
{
int width;
int height;
if ( m_full_page ) {
// default values
int w_mm = 210;
int h_mm = 297;
// take page size from workbook
if ( m_workbook && m_workbook->printer() ) {
QPaintDeviceMetrics pdm(m_workbook->printer());
w_mm = pdm.widthMM();
h_mm = pdm.heightMM();
}
QRect r( 0, 0,
int(QSCoord::mmToPixels(w_mm,dpi())+0.5),
int(QSCoord::mmToPixels(h_mm,dpi())+0.5) );
width = r.width();
height = r.height();
} else {
// single view mode
width = m_interior->width();
height = m_interior->height();
}
m_canvas->resize( width, height );
if ( m_canvas->width() <= m_interior->width() ) {
int x = ( m_interior->width() - m_canvas->width() ) / 2;
int y = m_canvas->y();
m_canvas->move( x, y );
} else {
if ( m_canvas->x() > 0 ) m_canvas->move( 0, m_canvas->y() );
}
if ( m_canvas->height() <= m_interior->height() ) {
int x = m_canvas->x();
int y = ( m_interior->height() - m_canvas->height() ) / 2;
m_canvas->move( x, y );
} else {
if ( m_canvas->y() > 0 ) m_canvas->move( m_canvas->x(), 0 );
}
m_shadow->move( m_canvas->x()+SHADOW_WIDTH,
m_canvas->y()+SHADOW_WIDTH );
m_shadow->resize( m_canvas->size() );
// invalidate pixmap buffer
if ( m_screen_buffer &&
( m_screen_buffer->width() != m_canvas->width() ||
m_screen_buffer->height() != m_canvas->height() ) ) m_screen_buffer_valid = false;
// hide pages if there is no current page
if ( !m_curr_page ) {
m_shadow->hide();
m_canvas->hide();
} else {
m_shadow->show();
m_canvas->show();
}
if ( m_full_page ) {
update_canvas_scrollbars();
update_rulers();
}
}
//-------------------------------------------------------------//
void QSPlotView::setWorkbook( QSWorkbook *workbook )
// wach out canvas size changes - printer()
{
if ( m_workbook != workbook ) {
if ( m_workbook ) {
disconnect( m_workbook, SIGNAL(sigPrinterChanged()), this, SLOT(adjust_canvas_size()) );
disconnect( m_workbook, SIGNAL(sigPageRemoved(QSPage*)), this, SLOT(slot_page_removed(QSPage*)) );
disconnect( m_workbook, SIGNAL(sigPageAdded(QSPage*)), this, SLOT(slot_page_added(QSPage*)) );
disconnect( m_workbook, SIGNAL(sigPageOrder()), this, SLOT(slot_page_order()) );
disconnect( m_workbook, SIGNAL(sigPageListChanged()), this, SLOT(slot_page_list_changed()) );
disconnect( m_workbook, SIGNAL(sigObjectRemoved(QSCObject*)), this, SLOT(slot_object_removed(QSCObject*)) );
}
m_workbook = workbook;
m_selection->set_workbook( workbook );
if ( m_workbook ) {
connect( m_workbook, SIGNAL(sigPrinterChanged()), this, SLOT(adjust_canvas_size()) );
connect( m_workbook, SIGNAL(sigPageRemoved(QSPage*)), this, SLOT(slot_page_removed(QSPage*)) );
connect( m_workbook, SIGNAL(sigPageAdded(QSPage*)), this, SLOT(slot_page_added(QSPage*)) );
connect( m_workbook, SIGNAL(sigPageOrder()), this, SLOT(slot_page_order()) );
connect( m_workbook, SIGNAL(sigPageListChanged()), this, SLOT(slot_page_list_changed()) );
connect( m_workbook, SIGNAL(sigObjectRemoved(QSCObject*)), this, SLOT(slot_object_removed(QSCObject*)) );
}
recreate_page_bar(); // new number of pages
adjust_canvas_size(); // new printer() - page size
setCurrentPage( 0 ); // new current page
}
}
//-------------------------------------------------------------//
void QSPlotView::slot_page_removed( QSPage *page )
{
//if ( page->objectFind(activeObject())>=0 ) setActiveObject( NULL );
// page isn't deleted yet so we can disconnect all signals safely
if ( page == currentPage() ) setCurrentPage( 0 );
}
//-------------------------------------------------------------//
void QSPlotView::slot_page_added( QSPage *page )
{
//recreate_page_bar();
if ( currentPage() == NULL ) setCurrentPage( m_workbook->pageFind(page) );
}
//-------------------------------------------------------------//
void QSPlotView::slot_page_order()
{
}
//-------------------------------------------------------------//
void QSPlotView::slot_page_list_changed()
{
recreate_page_bar();
}
//-------------------------------------------------------------//
void QSPlotView::slot_object_removed( QSCObject * )
{
// object isnt deleted yet so we can disconnect all signals safely
//if ( object == activeObject() ) setActiveObject( NULL );
}
//-------------------------------------------------------------//
void QSPlotView::setCurrentPage( int index )
{
QSPage *new_page = m_workbook ? m_workbook->page(index) : NULL;
if ( new_page != m_curr_page ) {
if ( m_curr_page && m_curr_page->objects()->busy() ) m_curr_page->objects()->stop();
deactivate_tool();
// hope that page isn't deleted yet
if ( m_curr_page ) {
disconnect( m_curr_page, SIGNAL(sigPageChanged()), this, SLOT(updateCanvas()) );
disconnect( m_curr_page, SIGNAL(sigTitleChanged(const QString&)), this, SLOT(slot_curr_page_title_changed(const QString&)) );
disconnect( m_curr_page->objects(), SIGNAL(sigListChanged()), this, SLOT(slot_curr_page_object_list_changed()) );
}
m_curr_page = new_page;
if ( m_curr_page ) {
connect( m_curr_page, SIGNAL(sigPageChanged()), this, SLOT(updateCanvas()) );
connect( m_curr_page, SIGNAL(sigTitleChanged(const QString&)), this, SLOT(slot_curr_page_title_changed(const QString&)) );
connect( m_curr_page->objects(), SIGNAL(sigListChanged()), this, SLOT(slot_curr_page_object_list_changed()) );
}
update_page_bar();
activate_tool(); // page can't change when tool is active
adjust_canvas_size(); // hide page if is null
updateCanvas();
emit sigCurrentPageChanged();
}
}
//-------------------------------------------------------------//
void QSPlotView::slot_curr_page_object_list_changed()
{
emit sigCurrentPageObjectListChanged();
}
//-------------------------------------------------------------//
void QSPlotView::set_active_object( QSCObject *o, bool )
{
if ( m_active_object != o ) {
//if ( !tool ) deactivate_tool();
m_active_object = o;
set_active_axes( o ? o->parentAxes() : NULL );
//if ( !tool ) activate_tool();
emit sigActiveObjectChanged();
if ( !m_full_page ) updateCanvas();
}
}
//-------------------------------------------------------------//
void QSPlotView::activate_tool()
{
if ( m_ctool && !m_tool_activated && !m_tool_activating ) {
// if in activate() or deactivate() you change active axes
// there can appear infinity loop ->set_active_axes -> deactivate -> set_active_axes ...
m_tool_activating = true;
m_tool_activated = true;
m_ctool->activate(this);
m_tool_activating = false;
}
}
//-------------------------------------------------------------//
void QSPlotView::deactivate_tool()
{
if ( m_ctool && m_tool_activated && !m_tool_activating ) {
m_tool_activating = true;
m_tool_activated = false;
m_ctool->deactivate();
// clean after tool - set default values.
m_canvas->setCursor( ArrowCursor );
m_canvas->setFocusPolicy(QWidget::NoFocus);
m_tool_activating = false;
}
}
//-------------------------------------------------------------//
void QSPlotView::set_active_axes( QSAxes *axes )
{
if ( m_active_axes != axes ) {
deactivate_tool();
clean();
m_active_axes = axes;
init();
activate_tool();
emit sigActiveAxesChanged();
}
}
//-------------------------------------------------------------//
void QSPlotView::init()
// called after new active axes has been set
{
if ( m_active_axes ) {
connect(m_active_axes,SIGNAL(sigUpdate()),this,SLOT(slot_active_axes_modified()) );
connect(m_active_axes,SIGNAL(sigRangesValid()),this,SLOT(slot_active_axes_ranges_changed()) );
connect(m_active_axes,SIGNAL(sigChildRemoved(QSData*)),this,SLOT(slot_active_axes_changed(QSData*)));
connect(m_active_axes,SIGNAL(sigChildAdded(QSData*)),this,SLOT(slot_active_axes_changed(QSData*)));
connect(m_active_axes,SIGNAL(sigChildOrder()),this,SLOT(slot_active_axes_order()));
}
set_backstore_internal( !m_screen_buffering );
update_parameter_scrollbars();
update_parameter_sliders();
}
//-------------------------------------------------------------//
void QSPlotView::clean()
// called when active axes are to be changed
{
if ( m_active_axes ) {
disconnect(m_active_axes, SIGNAL(sigUpdate()), this, SLOT(slot_active_axes_modified()) );
disconnect(m_active_axes,SIGNAL(sigRangesValid()),this,SLOT(slot_active_axes_ranges_changed()));
disconnect(m_active_axes,SIGNAL(sigChildRemoved(QSData*)),this,SLOT(slot_active_axes_changed(QSData*)));
disconnect(m_active_axes,SIGNAL(sigChildAdded(QSData*)),this,SLOT(slot_active_axes_changed(QSData*)));
disconnect(m_active_axes,SIGNAL(sigChildOrder()),this,SLOT(slot_active_axes_order()));
}
}
//-------------------------------------------------------------//
void QSPlotView::slot_active_axes_changed( QSData * )
{
emit sigActiveAxesDatasetsChanged();
}
//-------------------------------------------------------------//
void QSPlotView::slot_active_axes_order()
{
emit sigActiveAxesDatasetsChanged();
}
//-------------------------------------------------------------//
void QSPlotView::slot_active_axes_modified()
{
update_parameter_sliders();
}
//-------------------------------------------------------------//
void QSPlotView::slot_active_axes_ranges_changed()
// called EVERY times plot is redrawn ( not only when ranges really change )
{
update_parameter_scrollbars();
}
//-------------------------------------------------------------//
QSCObject *QSPlotView::objectAt( const QPoint& p, bool recursive )
{
QSCObject *result = NULL;
if ( m_full_page && m_curr_page ) {
QSPt2f pos( p.x(), p.y() );
QSDrvHitTest drv(pos);
drv.setDC(new QPainter(m_canvas),dpi(),true);
drv.startDrawing(); // needed
result = m_curr_page->objects()->objectAt(pos,&drv,recursive);
} else {
if ( m_active_axes ) return m_active_axes->shadowObject();
}
return result;
}
//-------------------------------------------------------------//
void QSPlotView::setGridSpacing( int x_mm, int y_mm )
{
if ( m_grid_x != x_mm ||
m_grid_y != y_mm ) {
m_grid_x = x_mm;
m_grid_y = y_mm;
updateCanvas();
}
}
//-------------------------------------------------------------//
void QSPlotView::setGridVisible( bool visible )
{
if ( m_grid_visible != visible ) {
m_grid_visible = visible;
updateCanvas();
}
}
//-------------------------------------------------------------//
void QSPlotView::setPageMarginsVisible( bool visible )
{
if ( m_page_margins_visible != visible ) {
m_page_margins_visible = visible;
updateCanvas();
}
}
//-------------------------------------------------------------//
void QSPlotView::setRulersVisible( bool visible )
{
if ( m_rulers_visible != visible ) {
m_rulers_visible = visible;
recreate_rulers();
}
}
//-------------------------------------------------------------//
void QSPlotView::setFullPage( bool enabled )
{
if ( m_full_page != enabled ) {
deactivate_tool();
m_full_page = enabled;
recreate_gui(); // hide rulers/scrollbars - show sliders
adjust_canvas_size(); // fit/unfit canvas to the view area
activate_tool();
}
}
//-------------------------------------------------------------//
void QSPlotView::setZoom( double new_zoom )
{
if ( new_zoom >= 0.1 && new_zoom < 5.0 ) {
deactivate_tool();
m_zoom = new_zoom;
adjust_canvas_size();
activate_tool();
}
}
//-------------------------------------------------------------//
void QSPlotView::bindSlider( QSPlotView::SliderType t, const char* property, int min, int max )
{
m_sliders[t].property = property;
m_sliders[t].min = min;
m_sliders[t].max = max;
recreate_parameter_sliders();
}
//-------------------------------------------------------------//
void QSPlotView::bindScrollBar( QSPlotView::ScrollBarType t, QSAxis::AxisType type )
{
m_scrollbars[t].type = type;
recreate_parameter_scrollbars();
}
//-------------------------------------------------------------//
void QSPlotView::setSlidersVisible( bool visible )
{
if ( m_sliders_visible != visible ) {
m_sliders_visible = visible;
recreate_parameter_scrollbars();
recreate_parameter_sliders();
}
}
//-------------------------------------------------------------//
void QSPlotView::setTool( QSTool *t )
{
if ( t != m_ctool ) {
deactivate_tool();
delete m_ctool;
m_ctool = t;
if ( m_ctool ) {
activate_tool();
m_canvas->setMouseTracking(TRUE);
} else {
m_canvas->setMouseTracking(FALSE);
showUserMessage( QString::null );
}
}
}
//-------------------------------------------------------------//
void QSPlotView::draw_tool()
// tool can provide its own drawing procedure ( handles, cursors, etc. )
// this is called after page is drawn
{
//emit message( "Time " + QString::number(time.msecsTo(QTime::currentTime()))+" ms." );
if ( m_ctool && m_tool_activated ) m_ctool->draw();
}
//-------------------------------------------------------------//
bool QSPlotView::eventFilter( QObject *o, QEvent *e )
{
if ( o == m_canvas ) {
// why not working in switch-case - strange bug in Qt ??
// simply ignore Focus events ( no redraws when focus changes !! )
if ( dynamic_cast<QFocusEvent *>(e) ) return TRUE;
switch( e->type() ) {
// avoid some unwise clip restrictions in Qt
case QEvent::Paint:
QApplication::postEvent( m_canvas, new QEvent(QEvent::User) );
return TRUE;
// here we have no permament clip area.
case QEvent::User:
draw_page(); return TRUE;
// turn on X11- backstoring
case QEvent::Show:
set_backstore_internal( !m_screen_buffering ); break;
// do not repaint if m_canvas gets focus.
// - repainting is too time-expensive
// ( OK, we block all focus events ! ).
//case QEvent::FocusIn:
//case QEvent::FocusOut:
// pass all events to the tool currently active
default:
if ( m_ctool && m_tool_activated ) return m_ctool->canvasEvent( e );
}
}
else if ( o == m_interior ) {
// in single view mode canvas fits to the view area
if ( e->type() == QEvent::Resize ) {
adjust_canvas_size();
return FALSE;
}
}
else if ( o == m_page_list && m_page_list ) {
if ( e->type() == QEvent::MouseButtonPress )
if ( ((QMouseEvent *)e)->button() == QEvent::RightButton ) emit sigPageBarClicked();
}
return FALSE;
}
//-------------------------------------------------------------//
bool QSPlotView::confirm( const QString& message )
{
return QMessageBox::warning( NULL, tr("Confirmation"), message, QMessageBox::Yes, QMessageBox::No, 0 ) == QMessageBox::Yes;
}
//-------------------------------------------------------------//
void QSPlotView::draw_page()
{
// just update screen from a buffer if the buffer exists
if ( m_screen_buffering && m_screen_buffer && m_screen_buffer_valid ) {
slot_refresh_screen_buffer();
return;
}
// we must draw all from the begining
// stop all pending drawing operations
if ( m_curr_page &&
m_curr_page->objects()->busy() )
m_curr_page->objects()->stop();
if ( m_active_axes &&
m_active_axes->shadowObject()->busy() )
m_active_axes->shadowObject()->stop();
// delete an old screen buffer
delete m_screen_buffer; m_screen_buffer = NULL;
// paint device can be m_screen_buffer or directly m_canvas
QPaintDevice *paint_device;
if ( m_screen_buffering ) {
m_screen_buffer = new QPixmap( m_canvas->width(), m_canvas->height() );
m_screen_buffer_valid = true;
paint_device = m_screen_buffer;
} else {
paint_device = m_canvas;
}
// ok start flushing bitmap to screen at given intervals
if ( m_screen_buffering ) m_refresh_buffer->start( 300 );
// draw page - can stop timer if there is no jobs in the background
if ( m_full_page ) draw_full_page( paint_device ); else draw_single_view( paint_device );
//
if ( !m_screen_buffering ) draw_tool();
}
//-------------------------------------------------------------//
void QSPlotView::draw_single_view( QPaintDevice *paint_device )
{
QPainter p( paint_device );
QRect r = m_canvas->rect();
// draw margins
int mw = r.width() * 15 / 100;
int mh = r.height() * 15 / 100;
QRect m;
m = r; m.setBottom( m.top()+mh ); p.fillRect( m, white );
m = r; m.setTop( m.bottom()-mh ); p.fillRect( m, white );
m = r; m.setRight( m.left()+mw ); p.fillRect( m, white );
m = r; m.setLeft( m.right()-mw ); p.fillRect( m, white );
r.setTop( r.top()+mh );
r.setBottom( r.bottom()-mh );
r.setLeft( r.left()+mw );
r.setRight( r.right()-mw );
if ( m_active_axes ) {
m_perf_monitor.start();
connect( m_active_axes, SIGNAL(sigDrawEnds()), this, SLOT(slot_axes_draw_ends()) );
m_active_axes->setCanvasRect( QSRectf( r.x(), r.y(), r.width(), r.height() ) );
m_active_axes->paintPlot( &p, dpi(), false, false );
} else {
p.fillRect( r, white );
}
}
//-------------------------------------------------------------//
void QSPlotView::draw_full_page( QPaintDevice *paint_device )
{
QPainter p( paint_device );
// page color - always white
p.fillRect( m_canvas->rect(), white );
// draw margins
if ( m_workbook && m_workbook->printer() ) {
QRect margs = m_canvas->rect();
// draw frame
p.setPen( black );
p.setBrush( NoBrush );
p.drawRect( margs );
// draw margins
QPaintDeviceMetrics pdm(m_workbook->printer());
int mw = m_workbook->printer()->margins().width() * m_canvas->width() / pdm.width();
int mh = m_workbook->printer()->margins().height() * m_canvas->height() / pdm.height();
margs.setTop( mh );
margs.setLeft( mw );
margs.setRight( margs.right() - mw + 1 );
margs.setBottom( margs.bottom() - mh + 1 );
p.setBrush( NoBrush );
p.setPen( QPen( blue, 0, DotLine ) );
if ( m_page_margins_visible ) p.drawRect( margs );
}
if ( m_grid_visible ) draw_grid( &p );
if ( m_curr_page ) {
connect( m_curr_page->objects(), SIGNAL(sigDrawEnds()), this, SLOT(slot_page_draw_ends()) );
m_curr_page->paint( &p, dpi(), false );
}
}
//-------------------------------------------------------------//
void QSPlotView::draw_grid( QPainter *p )
{
double x_step = QSCoord::mmToPixels((double)m_grid_x,dpi());
double y_step = QSCoord::mmToPixels((double)m_grid_y,dpi());
double curr_y_pos = y_step;
double curr_x_pos = x_step;
if ( x_step >= 2.0 && y_step >= 2.0 ) {
p->setPen( gray );
while( curr_y_pos < m_canvas->rect().bottom() ) {
while ( curr_x_pos < m_canvas->rect().right() ) {
p->drawPoint( int(curr_x_pos+0.5), int(curr_y_pos+0.5) );
curr_x_pos += x_step;
}
curr_x_pos = x_step;
curr_y_pos += y_step;
}
}
}
//-------------------------------------------------------------//
void QSPlotView::showUserMessage( const QString& msg )
{
emit message( msg );
}
//-------------------------------------------------------------//
void QSPlotView::set_backstore_internal( bool enabled )
{
#ifdef Q_WS_X11
XSetWindowAttributes attributes;
// NotUseful, WhenMapped or Always
if ( enabled ) attributes.backing_store = Always;
else attributes.backing_store = NotUseful;
XChangeWindowAttributes( m_canvas->x11Display(),
m_canvas->winId(),
CWBackingStore,
&attributes );
#endif
}
//-------------------------------------------------------------//
bool QSPlotView::backstore_internal()
{
bool result = false;
#ifdef Q_WS_X11
XWindowAttributes attributes;
// NotUseful, WhenMapped or Always
XGetWindowAttributes( m_canvas->x11Display(), m_canvas->winId(), &attributes );
result = (attributes.backing_store != NotUseful );
#endif
return result;
}
//-------------------------------------------------------------//
QSCObjectCollection *QSPlotView::activeCollection() const
{
if ( currentPage() &&
dynamic_cast<QSCGroup*>(activeObject()) &&
currentPage()->objects() == activeObject()->rootCollection() ) {
return ((QSCGroup *)activeObject())->objects();
}
else if ( currentPage() ) {
return currentPage()->objects();
}
return NULL;
}
//-------------------------------------------------------------//
void QSPlotView::slot_axes_draw_ends()
{
if ( !m_full_page ) showUserMessage( QString(" Drawing time ")+QString::number(m_perf_monitor.elapsed())+" ms." );
disconnect( m_active_axes, SIGNAL(sigDrawEnds()), this, SLOT(slot_axes_draw_ends()) );
slot_refresh_screen_buffer();
m_refresh_buffer->stop();
}
//-------------------------------------------------------------//
void QSPlotView::slot_page_draw_ends()
{
disconnect( m_curr_page->objects(), SIGNAL(sigDrawEnds()), this, SLOT(slot_page_draw_ends()) );
slot_refresh_screen_buffer();
m_refresh_buffer->stop();
}
//-------------------------------------------------------------//
void QSPlotView::slot_refresh_screen_buffer()
{
if ( m_screen_buffer ) {
bitBlt( m_canvas, 0, 0, m_screen_buffer, 0, 0, m_screen_buffer->width(), m_screen_buffer->height(), CopyROP, TRUE );
draw_tool();
}
}
//-------------------------------------------------------------//
void QSPlotView::slot_curr_page_title_changed( const QString& new_title )
{
emit sigCurrentPageTitleChanged( new_title );
}
//-------------------------------------------------------------//