1 /* This file is (c) 2012 Tvangeste <i.4m.l33t@yandex.ru>
2 * Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
3
4 #include "translatebox.hh"
5
6 #include <QHBoxLayout>
7 #include <QEvent>
8 #include <QKeyEvent>
9 #include <QApplication>
10 #include <QDebug>
11 #include <QModelIndex>
12 #include <QScrollBar>
13 #include <QStyle>
14
15 namespace
16 {
17 #define MAX_POPUP_ROWS 17
18 }
19
CompletionList(TranslateBox * parent)20 CompletionList::CompletionList(TranslateBox * parent) : WordList(parent),
21 translateBox(parent)
22 {
23 #ifdef Q_OS_WIN
24 setWindowFlags(Qt::ToolTip);
25 #else
26 setParent( parent->window() );
27 setAutoFillBackground( true );
28 #endif
29
30 connect(this, SIGNAL( activated( QModelIndex ) ),
31 this, SLOT( acceptCurrentEntry() ) );
32
33 connect(this, SIGNAL( itemClicked( QListWidgetItem * ) ),
34 this, SLOT( acceptCurrentEntry() ) );
35
36 translateBox->window()->installEventFilter(this);
37 }
38
eventFilter(QObject * obj,QEvent * ev)39 bool CompletionList::eventFilter( QObject * obj, QEvent * ev )
40 {
41 // when the main window is moved or resized, hide the word list suggestions
42 if ( obj != this && !isAncestorOf( qobject_cast< QWidget * >( obj ) )
43 && ( ev->type() == QEvent::Move || ev->type() == QEvent::Resize ) )
44 {
45 translateBox->setPopupEnabled( false );
46 return false;
47 }
48
49 return QWidget::eventFilter( obj, ev );
50 }
51
preferredHeight() const52 int CompletionList::preferredHeight() const
53 {
54 const QSize itemSizeHint = itemDelegate()->sizeHint(viewOptions(), model()->index( 0, 0 ) );
55 int rows = qMin( count(), MAX_POPUP_ROWS );
56
57 int scrollBarHeight = 0;
58
59 bool hBarIsVisible = horizontalScrollBar()->maximum() > 0;
60
61 if ( hBarIsVisible )
62 scrollBarHeight += QApplication::style()->pixelMetric( QStyle::PM_ScrollBarExtent );
63
64 return rows == 0 ? 0 : itemSizeHint.height() * rows + frameWidth() * 2 + scrollBarHeight;
65 }
66
acceptCurrentEntry()67 bool CompletionList::acceptCurrentEntry()
68 {
69 if (!isVisible())
70 {
71 return false;
72 }
73
74 const QModelIndex index = currentIndex();
75 if ( !index.isValid() )
76 {
77 return false;
78 }
79
80 emit doubleClicked(index);
81 translateBox->setPopupEnabled( false );
82
83 return true;
84 }
85
TranslateBox(QWidget * parent)86 TranslateBox::TranslateBox(QWidget *parent) : QWidget(parent),
87 word_list(new CompletionList(this)), translate_line(new ExtLineEdit(this)), m_popupEnabled(false)
88 {
89 // initially hidden
90 word_list->hide();
91
92 resize(200, 90);
93 QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
94 sizePolicy.setHorizontalStretch(0);
95 sizePolicy.setVerticalStretch(0);
96 setSizePolicy(sizePolicy);
97 // setMinimumSize(QSize(800, 0));
98
99 setFocusProxy(translate_line);
100 translate_line->setObjectName("translateLine");
101 #if QT_VERSION >= 0x040700
102 translate_line->setPlaceholderText( tr( "Type a word or phrase to search dictionaries" ) );
103 #endif
104
105 word_list->setTranslateLine(translate_line);
106
107 // completer = new QCompleter(m_completionList->model(), this);
108 // completer->setCaseSensitivity(Qt::CaseInsensitive);
109 // completer->setCompletionMode(QCompleter::InlineCompletion);
110
111 QHBoxLayout *layout = new QHBoxLayout(this);
112 setLayout(layout);
113 layout->setMargin(0);
114 layout->addWidget(translate_line);
115
116 QPixmap image(":/icons/system-search.png");
117 translate_line->setButtonPixmap(ExtLineEdit::Left, image.scaled(18, 18, Qt::KeepAspectRatio, Qt::SmoothTransformation));
118 // translate_line->setButtonToolTip(ExtLineEdit::Left, tr("Options"));
119 translate_line->setButtonVisible(ExtLineEdit::Left, true);
120 translate_line->setButtonFocusPolicy(ExtLineEdit::Left, Qt::ClickFocus);
121
122 QPixmap right(":/icons/1downarrow.png");
123 translate_line->setButtonPixmap(ExtLineEdit::Right, right);
124 translate_line->setButtonToolTip(ExtLineEdit::Right, tr("Drop-down"));
125 translate_line->setButtonVisible(ExtLineEdit::Right, true);
126 translate_line->setButtonFocusPolicy(ExtLineEdit::Right, Qt::NoFocus);
127
128 translate_line->setFocusPolicy(Qt::ClickFocus);
129
130 translate_line->installEventFilter( this );
131 this->installEventFilter( this );
132
133 connect(translate_line, SIGNAL( textChanged( QString const & ) ),
134 this, SLOT( onTextEdit() ) );
135
136 connect(translate_line, SIGNAL( rightButtonClicked() ),
137 this, SLOT( rightButtonClicked() ) );
138
139 connect(word_list, SIGNAL( contentChanged() ),
140 this, SLOT( showPopup() ) );
141 }
142
eventFilter(QObject * obj,QEvent * event)143 bool TranslateBox::eventFilter(QObject *obj, QEvent *event)
144 {
145 // hide the suggestions list when the window is not active
146 if ( event->type() == QEvent::WindowDeactivate )
147 {
148 if (!word_list->isActiveWindow())
149 {
150 setPopupEnabled( false );
151 }
152 return false;
153 }
154
155 if (obj == translate_line && event->type() == QEvent::KeyPress) {
156 QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
157 switch (keyEvent->key()) {
158 case Qt::Key_Up:
159 case Qt::Key_Down:
160 case Qt::Key_PageUp:
161 case Qt::Key_PageDown:
162 if ( !word_list->isVisible() )
163 {
164 setPopupEnabled( true );
165 }
166 else
167 {
168 QApplication::sendEvent(word_list, event);
169 }
170 return true;
171 case Qt::Key_Enter:
172 case Qt::Key_Return:
173 return word_list->acceptCurrentEntry();
174 case Qt::Key_Escape:
175 setPopupEnabled( false );
176 return true;
177 case Qt::Key_Tab:
178 if ( !word_list->isVisible() )
179 {
180 setPopupEnabled( true );
181 }
182 else
183 {
184 QKeyEvent event( QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier );
185 QApplication::sendEvent( word_list, &event );
186 }
187 return true;
188 case Qt::Key_Backtab:
189 if ( !word_list->isVisible() )
190 {
191 setPopupEnabled( true );
192 }
193 else
194 {
195 QKeyEvent event( QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier );
196 QApplication::sendEvent( word_list, &event );
197 }
198 return true;
199 default:
200 break;
201 }
202 } else if (obj == translate_line && event->type() == QEvent::FocusOut) {
203 #if defined(Q_OS_WIN)
204 QFocusEvent *fev = static_cast<QFocusEvent*>(event);
205 if (fev->reason() != Qt::ActiveWindowFocusReason ||
206 (fev->reason() == Qt::ActiveWindowFocusReason && !word_list->isActiveWindow()))
207 #endif
208 setPopupEnabled( false );
209 } else if (obj == translate_line && event->type() == QEvent::FocusIn) {
210 // By default, focusing the traslate line does not show
211 // the popup window.
212 } else if (obj == this && event->type() == QEvent::ShortcutOverride) {
213 QKeyEvent *ke = static_cast<QKeyEvent *>(event);
214 if (ke->key() == Qt::Key_Escape && !ke->modifiers() && word_list->isVisible() ) {
215 event->accept();
216 return true;
217 }
218 }
219 return QWidget::eventFilter(obj, event);
220 }
221
setText(QString text,bool showPopup)222 void TranslateBox::setText( QString text, bool showPopup )
223 {
224 setPopupEnabled( showPopup );
225 translate_line->setText( text );
226 }
227
setPopupEnabled(bool enable)228 void TranslateBox::setPopupEnabled( bool enable )
229 {
230 m_popupEnabled = enable;
231 showPopup();
232 }
233
setSizePolicy(QSizePolicy policy)234 void TranslateBox::setSizePolicy( QSizePolicy policy )
235 {
236 QWidget::setSizePolicy( policy );
237 if ( translate_line )
238 translate_line->setSizePolicy( policy );
239 }
240
showPopup()241 void TranslateBox::showPopup()
242 {
243 // completer->setCompletionPrefix( m_fileLineEdit->text() );
244 // qDebug() << "COMPLETION:" << completer->currentCompletion();
245
246 // Don't allow recursive call
247 if( translateBoxMutex.tryLock() )
248 translateBoxMutex.unlock();
249 else
250 return;
251 Mutex::Lock _( translateBoxMutex );
252
253 if (translate_line->text().trimmed().isEmpty() || word_list->count() == 0)
254 {
255 // nothing to show
256 if (word_list->isVisible())
257 {
258 word_list->hide();
259 translate_line->setFocus();
260 }
261 return;
262 }
263
264 if ( !m_popupEnabled )
265 {
266 word_list->hide();
267 return;
268 }
269
270 int preferredHeight = word_list->preferredHeight();
271
272 QPoint origin( translate_line->x(), translate_line->y() + translate_line->height() );
273
274 if ( word_list->isWindow() )
275 {
276 origin = mapToGlobal( origin );
277 }
278 else
279 {
280 origin = mapTo( window(), origin );
281 preferredHeight = qMin( translate_line->window()->height() - origin.y(), preferredHeight );
282 }
283
284 const QSize size(width(), preferredHeight);
285 const QRect rect( origin, size );
286
287 word_list->setGeometry(rect);
288 word_list->show();
289 word_list->raise();
290 translate_line->setFocus();
291 }
292
translateLine()293 QLineEdit * TranslateBox::translateLine()
294 {
295 return translate_line;
296 }
297
wordList()298 WordList * TranslateBox::wordList()
299 {
300 return word_list;
301 }
302
rightButtonClicked()303 void TranslateBox::rightButtonClicked()
304 {
305 setPopupEnabled( !m_popupEnabled );
306 }
307
onTextEdit()308 void TranslateBox::onTextEdit()
309 {
310 if ( translate_line->hasFocus() )
311 setPopupEnabled( true );
312 }
313