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