]> icculus.org git repositories - duncan/yast2-qt4.git/blob - src/YQMultiProgressMeter.cc
- Don't create layouts with parent. Qt 4.x automatically reparents
[duncan/yast2-qt4.git] / src / YQMultiProgressMeter.cc
1 /*---------------------------------------------------------------------\
2 |                                                                      |
3 |                      __   __    ____ _____ ____                      |
4 |                      \ \ / /_ _/ ___|_   _|___ \                     |
5 |                       \ V / _` \___ \ | |   __) |                    |
6 |                        | | (_| |___) || |  / __/                     |
7 |                        |_|\__,_|____/ |_| |_____|                    |
8 |                                                                      |
9 |                               core system                            |
10 |                                                        (C) SuSE GmbH |
11 \----------------------------------------------------------------------/
12
13   File:       YQMultiProgressMeter.cc
14
15   Author:     Stefan Hundhammer <sh@suse.de>
16
17 /-*/
18
19
20 #define y2log_component "qt-ui"
21 #include <ycp/y2log.h>
22
23 #include <qevent.h>
24 #include <QPointF>
25
26 #include "YQUI.h"
27 #include "YQMultiProgressMeter.h"
28
29
30
31 YQMultiProgressMeter::YQMultiProgressMeter( YWidget *                   parent,
32                                             YUIDimension                dim,
33                                             const vector<float> &       maxValues )
34     : QWidget( (QWidget *) parent->widgetRep() )
35     , YMultiProgressMeter( parent, dim, maxValues )
36 {
37     _triangularShaped = false;
38     init();
39     setWidgetRep( this );
40 }
41
42
43 YQMultiProgressMeter::~YQMultiProgressMeter()
44 {
45     // NOP
46 }
47
48
49 void YQMultiProgressMeter::init()
50 {
51     _margin             = 2;
52     _segmentMinLength   = 12;
53     _triSpacing         = 1;
54
55     if ( triangularShaped() )
56     {
57         _spacing        = 0;
58         setTriThickness( -1 );
59     }
60     else
61     {
62         _spacing        = 2;
63         setTriThickness( 4 );
64     }
65 }
66
67
68 void YQMultiProgressMeter::setTriangularShaped( bool triangular )
69 {
70     _triangularShaped = triangular;
71     init();
72 }
73
74
75 void YQMultiProgressMeter::doUpdate()
76 {
77     QWidget::update();
78 }
79
80
81 void YQMultiProgressMeter::paintEvent ( QPaintEvent * event )
82 {
83     if ( ! event )
84         return;
85
86     QPainter painter( this );
87
88 //     if ( ! event->erased() )
89 //         painter.eraseRect( event->rect() );
90
91     int totalLength     = horizontal() ? width() : height();
92     int thickness       = horizontal() ? height() : width();
93
94     totalLength         -= 2 * margin() + spacing() * ( segments()-1 );
95     thickness           -= 2 * margin();
96
97     if ( triThickness() > 0 )
98         thickness -= 2 * triThickness() + 2 * triSpacing();
99
100     if ( totalLength < 1 || thickness < 1 || segments() < 1 )
101         return;
102
103
104     // Add up the total sum of all maxValues
105
106     float totalSum = 0.0;
107
108     for( int i=0; i < segments(); i++ )
109         totalSum += maxValue( i );
110
111
112     // Figure out minimal segment length
113
114     int minLength = segmentMinLength();
115
116
117     // Limit the minimum if there isn't even that much space
118
119     if ( minLength * segments() > totalLength )
120         minLength = totalLength / ( 2 * segments() );
121
122
123     // First attempt of scaling factor from values to pixel coordinates
124
125     if ( totalSum == 0.0 )
126     {
127         y2error( "Avoiding division by zero: totalSum" );
128         return;
129     }
130
131     float scale = ( (float) totalLength ) / totalSum;
132     float scaledMinLength = ( (float) minLength ) / scale;
133
134
135     // Check how many segments would become smaller than the minimum
136
137     int smallSegmentsCount = 0;
138     float restSum = 0.0;
139
140     for ( int i=0; i < segments(); i++ )
141     {
142         if ( maxValue( i ) < scaledMinLength )
143             smallSegmentsCount++;
144         else
145             restSum += maxValue( i );
146     }
147
148
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
151
152     int distributableLength = totalLength - smallSegmentsCount * minLength;
153
154     if ( restSum == 0.0 )
155     {
156         y2error( "Avoiding division by zero: restSum" );
157         return;
158     }
159
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 );
163
164
165     // Calculate indentation
166
167     int indent = triangularShaped() ? (int) ( thickness * 0.37 ) : 0;
168
169
170     // Set up painter
171
172     if ( vertical() )
173     {
174         painter.rotate( 90 );
175         painter.scale( 1.0, -1.0 );
176     }
177
178     int offset = margin();
179
180     // Draw each segment in turn
181
182     for ( int i=0; i < segments(); i++ )
183     {
184         int length;
185
186         if ( maxValue( i ) < scaledMinLength )
187             length = minLength;
188         else
189             length = (int) ( maxValue( i ) * scale + 0.5 );
190
191         drawSegment( i, painter, offset, length, thickness, indent );
192
193         if ( i > 0 )
194             drawMarkers( painter, offset, thickness );
195
196         offset += length + spacing();
197     }
198 }
199
200
201 void YQMultiProgressMeter::mouseDoubleClickEvent ( QMouseEvent * event )
202 {
203     if ( event && event->button() == Qt::RightButton )
204     {
205         // Easter egg: Switch between rectangular and triangular shape
206
207         y2milestone( "Switching shape" );
208         setTriangularShaped( ! triangularShaped() );
209         setSize( vertical()   ? preferredWidth()  : width(),
210                  horizontal() ? preferredHeight() : height() );
211         YQUI::ui()->evaluateRecalcLayout();
212         QWidget::update();
213     }
214 }
215
216
217 void YQMultiProgressMeter::drawSegment( int segment,
218                                         QPainter & painter,
219                                         int offset,
220                                         int length,
221                                         int thickness,
222                                         int indent )
223 {
224     //
225     // Fill segment
226     //
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.
230     //
231
232     int fillStart  = 0;
233     int fillHeight = 0;
234     int border = margin();
235
236     if ( triThickness() > 0 )
237         border += triThickness() + triSpacing();
238
239     if ( currentValue( segment ) < maxValue( segment ) )
240     {
241         if ( maxValue( segment ) == 0.0 )
242         {
243             y2error( "Avoiding division by zero: maxValue[%d]", segment );
244             return;
245         }
246
247         float emptyPart = 1.0 - ( currentValue( segment ) ) / ( maxValue( segment ) );
248         fillStart  = (int) ( length * emptyPart );
249         fillHeight = (int) ( indent * emptyPart );
250     }
251
252     thickness--; // We always deal with tickness-1 anyway, so let's cut this short
253
254     if ( vertical() )   // fill thermometer-like from bottom to top
255     {
256         if ( fillStart < length )
257         {
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 )
263               };
264
265             painter.setBrush( palette().highlight() );
266             painter.setPen( Qt::NoPen );
267             painter.drawConvexPolygon( points, 4 );
268         }
269     }
270     else        // horizontal - fill from left to right like a normal progress bar
271     {
272         if ( fillStart > 0 )
273         {
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 )
279               };
280
281             painter.setBrush( palette().highlight() );
282             painter.setPen( Qt::NoPen );
283             painter.drawConvexPolygon( points, 4 );
284         }
285     }
286
287
288     //
289     // Draw outline
290     //
291
292     const QBrush & dark  = palette().dark();
293     const QBrush & light = palette().light();
294
295     // Draw arrow base (left)
296
297     painter.setBrush( dark );
298     painter.setPen( Qt::SolidLine );
299     painter.drawLine( offset, border,
300                       offset, border + thickness );
301
302
303     // Draw upper outline
304
305     painter.drawLine( offset, border,
306                       offset + length - 1, border + indent );
307
308     // Draw arrow point (right)
309
310     painter.setBrush( light );
311     painter.drawLine( offset + length - 1, border + indent,
312                       offset + length - 1, border + thickness - indent );
313
314     // Draw lower outline
315
316     painter.drawLine( offset, border + thickness,
317                       offset + length - 1, border + thickness - indent );
318
319 }
320
321
322 void YQMultiProgressMeter::drawMarkers( QPainter & painter, int offset, int thickness )
323 {
324     if ( triThickness() < 1 )
325         return;
326
327     offset -= spacing() / 2 + 1;        // integer division rounds down!
328
329     const QBrush & color = palette().foreground();
330     painter.setBrush( color );
331     // painter.setBrush( NoBrush );
332
333
334     // Draw upper marker triangle
335
336     int tri = triThickness();
337       
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)
342      };
343
344     painter.drawConvexPolygon( points, 3 );
345
346
347     // Draw lower marker triangle
348
349     int pointOffset = margin() + tri + thickness + 2 * triSpacing();
350
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)
355      };
356
357     painter.drawConvexPolygon( points2, 3 );
358 }
359
360
361 int YQMultiProgressMeter::thickness()
362 {
363     int thickness = triangularShaped() ? 35 : 23;
364     thickness += 2 * margin();
365
366     if ( triThickness() > 0 )
367         thickness += 2 * triThickness() + 2 * triSpacing();
368
369     return thickness;
370 }
371
372
373 int YQMultiProgressMeter::length()
374 {
375     int length = 70 * segments() + 2 * margin();
376
377     return length;
378 }
379
380
381 void YQMultiProgressMeter::setTriThickness( int value )
382 {
383     _triThickness = value;
384
385     if ( _triThickness < 1 )
386         setTriSpacing( 0 );
387 }
388
389
390 void YQMultiProgressMeter::setEnabled( bool enabled )
391 {
392     QWidget::setEnabled( enabled );
393     QWidget::update();
394     YWidget::setEnabled( enabled );
395 }
396
397
398 int YQMultiProgressMeter::preferredWidth()
399 {
400     return horizontal() ? length() : thickness();
401 }
402
403
404 int YQMultiProgressMeter::preferredHeight()
405 {
406     return horizontal() ? thickness() : length();
407 }
408
409
410 void YQMultiProgressMeter::setSize( int newWidth, int newHeight )
411 {
412     resize( newWidth, newHeight );
413     doUpdate();
414 }
415
416
417 #include "YQMultiProgressMeter.moc"