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

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

(trunk, qt) #4813: allow launching files in Qt interface

  • Property svn:keywords set to Date Rev Author Id
File size: 23.5 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 14196 2013-09-08 19:03:25Z 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
383QString
384FileTreeItem :: path () const
385{
386  QString itemPath;
387  const FileTreeItem * item = this;
388
389  while (item != NULL && !item->name().isEmpty())
390    {
391      if (itemPath.isEmpty())
392        itemPath = item->name();
393      else
394        itemPath = item->name() + "/" + itemPath;
395      item = item->parent ();
396    }
397
398  return itemPath;
399}
400
401bool
402FileTreeItem :: isComplete () const
403{
404  return myHaveSize == totalSize ();
405}
406
407/***
408****
409****
410***/
411
412FileTreeModel :: FileTreeModel (QObject *parent, bool isEditable):
413  QAbstractItemModel(parent),
414  myRootItem (new FileTreeItem),
415  myIsEditable (isEditable)
416{
417}
418
419FileTreeModel :: ~FileTreeModel()
420{
421  clear();
422
423  delete myRootItem;
424}
425
426FileTreeItem *
427FileTreeModel :: itemFromIndex (const QModelIndex& index) const
428{
429  return static_cast<FileTreeItem*>(index.internalPointer()); 
430}
431
432QVariant
433FileTreeModel :: data (const QModelIndex &index, int role) const
434{
435  QVariant value;
436
437  if (index.isValid())
438    value = itemFromIndex(index)->data (index.column(), role);
439
440  return value;
441}
442
443Qt::ItemFlags
444FileTreeModel :: flags (const QModelIndex& index) const
445{
446  int i(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
447
448  if(myIsEditable && (index.column() == COL_NAME))
449    i |= Qt::ItemIsEditable;
450
451  if(index.column() == COL_WANTED)
452    i |= Qt::ItemIsUserCheckable | Qt::ItemIsTristate;
453
454  return (Qt::ItemFlags)i;
455}
456
457bool
458FileTreeModel :: setData (const QModelIndex& index, const QVariant& newname, int role)
459{
460  if (role == Qt::EditRole)
461    {
462      FileTreeItem * item = itemFromIndex (index);
463
464      emit pathEdited (item->path (), newname.toString ());
465    }
466
467  return false; // don't update the view until the session confirms the change
468}
469
470QVariant
471FileTreeModel :: headerData (int column, Qt::Orientation orientation, int role) const
472{
473  QVariant data;
474
475  if (orientation==Qt::Horizontal && role==Qt::DisplayRole)
476    {
477      switch (column)
478        {
479          case COL_NAME:
480            data.setValue (tr("File"));
481            break;
482
483          case COL_SIZE:
484            data.setValue (tr("Size"));
485            break;
486
487          case COL_PROGRESS:
488            data.setValue (tr("Progress"));
489            break;
490
491          case COL_WANTED:
492            data.setValue (tr("Download"));
493            break;
494
495          case COL_PRIORITY:
496            data.setValue (tr("Priority")); 
497            break;
498
499          default:
500            break;
501        }
502    }
503
504  return data;
505}
506
507QModelIndex
508FileTreeModel :: index (int row, int column, const QModelIndex& parent) const
509{
510  QModelIndex i;
511
512  if (hasIndex (row, column, parent))
513    {
514      FileTreeItem * parentItem;
515
516      if (!parent.isValid ())
517        parentItem = myRootItem;
518      else
519        parentItem = itemFromIndex (parent);
520
521      FileTreeItem * childItem = parentItem->child (row);
522
523      if (childItem)
524        i = createIndex (row, column, childItem);
525    }
526
527  return i;
528}
529
530QModelIndex
531FileTreeModel :: parent (const QModelIndex& child) const
532{
533  return parent (child, 0); // QAbstractItemModel::parent() wants col 0
534}
535
536QModelIndex
537FileTreeModel :: parent (const QModelIndex& child, int column) const
538{
539  QModelIndex parent;
540
541  if (child.isValid())
542    parent = indexOf (itemFromIndex(child)->parent(), column);
543
544  return parent;
545}
546
547int
548FileTreeModel :: rowCount (const QModelIndex& parent) const
549{
550  FileTreeItem * parentItem;
551
552  if (parent.isValid())
553    parentItem = itemFromIndex (parent);
554  else
555    parentItem = myRootItem;
556
557  return parentItem->childCount();
558}
559
560int
561FileTreeModel :: columnCount (const QModelIndex &parent) const
562{
563  Q_UNUSED(parent);
564
565  return NUM_COLUMNS;
566}
567
568QModelIndex
569FileTreeModel :: indexOf (FileTreeItem * item, int column) const
570{
571  if (!item || item==myRootItem)
572    return QModelIndex();
573
574  return createIndex(item->row(), column, item);
575}
576
577void
578FileTreeModel :: clearSubtree (const QModelIndex& top)
579{
580  size_t i = rowCount (top);
581
582  while (i > 0)
583    clearSubtree(index(--i, 0, top));
584
585  delete static_cast<FileTreeItem*>(itemFromIndex(top));
586}
587
588void
589FileTreeModel :: clear ()
590{
591  beginResetModel ();
592  clearSubtree (QModelIndex());
593  endResetModel ();
594}
595
596FileTreeItem *
597FileTreeModel :: findItemForFileIndex (int fileIndex) const
598{
599  FileTreeItem * ret = 0;
600
601  QModelIndexList indices = match (index (0,COL_FILE_INDEX),
602                                   Qt::DisplayRole,
603                                   fileIndex,
604                                   1,
605                                   Qt::MatchFlags (Qt::MatchExactly | Qt::MatchRecursive));
606
607  if (!indices.isEmpty ())
608    {
609      QModelIndex& index = indices.front ();
610      if (index.isValid())
611        ret = itemFromIndex (index);
612    }
613
614  return ret;
615}
616
617void
618FileTreeModel :: addFile (int                   fileIndex,
619                          const QString       & filename,
620                          bool                  wanted,
621                          int                   priority,
622                          uint64_t              totalSize,
623                          uint64_t              have,
624                          QList<QModelIndex>  & rowsAdded,
625                          bool                  updateFields)
626{
627  bool added = false;
628  FileTreeItem * item;
629  QStringList tokens = filename.split (QChar::fromLatin1('/'));
630
631  item = findItemForFileIndex (fileIndex);
632
633  if (item) // this file is already in the tree, we've added this
634    {
635      while (!tokens.isEmpty())
636        {
637          const QString token = tokens.takeLast();
638          const std::pair<int,int> changed = item->update (token, wanted, priority, have, updateFields);
639          if (changed.first >= 0)
640            dataChanged (indexOf (item, changed.first), indexOf (item, changed.second));
641          item = item->parent();
642        }
643      assert (item == myRootItem);
644    }
645  else // we haven't build the FileTreeItems for these tokens yet
646    {
647      item = myRootItem;
648      while (!tokens.isEmpty())
649        {
650          const QString token = tokens.takeFirst();
651          FileTreeItem * child(item->child(token));
652          if (!child)
653            {
654              added = true;
655              QModelIndex parentIndex (indexOf(item, 0));
656              const int n (item->childCount());
657
658              beginInsertRows (parentIndex, n, n);
659              if (tokens.isEmpty())
660                child = new FileTreeItem (token, fileIndex, totalSize);
661              else
662                child = new FileTreeItem (token);
663              item->appendChild (child);
664              endInsertRows ();
665
666              rowsAdded.append (indexOf(child, 0));
667            }
668          item = child;
669        }
670
671      if (item != myRootItem)
672        {
673          assert (item->fileIndex() == fileIndex);
674          assert (item->totalSize() == totalSize);
675
676          const std::pair<int,int> changed = item->update (item->name(), wanted, priority, have, added || updateFields);
677          if (changed.first >= 0)
678            dataChanged (indexOf (item, changed.first), indexOf (item, changed.second));
679        }
680    }
681}
682
683void
684FileTreeModel :: parentsChanged (const QModelIndex& index, int column)
685{
686  QModelIndex walk = index;
687
688  for (;;)
689    {
690      walk = parent(walk, column);
691      if(!walk.isValid())
692        break;
693
694      dataChanged(walk, walk);
695    }
696}
697
698void
699FileTreeModel :: subtreeChanged (const QModelIndex& index, int column)
700{
701  const int childCount = rowCount (index);
702  if (!childCount)
703    return;
704
705  // tell everyone that this tier changed
706  dataChanged (index.child(0,column), index.child(childCount-1,column));
707
708  // walk the subtiers
709  for (int i=0; i<childCount; ++i)
710    subtreeChanged (index.child(i,column), column);
711}
712
713void
714FileTreeModel :: clicked (const QModelIndex& index)
715{
716  const int column (index.column());
717
718  if (!index.isValid())
719    return;
720
721  if (column == COL_WANTED)
722    {
723      bool want;
724      QSet<int> file_ids;
725      FileTreeItem * item;
726
727      item = itemFromIndex (index);
728      item->twiddleWanted (file_ids, want);
729      emit wantedChanged (file_ids, want);
730
731      // this changes the name column's parenthetical size-wanted string too...
732      QModelIndex nameSibling = index.sibling (index.row(), COL_SIZE);
733      dataChanged (nameSibling, nameSibling);
734      parentsChanged (nameSibling, COL_SIZE);
735
736      dataChanged (index, index);
737      parentsChanged (index, column);
738      subtreeChanged (index, column);
739    }
740  else if (column == COL_PRIORITY)
741    {
742      int priority;
743      QSet<int> file_ids;
744      FileTreeItem * item;
745
746      item = itemFromIndex (index);
747      item->twiddlePriority (file_ids, priority);
748      emit priorityChanged (file_ids, priority);
749
750      dataChanged(index, index);
751      parentsChanged(index, column);
752      subtreeChanged(index, column);
753    }
754}
755
756void
757FileTreeModel :: doubleClicked (const QModelIndex& index)
758{
759  if (!index.isValid())
760    return;
761
762  const int column (index.column());
763  if (column == COL_WANTED || column == COL_PRIORITY)
764    return;
765
766  FileTreeItem * item = itemFromIndex (index);
767
768  if (item->childCount () == 0 && item->isComplete ())
769    emit openRequested (item->path ());
770}
771
772/****
773*****
774****/
775
776QSize
777FileTreeDelegate :: sizeHint(const QStyleOptionViewItem& item, const QModelIndex& index) const
778{
779  QSize size;
780
781  switch(index.column())
782    {
783      case COL_NAME:
784        {
785          const QFontMetrics fm(item.font);
786          const int iconSize = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
787          size.rwidth() = HIG::PAD_SMALL + iconSize;
788          size.rheight() = std::max(iconSize, fm.height());
789          break;
790        }
791
792      case COL_PROGRESS:
793      case COL_WANTED:
794        size = QSize(20, 1);
795        break;
796
797      default:
798        {
799          const QFontMetrics fm(item.font);
800          const QString text = index.data().toString();
801          size = fm.size(0, text);
802          break;
803        }
804    }
805
806  size.rheight() += 8; // make the spacing a little nicer
807  return size;
808}
809
810void
811FileTreeDelegate :: paint (QPainter                    * painter,
812                           const QStyleOptionViewItem  & option,
813                           const QModelIndex           & index) const
814{
815  const int column(index.column());
816
817  if ((column != COL_PROGRESS) && (column != COL_WANTED) && (column != COL_NAME))
818    {
819      QItemDelegate::paint(painter, option, index);
820      return;
821    }
822
823  QStyle * style (QApplication :: style());
824  if (option.state & QStyle::State_Selected)
825    painter->fillRect (option.rect, option.palette.highlight());
826  painter->save();
827  if (option.state & QStyle::State_Selected)
828    painter->setBrush (option.palette.highlightedText());
829
830  if (column == COL_NAME)
831    {
832      // draw the file icon
833      static const int iconSize (style->pixelMetric(QStyle :: PM_SmallIconSize));
834      const QRect iconArea (option.rect.x(),
835                            option.rect.y() + (option.rect.height()-iconSize)/2,
836                            iconSize, iconSize);
837      QIcon icon;
838      if (index.model()->hasChildren(index))
839        {
840          icon = style->standardIcon(QStyle::StandardPixmap(QStyle::SP_DirOpenIcon));
841        }
842      else
843        {
844          QString name = index.data().toString();
845          icon = Utils :: guessMimeIcon (name);
846        }
847      icon.paint (painter, iconArea, Qt::AlignCenter, QIcon::Normal, QIcon::On);
848
849      // draw the name
850      QStyleOptionViewItem tmp (option);
851      tmp.rect.setWidth (option.rect.width() - iconArea.width() - HIG::PAD_SMALL);
852      tmp.rect.moveRight (option.rect.right());
853      QItemDelegate::paint (painter, tmp, index);
854    }
855  else if(column == COL_PROGRESS)
856    {
857      QStyleOptionProgressBar p;
858      p.state = option.state | QStyle::State_Small;
859      p.direction = QApplication::layoutDirection();
860      p.rect = option.rect;
861      p.rect.setSize (QSize(option.rect.width()-2, option.rect.height()-8));
862      p.rect.moveCenter (option.rect.center());
863      p.fontMetrics = QApplication::fontMetrics();
864      p.minimum = 0;
865      p.maximum = 100;
866      p.textAlignment = Qt::AlignCenter;
867      p.textVisible = true;
868      p.progress = (int)(100.0*index.data().toDouble());
869      p.text = QString().sprintf("%d%%", p.progress);
870      style->drawControl(QStyle::CE_ProgressBar, &p, painter);
871    }
872  else if(column == COL_WANTED)
873    {
874      QStyleOptionButton o;
875      o.state = option.state;
876      o.direction = QApplication::layoutDirection();
877      o.rect.setSize (QSize(20, option.rect.height()));
878      o.rect.moveCenter (option.rect.center());
879      o.fontMetrics = QApplication::fontMetrics();
880      switch (index.data().toInt())
881        {
882          case Qt::Unchecked: o.state |= QStyle::State_Off; break;
883          case Qt::Checked:   o.state |= QStyle::State_On; break;
884          default:            o.state |= QStyle::State_NoChange;break;
885        }
886      style->drawControl (QStyle::CE_CheckBox, &o, painter);
887    }
888
889  painter->restore();
890}
891
892/****
893*****
894*****
895*****
896****/
897
898FileTreeView :: FileTreeView (QWidget * parent, bool isEditable):
899  QTreeView (parent),
900  myModel (this, isEditable),
901  myProxy (new QSortFilterProxyModel()),
902  myDelegate (this)
903{
904  setSortingEnabled (true);
905  setAlternatingRowColors (true);
906  setSelectionBehavior (QAbstractItemView::SelectRows);
907  setSelectionMode (QAbstractItemView::ExtendedSelection);
908  myProxy->setSourceModel (&myModel);
909  setModel (myProxy);
910  setItemDelegate (&myDelegate);
911  setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
912  sortByColumn (COL_NAME, Qt::AscendingOrder);
913  installEventFilter (this);
914
915  for (int i=0; i<NUM_COLUMNS; ++i)
916    {
917      setColumnHidden (i, (i<FIRST_VISIBLE_COLUMN) || (LAST_VISIBLE_COLUMN<i));
918
919#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
920      header()->setResizeMode(i, QHeaderView::Interactive);
921#else
922      header()->setSectionResizeMode(i, QHeaderView::Interactive);
923#endif
924    }
925
926  connect (this, SIGNAL(clicked(const QModelIndex&)),
927           this, SLOT(onClicked(const QModelIndex&)));
928
929  connect (this, SIGNAL(doubleClicked(const QModelIndex&)),
930           this, SLOT(onDoubleClicked(const QModelIndex&)));
931
932  connect (&myModel, SIGNAL(priorityChanged(const QSet<int>&, int)),
933           this,     SIGNAL(priorityChanged(const QSet<int>&, int)));
934
935  connect (&myModel, SIGNAL(wantedChanged(const QSet<int>&, bool)),
936           this,     SIGNAL(wantedChanged(const QSet<int>&, bool)));
937
938  connect (&myModel, SIGNAL(pathEdited(const QString&, const QString&)),
939           this,     SIGNAL(pathEdited(const QString&, const QString&)));
940
941  connect (&myModel, SIGNAL (openRequested (const QString&)),
942           this,     SLOT (onOpenRequested (const QString&)),
943           Qt::QueuedConnection);
944}
945
946FileTreeView :: ~FileTreeView ()
947{
948  myProxy->deleteLater();
949}
950
951void
952FileTreeView :: onClicked (const QModelIndex& proxyIndex)
953{
954  const QModelIndex modelIndex = myProxy->mapToSource (proxyIndex);
955  myModel.clicked (modelIndex);
956}
957
958void
959FileTreeView :: onDoubleClicked (const QModelIndex& proxyIndex)
960{
961  const QModelIndex modelIndex = myProxy->mapToSource (proxyIndex);
962  myModel.doubleClicked (modelIndex);
963}
964
965void
966FileTreeView :: onOpenRequested (const QString& path)
967{
968  if (state () == EditingState)
969    return;
970
971  emit openRequested (path);
972}
973
974bool
975FileTreeView :: eventFilter (QObject * o, QEvent * event)
976{
977  // this is kind of a hack to get the last three columns be the
978  // right size, and to have the filename column use whatever
979  // space is left over...
980  if ((o == this) && (event->type() == QEvent::Resize))
981    {
982      QResizeEvent * r = dynamic_cast<QResizeEvent*>(event);
983      int left = r->size().width();
984      const QFontMetrics fontMetrics(font());
985      for (int column=FIRST_VISIBLE_COLUMN; column<=LAST_VISIBLE_COLUMN; ++column)
986        {
987          if (column == COL_NAME)
988            continue;
989          if (isColumnHidden (column))
990            continue;
991
992          QString header;
993          if (column == COL_SIZE)
994            header = "999.9 KiB";
995          else
996            header = myModel.headerData (column, Qt::Horizontal).toString();
997          header += "    ";
998          const int width = fontMetrics.size (0, header).width();
999          setColumnWidth (column, width);
1000            left -= width;
1001        }
1002      left -= 20; // not sure why this is necessary.  it works in different themes + font sizes though...
1003      setColumnWidth(COL_NAME, std::max(left,0));
1004    }
1005
1006  return false;
1007}
1008
1009void
1010FileTreeView :: update (const FileList& files, bool updateFields)
1011{
1012  foreach (const TrFile file, files)
1013    {
1014      QList<QModelIndex> added;
1015      myModel.addFile (file.index, file.filename, file.wanted, file.priority, file.size, file.have, added, updateFields);
1016      foreach (QModelIndex i, added)
1017        expand (myProxy->mapFromSource(i));
1018    }
1019}
1020
1021void
1022FileTreeView :: clear ()
1023{
1024  myModel.clear();
1025}
Note: See TracBrowser for help on using the repository browser.