24 #include "gobchartsview.h"
25 #include "label/gobchartstextitem.h"
26 #include "utils/gobchartscolours.h"
27 #include "utils/gobchartsgrid.h"
28 #include "utils/gobchartsgraphitems.h"
29 #include "utils/gobchartsvaliditems.h"
31 #include <QtCore/qmath.h>
32 #include <QGraphicsView>
33 #include <QVBoxLayout>
34 #include <QDomDocument>
41 const qreal PERC_HEADER_SPACE = 0.7;
42 const qreal PERC_HEADER_TOP = 0.2;
45 const qreal PERC_XLABEL_SPACE = 0.7;
46 const qreal PERC_XLABEL_BOTTOM = 0.8;
49 const qreal PERC_YLABEL_SPACE = 0.7;
50 const qreal PERC_YLABEL_LEFT = 0.2;
52 const qreal LEFT_RIGHT_MARGIN_PERC = 0.15;
53 const qreal TOP_BOTTOM_MARGIN_PERC = 0.15;
56 const qreal STRIPSPACE_OFFSET = 0.05;
67 inline void emitDebugLogMsg( QString msg )
71 emit m_gobChartsView->lastDebugLogMsg( msg );
78 void calculateActiveTotals(
int end )
83 m_validItems->clear();
85 bool toDoubleOK =
true;
87 for(
int row = 0; row < end; row++ )
89 QString cat = m_gobChartsView->model()->data( m_gobChartsView->model()->index( row, CATEGORY ) ).toString().trimmed();
90 QString val = m_gobChartsView->model()->data( m_gobChartsView->model()->index( row, VALUE ) ).toString().trimmed();
94 if( !cat.isEmpty() || !val.isEmpty() )
98 cat = QString(
"Uncategorised" );
99 emitDebugLogMsg( tr(
"GobChartsView::calculateActiveTotals# No category provided for row [%1]." ).arg( row + 1 ) );
105 emitDebugLogMsg( tr(
"GobChartsView::calculateActiveTotals# No value provided for row [%1], defaulting to \"0.0\"." ).arg( row + 1 ) );
109 value = val.toDouble( &toDoubleOK );
116 emitDebugLogMsg( tr(
"GobChartsView::calculateActiveTotals# Value provided for row [%1] is negative, defaulting to \"0.0\"." ).arg( row + 1 ) );
119 m_totalValue +=
value;
120 m_maxValue = ( value > m_maxValue ) ? value : m_maxValue;
125 emitDebugLogMsg( tr(
"GobChartsView::calculateActiveTotals# Failed to convert the data value of row [%1] to \"double\"" ).arg( row + 1 ) );
129 m_validItems->addValidItem( row, cat, value );
139 void calculateGeometries()
141 m_leftRightMargin = m_gobChartsView->rect().width() * LEFT_RIGHT_MARGIN_PERC;
142 m_topBottomMargin = m_gobChartsView->rect().height() * TOP_BOTTOM_MARGIN_PERC;
144 m_graphScene->setSceneRect( QRectF( m_gobChartsView->rect() ) );
145 m_graphicsView->setSceneRect( QRectF( m_gobChartsView->rect() ) );
147 m_innerSceneRectF.setRect( m_gobChartsView->rect().x() + m_leftRightMargin,
148 m_gobChartsView->rect().y() + m_topBottomMargin,
149 m_gobChartsView->rect().width() - 2 * m_leftRightMargin,
150 m_gobChartsView->rect().height() - 2 * m_topBottomMargin );
152 m_grid->setGridRectF( QRectF( m_innerSceneRectF.left(),
153 m_innerSceneRectF.top(),
154 m_innerSceneRectF.width(),
155 m_innerSceneRectF.height() ) );
157 m_header->setRectF( QRectF( QPointF( m_innerSceneRectF.left(), m_topBottomMargin * PERC_HEADER_TOP ),
158 QSizeF( m_innerSceneRectF.width(), m_topBottomMargin * PERC_HEADER_SPACE ) ) );
160 m_xLabel->setRectF( QRectF( QPointF( m_innerSceneRectF.left(), m_gobChartsView->rect().height() - m_topBottomMargin * PERC_XLABEL_BOTTOM ),
161 QSizeF( m_innerSceneRectF.width(), m_topBottomMargin * PERC_XLABEL_SPACE ) ) );
163 m_yLabel->setRectF( QRectF( QPointF( m_leftRightMargin * PERC_YLABEL_LEFT, m_innerSceneRectF.top() ),
164 QSizeF( m_leftRightMargin * PERC_YLABEL_SPACE, m_innerSceneRectF.height() ) ) );
169 QString labelText( GobChartsLabel label )
171 if( m_labels.contains( label ) )
173 return m_labels.value( label )->toPlainText();
176 return QString(
"" );
181 QFont labelFont( GobChartsLabel label )
183 if( m_labels.contains( label ) )
185 return m_labels.value( label )->font();
193 QColor labelColour( GobChartsLabel label )
195 if( m_labels.contains( label ) )
197 return m_labels.value( label )->defaultTextColor();
205 Qt::Alignment labelAlignment( GobChartsLabel label )
207 if( m_labels.contains( label ) )
209 return m_labels.value( label )->alignment();
212 return Qt::AlignHCenter;
219 if( m_labels.contains( label ) )
233 m_gobChartsView ( view ),
234 m_graphicsView ( new QGraphicsView ),
235 m_graphScene ( new QGraphicsScene ),
242 m_selectedLabel ( NONE ),
243 m_innerSceneRectF (),
246 m_leftRightMargin ( 0.0 ),
247 m_topBottomMargin ( 0.0 ),
249 m_lowerDataBoundary( 0.0 ),
250 m_upperDataBoundary( 0.0 ),
251 m_totalValue ( 0.0 ),
253 m_showTotalRange ( true ),
254 m_loggingOn ( false ),
255 m_fixedColourOn ( false ),
256 m_chartIsLoading ( false )
258 m_graphScene->setBackgroundBrush( QBrush( QColor( 245,245,245 ) ) );
260 m_graphScene->addItem( m_header );
261 m_graphScene->addItem( m_xLabel );
262 m_graphScene->addItem( m_yLabel );
264 m_graphicsView->setScene( m_graphScene );
265 m_graphicsView->setResizeAnchor( QGraphicsView::AnchorViewCenter );
266 m_graphicsView->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
267 m_graphicsView->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
270 m_labels.insert( HEADER, m_header );
271 m_labels.insert( YLABEL, m_yLabel );
272 m_labels.insert( XLABEL, m_xLabel );
274 m_labelNames.insert(
"HEADER", HEADER );
275 m_labelNames.insert(
"YLABEL", YLABEL );
276 m_labelNames.insert(
"XLABEL", XLABEL );
281 m_graphItems->removeItemsFromScene( m_graphScene );
282 m_grid->removeGridFromScene( m_graphScene );
284 delete m_graphicsView;
292 QGraphicsView *m_graphicsView;
293 QGraphicsScene *m_graphScene;
300 GobChartsLabel m_selectedLabel;
301 QRectF m_innerSceneRectF;
302 QColor m_fixedColour;
303 QString m_legendText;
304 qreal m_leftRightMargin;
305 qreal m_topBottomMargin;
307 qreal m_lowerDataBoundary;
308 qreal m_upperDataBoundary;
311 bool m_showTotalRange;
313 bool m_fixedColourOn;
314 bool m_chartIsLoading;
317 QMap< GobChartsLabel, GobChartsTextItem* > m_labels;
318 QMap< QString, GobChartsLabel > m_labelNames;
325 QAbstractItemView( parent ),
326 m_private ( new GobChartsViewPrivate( this ) )
328 setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
329 setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
331 m_private->calculateGeometries();
334 connect( m_private->m_header, SIGNAL( identity (
const QString& ) ),
335 this, SLOT ( setSelectedTextItem(
const QString& ) ) );
337 connect( m_private->m_xLabel, SIGNAL( identity (
const QString& ) ),
338 this, SLOT ( setSelectedTextItem(
const QString& ) ) );
340 connect( m_private->m_yLabel, SIGNAL( identity (
const QString& ) ),
341 this, SLOT ( setSelectedTextItem(
const QString& ) ) );
347 QVBoxLayout *layout =
new QVBoxLayout;
348 layout->addWidget( m_private->m_graphicsView );
351 setViewport( m_private->m_graphicsView );
365 if( m_private->m_labels.contains( label ) )
368 const_cast< GobChartsTextItem*
>( m_private->m_labels.value( label ) )->setPlainText( text );
369 const_cast< GobChartsTextItem*
>( m_private->m_labels.value( label ) )->setMaxFontSize( font );
370 const_cast< GobChartsTextItem*
>( m_private->m_labels.value( label ) )->setAlignment( align );
371 const_cast< GobChartsTextItem*
>( m_private->m_labels.value( label ) )->setDefaultTextColor( colour );
379 if( m_private->m_labels.contains( label ) )
381 m_private->m_selectedLabel = label;
382 emit
emitLabelDetails( label, m_private->labelText( label ), m_private->labelFont( label ), m_private->labelColour( label ), m_private->labelAlignment( label ) );
384 m_private->m_graphScene->clearFocus();
385 m_private->m_graphScene->setFocusItem( m_private->labelGraphicsItem( label ) );
389 m_private->m_selectedLabel = NONE;
395 void GobChartsView::setSelectedTextItem(
const QString &itemName )
404 m_private->m_grid->setGridLineStyle( style );
412 m_private->m_grid->setHorizontalGridLines(
set, number );
420 m_private->m_grid->setVerticalGridLines(
set, number );
428 m_private->m_grid->setGridColour( colour );
438 m_private->calculateGeometries();
440 m_private->m_graphItems->removeItemsFromScene( m_private->m_graphScene );
441 m_private->m_graphItems->deleteItems();
445 m_private->m_grid->removeGridFromScene( m_private->m_graphScene );
446 m_private->m_grid->constructGrid();
447 m_private->m_grid->addGridToScene( m_private->m_graphScene );
453 m_private->m_graphItems->addItemsToScene( m_private->m_graphScene );
457 m_private->emitDebugLogMsg( tr(
"GobChartsView::drawChart# No valid data model set." ) );
465 QRectF rectF = m_private->m_graphItems->getItemRectF( text );
466 QRect rect = rectF.toRect();
467 m_private->m_legendText = text;
469 selectionModel()->setCurrentIndex( m_private->m_graphItems->getModelIndex( text ), QItemSelectionModel::NoUpdate );
478 if( !viewNode.isNull() )
481 QDomNode label = viewNode.firstChildElement(
"Header" );
483 if( !m_private->m_header->setStateXML( label ) )
485 m_private->emitDebugLogMsg( tr(
"GobChartsView::setStateXML# Header node is invalid or NULL." ) );;
489 label = viewNode.firstChildElement(
"YLabel" );
491 if( !m_private->m_yLabel->setStateXML( label ) )
493 m_private->emitDebugLogMsg( tr(
"GobChartsView::setStateXML# Y Label node is invalid or NULL." ) );
497 label = viewNode.firstChildElement(
"XLabel" );
499 if( !m_private->m_xLabel->setStateXML( label ) )
501 m_private->emitDebugLogMsg( tr(
"GobChartsView::setStateXML# Y Label node is invalid or NULL." ) );
508 m_private->m_yLabel->setVisible(
true );
509 m_private->m_xLabel->setVisible(
true );
513 m_private->m_yLabel->setVisible(
false );
514 m_private->m_xLabel->setVisible(
false );
519 m_private->emitDebugLogMsg( tr(
"GobChartsView::setStateXML# View node is invalid or NULL." ) );
527 if( !dataNode.isNull() )
530 m_private->m_chartIsLoading =
true;
533 for(
int row = 0; row < model()->rowCount(); row++ )
535 model()->setData( model()->index( row, CATEGORY ), QString() );
536 model()->setData( model()->index( row, VALUE ), QString() );
540 QDomElement itemElement = dataNode.firstChildElement(
"Item" );
543 while( !itemElement.isNull() )
545 model()->setData( model()->index( row, CATEGORY ), itemElement.attribute(
"category",
"Uncategorised" ) );
546 model()->setData( model()->index( row, VALUE ), itemElement.attribute(
"value",
"0.0" ) );
547 itemElement = itemElement.nextSiblingElement(
"Item" );
551 m_private->m_chartIsLoading =
false;
552 m_private->calculateActiveTotals( row );
556 m_private->emitDebugLogMsg( tr(
"GobChartsView::setStateXML# Data node is invalid or NULL." ) );
561 m_private->emitDebugLogMsg( tr(
"GobChartsView::setStateXML# No data model associated with this view." ) );
576 xml += m_private->m_header->getStateXML();
580 xml += m_private->m_yLabel->getStateXML();
584 xml += m_private->m_xLabel->getStateXML();
587 xml +=
"<ChartType value=\"" +
typeInteger() +
"\" />";
596 xml +=
"<Item category=\"" +
category( i ) +
"\" value=\"" + QString(
"%1" ).arg(
value( i ) ) +
"\" />";
612 if( m_private->m_totalValue > 0.0 )
615 qreal maxStrip = m_private->m_innerSceneRectF.height() - ( ( m_private->m_maxValue/m_private->m_totalValue ) + STRIPSPACE_OFFSET ) * m_private->m_innerSceneRectF.height();
616 return ( perc/( m_private->m_maxValue/m_private->m_totalValue ) ) * maxStrip;
626 if( colour.isValid() )
628 m_private->m_fixedColour = colour;
629 m_private->m_fixedColourOn =
true;
633 m_private->emitDebugLogMsg( tr(
"GobChartsView::setFixedColour# Invalid colour provided" ) );
634 m_private->m_fixedColourOn =
false;
644 m_private->m_fixedColourOn =
false;
652 return m_private->m_fixedColourOn;
659 return m_private->m_fixedColour;
666 m_private->m_loggingOn = logging;
667 m_private->m_grid->setDebugLoggingOn( logging );
668 m_private->m_graphItems->setDebugLoggingOn( logging );
675 m_private->m_lowerDataBoundary = lowerBoundary;
676 m_private->m_upperDataBoundary = upperBoundary;
677 m_private->m_showTotalRange =
false;
685 m_private->m_showTotalRange =
true;
693 if( m_private->m_showTotalRange )
699 return ( value >= m_private->m_lowerDataBoundary ) && ( value <= m_private->m_upperDataBoundary );
707 m_private->m_graphItems->addItem( valueIndex, item, legendText );
714 return m_private->m_grid->gridWidth();
721 return m_private->m_validItems->size();
728 return m_private->m_validItems->validRows();
735 return m_private->m_validItems->category( row );
742 return m_private->m_validItems->data( row );
749 return m_private->m_innerSceneRectF;
756 return m_private->m_totalValue;
763 m_private->emitDebugLogMsg( msg );
770 void GobChartsView::resizeEvent( QResizeEvent *event )
772 QAbstractItemView::resizeEvent( event );
780 QAbstractItemView::dataChanged( topLeft, bottomRight );
782 if( !m_private->m_chartIsLoading )
784 int rowCount = ( ( bottomRight.row() + 1 ) > m_private->m_maxRow ) ? ( bottomRight.row() + 1 ) : m_private->m_maxRow;
785 m_private->calculateActiveTotals( rowCount );
794 QAbstractItemView::rowsInserted( parent, start, end );
795 int rowCount = ( ( end + 1 ) > m_private->m_maxRow ) ? ( end + 1 ) : m_private->m_maxRow;
796 m_private->calculateActiveTotals( rowCount );
803 QAbstractItemView::rowsAboutToBeRemoved( parent, start, end );
804 int rowCount = ( ( end + 1 ) > m_private->m_maxRow ) ? ( end + 1 ) : m_private->m_maxRow;
805 m_private->calculateActiveTotals( rowCount - ( end - start ) );
812 if( !m_private->m_graphItems->getItemRectF( index ).isNull() )
814 QRectF rect = m_private->m_graphItems->getItemRectF( index );
815 return rect.toRect();
818 return m_private->m_graphicsView->rect();
833 QAbstractItemView::setModel( model );
837 m_private->calculateActiveTotals( model->rowCount() );
841 m_private->emitDebugLogMsg( tr(
"GobChartsView::setModel# Attempting to set a NULL model on this view." ) );
849 if( m_private->m_graphItems->getModelIndex( m_private->m_graphScene->itemAt( point) ).isValid() )
851 return m_private->m_graphItems->getModelIndex( m_private->m_graphScene->itemAt( point ) );
854 return QModelIndex();
861 Q_UNUSED( modifiers );
863 QModelIndex current = currentIndex();
865 switch ( cursorAction )
870 if( current.row() > 0 )
872 current = model()->index( current.row() - 1, current.column(), rootIndex() );
876 current = model()->index( 0, current.column(), rootIndex() );
882 if( current.row() < model()->rowCount( model()->parent( current ) ) - 1 )
884 current = model()->index( current.row() + 1, current.column(), rootIndex() );
888 current = model()->index( model()->rowCount( model()->parent( current ) ) - 1,
889 current.column(), rootIndex() );
928 if( selectionModel() )
930 int firstRow = m_private->m_maxRow;
935 QList< QGraphicsItem* > itemList = m_private->m_graphScene->items( rect );
938 if( itemList.size() == 1 )
940 foreach( QGraphicsItem* itemIt, itemList )
942 QModelIndex index = m_private->m_graphItems->getModelIndex( itemIt );
944 m_private->m_legendText = m_private->m_graphItems->getItemLegend( itemIt );
947 if( index.isValid() )
949 m_private->m_graphItems->setSelected( index.row() );
950 firstRow = qMin( firstRow, index.row() );
951 lastRow = qMax( lastRow, index.row() );
952 firstColumn = qMin( firstColumn, index.column() );
953 lastColumn = qMax( firstColumn, index.column() );
957 QItemSelection selection( model()->index( firstRow, firstColumn, rootIndex() ),
958 model()->index( lastRow, lastColumn, rootIndex() ) );
960 selectionModel()->select( selection, QItemSelectionModel::ClearAndSelect );
965 QModelIndex noIndex = m_private->m_graphItems->getModelIndex( m_private->m_legendText );
966 QItemSelection selection( noIndex, noIndex );
967 selectionModel()->select( selection, QItemSelectionModel::ClearAndSelect );
968 m_private->m_graphItems->clearSelection();
973 m_private->emitDebugLogMsg( tr(
"GobChartsView::setModel# No selection model associated with this view." ) );
981 QAbstractItemView::mousePressEvent( event );
982 m_private->m_selectedLabel = NONE;
989 if( m_private->m_selectedLabel == HEADER )
991 m_private->m_header->receiveKeyEvent( event );
994 else if( m_private->m_selectedLabel == XLABEL )
996 m_private->m_xLabel->receiveKeyEvent( event );
999 else if( m_private->m_selectedLabel == YLABEL )
1001 m_private->m_yLabel->receiveKeyEvent( event );
1006 QAbstractItemView::keyPressEvent( event );
1016 foreach( QModelIndex indexIt, selection.indexes() )
1018 QRectF rect = m_private->m_graphItems->getItemRectF( indexIt );
1020 if( !rect.isNull() )
1022 region += rect.toRect();