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