1 /*---------------------------------------------------------------------\
3 | __ __ ____ _____ ____ |
4 | \ \ / /_ _/ ___|_ _|___ \ |
5 | \ V / _` \___ \ | | __) | |
6 | | | (_| |___) || | / __/ |
7 | |_|\__,_|____/ |_| |_____| |
11 \----------------------------------------------------------------------/
13 File: YQMultiProgressMeter.cc
15 Author: Stefan Hundhammer <sh@suse.de>
20 #define y2log_component "qt-ui"
21 #include <ycp/y2log.h>
27 #include "YQMultiProgressMeter.h"
31 YQMultiProgressMeter::YQMultiProgressMeter( YWidget * parent,
33 const vector<float> & maxValues )
34 : QWidget( (QWidget *) parent->widgetRep() )
35 , YMultiProgressMeter( parent, dim, maxValues )
37 _triangularShaped = false;
43 YQMultiProgressMeter::~YQMultiProgressMeter()
49 void YQMultiProgressMeter::init()
52 _segmentMinLength = 12;
55 if ( triangularShaped() )
58 setTriThickness( -1 );
68 void YQMultiProgressMeter::setTriangularShaped( bool triangular )
70 _triangularShaped = triangular;
75 void YQMultiProgressMeter::doUpdate()
81 void YQMultiProgressMeter::paintEvent ( QPaintEvent * event )
86 QPainter painter( this );
88 // if ( ! event->erased() )
89 // painter.eraseRect( event->rect() );
91 int totalLength = horizontal() ? width() : height();
92 int thickness = horizontal() ? height() : width();
94 totalLength -= 2 * margin() + spacing() * ( segments()-1 );
95 thickness -= 2 * margin();
97 if ( triThickness() > 0 )
98 thickness -= 2 * triThickness() + 2 * triSpacing();
100 if ( totalLength < 1 || thickness < 1 || segments() < 1 )
104 // Add up the total sum of all maxValues
106 float totalSum = 0.0;
108 for( int i=0; i < segments(); i++ )
109 totalSum += maxValue( i );
112 // Figure out minimal segment length
114 int minLength = segmentMinLength();
117 // Limit the minimum if there isn't even that much space
119 if ( minLength * segments() > totalLength )
120 minLength = totalLength / ( 2 * segments() );
123 // First attempt of scaling factor from values to pixel coordinates
125 if ( totalSum == 0.0 )
127 y2error( "Avoiding division by zero: totalSum" );
131 float scale = ( (float) totalLength ) / totalSum;
132 float scaledMinLength = ( (float) minLength ) / scale;
135 // Check how many segments would become smaller than the minimum
137 int smallSegmentsCount = 0;
140 for ( int i=0; i < segments(); i++ )
142 if ( maxValue( i ) < scaledMinLength )
143 smallSegmentsCount++;
145 restSum += maxValue( i );
149 // Small segments that get at least minLength pixels consume more screen
150 // space than initially planned, so recompute what is left for the others
152 int distributableLength = totalLength - smallSegmentsCount * minLength;
154 if ( restSum == 0.0 )
156 y2error( "Avoiding division by zero: restSum" );
160 // Recompute scale to take small segments into account that now get screen
161 // space disproportional to their real size (maxValue).
162 scale = ( (float) distributableLength ) / ( restSum );
165 // Calculate indentation
167 int indent = triangularShaped() ? (int) ( thickness * 0.37 ) : 0;
174 painter.rotate( 90 );
175 painter.scale( 1.0, -1.0 );
178 int offset = margin();
180 // Draw each segment in turn
182 for ( int i=0; i < segments(); i++ )
186 if ( maxValue( i ) < scaledMinLength )
189 length = (int) ( maxValue( i ) * scale + 0.5 );
191 drawSegment( i, painter, offset, length, thickness, indent );
194 drawMarkers( painter, offset, thickness );
196 offset += length + spacing();
201 void YQMultiProgressMeter::mouseDoubleClickEvent ( QMouseEvent * event )
203 if ( event && event->button() == Qt::RightButton )
205 // Easter egg: Switch between rectangular and triangular shape
207 y2milestone( "Switching shape" );
208 setTriangularShaped( ! triangularShaped() );
209 setSize( vertical() ? preferredWidth() : width(),
210 horizontal() ? preferredHeight() : height() );
211 YQUI::ui()->evaluateRecalcLayout();
217 void YQMultiProgressMeter::drawSegment( int segment,
227 // Vertical MultiProgressMeters will be filled thermometer-like from bottom
228 // to top, horizontal ones like normal progress bars from left to right,
229 // i.e. just the opposite way.
234 int border = margin();
236 if ( triThickness() > 0 )
237 border += triThickness() + triSpacing();
239 if ( currentValue( segment ) < maxValue( segment ) )
241 if ( maxValue( segment ) == 0.0 )
243 y2error( "Avoiding division by zero: maxValue[%d]", segment );
247 float emptyPart = 1.0 - ( currentValue( segment ) ) / ( maxValue( segment ) );
248 fillStart = (int) ( length * emptyPart );
249 fillHeight = (int) ( indent * emptyPart );
252 thickness--; // We always deal with tickness-1 anyway, so let's cut this short
254 if ( vertical() ) // fill thermometer-like from bottom to top
256 if ( fillStart < length )
258 static const QPointF points[4] =
259 { QPointF( offset + fillStart, border + fillHeight ),
260 QPointF( offset + fillStart, border + thickness - fillHeight ),
261 QPointF( offset + length, border + thickness - indent ),
262 QPointF( offset + length, border + indent )
265 painter.setBrush( palette().highlight() );
266 painter.setPen( Qt::NoPen );
267 painter.drawConvexPolygon( points, 4 );
270 else // horizontal - fill from left to right like a normal progress bar
274 static const QPointF points[4] =
275 { QPointF( offset, border + thickness ),
276 QPointF( offset, border ),
277 QPointF( offset + fillStart, border + fillHeight ),
278 QPointF( offset + fillStart, border + thickness - fillHeight )
281 painter.setBrush( palette().highlight() );
282 painter.setPen( Qt::NoPen );
283 painter.drawConvexPolygon( points, 4 );
292 const QBrush & dark = palette().dark();
293 const QBrush & light = palette().light();
295 // Draw arrow base (left)
297 painter.setBrush( dark );
298 painter.setPen( Qt::SolidLine );
299 painter.drawLine( offset, border,
300 offset, border + thickness );
303 // Draw upper outline
305 painter.drawLine( offset, border,
306 offset + length - 1, border + indent );
308 // Draw arrow point (right)
310 painter.setBrush( light );
311 painter.drawLine( offset + length - 1, border + indent,
312 offset + length - 1, border + thickness - indent );
314 // Draw lower outline
316 painter.drawLine( offset, border + thickness,
317 offset + length - 1, border + thickness - indent );
322 void YQMultiProgressMeter::drawMarkers( QPainter & painter, int offset, int thickness )
324 if ( triThickness() < 1 )
327 offset -= spacing() / 2 + 1; // integer division rounds down!
329 const QBrush & color = palette().foreground();
330 painter.setBrush( color );
331 // painter.setBrush( NoBrush );
334 // Draw upper marker triangle
336 int tri = triThickness();
338 static const QPointF points[3] =
339 { QPointF( offset - tri+1, margin() ), // top left (base)
340 QPointF( offset, margin() + tri-1 ), // lower center (point)
341 QPointF( offset + tri-1, margin() ) // top right (base)
344 painter.drawConvexPolygon( points, 3 );
347 // Draw lower marker triangle
349 int pointOffset = margin() + tri + thickness + 2 * triSpacing();
351 static const QPointF points2[3] =
352 { QPointF( offset, pointOffset ), // top center (point)
353 QPointF( offset + tri-1, pointOffset + tri-1 ), // top right (base)
354 QPointF( offset - tri+1, pointOffset + tri-1 ) // bottom left (base)
357 painter.drawConvexPolygon( points2, 3 );
361 int YQMultiProgressMeter::thickness()
363 int thickness = triangularShaped() ? 35 : 23;
364 thickness += 2 * margin();
366 if ( triThickness() > 0 )
367 thickness += 2 * triThickness() + 2 * triSpacing();
373 int YQMultiProgressMeter::length()
375 int length = 70 * segments() + 2 * margin();
381 void YQMultiProgressMeter::setTriThickness( int value )
383 _triThickness = value;
385 if ( _triThickness < 1 )
390 void YQMultiProgressMeter::setEnabled( bool enabled )
392 QWidget::setEnabled( enabled );
394 YWidget::setEnabled( enabled );
398 int YQMultiProgressMeter::preferredWidth()
400 return horizontal() ? length() : thickness();
404 int YQMultiProgressMeter::preferredHeight()
406 return horizontal() ? thickness() : length();
410 void YQMultiProgressMeter::setSize( int newWidth, int newHeight )
412 resize( newWidth, newHeight );
417 #include "YQMultiProgressMeter.moc"