libyui-qt  2.53.0
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 using std::endl;
38 
39 
40 
42  YUIDimension dim,
43  const vector<float> & maxValues )
44  : QWidget( (QWidget *) parent->widgetRep() )
45  , YMultiProgressMeter( parent, dim, maxValues )
46 {
47  init();
48  setWidgetRep( this );
49 }
50 
51 
53 {
54  // NOP
55 }
56 
57 
59 {
60  _margin = 2;
61  _segmentMinLength = 12;
62  _triSpacing = 1;
63  _spacing = 2;
64  setTriThickness( 4 );
65 }
66 
67 
69 {
70  QWidget::update();
71 }
72 
73 
74 void YQMultiProgressMeter::paintEvent ( QPaintEvent * event )
75 {
76  if ( ! event )
77  return;
78 
79  QPainter painter( this );
80 
81 // if ( ! event->erased() )
82 // painter.eraseRect( event->rect() );
83 
84  int totalLength = horizontal() ? width() : height();
85  int thickness = horizontal() ? height() : width();
86 
87  totalLength -= 2 * margin() + spacing() * ( segments()-1 );
88  thickness -= 2 * margin();
89 
90  if ( triThickness() > 0 )
91  thickness -= 2 * triThickness() + 2 * triSpacing();
92 
93  if ( totalLength < 1 || thickness < 1 || segments() < 1 )
94  return;
95 
96 
97  // Add up the total sum of all maxValues
98 
99  float totalSum = 0.0;
100 
101  for( int i=0; i < segments(); i++ )
102  totalSum += maxValue( i );
103 
104 
105  // Figure out minimal segment length
106 
107  int minLength = segmentMinLength();
108 
109 
110  // Limit the minimum if there isn't even that much space
111 
112  if ( minLength * segments() > totalLength )
113  minLength = totalLength / ( 2 * segments() );
114 
115 
116  // First attempt of scaling factor from values to pixel coordinates
117 
118  if ( totalSum == 0.0 )
119  {
120  yuiError() << "Avoiding division by zero: totalSum" << endl;
121  return;
122  }
123 
124  float scale = ( (float) totalLength ) / totalSum;
125  float scaledMinLength = ( (float) minLength ) / scale;
126 
127 
128  // Check how many segments would become smaller than the minimum
129 
130  int smallSegmentsCount = 0;
131  float restSum = 0.0;
132 
133  for ( int i=0; i < segments(); i++ )
134  {
135  if ( maxValue( i ) < scaledMinLength )
136  smallSegmentsCount++;
137  else
138  restSum += maxValue( i );
139  }
140 
141 
142  // Small segments that get at least minLength pixels consume more screen
143  // space than initially planned, so recompute what is left for the others.
144 
145  int distributableLength = totalLength - smallSegmentsCount * minLength;
146 
147  if ( restSum == 0.0 )
148  {
149  yuiError() << "Avoiding division by zero: restSum" << endl;
150  return;
151  }
152 
153  // Recompute scale to take small segments into account that now get screen
154  // space disproportional to their real size (maxValue).
155  scale = ( (float) distributableLength ) / ( restSum );
156 
157  // Set up painter
158 
159  if ( vertical() )
160  {
161  painter.rotate( 90 );
162  painter.scale( 1.0, -1.0 );
163  }
164 
165  int offset = margin();
166 
167  // Draw each segment in turn
168 
169  for ( int i=0; i < segments(); i++ )
170  {
171  int length;
172 
173  if ( maxValue( i ) < scaledMinLength )
174  length = minLength;
175  else
176  length = (int) ( maxValue( i ) * scale + 0.5 );
177 
178  drawSegment( i, painter, offset, length, thickness );
179 
180  if ( i > 0 )
181  drawMarkers( painter, offset, thickness );
182 
183  offset += length + spacing();
184  }
185 }
186 
187 
189  QPainter & painter,
190  int offset,
191  int length,
192  int thickness )
193 {
194  //
195  // Fill segment
196  //
197  // Vertical MultiProgressMeters will be filled thermometer-like from bottom
198  // to top, horizontal ones like normal progress bars from left to right,
199  // i.e. just the opposite way.
200  //
201 
202  int border = margin();
203 
204  if ( triThickness() > 0 )
205  border += triThickness() + triSpacing();
206 
207  if ( maxValue( segment ) == 0.0 )
208  {
209  yuiError() << "Avoiding division by zero: maxValue[" << segment << "]" << endl;
210  return;
211  }
212 
213  // Use 0..1000 range to avoid overflow with huge numbers (Gigabytes).
214  const int scaledMax = 1000;
215  int scaledProgress =
216  (int) ( 0.5 + ( currentValue( segment ) / maxValue( segment ) ) * ( (float) scaledMax ) );
217 
218  if ( vertical() ) // fill thermometer-like from bottom to top
219  {
220  QStyleOptionProgressBar opts;
221  opts.initFrom(this);
222  opts.progress = scaledMax - scaledProgress;
223  opts.minimum = 0;
224  opts.maximum = scaledMax;
225  opts.invertedAppearance = true;
226  opts.rect = QRect( offset, border, length, thickness );
227  style()->drawControl(QStyle::CE_ProgressBarGroove, &opts, &painter, this);
228 
229  if ( opts.progress > 0 )
230  style()->drawControl(QStyle::CE_ProgressBarContents, &opts, &painter, this);
231  }
232  else // horizontal - fill from left to right like a normal progress bar
233  {
234  QStyleOptionProgressBar opts;
235  opts.initFrom(this);
236  opts.progress = scaledProgress;
237  opts.minimum = 0;
238  opts.maximum = scaledMax;
239  opts.rect = QRect( offset, border, length, thickness );
240 
241  style()->drawControl(QStyle::CE_ProgressBarGroove, &opts, &painter, this);
242  if ( opts.progress > 0 )
243  style()->drawControl(QStyle::CE_ProgressBarContents, &opts, &painter, this);
244  }
245 }
246 
247 
248 void YQMultiProgressMeter::drawMarkers( QPainter & painter, int offset, int thickness )
249 {
250  if ( triThickness() < 1 )
251  return;
252 
253  offset -= spacing() / 2 + 1; // integer division rounds down!
254 
255  const QBrush & color = palette().windowText();
256  painter.setBrush( color );
257  // painter.setBrush( NoBrush );
258 
259 
260  // Draw upper marker triangle
261 
262  int tri = triThickness();
263 
264  QPointF points[3] =
265  {
266  QPointF( offset - tri+1, margin() ), // top left (base)
267  QPointF( offset, margin() + tri-1 ), // lower center (point)
268  QPointF( offset + tri-1, margin() ) // top right (base)
269  };
270 
271  painter.drawConvexPolygon( points, 3 );
272 
273  // Draw lower marker triangle
274 
275  int pointOffset = margin() + tri + thickness + 2 * triSpacing();
276 
277  QPointF points2[3] =
278  {
279  QPointF( offset, pointOffset ), // top center (point)
280  QPointF( offset + tri-1, pointOffset + tri-1 ), // top right (base)
281  QPointF( offset - tri+1, pointOffset + tri-1 ) // bottom left (base)
282  };
283 
284  painter.drawConvexPolygon( points2, 3 );
285 }
286 
287 
289 {
290  int thickness = 23;
291  thickness += 2 * margin();
292 
293  if ( triThickness() > 0 )
294  thickness += 2 * triThickness() + 2 * triSpacing();
295 
296  return thickness;
297 }
298 
299 
301 {
302  int length = 70 * segments() + 2 * margin();
303 
304  return length;
305 }
306 
307 
309 {
310  _triThickness = value;
311 
312  if ( _triThickness < 1 )
313  setTriSpacing( 0 );
314 }
315 
316 
318 {
319  QWidget::setEnabled( enabled );
320  QWidget::update();
321  YWidget::setEnabled( enabled );
322 }
323 
324 
326 {
327  return horizontal() ? length() : thickness();
328 }
329 
330 
332 {
333  return horizontal() ? thickness() : length();
334 }
335 
336 
337 void YQMultiProgressMeter::setSize( int newWidth, int newHeight )
338 {
339  resize( newWidth, newHeight );
340  doUpdate();
341 }
void drawMarkers(QPainter &painter, int offset, int thickness)
Draw markers between segments (or beside that spacing).
virtual ~YQMultiProgressMeter()
Destructor.
void drawSegment(int segment, QPainter &painter, int offset, int length, int thickness)
Draw segment number 'segment' with pixel length 'length' from pixel coordinate 'offset' on and fill i...
int triSpacing() const
Returns the spacing between the segment indicators and the small triangles next to the spacing betwee...
int triThickness() const
Returns the thickness (base to point) of the small triangles next to the spacing between individual s...
int segmentMinLength() const
Returns the minimal length of a segment in pixels.
void init()
Common initialization.
int thickness()
Overall thickness (in pixels) of the MultiProgressMeter.
YQMultiProgressMeter(YWidget *parent, YUIDimension dim, const vector< float > &maxValues)
Constructor.
int margin() const
Returns the margin around the widget contents.
void setTriSpacing(int value)
Sets the spacing between the segment indicators and the small triangles next to the spacing between s...
virtual void setSize(int newWidth, int newHeight)
Set the new size of the widget.
virtual void setEnabled(bool enabled)
Set enabled/disabled state.
virtual void doUpdate()
Perform a visual update on the screen.
void setTriThickness(int value)
Set the thickness (base to point) of the small triangles next to the spacing between individual segme...
int spacing() const
Returns the spacing between segments in pixels.
virtual void paintEvent(QPaintEvent *)
Paint the widget's contents.
virtual int preferredWidth()
Preferred width of the widget.
virtual int preferredHeight()
Preferred height of the widget.
int length()
Overall length (in pixels) of the MultiProgressMeter.