source: trunk/qt/file-tree.cc @ 14000

Last change on this file since 14000 was 14000, checked in by jordan, 10 years ago

(qt) #5285 'Qt client's file tree should have a size column like the GTK+ client's': done.

  • Property svn:keywords set to Date Rev Author Id
File size: 22.3 KB
Line 
1/*
2 * This file Copyright (C) Mnemosyne LLC
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2
6 * as published by the Free Software Foundation.
7 *
8 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
9 *
10 * $Id: file-tree.cc 14000 2013-02-09 20:51:17Z jordan $
11 */
12
13#include <algorithm>
14#include <cassert>
15
16#include <QApplication>
17#include <QHeaderView>
18#include <QPainter>
19#include <QResizeEvent>
20#include <QSortFilterProxyModel>
21#include <QStringList>
22
23#include <libtransmission/transmission.h> // priorities
24
25#include "file-tree.h"
26#include "formatter.h"
27#include "hig.h"
28#include "torrent.h" // FileList
29#include "utils.h" // mime icons
30
31enum
32{
33  COL_NAME,
34  FIRST_VISIBLE_COLUMN = COL_NAME,
35  COL_SIZE,
36  COL_PROGRESS,
37  COL_WANTED,
38  COL_PRIORITY,
39  LAST_VISIBLE_COLUMN = COL_PRIORITY,
40
41  COL_FILE_INDEX,
42  NUM_COLUMNS
43};
44
45/****
46*****
47****/
48
49const QHash<QString,int>&
50FileTreeItem :: getMyChildRows ()
51{
52  const size_t n = childCount();
53
54  // ensure that all the rows are hashed
55  while (myFirstUnhashedRow < n)
56    {
57      myChildRows.insert (myChildren[myFirstUnhashedRow]->name(),
58                          myFirstUnhashedRow);
59      ++myFirstUnhashedRow;
60    }
61
62  return myChildRows;
63}
64
65
66FileTreeItem :: ~FileTreeItem ()
67{
68  assert(myChildren.isEmpty());
69
70  if (myParent != 0)
71    {
72      const int pos = row();
73      assert ((pos>=0) && "couldn't find child in parent's lookup");
74      myParent->myChildren.removeAt(pos);
75      myParent->myChildRows.remove(name());
76      myParent->myFirstUnhashedRow = pos;
77    }
78}
79
80void
81FileTreeItem :: appendChild (FileTreeItem * child)
82{
83  const size_t n = childCount();
84  child->myParent = this;
85  myChildren.append (child);
86  myFirstUnhashedRow = n;
87}
88
89FileTreeItem *
90FileTreeItem :: child (const QString& filename)
91{
92  FileTreeItem * item(0);
93
94  const int row = getMyChildRows().value (filename, -1);
95  if (row != -1)
96    {
97      item = child (row);
98      assert (filename == item->name());
99    }
100
101  return item;
102}
103
104int
105FileTreeItem :: row () const
106{
107  int i(-1);
108
109  if(myParent)
110    {
111      i = myParent->getMyChildRows().value (name(), -1);
112      assert (this == myParent->myChildren[i]);
113    }
114
115  return i;
116}
117
118QVariant
119FileTreeItem :: data (int column, int role) const
120{
121  QVariant value;
122
123  if (column == COL_FILE_INDEX)
124    {
125      value.setValue (myFileIndex);
126    }
127  else if (role == Qt::EditRole)
128    {
129      if (column == 0)
130        value.setValue (name());
131    }
132  else if ((role == Qt::TextAlignmentRole) && column == COL_SIZE)
133    {
134      value = Qt::AlignRight + Qt::AlignVCenter;
135    }
136  else if (role == Qt::DisplayRole)
137    {
138      switch(column)
139       {
140         case COL_NAME:
141           value.setValue (name());
142           break;
143
144         case COL_SIZE:
145           value.setValue (sizeString() + "  ");
146           break;
147
148         case COL_PROGRESS:
149           value.setValue (progress());
150           break;
151
152         case COL_WANTED:
153           value.setValue (isSubtreeWanted());
154           break;
155
156         case COL_PRIORITY:
157           value.setValue (priorityString());
158           break;
159        }
160    }
161
162  return value;
163}
164
165void
166FileTreeItem :: getSubtreeWantedSize (uint64_t& have, uint64_t& total) const
167{
168  if (myIsWanted)
169    {
170      have += myHaveSize;
171      total += myTotalSize;
172    }
173
174  foreach(const FileTreeItem * i, myChildren)
175    i->getSubtreeWantedSize(have, total);
176}
177
178double
179FileTreeItem :: progress () const
180{
181  double d(0);
182  uint64_t have(0), total(0);
183
184  getSubtreeWantedSize (have, total);
185  if (total)
186    d = have / (double)total;
187
188  return d;
189}
190
191QString
192FileTreeItem :: sizeString () const
193{
194  QString str; 
195
196  if (myChildren.isEmpty())
197    {
198      str = Formatter::sizeToString (myTotalSize);
199    }
200  else
201    {
202      uint64_t have = 0;
203      uint64_t total = 0;
204      getSubtreeWantedSize (have, total);
205      str = Formatter::sizeToString (total);
206    }
207
208  return str;
209}
210
211std::pair<int,int>
212FileTreeItem :: update (const QString& name,
213                        bool           wanted,
214                        int            priority,
215                        uint64_t       haveSize,
216                        bool           updateFields)
217{
218  int changed_count = 0;
219  int changed_columns[4];
220
221  if (myName != name)
222    {
223      if (myParent)
224        myParent->myFirstUnhashedRow = row();
225
226      myName = name;
227      changed_columns[changed_count++] = COL_NAME;
228    }
229
230  if (myHaveSize != haveSize)
231    {
232      myHaveSize = haveSize;
233      changed_columns[changed_count++] = COL_PROGRESS;
234    }
235
236  if (fileIndex() != -1)
237    {
238      if (updateFields)
239        {
240          if (myIsWanted != wanted)
241            {
242              myIsWanted = wanted;
243              changed_columns[changed_count++] = COL_WANTED;
244            }
245
246          if (myPriority != priority)
247            {
248              myPriority = priority;
249              changed_columns[changed_count++] = COL_PRIORITY;
250            }
251        }
252    }
253
254  std::pair<int,int> changed (-1, -1);
255  if (changed_count > 0)
256    {
257      std::sort (changed_columns, changed_columns+changed_count);
258      changed.first = changed_columns[0];
259      changed.second = changed_columns[changed_count-1];
260    }
261  return changed;
262}
263
264QString
265FileTreeItem :: priorityString () const
266{
267  const int i = priority();
268
269  switch (i)
270    {
271      case LOW:    return tr("Low");
272      case HIGH:   return tr("High");
273      case NORMAL: return tr("Normal");
274      default:     return tr("Mixed");
275    }
276}
277
278int
279FileTreeItem :: priority () const
280{
281  int i(0);
282
283  if (myChildren.isEmpty())
284    {
285      switch (myPriority)
286        {
287          case TR_PRI_LOW:
288            i |= LOW;
289            break;
290
291          case TR_PRI_HIGH:
292            i |= HIGH;
293            break;
294
295          default:
296            i |= NORMAL;
297            break;
298        }
299    }
300
301  foreach (const FileTreeItem * child, myChildren)
302    i |= child->priority();
303
304  return i;
305}
306
307void
308FileTreeItem :: setSubtreePriority (int i, QSet<int>& ids)
309{
310  if (myPriority != i)
311    {
312      myPriority = i;
313
314      if (myFileIndex >= 0)
315        ids.insert (myFileIndex);
316    }
317
318  foreach (FileTreeItem * child, myChildren)
319    child->setSubtreePriority (i, ids);
320}
321
322void
323FileTreeItem :: twiddlePriority (QSet<int>& ids, int& p)
324{
325  const int old(priority());
326
327  if (old & LOW)
328    p = TR_PRI_NORMAL;
329  else if (old & NORMAL)
330    p = TR_PRI_HIGH;
331  else
332    p = TR_PRI_LOW;
333
334  setSubtreePriority (p, ids);
335}
336
337int
338FileTreeItem :: isSubtreeWanted () const
339{
340  if(myChildren.isEmpty())
341    return myIsWanted ? Qt::Checked : Qt::Unchecked;
342
343  int wanted(-1);
344  foreach (const FileTreeItem * child, myChildren)
345    {
346      const int childWanted = child->isSubtreeWanted();
347
348      if (wanted == -1)
349        wanted = childWanted;
350
351      if (wanted != childWanted)
352        wanted = Qt::PartiallyChecked;
353
354      if (wanted == Qt::PartiallyChecked)
355        return wanted;
356    }
357
358  return wanted;
359}
360
361void
362FileTreeItem :: setSubtreeWanted (bool b, QSet<int>& ids)
363{
364  if (myIsWanted != b)
365    {
366      myIsWanted = b;
367
368      if (myFileIndex >= 0)
369        ids.insert(myFileIndex);
370    }
371
372  foreach (FileTreeItem * child, myChildren)
373    child->setSubtreeWanted (b, ids);
374}
375
376void
377FileTreeItem :: twiddleWanted (QSet<int>& ids, bool& wanted)
378{
379  wanted = isSubtreeWanted() != Qt::Checked;
380  setSubtreeWanted (wanted, ids);
381}
382
383/***
384****
385****
386***/
387
388FileTreeModel :: FileTreeModel (QObject *parent, bool isEditable):
389  QAbstractItemModel(parent),
390  myRootItem (new FileTreeItem),
391  myIsEditable (isEditable)
392{
393}
394
395FileTreeModel :: ~FileTreeModel()
396{
397  clear();
398
399  delete myRootItem;
400}
401
402FileTreeItem *
403FileTreeModel :: itemFromIndex (const QModelIndex& index) const
404{
405  return static_cast<FileTreeItem*>(index.internalPointer()); 
406}
407
408QVariant
409FileTreeModel :: data (const QModelIndex &index, int role) const
410{
411  QVariant value;
412
413  if (index.isValid())
414    value = itemFromIndex(index)->data (index.column(), role);
415
416  return value;
417}
418
419Qt::ItemFlags
420FileTreeModel :: flags (const QModelIndex& index) const
421{
422  int i(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
423
424  if(myIsEditable && (index.column() == COL_NAME))
425    i |= Qt::ItemIsEditable;
426
427  if(index.column() == COL_WANTED)
428    i |= Qt::ItemIsUserCheckable | Qt::ItemIsTristate;
429
430  return (Qt::ItemFlags)i;
431}
432
433bool
434FileTreeModel :: setData (const QModelIndex& index, const QVariant& newname, int role)
435{
436  if (role == Qt::EditRole)
437    {
438      QString oldpath;
439      QModelIndex walk = index;
440      FileTreeItem * item = itemFromIndex (index);
441
442      while (item && !item->name().isEmpty())
443        {
444          if (oldpath.isEmpty())
445            oldpath = item->name();
446          else
447            oldpath = item->name() + "/" + oldpath;
448          item = item->parent ();
449        }
450
451      emit pathEdited (oldpath, newname.toString());
452    }
453
454  return false; // don't update the view until the session confirms the change
455}
456
457QVariant
458FileTreeModel :: headerData (int column, Qt::Orientation orientation, int role) const
459{
460  QVariant data;
461
462  if (orientation==Qt::Horizontal && role==Qt::DisplayRole)
463    {
464      switch (column)
465        {
466          case COL_NAME:
467            data.setValue (tr("File"));
468            break;
469
470          case COL_SIZE:
471            data.setValue (tr("Size"));
472            break;
473
474          case COL_PROGRESS:
475            data.setValue (tr("Progress"));
476            break;
477
478          case COL_WANTED:
479            data.setValue (tr("Download"));
480            break;
481
482          case COL_PRIORITY:
483            data.setValue (tr("Priority")); 
484            break;
485
486          default:
487            break;
488        }
489    }
490
491  return data;
492}
493
494QModelIndex
495FileTreeModel :: index (int row, int column, const QModelIndex& parent) const
496{
497  QModelIndex i;
498
499  if (hasIndex (row, column, parent))
500    {
501      FileTreeItem * parentItem;
502
503      if (!parent.isValid ())
504        parentItem = myRootItem;
505      else
506        parentItem = itemFromIndex (parent);
507
508      FileTreeItem * childItem = parentItem->child (row);
509
510      if (childItem)
511        i = createIndex (row, column, childItem);
512    }
513
514  return i;
515}
516
517QModelIndex
518FileTreeModel :: parent (const QModelIndex& child) const
519{
520  return parent (child, 0); // QAbstractItemModel::parent() wants col 0
521}
522
523QModelIndex
524FileTreeModel :: parent (const QModelIndex& child, int column) const
525{
526  QModelIndex parent;
527
528  if (child.isValid())
529    parent = indexOf (itemFromIndex(child)->parent(), column);
530
531  return parent;
532}
533
534int
535FileTreeModel :: rowCount (const QModelIndex& parent) const
536{
537  FileTreeItem * parentItem;
538
539  if (parent.isValid())
540    parentItem = itemFromIndex (parent);
541  else
542    parentItem = myRootItem;
543
544  return parentItem->childCount();
545}
546
547int
548FileTreeModel :: columnCount (const QModelIndex &parent) const
549{
550  Q_UNUSED(parent);
551
552  return NUM_COLUMNS;
553}
554
555QModelIndex
556FileTreeModel :: indexOf (FileTreeItem * item, int column) const
557{
558  if (!item || item==myRootItem)
559    return QModelIndex();
560
561  return createIndex(item->row(), column, item);
562}
563
564void
565FileTreeModel :: clearSubtree (const QModelIndex& top)
566{
567  size_t i = rowCount (top);
568
569  while (i > 0)
570    clearSubtree(index(--i, 0, top));
571
572  delete static_cast<FileTreeItem*>(itemFromIndex(top));
573}
574
575void
576FileTreeModel :: clear ()
577{
578  clearSubtree (QModelIndex());
579
580  reset ();
581}
582
583FileTreeItem *
584FileTreeModel :: findItemForFileIndex (int fileIndex) const
585{
586  FileTreeItem * ret = 0;
587
588  QModelIndexList indices = match (index (0,COL_FILE_INDEX),
589                                   Qt::DisplayRole,
590                                   fileIndex,
591                                   1,
592                                   Qt::MatchFlags (Qt::MatchExactly | Qt::MatchRecursive));
593
594  if (!indices.isEmpty ())
595    {
596      QModelIndex& index = indices.front ();
597      if (index.isValid())
598        ret = itemFromIndex (index);
599    }
600
601  return ret;
602}
603
604void
605FileTreeModel :: addFile (int                   fileIndex,
606                          const QString       & filename,
607                          bool                  wanted,
608                          int                   priority,
609                          uint64_t              totalSize,
610                          uint64_t              have,
611                          QList<QModelIndex>  & rowsAdded,
612                          bool                  updateFields)
613{
614  bool added = false;
615  FileTreeItem * item;
616  QStringList tokens = filename.split (QChar::fromAscii('/'));
617
618  item = findItemForFileIndex (fileIndex);
619
620  if (item) // this file is already in the tree, we've added this
621    {
622      while (!tokens.isEmpty())
623        {
624          const QString token = tokens.takeLast();
625          const std::pair<int,int> changed = item->update (token, wanted, priority, have, updateFields);
626          if (changed.first >= 0)
627            dataChanged (indexOf (item, changed.first), indexOf (item, changed.second));
628          item = item->parent();
629        }
630      assert (item == myRootItem);
631    }
632  else // we haven't build the FileTreeItems for these tokens yet
633    {
634      item = myRootItem;
635      while (!tokens.isEmpty())
636        {
637          const QString token = tokens.takeFirst();
638          FileTreeItem * child(item->child(token));
639          if (!child)
640            {
641              added = true;
642              QModelIndex parentIndex (indexOf(item, 0));
643              const int n (item->childCount());
644
645              beginInsertRows (parentIndex, n, n);
646              if (tokens.isEmpty())
647                child = new FileTreeItem (token, fileIndex, totalSize);
648              else
649                child = new FileTreeItem (token);
650              item->appendChild (child);
651              endInsertRows ();
652
653              rowsAdded.append (indexOf(child, 0));
654            }
655          item = child;
656        }
657
658      if (item != myRootItem)
659        {
660          assert (item->fileIndex() == fileIndex);
661          assert (item->totalSize() == totalSize);
662
663          const std::pair<int,int> changed = item->update (item->name(), wanted, priority, have, added || updateFields);
664          if (changed.first >= 0)
665            dataChanged (indexOf (item, changed.first), indexOf (item, changed.second));
666        }
667    }
668}
669
670void
671FileTreeModel :: parentsChanged (const QModelIndex& index, int column)
672{
673  QModelIndex walk = index;
674
675  for (;;)
676    {
677      walk = parent(walk, column);
678      if(!walk.isValid())
679        break;
680
681      dataChanged(walk, walk);
682    }
683}
684
685void
686FileTreeModel :: subtreeChanged (const QModelIndex& index, int column)
687{
688  const int childCount = rowCount (index);
689  if (!childCount)
690    return;
691
692  // tell everyone that this tier changed
693  dataChanged (index.child(0,column), index.child(childCount-1,column));
694
695  // walk the subtiers
696  for (int i=0; i<childCount; ++i)
697    subtreeChanged (index.child(i,column), column);
698}
699
700void
701FileTreeModel :: clicked (const QModelIndex& index)
702{
703  const int column (index.column());
704
705  if (!index.isValid())
706    return;
707
708  if (column == COL_WANTED)
709    {
710      bool want;
711      QSet<int> file_ids;
712      FileTreeItem * item;
713
714      item = itemFromIndex (index);
715      item->twiddleWanted (file_ids, want);
716      emit wantedChanged (file_ids, want);
717
718      // this changes the name column's parenthetical size-wanted string too...
719      QModelIndex nameSibling = index.sibling (index.row(), COL_SIZE);
720      dataChanged (nameSibling, nameSibling);
721      parentsChanged (nameSibling, COL_SIZE);
722
723      dataChanged (index, index);
724      parentsChanged (index, column);
725      subtreeChanged (index, column);
726    }
727  else if (column == COL_PRIORITY)
728    {
729      int priority;
730      QSet<int> file_ids;
731      FileTreeItem * item;
732
733      item = itemFromIndex (index);
734      item->twiddlePriority (file_ids, priority);
735      emit priorityChanged (file_ids, priority);
736
737      dataChanged(index, index);
738      parentsChanged(index, column);
739      subtreeChanged(index, column);
740    }
741}
742
743/****
744*****
745****/
746
747QSize
748FileTreeDelegate :: sizeHint(const QStyleOptionViewItem& item, const QModelIndex& index) const
749{
750  QSize size;
751
752  switch(index.column())
753    {
754      case COL_NAME:
755        {
756          const QFontMetrics fm(item.font);
757          const int iconSize = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
758          size.rwidth() = HIG::PAD_SMALL + iconSize;
759          size.rheight() = std::max(iconSize, fm.height());
760          break;
761        }
762
763      case COL_PROGRESS:
764      case COL_WANTED:
765        size = QSize(20, 1);
766        break;
767
768      default:
769        {
770          const QFontMetrics fm(item.font);
771          const QString text = index.data().toString();
772          size = fm.size(0, text);
773          break;
774        }
775    }
776
777  size.rheight() += 8; // make the spacing a little nicer
778  return size;
779}
780
781void
782FileTreeDelegate :: paint (QPainter                    * painter,
783                           const QStyleOptionViewItem  & option,
784                           const QModelIndex           & index) const
785{
786  const int column(index.column());
787
788  if ((column != COL_PROGRESS) && (column != COL_WANTED) && (column != COL_NAME))
789    {
790      QItemDelegate::paint(painter, option, index);
791      return;
792    }
793
794  QStyle * style (QApplication :: style());
795  if (option.state & QStyle::State_Selected)
796    painter->fillRect (option.rect, option.palette.highlight());
797  painter->save();
798  if (option.state & QStyle::State_Selected)
799    painter->setBrush (option.palette.highlightedText());
800
801  if (column == COL_NAME)
802    {
803      // draw the file icon
804      static const int iconSize (style->pixelMetric(QStyle :: PM_SmallIconSize));
805      const QRect iconArea (option.rect.x(),
806                            option.rect.y() + (option.rect.height()-iconSize)/2,
807                            iconSize, iconSize);
808      QIcon icon;
809      if (index.model()->hasChildren(index))
810        {
811          icon = style->standardIcon(QStyle::StandardPixmap(QStyle::SP_DirOpenIcon));
812        }
813      else
814        {
815          QString name = index.data().toString();
816          icon = Utils :: guessMimeIcon (name.left(name.lastIndexOf(" (")));
817        }
818      icon.paint (painter, iconArea, Qt::AlignCenter, QIcon::Normal, QIcon::On);
819
820      // draw the name
821      QStyleOptionViewItem tmp (option);
822      tmp.rect.setWidth (option.rect.width() - iconArea.width() - HIG::PAD_SMALL);
823      tmp.rect.moveRight (option.rect.right());
824      QItemDelegate::paint (painter, tmp, index);
825    }
826  else if(column == COL_PROGRESS)
827    {
828      QStyleOptionProgressBar p;
829      p.state = option.state | QStyle::State_Small;
830      p.direction = QApplication::layoutDirection();
831      p.rect = option.rect;
832      p.rect.setSize (QSize(option.rect.width()-2, option.rect.height()-8));
833      p.rect.moveCenter (option.rect.center());
834      p.fontMetrics = QApplication::fontMetrics();
835      p.minimum = 0;
836      p.maximum = 100;
837      p.textAlignment = Qt::AlignCenter;
838      p.textVisible = true;
839      p.progress = (int)(100.0*index.data().toDouble());
840      p.text = QString().sprintf("%d%%", p.progress);
841      style->drawControl(QStyle::CE_ProgressBar, &p, painter);
842    }
843  else if(column == COL_WANTED)
844    {
845      QStyleOptionButton o;
846      o.state = option.state;
847      o.direction = QApplication::layoutDirection();
848      o.rect.setSize (QSize(20, option.rect.height()));
849      o.rect.moveCenter (option.rect.center());
850      o.fontMetrics = QApplication::fontMetrics();
851      switch (index.data().toInt())
852        {
853          case Qt::Unchecked: o.state |= QStyle::State_Off; break;
854          case Qt::Checked:   o.state |= QStyle::State_On; break;
855          default:            o.state |= QStyle::State_NoChange;break;
856        }
857      style->drawControl (QStyle::CE_CheckBox, &o, painter);
858    }
859
860  painter->restore();
861}
862
863/****
864*****
865*****
866*****
867****/
868
869FileTreeView :: FileTreeView (QWidget * parent, bool isEditable):
870  QTreeView (parent),
871  myModel (this, isEditable),
872  myProxy (new QSortFilterProxyModel()),
873  myDelegate (this)
874{
875  setSortingEnabled (true);
876  setAlternatingRowColors (true);
877  setSelectionBehavior (QAbstractItemView::SelectRows);
878  setSelectionMode (QAbstractItemView::ExtendedSelection);
879  myProxy->setSourceModel (&myModel);
880  setModel (myProxy);
881  setItemDelegate (&myDelegate);
882  setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
883  sortByColumn (COL_NAME, Qt::AscendingOrder);
884  installEventFilter (this);
885
886  for (int i=0; i<NUM_COLUMNS; ++i)
887    {
888      setColumnHidden (i, (i<FIRST_VISIBLE_COLUMN) || (LAST_VISIBLE_COLUMN<i));
889      header()->setResizeMode(i, QHeaderView::Interactive);
890    }
891
892  connect (this, SIGNAL(clicked(const QModelIndex&)),
893           this, SLOT(onClicked(const QModelIndex&)));
894
895  connect (&myModel, SIGNAL(priorityChanged(const QSet<int>&, int)),
896           this,     SIGNAL(priorityChanged(const QSet<int>&, int)));
897
898  connect (&myModel, SIGNAL(wantedChanged(const QSet<int>&, bool)),
899           this,     SIGNAL(wantedChanged(const QSet<int>&, bool)));
900
901  connect (&myModel, SIGNAL(pathEdited(const QString&, const QString&)),
902           this,     SIGNAL(pathEdited(const QString&, const QString&)));
903}
904
905FileTreeView :: ~FileTreeView ()
906{
907  myProxy->deleteLater();
908}
909
910void
911FileTreeView :: onClicked (const QModelIndex& proxyIndex)
912{
913  const QModelIndex modelIndex = myProxy->mapToSource (proxyIndex);
914  myModel.clicked (modelIndex);
915}
916
917bool
918FileTreeView :: eventFilter (QObject * o, QEvent * event)
919{
920  // this is kind of a hack to get the last three columns be the
921  // right size, and to have the filename column use whatever
922  // space is left over...
923  if ((o == this) && (event->type() == QEvent::Resize))
924    {
925      QResizeEvent * r = dynamic_cast<QResizeEvent*>(event);
926      int left = r->size().width();
927      const QFontMetrics fontMetrics(font());
928      for (int column=FIRST_VISIBLE_COLUMN; column<=LAST_VISIBLE_COLUMN; ++column)
929        {
930          if (column == COL_NAME)
931            continue;
932          if (isColumnHidden (column))
933            continue;
934
935          QString header;
936          if (column == COL_SIZE)
937            header = "999.9 KiB";
938          else
939            header = myModel.headerData (column, Qt::Horizontal).toString();
940          header += "    ";
941          const int width = fontMetrics.size (0, header).width();
942          setColumnWidth (column, width);
943            left -= width;
944        }
945      left -= 20; // not sure why this is necessary.  it works in different themes + font sizes though...
946      setColumnWidth(COL_NAME, std::max(left,0));
947    }
948
949  return false;
950}
951
952void
953FileTreeView :: update (const FileList& files, bool updateFields)
954{
955  foreach (const TrFile file, files)
956    {
957      QList<QModelIndex> added;
958      myModel.addFile (file.index, file.filename, file.wanted, file.priority, file.size, file.have, added, updateFields);
959      foreach (QModelIndex i, added)
960        expand (myProxy->mapFromSource(i));
961    }
962}
963
964void
965FileTreeView :: clear ()
966{
967  myModel.clear();
968}
Note: See TracBrowser for help on using the repository browser.