picking up branches/tmp/sh/qt4-port/, merging it with trunk
[duncan/yast2-qt4.git] / src / QY2DiskUsageList.cc
1 /*---------------------------------------------------------------------\
2 |                                                                      |
3 |                      __   __    ____ _____ ____                      |
4 |                      \ \ / /_ _/ ___|_   _|___ \                     |
5 |                       \ V / _` \___ \ | |   __) |                    |
6 |                        | | (_| |___) || |  / __/                     |
7 |                        |_|\__,_|____/ |_| |_____|                    |
8 |                                                                      |
9 |                               core system                            |
10 |                                                        (C) SuSE GmbH |
11 \----------------------------------------------------------------------/
12
13   File:       QY2DiskUsageList.cc
14
15   Author:     Stefan Hundhammer <sh@suse.de>
16
17   Textdomain "packages-qt"
18
19   This is a pure Qt widget - it can be used independently of YaST2.
20
21
22 /-*/
23
24
25 #include "QY2DiskUsageList.h"
26 #include "YQi18n.h"
27 #include "qpainter.h"
28
29
30 QY2DiskUsageList::QY2DiskUsageList( QWidget * parent, bool addStdColumns )
31     : QY2ListView( parent )
32 {
33     _nameCol            = -42;
34     _percentageBarCol   = -42;
35     _percentageCol      = -42;
36     _usedSizeCol        = -42;
37     _freeSizeCol        = -42;
38     _totalSizeCol       = -42;
39     _deviceNameCol      = -42;
40
41     if ( addStdColumns )
42     {
43         int numCol = 0;
44         addColumn( _( "Name"            ) );    _nameCol                = numCol++;
45
46         // Translators: Please keep this short!
47         addColumn( _( "Disk Usage"      ) );    _percentageBarCol       = numCol++;
48         addColumn( ""                     );    _percentageCol          = numCol++;
49         addColumn( _( "Used"            ) );    _usedSizeCol            = numCol++;
50         addColumn( _( "Free"            ) );    _freeSizeCol            = numCol++;
51         addColumn( _( "Total"           ) );    _totalSizeCol           = numCol++;
52 #if 0
53         addColumn( _( "Device"          ) );    _deviceNameCol          = numCol++;
54 #endif
55
56         setColumnAlignment( percentageCol(),    Qt::AlignRight );
57         setColumnAlignment( usedSizeCol(),      Qt::AlignRight );
58         setColumnAlignment( freeSizeCol(),      Qt::AlignRight );
59         setColumnAlignment( totalSizeCol(),     Qt::AlignRight );
60
61         setSorting( percentageBarCol() );
62     }
63
64     saveColumnWidths();
65     setSelectionMode( Q3ListView::NoSelection );
66 }
67
68
69 QY2DiskUsageList::~QY2DiskUsageList()
70 {
71 }
72
73
74
75
76
77
78 QY2DiskUsageListItem::QY2DiskUsageListItem( QY2DiskUsageList * parent )
79     : QY2ListViewItem( parent )
80     , _diskUsageList( parent )
81 {
82 }
83
84
85
86
87 QY2DiskUsageListItem::~QY2DiskUsageListItem()
88 {
89     // NOP
90 }
91
92
93
94
95 void
96 QY2DiskUsageListItem::init( bool allFields )
97 {
98     if ( percentageCol()        >= 0 )
99     {
100         QString percentageText;
101         percentageText.sprintf( "%d%%", usedPercent() );
102         setText( percentageCol(), percentageText );
103     }
104
105     if ( usedSizeCol()          >= 0 ) setText( usedSizeCol(),          usedSize()      );
106     if ( freeSizeCol()          >= 0 ) setText( freeSizeCol(),          freeSize()      );
107
108     if ( allFields )
109     {
110         if ( totalSizeCol()     >= 0 ) setText( totalSizeCol(),         totalSize()     );
111         if ( nameCol()          >= 0 ) setText( nameCol(),              " " + name()    );
112         if ( deviceNameCol()    >= 0 ) setText( deviceNameCol(),        deviceName()    );
113     }
114 }
115
116
117 void
118 QY2DiskUsageListItem::setText( int column, const FSize & size )
119 {
120     QString sizeText = size.form( 0, 1, true ).c_str();
121     sizeText += " ";
122     setText( column, sizeText );
123 }
124
125
126 FSize
127 QY2DiskUsageListItem::freeSize() const
128 {
129     return totalSize() - usedSize();
130 }
131
132
133 int
134 QY2DiskUsageListItem::usedPercent() const
135 {
136     int percent = 0;
137
138     if ( totalSize() != 0 )
139         percent = ( 100 * usedSize() ) / totalSize();
140
141     return percent;
142 }
143
144
145 void
146 QY2DiskUsageListItem::updateStatus()
147 {
148     init( false );
149 }
150
151
152 void
153 QY2DiskUsageListItem::updateData()
154 {
155     init( true );
156 }
157
158
159
160
161
162 /**
163  * Comparison function used for sorting the list.
164  * Returns:
165  * -1 if this <  other
166  *  0 if this == other
167  * +1 if this >  other
168  **/
169 int
170 QY2DiskUsageListItem::compare( Q3ListViewItem * otherListViewItem,
171                                int              col,
172                                bool             ascending ) const
173 {
174     QY2DiskUsageListItem * other = dynamic_cast<QY2DiskUsageListItem *> (otherListViewItem);
175
176     if ( other )
177     {
178         if ( col == percentageCol()    ||
179              col == percentageBarCol()   )
180         {
181             // Intentionally reverting sort order: Fullest first
182
183             if ( this->usedPercent() < other->usedPercent() )   return  1;
184             if ( this->usedPercent() > other->usedPercent() )   return -1;
185             return 0;
186         }
187         else if ( col == usedSizeCol() )
188         {
189             if ( this->usedSize() < other->usedSize() )         return -1;
190             if ( this->usedSize() > other->usedSize() )         return  1;
191             return 0;
192         }
193         else if ( col == freeSizeCol() )
194         {
195             if ( this->freeSize() < other->freeSize() )         return -1;
196             if ( this->freeSize() > other->freeSize() )         return  1;
197             return 0;
198         }
199         else if ( col == totalSizeCol() )
200         {
201             if ( this->totalSize() < other->totalSize() )       return -1;
202             if ( this->totalSize() > other->totalSize() )       return  1;
203             return 0;
204         }
205     }
206
207     return QY2ListViewItem::compare( otherListViewItem, col, ascending );
208 }
209
210
211 void
212 QY2DiskUsageListItem::paintCell( QPainter *             painter,
213                                  const QColorGroup &    colorGroup,
214                                  int                    column,
215                                  int                    width,
216                                  int                    alignment )
217 {
218     if ( column == percentageBarCol() )
219     {
220         QColor background = colorGroup.base();
221         painter->setBackgroundColor( background );
222
223         paintPercentageBar( usedPercent(),
224                             painter,
225                             _diskUsageList->treeStepSize() * depth(),
226                             width,
227                             interpolateColor( usedPercent(),
228                                               60, 95,
229                                               QColor( 0, 0x80, 0 ),     // Medium dark green
230                                               QColor( 0xFF, 0, 0 ) ),   // Bright red
231                             background.dark( 115 ) );
232
233     }
234     else
235     {
236         QColorGroup cg( colorGroup );
237
238         if ( usedSize() > totalSize() )
239             cg.setColor( QColorGroup::Text, Qt::red );          // Set red text foreground
240
241         // Intentionally bypassing the direct parent class method, use the grandparent's:
242         // Don't let QY2ListViewItem::_textColor / _backgroundColor interfere with our colors.
243
244         Q3ListViewItem::paintCell( painter, cg, column, width, alignment );
245     }
246 }
247
248
249 /**
250  * Stolen from KDirStat::KDirTreeView with the author's permission.
251  **/
252 void
253 QY2DiskUsageListItem::paintPercentageBar( float                 percent,
254                                           QPainter *            painter,
255                                           int                   indent,
256                                           int                   width,
257                                           const QColor &        fillColor,
258                                           const QColor &        barBackground )
259 {
260     if ( percent > 100.0 )      percent = 100.0;
261     if ( percent < 0.0   )      percent = 0.0;
262     int penWidth = 2;
263     int extraMargin = 3;
264     int x = _diskUsageList->itemMargin();
265     int y = extraMargin;
266     int w = width    - 2 * _diskUsageList->itemMargin();
267     int h = height() - 2 * extraMargin;
268     int fillWidth;
269
270     painter->eraseRect( 0, 0, width, height() );
271     w -= indent;
272     x += indent;
273
274     if ( w > 0 )
275     {
276         QPen pen( painter->pen() );
277         pen.setWidth(0);
278         painter->setPen( pen );
279         painter->setBrush( Qt::NoBrush );
280         fillWidth = (int) ( ( w - 2 * penWidth ) * percent / 100.0 );
281
282
283         // Fill bar background.
284
285         painter->fillRect( x + penWidth, y + penWidth,
286                            w - 2 * penWidth + 1, h - 2 * penWidth + 1,
287                            barBackground );
288         /*
289          * Notice: The Xlib XDrawRectangle() function always fills one
290          * pixel less than specified. Altough this is very likely just a
291          * plain old bug, it is documented that way. Obviously, Qt just
292          * maps the fillRect() call directly to XDrawRectangle() so they
293          * inherited that bug ( although the Qt doc stays silent about
294          * it ). So it is really necessary to compensate for that missing
295          * pixel in each dimension.
296          *
297          * If you don't believe it, see for yourself.
298          * Hint: Try the xmag program to zoom into the drawn pixels.
299          **/
300
301         // Fill the desired percentage.
302
303         painter->fillRect( x + penWidth, y + penWidth,
304                            fillWidth+1, h - 2 * penWidth+1,
305                            fillColor );
306
307
308         // Draw 3D shadows.
309
310         pen.setColor( contrastingColor ( Qt::black,
311                                          painter->backgroundColor() ) );
312         painter->setPen( pen );
313         painter->drawLine( x, y, x+w, y );
314         painter->drawLine( x, y, x, y+h );
315
316         pen.setColor( contrastingColor( barBackground.dark(),
317                                         painter->backgroundColor() ) );
318         painter->setPen( pen );
319         painter->drawLine( x+1, y+1, x+w-1, y+1 );
320         painter->drawLine( x+1, y+1, x+1, y+h-1 );
321
322         pen.setColor( contrastingColor( barBackground.light(),
323                                         painter->backgroundColor() ) );
324         painter->setPen( pen );
325         painter->drawLine( x+1, y+h, x+w, y+h );
326         painter->drawLine( x+w, y, x+w, y+h );
327
328         pen.setColor( contrastingColor( Qt::white,
329                                         painter->backgroundColor() ) );
330         painter->setPen( pen );
331         painter->drawLine( x+2, y+h-1, x+w-1, y+h-1 );
332         painter->drawLine( x+w-1, y+1, x+w-1, y+h-1 );
333     }
334 }
335
336
337 /**
338  * Stolen from KDirStat::KDirTreeView with the author's permission.
339  **/
340 QColor
341 QY2DiskUsageListItem::contrastingColor( const QColor & desiredColor,
342                                         const QColor & contrastColor )
343 {
344     if ( desiredColor != contrastColor )
345     {
346         return desiredColor;
347     }
348
349     if ( contrastColor != contrastColor.light() )
350     {
351         // try a little lighter
352         return contrastColor.light();
353     }
354     else
355     {
356         // try a little darker
357         return contrastColor.dark();
358     }
359 }
360
361
362 QColor
363 QY2DiskUsageListItem::interpolateColor( int             val,
364                                         int             minVal,
365                                         int             maxVal,
366                                         const QColor &  minColor,
367                                         const QColor &  maxColor )
368 {
369     int minH, maxH;
370     int minS, maxS;
371     int minV, maxV;
372
373     minColor.hsv( &minH, &minS, &minV );
374     maxColor.hsv( &maxH, &maxS, &maxV );
375
376     return QColor( interpolate( val, minVal, maxVal, minH, maxH ),
377                    interpolate( val, minVal, maxVal, minS, maxS ),
378                    interpolate( val, minVal, maxVal, minV, maxV ),
379                    QColor::Hsv );
380 }
381
382
383 int
384 QY2DiskUsageListItem::interpolate( int from,
385                                    int minFrom, int maxFrom,
386                                    int minTo,   int maxTo       )
387 {
388     if ( minFrom > maxFrom )
389     {
390         // Swap min/max values
391
392         int tmp = maxFrom;
393         maxFrom = minFrom;
394         minFrom = tmp;
395     }
396
397     long x = from - minFrom;
398     x *= maxTo - minTo;
399     x /= maxFrom - minFrom;
400     x += minTo;
401
402     if ( minTo < maxTo )
403     {
404         if ( x < minTo )        x = minTo;
405         if ( x > maxTo )        x = maxTo;
406     }
407     else
408     {
409         if ( x < maxTo )        x = maxTo;
410         if ( x > minTo )        x = minTo;
411     }
412
413     return (int) x;
414 }
415
416
417
418 #include "QY2DiskUsageList.moc"