libyui-qt  2.49.2
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 <QStyleOptionProgressBar>
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  QStyleOptionProgressBar 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  QStyleOptionProgressBar 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)
Set the thickness (base to point) of the small triangles next to the spacing between individual segme...
void setTriSpacing(int value)
Sets the spacing between the segment indicators and the small triangles next to the spacing between s...
void init()
Common initialization.
int segmentMinLength() const
Returns the minimal length of a segment in pixels.
int triThickness() const
Returns the thickness (base to point) of the small triangles next to the spacing between individual s...
int triSpacing() const
Returns the spacing between the segment indicators and the small triangles next to the spacing betwee...
virtual int preferredHeight()
Preferred height of the widget.
int spacing() const
Returns the spacing between segments in pixels.
YQMultiProgressMeter(YWidget *parent, YUIDimension dim, const vector< float > &maxValues)
Constructor.
virtual void setEnabled(bool enabled)
Set enabled/disabled state.
virtual void doUpdate()
Perform a visual update on the screen.
void drawSegment(int segment, QPainter &painter, int offset, int length, int thickness)
Draw segment number &#39;segment&#39; with pixel length &#39;length&#39; from pixel coordinate &#39;offset&#39; on and fill i...
virtual ~YQMultiProgressMeter()
Destructor.
int thickness()
Overall thickness (in pixels) of the MultiProgressMeter.
virtual void setSize(int newWidth, int newHeight)
Set the new size of the widget.
virtual int preferredWidth()
Preferred width of the widget.
int margin() const
Returns the margin around the widget contents.
virtual void paintEvent(QPaintEvent *)
Paint the widget&#39;s contents.
int length()
Overall length (in pixels) of the MultiProgressMeter.
void drawMarkers(QPainter &painter, int offset, int thickness)
Draw markers between segments (or beside that spacing).