]> icculus.org git repositories - duncan/yast2-qt4.git/blob - src/YQMultiProgressMeter.cc
clicking packages work! so the package selector is now
[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 #include <QStyleOptionProgressBarV2>
26 #include <QDebug>
27 #include "YQUI.h"
28 #include "YQMultiProgressMeter.h"
29
30
31
32 YQMultiProgressMeter::YQMultiProgressMeter( YWidget *                   parent,
33                                             YUIDimension                dim,
34                                             const vector<float> &       maxValues )
35     : QWidget( (QWidget *) parent->widgetRep() )
36     , YMultiProgressMeter( parent, dim, maxValues )
37 {
38     _triangularShaped = false;
39     init();
40     setWidgetRep( this );
41 }
42
43
44 YQMultiProgressMeter::~YQMultiProgressMeter()
45 {
46     // NOP
47 }
48
49
50 void YQMultiProgressMeter::init()
51 {
52     _margin             = 2;
53     _segmentMinLength   = 12;
54     _triSpacing         = 1;
55
56     if ( triangularShaped() )
57     {
58         _spacing        = 0;
59         setTriThickness( -1 );
60     }
61     else
62     {
63         _spacing        = 2;
64         setTriThickness( 4 );
65     }
66 }
67
68
69 void YQMultiProgressMeter::setTriangularShaped( bool triangular )
70 {
71     _triangularShaped = triangular;
72     init();
73 }
74
75
76 void YQMultiProgressMeter::doUpdate()
77 {
78     QWidget::update();
79 }
80
81
82 void YQMultiProgressMeter::paintEvent ( QPaintEvent * event )
83 {
84     if ( ! event )
85         return;
86
87     QPainter painter( this );
88
89 //     if ( ! event->erased() )
90 //         painter.eraseRect( event->rect() );
91
92     int totalLength     = horizontal() ? width() : height();
93     int thickness       = horizontal() ? height() : width();
94
95     totalLength         -= 2 * margin() + spacing() * ( segments()-1 );
96     thickness           -= 2 * margin();
97
98     if ( triThickness() > 0 )
99         thickness -= 2 * triThickness() + 2 * triSpacing();
100
101     if ( totalLength < 1 || thickness < 1 || segments() < 1 )
102         return;
103
104
105     // Add up the total sum of all maxValues
106
107     float totalSum = 0.0;
108
109     for( int i=0; i < segments(); i++ )
110         totalSum += maxValue( i );
111
112
113     // Figure out minimal segment length
114
115     int minLength = segmentMinLength();
116
117
118     // Limit the minimum if there isn't even that much space
119
120     if ( minLength * segments() > totalLength )
121         minLength = totalLength / ( 2 * segments() );
122
123
124     // First attempt of scaling factor from values to pixel coordinates
125
126     if ( totalSum == 0.0 )
127     {
128         y2error( "Avoiding division by zero: totalSum" );
129         return;
130     }
131
132     float scale = ( (float) totalLength ) / totalSum;
133     float scaledMinLength = ( (float) minLength ) / scale;
134
135
136     // Check how many segments would become smaller than the minimum
137
138     int smallSegmentsCount = 0;
139     float restSum = 0.0;
140
141     for ( int i=0; i < segments(); i++ )
142     {
143         if ( maxValue( i ) < scaledMinLength )
144             smallSegmentsCount++;
145         else
146             restSum += maxValue( i );
147     }
148
149
150     // Small segments that get at least minLength pixels consume more screen
151     // space than initially planned, so recompute what is left for the others
152
153     int distributableLength = totalLength - smallSegmentsCount * minLength;
154
155     if ( restSum == 0.0 )
156     {
157         y2error( "Avoiding division by zero: restSum" );
158         return;
159     }
160
161     // Recompute scale to take small segments into account that now get screen
162     // space disproportional to their real size (maxValue).
163     scale = ( (float) distributableLength ) / ( restSum );
164
165
166     // Calculate indentation
167
168     int indent = triangularShaped() ? (int) ( thickness * 0.37 ) : 0;
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 border = margin();
233
234     if ( triThickness() > 0 )
235         border += triThickness() + triSpacing();
236
237     if ( maxValue( segment ) == 0.0 )
238     {
239         y2error( "Avoiding division by zero: maxValue[%d]", segment );
240         return;
241     }
242
243     if ( vertical() )   // fill thermometer-like from bottom to top
244     {
245         QStyleOptionProgressBarV2 opts;
246         opts.initFrom(this);
247         opts.progress = currentValue( segment);
248         opts.minimum = 0;
249         opts.maximum = maxValue( segment);
250         opts.invertedAppearance = true;
251         opts.rect = QRect( offset, border, length, thickness );
252         style()->drawControl(QStyle::CE_ProgressBarGroove, &opts, &painter, this);
253
254         if ( opts.progress > 0 )
255             style()->drawControl(QStyle::CE_ProgressBarContents, &opts, &painter, this);
256     }
257     else        // horizontal - fill from left to right like a normal progress bar
258     {
259         QStyleOptionProgressBarV2 opts;
260         opts.initFrom(this);
261         opts.progress = maxValue( segment) - currentValue( segment);
262         opts.minimum = 0;
263         opts.maximum = maxValue( segment);
264         opts.rect = QRect( offset, border, length, thickness );
265
266         style()->drawControl(QStyle::CE_ProgressBarGroove, &opts, &painter, this);
267         if ( opts.progress > 0 )
268             style()->drawControl(QStyle::CE_ProgressBarContents, &opts, &painter, this);
269     }
270 }
271
272
273 void YQMultiProgressMeter::drawMarkers( QPainter & painter, int offset, int thickness )
274 {
275     if ( triThickness() < 1 )
276         return;
277
278     offset -= spacing() / 2 + 1;        // integer division rounds down!
279
280     const QBrush & color = palette().foreground();
281     painter.setBrush( color );
282     // painter.setBrush( NoBrush );
283
284
285     // Draw upper marker triangle
286
287     int tri = triThickness();
288
289     QPointF points[3] =
290      { QPointF( offset - tri+1, margin() ),             // top left (base)
291        QPointF( offset,         margin() + tri-1 ),     // lower center (point)
292        QPointF( offset + tri-1,         margin() )              // top right (base)
293      };
294
295     painter.drawConvexPolygon( points, 3 );
296
297     // Draw lower marker triangle
298
299     int pointOffset = margin() + tri + thickness + 2 * triSpacing();
300
301     QPointF points2[3] =
302      { QPointF( offset,         pointOffset ),          // top center (point)
303        QPointF( offset + tri-1, pointOffset + tri-1 ),  // top right (base)
304        QPointF( offset - tri+1, pointOffset + tri-1 )   // bottom left (base)
305      };
306
307     painter.drawConvexPolygon( points2, 3 );
308 }
309
310
311 int YQMultiProgressMeter::thickness()
312 {
313     int thickness = triangularShaped() ? 35 : 23;
314     thickness += 2 * margin();
315
316     if ( triThickness() > 0 )
317         thickness += 2 * triThickness() + 2 * triSpacing();
318
319     return thickness;
320 }
321
322
323 int YQMultiProgressMeter::length()
324 {
325     int length = 70 * segments() + 2 * margin();
326
327     return length;
328 }
329
330
331 void YQMultiProgressMeter::setTriThickness( int value )
332 {
333     _triThickness = value;
334
335     if ( _triThickness < 1 )
336         setTriSpacing( 0 );
337 }
338
339
340 void YQMultiProgressMeter::setEnabled( bool enabled )
341 {
342     QWidget::setEnabled( enabled );
343     QWidget::update();
344     YWidget::setEnabled( enabled );
345 }
346
347
348 int YQMultiProgressMeter::preferredWidth()
349 {
350     return horizontal() ? length() : thickness();
351 }
352
353
354 int YQMultiProgressMeter::preferredHeight()
355 {
356     return horizontal() ? thickness() : length();
357 }
358
359
360 void YQMultiProgressMeter::setSize( int newWidth, int newHeight )
361 {
362     resize( newWidth, newHeight );
363     doUpdate();
364 }
365
366
367 #include "YQMultiProgressMeter.moc"