24 #include "KDChartLegend_p.h"
34 #include <QGridLayout>
36 #include <QTextTableCell>
37 #include <QTextCursor>
38 #include <QTextCharFormat>
39 #include <QTextDocumentFragment>
41 #include <QAbstractTextDocumentLayout>
45 #include <KDABLibFakes>
49 Legend::Private::Private() :
52 alignment( Qt::AlignCenter ),
53 textAlignment( Qt::AlignCenter ),
55 orientation( Qt::Vertical ),
56 order( Qt::AscendingOrder ),
58 titleText(
QObject::tr(
"Legend" ) ),
60 useAutomaticMarkerSize( true ),
61 legendStyle( MarkersOnly )
65 relativePosition.setReferencePoints(
PositionPoints( QPointF( 0.0, 0.0 ) ) );
67 relativePosition.setAlignment( Qt::AlignTop | Qt::AlignLeft );
72 Legend::Private::~Private()
84 d->referenceArea = parent;
91 d->referenceArea = parent;
103 setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
105 d->layout =
new QGridLayout(
this );
106 d->layout->setMargin( 2 );
107 d->layout->setSpacing(
d->spacing );
114 textAttrs.
setPen( QPen( Qt::black ) );
115 textAttrs.
setFont( QFont( QLatin1String(
"helvetica" ), 10, QFont::Normal,
false ) );
121 titleTextAttrs.
setPen( QPen( Qt::black ) );
122 titleTextAttrs.
setFont( QFont( QLatin1String(
"helvetica" ), 12, QFont::Bold,
false ) );
129 frameAttrs.
setPen( QPen( Qt::black ) );
134 d->alignment = Qt::AlignCenter;
147 #ifdef DEBUG_LEGEND_PAINT
148 qDebug() <<
"Legend::sizeHint() started";
151 paintItem->sizeHint();
153 return AbstractAreaWidget::sizeHint();
163 #ifdef DEBUG_LEGEND_PAINT
164 qDebug() <<
"Legend::resizeLayout started";
167 d->reflowHDatasetItems(
this );
168 d->layout->setGeometry( QRect(QPoint( 0,0 ), size) );
171 #ifdef DEBUG_LEGEND_PAINT
172 qDebug() <<
"Legend::resizeLayout done";
176 void Legend::activateTheLayout()
178 if (
d->layout &&
d->layout->parent() ) {
179 d->layout->activate();
185 if (
d->legendStyle == style ) {
188 d->legendStyle = style;
194 return d->legendStyle;
217 if ( other ==
this ) {
225 (isVisible() == other->isVisible()) &&
247 #ifdef DEBUG_LEGEND_PAINT
248 qDebug() <<
"entering Legend::paint( QPainter* painter )";
257 paintItem->
paint( painter );
260 #ifdef DEBUG_LEGEND_PAINT
261 qDebug() <<
"leaving Legend::paint( QPainter* painter )";
268 int modelLabelsCount = 0;
274 return modelLabelsCount;
280 if ( area ==
d->referenceArea ) {
283 d->referenceArea = area;
289 return d->referenceArea ?
d->referenceArea : qobject_cast< const QWidget* >( parent() );
295 if (
d->observers.isEmpty() ) {
298 return d->observers.first()->diagram();
304 for (
int i = 0; i <
d->observers.size(); ++i ) {
305 list <<
d->observers.at(i)->diagram();
313 for (
int i = 0; i <
d->observers.size(); ++i ) {
314 list <<
d->observers.at(i)->diagram();
327 d->observers[
d->observers.indexOf( oldObs ) ] = observer;
329 d->observers.append( observer );
331 connect( observer, SIGNAL( diagramAboutToBeDestroyed(
AbstractDiagram*) ),
334 SLOT( setNeedRebuild() ));
336 SLOT( setNeedRebuild() ));
337 connect( observer, SIGNAL( diagramAttributesChanged(
AbstractDiagram*) ),
338 SLOT( setNeedRebuild() ));
345 int datasetBrushOffset = 0;
347 for (
int i = 0; i <
diagrams.count(); i++ ) {
348 if (
diagrams.at( i ) == oldDiagram ) {
349 for (
int i = 0; i < oldDiagram->
datasetBrushes().count(); i++ ) {
350 d->brushes.remove(datasetBrushOffset + i);
351 d->texts.remove(datasetBrushOffset + i);
353 for (
int i = 0; i < oldDiagram->
datasetPens().count(); i++ ) {
354 d->pens.remove(datasetBrushOffset + i);
358 datasetBrushOffset +=
diagrams.at(i)->datasetBrushes().count();
365 d->observers.removeAt(
d->observers.indexOf( oldObs ) );
376 for (
int i = 0; i <
d->observers.size(); ++i ) {
377 diagrams.append(
d->observers.at( i )->diagram() );
379 for (
int i = 0; i <
diagrams.count(); ++i ) {
388 if ( !
d->observers.isEmpty() && !old ) {
389 old =
d->observers.first()->diagram();
391 d->observers.removeFirst();
406 for (
int i = 0; i <
d->observers.count(); ++i ) {
407 if (
d->observers.at(i)->diagram() ==
diagram ) {
414 offset = offset +
diagram->model()->columnCount();
434 QWidget::setVisible( visible );
435 emitPositionChanged();
438 void Legend::setNeedRebuild()
450 emitPositionChanged();
453 void Legend::emitPositionChanged()
471 emitPositionChanged();
485 emitPositionChanged();
490 return d->textAlignment;
495 if (
d->legendLineSymbolAlignment ==
alignment ) {
499 emitPositionChanged();
504 return d->legendLineSymbolAlignment ;
510 if (
d->relativePosition != relativePosition ) {
511 d->relativePosition = relativePosition;
512 emitPositionChanged();
518 return d->relativePosition;
528 emitPositionChanged();
533 return d->orientation;
538 if (
d->order == order ) {
543 emitPositionChanged();
553 if (
d->showLines == legendShowLines ) {
556 d->showLines = legendShowLines;
558 emitPositionChanged();
570 emitPositionChanged();
575 return d->useAutomaticMarkerSize;
585 if ( !
d->texts.count() ) {
594 if (
d->texts[ dataset ] ==
text ) {
597 d->texts[ dataset ] =
text;
603 if (
d->texts.find( dataset ) !=
d->texts.end() ) {
604 return d->texts[ dataset ];
606 return d->modelLabels[ dataset ];
617 if (
d->brushes[ dataset ] != color ) {
618 d->brushes[ dataset ] = color;
626 if (
d->brushes[ dataset ] !=
brush ) {
627 d->brushes[ dataset ] =
brush;
635 if (
d->brushes.contains( dataset ) ) {
636 return d->brushes[ dataset ];
638 return d->modelBrushes[ dataset ];
650 bool changed =
false;
652 for (
int i = 0; i < datasetBrushes.count(); i++ ) {
653 if (
d->brushes[ i ] != datasetBrushes[ i ] ) {
654 d->brushes[ i ] = datasetBrushes[ i ];
667 if (
d->pens[dataset] ==
pen ) {
670 d->pens[dataset] =
pen;
677 if (
d->pens.find( dataset ) !=
d->pens.end() ) {
678 return d->pens[ dataset ];
680 return d->modelPens[ dataset ];
702 if (
d->markerAttributes.find( dataset ) !=
d->markerAttributes.end() ) {
703 return d->markerAttributes[ dataset ];
704 }
else if (
static_cast<uint
>(
d->modelMarkers.count() ) > dataset ) {
705 return d->modelMarkers[ dataset ];
713 return d->markerAttributes;
719 if (
d->textAttributes == a ) {
722 d->textAttributes = a;
728 return d->textAttributes;
733 if (
d->titleText ==
text ) {
747 if (
d->titleTextAttributes == a ) {
750 d->titleTextAttributes = a;
756 return d->titleTextAttributes;
761 #ifdef DEBUG_LEGEND_PAINT
762 qDebug() <<
"entering Legend::forceRebuild()";
765 #ifdef DEBUG_LEGEND_PAINT
766 qDebug() <<
"leaving Legend::forceRebuild()";
772 if (
d->spacing == space &&
d->layout->spacing() ==
int( space ) ) {
776 d->layout->setSpacing( space );
788 for (
int i = 0; i < pal.
size(); i++ ) {
796 for (
int i = 0; i < pal.
size(); i++ ) {
805 for (
int i = 0; i < pal.
size(); i++ ) {
809 static const int s_subduedColorsCount = 18;
810 Q_ASSERT( pal.
size() >= s_subduedColorsCount );
811 static const int order[ s_subduedColorsCount ] = {
812 0, 5, 10, 15, 2, 7, 12, 17, 4,
813 9, 14, 1, 6, 11, 16, 3, 8, 13
815 for (
int i = 0; i < s_subduedColorsCount; i++ ) {
824 #ifdef DEBUG_LEGEND_PAINT
825 qDebug() <<
"Legend::resizeEvent() called";
829 QTimer::singleShot( 0,
this, SLOT(emitPositionChanged()) );
832 void Legend::Private::fetchPaintOptions(
Legend *q )
835 modelBrushes.clear();
837 modelMarkers.clear();
839 for (
int i = 0; i < observers.size(); ++i ) {
849 const bool ascend = q->
sortOrder() == Qt::AscendingOrder;
850 int dataset = ascend ? 0 : diagramLabels.count() - 1;
851 const int end = ascend ? diagramLabels.count() : -1;
852 for ( ; dataset != end; dataset += ascend ? 1 : -1 ) {
856 modelLabels += diagramLabels[ dataset ];
857 modelBrushes += diagramBrushes[ dataset ];
858 modelPens += diagramPens[ dataset ];
859 modelMarkers += diagramMarkers[ dataset ];
863 Q_ASSERT( modelLabels.count() == modelBrushes.count() );
866 QSizeF Legend::Private::markerSize(
Legend *q,
int dataset, qreal fontHeight )
const
870 return QSizeF( fontHeight, fontHeight );
876 QSizeF Legend::Private::maxMarkerSize(
Legend *q, qreal fontHeight )
const
878 QSizeF ret( 1.0, 1.0 );
880 for (
int dataset = 0; dataset < modelLabels.count(); ++dataset ) {
881 ret = ret.expandedTo( markerSize( q, dataset, fontHeight ) );
887 HDatasetItem::HDatasetItem()
897 if ( w->isTopLevel() ) {
900 w->layout()->update();
904 w = qobject_cast< QWidget * >( w->parent() );
910 void Legend::buildLegend()
917 d->destroyOldLayout();
920 d->layout->setColumnStretch( 6, 1 );
922 d->layout->setColumnStretch( 6, 0 );
925 d->fetchPaintOptions(
this );
935 measureOrientation,
d->textAlignment );
938 d->paintItems << titleItem;
939 d->layout->addItem( titleItem, 0, 0, 1, 5, Qt::AlignCenter );
942 if (
showLines() &&
d->modelLabels.count() ) {
944 d->paintItems << lineItem;
945 d->layout->addItem( lineItem, 1, 0, 1, 5, Qt::AlignCenter );
952 tmpFont.setPointSizeF( fontHeight );
956 fontHeight = QFontMetricsF( tmpFont ).height();
960 const QSizeF maxMarkerSize =
d->maxMarkerSize(
this, fontHeight );
967 const int lineLengthLeftOfMarker = 8;
969 int maxLineLength = 18;
971 bool hasComplexPenStyle =
false;
972 for (
int dataset = 0; dataset <
d->modelLabels.count(); ++dataset ) {
973 const QPen pn =
pen( dataset );
974 const Qt::PenStyle ps = pn.style();
975 if ( ps != Qt::NoPen ) {
976 maxLineLength = qMax( pn.width() * 18, maxLineLength );
977 if ( ps != Qt::SolidLine ) {
978 hasComplexPenStyle =
true;
983 maxLineLength += lineLengthLeftOfMarker + int( maxMarkerSize.width() );
989 for (
int dataset = 0; dataset <
d->modelLabels.count(); ++dataset ) {
990 const int vLayoutRow = 2 + dataset * 2;
996 markerAttrs.
setMarkerSize(
d->markerSize(
this, dataset, fontHeight ) );
997 const QBrush markerBrush = markerAttrs.
markerColor().isValid() ?
1003 markerAttrs.
pen(), Qt::AlignLeft | Qt::AlignVCenter );
1007 d->legendLineSymbolAlignment, Qt::AlignCenter );
1011 diagram(), maxLineLength,
pen( dataset ), lineLengthLeftOfMarker, markerAttrs,
1012 markerBrush, markerAttrs.
pen(), Qt::AlignCenter );
1019 measureOrientation,
d->textAlignment );
1020 dsItem.label->setParentWidget(
this );
1025 d->hLayoutDatasets << dsItem;
1031 if ( dsItem.markerLine ) {
1032 d->layout->addItem( dsItem.markerLine, vLayoutRow, 1, 1, 1, Qt::AlignCenter );
1033 d->paintItems << dsItem.markerLine;
1035 d->layout->addItem( dsItem.label, vLayoutRow, 3, 1, 1, Qt::AlignLeft | Qt::AlignVCenter );
1036 d->paintItems << dsItem.label;
1039 if (
showLines() && dataset !=
d->modelLabels.count() - 1 ) {
1041 d->layout->addItem( lineItem, vLayoutRow + 1, 0, 1, 5, Qt::AlignCenter );
1042 d->paintItems << lineItem;
1047 d->flowHDatasetItems(
this );
1053 d->paintItems << lineItem;
1054 d->layout->addItem( lineItem, 2, 2,
d->modelLabels.count() * 2, 1 );
1060 #ifdef DEBUG_LEGEND_PAINT
1061 qDebug() <<
"leaving Legend::buildLegend()";
1065 int HDatasetItem::height()
const
1067 return qMax( markerLine->sizeHint().height(), label->sizeHint().height() );
1070 void Legend::Private::reflowHDatasetItems(
Legend *q )
1072 if (hLayoutDatasets.isEmpty()) {
1079 for (
int i = layout->count() - 1; i >= 0; i-- ) {
1081 QLayout *
const hbox = item->layout();
1085 paintItems << alItem;
1088 Q_ASSERT(
dynamic_cast< QHBoxLayout *
>( hbox ) );
1089 layout->takeAt( i );
1091 for (
int j = hbox->count() - 1; j >= 0; j-- ) {
1097 flowHDatasetItems( q );
1102 void Legend::Private::flowHDatasetItems(
Legend *q )
1104 const int separatorLineWidth = 3;
1108 QHBoxLayout *currentLine =
new QHBoxLayout;
1109 int mainLayoutRow = 1;
1110 layout->addItem( currentLine, mainLayoutRow++, 0,
1111 1 , 5, Qt::AlignLeft | Qt::AlignVCenter );
1113 for (
int dataset = 0; dataset < hLayoutDatasets.size(); dataset++ ) {
1114 HDatasetItem *hdsItem = &hLayoutDatasets[ dataset ];
1116 bool spacerUsed =
false;
1117 bool separatorUsed =
false;
1118 if ( !currentLine->isEmpty() ) {
1119 const int separatorWidth = ( q->
showLines() ? separatorLineWidth : 0 ) + q->
spacing();
1120 const int payloadWidth = hdsItem->markerLine->sizeHint().width() +
1121 hdsItem->label->sizeHint().width();
1122 if ( currentLine->sizeHint().width() + separatorWidth + payloadWidth > allowedWidth ) {
1124 #ifdef DEBUG_LEGEND_PAINT
1125 qDebug() << Q_FUNC_INFO <<
"break" << mainLayoutRow
1126 << currentLine->sizeHint().width()
1127 << currentLine->sizeHint().width() + separatorWidth + payloadWidth
1130 currentLine =
new QHBoxLayout;
1131 layout->addItem( currentLine, mainLayoutRow++, 0,
1132 1 , 5, Qt::AlignLeft | Qt::AlignVCenter );
1135 if ( !hdsItem->spacer ) {
1136 hdsItem->spacer =
new QSpacerItem( q->
spacing(), 1 );
1138 currentLine->addItem( hdsItem->spacer );
1142 if ( !hdsItem->separatorLine ) {
1145 paintItems << hdsItem->separatorLine;
1146 currentLine->addItem( hdsItem->separatorLine );
1147 separatorUsed =
true;
1152 if ( !spacerUsed ) {
1153 delete hdsItem->spacer;
1154 hdsItem->spacer = 0;
1156 if ( !separatorUsed ) {
1157 delete hdsItem->separatorLine;
1158 hdsItem->separatorLine = 0;
1161 currentLine->addItem( hdsItem->markerLine );
1162 paintItems << hdsItem->markerLine;
1163 currentLine->addItem( hdsItem->label );
1164 paintItems << hdsItem->label;
1173 return !
d->hLayoutDatasets.isEmpty();
1178 if (
d->hLayoutDatasets.isEmpty() ) {
1184 for (
int i = 0; i < 2; i++) {
1185 if (
QLayoutItem *item =
d->layout->itemAtPosition( i, 0 ) ) {
1186 ret += item->sizeHint().height();
1189 const int separatorLineWidth = 3;
1191 int currentLineWidth = 0;
1192 int currentLineHeight = 0;
1193 Q_FOREACH(
const HDatasetItem &hdsItem,
d->hLayoutDatasets ) {
1194 const int payloadWidth = hdsItem.markerLine->sizeHint().width() +
1195 hdsItem.label->sizeHint().width();
1196 if ( !currentLineWidth ) {
1198 currentLineWidth = payloadWidth;
1200 const int separatorWidth = (
showLines() ? separatorLineWidth : 0 ) +
spacing();
1201 currentLineWidth += separatorWidth + payloadWidth;
1202 if ( currentLineWidth > width ) {
1204 #ifdef DEBUG_LEGEND_PAINT
1205 qDebug() << Q_FUNC_INFO <<
"heightForWidth break" << currentLineWidth
1206 << currentLineWidth + separatorWidth + payloadWidth
1209 ret += currentLineHeight +
spacing();
1210 currentLineWidth = payloadWidth;
1211 currentLineHeight = 0;
1214 currentLineHeight = qMax( currentLineHeight, hdsItem.height() );
1216 ret += currentLineHeight;
1220 void Legend::Private::destroyOldLayout()
1224 for (
int i = layout->count() - 1; i >= 0; i-- ) {
1225 delete layout->takeAt( i );
1227 Q_ASSERT( !layout->count() );
1228 hLayoutDatasets.clear();
1239 return d->hiddenDatasets;
1244 if ( hidden && !
d->hiddenDatasets.contains( dataset ) ) {
1245 d->hiddenDatasets.append( dataset );
1246 }
else if ( !hidden &&
d->hiddenDatasets.contains( dataset ) ) {
1247 d->hiddenDatasets.removeAll( dataset );
1253 return d->hiddenDatasets.contains( dataset );