1 /************************************************************************
2 *
3 * Copyright 2010-2012 Jakob Leben (jakob.leben@gmail.com)
4 *
5 * This file is part of SuperCollider Qt GUI.
6 *
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 2 of the License, or
10 * (at your option) any later version.
11 *
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.
16 *
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/>.
19 *
20 ************************************************************************/
21
22 #include "primitives.h"
23
24 #include "../Common.h"
25 #include "../type_codec.hpp"
26 #include "../QcApplication.h"
27 #include "../QObjectProxy.h"
28 #include "../style/style.hpp"
29 #include "../QcCallback.hpp"
30 #include "QtCollider.h"
31
32 #ifdef Q_OS_MAC
33 # include "../hacks/hacks_mac.hpp"
34 #endif
35
36 #include <PyrKernel.h>
37
38 #include <QDesktopServices>
39 #include <QFontDatabase>
40 #include <QFontInfo>
41 #include <QFontMetrics>
42 #include <QDesktopWidget>
43 #include <QStyleFactory>
44 #include <QCursor>
45 #include <QScreen>
46
47 namespace QtCollider {
48
49 QC_LANG_PRIMITIVE(QtGUI_SetDebugLevel, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
50 QtCollider::setDebugLevel(QtCollider::get(a));
51 return errNone;
52 }
53
54 QC_LANG_PRIMITIVE(QtGUI_DebugLevel, 0, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
55 SetInt(r, QtCollider::debugLevel());
56 return errNone;
57 }
58
59 QC_LANG_PRIMITIVE(QWindow_ScreenBounds, 0, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
60 if (!QcApplication::compareThread())
61 return QtCollider::wrongThreadError();
62
63 QRect screenGeometry = QApplication::primaryScreen()->geometry();
64 QtCollider::set(r, screenGeometry);
65 return errNone;
66 }
67
68 QC_LANG_PRIMITIVE(QWindow_AvailableGeometry, 0, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
69 if (!QcApplication::compareThread())
70 return QtCollider::wrongThreadError();
71
72 QRect rect = QApplication::primaryScreen()->availableGeometry();
73 QtCollider::set(r, rect);
74 return errNone;
75 }
76
77 QC_LANG_PRIMITIVE(Qt_StringBounds, 2, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
78 QString str = QtCollider::get(a);
79
80 QFont f = QtCollider::get(a + 1);
81
82 QFontMetrics fm(f);
83 QRect bounds = fm.boundingRect(str);
84
85 // we keep the font height even on empty string;
86 if (str.isEmpty())
87 bounds.setHeight(fm.height());
88
89 QtCollider::set(r, bounds);
90 return errNone;
91 }
92
93 QC_LANG_PRIMITIVE(Qt_AvailableFonts, 0, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
94 QFontDatabase database;
95 QVariantList list;
96 Q_FOREACH (QString family, database.families())
97 list << family;
98 QtCollider::set(r, list);
99 return errNone;
100 }
101
102 QC_LANG_PRIMITIVE(QFont_SetDefaultFont, 2, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
103 if (!QcApplication::compareThread())
104 return QtCollider::wrongThreadError();
105
106 if (!isKindOfSlot(a + 0, SC_CLASS(Font)))
107 return errWrongType;
108
109 QFont font(QtCollider::read<QFont>(a + 0));
110 const char* className = IsSym(a + 1) ? slotRawSymbol(a + 1)->name : 0;
111
112 QApplication::setFont(font, className);
113
114 return errNone;
115 }
116
117 QC_LANG_PRIMITIVE(QFont_DefaultFamilyForStyle, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
118 // NOTE:
119 // On X11 systems we rely on default fontconfig mappings of font familiy names,
120 // as style hints are not necessarily supported.
121 // On other systems, style hints should work.
122
123 if (!QcApplication::compareThread())
124 return QtCollider::wrongThreadError();
125
126 if (!IsInt(a))
127 return errWrongType;
128
129 QFont::StyleHint styleHint;
130 QString family;
131 switch (slotRawInt(a)) {
132 case 0:
133 styleHint = QFont::SansSerif;
134 family = "sans-serif";
135 break;
136 case 1:
137 styleHint = QFont::Serif;
138 family = "serif";
139 break;
140 case 2:
141 styleHint = QFont::Monospace;
142 family = "monospace";
143 break;
144 default:
145 styleHint = QFont::AnyStyle;
146 }
147
148 QFont font(family);
149 font.setStyleHint(styleHint, QFont::PreferMatch);
150
151 QtCollider::set(r, font.defaultFamily());
152
153 return errNone;
154 }
155
156 QC_LANG_PRIMITIVE(Qt_GlobalPalette, 0, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
157 if (!QcApplication::compareThread())
158 return QtCollider::wrongThreadError();
159
160 QPalette p(QApplication::palette());
161 QtCollider::set(r, p);
162 return errNone;
163 }
164
165 QC_LANG_PRIMITIVE(Qt_SetGlobalPalette, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
166 if (!QcApplication::compareThread())
167 return QtCollider::wrongThreadError();
168
169 // The line below is a workaround. The non-win term causes Error C2440 in VS
170 // https://msdn.microsoft.com/en-us/library/sy5tsf8z.aspx
171 #if defined(_MSC_VER)
172 QPalette p = (QPalette &&) QtCollider::get(a);
173 #else
174 QPalette p = QtCollider::get(a);
175 #endif
176
177 QApplication::setPalette(p);
178
179 return errNone;
180 }
181
182 QC_LANG_PRIMITIVE(Qt_FocusWidget, 0, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
183 if (!QcApplication::compareThread())
184 return QtCollider::wrongThreadError();
185
186 QWidget* w = QApplication::focusWidget();
187
188 #ifdef Q_OS_MAC
189 // On Mac we need to make additional checks, as Qt does not monitor
190 // focus changes to native Cocoa windows in the same application.
191 if (w && !QtCollider::Mac::isKeyWindow(w))
192 w = 0;
193 #endif
194
195 if (w) {
196 QObjectProxy* proxy = QObjectProxy::fromObject(w);
197 if (proxy && proxy->scObject()) {
198 SetObject(r, proxy->scObject());
199 return errNone;
200 }
201 }
202
203 SetNil(r);
204 return errNone;
205 }
206
207 QC_LANG_PRIMITIVE(Qt_SetStyle, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
208 if (!QcApplication::compareThread())
209 return QtCollider::wrongThreadError();
210
211 QString str = QtCollider::get(a);
212 if (str.isEmpty())
213 return errFailed;
214
215 QStyle* style = QStyleFactory::create(str);
216 if (!style)
217 return errFailed;
218
219 QApplication::setStyle(new QtCollider::Style::StyleImpl(style));
220 return errNone;
221 }
222
223 QC_LANG_PRIMITIVE(Qt_AvailableStyles, 0, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
224 if (!QcApplication::compareThread())
225 return QtCollider::wrongThreadError();
226
227 QVariantList list;
228 Q_FOREACH (QString key, QStyleFactory::keys())
229 list << key;
230
231 QtCollider::set(r, list);
232 return errNone;
233 }
234
235 QC_LANG_PRIMITIVE(QWebView_ClearMemoryCaches, 0, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
236 if (!QcApplication::compareThread())
237 return QtCollider::wrongThreadError();
238
239 // @TODO WebEngine: New cache method?
240 // QWebEngineSettings::clearMemoryCaches();
241
242 return errNone;
243 }
244
245 QC_LANG_PRIMITIVE(Qt_IsMethodOverridden, 2, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
246 if (NotObj(a) || NotSym(a + 1))
247 return errWrongType;
248
249 PyrObject* self = slotRawObject(r);
250 PyrObjectHdr* superclass = slotRawObject(a);
251 PyrSymbol* method = slotRawSymbol(a + 1);
252
253 for (PyrClass* klass = self->classptr; klass != superclass && klass != class_object;
254 klass = slotRawSymbol(&klass->superclass)->u.classobj) {
255 PyrSlot* methodSlot = &klass->methods;
256 if (!IsObj(methodSlot))
257 continue;
258 PyrObject* methodArray = slotRawObject(methodSlot);
259 PyrSlot* methods = methodArray->slots;
260 for (int i = 0; i < methodArray->size; ++i) {
261 PyrMethod* m = slotRawMethod(methods + i);
262 if (slotRawSymbol(&m->name) == method) {
263 SetTrue(r);
264 return errNone;
265 }
266 }
267 }
268
269 SetFalse(r);
270 return errNone;
271 }
272
273 QC_LANG_PRIMITIVE(Qt_CursorPosition, 0, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
274 if (!QcApplication::compareThread())
275 return QtCollider::wrongThreadError();
276
277 QtCollider::set(r, QCursor::pos());
278
279 return errNone;
280 }
281
282 QC_LANG_PRIMITIVE(Qt_SetUrlHandler, 2, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
283 if (!QcApplication::compareThread())
284 return QtCollider::wrongThreadError();
285
286 QString str = QtCollider::get(a);
287
288 if (IsNil(a + 1)) {
289 QDesktopServices::unsetUrlHandler(str);
290 } else {
291 QcCallback* cb = QtCollider::get(a + 1);
292 QDesktopServices::setUrlHandler(str, cb, "onCalled");
293 }
294
295 return errNone;
296 }
297
298 QC_LANG_PRIMITIVE(Qt_SetAppMenus, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
299 if (!QcApplication::compareThread())
300 return QtCollider::wrongThreadError();
301
302 QList<QMenu*> menuList;
303
304
305 if (isKindOfSlot(a, class_array)) {
306 QMenuBar* menuBar = QcApplication::getMainMenu();
307 if (menuBar) {
308 menuBar->clear();
309
310 PyrObject* obj = slotRawObject(a);
311 PyrSlot* slots = obj->slots;
312 int size = obj->size;
313
314 for (int i = 0; i < size; ++i, ++slots) {
315 QMenu* menu = QtCollider::get<QMenu*>(slots);
316 if (menu) {
317 menuBar->addMenu(menu);
318 } else {
319 menuBar->addSeparator();
320 }
321 }
322 }
323 }
324
325 return errNone;
326 }
327
328 QC_LANG_PRIMITIVE(QView_AddActionToView, 3, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
329 if (!QcApplication::compareThread())
330 return QtCollider::wrongThreadError();
331
332 QObjectProxy* widgetObj = TypeCodec<QObjectProxy*>::safeRead(a);
333 QObjectProxy* actionObj = TypeCodec<QObjectProxy*>::safeRead(a + 1);
334 QObjectProxy* beforeObj = TypeCodec<QObjectProxy*>::safeRead(a + 2);
335
336 if (widgetObj && actionObj) {
337 QWidget* widget = qobject_cast<QWidget*>(widgetObj->object());
338 QAction* action = qobject_cast<QAction*>(actionObj->object());
339 QAction* beforeAction = beforeObj ? qobject_cast<QAction*>(beforeObj->object()) : 0;
340
341 if (!beforeAction) {
342 PyrSlot* indexArg = a + 2;
343
344 if (NotNil(indexArg)) {
345 if (IsInt(indexArg)) {
346 int index = std::max(slotRawInt(indexArg), 0);
347
348 auto actions = widget->actions();
349
350 if (index < actions.size()) {
351 beforeAction = actions[index];
352 }
353 } else {
354 return errFailed;
355 }
356 }
357 }
358
359 if (widget && action) {
360 if (beforeAction) {
361 widget->insertAction(beforeAction, action);
362 } else {
363 widget->addAction(action);
364 }
365 return errNone;
366 }
367 }
368
369 return errFailed;
370 }
371
372 QC_LANG_PRIMITIVE(QView_RemoveActionFromView, 2, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
373 if (!QcApplication::compareThread())
374 return QtCollider::wrongThreadError();
375
376 QObjectProxy* widgetObj = TypeCodec<QObjectProxy*>::safeRead(a);
377 QObjectProxy* actionObj = TypeCodec<QObjectProxy*>::safeRead(a + 1);
378
379 if (widgetObj && actionObj) {
380 QWidget* widget = qobject_cast<QWidget*>(widgetObj->object());
381 QAction* action = qobject_cast<QAction*>(actionObj->object());
382
383 if (widget && action) {
384 widget->removeAction(action);
385 return errNone;
386 }
387 }
388
389 return errFailed;
390 }
391
392 QC_LANG_PRIMITIVE(QView_RemoveAllActionsFromView, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
393 if (!QcApplication::compareThread())
394 return QtCollider::wrongThreadError();
395
396 QObjectProxy* widgetObj = TypeCodec<QObjectProxy*>::safeRead(a);
397
398 if (widgetObj) {
399 QWidget* widget = qobject_cast<QWidget*>(widgetObj->object());
400
401 if (widget) {
402 auto actions = widget->actions();
403 for (auto action : actions) {
404 widget->removeAction(action);
405 }
406
407 return errNone;
408 }
409 }
410
411 return errFailed;
412 }
413
414
defineMiscPrimitives()415 void defineMiscPrimitives() {
416 LangPrimitiveDefiner definer;
417 definer.define<QtGUI_SetDebugLevel>();
418 definer.define<QtGUI_DebugLevel>();
419 definer.define<QWindow_ScreenBounds>();
420 definer.define<QWindow_AvailableGeometry>();
421 definer.define<Qt_StringBounds>();
422 definer.define<Qt_AvailableFonts>();
423 definer.define<QFont_SetDefaultFont>();
424 definer.define<QFont_DefaultFamilyForStyle>();
425 definer.define<Qt_GlobalPalette>();
426 definer.define<Qt_SetGlobalPalette>();
427 definer.define<Qt_FocusWidget>();
428 definer.define<Qt_SetStyle>();
429 definer.define<Qt_AvailableStyles>();
430 definer.define<Qt_IsMethodOverridden>();
431 definer.define<QWebView_ClearMemoryCaches>();
432 definer.define<Qt_CursorPosition>();
433 definer.define<Qt_SetUrlHandler>();
434 definer.define<Qt_SetAppMenus>();
435 definer.define<QView_AddActionToView>();
436 definer.define<QView_RemoveActionFromView>();
437 definer.define<QView_RemoveAllActionsFromView>();
438 }
439
440 } // namespace QtCollider
441