1 /* Copyright (C) 2006 - 2015 Jan Kundrát <jkt@kde.org>
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 #include "Gui/AddressRowWidget.h"
23 #include "Gui/FlowLayout.h"
24 #include "Gui/OneEnvelopeAddress.h"
25 
26 #include <QLabel>
27 #include <QMouseEvent>
28 #include <QPainter>
29 #include <QTextDocument>
30 
31 namespace Gui {
32 
33 static const int nonExpandingLength = 3*33;
34 
plainChars(const QLabel * l)35 static int plainChars(const QLabel *l)
36 {
37     static QTextDocument converter;
38     converter.setHtml(l->text());
39     return converter.toPlainText().length();
40 }
41 
AddressRowWidget(QWidget * parent,const QString & description,const QList<Imap::Message::MailAddress> & addresses,MessageView * messageView)42 AddressRowWidget::AddressRowWidget(QWidget *parent, const QString &description,
43                                    const QList<Imap::Message::MailAddress> &addresses, MessageView *messageView):
44     QWidget(parent), m_expander(0), m_expandedLength(0)
45 {
46     FlowLayout *lay = new FlowLayout(this, 0, 0, 0);
47     setLayout(lay);
48 
49     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
50 
51     addAddresses(description, addresses, messageView);
52 }
53 
addAddresses(const QString & description,const QList<Imap::Message::MailAddress> & addresses,MessageView * messageView)54 void AddressRowWidget::addAddresses(const QString &description, const QList<Imap::Message::MailAddress> &addresses, MessageView *messageView)
55 {
56     if (m_expander)
57         layout()->removeWidget(m_expander);
58     if (!description.isEmpty()) {
59         QLabel *title = new QLabel(description, this);
60         title->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
61         title->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);
62         layout()->addWidget(title);
63         if (m_expander)
64             title->hide();
65     }
66     int collapse = m_expander ? m_expander->expanding() : 0;
67     for (int i = 0; i < addresses.size(); ++i) {
68         auto *w = new OneEnvelopeAddress(this, addresses[i], messageView,
69                                          i == addresses.size() - 1 ?
70                                          OneEnvelopeAddress::Position::Last :
71                                          OneEnvelopeAddress::Position::Middle);
72         m_expandedLength += plainChars(w);
73         layout()->addWidget(w);
74         if (collapse || (i > 1 && m_expandedLength > nonExpandingLength)) {
75             w->hide();
76             ++collapse;
77         }
78     }
79     if (collapse && !m_expander) {
80         m_expander = new Expander(this, collapse);
81         connect(m_expander, &Expander::clicked, this, &AddressRowWidget::toggle);
82     }
83     if (m_expander)
84         layout()->addWidget(m_expander);
85 }
86 
toggle()87 void AddressRowWidget::toggle()
88 {
89     Q_ASSERT(m_expander);
90     if (m_expander->expanding()) {
91         m_expander->setExpanding(false);
92         for (int i = 0; i < layout()->count(); ++i) {
93             if (QWidget *w = layout()->itemAt(i)->widget())
94                 w->show();
95         }
96     } else {
97         int chars = 0, addresses = 0;
98         int collapse = 0;
99         for (int i = 0; i < layout()->count(); ++i) {
100             QWidget *w = layout()->itemAt(i)->widget();
101             if (collapse && w != m_expander) {
102                 ++collapse;
103                 w->hide();
104                 continue;
105             }
106             if (OneEnvelopeAddress *oea = qobject_cast<OneEnvelopeAddress*>(w)) {
107                 ++addresses;
108                 chars += plainChars(oea);
109                 if ((collapse = (addresses > 1 && chars > nonExpandingLength)))
110                     w->hide();
111             }
112         }
113         m_expander->setExpanding(collapse);
114     }
115 }
116 
Expander(QWidget * parent,int count)117 Expander::Expander(QWidget *parent, int count) : QLabel(parent)
118 {
119     setCursor(Qt::PointingHandCursor);
120     setExpanding(count);
121 }
122 
expanding() const123 int Expander::expanding() const {
124     return m_expanding;
125 }
126 
setExpanding(const int expanding)127 void Expander::setExpanding(const int expanding)
128 {
129     m_expanding = expanding;
130     setToolTip(expanding ? tr("Click to see %1 more items").arg(expanding) : tr("Click to collapse"));
131 }
132 
sizeHint() const133 QSize Expander::sizeHint() const
134 {
135     QSize s = QLabel::sizeHint();
136     s.setWidth(m_expanding ? 2*s.height() : s.height());
137     return s;
138 }
139 
mouseReleaseEvent(QMouseEvent * me)140 void Expander::mouseReleaseEvent(QMouseEvent *me)
141 {
142     if (me->button() == Qt::LeftButton)
143         emit clicked();
144 }
145 
paintEvent(QPaintEvent * pe)146 void Expander::paintEvent(QPaintEvent *pe)
147 {
148     QPainter p(this);
149     p.setRenderHint(QPainter::Antialiasing);
150     QRectF r(rect());
151     r.setWidth(height());
152     if (m_expanding) {
153         // ellipsis
154         p.setPen(palette().color(foregroundRole()));
155         p.setBrush(Qt::NoBrush);
156         p.drawText(r, Qt::AlignCenter, QStringLiteral("\u2026"));
157         r.moveRight(rect().right());
158     }
159     p.setBrush(palette().color(foregroundRole()));
160     p.setPen(Qt::NoPen);
161     p.drawEllipse(r);
162     p.setBrush(palette().color(backgroundRole()));
163     float d = r.height()/4.0f;
164     r.adjust(d,d,-d,-d);
165     // the unicode triangles are not necessarily centered - looks crap
166     if (m_expanding) {
167         r.translate(r.width()/8.0f,0); // fix gravity
168         const QPointF points[3] = { r.topLeft(), r.bottomLeft(), QPointF(r.right(), r.y() + r.height()/2.0) };
169         p.drawConvexPolygon(points, 3);
170     } else {
171         r.translate(-r.width()/8.0f,0); // fix gravity
172         const QPointF points[3] = { r.topRight(), r.bottomRight(), QPointF(r.left(), r.y() + r.height()/2.0) };
173         p.drawConvexPolygon(points, 3);
174     }
175     p.end();
176 }
177 
178 }
179