"Post Window" -> "Post window" prevents it being seen as two separate
[supercollider.git] / QtCollider / QWidgetProxy.cpp
blob9fb53a8211c1e37a995cafcbaa1f18add335eaad
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"
23 #include "painting.h"
24 #include "Common.h"
26 #include <QApplication>
27 #include <QLayout>
28 #include <QMouseEvent>
29 #include <QKeyEvent>
30 #include <QPainter>
31 #include <QFontMetrics>
32 #include <QUrl>
34 #ifdef Q_WS_X11
35 # include "hacks/hacks_x11.hpp"
36 # include <QX11Info>
37 # include <X11/Xlib.h>
38 // X11 defines the following, clashing with QEvent::Type enum
39 # undef KeyPress
40 # undef KeyRelease
41 #endif
43 using namespace QtCollider;
45 QAtomicInt QWidgetProxy::_globalEventMask = 0;
47 QWidgetProxy::QWidgetProxy( QWidget *w, PyrObject *po )
48 : QObjectProxy( w, po ), _keyEventWidget( w ), _mouseEventWidget( w )
49 { }
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 );
60 _keyEventWidget = w;
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();
86 if(!w) return false;
88 Qt::WindowFlags flags = w->windowFlags();
89 if( flags & Qt::Window && flags & Qt::WindowStaysOnTopHint ) return true;
90 else return false;
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.") );
108 delete exLayout;
110 qcDebugMsg( 2, QString("Setting layout.") );
111 w->setLayout( l );
112 l->activate();
114 else {
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 );
128 return true;
130 return false;
133 void QWidgetProxy::customEvent( QEvent *e )
135 int type = e->type();
136 switch( type ) {
137 case QtCollider::Event_Proxy_BringFront:
138 bringFrontEvent();
139 return;
140 case QtCollider::Event_Proxy_SetFocus:
141 setFocusEvent( static_cast<SetFocusEvent*>(e) );
142 return;
143 case QtCollider::Event_Proxy_SetAlwaysOnTop:
144 setAlwaysOnTopEvent( static_cast<SetAlwaysOnTopEvent*>(e) );
145 return;
146 case QtCollider::Event_Proxy_StartDrag:
147 startDragEvent( static_cast<StartDragEvent*>(e) );
148 return;
149 default:
150 QObjectProxy::customEvent(e);
157 void QWidgetProxy::bringFrontEvent() {
158 QWidget *w = widget();
159 if( !w ) return;
161 w->setWindowState( w->windowState() & ~Qt::WindowMinimized
162 | Qt::WindowActive );
163 w->show();
164 w->raise();
166 #ifdef Q_WS_X11
167 raise_window(QX11Info::display(), w);
168 #endif
171 void QWidgetProxy::setFocusEvent( QtCollider::SetFocusEvent *e ) {
172 if( !widget() ) return;
174 if( e->focus )
175 widget()->setFocus( Qt::OtherFocusReason );
176 else
177 widget()->clearFocus();
180 void QWidgetProxy::setAlwaysOnTopEvent( QtCollider::SetAlwaysOnTopEvent *e )
182 QWidget *w = widget();
183 if( !w ) return;
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
198 w->move(pos);
199 if( visible ) w->show();
203 void QWidgetProxy::startDragEvent( StartDragEvent* e )
205 QWidget *w = widget();
206 if( !w ) return;
208 QFont f;
209 const QString & label = e->label;
210 QFontMetrics fm( f );
211 QSize size = fm.size( 0, label ) + QSize(8,4);
213 QPixmap pix( size );
214 QPainter p( &pix );
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 );
219 p.end();
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 ) );
228 drag->exec();
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
236 switch( eh.type ) {
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:
250 case QEvent::Enter:
251 case QEvent::Leave:
252 return eh.enabled && interpretMouseEvent( o, e, args );
254 case QEvent::Wheel:
255 return eh.enabled && interpretMouseWheelEvent( o, e, args );
257 case QEvent::DragEnter:
258 case QEvent::DragMove:
259 case QEvent::Drop:
260 return eh.enabled && interpretDragEvent( o, e, args );
262 default:
263 return eh.enabled;
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 )
277 return true;
279 QMouseEvent *mouse = static_cast<QMouseEvent*>( e );
280 QPoint pt = ( _mouseEventWidget == w ?
281 mouse->pos() :
282 _mouseEventWidget->mapTo( w, mouse->pos() ) );
283 args << pt.x();
284 args << pt.y();
286 args << (int) mouse->modifiers();
288 if( etype == QEvent::MouseMove )
290 int buttons = mouse->buttons();
292 if( buttons == 0 ) {
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()) )
299 return false;
301 // Reject the events when mouse pointer leaves the window,
302 // resulting in out-of-bounds coordinates
303 if( win == w ) {
304 if( pt.x() < 0 || pt.x() >= w->width() || pt.y() < 0 || pt.y() >= w->height() )
305 return false;
309 args << (int) mouse->buttons();
311 else
313 // MouseButtonPress, MouseButtonDblClick, MouseButtonRelease
315 int button;
317 switch( mouse->button() ) {
318 case Qt::LeftButton:
319 button = 0; break;
320 case Qt::RightButton:
321 button = 1; break;
322 case Qt::MidButton:
323 button = 2; break;
324 default:
325 button = -1;
328 args << button;
330 if( etype == QEvent::MouseButtonPress )
331 args << 1;
332 else if( etype == QEvent::MouseButtonDblClick )
333 args << 2;
336 return true;
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 ?
352 we->pos() :
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;
358 args << pt.x();
359 args << pt.y();
360 args << (int) we->modifiers();
361 args << (ort == Qt::Horizontal ? deg : 0);
362 args << (ort == Qt::Vertical ? deg : 0);
364 return true;
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 );
373 int key = ke->key();
375 int mods = ke->modifiers();
377 QChar character;
379 #ifdef Q_WS_MAC
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 );
389 else
390 #endif
392 QString text( ke->text() );
393 if (text.count()) character = text[0];
396 int unicode = character.unicode();
398 #ifdef Q_WS_X11
399 KeySym sym = ke->nativeVirtualKey();
400 int keycode = XKeysymToKeycode( QX11Info::display(), sym );
401 #else
402 // FIXME: On Mac OS X, this does not work for modifier keys
403 int keycode = ke->nativeVirtualKey();
404 #endif
406 args << character;
407 args << mods;
408 args << unicode;
409 args << keycode;
410 args << key;
411 args << ke->spontaneous();
413 return true;
416 static QString urlAsString( const QUrl & url )
418 if(url.scheme() == "file")
419 return url.toLocalFile();
420 else
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 ) {
430 VariantList list;
431 Q_FOREACH( QUrl url, urls )
432 list.data << urlAsString( url );
433 args << QVariant::fromValue<VariantList>(list);
435 else {
436 args << urlAsString( urls[0] );
439 else if( data->hasText() )
441 args << data->text();
443 else
445 return false;
448 return true;
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" );
462 args << internal;
463 if(!internal)
464 interpretMimeData(data, args);
466 else
468 QPoint pos = dnd->pos();
469 args << pos.x() << pos.y();
472 return true;
476 void QWidgetProxy::customPaint( QPainter *painter )
478 if( QtCollider::paintingAnnounced() ) {
479 qcDebugMsg(1, "WARNING: Custom painting already in progress. Will not paint." );
480 return;
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 ) );