]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/util/fmtwidgetitem.cpp
Riccardo's patch to correct sorting on numeric fields.
[bacula/bacula] / bacula / src / qt-console / util / fmtwidgetitem.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2007 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 two of the GNU 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 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 John Walker.
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  *   Version $Id$
31  *
32  *  Helper functions for tree widget formatting
33  *
34  *   Riccardo Ghetta, May 2008
35  *
36  */ 
37
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  * ItemFormatterBase static members
50  *
51  ***********************************************/
52
53 ItemFormatterBase::BYTES_CONVERSION ItemFormatterBase::cnvFlag(BYTES_CONVERSION_IEC);
54
55 QString ItemFormatterBase::convertBytesIEC(qint64 qfld)
56 {
57    static const qint64 KB = Q_INT64_C(1024);
58    static const qint64 MB = (KB * KB);
59    static const qint64 GB = (MB * KB);
60    static const qint64 TB = (GB * KB);
61    static const qint64 PB = (TB * KB);
62    static const qint64 EB = (PB * KB);
63
64    /* note: division is integer, so to have some decimals we divide for a
65       smaller unit (e.g. GB for a TB number and so on) */
66    char suffix;
67    if (qfld >= EB) {
68       qfld /= PB; 
69       suffix = 'E';
70    }
71    else if (qfld >= PB) {
72       qfld /= TB; 
73       suffix = 'P';
74    }
75    else if (qfld >= TB) {
76       qfld /= GB; 
77       suffix = 'T';
78    }
79    else if (qfld >= GB) {
80       qfld /= MB;
81       suffix = 'G';
82    }
83    else if (qfld >= MB) {
84       qfld /= KB;
85       suffix = 'M';
86    }
87    else if (qfld >= KB) {
88       suffix = 'K';
89    }
90    else  {
91       /* plain bytes, no need to reformat */
92       return QString("%1 B").arg(qfld); 
93    }
94
95    /* having divided for a smaller unit, now we can safely convert to double and
96       use the extra room for decimals */
97    return QString("%1 %2iB").arg(qfld / 1000.0, 0, 'f', 2).arg(suffix);
98 }
99
100 QString ItemFormatterBase::convertBytesSI(qint64 qfld)
101 {
102    static const qint64 KB = Q_INT64_C(1000);
103    static const qint64 MB = (KB * KB);
104    static const qint64 GB = (MB * KB);
105    static const qint64 TB = (GB * KB);
106    static const qint64 PB = (TB * KB);
107    static const qint64 EB = (PB * KB);
108
109    /* note: division is integer, so to have some decimals we divide for a
110       smaller unit (e.g. GB for a TB number and so on) */
111    char suffix;
112    if (qfld >= EB) {
113       qfld /= PB; 
114       suffix = 'E';
115    }
116    else if (qfld >= PB) {
117       qfld /= TB; 
118       suffix = 'P';
119    }
120    else if (qfld >= TB) {
121       qfld /= GB; 
122       suffix = 'T';
123    }
124    else if (qfld >= GB) {
125       qfld /= MB;
126       suffix = 'G';
127    }
128    else if (qfld >= MB) {
129       qfld /= KB;
130       suffix = 'M';
131    }
132    else if (qfld >= KB) {
133       suffix = 'k'; /* SI uses lowercase k */
134    }
135    else  {
136       /* plain bytes, no need to reformat */
137       return QString("%1 B").arg(qfld); 
138    }
139
140    /* having divided for a smaller unit, now we can safely convert to double and
141       use the extra room for decimals */
142    return QString("%1 %2B").arg(qfld / 1000.0, 0, 'f', 2).arg(suffix);
143 }
144
145 /***********************************************
146  *
147  * base formatting routines
148  *
149  ***********************************************/
150
151 ItemFormatterBase::ItemFormatterBase()
152 {
153 }
154
155 ItemFormatterBase::~ItemFormatterBase()
156 {
157 }
158
159 void ItemFormatterBase::setTextFld(int index, const QString &fld, bool center)
160 {
161    setText(index, fld.trimmed());
162    if (center) {
163       setTextAlignment(index, Qt::AlignCenter);
164    }
165 }
166
167 void ItemFormatterBase::setRightFld(int index, const QString &fld)
168 {
169    setText(index, fld.trimmed());
170    setTextAlignment(index, Qt::AlignRight | Qt::AlignVCenter);
171 }
172
173 void ItemFormatterBase::setBoolFld(int index, const QString &fld, bool center)
174 {
175    if (fld.trimmed().toInt())
176      setTextFld(index, QObject::tr("Yes"), center);
177    else
178      setTextFld(index, QObject::tr("No"), center);
179 }
180
181 void ItemFormatterBase::setBoolFld(int index, int fld, bool center)
182 {
183    if (fld)
184      setTextFld(index, QObject::tr("Yes"), center);
185    else
186      setTextFld(index, QObject::tr("No"), center);
187 }
188
189 void ItemFormatterBase::setNumericFld(int index, const QString &fld)
190 {
191    setRightFld(index, fld.trimmed());
192    setSortValue(index, fld.toDouble() );
193 }
194
195 void ItemFormatterBase::setNumericFld(int index, const QString &fld, const QVariant &sortval)
196 {
197    setRightFld(index, fld.trimmed());
198    setSortValue(index, sortval );
199 }
200
201 void ItemFormatterBase::setBytesFld(int index, const QString &fld)
202 {
203    qint64 qfld = fld.trimmed().toLongLong();
204    QString msg;
205    switch (cnvFlag) {
206    case BYTES_CONVERSION_NONE:
207       msg = QString::number(qfld);
208       break;
209    case BYTES_CONVERSION_IEC:
210       msg = convertBytesIEC(qfld);
211       break;
212    case BYTES_CONVERSION_SI:
213       msg = convertBytesSI(qfld);
214       break;
215    }
216
217    setNumericFld(index, msg, qfld);
218 }
219
220 void ItemFormatterBase::setDurationFld(int index, const QString &fld)
221 {
222    static const qint64 HOUR = Q_INT64_C(3600);
223    static const qint64 DAY = HOUR * 24;
224    static const qint64 WEEK = DAY * 7;
225    static const qint64 MONTH = DAY * 30;
226    static const qint64 YEAR = DAY * 365;
227    static const qint64 divs[] = { YEAR, MONTH, WEEK, DAY, HOUR };
228    static const char sufs[] = { 'y', 'm', 'w', 'd', 'h', '\0' };
229
230    qint64 dfld = fld.trimmed().toLongLong();
231
232    char suffix = 's';
233    if (dfld) {
234       for (int pos = 0 ; sufs[pos] ; ++pos) {
235           if (dfld % divs[pos] == 0) {
236              dfld /= divs[pos];
237              suffix = sufs[pos];
238              break;
239           }
240       }
241    }
242    QString msg;
243    if (dfld < 100) {
244       msg = QString("%1%2").arg(dfld).arg(suffix);
245    } else {
246       /* previous check returned a number too big. The original specification perhaps
247          was mixed, like 1d 2h, so we try to match with this routine */
248       dfld = fld.trimmed().toLongLong();
249       msg = "";
250       for (int pos = 0 ; sufs[pos] ; ++pos) {
251           if (dfld / divs[pos] != 0) {
252              msg += QString(" %1%2").arg(dfld / divs[pos]).arg(sufs[pos]);
253              dfld %= divs[pos];
254           }
255       }
256       if (dfld)
257          msg += QString(" %1s").arg(dfld);
258    }
259
260    setNumericFld(index, msg, fld.trimmed().toLongLong());
261 }
262
263 void ItemFormatterBase::setVolStatusFld(int index, const QString &fld, bool center)
264 {
265    setTextFld(index, fld, center);
266
267    if (fld == "Append" ) {
268       setBackground(index, Qt::green);
269    } else if (fld == "Error") {
270       setBackground(index, Qt::red);
271    } else if (fld == "Used" || fld == "Full"){
272       setBackground(index, Qt::yellow);
273    }
274 }
275
276 void ItemFormatterBase::setJobStatusFld(int index, const QString &shortstatus, 
277                                         const QString &longstatus, bool center)
278 {
279    /* C (created, not yet running) uses the default background */
280    static QString greenchars("TR");
281    static QString redchars("BEf");
282    static QString yellowchars("eDAFSMmsjdctp");
283
284    setTextFld(index, longstatus, center);
285
286    QString st(shortstatus.trimmed());
287    if (greenchars.contains(st, Qt::CaseSensitive)) {
288       setBackground(index, Qt::green);
289    } else if (redchars.contains(st, Qt::CaseSensitive)) {
290       setBackground(index, Qt::red);
291    } else if (yellowchars.contains(st, Qt::CaseSensitive)){ 
292       setBackground(index, Qt::yellow);
293    }
294 }
295
296 void ItemFormatterBase::setJobTypeFld(int index, const QString &fld, bool center)
297 {
298    static QHash<QString, QString> jobt;
299    if (jobt.isEmpty()) {
300       jobt.insert("B", QObject::tr("Backup"));
301       jobt.insert("R", QObject::tr("Restore"));
302       jobt.insert("V", QObject::tr("Verify"));
303       jobt.insert("A", QObject::tr("Admin"));
304    }
305
306    setTextFld(index, jobt.value(fld.trimmed(), fld.trimmed()), center);
307 }
308
309 void ItemFormatterBase::setJobLevelFld(int index, const QString &fld, bool center)
310 {
311    static QHash<QString, QString> jobt;
312    if (jobt.isEmpty()) {
313       jobt.insert("F", QObject::tr("Full"));
314       jobt.insert("D", QObject::tr("Differential"));
315       jobt.insert("I", QObject::tr("Incremental"));
316       jobt.insert("C", QObject::tr("Catalog"));
317       jobt.insert("O", QObject::tr("VolToCatalog"));
318    }
319
320    setTextFld(index, jobt.value(fld.trimmed(), fld.trimmed()), center);
321 }
322
323
324
325 /***********************************************
326  *
327  * treeitem formatting routines
328  *
329  ***********************************************/
330 TreeItemFormatter::TreeItemFormatter(QTreeWidgetItem &parent, int indent_level):
331 ItemFormatterBase(),
332 wdg(new QTreeWidgetItem(&parent)),
333 level(indent_level)
334 {
335 }
336
337 void TreeItemFormatter::setText(int index, const QString &fld)
338 {
339    wdg->setData(index, Qt::UserRole, level);
340    wdg->setText(index, fld);
341 }
342
343 void TreeItemFormatter::setTextAlignment(int index, int align)
344 {
345    wdg->setTextAlignment(index, align);
346 }
347
348 void TreeItemFormatter::setBackground(int index, const QBrush &qb)
349 {
350    wdg->setBackground(index, qb);
351 }
352
353 /* at this time we don't sort trees, so this method does nothing */
354 void TreeItemFormatter::setSortValue(int /* index */, const QVariant & /* value */)
355 {
356 }
357
358 /***********************************************
359  *
360  * Specialized table widget used for sorting
361  *
362  ***********************************************/
363 TableItemFormatter::BatSortingTableItem::BatSortingTableItem():
364 QTableWidgetItem(1)
365 {
366 }
367
368 void TableItemFormatter::BatSortingTableItem::setSortData(const QVariant &d)
369 {
370    setData(SORTDATA_ROLE, d);
371 }
372
373 bool TableItemFormatter::BatSortingTableItem::operator< ( const QTableWidgetItem & o ) const 
374 {
375    QVariant my = data(SORTDATA_ROLE);
376    QVariant other = o.data(SORTDATA_ROLE);
377    if (!my.isValid() || !other.isValid() || my.type() != other.type())
378       return QTableWidgetItem::operator< (o); /* invalid combination, revert to default sorting */
379
380    /* 64bit integers must be handled separately, others can be converted to double */
381    if (QVariant::ULongLong == my.type()) {
382       return my.toULongLong() < other.toULongLong(); 
383    } else if (QVariant::LongLong == my.type()) {
384       return my.toLongLong() < other.toLongLong(); 
385    } else if (my.canConvert(QVariant::Double)) {
386       return my.toDouble() < other.toDouble(); 
387    } else {
388       return QTableWidgetItem::operator< (o); /* invalid combination, revert to default sorting */
389    }
390 }
391
392 /***********************************************
393  *
394  * tableitem formatting routines
395  *
396  ***********************************************/
397 TableItemFormatter::TableItemFormatter(QTableWidget &tparent, int trow):
398 ItemFormatterBase(),
399 parent(&tparent),
400 row(trow),
401 last(NULL)
402 {
403 }
404
405 void TableItemFormatter::setText(int col, const QString &fld)
406 {
407    last = new BatSortingTableItem;
408    parent->setItem(row, col, last);
409    last->setText(fld);
410 }
411
412 void TableItemFormatter::setTextAlignment(int /*index*/, int align)
413 {
414    last->setTextAlignment(align);
415 }
416
417 void TableItemFormatter::setBackground(int /*index*/, const QBrush &qb)
418 {
419    last->setBackground(qb);
420 }
421
422 void TableItemFormatter::setSortValue(int /* index */, const QVariant &value )
423 {
424    last->setSortData(value);
425 }
426
427 QTableWidgetItem *TableItemFormatter::widget(int col)
428 {
429    return parent->item(row, col);
430 }
431
432 const QTableWidgetItem *TableItemFormatter::widget(int col) const
433 {
434    return parent->item(row, col);
435 }
436