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