]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/util/fmtwidgetitem.cpp
bat: Implement a Date and FileType helper in ItemFormatter
[bacula/bacula] / bacula / src / qt-console / util / fmtwidgetitem.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2010 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28  
29 /*
30  *
31  *  Helper functions for tree widget formatting
32  *
33  *   Riccardo Ghetta, May 2008
34  *
35  */ 
36
37 #include "bat.h"
38 #include <QTreeWidgetItem>
39 #include <QTableWidget>
40 #include <QTableWidgetItem>
41 #include <QBrush>
42 #include <QString>
43 #include <QStringList>
44 #include <math.h>
45 #include "fmtwidgetitem.h"
46
47 /***********************************************
48  *
49  * common helpers
50  *
51  ***********************************************/
52
53 QString convertJobStatus(const QString &sts)
54 {
55    QString code( sts.trimmed() );
56    if ( code.size() != 1) {
57       return QObject::tr("Invalid job status %1").arg(sts);
58    }
59
60    char buf[256];
61    jobstatus_to_ascii_gui( code[0].toAscii(), buf, sizeof(buf));
62    return QString(buf);
63 }
64
65 /*
66  * disable widget updating
67  */
68 Freeze::Freeze(QWidget &q):
69 qw(&q)
70 {
71    qw->setUpdatesEnabled(false); 
72 }
73
74 Freeze::~Freeze()
75 {
76    if (qw) {
77       qw->setUpdatesEnabled(true); 
78       qw->update();
79    }
80 }
81
82 /***********************************************
83  *
84  * ItemFormatterBase static members
85  *
86  ***********************************************/
87
88 ItemFormatterBase::BYTES_CONVERSION ItemFormatterBase::cnvFlag(BYTES_CONVERSION_IEC);
89
90 QString convertBytesIEC(qint64 qfld)
91 {
92    static const qint64 KB = Q_INT64_C(1024);
93    static const qint64 MB = (KB * KB);
94    static const qint64 GB = (MB * KB);
95    static const qint64 TB = (GB * KB);
96    static const qint64 PB = (TB * KB);
97    static const qint64 EB = (PB * KB);
98
99    /* note: division is integer, so to have some decimals we divide for a
100       smaller unit (e.g. GB for a TB number and so on) */
101    char suffix;
102    if (qfld >= EB) {
103       qfld /= PB; 
104       suffix = 'E';
105    }
106    else if (qfld >= PB) {
107       qfld /= TB; 
108       suffix = 'P';
109    }
110    else if (qfld >= TB) {
111       qfld /= GB; 
112       suffix = 'T';
113    }
114    else if (qfld >= GB) {
115       qfld /= MB;
116       suffix = 'G';
117    }
118    else if (qfld >= MB) {
119       qfld /= KB;
120       suffix = 'M';
121    }
122    else if (qfld >= KB) {
123       suffix = 'K';
124    }
125    else  {
126       /* plain bytes, no need to reformat */
127       return QString("%1 B").arg(qfld); 
128    }
129
130    /* having divided for a smaller unit, now we can safely convert to double and
131       use the extra room for decimals */
132    return QString("%1 %2iB").arg(qfld / 1000.0, 0, 'f', 2).arg(suffix);
133 }
134
135 QString convertBytesSI(qint64 qfld)
136 {
137    static const qint64 KB = Q_INT64_C(1000);
138    static const qint64 MB = (KB * KB);
139    static const qint64 GB = (MB * KB);
140    static const qint64 TB = (GB * KB);
141    static const qint64 PB = (TB * KB);
142    static const qint64 EB = (PB * KB);
143
144    /* note: division is integer, so to have some decimals we divide for a
145       smaller unit (e.g. GB for a TB number and so on) */
146    char suffix;
147    if (qfld >= EB) {
148       qfld /= PB; 
149       suffix = 'E';
150    }
151    else if (qfld >= PB) {
152       qfld /= TB; 
153       suffix = 'P';
154    }
155    else if (qfld >= TB) {
156       qfld /= GB; 
157       suffix = 'T';
158    }
159    else if (qfld >= GB) {
160       qfld /= MB;
161       suffix = 'G';
162    }
163    else if (qfld >= MB) {
164       qfld /= KB;
165       suffix = 'M';
166    }
167    else if (qfld >= KB) {
168       suffix = 'k'; /* SI uses lowercase k */
169    }
170    else  {
171       /* plain bytes, no need to reformat */
172       return QString("%1 B").arg(qfld); 
173    }
174
175    /* having divided for a smaller unit, now we can safely convert to double and
176       use the extra room for decimals */
177    return QString("%1 %2B").arg(qfld / 1000.0, 0, 'f', 2).arg(suffix);
178 }
179
180 /***********************************************
181  *
182  * base formatting routines
183  *
184  ***********************************************/
185
186 ItemFormatterBase::ItemFormatterBase()
187 {
188 }
189
190 ItemFormatterBase::~ItemFormatterBase()
191 {
192 }
193
194 void ItemFormatterBase::setPercent(int index, float value)
195 {
196    char buf[100];
197    bsnprintf(buf, sizeof(buf), "%.2f%%", value);
198    QString val = buf;
199    QString pix;
200    if (value < 8) {
201       pix = ":images/0p.png";
202    } else if (value < 24) {
203       pix = ":images/16p.png";
204    } else if (value < 40) {
205       pix = ":images/32p.png";
206    } else if (value < 56) {
207       pix = ":images/48p.png";
208    } else if (value < 72) {
209       pix = ":images/64p.png";
210    } else if (value < 88) {
211       pix = ":images/80p.png";
212    } else {
213       pix = ":images/96p.png";
214    }
215    setPixmap(index, QPixmap(pix), val);
216    //setSortValue(index, (int) value);
217    //setBackground(index, Qt::green);
218 }
219
220 /* By default, the setPixmap implementation with tooltip don't implement
221  * the tooltip stuff
222  */
223 void ItemFormatterBase::setPixmap(int index, const QPixmap &pix, 
224                                   const QString & /* tip */)
225 {
226    setPixmap(index, pix);
227 }
228
229 void ItemFormatterBase::setInChanger(int index, const QString &InChanger)
230 {
231    setPixmap(index, QPixmap(":images/inflag"+InChanger+".png"));
232    //setSortValue(index, InChanger.toInt() );
233 }
234
235 void ItemFormatterBase::setFileType(int index, const QString &type)
236 {
237    setPixmap(index, QPixmap(":images/"+type+".png"));
238    //setSortValue(index, InChanger.toInt() );
239 }
240
241 void ItemFormatterBase::setTextFld(int index, const QString &fld, bool center)
242 {
243    setText(index, fld.trimmed());
244    if (center) {
245       setTextAlignment(index, Qt::AlignCenter);
246    }
247 }
248
249 void ItemFormatterBase::setDateFld(int index, utime_t fld, bool center)
250 {
251    char buf[200];
252    bstrutime(buf, sizeof(buf), fld);
253    setText(index, QString(buf).trimmed());
254    if (center) {
255       setTextAlignment(index, Qt::AlignCenter);
256    }
257 }
258
259 void ItemFormatterBase::setRightFld(int index, const QString &fld)
260 {
261    setText(index, fld.trimmed());
262    setTextAlignment(index, Qt::AlignRight | Qt::AlignVCenter);
263 }
264
265 void ItemFormatterBase::setBoolFld(int index, const QString &fld, bool center)
266 {
267    if (fld.trimmed().toInt())
268      setTextFld(index, QObject::tr("Yes"), center);
269    else
270      setTextFld(index, QObject::tr("No"), center);
271 }
272
273 void ItemFormatterBase::setBoolFld(int index, int fld, bool center)
274 {
275    if (fld)
276      setTextFld(index, QObject::tr("Yes"), center);
277    else
278      setTextFld(index, QObject::tr("No"), center);
279 }
280
281 void ItemFormatterBase::setNumericFld(int index, const QString &fld)
282 {
283    setRightFld(index, fld.trimmed());
284    setSortValue(index, fld.toDouble() );
285 }
286
287 void ItemFormatterBase::setNumericFld(int index, const QString &fld, const QVariant &sortval)
288 {
289    setRightFld(index, fld.trimmed());
290    setSortValue(index, sortval );
291 }
292
293 void ItemFormatterBase::setBytesFld(int index, const QString &fld)
294 {
295    qint64 qfld = fld.trimmed().toLongLong();
296    QString msg;
297    switch (cnvFlag) {
298    case BYTES_CONVERSION_NONE:
299       msg = QString::number(qfld);
300       break;
301    case BYTES_CONVERSION_IEC:
302       msg = convertBytesIEC(qfld);
303       break;
304    case BYTES_CONVERSION_SI:
305       msg = convertBytesSI(qfld);
306       break;
307    }
308
309    setNumericFld(index, msg, qfld);
310 }
311
312 void ItemFormatterBase::setDurationFld(int index, const QString &fld)
313 {
314    static const qint64 HOUR = Q_INT64_C(3600);
315    static const qint64 DAY = HOUR * 24;
316    static const qint64 WEEK = DAY * 7;
317    static const qint64 MONTH = DAY * 30;
318    static const qint64 YEAR = DAY * 365;
319    static const qint64 divs[] = { YEAR, MONTH, WEEK, DAY, HOUR };
320    static const char sufs[] = { 'y', 'm', 'w', 'd', 'h', '\0' };
321
322    qint64 dfld = fld.trimmed().toLongLong();
323
324    char suffix = 's';
325    if (dfld) {
326       for (int pos = 0 ; sufs[pos] ; ++pos) {
327           if (dfld % divs[pos] == 0) {
328              dfld /= divs[pos];
329              suffix = sufs[pos];
330              break;
331           }
332       }
333    }
334    QString msg;
335    if (dfld < 100) {
336       msg = QString("%1%2").arg(dfld).arg(suffix);
337    } else {
338       /* previous check returned a number too big. The original specification perhaps
339          was mixed, like 1d 2h, so we try to match with this routine */
340       dfld = fld.trimmed().toLongLong();
341       msg = "";
342       for (int pos = 0 ; sufs[pos] ; ++pos) {
343           if (dfld / divs[pos] != 0) {
344              msg += QString(" %1%2").arg(dfld / divs[pos]).arg(sufs[pos]);
345              dfld %= divs[pos];
346           }
347       }
348       if (dfld)
349          msg += QString(" %1s").arg(dfld);
350    }
351
352    setNumericFld(index, msg, fld.trimmed().toLongLong());
353 }
354
355 void ItemFormatterBase::setVolStatusFld(int index, const QString &fld, bool center)
356 {
357   QString mp(fld.trimmed());
358    setTextFld(index, volume_status_to_str(mp.toUtf8()), center);
359
360    if (mp == "Append" ) {
361       setBackground(index, Qt::green);
362    } else if (mp == "Error") {
363       setBackground(index, Qt::red);
364    } else if (mp == "Used" || mp == "Full"){
365       setBackground(index, Qt::yellow);
366    } else if (mp == "Read-only" || mp == "Disabled"){
367       setBackground(index, Qt::lightGray);
368    }
369 }
370
371 void ItemFormatterBase::setJobStatusFld(int index, const QString &status, bool center)
372 {
373    /* C (created, not yet running) uses the default background */
374    static QString greenchars("TR");
375    static QString redchars("BEf");
376    static QString yellowchars("eDAFSMmsjdctp");
377
378    setTextFld(index, convertJobStatus(status), center);
379
380    QString st(status.trimmed());
381    if (greenchars.contains(st, Qt::CaseSensitive)) {
382       setBackground(index, Qt::green);
383    } else if (redchars.contains(st, Qt::CaseSensitive)) {
384       setBackground(index, Qt::red);
385    } else if (yellowchars.contains(st, Qt::CaseSensitive)){ 
386       setBackground(index, Qt::yellow);
387    }
388 }
389
390 void ItemFormatterBase::setJobTypeFld(int index, const QString &fld, bool center)
391 {
392    QByteArray jtype(fld.trimmed().toAscii());
393    if (jtype.size()) {
394       setTextFld(index, job_type_to_str(jtype[0]), center);
395    } else {
396       setTextFld(index, "", center);
397    }
398 }
399
400 void ItemFormatterBase::setJobLevelFld(int index, const QString &fld, bool center)
401 {
402    QByteArray lvl(fld.trimmed().toAscii());
403    if (lvl.size()) {
404       setTextFld(index, job_level_to_str(lvl[0]), center);
405    } else {
406       setTextFld(index, "", center);
407    }
408 }
409
410
411
412 /***********************************************
413  *
414  * treeitem formatting routines
415  *
416  ***********************************************/
417 TreeItemFormatter::TreeItemFormatter(QTreeWidgetItem &parent, int indent_level):
418 ItemFormatterBase(),
419 wdg(new QTreeWidgetItem(&parent)),
420 level(indent_level)
421 {
422 }
423
424 void TreeItemFormatter::setText(int index, const QString &fld)
425 {
426    wdg->setData(index, Qt::UserRole, level);
427    wdg->setText(index, fld);
428 }
429
430 void TreeItemFormatter::setTextAlignment(int index, int align)
431 {
432    wdg->setTextAlignment(index, align);
433 }
434
435 void TreeItemFormatter::setBackground(int index, const QBrush &qb)
436 {
437    wdg->setBackground(index, qb);
438 }
439
440 /* at this time we don't sort trees, so this method does nothing */
441 void TreeItemFormatter::setSortValue(int /* index */, const QVariant & /* value */)
442 {
443 }
444
445 void TreeItemFormatter::setPixmap(int index, const QPixmap &pix)
446 {
447    wdg->setIcon(index, QIcon(pix));
448 }
449
450 /***********************************************
451  *
452  * Specialized table widget used for sorting
453  *
454  ***********************************************/
455 TableItemFormatter::BatSortingTableItem::BatSortingTableItem():
456 QTableWidgetItem(1)
457 {
458 }
459
460 void TableItemFormatter::BatSortingTableItem::setSortData(const QVariant &d)
461 {
462    setData(SORTDATA_ROLE, d);
463 }
464
465 bool TableItemFormatter::BatSortingTableItem::operator< ( const QTableWidgetItem & o ) const 
466 {
467    QVariant my = data(SORTDATA_ROLE);
468    QVariant other = o.data(SORTDATA_ROLE);
469    if (!my.isValid() || !other.isValid() || my.type() != other.type())
470       return QTableWidgetItem::operator< (o); /* invalid combination, revert to default sorting */
471
472    /* 64bit integers must be handled separately, others can be converted to double */
473    if (QVariant::ULongLong == my.type()) {
474       return my.toULongLong() < other.toULongLong(); 
475    } else if (QVariant::LongLong == my.type()) {
476       return my.toLongLong() < other.toLongLong(); 
477    } else if (my.canConvert(QVariant::Double)) {
478       return my.toDouble() < other.toDouble(); 
479    } else {
480       return QTableWidgetItem::operator< (o); /* invalid combination, revert to default sorting */
481    }
482 }
483
484 /***********************************************
485  *
486  * tableitem formatting routines
487  *
488  ***********************************************/
489 TableItemFormatter::TableItemFormatter(QTableWidget &tparent, int trow):
490 ItemFormatterBase(),
491 parent(&tparent),
492 row(trow),
493 last(NULL)
494 {
495 }
496
497 void TableItemFormatter::setPixmap(int index, const QPixmap &pix)
498 {
499 // Centered, but not sortable !
500    QLabel *lbl = new QLabel();
501    lbl->setAlignment(Qt::AlignCenter);
502    lbl->setPixmap(pix);
503    parent->setCellWidget(row, index, lbl);
504 }
505
506 void TableItemFormatter::setPixmap(int index, const QPixmap &pix, 
507                                    const QString &tips)
508 {
509 // Centered, but not sortable !
510    QLabel *lbl = new QLabel();
511    lbl->setAlignment(Qt::AlignCenter);
512    lbl->setPixmap(pix);
513    if (!tips.isEmpty()) {
514       lbl->setToolTip(tips);
515    }
516    parent->setCellWidget(row, index, lbl);
517
518 //   last = new BatSortingTableItem;
519 //   parent->setItem(row, index, last);
520 //   last->setIcon(pix);
521 }
522
523 void TableItemFormatter::setText(int col, const QString &fld)
524 {
525    last = new BatSortingTableItem;
526    parent->setItem(row, col, last);
527    last->setText(fld);
528 }
529
530 void TableItemFormatter::setTextAlignment(int /*index*/, int align)
531 {
532    last->setTextAlignment(align);
533 }
534
535 void TableItemFormatter::setBackground(int /*index*/, const QBrush &qb)
536 {
537    last->setBackground(qb);
538 }
539
540 void TableItemFormatter::setSortValue(int /* index */, const QVariant &value )
541 {
542    last->setSortData(value);
543 }
544
545 QTableWidgetItem *TableItemFormatter::widget(int col)
546 {
547    return parent->item(row, col);
548 }
549
550 const QTableWidgetItem *TableItemFormatter::widget(int col) const
551 {
552    return parent->item(row, col);
553 }