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