libyui-qt  2.46.1
 All Classes Functions Variables
YQMultiProgressMeter.cc
1 /*
2  Copyright (C) 2000-2012 Novell, Inc
3  This library is free software; you can redistribute it and/or modify
4  it under the terms of the GNU Lesser General Public License as
5  published by the Free Software Foundation; either version 2.1 of the
6  License, or (at your option) version 3.0 of the License. This library
7  is distributed in the hope that it will be useful, but WITHOUT ANY
8  WARRANTY; without even the implied warranty of MERCHANTABILITY or
9  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
10  License for more details. You should have received a copy of the GNU
11  Lesser General Public License along with this library; if not, write
12  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
13  Floor, Boston, MA 02110-1301 USA
14 */
15 
16 
17 /*-/
18 
19  File: YQMultiProgressMeter.cc
20 
21  Author: Stefan Hundhammer <sh@suse.de>
22 
23 /-*/
24 
25 
26 #define YUILogComponent "qt-ui"
27 #include <yui/YUILog.h>
28 
29 #include <qevent.h>
30 #include <QPointF>
31 #include <QStyleOptionProgressBarV2>
32 #include <QDebug>
33 #include "YQUI.h"
34 #include "YQMultiProgressMeter.h"
35 #include <yui/YDialog.h>
36 
37 
38 
40  YUIDimension dim,
41  const vector<float> & maxValues )
42  : QWidget( (QWidget *) parent->widgetRep() )
43  , YMultiProgressMeter( parent, dim, maxValues )
44 {
45  init();
46  setWidgetRep( this );
47 }
48 
49 
51 {
52  // NOP
53 }
54 
55 
57 {
58  _margin = 2;
59  _segmentMinLength = 12;
60  _triSpacing = 1;
61  _spacing = 2;
62  setTriThickness( 4 );
63 }
64 
65 
67 {
68  QWidget::update();
69 }
70 
71 
72 void YQMultiProgressMeter::paintEvent ( QPaintEvent * event )
73 {
74  if ( ! event )
75  return;
76 
77  QPainter painter( this );
78 
79 // if ( ! event->erased() )
80 // painter.eraseRect( event->rect() );
81 
82  int totalLength = horizontal() ? width() : height();
83  int thickness = horizontal() ? height() : width();
84 
85  totalLength -= 2 * margin() + spacing() * ( segments()-1 );
86  thickness -= 2 * margin();
87 
88  if ( triThickness() > 0 )
89  thickness -= 2 * triThickness() + 2 * triSpacing();
90 
91  if ( totalLength < 1 || thickness < 1 || segments() < 1 )
92  return;
93 
94 
95  // Add up the total sum of all maxValues
96 
97  float totalSum = 0.0;
98 
99  for( int i=0; i < segments(); i++ )
100  totalSum += maxValue( i );
101 
102 
103  // Figure out minimal segment length
104 
105  int minLength = segmentMinLength();
106 
107 
108  // Limit the minimum if there isn't even that much space
109 
110  if ( minLength * segments() > totalLength )
111  minLength = totalLength / ( 2 * segments() );
112 
113 
114  // First attempt of scaling factor from values to pixel coordinates
115 
116  if ( totalSum == 0.0 )
117  {
118  yuiError() << "Avoiding division by zero: totalSum" << std::endl;
119  return;
120  }
121 
122  float scale = ( (float) totalLength ) / totalSum;
123  float scaledMinLength = ( (float) minLength ) / scale;
124 
125 
126  // Check how many segments would become smaller than the minimum
127 
128  int smallSegmentsCount = 0;
129  float restSum = 0.0;
130 
131  for ( int i=0; i < segments(); i++ )
132  {
133  if ( maxValue( i ) < scaledMinLength )
134  smallSegmentsCount++;
135  else
136  restSum += maxValue( i );
137  }
138 
139 
140  // Small segments that get at least minLength pixels consume more screen
141  // space than initially planned, so recompute what is left for the others.
142 
143  int distributableLength = totalLength - smallSegmentsCount * minLength;
144 
145  if ( restSum == 0.0 )
146  {
147  yuiError() << "Avoiding division by zero: restSum" << std::endl;
148  return;
149  }
150 
151  // Recompute scale to take small segments into account that now get screen
152  // space disproportional to their real size (maxValue).
153  scale = ( (float) distributableLength ) / ( restSum );
154 
155  // Set up painter
156 
157  if ( vertical() )
158  {
159  painter.rotate( 90 );
160  painter.scale( 1.0, -1.0 );
161  }
162 
163  int offset = margin();
164 
165  // Draw each segment in turn
166 
167  for ( int i=0; i < segments(); i++ )
168  {
169  int length;
170 
171  if ( maxValue( i ) < scaledMinLength )
172  length = minLength;
173  else
174  length = (int) ( maxValue( i ) * scale + 0.5 );
175 
176  drawSegment( i, painter, offset, length, thickness );
177 
178  if ( i > 0 )
179  drawMarkers( painter, offset, thickness );
180 
181  offset += length + spacing();
182  }
183 }
184 
185 
187  QPainter & painter,
188  int offset,
189  int length,
190  int thickness )
191 {
192  //
193  // Fill segment
194  //
195  // Vertical MultiProgressMeters will be filled thermometer-like from bottom
196  // to top, horizontal ones like normal progress bars from left to right,
197  // i.e. just the opposite way.
198  //
199 
200  int border = margin();
201 
202  if ( triThickness() > 0 )
203  border += triThickness() + triSpacing();
204 
205  if ( maxValue( segment ) == 0.0 )
206  {
207  yuiError() << "Avoiding division by zero: maxValue[" << segment << "]" << std::endl;
208  return;
209  }
210 
211  // Use 0..1000 range to avoid overflow with huge numbers (Gigabytes).
212  const int scaledMax = 1000;
213  int scaledProgress =
214  (int) ( 0.5 + ( currentValue( segment ) / maxValue( segment ) ) * ( (float) scaledMax ) );
215 
216  if ( vertical() ) // fill thermometer-like from bottom to top
217  {
218  QStyleOptionProgressBarV2 opts;
219  opts.initFrom(this);
220  opts.progress = scaledMax - scaledProgress;
221  opts.minimum = 0;
222  opts.maximum = scaledMax;
223  opts.invertedAppearance = true;
224  opts.rect = QRect( offset, border, length, thickness );
225  style()->drawControl(QStyle::CE_ProgressBarGroove, &opts, &painter, this);
226 
227  if ( opts.progress > 0 )
228  style()->drawControl(QStyle::CE_ProgressBarContents, &opts, &painter, this);
229  }
230  else // horizontal - fill from left to right like a normal progress bar
231  {
232  QStyleOptionProgressBarV2 opts;
233  opts.initFrom(this);
234  opts.progress = scaledProgress;
235  opts.minimum = 0;
236  opts.maximum = scaledMax;
237  opts.rect = QRect( offset, border, length, thickness );
238 
239  style()->drawControl(QStyle::CE_ProgressBarGroove, &opts, &painter, this);
240  if ( opts.progress > 0 )
241  style()->drawControl(QStyle::CE_ProgressBarContents, &opts, &painter, this);
242  }
243 }
244 
245 
246 void YQMultiProgressMeter::drawMarkers( QPainter & painter, int offset, int thickness )
247 {
248  if ( triThickness() < 1 )
249  return;
250 
251  offset -= spacing() / 2 + 1; // integer division rounds down!
252 
253  const QBrush & color = palette().foreground();
254  painter.setBrush( color );
255  // painter.setBrush( NoBrush );
256 
257 
258  // Draw upper marker triangle
259 
260  int tri = triThickness();
261 
262  QPointF points[3] =
263  {
264  QPointF( offset - tri+1, margin() ), // top left (base)
265  QPointF( offset, margin() + tri-1 ), // lower center (point)
266  QPointF( offset + tri-1, margin() ) // top right (base)
267  };
268 
269  painter.drawConvexPolygon( points, 3 );
270 
271  // Draw lower marker triangle
272 
273  int pointOffset = margin() + tri + thickness + 2 * triSpacing();
274 
275  QPointF points2[3] =
276  {
277  QPointF( offset, pointOffset ), // top center (point)
278  QPointF( offset + tri-1, pointOffset + tri-1 ), // top right (base)
279  QPointF( offset - tri+1, pointOffset + tri-1 ) // bottom left (base)
280  };
281 
282  painter.drawConvexPolygon( points2, 3 );
283 }
284 
285 
287 {
288  int thickness = 23;
289  thickness += 2 * margin();
290 
291  if ( triThickness() > 0 )
292  thickness += 2 * triThickness() + 2 * triSpacing();
293 
294  return thickness;
295 }
296 
297 
299 {
300  int length = 70 * segments() + 2 * margin();
301 
302  return length;
303 }
304 
305 
307 {
308  _triThickness = value;
309 
310  if ( _triThickness < 1 )
311  setTriSpacing( 0 );
312 }
313 
314 
316 {
317  QWidget::setEnabled( enabled );
318  QWidget::update();
319  YWidget::setEnabled( enabled );
320 }
321 
322 
324 {
325  return horizontal() ? length() : thickness();
326 }
327 
328 
330 {
331  return horizontal() ? thickness() : length();
332 }
333 
334 
335 void YQMultiProgressMeter::setSize( int newWidth, int newHeight )
336 {
337  resize( newWidth, newHeight );
338  doUpdate();
339 }
340 
341 
342 #include "YQMultiProgressMeter.moc"
void setTriThickness(int value)
void setTriSpacing(int value)
YQMultiProgressMeter(YWidget *parent, YUIDimension dim, const vector< float > &maxValues)
virtual void setEnabled(bool enabled)
void drawSegment(int segment, QPainter &painter, int offset, int length, int thickness)
virtual void setSize(int newWidth, int newHeight)
virtual void paintEvent(QPaintEvent *)
void drawMarkers(QPainter &painter, int offset, int thickness)