1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2008-08-21
7 * Description : a combo box with a width not depending of text
8 * content size
9 *
10 * Copyright (C) 2006-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
11 * Copyright (C) 2008 by Andi Clemens <andi dot clemens at googlemail dot com>
12 * Copyright (C) 2005 by Tom Albers <tomalbers at kde dot nl>
13 *
14 * This program is free software; you can redistribute it
15 * and/or modify it under the terms of the GNU General
16 * Public License as published by the Free Software Foundation;
17 * either version 2, or (at your option)
18 * any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * ============================================================ */
26
27 #include "squeezedcombobox.h"
28
29 // Qt includes
30
31 #include <QComboBox>
32 #include <QTimer>
33 #include <QStyle>
34 #include <QApplication>
35 #include <QResizeEvent>
36
37 namespace Digikam
38 {
39
40 class Q_DECL_HIDDEN SqueezedComboBox::Private
41 {
42 public:
43
Private()44 explicit Private()
45 : timer(nullptr)
46 {
47 }
48
49 QMap<int, QString> originalItems;
50
51 QTimer* timer;
52 };
53
SqueezedComboBox(QWidget * const parent,const char * name)54 SqueezedComboBox::SqueezedComboBox(QWidget* const parent, const char* name)
55 : QComboBox(parent),
56 d (new Private)
57 {
58 setObjectName(QString::fromUtf8(name));
59 setMinimumWidth(100);
60
61 d->timer = new QTimer(this);
62 d->timer->setSingleShot(true);
63
64 connect(d->timer, &QTimer::timeout,
65 this, &SqueezedComboBox::slotTimeOut);
66
67 connect(this, static_cast<void (SqueezedComboBox::*)(int)>(&SqueezedComboBox::activated),
68 this, &SqueezedComboBox::slotUpdateToolTip);
69 }
70
~SqueezedComboBox()71 SqueezedComboBox::~SqueezedComboBox()
72 {
73 d->originalItems.clear();
74 delete d->timer;
75 delete d;
76 }
77
contains(const QString & text) const78 bool SqueezedComboBox::contains(const QString& text) const
79 {
80 if (text.isEmpty())
81 {
82 return false;
83 }
84
85 for (QMap<int, QString>::const_iterator it = d->originalItems.constBegin() ;
86 it != d->originalItems.constEnd() ; ++it)
87 {
88 if (it.value() == text)
89 {
90 return true;
91 }
92 }
93
94 return false;
95 }
96
findOriginalText(const QString & text,Qt::CaseSensitivity cs) const97 int SqueezedComboBox::findOriginalText(const QString& text,
98 Qt::CaseSensitivity cs) const
99 {
100 if (text.isEmpty())
101 {
102 return -1;
103 }
104
105 for (QMap<int, QString>::const_iterator it = d->originalItems.constBegin() ;
106 it != d->originalItems.constEnd() ; ++it)
107 {
108 if (it.value().compare(text, cs) == 0)
109 {
110 return it.key();
111 }
112 }
113
114 return -1;
115 }
116
sizeHint() const117 QSize SqueezedComboBox::sizeHint() const
118 {
119 ensurePolished();
120 QFontMetrics fm = fontMetrics();
121
122 #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
123
124 int maxW = count() ? 18 : 7 * fm.horizontalAdvance(QLatin1Char('x')) + 18;
125
126 #else
127
128 int maxW = count() ? 18 : 7 * fm.width(QLatin1Char('x')) + 18;
129
130 #endif
131
132 int maxH = qMax(fm.lineSpacing(), 14) + 2;
133
134 QStyleOptionComboBox options;
135 options.initFrom(this);
136
137 return style()->sizeFromContents(QStyle::CT_ComboBox, &options,
138 QSize(maxW, maxH), this).expandedTo(QApplication::globalStrut());
139 }
140
insertSqueezedItem(const QString & newItem,int index,const QVariant & userData)141 void SqueezedComboBox::insertSqueezedItem(const QString& newItem, int index,
142 const QVariant& userData)
143 {
144 d->originalItems[index] = newItem;
145 QComboBox::insertItem(index, squeezeText(newItem), userData);
146
147 // if this is the first item, set the tooltip.
148
149 if (index == 0)
150 {
151 slotUpdateToolTip(0);
152 }
153 }
154
insertSqueezedList(const QStringList & newItems,int index)155 void SqueezedComboBox::insertSqueezedList(const QStringList& newItems, int index)
156 {
157 for (QStringList::const_iterator it = newItems.constBegin() ;
158 it != newItems.constEnd() ; ++it)
159 {
160 insertSqueezedItem(*it, index);
161 ++index;
162 }
163 }
164
addSqueezedItem(const QString & newItem,const QVariant & userData)165 void SqueezedComboBox::addSqueezedItem(const QString& newItem,
166 const QVariant& userData)
167 {
168 insertSqueezedItem(newItem, count(), userData);
169 }
170
setCurrent(const QString & itemText)171 void SqueezedComboBox::setCurrent(const QString& itemText)
172 {
173 QString squeezedText = squeezeText(itemText);
174 qint32 itemIndex = findText(squeezedText);
175
176 if (itemIndex >= 0)
177 {
178 setCurrentIndex(itemIndex);
179 }
180 }
181
resizeEvent(QResizeEvent *)182 void SqueezedComboBox::resizeEvent(QResizeEvent *)
183 {
184 d->timer->start(200);
185 }
186
slotTimeOut()187 void SqueezedComboBox::slotTimeOut()
188 {
189 for (QMap<int, QString>::iterator it = d->originalItems.begin() ;
190 it != d->originalItems.end() ; ++it)
191 {
192 setItemText(it.key(), squeezeText(it.value()));
193 }
194 }
195
squeezeText(const QString & original) const196 QString SqueezedComboBox::squeezeText(const QString& original) const
197 {
198 // not the complete widgetSize is usable. Need to compensate for that.
199
200 int widgetSize = width() - 30;
201 QFontMetrics fm(fontMetrics());
202
203 // If we can fit the full text, return that.
204
205 #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
206
207 if (fm.horizontalAdvance(original) < widgetSize)
208
209 #else
210
211 if (fm.width(original) < widgetSize)
212
213 #endif
214
215 {
216 return(original);
217 }
218
219 // We need to squeeze.
220
221 QString sqItem = original; // prevent empty return value;
222
223 #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
224
225 widgetSize = widgetSize-fm.horizontalAdvance(QLatin1String("..."));
226
227 #else
228
229 widgetSize = widgetSize-fm.width(QLatin1String("..."));
230
231 #endif
232
233 for (int i = 0 ; i != original.length() ; ++i)
234 {
235
236 #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
237
238 if ((int)fm.horizontalAdvance(original.right(i)) > widgetSize)
239
240 #else
241
242 if ((int)fm.width(original.right(i)) > widgetSize)
243
244 #endif
245
246 {
247 sqItem = QString(original.left(i) + QLatin1String("..."));
248 break;
249 }
250 }
251
252 return sqItem;
253 }
254
slotUpdateToolTip(int index)255 void SqueezedComboBox::slotUpdateToolTip(int index)
256 {
257 setToolTip(d->originalItems[index]);
258 }
259
itemHighlighted() const260 QString SqueezedComboBox::itemHighlighted() const
261 {
262 int curItem = currentIndex();
263 return d->originalItems[curItem];
264 }
265
item(int index) const266 QString SqueezedComboBox::item(int index) const
267 {
268 return d->originalItems[index];
269 }
270
271 } // namespace Digikam
272