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