]> git.sesse.net Git - vlc/blob - modules/gui/qt4/util/customwidgets.cpp
Qt4: correctly save the keys, indepententy from the locale
[vlc] / modules / gui / qt4 / util / customwidgets.cpp
1 /*****************************************************************************
2  * customwidgets.cpp: Custom widgets
3  ****************************************************************************
4  * Copyright (C) 2006-2011 the VideoLAN team
5  * Copyright (C) 2004 Daniel Molkentin <molkentin@kde.org>
6  * $Id$
7  *
8  * Authors: ClĂ©ment Stenac <zorglub@videolan.org>
9  * The "ClickLineEdit" control is based on code by  Daniel Molkentin
10  * <molkentin@kde.org> for libkdepim
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include "customwidgets.hpp"
32 #include "qt4.hpp"               /* needed for qtr,  but not necessary */
33
34 #include <QPainter>
35 #include <QRect>
36 #include <QKeyEvent>
37 #include <QWheelEvent>
38 #include <QPixmap>
39 #include <QApplication>
40 #include <vlc_keys.h>
41
42 QFramelessButton::QFramelessButton( QWidget *parent )
43                     : QPushButton( parent )
44 {
45     setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );
46 }
47
48 void QFramelessButton::paintEvent( QPaintEvent * )
49 {
50     QPainter painter( this );
51     QPixmap pix = icon().pixmap( size() );
52     QPoint pos( (width() - pix.width()) / 2, (height() - pix.height()) / 2 );
53     painter.drawPixmap( QRect( pos.x(), pos.y(), pix.width(), pix.height() ), pix );
54 }
55
56 QElidingLabel::QElidingLabel( const QString &s, Qt::TextElideMode mode, QWidget * parent )
57                  : QLabel( s, parent ), elideMode( mode )
58 { }
59
60 void QElidingLabel::setElideMode( Qt::TextElideMode mode )
61 {
62     elideMode = mode;
63     repaint();
64 }
65
66 void QElidingLabel::paintEvent( QPaintEvent * )
67 {
68     QPainter p( this );
69     int space = frameWidth() + margin();
70     QRect r = rect().adjusted( space, space, -space, -space );
71     p.drawText( r, fontMetrics().elidedText( text(), elideMode, r.width() ), alignment() );
72 }
73
74 QString QVLCDebugLevelSpinBox::textFromValue( int v ) const
75 {
76     QString const texts[] = {
77     /* Note that min level 0 is 'errors' in Qt Ui
78        FIXME: fix debug levels accordingly to documentation */
79     /*  qtr("infos"),*/
80         qtr("errors"),
81         qtr("warnings"),
82         qtr("debug")
83     };
84     if ( v < 0 ) v = 0;
85     if ( v >= 2 ) v = 2;
86
87     return QString( "%1 (%2)" ).arg( v ).arg( texts[v] );
88 }
89
90 VLCQDial::VLCQDial( QWidget *parent ) : QDial( parent )
91 {
92
93 }
94
95 void VLCQDial::paintEvent( QPaintEvent *event )
96 {
97     QDial::paintEvent( event );
98     QPainter painter( this );
99     painter.setPen( QPen( palette().color( QPalette::WindowText ) ) );
100     float radius = 0.5 * 0.707106 * qMin( size().width(), size().height() );
101     painter.drawText( QRectF( rect().center().x() + radius,
102                               rect().center().y() + radius,
103                               size().width(),
104                               size().height() ),
105                       0, QString::number( value() ), 0 );
106     painter.end();
107 }
108
109 /***************************************************************************
110  * Hotkeys converters
111  ***************************************************************************/
112 int qtKeyModifiersToVLC( QInputEvent* e )
113 {
114     int i_keyModifiers = 0;
115     if( e->modifiers() & Qt::ShiftModifier ) i_keyModifiers |= KEY_MODIFIER_SHIFT;
116     if( e->modifiers() & Qt::AltModifier ) i_keyModifiers |= KEY_MODIFIER_ALT;
117     if( e->modifiers() & Qt::ControlModifier ) i_keyModifiers |= KEY_MODIFIER_CTRL;
118     if( e->modifiers() & Qt::MetaModifier ) i_keyModifiers |= KEY_MODIFIER_META;
119     return i_keyModifiers;
120 }
121
122 typedef struct
123 {
124     int      qt;
125     uint32_t vlc;
126 } vlc_qt_key_t;
127
128 static const vlc_qt_key_t keys[] =
129 {
130     { Qt::Key_Escape,                KEY_ESC },
131     { Qt::Key_Tab,                   '\t', },
132     // Qt::Key_Backtab
133     { Qt::Key_Backspace,             '\b' },
134     { Qt::Key_Return,                '\r' },
135     { Qt::Key_Enter,                 '\r' }, // numeric pad
136     { Qt::Key_Insert,                KEY_INSERT },
137     { Qt::Key_Delete,                KEY_DELETE },
138     // Qt::Key_Pause
139     // Qt::Key_Print
140     // Qt::Key_SysReq
141     // Qt::Key_Clear
142     { Qt::Key_Home,                  KEY_HOME },
143     { Qt::Key_End,                   KEY_END },
144     { Qt::Key_Left,                  KEY_LEFT },
145     { Qt::Key_Up,                    KEY_UP },
146     { Qt::Key_Right,                 KEY_RIGHT },
147     { Qt::Key_Down,                  KEY_DOWN },
148     { Qt::Key_PageUp,                KEY_PAGEUP },
149     { Qt::Key_PageDown,              KEY_PAGEDOWN },
150     // Qt::Key_Shift
151     // Qt::Key_Control
152     // Qt::Key_Meta
153     // Qt::Key_Alt
154     // Qt::Key_CapsLock
155     // Qt::Key_NumLock
156     // Qt::Key_ScrollLock
157     /* F1 - F35 - Qt goes to F35, VLC stops at F12 */
158     { Qt::Key_F1,                    KEY_F1 },
159     { Qt::Key_F2,                    KEY_F2 },
160     { Qt::Key_F3,                    KEY_F3 },
161     { Qt::Key_F4,                    KEY_F4 },
162     { Qt::Key_F5,                    KEY_F5 },
163     { Qt::Key_F6,                    KEY_F6 },
164     { Qt::Key_F7,                    KEY_F7 },
165     { Qt::Key_F8,                    KEY_F8 },
166     { Qt::Key_F9,                    KEY_F9 },
167     { Qt::Key_F10,                   KEY_F10 },
168     { Qt::Key_F11,                   KEY_F11 },
169     { Qt::Key_F12,                   KEY_F12 },
170     // Qt::Key_Super_L
171     // Qt::Key_Super_R
172     { Qt::Key_Menu,                  KEY_MENU },
173     // Qt::Key_Hyper_L
174     // Qt::Key_Hyper_R
175     // Qt::Key_Help
176     // Qt::Key_Direction_L
177     // Qt::Key_Direction_R
178
179     // Qt::Key_Multi_key
180     // Qt::Key_Codeinput
181     // Qt::Key_SingleCandidate
182     // Qt::Key_MultipleCandidate
183     // Qt::Key_PreviousCandidate
184     // Qt::Key_Mode_switch
185     // Qt::Key_Kanji
186     // Qt::Key_Muhenkan
187     // Qt::Key_Henkan
188     // Qt::Key_Romaji
189     // Qt::Key_Hiragana
190     // Qt::Key_Katakana
191     // Qt::Key_Hiragana_Katakana
192     // Qt::Key_Zenkaku
193     // Qt::Key_Hankaku
194     // Qt::Key_Zenkaku_Hankaku
195     // Qt::Key_Touroku
196     // Qt::Key_Massyo
197     // Qt::Key_Kana_Lock
198     // Qt::Key_Kana_Shift
199     // Qt::Key_Eisu_Shift
200     // Qt::Key_Eisu_toggle
201     // Qt::Key_Hangul
202     // Qt::Key_Hangul_Start
203     // Qt::Key_Hangul_End
204     // Qt::Key_Hangul_Hanja
205     // Qt::Key_Hangul_Jamo
206     // Qt::Key_Hangul_Romaja
207     // Qt::Key_Hangul_Jeonja
208     // Qt::Key_Hangul_Banja
209     // Qt::Key_Hangul_PreHanja
210     // Qt::Key_Hangul_PostHanja
211     // Qt::Key_Hangul_Special
212     // Qt::Key_Dead_Grave
213     // Qt::Key_Dead_Acute
214     // Qt::Key_Dead_Circumflex
215     // Qt::Key_Dead_Tilde
216     // Qt::Key_Dead_Macron
217     // Qt::Key_Dead_Breve
218     // Qt::Key_Dead_Abovedot
219     // Qt::Key_Dead_Diaeresis
220     // Qt::Key_Dead_Abovering
221     // Qt::Key_Dead_Doubleacute
222     // Qt::Key_Dead_Caron
223     // Qt::Key_Dead_Cedilla
224     // Qt::Key_Dead_Ogonek
225     // Qt::Key_Dead_Iota
226     // Qt::Key_Dead_Voiced_Sound
227     // Qt::Key_Dead_Semivoiced_Sound
228     // Qt::Key_Dead_Belowdot
229     // Qt::Key_Dead_Hook
230     // Qt::Key_Dead_Horn
231     { Qt::Key_Back,                  KEY_BROWSER_BACK },
232     { Qt::Key_Forward,               KEY_BROWSER_FORWARD },
233     { Qt::Key_Stop,                  KEY_BROWSER_STOP },
234     { Qt::Key_Refresh,               KEY_BROWSER_REFRESH },
235     { Qt::Key_VolumeDown,            KEY_VOLUME_DOWN },
236     { Qt::Key_VolumeMute,            KEY_VOLUME_MUTE },
237     { Qt::Key_VolumeUp,              KEY_VOLUME_UP },
238     // Qt::Key_BassBoost
239     // Qt::Key_BassUp
240     // Qt::Key_BassDown
241     // Qt::Key_TrebleUp
242     // Qt::Key_TrebleDown
243     { Qt::Key_MediaPlay,             KEY_MEDIA_PLAY_PAUSE },
244     { Qt::Key_MediaStop,             KEY_MEDIA_STOP },
245     { Qt::Key_MediaPrevious,         KEY_MEDIA_PREV_TRACK },
246     { Qt::Key_MediaNext,             KEY_MEDIA_NEXT_TRACK },
247     // Qt::Key_MediaRecord
248     { Qt::Key_HomePage,              KEY_BROWSER_HOME },
249     { Qt::Key_Favorites,             KEY_BROWSER_FAVORITES },
250     { Qt::Key_Search,                KEY_BROWSER_SEARCH },
251     // Qt::Key_Standby
252     // Qt::Key_OpenUrl
253     // Qt::Key_LaunchMail
254     // Qt::Key_LaunchMedia
255     /* Qt::Key_Launch0 through Qt::Key_LaunchF */
256     /* ... */
257     { Qt::Key_Reload,                KEY_BROWSER_REFRESH },
258 };
259
260 static int keycmp( const void *a, const void *b )
261 {
262     const int *q = (const int *)a;
263     const vlc_qt_key_t *m = (const vlc_qt_key_t *)b;
264
265     return *q - m->qt;
266 }
267
268 int qtEventToVLCKey( QKeyEvent *e )
269 {
270     int qtk = e->key();
271     uint32_t i_vlck = 0;
272
273     if( qtk <= 0xff )
274     {
275         /* VLC and X11 use lowercase whereas Qt uses uppercase, this
276          * method should be equal to towlower in case of latin1 */
277         if( qtk >= 'A' && qtk <= 'Z' ) i_vlck = qtk+32;
278         else if( qtk >= 0xC0 && qtk <= 0xDE && qtk != 0xD7) i_vlck = qtk+32;
279         else i_vlck = qtk;
280     }
281     else
282     {
283         const vlc_qt_key_t *map;
284
285         map = (const vlc_qt_key_t *)
286               bsearch( &qtk, (const void *)keys, sizeof(keys)/sizeof(keys[0]),
287                        sizeof(*keys), keycmp );
288         if( map != NULL )
289             i_vlck = map->vlc;
290     }
291
292     /* Handle modifiers */
293     i_vlck |= qtKeyModifiersToVLC( e );
294     return i_vlck;
295 }
296
297 int qtWheelEventToVLCKey( QWheelEvent *e )
298 {
299     int i_vlck = 0;
300     /* Handle modifiers */
301     i_vlck |= qtKeyModifiersToVLC( e );
302     if ( e->delta() > 0 )
303         i_vlck |= KEY_MOUSEWHEELUP;
304     else
305         i_vlck |= KEY_MOUSEWHEELDOWN;
306     return i_vlck;
307 }
308
309 QString VLCKeyToString( unsigned val, bool locale )
310 {
311     char *base = vlc_keycode2str (val, locale);
312     if (base == NULL)
313         return qtr( "Unset" );
314
315     QString r = qfu( base );
316
317     free( base );
318     return r;
319 }
320
321 PixmapAnimator::PixmapAnimator( QWidget *parent, QList<QString> frames )
322     : QAbstractAnimation( parent ), current_frame( 0 )
323 {
324     foreach( QString name, frames )
325         pixmaps.append( new QPixmap( name ) );
326     currentPixmap = pixmaps.at( 0 );
327     setFps( frames.count() ); /* default to 1 sec loop */
328     setLoopCount( -1 );
329 }
330
331 void PixmapAnimator::updateCurrentTime( int msecs )
332 {
333     int i = msecs / interval;
334     if ( i >= pixmaps.count() ) i = pixmaps.count() - 1; /* roundings */
335     if ( i != current_frame )
336     {
337         current_frame = i;
338         currentPixmap = pixmaps.at( current_frame );
339         emit pixmapReady( *currentPixmap );
340     }
341 }
342
343 /* Animated Icon implementation */
344 SpinningIcon::SpinningIcon( QWidget *parent ) : QLabel( parent )
345 {
346     QList<QString> frames;
347     frames << ":/util/wait1";
348     frames << ":/util/wait2";
349     frames << ":/util/wait3";
350     frames << ":/util/wait4";
351     animator = new PixmapAnimator( this, frames );
352     CONNECT( animator, pixmapReady( const QPixmap & ), this, setPixmap( const QPixmap & ) );
353     CONNECT( animator, pixmapReady( const QPixmap & ), this, repaint() );
354     setScaledContents( true );
355     setFixedSize( 16, 16 );
356     animator->setCurrentTime( 0 );
357 }
358
359 QToolButtonExt::QToolButtonExt(QWidget *parent, int ms )
360     : QToolButton( parent ),
361       shortClick( false ),
362       longClick( false )
363 {
364     setAutoRepeat( true );
365     /* default to twice the doubleclick delay */
366     setAutoRepeatDelay( ( ms > 0 )? ms : 2 * QApplication::doubleClickInterval() );
367     setAutoRepeatInterval( 100 );
368     connect( this, SIGNAL(released()), this, SLOT(releasedSlot()) );
369     connect( this, SIGNAL(clicked()), this, SLOT(clickedSlot()) );
370 }
371
372 /* table illustrating the different scenarios and the events generated
373  * ====================
374  *
375  *  event     isDown()
376  *
377  *  released  false   }
378  *  clicked   false   }= short click
379  *
380  *  released  false    = cancelled click (mouse released outside of button area,
381  *                                        before long click delay kicks in)
382  *
383  *  released  true    }
384  *  clicked   true    }= long click (multiple of these generated)
385  *  released  false    = stop long click (mouse released / moved outside of
386  *                                        button area)
387  * (clicked   false)   = stop long click (additional event if mouse released
388  *                                        inside of button area)
389  */
390
391 void QToolButtonExt::releasedSlot()
392 {
393     if( isDown() )
394     {
395         // we are beginning a long click
396         longClick = true;
397         shortClick = false;
398     }
399     else
400     {
401         if( longClick )
402         {
403             // we are stopping a long click
404             longClick = false;
405             shortClick = false;
406         }
407         else
408         {
409             // we are generating a short click
410             longClick = false;
411             shortClick = true;
412         }
413     }
414 }
415
416 void QToolButtonExt::clickedSlot()
417 {
418     if( longClick )
419         emit longClicked();
420     else if( shortClick )
421         emit shortClicked();
422 }
423
424 YesNoCheckBox::YesNoCheckBox( QWidget *parent ) : QCheckBox( parent )
425 {
426     setEnabled( false );
427     setStyleSheet("\
428                   QCheckBox::indicator:unchecked:hover,\
429                   QCheckBox::indicator:unchecked {\
430                       image: url(:/menu/quit);\
431                   }\
432                   QCheckBox::indicator:checked:hover,\
433                   QCheckBox::indicator:checked {\
434                       image: url(:/valid);\
435                   }\
436         ");
437 }