1 /* Copyright (C) 2012 Thomas Lübking <thomas.luebking@gmail.com>
2 
3    This file is part of the Trojita Qt IMAP e-mail client,
4    http://trojita.flaska.net/
5 
6    This program is free software; you can redistribute it and/or
7    modify it under the terms of the GNU General Public License as
8    published by the Free Software Foundation; either version 2 of
9    the License or (at your option) version 3 or any later version
10    accepted by the membership of KDE e.V. (or its successor approved
11    by the membership of KDE e.V.), which shall act as a proxy
12    defined in Section 14 of version 3 of the license.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22 
23 #include <QAction>
24 #include <QDragEnterEvent>
25 #include <QDebug>
26 #include <QInputDialog>
27 #include <QMenu>
28 #include "Composer/MessageComposer.h"
29 #include "Gui/ComposerAttachmentsList.h"
30 #include "Imap/Model/ItemRoles.h"
31 #include "UiUtils/IconLoader.h"
32 
ComposerAttachmentsList(QWidget * parent)33 ComposerAttachmentsList::ComposerAttachmentsList(QWidget *parent):
34     QListView(parent), m_dragging(false), m_dragInside(false), m_composer(0)
35 {
36     setMouseTracking( true );
37     setAcceptDrops(true);
38     setDragDropMode( DragDrop );
39     setDragDropOverwriteMode( false );
40     setDragEnabled(true);
41     setDropIndicatorShown(false);
42     setContextMenuPolicy(Qt::CustomContextMenu);
43     connect(this, &QWidget::customContextMenuRequested, this, &ComposerAttachmentsList::showContextMenu);
44 
45     m_actionSendInline = new QAction(UiUtils::loadIcon(QStringLiteral("insert-image")), tr("Send Inline"), this);
46     m_actionSendInline->setCheckable(true);
47     connect(m_actionSendInline, &QAction::triggered, this, &ComposerAttachmentsList::slotToggledContentDispositionInline);
48     addAction(m_actionSendInline);
49 
50     m_actionRename = new QAction(UiUtils::loadIcon(QStringLiteral("edit-rename")), tr("Rename..."), this);
51     connect(m_actionRename, &QAction::triggered, this, &ComposerAttachmentsList::slotRenameAttachment);
52     addAction(m_actionRename);
53 
54     m_actionRemoveAttachment = new QAction(UiUtils::loadIcon(QStringLiteral("list-remove")), tr("Remove"), this);
55     connect(m_actionRemoveAttachment, &QAction::triggered, this, &ComposerAttachmentsList::slotRemoveAttachment);
56     addAction(m_actionRemoveAttachment);
57 
58     connect(this, &ComposerAttachmentsList::itemDroppedOut, this, &ComposerAttachmentsList::slotRemoveAttachment);
59     connect(this, &QAbstractItemView::clicked, this, &ComposerAttachmentsList::onCurrentChanged);
60     connect(this, &QAbstractItemView::activated, this, &ComposerAttachmentsList::onCurrentChanged);
61 }
62 
setComposer(Composer::MessageComposer * composer)63 void ComposerAttachmentsList::setComposer(Composer::MessageComposer *composer)
64 {
65     // prevent double connections etc
66     Q_ASSERT(!m_composer);
67 
68     m_composer = composer;
69     setModel(m_composer);
70     connect(model(), &QAbstractItemModel::rowsRemoved, this, &ComposerAttachmentsList::onAttachmentNumberChanged);
71     connect(model(), &QAbstractItemModel::rowsInserted, this, &ComposerAttachmentsList::onAttachmentNumberChanged);
72     connect(model(), &QAbstractItemModel::layoutChanged, this, &ComposerAttachmentsList::onAttachmentNumberChanged);
73     connect(model(), &QAbstractItemModel::modelReset, this, &ComposerAttachmentsList::onAttachmentNumberChanged);
74 
75     onAttachmentNumberChanged();
76 }
77 
startDrag(Qt::DropActions da)78 void ComposerAttachmentsList::startDrag(Qt::DropActions da)
79 {
80     m_dragging = true;
81     m_dragInside = true;
82     const QModelIndex idx = indexAt(mapFromGlobal(QCursor::pos()));
83     if (!idx.isValid())
84         setCurrentIndex(idx);
85     QListView::startDrag(da);
86     m_dragging = false;
87     if (!m_dragInside) {
88         emit itemDroppedOut();
89     }
90 }
91 
92 
dragEnterEvent(QDragEnterEvent * de)93 void ComposerAttachmentsList::dragEnterEvent(QDragEnterEvent *de)
94 {
95     if (m_dragging)
96         m_dragInside = true;
97     QListView::dragEnterEvent(de);
98 }
99 
dragLeaveEvent(QDragLeaveEvent * de)100 void ComposerAttachmentsList::dragLeaveEvent(QDragLeaveEvent *de)
101 {
102     if (m_dragging)
103         m_dragInside = false;
104     QListView::dragLeaveEvent(de);
105 }
106 
slotRemoveAttachment()107 void ComposerAttachmentsList::slotRemoveAttachment()
108 {
109     m_composer->removeAttachment(currentIndex());
110 }
111 
slotToggledContentDispositionInline(bool checked)112 void ComposerAttachmentsList::slotToggledContentDispositionInline(bool checked)
113 {
114     m_composer->setAttachmentContentDisposition(currentIndex(), checked ? Composer::CDN_INLINE : Composer::CDN_ATTACHMENT);
115 }
116 
slotRenameAttachment()117 void ComposerAttachmentsList::slotRenameAttachment()
118 {
119     bool ok;
120     QString newName = QInputDialog::getText(this, tr("Rename Attachment"), tr("New Name"), QLineEdit::Normal,
121                                             currentIndex().data().toString(), &ok);
122     if (ok) {
123         m_composer->setAttachmentName(currentIndex(), newName);
124     }
125 }
126 
onAttachmentNumberChanged()127 void ComposerAttachmentsList::onAttachmentNumberChanged()
128 {
129     Q_ASSERT(model());
130     bool current = currentIndex().isValid();
131     m_actionRemoveAttachment->setEnabled(current);
132     m_actionSendInline->setEnabled(current);
133     m_actionRename->setEnabled(current);
134     onCurrentChanged();
135 }
136 
onCurrentChanged()137 void ComposerAttachmentsList::onCurrentChanged()
138 {
139     m_actionSendInline->setChecked(false);
140 
141     // This is needed because the QVariant::toInt returns zero when faced with null QVariant
142     bool ok;
143     int res = currentIndex().data(Imap::Mailbox::RoleAttachmentContentDispositionMode).toInt(&ok);
144     if (!ok)
145         return;
146 
147     switch (static_cast<Composer::ContentDisposition>(res)) {
148     case Composer::CDN_INLINE:
149         m_actionSendInline->setChecked(true);
150         break;
151     case Composer::CDN_ATTACHMENT:
152         // nothing is needed here
153         break;
154     }
155 }
156 
showContextMenu(const QPoint & pos)157 void ComposerAttachmentsList::showContextMenu(const QPoint &pos)
158 {
159     // Sometimes currentChanged() is not enough -- we really want to to have these actions to reflect the current selection, if any
160     onAttachmentNumberChanged();
161     QMenu::exec(actions(), mapToGlobal(pos), 0, this);
162 }
163