]> icculus.org git repositories - duncan/yast2-qt4.git/blob - src/YQMultiProgressMeter.cc
YQPkgTextDialog
[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 <Qt3Support/Q3PointArray>
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             Q3PointArray points( 4 );
259             int p=0;
260
261             points.setPoint( p++, offset + fillStart,   border + fillHeight );
262             points.setPoint( p++, offset + fillStart,   border + thickness - fillHeight );
263             points.setPoint( p++, offset + length,      border + thickness - indent );
264             points.setPoint( p++, offset + length,      border + indent );
265
266             painter.setBrush( palette().highlight() );
267             painter.setPen( Qt::NoPen );
268             painter.drawConvexPolygon( points );
269         }
270     }
271     else        // horizontal - fill from left to right like a normal progress bar
272     {
273         if ( fillStart > 0 )
274         {
275             Q3PointArray points( 4 );
276             int p=0;
277
278             points.setPoint( p++, offset,               border + thickness );
279             points.setPoint( p++, offset,               border );
280             points.setPoint( p++, offset + fillStart,   border + fillHeight );
281             points.setPoint( p++, offset + fillStart,   border + thickness - fillHeight );
282
283             painter.setBrush( palette().highlight() );
284             painter.setPen( Qt::NoPen );
285             painter.drawConvexPolygon( points );
286         }
287     }
288
289
290     //
291     // Draw outline
292     //
293
294     const QBrush & dark  = palette().dark();
295     const QBrush & light = palette().light();
296
297     // Draw arrow base (left)
298
299     painter.setBrush( dark );
300     painter.setPen( Qt::SolidLine );
301     painter.drawLine( offset, border,
302                       offset, border + thickness );
303
304
305     // Draw upper outline
306
307     painter.drawLine( offset, border,
308                       offset + length - 1, border + indent );
309
310     // Draw arrow point (right)
311
312     painter.setBrush( light );
313     painter.drawLine( offset + length - 1, border + indent,
314                       offset + length - 1, border + thickness - indent );
315
316     // Draw lower outline
317
318     painter.drawLine( offset, border + thickness,
319                       offset + length - 1, border + thickness - indent );
320
321 }
322
323
324 void YQMultiProgressMeter::drawMarkers( QPainter & painter, int offset, int thickness )
325 {
326     if ( triThickness() < 1 )
327         return;
328
329     offset -= spacing() / 2 + 1;        // integer division rounds down!
330
331     const QBrush & color = palette().foreground();
332     painter.setBrush( color );
333     // painter.setBrush( NoBrush );
334
335
336     // Draw upper marker triangle
337
338     int tri = triThickness();
339     Q3PointArray points( 3 );
340
341     int p=0;
342     points.setPoint( p++, offset - tri+1,       margin() );             // top left (base)
343     points.setPoint( p++, offset,               margin() + tri-1 );     // lower center (point)
344     points.setPoint( p++, offset + tri-1,       margin() );             // top right (base)
345
346     painter.drawConvexPolygon( points );
347
348
349     // Draw lower marker triangle
350
351     int pointOffset = margin() + tri + thickness + 2 * triSpacing();
352
353     p=0;
354     points.setPoint( p++, offset,               pointOffset );          // top center (point)
355     points.setPoint( p++, offset + tri-1,       pointOffset + tri-1 );  // top right (base)
356     points.setPoint( p++, offset - tri+1,       pointOffset + tri-1 );  // bottom left (base)
357
358     painter.drawConvexPolygon( points );
359 }
360
361
362 int YQMultiProgressMeter::thickness()
363 {
364     int thickness = triangularShaped() ? 35 : 23;
365     thickness += 2 * margin();
366
367     if ( triThickness() > 0 )
368         thickness += 2 * triThickness() + 2 * triSpacing();
369
370     return thickness;
371 }
372
373
374 int YQMultiProgressMeter::length()
375 {
376     int length = 70 * segments() + 2 * margin();
377
378     return length;
379 }
380
381
382 void YQMultiProgressMeter::setTriThickness( int value )
383 {
384     _triThickness = value;
385
386     if ( _triThickness < 1 )
387         setTriSpacing( 0 );
388 }
389
390
391 void YQMultiProgressMeter::setEnabled( bool enabled )
392 {
393     QWidget::setEnabled( enabled );
394     QWidget::update();
395     YWidget::setEnabled( enabled );
396 }
397
398
399 int YQMultiProgressMeter::preferredWidth()
400 {
401     return horizontal() ? length() : thickness();
402 }
403
404
405 int YQMultiProgressMeter::preferredHeight()
406 {
407     return horizontal() ? thickness() : length();
408 }
409
410
411 void YQMultiProgressMeter::setSize( int newWidth, int newHeight )
412 {
413     resize( newWidth, newHeight );
414     doUpdate();
415 }
416
417
418 #include "YQMultiProgressMeter.moc"