1 /**
2  * \file fancylineedit.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Nokia Corporation (qt-info@nokia.com)
7  *
8  * Full author contact details are available in file CREDITS.
9  *
10  */
11 
12 // Code taken from the Qt Creator project and customized a little
13 
14 #include <config.h>
15 
16 #include "FancyLineEdit.h"
17 
18 #if QT_VERSION >= 0x040600
19 
20 #include <QEvent>
21 #include <QDebug>
22 #include <QString>
23 #include <QPropertyAnimation>
24 #include <QApplication>
25 #include <QMenu>
26 #include <QMouseEvent>
27 #include <QLabel>
28 #include <QAbstractButton>
29 #include <QPainter>
30 #include <QStyle>
31 #include <QPaintEvent>
32 
33 enum { margin = 6 };
34 
35 #define ICONBUTTON_HEIGHT 18
36 #define FADE_TIME 160
37 
38 
39 namespace lyx {
40 namespace frontend {
41 
42 // --------- FancyLineEditPrivate
43 class FancyLineEditPrivate : public QObject {
44 public:
45     explicit FancyLineEditPrivate(FancyLineEdit *parent);
46 
47     virtual bool eventFilter(QObject *obj, QEvent *event);
48 
49     FancyLineEdit  *m_lineEdit;
50     QPixmap m_pixmap[2];
51     QMenu *m_menu[2];
52     bool m_menuTabFocusTrigger[2];
53     IconButton *m_iconbutton[2];
54     bool m_iconEnabled[2];
55 };
56 
57 
FancyLineEditPrivate(FancyLineEdit * parent)58 FancyLineEditPrivate::FancyLineEditPrivate(FancyLineEdit *parent) :
59     QObject(parent),
60     m_lineEdit(parent)
61 {
62     for (int i = 0; i < 2; ++i) {
63         m_menu[i] = 0;
64         m_menuTabFocusTrigger[i] = false;
65         m_iconbutton[i] = new IconButton(parent);
66         m_iconbutton[i]->installEventFilter(this);
67         m_iconbutton[i]->hide();
68         m_iconbutton[i]->setAutoHide(false);
69         m_iconEnabled[i] = false;
70     }
71 }
72 
eventFilter(QObject * obj,QEvent * event)73 bool FancyLineEditPrivate::eventFilter(QObject *obj, QEvent *event)
74 {
75     int buttonIndex = -1;
76     for (int i = 0; i < 2; ++i) {
77         if (obj == m_iconbutton[i]) {
78             buttonIndex = i;
79             break;
80         }
81     }
82     if (buttonIndex == -1)
83         return QObject::eventFilter(obj, event);
84     switch (event->type()) {
85     case QEvent::FocusIn:
86         if (m_menuTabFocusTrigger[buttonIndex] && m_menu[buttonIndex]) {
87             m_lineEdit->setFocus();
88             m_menu[buttonIndex]->exec(m_iconbutton[buttonIndex]->mapToGlobal(
89                     m_iconbutton[buttonIndex]->rect().center()));
90             return true;
91         }
92     default:
93         break;
94     }
95     return QObject::eventFilter(obj, event);
96 }
97 
98 
99 // --------- FancyLineEdit
FancyLineEdit(QWidget * parent)100 FancyLineEdit::FancyLineEdit(QWidget *parent) :
101     QLineEdit(parent),
102     m_d(new FancyLineEditPrivate(this))
103 {
104     ensurePolished();
105     updateMargins();
106 
107     connect(this, SIGNAL(textChanged(QString)),
108             this, SLOT(checkButtons(QString)));
109     connect(m_d->m_iconbutton[Left], SIGNAL(clicked()),
110             this, SLOT(iconClicked()));
111     connect(m_d->m_iconbutton[Right], SIGNAL(clicked()),
112             this, SLOT(iconClicked()));
113 }
114 
checkButtons(const QString & text)115 void FancyLineEdit::checkButtons(const QString &text)
116 {
117     if (m_oldText.isEmpty() || text.isEmpty()) {
118         for (int i = 0; i < 2; ++i) {
119             if (m_d->m_iconbutton[i]->hasAutoHide())
120                 m_d->m_iconbutton[i]->animateShow(!text.isEmpty());
121         }
122         m_oldText = text;
123     }
124 }
125 
~FancyLineEdit()126 FancyLineEdit::~FancyLineEdit()
127 {
128 }
129 
setButtonVisible(Side side,bool visible)130 void FancyLineEdit::setButtonVisible(Side side, bool visible)
131 {
132     m_d->m_iconbutton[side]->setVisible(visible);
133     m_d->m_iconEnabled[side] = visible;
134     updateMargins();
135 }
136 
isButtonVisible(Side side) const137 bool FancyLineEdit::isButtonVisible(Side side) const
138 {
139     return m_d->m_iconEnabled[side];
140 }
141 
iconClicked()142 void FancyLineEdit::iconClicked()
143 {
144     IconButton *button = qobject_cast<IconButton *>(sender());
145     int index = -1;
146     for (int i = 0; i < 2; ++i)
147         if (m_d->m_iconbutton[i] == button)
148             index = i;
149     if (index == -1)
150         return;
151     if (m_d->m_menu[index]) {
152         m_d->m_menu[index]->exec(QCursor::pos());
153     } else {
154 		buttonClicked((Side)index);
155         if (index == Left)
156 			leftButtonClicked();
157         else if (index == Right)
158 			rightButtonClicked();
159     }
160 }
161 
updateMargins()162 void FancyLineEdit::updateMargins()
163 {
164     bool leftToRight = (layoutDirection() == Qt::LeftToRight);
165     Side realLeft = (leftToRight ? Left : Right);
166     Side realRight = (leftToRight ? Right : Left);
167 
168     int leftMargin = m_d->m_iconbutton[realLeft]->pixmap().width() + 8;
169     int rightMargin = m_d->m_iconbutton[realRight]->pixmap().width() + 8;
170     // Note KDE does not reserve space for the highlight color
171     if (style()->inherits("OxygenStyle")) {
172         leftMargin = qMax(24, leftMargin);
173         rightMargin = qMax(24, rightMargin);
174     }
175 
176     QMargins margins((m_d->m_iconEnabled[realLeft] ? leftMargin : 0), 0,
177                      (m_d->m_iconEnabled[realRight] ? rightMargin : 0), 0);
178 
179     setTextMargins(margins);
180 }
181 
updateButtonPositions()182 void FancyLineEdit::updateButtonPositions()
183 {
184     QRect contentRect = rect();
185     for (int i = 0; i < 2; ++i) {
186         Side iconpos = (Side)i;
187         if (layoutDirection() == Qt::RightToLeft)
188             iconpos = (iconpos == Left ? Right : Left);
189 
190         if (iconpos == FancyLineEdit::Right) {
191             const int iconoffset = textMargins().right() + 4;
192             m_d->m_iconbutton[i]->
193 	            setGeometry(contentRect.adjusted(width() - iconoffset, 0, 0, 0));
194         } else {
195             const int iconoffset = textMargins().left() + 4;
196             m_d->m_iconbutton[i]->
197 	            setGeometry(contentRect.adjusted(0, 0, -width() + iconoffset, 0));
198         }
199     }
200 }
201 
resizeEvent(QResizeEvent *)202 void FancyLineEdit::resizeEvent(QResizeEvent *)
203 {
204     updateButtonPositions();
205 }
206 
207 
keyPressEvent(QKeyEvent * e)208 void FancyLineEdit::keyPressEvent(QKeyEvent * e)
209 {
210 	if (e->type() == QEvent::KeyPress && e->key() == Qt::Key_Down)
211 		Q_EMIT downPressed();
212 	else
213 		QLineEdit::keyPressEvent(e);
214 }
215 
216 
setButtonPixmap(Side side,const QPixmap & buttonPixmap)217 void FancyLineEdit::setButtonPixmap(Side side, const QPixmap &buttonPixmap)
218 {
219     m_d->m_iconbutton[side]->setPixmap(buttonPixmap);
220     updateMargins();
221     updateButtonPositions();
222     update();
223 }
224 
buttonPixmap(Side side) const225 QPixmap FancyLineEdit::buttonPixmap(Side side) const
226 {
227     return m_d->m_pixmap[side];
228 }
229 
setButtonMenu(Side side,QMenu * buttonMenu)230 void FancyLineEdit::setButtonMenu(Side side, QMenu *buttonMenu)
231 {
232      m_d->m_menu[side] = buttonMenu;
233      m_d->m_iconbutton[side]->setIconOpacity(1.0);
234  }
235 
buttonMenu(Side side) const236 QMenu *FancyLineEdit::buttonMenu(Side side) const
237 {
238     return  m_d->m_menu[side];
239 }
240 
hasMenuTabFocusTrigger(Side side) const241 bool FancyLineEdit::hasMenuTabFocusTrigger(Side side) const
242 {
243     return m_d->m_menuTabFocusTrigger[side];
244 }
245 
setMenuTabFocusTrigger(Side side,bool v)246 void FancyLineEdit::setMenuTabFocusTrigger(Side side, bool v)
247 {
248     if (m_d->m_menuTabFocusTrigger[side] == v)
249         return;
250 
251     m_d->m_menuTabFocusTrigger[side] = v;
252     m_d->m_iconbutton[side]->setFocusPolicy(v ? Qt::TabFocus : Qt::NoFocus);
253 }
254 
hasAutoHideButton(Side side) const255 bool FancyLineEdit::hasAutoHideButton(Side side) const
256 {
257     return m_d->m_iconbutton[side]->hasAutoHide();
258 }
259 
setAutoHideButton(Side side,bool h)260 void FancyLineEdit::setAutoHideButton(Side side, bool h)
261 {
262     m_d->m_iconbutton[side]->setAutoHide(h);
263     if (h)
264         m_d->m_iconbutton[side]->setIconOpacity(text().isEmpty() ?  0.0 : 1.0);
265     else
266         m_d->m_iconbutton[side]->setIconOpacity(1.0);
267 }
268 
setButtonToolTip(Side side,const QString & tip)269 void FancyLineEdit::setButtonToolTip(Side side, const QString &tip)
270 {
271     m_d->m_iconbutton[side]->setToolTip(tip);
272 }
273 
setButtonFocusPolicy(Side side,Qt::FocusPolicy policy)274 void FancyLineEdit::setButtonFocusPolicy(Side side, Qt::FocusPolicy policy)
275 {
276     m_d->m_iconbutton[side]->setFocusPolicy(policy);
277 }
278 
279 // IconButton - helper class to represent a clickable icon
280 
IconButton(QWidget * parent)281 IconButton::IconButton(QWidget *parent)
282     : QAbstractButton(parent), m_iconOpacity(0.0), m_autoHide(false)
283 {
284     setCursor(Qt::ArrowCursor);
285     setFocusPolicy(Qt::NoFocus);
286 }
287 
paintEvent(QPaintEvent *)288 void IconButton::paintEvent(QPaintEvent *)
289 {
290     QPainter painter(this);
291     QRect pixmapRect = QRect(0, 0, m_pixmap.width(), m_pixmap.height());
292     pixmapRect.moveCenter(rect().center());
293 
294     if (m_autoHide)
295         painter.setOpacity(m_iconOpacity);
296 
297     painter.drawPixmap(pixmapRect, m_pixmap);
298 }
299 
animateShow(bool visible)300 void IconButton::animateShow(bool visible)
301 {
302     if (visible) {
303         QPropertyAnimation *animation =
304 	        new QPropertyAnimation(this, "iconOpacity");
305         animation->setDuration(FADE_TIME);
306         animation->setEndValue(1.0);
307         animation->start(QAbstractAnimation::DeleteWhenStopped);
308     } else {
309         QPropertyAnimation *animation =
310 	        new QPropertyAnimation(this, "iconOpacity");
311         animation->setDuration(FADE_TIME);
312         animation->setEndValue(0.0);
313         animation->start(QAbstractAnimation::DeleteWhenStopped);
314     }
315 }
316 
317 } // namespace frontend
318 
319 } // namespace lyx
320 
321 #endif // QT_VERSION >= 0x040600
322 
323 #include "moc_FancyLineEdit.cpp"
324