|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the Qt Messaging Framework. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:BSD$ |
|
10 ** You may use this file under the terms of the BSD license as follows: |
|
11 ** |
|
12 ** "Redistribution and use in source and binary forms, with or without |
|
13 ** modification, are permitted provided that the following conditions are |
|
14 ** met: |
|
15 ** * Redistributions of source code must retain the above copyright |
|
16 ** notice, this list of conditions and the following disclaimer. |
|
17 ** * Redistributions in binary form must reproduce the above copyright |
|
18 ** notice, this list of conditions and the following disclaimer in |
|
19 ** the documentation and/or other materials provided with the |
|
20 ** distribution. |
|
21 ** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor |
|
22 ** the names of its contributors may be used to endorse or promote |
|
23 ** products derived from this software without specific prior written |
|
24 ** permission. |
|
25 ** |
|
26 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
27 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
28 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
29 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
30 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
31 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
32 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
33 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
34 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
35 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
36 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
|
37 ** $QT_END_LICENSE$ |
|
38 ** |
|
39 ****************************************************************************/ |
|
40 |
|
41 #include "attachmentlistwidget.h" |
|
42 #include <QStringListModel> |
|
43 #include <QListView> |
|
44 #include <QVBoxLayout> |
|
45 #include <QLabel> |
|
46 #include <QDebug> |
|
47 #include <QMessageBox> |
|
48 #include <QDialogButtonBox> |
|
49 #include <QTreeView> |
|
50 #include <QFileInfo> |
|
51 #include <QItemDelegate> |
|
52 #include <QPainter> |
|
53 #include <QPointer> |
|
54 #include <QMouseEvent> |
|
55 #include <QHeaderView> |
|
56 //#include <support/qmailnamespace.h> |
|
57 |
|
58 class AttachmentListWidget; |
|
59 |
|
60 static QString sizeString(uint size) |
|
61 { |
|
62 if(size < 1024) |
|
63 return QObject::tr("%n byte(s)", "", size); |
|
64 else if(size < (1024 * 1024)) |
|
65 return QObject::tr("%1 KB").arg(((float)size)/1024.0, 0, 'f', 1); |
|
66 else if(size < (1024 * 1024 * 1024)) |
|
67 return QObject::tr("%1 MB").arg(((float)size)/(1024.0 * 1024.0), 0, 'f', 1); |
|
68 else |
|
69 return QObject::tr("%1 GB").arg(((float)size)/(1024.0 * 1024.0 * 1024.0), 0, 'f', 1); |
|
70 } |
|
71 |
|
72 static QStringList headers(QStringList() << "Attachment" << "Size" << "Type" << ""); |
|
73 |
|
74 class AttachmentListHeader : public QHeaderView |
|
75 { |
|
76 Q_OBJECT |
|
77 public: |
|
78 AttachmentListHeader(AttachmentListWidget* parent); |
|
79 |
|
80 signals: |
|
81 void clear(); |
|
82 |
|
83 protected: |
|
84 void paintSection(QPainter * painter, const QRect & rect, int logicalIndex) const; |
|
85 #ifndef QT_NO_CURSOR |
|
86 bool viewportEvent(QEvent* e); |
|
87 void mouseMoveEvent(QMouseEvent* e); |
|
88 #endif |
|
89 void mousePressEvent(QMouseEvent* e); |
|
90 bool overRemoveLink(QMouseEvent* e); |
|
91 |
|
92 |
|
93 private: |
|
94 AttachmentListWidget* m_parent; |
|
95 mutable QRect m_removeButtonRect; |
|
96 |
|
97 }; |
|
98 |
|
99 AttachmentListHeader::AttachmentListHeader(AttachmentListWidget* parent) |
|
100 : |
|
101 QHeaderView(Qt::Horizontal,parent), |
|
102 m_parent(parent) |
|
103 { |
|
104 } |
|
105 |
|
106 void AttachmentListHeader::paintSection(QPainter * painter, const QRect & rect, int logicalIndex) const |
|
107 { |
|
108 if(logicalIndex == 3 && m_parent->attachments().count() > 1) |
|
109 { |
|
110 painter->save(); |
|
111 QFont font = painter->font(); |
|
112 font.setUnderline(true); |
|
113 painter->setFont(font); |
|
114 painter->drawText(rect,Qt::AlignHCenter | Qt::AlignVCenter,"Remove All",&m_removeButtonRect); |
|
115 painter->restore(); |
|
116 } |
|
117 else |
|
118 QHeaderView::paintSection(painter,rect,logicalIndex); |
|
119 } |
|
120 |
|
121 #ifndef QT_NO_CURSOR |
|
122 bool AttachmentListHeader::viewportEvent(QEvent* e) |
|
123 { |
|
124 if(e->type() == QEvent::Leave) |
|
125 setCursor(QCursor()); |
|
126 return QAbstractItemView::viewportEvent(e); |
|
127 } |
|
128 |
|
129 void AttachmentListHeader::mouseMoveEvent(QMouseEvent* e) |
|
130 { |
|
131 QHeaderView::mouseMoveEvent(e); |
|
132 if(overRemoveLink(e)) |
|
133 { |
|
134 QCursor handCursor(Qt::PointingHandCursor); |
|
135 setCursor(handCursor); |
|
136 } |
|
137 else if(cursor().shape() == Qt::PointingHandCursor) |
|
138 setCursor(QCursor()); |
|
139 } |
|
140 #endif |
|
141 |
|
142 void AttachmentListHeader::mousePressEvent(QMouseEvent* e) |
|
143 { |
|
144 if(overRemoveLink(e)) |
|
145 emit clear(); |
|
146 QHeaderView::mousePressEvent(e); |
|
147 } |
|
148 |
|
149 bool AttachmentListHeader::overRemoveLink(QMouseEvent* e) |
|
150 { |
|
151 return m_removeButtonRect.contains(e->pos()); |
|
152 } |
|
153 |
|
154 class AttachmentListDelegate : public QItemDelegate |
|
155 { |
|
156 Q_OBJECT |
|
157 public: |
|
158 AttachmentListDelegate(AttachmentListWidget* parent = 0); |
|
159 void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const; |
|
160 bool isOverRemoveLink(const QRect& parentRect, const QPoint& pos) const; |
|
161 |
|
162 protected slots: |
|
163 bool helpEvent(QHelpEvent * event, QAbstractItemView * view, const QStyleOptionViewItem & option, const QModelIndex & index); |
|
164 |
|
165 private: |
|
166 QPointer<AttachmentListWidget> m_parent; |
|
167 }; |
|
168 |
|
169 AttachmentListDelegate::AttachmentListDelegate(AttachmentListWidget* parent) |
|
170 : |
|
171 QItemDelegate(parent), |
|
172 m_parent(parent) |
|
173 { |
|
174 } |
|
175 |
|
176 void AttachmentListDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const |
|
177 { |
|
178 if(index.isValid() && index.column() == 3) |
|
179 { |
|
180 painter->save(); |
|
181 QFont font = painter->font(); |
|
182 QColor c = option.palette.brush(QPalette::Link).color(); |
|
183 font.setUnderline(true); |
|
184 painter->setPen(c); |
|
185 painter->setFont(font); |
|
186 painter->drawText(option.rect,Qt::AlignHCenter,"Remove"); |
|
187 painter->restore(); |
|
188 } |
|
189 else |
|
190 QItemDelegate::paint(painter,option,index); |
|
191 } |
|
192 |
|
193 bool AttachmentListDelegate::isOverRemoveLink(const QRect& parentRect, const QPoint& pos) const |
|
194 { |
|
195 QFont font; |
|
196 font.setUnderline(true); |
|
197 QFontMetrics fm(font); |
|
198 QRect textRect = fm.boundingRect(parentRect,Qt::AlignHCenter,"Remove"); |
|
199 return textRect.contains(pos); |
|
200 } |
|
201 |
|
202 bool AttachmentListDelegate::helpEvent(QHelpEvent *, QAbstractItemView *view, const QStyleOptionViewItem &, const QModelIndex &index) |
|
203 { |
|
204 if (!index.isValid()) { |
|
205 view->setToolTip(QString()); |
|
206 return false; |
|
207 } |
|
208 |
|
209 QString attachment = m_parent->attachmentAt(index.row()); |
|
210 view->setToolTip(attachment); |
|
211 return false; |
|
212 } |
|
213 |
|
214 class AttachmentListView : public QTreeView |
|
215 { |
|
216 Q_OBJECT |
|
217 |
|
218 public: |
|
219 AttachmentListView(QWidget* parent = 0); |
|
220 |
|
221 signals: |
|
222 void removeAttachmentAtIndex(int index); |
|
223 |
|
224 protected: |
|
225 #ifndef QT_NO_CURSOR |
|
226 bool viewportEvent(QEvent* e); |
|
227 void mouseMoveEvent(QMouseEvent* e); |
|
228 #endif |
|
229 void mousePressEvent(QMouseEvent* e); |
|
230 bool overRemoveLink(QMouseEvent* e); |
|
231 }; |
|
232 |
|
233 AttachmentListView::AttachmentListView(QWidget* parent) |
|
234 : |
|
235 QTreeView(parent) |
|
236 { |
|
237 setMouseTracking(true); |
|
238 installEventFilter(this); |
|
239 } |
|
240 |
|
241 #ifndef QT_NO_CURSOR |
|
242 bool AttachmentListView::viewportEvent(QEvent* e) |
|
243 { |
|
244 if(e->type() == QEvent::Leave) |
|
245 setCursor(QCursor()); |
|
246 return QAbstractItemView::viewportEvent(e); |
|
247 } |
|
248 |
|
249 void AttachmentListView::mouseMoveEvent(QMouseEvent* e) |
|
250 { |
|
251 if(overRemoveLink(e)) |
|
252 { |
|
253 QCursor handCursor(Qt::PointingHandCursor); |
|
254 setCursor(handCursor); |
|
255 } |
|
256 else if(cursor().shape() == Qt::PointingHandCursor) |
|
257 setCursor(QCursor()); |
|
258 QTreeView::mouseMoveEvent(e); |
|
259 } |
|
260 #endif |
|
261 |
|
262 void AttachmentListView::mousePressEvent(QMouseEvent* e) |
|
263 { |
|
264 if(overRemoveLink(e)) |
|
265 { |
|
266 QModelIndex index = indexAt(e->pos()); |
|
267 emit removeAttachmentAtIndex(index.row()); |
|
268 } |
|
269 QTreeView::mousePressEvent(e); |
|
270 } |
|
271 |
|
272 bool AttachmentListView::overRemoveLink(QMouseEvent* e) |
|
273 { |
|
274 QModelIndex index = indexAt(e->pos()); |
|
275 if(index.isValid() && index.column() == 3) |
|
276 { |
|
277 AttachmentListDelegate* delegate = static_cast<AttachmentListDelegate*>(itemDelegate()); |
|
278 return delegate->isOverRemoveLink(visualRect(index),e->pos()); |
|
279 } |
|
280 return false; |
|
281 } |
|
282 |
|
283 class AttachmentListModel : public QAbstractListModel |
|
284 { |
|
285 public: |
|
286 AttachmentListModel(QWidget* parent ); |
|
287 QVariant headerData(int section,Qt::Orientation orientation, int role = Qt::DisplayRole ) const; |
|
288 |
|
289 bool isEmpty() const; |
|
290 |
|
291 int columnCount(const QModelIndex & parent = QModelIndex()) const; |
|
292 int rowCount(const QModelIndex & parent = QModelIndex()) const; |
|
293 |
|
294 QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; |
|
295 |
|
296 QStringList attachments() const; |
|
297 void setAttachments(const QStringList& attachments); |
|
298 |
|
299 private: |
|
300 QStringList m_attachments; |
|
301 }; |
|
302 |
|
303 AttachmentListModel::AttachmentListModel(QWidget* parent) |
|
304 : |
|
305 QAbstractListModel(parent) |
|
306 { |
|
307 } |
|
308 |
|
309 QVariant AttachmentListModel::headerData(int section, Qt::Orientation o, int role) const |
|
310 { |
|
311 if (role == Qt::DisplayRole) |
|
312 { |
|
313 if(section < headers.count()) |
|
314 return headers.at(section); |
|
315 } |
|
316 |
|
317 return QAbstractListModel::headerData(section,o,role); |
|
318 } |
|
319 |
|
320 bool AttachmentListModel::isEmpty() const |
|
321 { |
|
322 return m_attachments.isEmpty(); |
|
323 } |
|
324 |
|
325 int AttachmentListModel::columnCount(const QModelIndex & parent ) const |
|
326 { |
|
327 Q_UNUSED(parent); |
|
328 return headers.count(); |
|
329 } |
|
330 |
|
331 int AttachmentListModel::rowCount(const QModelIndex& parent) const |
|
332 { |
|
333 Q_UNUSED(parent); |
|
334 return m_attachments.count(); |
|
335 } |
|
336 |
|
337 QVariant AttachmentListModel::data( const QModelIndex & index, int role) const |
|
338 { |
|
339 if(index.isValid()) |
|
340 { |
|
341 if(role == Qt::DisplayRole && index.isValid()) |
|
342 { |
|
343 QString path = m_attachments.at(index.row()); |
|
344 QFileInfo fi(path); |
|
345 |
|
346 switch(index.column()) |
|
347 { |
|
348 case 0: |
|
349 return fi.fileName(); |
|
350 break; |
|
351 case 1: |
|
352 return sizeString(fi.size()); |
|
353 break; |
|
354 case 2: |
|
355 //QString mimeType = QMail::mimeTypeFromFileName(path); |
|
356 //if(mimeType.isEmpty()) mimeType = "Unknown"; |
|
357 //return mimeType; |
|
358 return "Unknown"; |
|
359 break; |
|
360 } |
|
361 } |
|
362 else if((role == Qt::DecorationRole || role == Qt::CheckStateRole )&& index.column() > 0) |
|
363 return QVariant(); |
|
364 else if(role == Qt::DecorationRole) |
|
365 { |
|
366 static QIcon attachIcon( ":icon/attach" ); |
|
367 return attachIcon; |
|
368 } |
|
369 } |
|
370 return QVariant(); |
|
371 } |
|
372 |
|
373 QStringList AttachmentListModel::attachments() const |
|
374 { |
|
375 return m_attachments; |
|
376 } |
|
377 |
|
378 void AttachmentListModel::setAttachments(const QStringList& attachments) |
|
379 { |
|
380 m_attachments.clear(); |
|
381 |
|
382 foreach (const QString &att, attachments) { |
|
383 if (!att.startsWith("ref:") && !att.startsWith("partRef:")) { |
|
384 m_attachments.append(att); |
|
385 } |
|
386 } |
|
387 |
|
388 reset(); |
|
389 } |
|
390 |
|
391 AttachmentListWidget::AttachmentListWidget(QWidget* parent) |
|
392 : |
|
393 QWidget(parent), |
|
394 m_listView(new AttachmentListView(this)), |
|
395 m_model(new AttachmentListModel(this)), |
|
396 m_delegate(new AttachmentListDelegate(this)), |
|
397 m_clearLink(new QLabel(this)) |
|
398 { |
|
399 m_clearLink->setTextInteractionFlags(Qt::LinksAccessibleByMouse); |
|
400 m_clearLink->setTextFormat(Qt::RichText); |
|
401 |
|
402 m_listView->setModel(m_model); |
|
403 m_listView->setSelectionMode(QAbstractItemView::NoSelection); |
|
404 AttachmentListHeader* header = new AttachmentListHeader(this); |
|
405 connect(header,SIGNAL(clear()),this,SLOT(clearClicked())); |
|
406 m_listView->setHeader(header); |
|
407 m_listView->header()->setStretchLastSection(true); |
|
408 m_listView->header()->setResizeMode(QHeaderView::ResizeToContents); |
|
409 m_listView->header()->setDefaultSectionSize(180); |
|
410 m_listView->setUniformRowHeights(true); |
|
411 m_listView->setRootIsDecorated(false); |
|
412 m_listView->setItemDelegate(m_delegate); |
|
413 |
|
414 |
|
415 QVBoxLayout* layout = new QVBoxLayout(this); |
|
416 layout->setSpacing(0); |
|
417 layout->setContentsMargins(0,0,0,0); |
|
418 layout->addWidget(m_listView); |
|
419 |
|
420 connect(m_clearLink,SIGNAL(linkActivated(QString)),this,SLOT(clearClicked())); |
|
421 connect(m_listView,SIGNAL(removeAttachmentAtIndex(int)),this,SLOT(removeAttachmentAtIndex(int))); |
|
422 } |
|
423 |
|
424 QStringList AttachmentListWidget::attachments() const |
|
425 { |
|
426 return m_attachments; |
|
427 } |
|
428 |
|
429 QString AttachmentListWidget::attachmentAt(int index) const |
|
430 { |
|
431 return m_attachments.at(index); |
|
432 } |
|
433 |
|
434 int AttachmentListWidget::count() const |
|
435 { |
|
436 return m_attachments.count(); |
|
437 } |
|
438 |
|
439 bool AttachmentListWidget::isEmpty() const |
|
440 { |
|
441 return m_attachments.isEmpty(); |
|
442 } |
|
443 |
|
444 void AttachmentListWidget::addAttachment(const QString& attachment) |
|
445 { |
|
446 if(m_attachments.contains(attachment)) |
|
447 return; |
|
448 |
|
449 m_attachments.append(attachment); |
|
450 |
|
451 m_model->setAttachments(m_attachments); |
|
452 setVisible(!m_model->isEmpty()); |
|
453 |
|
454 emit attachmentsAdded(QStringList() << attachment); |
|
455 } |
|
456 |
|
457 void AttachmentListWidget::addAttachments(const QStringList& attachments) |
|
458 { |
|
459 QSet<QString> newAttachments = attachments.toSet() - m_attachments.toSet(); |
|
460 |
|
461 if (!newAttachments.isEmpty()) { |
|
462 m_attachments += newAttachments.toList(); |
|
463 |
|
464 m_model->setAttachments(m_attachments); |
|
465 setVisible(!m_model->isEmpty()); |
|
466 |
|
467 emit attachmentsAdded(newAttachments.toList()); |
|
468 } |
|
469 } |
|
470 |
|
471 void AttachmentListWidget::removeAttachment(const QString& attachment) |
|
472 { |
|
473 if (!m_attachments.contains(attachment)) |
|
474 return; |
|
475 |
|
476 m_attachments.removeAll(attachment); |
|
477 |
|
478 m_model->setAttachments(m_attachments); |
|
479 setVisible(!m_model->isEmpty()); |
|
480 |
|
481 emit attachmentsRemoved(attachment); |
|
482 } |
|
483 |
|
484 void AttachmentListWidget::clear() |
|
485 { |
|
486 m_attachments.clear(); |
|
487 m_model->setAttachments(m_attachments); |
|
488 setVisible(false); |
|
489 } |
|
490 |
|
491 void AttachmentListWidget::clearClicked() |
|
492 { |
|
493 if(QMessageBox::question(this, |
|
494 "Remove attachments", |
|
495 QString("Remove %1 attachments?").arg(m_attachments.count()), |
|
496 QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) |
|
497 clear(); |
|
498 } |
|
499 |
|
500 void AttachmentListWidget::removeAttachmentAtIndex(int index) |
|
501 { |
|
502 if(index >= m_attachments.count()) |
|
503 return; |
|
504 |
|
505 QString attachment = m_attachments.at(index); |
|
506 m_attachments.removeAt(index); |
|
507 |
|
508 m_model->setAttachments(m_attachments); |
|
509 setVisible(!m_model->isEmpty()); |
|
510 |
|
511 emit attachmentsRemoved(attachment); |
|
512 } |
|
513 |
|
514 #include <attachmentlistwidget.moc> |
|
515 |