libkdepim

kfoldertree.cpp
1 // -*- c-basic-offset: 2 -*-
2 
3 #include "kfoldertree.h"
4 #include <klocale.h>
5 #include <kio/global.h>
6 #include <kiconloader.h>
7 #include <kdebug.h>
8 #include <kstringhandler.h>
9 #include <tqpainter.h>
10 #include <tqapplication.h>
11 #include <tqheader.h>
12 #include <tqstyle.h>
13 
14 //-----------------------------------------------------------------------------
15 KFolderTreeItem::KFolderTreeItem( KFolderTree *parent, const TQString & label,
16  Protocol protocol, Type type )
17  : KListViewItem( parent, label ), mProtocol( protocol ), mType( type ),
18  mUnread(-1), mTotal(0), mSize(0), mFolderIsCloseToQuota( false )
19 {
20 }
21 
22 //-----------------------------------------------------------------------------
23 KFolderTreeItem::KFolderTreeItem( KFolderTreeItem *parent,
24  const TQString & label, Protocol protocol, Type type,
25  int unread, int total )
26  : KListViewItem( parent, label ), mProtocol( protocol ), mType( type ),
27  mUnread( unread ), mTotal( total ), mSize(0), mFolderIsCloseToQuota( false )
28 {
29 }
30 
31 //-----------------------------------------------------------------------------
32 int KFolderTreeItem::protocolSortingKey() const
33 {
34  // protocol dependant sorting order:
35  // local < imap < news < search < other
36  switch ( mProtocol ) {
37  case Local:
38  return 1;
39  case CachedImap:
40  case Imap:
41  return 2;
42  case News:
43  return 3;
44  case Search:
45  return 4;
46  default:
47  return 42;
48  }
49 }
50 
51 //-----------------------------------------------------------------------------
52 int KFolderTreeItem::typeSortingKey() const
53 {
54  // type dependant sorting order:
55  // inbox < outbox < sent-mail < trash < drafts
56  // < calendar < contacts < notes < tasks
57  // < normal folders
58  switch ( mType ) {
59  case Inbox:
60  return 1;
61  case Outbox:
62  return 2;
63  case SentMail:
64  return 3;
65  case Trash:
66  return 4;
67  case Drafts:
68  return 5;
69  case Templates:
70  return 6;
71  case Calendar:
72  return 7;
73  case Contacts:
74  return 8;
75  case Notes:
76  return 9;
77  case Tasks:
78  return 10;
79  default:
80  return 42;
81  }
82 }
83 
84 //-----------------------------------------------------------------------------
85 int KFolderTreeItem::compare( TQListViewItem * i, int col, bool ) const
86 {
87  KFolderTreeItem* other = static_cast<KFolderTreeItem*>( i );
88 
89  if (col == 0)
90  {
91  // sort by folder
92 
93  // local root-folder
94  if ( depth() == 0 && mProtocol == NONE )
95  return -1;
96  if ( other->depth() == 0 && other->protocol() == NONE )
97  return 1;
98 
99  // first compare by protocol
100  int thisKey = protocolSortingKey();
101  int thatKey = other->protocolSortingKey();
102  if ( thisKey < thatKey )
103  return -1;
104  if ( thisKey > thatKey )
105  return 1;
106 
107  // then compare by type
108  thisKey = typeSortingKey();
109  thatKey = other->typeSortingKey();
110  if ( thisKey < thatKey )
111  return -1;
112  if ( thisKey > thatKey )
113  return 1;
114 
115  // and finally compare by name
116  return text( 0 ).localeAwareCompare( other->text( 0 ) );
117  }
118  else
119  {
120  // sort by unread or total-column
121  TQ_INT64 a = 0, b = 0;
122  if (col == static_cast<KFolderTree*>(listView())->unreadIndex())
123  {
124  a = mUnread;
125  b = other->unreadCount();
126  }
127  else if (col == static_cast<KFolderTree*>(listView())->totalIndex())
128  {
129  a = mTotal;
130  b = other->totalCount();
131  }
132  else if (col == static_cast<KFolderTree*>(listView())->sizeIndex())
133  {
134  a = mSize;
135  b = other->folderSize();
136  }
137 
138  if ( a == b )
139  return 0;
140  else
141  return (a < b ? -1 : 1);
142  }
143 }
144 
145 //-----------------------------------------------------------------------------
146 void KFolderTreeItem::setUnreadCount( int aUnread )
147 {
148  if ( aUnread < 0 ) return;
149 
150  mUnread = aUnread;
151 
152  TQString unread = TQString();
153  if (mUnread == 0)
154  unread = "- ";
155  else {
156  unread.setNum(mUnread);
157  unread += " ";
158  }
159 
160  setText( static_cast<KFolderTree*>(listView())->unreadIndex(),
161  unread );
162 }
163 
164 //-----------------------------------------------------------------------------
165 void KFolderTreeItem::setTotalCount( int aTotal )
166 {
167  if ( aTotal < 0 ) return;
168 
169  mTotal = aTotal;
170 
171  TQString total = TQString();
172  if (mTotal == 0)
173  total = "- ";
174  else {
175  total.setNum(mTotal);
176  total += " ";
177  }
178 
179  setText( static_cast<KFolderTree*>(listView())->totalIndex(),
180  total );
181 }
182 
183 //-----------------------------------------------------------------------------
184 void KFolderTreeItem::setFolderSize( TQ_INT64 aSize )
185 {
186  if ( aSize < 0 ) return; // we need to update even if nothing changed, kids ...
187 
188  mSize = aSize;
189 
190  TQString size;
191  if (mType != Root) {
192  if (mSize == 0 && (childCount() == 0 || isOpen() ) )
193  size = "- ";
194  else
195  size = KIO::convertSize(mSize);
196  }
197  if ( childCount() > 0 && !isOpen() ) {
198  TQ_INT64 recursiveSize = recursiveFolderSize();
199  if ( recursiveSize != mSize ) {
200  if ( mType != Root )
201  size += TQString::fromLatin1(" + %1").arg( KIO::convertSize( recursiveSize - mSize ) );
202  else
203  size = KIO::convertSize( recursiveSize );
204  }
205  }
206  size += " ";
207 
208  setText( static_cast<KFolderTree*>(listView())->sizeIndex(), size );
209 }
210 
211 //-----------------------------------------------------------------------------
212 TQ_INT64 KFolderTreeItem::recursiveFolderSize() const
213 {
214  TQ_INT64 size = mSize;
215 
216  for ( TQListViewItem *item = firstChild() ;
217  item ; item = item->nextSibling() )
218  {
219  size += static_cast<KFolderTreeItem*>(item)->recursiveFolderSize();
220  }
221  return size;
222 }
223 
224 
225 
226 //-----------------------------------------------------------------------------
227 int KFolderTreeItem::countUnreadRecursive()
228 {
229  int count = (mUnread > 0) ? mUnread : 0;
230 
231  for ( TQListViewItem *item = firstChild() ;
232  item ; item = item->nextSibling() )
233  {
234  count += static_cast<KFolderTreeItem*>(item)->countUnreadRecursive();
235  }
236 
237  return count;
238 }
239 
240 //-----------------------------------------------------------------------------
241 void KFolderTreeItem::paintCell( TQPainter * p, const TQColorGroup & cg,
242  int column, int width, int align )
243 {
244  KFolderTree *ft = static_cast<KFolderTree*>(listView());
245 
246  const int unreadRecursiveCount = countUnreadRecursive();
247  const int unreadCount = ( mUnread > 0 ) ? mUnread : 0;
248 
249 
250  // use a special color for folders which are close to their quota
251  TQColorGroup mycg = cg;
252  if ( ( column == 0 || column == ft->sizeIndex() ) && folderIsCloseToQuota() )
253  {
254  mycg.setColor( TQColorGroup::Text, ft->paintInfo().colCloseToQuota );
255  }
256 
257  // use a bold-font for the folder- and the unread-columns
258  if ( (column == 0 || column == ft->unreadIndex())
259  && ( unreadCount > 0
260  || ( !isOpen() && unreadRecursiveCount > 0 ) ) )
261  {
262  TQFont f = p->font();
263  f.setWeight(TQFont::Bold);
264  p->setFont(f);
265  }
266 
267 
268  // most cells can be handled by KListView::paintCell, we only need to
269  // deal with the folder column if the unread column is not shown
270 
271  /* The below is exceedingly silly, but Ingo insists that the unread
272  * count that is shown in parenthesis after the folder name must
273  * be configurable in color. That means that paintCell needs to do
274  * two painting passes which flickers. Since that flicker is not
275  * needed when there is the unread column, special case that. */
276  if ( ft->isUnreadActive() || column != 0 ) {
277  KListViewItem::paintCell( p, mycg, column, width, align );
278  } else {
279  TQListView *lv = listView();
280  TQString oldText = text(column);
281 
282  // set an empty text so that we can have our own implementation (see further down)
283  // but still benefit from KListView::paintCell
284  setText( column, "" );
285 
286  KListViewItem::paintCell( p, mycg, column, width, align );
287 
288  const TQPixmap *icon = pixmap( column );
289  int marg = lv ? lv->itemMargin() : 1;
290  int r = marg;
291 
292  setText( column, oldText );
293  if ( isSelected() )
294  p->setPen( mycg.highlightedText() );
295  else
296  p->setPen( mycg.color( TQColorGroup::Text ) );
297 
298  if ( icon ) {
299  r += icon->width() + marg;
300  }
301  TQString t = text( column );
302  if (t.isEmpty())
303  return;
304 
305  // draw the unread-count if the unread-column is not active
306  TQString unread;
307 
308  if ( unreadCount > 0 || ( !isOpen() && unreadRecursiveCount > 0 ) ) {
309  if ( isOpen() )
310  unread = " (" + TQString::number( unreadCount ) + ")";
311  else if ( unreadRecursiveCount == unreadCount || mType == Root )
312  unread = " (" + TQString::number( unreadRecursiveCount ) + ")";
313  else
314  unread = " (" + TQString::number( unreadCount ) + " + " +
315  TQString::number( unreadRecursiveCount-unreadCount ) + ")";
316  }
317 
318  // check if the text needs to be squeezed
319  TQFontMetrics fm( p->fontMetrics() );
320  int unreadWidth = fm.width( unread );
321  if ( fm.width( t ) + marg + r + unreadWidth > width )
322  t = squeezeFolderName( t, fm, width - marg - r - unreadWidth );
323 
324  TQRect br;
325  p->drawText( r, 0, width-marg-r, height(),
326  align | AlignVCenter, t, -1, &br );
327 
328  if ( !unread.isEmpty() ) {
329  if (!isSelected())
330  p->setPen( ft->paintInfo().colUnread );
331  p->drawText( br.right(), 0, width-marg-br.right(), height(),
332  align | AlignVCenter, unread );
333  }
334  }
335 }
336 
337 
338 TQString KFolderTreeItem::squeezeFolderName( const TQString &text,
339  const TQFontMetrics &fm,
340  uint width ) const
341 {
342  return KStringHandler::rPixelSqueeze( text, fm, width );
343 }
344 
345 bool KFolderTreeItem::folderIsCloseToQuota() const
346 {
347  return mFolderIsCloseToQuota;
348 }
349 
350 void KFolderTreeItem::setFolderIsCloseToQuota( bool v )
351 {
352  if ( mFolderIsCloseToQuota != v) {
353  mFolderIsCloseToQuota = v;
354  repaint();
355  }
356 }
357 
358 
359 //=============================================================================
360 
361 
362 KFolderTree::KFolderTree( TQWidget *parent, const char* name )
363  : KListView( parent, name ), mUnreadIndex(-1), mTotalIndex(-1), mSizeIndex(-1)
364 {
365  // GUI-options
366  setStyleDependantFrameWidth();
367  setAcceptDrops(true);
368  setDropVisualizer(false);
369  setAllColumnsShowFocus(true);
370  setShowSortIndicator(true);
371  setUpdatesEnabled(true);
372  setItemsRenameable(false);
373  setRootIsDecorated(true);
374  setSelectionModeExt(Extended);
375  setAlternateBackground(TQColor());
376 #if KDE_IS_VERSION( 3, 3, 90 )
377  setShadeSortColumn ( false );
378 #endif
379  setFullWidth(true);
380  disableAutoSelection();
381  setColumnWidth( 0, 120 ); //reasonable default size
382 
383  disconnect( header(), TQT_SIGNAL( sizeChange( int, int, int ) ) );
384  connect( header(), TQT_SIGNAL( sizeChange( int, int, int ) ),
385  TQT_SLOT( slotSizeChanged( int, int, int ) ) );
386 }
387 
388 //-----------------------------------------------------------------------------
389 void KFolderTree::setStyleDependantFrameWidth()
390 {
391  // set the width of the frame to a reasonable value for the current GUI style
392  int frameWidth;
393  if( style().isA("KeramikStyle") )
394  frameWidth = style().pixelMetric( TQStyle::PM_DefaultFrameWidth ) - 1;
395  else
396  frameWidth = style().pixelMetric( TQStyle::PM_DefaultFrameWidth );
397  if ( frameWidth < 0 )
398  frameWidth = 0;
399  if ( frameWidth != lineWidth() )
400  setLineWidth( frameWidth );
401 }
402 
403 //-----------------------------------------------------------------------------
404 void KFolderTree::styleChange( TQStyle& oldStyle )
405 {
406  setStyleDependantFrameWidth();
407  KListView::styleChange( oldStyle );
408 }
409 
410 //-----------------------------------------------------------------------------
411 void KFolderTree::drawContentsOffset( TQPainter * p, int ox, int oy,
412  int cx, int cy, int cw, int ch )
413 {
414  bool oldUpdatesEnabled = isUpdatesEnabled();
415  setUpdatesEnabled(false);
416  KListView::drawContentsOffset( p, ox, oy, cx, cy, cw, ch );
417  setUpdatesEnabled(oldUpdatesEnabled);
418 }
419 
420 //-----------------------------------------------------------------------------
421 void KFolderTree::contentsMousePressEvent( TQMouseEvent *e )
422 {
423  setSelectionModeExt(Single);
424  KListView::contentsMousePressEvent(e);
425 }
426 
427 //-----------------------------------------------------------------------------
428 void KFolderTree::contentsMouseReleaseEvent( TQMouseEvent *e )
429 {
430  KListView::contentsMouseReleaseEvent(e);
431  setSelectionModeExt(Extended);
432 }
433 
434 //-----------------------------------------------------------------------------
435 void KFolderTree::addAcceptableDropMimetype( const char *mimeType, bool outsideOk )
436 {
437  int oldSize = mAcceptableDropMimetypes.size();
438  mAcceptableDropMimetypes.resize(oldSize+1);
439  mAcceptOutside.resize(oldSize+1);
440 
441  mAcceptableDropMimetypes.at(oldSize) = mimeType;
442  mAcceptOutside.setBit(oldSize, outsideOk);
443 }
444 
445 //-----------------------------------------------------------------------------
446 bool KFolderTree::acceptDrag( TQDropEvent* event ) const
447 {
448  TQListViewItem* item = itemAt(contentsToViewport(event->pos()));
449 
450  for (uint i = 0; i < mAcceptableDropMimetypes.size(); i++)
451  {
452  if (event->provides(mAcceptableDropMimetypes[i]))
453  {
454  if (item)
455  return (static_cast<KFolderTreeItem*>(item))->acceptDrag(event);
456  else
457  return mAcceptOutside[i];
458  }
459  }
460  return false;
461 }
462 
463 //-----------------------------------------------------------------------------
464 void KFolderTree::addUnreadColumn( const TQString & name, int width )
465 {
466  mUnreadIndex = addColumn( name, width );
467  setColumnAlignment( mUnreadIndex, tqApp->reverseLayout() ? TQt::AlignLeft : TQt::AlignRight );
468  header()->adjustHeaderSize();
469 }
470 
471 //-----------------------------------------------------------------------------
472 void KFolderTree::addTotalColumn( const TQString & name, int width )
473 {
474  mTotalIndex = addColumn( name, width );
475  setColumnAlignment( mTotalIndex, tqApp->reverseLayout() ? TQt::AlignLeft : TQt::AlignRight );
476  header()->adjustHeaderSize();
477 }
478 
479 //-----------------------------------------------------------------------------
480 void KFolderTree::removeUnreadColumn()
481 {
482  if ( !isUnreadActive() ) return;
483  removeColumn( mUnreadIndex );
484  if ( isTotalActive() && mTotalIndex > mUnreadIndex )
485  mTotalIndex--;
486  if ( isSizeActive() && mSizeIndex > mUnreadIndex )
487  mSizeIndex--;
488 
489  mUnreadIndex = -1;
490  header()->adjustHeaderSize();
491 }
492 
493 //-----------------------------------------------------------------------------
494 void KFolderTree::removeTotalColumn()
495 {
496  if ( !isTotalActive() ) return;
497  removeColumn( mTotalIndex );
498  if ( isUnreadActive() && mTotalIndex < mUnreadIndex )
499  mUnreadIndex--;
500  if ( isSizeActive() && mTotalIndex < mSizeIndex )
501  mSizeIndex--;
502  mTotalIndex = -1;
503  header()->adjustHeaderSize();
504 }
505 
506 //-----------------------------------------------------------------------------
507 void KFolderTree::addSizeColumn( const TQString & name, int width )
508 {
509  mSizeIndex = addColumn( name, width );
510  setColumnAlignment( mSizeIndex, tqApp->reverseLayout() ? TQt::AlignLeft : TQt::AlignRight );
511  header()->adjustHeaderSize();
512 }
513 
514 //-----------------------------------------------------------------------------
515 void KFolderTree::removeSizeColumn()
516 {
517  if ( !isSizeActive() ) return;
518  removeColumn( mSizeIndex );
519  if ( isUnreadActive() && mSizeIndex < mUnreadIndex )
520  mUnreadIndex--;
521  if ( isTotalActive() && mSizeIndex < mTotalIndex )
522  mTotalIndex--;
523  mSizeIndex = -1;
524  header()->adjustHeaderSize();
525 }
526 
527 
528 //-----------------------------------------------------------------------------
529 void KFolderTree::setFullWidth( bool fullWidth )
530 {
531  if (fullWidth)
532  header()->setStretchEnabled( true, 0 );
533 }
534 
535 //-----------------------------------------------------------------------------
536 void KFolderTree::slotSizeChanged( int section, int, int newSize )
537 {
538  viewport()->repaint(
539  header()->sectionPos(section), 0, newSize, visibleHeight(), false );
540 }
541 
542 #include "kfoldertree.moc"