1 /************************************************************************
3 * Copyright 2010-2011 Jakob Leben (jakob.leben@gmail.com)
5 * This file is part of SuperCollider Qt GUI.
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ************************************************************************/
22 #include "QWidgetProxy.h"
26 #include <QApplication>
28 #include <QMouseEvent>
31 #include <QFontMetrics>
35 # include "hacks/hacks_x11.hpp"
37 # include <X11/Xlib.h>
38 // X11 defines the following, clashing with QEvent::Type enum
43 using namespace QtCollider
;
45 QAtomicInt
QWidgetProxy::_globalEventMask
= 0;
47 QWidgetProxy::QWidgetProxy( QWidget
*w
, PyrObject
*po
)
48 : QObjectProxy( w
, po
), _keyEventWidget( w
), _mouseEventWidget( w
)
51 void QWidgetProxy::setKeyEventWidget( QWidget
*w
)
53 if( w
== 0 || w
== _keyEventWidget
) return;
55 QWidget
*me
= widget();
57 if( _keyEventWidget
!= me
)
58 _keyEventWidget
->removeEventFilter( this );
62 if( _keyEventWidget
!= me
) {
63 _keyEventWidget
->installEventFilter( this );
67 void QWidgetProxy::setMouseEventWidget( QWidget
*w
)
69 if( w
== 0 || w
== _mouseEventWidget
) return;
71 QWidget
*me
= widget();
73 if( _mouseEventWidget
!= me
)
74 _mouseEventWidget
->removeEventFilter( this );
76 _mouseEventWidget
= w
;
78 if( _mouseEventWidget
!= me
) {
79 _mouseEventWidget
->installEventFilter( this );
83 bool QWidgetProxy::alwaysOnTop()
85 QWidget
*w
= widget();
88 Qt::WindowFlags flags
= w
->windowFlags();
89 if( flags
& Qt::Window
&& flags
& Qt::WindowStaysOnTopHint
) return true;
93 void QWidgetProxy::refresh() {
94 QWidget
*w
= widget();
95 if( w
) sendRefreshEventRecursive( w
);
98 void QWidgetProxy::setLayout ( QObjectProxy
*layoutProxy
) {
100 QWidget
*w
= widget();
101 QLayout
*l
= qobject_cast
<QLayout
*>( layoutProxy
->object() );
102 if( !w
|| !l
) return;
104 QLayout
*exLayout
= w
->layout();
105 if( exLayout
!= l
) {
106 if( exLayout
!= 0 ) {
107 qcDebugMsg( 2, QString("Deleting old layout.") );
110 qcDebugMsg( 2, QString("Setting layout.") );
115 qcDebugMsg( 2, QString("Layout same as existing. Will do nothing.") );
119 bool QWidgetProxy::setParent( QObjectProxy
*parentProxy
)
121 QObject
*parent
= parentProxy
->object();
122 if( !parent
|| !widget() ) return true;
124 if( parent
->isWidgetType() ) {
125 QWidget
*pw
= qobject_cast
<QWidget
*>(parent
);
126 bool ok
= pw
->metaObject()->invokeMethod( pw
, "addChild", Q_ARG( QWidget
*, widget() ) );
127 if( !ok
) widget()->setParent( pw
);
133 void QWidgetProxy::customEvent( QEvent
*e
)
135 int type
= e
->type();
137 case QtCollider::Event_Proxy_BringFront
:
140 case QtCollider::Event_Proxy_SetFocus
:
141 setFocusEvent( static_cast<SetFocusEvent
*>(e
) );
143 case QtCollider::Event_Proxy_SetAlwaysOnTop
:
144 setAlwaysOnTopEvent( static_cast<SetAlwaysOnTopEvent
*>(e
) );
146 case QtCollider::Event_Proxy_StartDrag
:
147 startDragEvent( static_cast<StartDragEvent
*>(e
) );
150 QObjectProxy::customEvent(e
);
157 void QWidgetProxy::bringFrontEvent() {
158 QWidget
*w
= widget();
161 w
->setWindowState( w
->windowState() & ~Qt::WindowMinimized
162 | Qt::WindowActive
);
167 raise_window(QX11Info::display(), w
);
171 void QWidgetProxy::setFocusEvent( QtCollider::SetFocusEvent
*e
) {
172 if( !widget() ) return;
175 widget()->setFocus( Qt::OtherFocusReason
);
177 widget()->clearFocus();
180 void QWidgetProxy::setAlwaysOnTopEvent( QtCollider::SetAlwaysOnTopEvent
*e
)
182 QWidget
*w
= widget();
185 Qt::WindowFlags flags
= w
->windowFlags();
186 if( flags
& Qt::Window
) {
187 if( e
->alwaysOnTop
) flags
|= Qt::WindowStaysOnTopHint
;
188 else flags
&= ~Qt::WindowStaysOnTopHint
;
190 // record the initial state to restore it later
191 QPoint pos
= w
->pos();
192 bool visible
= w
->isVisible();
194 w
->setWindowFlags( flags
);
196 // setting window flags will move the window to (0,0) and hide it,
197 // so restore the initial state
199 if( visible
) w
->show();
203 void QWidgetProxy::startDragEvent( StartDragEvent
* e
)
205 QWidget
*w
= widget();
209 const QString
& label
= e
->label
;
210 QFontMetrics
fm( f
);
211 QSize size
= fm
.size( 0, label
) + QSize(8,4);
215 p
.setBrush( QColor(255,255,255) );
216 QRect
r( pix
.rect() );
217 p
.drawRect(r
.adjusted(0,0,-1,-1));
218 p
.drawText( r
, Qt::AlignCenter
, label
);
221 QMimeData
*mime
= e
->data
;
222 e
->data
= 0; // prevent deleting the data when event destroyed;
224 QDrag
*drag
= new QDrag(w
);
225 drag
->setMimeData( mime
);
226 drag
->setPixmap( pix
);
227 drag
->setHotSpot( QPoint( 0, + r
.height() + 2 ) );
231 bool QWidgetProxy::filterEvent( QObject
*o
, QEvent
*e
, EventHandlerData
&eh
, QList
<QVariant
> & args
)
233 // NOTE We assume that qObject need not be checked here, as we wouldn't get events if
234 // it wasn't existing
238 case QEvent::KeyPress
:
239 return ((_globalEventMask
& KeyPress
) || eh
.enabled
)
240 && interpretKeyEvent( o
, e
, args
);
242 case QEvent::KeyRelease
:
243 return ((_globalEventMask
& KeyRelease
) || eh
.enabled
)
244 && interpretKeyEvent( o
, e
, args
);
246 case QEvent::MouseButtonPress
:
247 case QEvent::MouseMove
:
248 case QEvent::MouseButtonRelease
:
249 case QEvent::MouseButtonDblClick
:
252 return eh
.enabled
&& interpretMouseEvent( o
, e
, args
);
255 return eh
.enabled
&& interpretMouseWheelEvent( o
, e
, args
);
257 case QEvent::DragEnter
:
258 case QEvent::DragMove
:
260 return eh
.enabled
&& interpretDragEvent( o
, e
, args
);
268 bool QWidgetProxy::interpretMouseEvent( QObject
*o
, QEvent
*e
, QList
<QVariant
> &args
)
270 if( o
!= _mouseEventWidget
|| !_mouseEventWidget
->isEnabled() ) return false;
272 QWidget
*w
= widget();
274 QEvent::Type etype
= e
->type();
276 if( etype
== QEvent::Enter
|| etype
== QEvent::Leave
)
279 QMouseEvent
*mouse
= static_cast<QMouseEvent
*>( e
);
280 QPoint pt
= ( _mouseEventWidget
== w
?
282 _mouseEventWidget
->mapTo( w
, mouse
->pos() ) );
286 args
<< (int) mouse
->modifiers();
288 if( etype
== QEvent::MouseMove
)
290 int buttons
= mouse
->buttons();
293 // Special treatment of mouse-tracking events.
295 QWidget
*win
= w
->window();
297 // Only accept if window has a special property enabled.
298 if( !(win
&& win
->property("_qc_win_mouse_tracking").toBool()) )
301 // Reject the events when mouse pointer leaves the window,
302 // resulting in out-of-bounds coordinates
304 if( pt
.x() < 0 || pt
.x() >= w
->width() || pt
.y() < 0 || pt
.y() >= w
->height() )
309 args
<< (int) mouse
->buttons();
313 // MouseButtonPress, MouseButtonDblClick, MouseButtonRelease
317 switch( mouse
->button() ) {
320 case Qt::RightButton
:
330 if( etype
== QEvent::MouseButtonPress
)
332 else if( etype
== QEvent::MouseButtonDblClick
)
339 bool QWidgetProxy::interpretMouseWheelEvent( QObject
*o
, QEvent
*e
, QList
<QVariant
> &args
)
341 // NOTE: There seems to be a bug in wheel event propagation:
342 // the event is propagated to parent twice!
343 // Therefore we do not let the propagated events through to SC,
344 // (we only let the "spontaneous" ones).
346 if( o
!= _mouseEventWidget
|| !e
->spontaneous() || !_mouseEventWidget
->isEnabled() ) return false;
348 QWheelEvent
*we
= static_cast<QWheelEvent
*>(e
);
350 QWidget
*w
= widget();
351 QPoint pt
= _mouseEventWidget
== w
?
353 _mouseEventWidget
->mapTo( w
, we
->pos() );
354 Qt::Orientation ort
= we
->orientation();
355 // calculate degrees: delta is in 1/8 of a degree.
356 int deg
= we
->delta() / 8;
360 args
<< (int) we
->modifiers();
361 args
<< (ort
== Qt::Horizontal
? deg
: 0);
362 args
<< (ort
== Qt::Vertical
? deg
: 0);
367 bool QWidgetProxy::interpretKeyEvent( QObject
*o
, QEvent
*e
, QList
<QVariant
> &args
)
369 if( o
!= _keyEventWidget
|| !_keyEventWidget
->isEnabled() ) return false;
371 QKeyEvent
*ke
= static_cast<QKeyEvent
*>( e
);
375 int mods
= ke
->modifiers();
380 bool isLetter
= key
>= Qt::Key_A
&& key
<= Qt::Key_Z
;
381 if (mods
& Qt::MetaModifier
&& isLetter
)
383 character
= QChar(key
- Qt::Key_A
+ 1);
385 else if(mods
& Qt::AltModifier
&& isLetter
)
387 character
= (mods
& Qt::ShiftModifier
) ? QChar(key
) : QChar(key
- Qt::Key_A
+ 97 );
392 QString
text( ke
->text() );
393 if (text
.count()) character
= text
[0];
396 int unicode
= character
.unicode();
399 KeySym sym
= ke
->nativeVirtualKey();
400 int keycode
= XKeysymToKeycode( QX11Info::display(), sym
);
402 // FIXME: On Mac OS X, this does not work for modifier keys
403 int keycode
= ke
->nativeVirtualKey();
411 args
<< ke
->spontaneous();
416 static QString
urlAsString( const QUrl
& url
)
418 if(url
.scheme() == "file")
419 return url
.toLocalFile();
421 return url
.toString();
424 static bool interpretMimeData( const QMimeData
*data
, QList
<QVariant
> &args
)
426 if( data
->hasUrls() )
428 QList
<QUrl
> urls
= data
->urls();
429 if( urls
.count() > 1 ) {
431 Q_FOREACH( QUrl url
, urls
)
432 list
.data
<< urlAsString( url
);
433 args
<< QVariant::fromValue
<VariantList
>(list
);
436 args
<< urlAsString( urls
[0] );
439 else if( data
->hasText() )
441 args
<< data
->text();
451 bool QWidgetProxy::interpretDragEvent( QObject
*o
, QEvent
*e
, QList
<QVariant
> &args
)
453 if( o
!= _mouseEventWidget
) return false;
455 QDropEvent
*dnd
= static_cast<QDropEvent
*>(e
);
457 const QMimeData
*data
= dnd
->mimeData();
459 if( dnd
->type() == QEvent::DragEnter
)
461 bool internal
= data
->hasFormat( "application/supercollider" );
464 interpretMimeData(data
, args
);
468 QPoint pos
= dnd
->pos();
469 args
<< pos
.x() << pos
.y();
476 void QWidgetProxy::customPaint( QPainter
*painter
)
478 if( QtCollider::paintingAnnounced() ) {
479 qcDebugMsg(1, "WARNING: Custom painting already in progress. Will not paint." );
483 QtCollider::announcePainting();
485 QtCollider::lockLang();
487 if( QtCollider::beginPainting( painter
) ) {
488 invokeScMethod( SC_SYM(doDrawFunc
), QList
<QVariant
>(), 0, true );
489 QtCollider::endPainting();
492 QtCollider::unlockLang();
495 void QWidgetProxy::sendRefreshEventRecursive( QWidget
*w
) {
496 QEvent
event( static_cast<QEvent::Type
>( QtCollider::Event_Refresh
) );
497 QApplication::sendEvent( w
, &event
);
499 const QObjectList
&children
= w
->children();
500 Q_FOREACH( QObject
*child
, children
) {
501 if( child
->isWidgetType() )
502 sendRefreshEventRecursive( static_cast<QWidget
*>( child
) );